diff --git a/exam_template/python/Graph.py b/exam_template/python/Graph.py index bd6eb43..8e57edf 100644 --- a/exam_template/python/Graph.py +++ b/exam_template/python/Graph.py @@ -2,6 +2,145 @@ from sys import argv from pathlib import Path from math import sin, cos, pi +# from run import printred + +class Matrix: + """ Adjacency matrix which supports 0 and 1 """ + + def __init__(self, matrix): + self.matrix = matrix + + def __iter__(self): + return iter(self.matrix) + + def __len__(self): + return len(self.matrix) + + def __eq__(self, other): + return self.matrix == other + + def __str__(self): + return "\n".join(' '.join('\033[32m1\033[0m' if b else '\033[31m0\033[0m' for b in line) for line in self) + + def __getitem__(self, key): + return self.matrix[key] + + def __setitem__(self, key, item): + self.matrix[key] = item + + @classmethod + def fromGraph(cls, graph): + return graph.toMatrix() + + @classmethod + def fromString(cls, string): + matrix = [] + for line in string.split('\n'): + matrix.append([True if char == '1' else False for char in line]) + + if len(matrix) != len(matrix[0]): + raise ValueError('Matrix not covering all points') + + return cls(matrix) + + def toGraph(self): + nodes = [chr(i + 65) for i in range(len(self))] + + edges = [] + for i, line in enumerate(self): + edges += [(nodes[i],nodes[j]) for j, b in enumerate(line) if b] + + return Graph(nodes, edges) + + def toLaTeX(self): + return '\\begin{pmatrix}\n' \ + + '\n'.join(' ' + ' & '.join('1' if b else '0' for b in line) + ' \\\\' for line in self.matrix) \ + + '\n\\end{pmatrix}' + + +class Graph: + def __init__(self, nodes, edges): + self.nodes = nodes + self.edges = edges + + def __str__(self): + print(self.isUndirected()) + return \ + f'Nodes: {" ".join(self.nodes)}\n' \ + + f'Edges: {" ".join(x+y for x,y in (self.undirectedEdgeSet() if self.isUndirected() else self.edges))}' + + @classmethod + def fromMatrix(cls, matrix): + return matrix.toGraph() + + @classmethod + def fromString(cls, string): + splitData = string.split('\n\n') + + if splitData[0] == 'complete': + data1 = 'undirected' + nodes = [chr(i + 65) for i in range(int(splitData[1]))] + data2 = ' '.join(nodes) + data3 = '\n'.join([a+b for a in nodes for b in nodes if a != b]) + elif splitData[0] == 'matrix': + return Graph.fromMatrix(Matrix.fromString(splitData[1])) + else: + data1, data2, data3 = splitData + + graphType = data1 + nodes = data2.split(' ') + edges = [(x,y) for x,y in data3.split('\n')] + + if graphType == 'undirected': + edges = [(a,b) for a in nodes for b in nodes if (a,b) in edges or (b,a) in edges] + + return cls(nodes, edges) + + def toMatrix(self): + rows = [] + for node in self.nodes: + rows.append([(node,node2) in self.edges for node2 in self.nodes]) + return Matrix(rows) + + def isUndirected(self): + matrix = self.toMatrix() + + flipv = lambda matrix: list(reversed(matrix)) + rot90cc = lambda matrix: list(list(x) for x in zip(*reversed(matrix))) + + return matrix == rot90cc(flipv(matrix)) \ + and all(matrix[i][i] == 0 for i in range(len(matrix))) + + def undirectedEdgeSet(self): + edges = self.edges + if self.isUndirected(): + edges = sorted(list(set((x,y) if x < y else (y,x) for x,y in edges))) + return edges + + def toLaTeX(self): + zippedNodes = zip(self.nodes, generateNodeCoords(len(self.nodes))) + nodeString = '\n'.join(f'\\node ({name}) at ({x},{y}) {{${name}$}};' for name,(x,y) in zippedNodes) + + if self.isUndirected(): + edgeString = '\n'.join(f'\\draw ({x}) -- ({y});' for x,y in self.undirectedEdgeSet()) + else: + edgeString = '\n'.join( + f'\\draw [-{{Latex[scale=1]}}, bend left=8] ({x}) to ({y});' if y != x and (y,x) in self.edges + else f'\\draw [-{{Latex[scale=1]}}] ({x}) to [{generateLoopInOutAngle(x, self.nodes)},looseness=8] ({y});' if x == y + else f'\\draw [-{{Latex[scale=1]}}] ({x}) to ({y});' + for x,y in self.edges + ) + + return (nodeString, edgeString) + + +def generateLoopInOutAngle(node, nodes): + baseAngle = 360 / len(nodes) + nodeNum = [i for i,n in enumerate(nodes) if n == node][0] + angle = nodeNum * baseAngle + 90 + return f'out={angle + 15},in={angle - 15}' + + def generateNodeCoords(n): vectorLength = n / 2 degreeToTurn = (2 * pi) / n @@ -18,50 +157,28 @@ def generateNodeCoords(n): )) return nodeCoords - -def latexify(graphType, nodes, edges): - zippedNodes = zip(nodes, generateNodeCoords(len(nodes))) - - nodeString = '\n'.join(f'\\node ({name}) at ({x},{y}) {{${name}$}};' for name,(x,y) in zippedNodes) - - if graphType == 'directed': - edgeString = '\n'.join(f'\\arrow{{{x}}}{{{y}}}' for x,y in edges) - elif graphType == 'undirected': - edgeString = '\n'.join(f'\\draw ({x}) -- ({y});' for x,y in edges) - else: - print('CAN\'t RECOGNIZE GRAPHTYPE: ' + graphType) - exit(1) - - return (nodeString, edgeString) - -def parseInput(inputData): - splitData = inputData.split('\n\n') - if splitData[0] == 'complete': - data1 = 'undirected' - nodes = [chr(i + 65) for i in range(int(splitData[1]))] - data2 = ' '.join(nodes) - data3 = '\n'.join([a+b for a in nodes for b in nodes if a < b]) - pass - else: - data1, data2, data3 = splitData - - graphType = data1 - nodes = data2.split(' ') - edges = [(x,y) for x,y in data3.split('\n')] - - return (graphType, nodes, edges) def processFileContent(raw, template): - content = latexify(*parseInput(raw)) - return template.replace('%NODES', content[0]).replace('%EDGES', content[1]) + lines = raw.split('\n') + lines.pop(1) + outputType = lines.pop(0) + + graph = Graph.fromString('\n'.join(lines)) + + print(graph) + print(graph.toMatrix()) + + if outputType == 'toGraph': + content = graph.toLaTeX() + return template.replace('%NODES', content[0]).replace('%EDGES', content[1]) + else: + content = graph.toMatrix().toLaTeX() + return template.replace('%CONTENT', content) + if __name__ == '__main__': - filename = argv[1] - - with open(filename) as file: - content = latexify(*parseInput(file.read())) - - with open(str(Path(__file__).parent.absolute()) + '/tex_templates/Graph.tex') as template: - with open(argv[2], 'w') as destination_file: - destination_file.write(template.read().replace('%NODES', content[0]).replace('%EDGES', content[1])) \ No newline at end of file + matrix = Matrix([[False, False, True], [True, False, True], [True, False, False]]) + print(matrix) + print(matrix.toGraph()) + print(matrix.toGraph().isUndirected()) \ No newline at end of file diff --git a/exam_template/python/Hasse.py b/exam_template/python/Hasse.py index e1ca3f7..01960bf 100644 --- a/exam_template/python/Hasse.py +++ b/exam_template/python/Hasse.py @@ -1,12 +1,11 @@ from sys import argv from pathlib import Path +from run import printred + # Increase if the diagram becomes too clobbered HEIGHT_SEPARATOR = 1 -def printred(text): - print(f'\033[31m{text}\033[0m') - # For manual usage via stdin def getRels(relations=None): if relations == None: diff --git a/exam_template/python/run.py b/exam_template/python/run.py index 2f47647..7d7083d 100644 --- a/exam_template/python/run.py +++ b/exam_template/python/run.py @@ -6,6 +6,9 @@ import Graph import Hasse import Truthtable +def printred(text): + print(f'\033[31m{text}\033[0m') + def fetchContentType(content): new_content = content.split('\n') contentType = new_content.pop(0)[2:] @@ -19,7 +22,9 @@ def processContent(content): elif contentType == 'FSA': result = FSA.processFileContent(content, template.read()) elif contentType == 'Graph': - result = Graph.processFileContent(content, template.read()) + result = Graph.processFileContent('toGraph\n\n' + content, template.read()) + elif contentType == 'Matrix': + result = Graph.processFileContent('toMatrix\n\n' + content, template.read()) elif contentType == 'Truthtable': result = Truthtable.processFileContent(content, template.read()) else: diff --git a/exam_template/python/tex_templates/Graph.tex b/exam_template/python/tex_templates/Graph.tex index a10727b..7c63fba 100644 --- a/exam_template/python/tex_templates/Graph.tex +++ b/exam_template/python/tex_templates/Graph.tex @@ -6,6 +6,8 @@ %NODES \end{scope} - %EDGES + \begin{scope}[every draw/.style={}] + %EDGES + \end{scope} \end{tikzpicture} diff --git a/exam_template/python/tex_templates/Matrix.tex b/exam_template/python/tex_templates/Matrix.tex new file mode 100644 index 0000000..cf0685b --- /dev/null +++ b/exam_template/python/tex_templates/Matrix.tex @@ -0,0 +1,5 @@ +\begin{figure}[H] + \[ + %CONTENT + \] +\end{figure} \ No newline at end of file diff --git a/exam_template_graphics/graphics/adjacency.tex b/exam_template_graphics/graphics/adjacency.tex new file mode 100644 index 0000000..5b0d4bf --- /dev/null +++ b/exam_template_graphics/graphics/adjacency.tex @@ -0,0 +1,19 @@ +\newcommand{\arrow}[2]{\path [-{Latex[scale=1]}] (#1) edge (#2);} + +\begin{tikzpicture} + + \begin{scope}[every node/.style={shape=circle, fill=white, draw, inner sep=2pt}] + \node (A) at (0,2.0) {$A$}; +\node (B) at (-2.0,0.0) {$B$}; +\node (C) at (-0.0,-2.0) {$C$}; +\node (D) at (2.0,-0.0) {$D$}; + \end{scope} + + \begin{scope}[every draw/.style={}] + \draw (A) -- (B); +\draw (A) -- (C); +\draw (B) -- (D); +\draw (C) -- (D); + \end{scope} + +\end{tikzpicture} diff --git a/exam_template_graphics/graphics/complete6.tex b/exam_template_graphics/graphics/complete6.tex index 9ad313a..029425d 100644 --- a/exam_template_graphics/graphics/complete6.tex +++ b/exam_template_graphics/graphics/complete6.tex @@ -11,7 +11,8 @@ \node (F) at (2.59808,1.5) {$F$}; \end{scope} - \draw (A) -- (B); + \begin{scope}[every draw/.style={}] + \draw (A) -- (B); \draw (A) -- (C); \draw (A) -- (D); \draw (A) -- (E); @@ -26,5 +27,6 @@ \draw (D) -- (E); \draw (D) -- (F); \draw (E) -- (F); + \end{scope} \end{tikzpicture} diff --git a/exam_template_graphics/graphics/directedFromMatrix.tex b/exam_template_graphics/graphics/directedFromMatrix.tex new file mode 100644 index 0000000..e071356 --- /dev/null +++ b/exam_template_graphics/graphics/directedFromMatrix.tex @@ -0,0 +1,52 @@ +\newcommand{\arrow}[2]{\path [-{Latex[scale=1]}] (#1) edge (#2);} + +\begin{tikzpicture} + + \begin{scope}[every node/.style={shape=circle, fill=white, draw, inner sep=2pt}] + \node (A) at (0,4.0) {$A$}; +\node (B) at (-2.82843,2.82843) {$B$}; +\node (C) at (-4.0,0.0) {$C$}; +\node (D) at (-2.82843,-2.82843) {$D$}; +\node (E) at (-0.0,-4.0) {$E$}; +\node (F) at (2.82843,-2.82843) {$F$}; +\node (G) at (4.0,-0.0) {$G$}; +\node (H) at (2.82843,2.82843) {$H$}; + \end{scope} + + \begin{scope}[every draw/.style={}] + \draw [-{Latex[scale=1]}] (A) to [out=105.0,in=75.0,looseness=8] (A); +\draw [-{Latex[scale=1]}, bend left=8] (A) to (C); +\draw [-{Latex[scale=1]}] (A) to (E); +\draw [-{Latex[scale=1]}, bend left=8] (A) to (G); +\draw [-{Latex[scale=1]}] (B) to (A); +\draw [-{Latex[scale=1]}] (B) to (C); +\draw [-{Latex[scale=1]}] (B) to (F); +\draw [-{Latex[scale=1]}] (B) to (H); +\draw [-{Latex[scale=1]}, bend left=8] (C) to (A); +\draw [-{Latex[scale=1]}, bend left=8] (C) to (D); +\draw [-{Latex[scale=1]}] (C) to (F); +\draw [-{Latex[scale=1]}] (C) to (H); +\draw [-{Latex[scale=1]}] (D) to (A); +\draw [-{Latex[scale=1]}, bend left=8] (D) to (C); +\draw [-{Latex[scale=1]}] (D) to [out=240.0,in=210.0,looseness=8] (D); +\draw [-{Latex[scale=1]}, bend left=8] (D) to (F); +\draw [-{Latex[scale=1]}, bend left=8] (D) to (H); +\draw [-{Latex[scale=1]}] (E) to (B); +\draw [-{Latex[scale=1]}] (E) to [out=285.0,in=255.0,looseness=8] (E); +\draw [-{Latex[scale=1]}] (E) to (G); +\draw [-{Latex[scale=1]}] (E) to (H); +\draw [-{Latex[scale=1]}] (F) to (A); +\draw [-{Latex[scale=1]}, bend left=8] (F) to (D); +\draw [-{Latex[scale=1]}] (F) to [out=330.0,in=300.0,looseness=8] (F); +\draw [-{Latex[scale=1]}, bend left=8] (F) to (H); +\draw [-{Latex[scale=1]}, bend left=8] (G) to (A); +\draw [-{Latex[scale=1]}] (G) to (D); +\draw [-{Latex[scale=1]}] (G) to (F); +\draw [-{Latex[scale=1]}] (G) to (H); +\draw [-{Latex[scale=1]}] (H) to (A); +\draw [-{Latex[scale=1]}, bend left=8] (H) to (D); +\draw [-{Latex[scale=1]}, bend left=8] (H) to (F); +\draw [-{Latex[scale=1]}] (H) to [out=420.0,in=390.0,looseness=8] (H); + \end{scope} + +\end{tikzpicture} diff --git a/exam_template_graphics/graphics/src/adjacency.txt b/exam_template_graphics/graphics/src/adjacency.txt new file mode 100644 index 0000000..bb5fa0b --- /dev/null +++ b/exam_template_graphics/graphics/src/adjacency.txt @@ -0,0 +1,7 @@ +# Graph +matrix + +0110 +1001 +1001 +0110 \ No newline at end of file diff --git a/exam_template_graphics/graphics/src/directedFromMatrix.txt b/exam_template_graphics/graphics/src/directedFromMatrix.txt new file mode 100644 index 0000000..52aa6c7 --- /dev/null +++ b/exam_template_graphics/graphics/src/directedFromMatrix.txt @@ -0,0 +1,11 @@ +# Graph +matrix + +10101010 +10100101 +10010101 +10110101 +01001011 +10010101 +10010101 +10010101 \ No newline at end of file diff --git a/exam_template_graphics/graphics/src/undirectedGraphToMatrix.txt b/exam_template_graphics/graphics/src/undirectedGraphToMatrix.txt new file mode 100644 index 0000000..7be04ed --- /dev/null +++ b/exam_template_graphics/graphics/src/undirectedGraphToMatrix.txt @@ -0,0 +1,17 @@ +# Matrix +undirected + +A B C D E F G H + +AB +AC +CD +DE +HA +FD +CB +FG +DE +DG +AH +FB \ No newline at end of file diff --git a/exam_template_graphics/graphics/undirectedGraph.tex b/exam_template_graphics/graphics/undirectedGraph.tex index 142fc6a..87b373d 100644 --- a/exam_template_graphics/graphics/undirectedGraph.tex +++ b/exam_template_graphics/graphics/undirectedGraph.tex @@ -13,17 +13,17 @@ \node (H) at (2.82843,2.82843) {$H$}; \end{scope} - \draw (A) -- (B); + \begin{scope}[every draw/.style={}] + \draw (A) -- (B); \draw (A) -- (C); +\draw (A) -- (H); +\draw (B) -- (C); +\draw (B) -- (F); \draw (C) -- (D); \draw (D) -- (E); -\draw (H) -- (A); -\draw (F) -- (D); -\draw (C) -- (B); -\draw (F) -- (G); -\draw (D) -- (E); +\draw (D) -- (F); \draw (D) -- (G); -\draw (A) -- (H); -\draw (F) -- (B); +\draw (F) -- (G); + \end{scope} \end{tikzpicture} diff --git a/exam_template_graphics/graphics/undirectedGraphToMatrix.tex b/exam_template_graphics/graphics/undirectedGraphToMatrix.tex new file mode 100644 index 0000000..16089f0 --- /dev/null +++ b/exam_template_graphics/graphics/undirectedGraphToMatrix.tex @@ -0,0 +1,14 @@ +\begin{figure}[H] + \[ + \begin{pmatrix} + 0 & 1 & 1 & 0 & 0 & 0 & 0 & 1 \\ + 1 & 0 & 1 & 0 & 0 & 1 & 0 & 0 \\ + 1 & 1 & 0 & 1 & 0 & 0 & 0 & 0 \\ + 0 & 0 & 1 & 0 & 1 & 1 & 1 & 0 \\ + 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ + 0 & 1 & 0 & 1 & 0 & 0 & 1 & 0 \\ + 0 & 0 & 0 & 1 & 0 & 1 & 0 & 0 \\ + 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ +\end{pmatrix} + \] +\end{figure} \ No newline at end of file diff --git a/exam_template_graphics/main.tex b/exam_template_graphics/main.tex index 9996190..009605b 100644 --- a/exam_template_graphics/main.tex +++ b/exam_template_graphics/main.tex @@ -17,6 +17,25 @@ \usepackage{verbatim} +\newcommand{\verbatimDiagram}[1]{ + \section{#1} + \verbatiminput{graphics/src/#1.txt} + + \includeDiagram{graphics/#1.tex} + + \break{} +} + +\newcommand{\verbatimInput}[1]{ + \section{#1} + + \verbatiminput{graphics/src/#1.txt} + + \input{graphics/#1.tex} + + \break{} +} + \begin{document} \ntnuTitle{} @@ -24,41 +43,20 @@ \tableofcontents - \begin{excs} + \verbatimDiagram{hasse} - \exc{} - \verbatiminput{graphics/src/hasse.txt} + \verbatimDiagram{automata} - \includeDiagram{graphics/hasse.tex} + \verbatimInput{truthtable} - \break{} + \verbatimDiagram{undirectedGraph} - \exc{} - \verbatiminput{graphics/src/automata.txt} + \verbatimDiagram{complete6} - \includeDiagram{graphics/automata.tex} + \verbatimDiagram{adjacency} - \break{} + \verbatimInput{undirectedGraphToMatrix} - \exc{} - \verbatiminput{graphics/src/truthtable.txt} - - \input{graphics/truthtable.tex} - - \break{} - - \exc{} - \verbatiminput{graphics/src/undirectedGraph.txt} - - \includeDiagram{graphics/undirectedGraph.tex} - - \break{} - - \exc{} - \verbatiminput{graphics/src/complete6.txt} - - \includeDiagram{graphics/complete6.tex} - - \end{excs} + \verbatimDiagram{directedFromMatrix} \end{document} \ No newline at end of file diff --git a/exam_template_graphics/main.toc b/exam_template_graphics/main.toc new file mode 100644 index 0000000..16a8a90 --- /dev/null +++ b/exam_template_graphics/main.toc @@ -0,0 +1,9 @@ +\babel@toc {english}{} +\contentsline {section}{\numberline {1}hasse}{1}{section.1}% +\contentsline {section}{\numberline {2}automata}{2}{section.2}% +\contentsline {section}{\numberline {3}truthtable}{3}{section.3}% +\contentsline {section}{\numberline {4}undirectedGraph}{4}{section.4}% +\contentsline {section}{\numberline {5}complete6}{5}{section.5}% +\contentsline {section}{\numberline {6}adjacency}{6}{section.6}% +\contentsline {section}{\numberline {7}undirectedGraphToMatrix}{7}{section.7}% +\contentsline {section}{\numberline {8}directedFromMatrix}{8}{section.8}%