From 7e0a7204589e72c38942908ecabd94bbaa7abb57 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 3 Dec 2024 18:27:07 +0100 Subject: [PATCH] Improved architecture --- .../Controller/PathFindingController.java | 10 +- .../AllShortestPath/AllShortestPathGraph.java | 158 +--------------- .../BellmanFord/BellmanFordService.java | 2 +- .../AllShortestPathDijkstraService.java | 2 +- .../com/example/algorithm/Graph/Graph.java | 55 ++++-- .../example/algorithm/Graph/GraphEdge.java | 39 ++-- .../example/algorithm/Graph/GraphNode.java | 21 ++- .../algorithm/Graph/GraphResponse.java | 54 ++++-- .../JarnikPrim/JarnikPrimService.java | 2 +- .../Kruskal/KruskalService.java | 2 +- .../MinimalSpanningTreeGraph.java | 110 +---------- .../BreadthFirstSearchService.java | 27 --- .../BreadthFirstSearchService.java | 71 ++++++++ .../DepthFirstSearchService.java | 27 --- .../PathFinding/DepthFirstSearchService.java | 60 ++++++ .../DijkstraAlgorithm/DijkstraService.java | 27 --- .../Graph/PathFinding/DijkstraService.java | 74 ++++++++ .../Graph/PathFinding/PathFindingGraph.java | 164 +---------------- .../Graph/PathFinding/PathFindingService.java | 20 +- .../Visualisation/GraphVisualisation.jsx | 54 +++++- .../src/components/control/GraphControl.jsx | 172 +++++------------- 21 files changed, 443 insertions(+), 708 deletions(-) delete mode 100644 algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/BreadthFirstSearch/BreadthFirstSearchService.java create mode 100644 algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/BreadthFirstSearchService.java delete mode 100644 algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DepthFirstSearch/DepthFirstSearchService.java create mode 100644 algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DepthFirstSearchService.java delete mode 100644 algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DijkstraAlgorithm/DijkstraService.java create mode 100644 algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DijkstraService.java diff --git a/algorithm/src/main/java/com/example/algorithm/Controller/PathFindingController.java b/algorithm/src/main/java/com/example/algorithm/Controller/PathFindingController.java index d6d1f93..aa08fef 100644 --- a/algorithm/src/main/java/com/example/algorithm/Controller/PathFindingController.java +++ b/algorithm/src/main/java/com/example/algorithm/Controller/PathFindingController.java @@ -2,9 +2,9 @@ import com.example.algorithm.Explanation.Explanation; import com.example.algorithm.Graph.GraphResponse; -import com.example.algorithm.Graph.PathFinding.BreadthFirstSearch.BreadthFirstSearchService; -import com.example.algorithm.Graph.PathFinding.DepthFirstSearch.DepthFirstSearchService; -import com.example.algorithm.Graph.PathFinding.DijkstraAlgorithm.DijkstraService; +import com.example.algorithm.Graph.PathFinding.BreadthFirstSearchService; +import com.example.algorithm.Graph.PathFinding.DepthFirstSearchService; +import com.example.algorithm.Graph.PathFinding.DijkstraService; import com.example.algorithm.Graph.PathFinding.PathFindingService; import org.json.JSONException; import org.slf4j.Logger; @@ -34,12 +34,12 @@ public PathFindingController(BreadthFirstSearchService bfService, DepthFirstSear @PostMapping( path = "/{searchType}/step" ) - public ResponseEntity step(@PathVariable("searchType") String searchType, RequestEntity graph) { + public ResponseEntity step(@PathVariable("searchType") String searchType, RequestEntity graph) { try { PathFindingService service = stringToService(searchType); logger.info("New PathFinding nextstep-request: " + graph.getBody()); - GraphResponse temp = service.step(graph.getBody()); + GraphResponse[] temp = service.execute(graph.getBody()); return new ResponseEntity<>(temp, HttpStatus.OK); } catch (JSONException e) { logger.error("PathFinding step JSON failed: " + graph); diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/AllShortestPath/AllShortestPathGraph.java b/algorithm/src/main/java/com/example/algorithm/Graph/AllShortestPath/AllShortestPathGraph.java index fb537de..884c146 100644 --- a/algorithm/src/main/java/com/example/algorithm/Graph/AllShortestPath/AllShortestPathGraph.java +++ b/algorithm/src/main/java/com/example/algorithm/Graph/AllShortestPath/AllShortestPathGraph.java @@ -4,6 +4,7 @@ import com.example.algorithm.Graph.GraphEdge; import com.example.algorithm.Graph.GraphNode; import com.example.algorithm.Graph.GraphResponse; +import lombok.Getter; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -12,166 +13,13 @@ import java.util.Arrays; import static com.example.algorithm.Graph.GraphEdge.UNVISITED; +@Getter public class AllShortestPathGraph extends Graph { - GraphNode[] vertices; - int start; - double[] oldReachingCosts; + private int start; public AllShortestPathGraph(String graphJSON) throws JSONException { super(graphJSON); JSONObject graph = new JSONObject(graphJSON); - convNodesFromJSON(graph); start = graph.getInt("start"); - if (vertices[start].getWeight() > 0) { - vertices[start].setWeight(0.0); - oldReachingCosts[start] = 0.0; - } - } - - private void convNodesFromJSON(JSONObject graphJSON) throws JSONException { - JSONArray vertices = graphJSON.getJSONArray("vertices"); - this.vertices = new GraphNode[vertices.length()]; - this.oldReachingCosts = new double[vertices.length()]; - - for (int i = 0; i < vertices.length(); i++) { - String vertexValue = vertices.getJSONObject(i).getString("value"); - Double vertexWeight = vertices.getJSONObject(i).optDouble("weight", Double.POSITIVE_INFINITY); - this.vertices[i] = new GraphNode(vertexValue, vertexWeight); - this.oldReachingCosts[i] = vertexWeight; - } - } - - public GraphResponse dijkstra() { - vertices[start].setWeight(0.0); - ArrayDeque pathToStart = new ArrayDeque<>(); - pathToStart.add(start); - ArrayDeque[] pathsAlreadyKnown = new ArrayDeque[adjacencyMatrix.length]; - Arrays.fill(pathsAlreadyKnown, null); - pathsAlreadyKnown[start] = pathToStart; - dijkstraRecursive(pathsAlreadyKnown); - - return new GraphResponse(adjacencyMatrix, vertices); - } - - private void dijkstraRecursive(ArrayDeque[] pathToVertices) { - double cheapestCost = Double.POSITIVE_INFINITY; - int indexFrom = -1; - int indexTo = -1; - for (int i = 0; i < adjacencyMatrix.length; i++) { - int cheapestEdgeIndex = findCheapestEdge(i); - if (cheapestEdgeIndex >= 0 && adjacencyMatrix[i][cheapestEdgeIndex].getWeight() < cheapestCost && vertices[i].getWeight() < Double.POSITIVE_INFINITY) { - indexFrom = i; - indexTo = cheapestEdgeIndex; - cheapestCost = vertices[indexFrom].getWeight() + adjacencyMatrix[indexFrom][indexTo].getWeight(); - } - } - - if (indexFrom == -1) { - colorAllVisitedEdges(); - return; - } - - vertices[indexTo].setWeight(vertices[indexFrom].getWeight() + adjacencyMatrix[indexFrom][indexTo].getWeight()); - - ArrayDeque currentUpdated = pathToVertices[indexFrom].clone(); - currentUpdated.add(indexTo); - pathToVertices[indexTo] = currentUpdated; - - if (adjacencyMatrix[indexFrom][indexTo].tryToVisit()) { - dijkstraRecursive(pathToVertices); - } - } - - public GraphResponse bellmanFord() { - boolean finished = false; - boolean changes = true; - - for (int k = 0; k < vertices.length - 1 && changes; k++) { - changes = false; - for (int i = 0; i < adjacencyMatrix.length; i++) { - for (int j = 0; j < adjacencyMatrix.length; j++) { - if (adjacencyMatrix[i][j] != null && vertices[j].getWeight() > vertices[i].getWeight() + adjacencyMatrix[i][j].getWeight()) { - if (vertices[i].getWeight() < 0 && adjacencyMatrix[i][j].getWeight() < 0) { - vertices[j].setWeight(Double.NEGATIVE_INFINITY); - } else { - vertices[j].setWeight(vertices[i].getWeight() + adjacencyMatrix[i][j].getWeight()); - } - colorEddgesFromNode(i); - if (vertices[j].getWeight() < oldReachingCosts[j]) { - finished = true; - } - changes = true; - } - } - if (finished) { - return new GraphResponse(adjacencyMatrix, vertices); - } - - } - } - - changes = true; - resetEdgeColoring(); - for (int k = 0; k < vertices.length - 1 && changes; k++) { - changes = false; - for (int i = 0; i < adjacencyMatrix.length; i++) { - for (int j = 0; j < adjacencyMatrix.length; j++) { - if (adjacencyMatrix[i][j] != null && vertices[j].getWeight() > vertices[i].getWeight() + adjacencyMatrix[i][j].getWeight()) { - vertices[j].setWeight(Double.NEGATIVE_INFINITY); - colorEddgesFromNode(i); - if (oldReachingCosts[j] > Double.NEGATIVE_INFINITY) { - finished = true; - } - changes = true; - } - } - if (finished) { - return new GraphResponse(adjacencyMatrix, vertices); - } - } - } - - colorEddgesFromNode(vertices.length); - return new GraphResponse(adjacencyMatrix, vertices); - } - - private void colorEddgesFromNode(int nodeIndex) { - for (int i = 0; i < adjacencyMatrix.length; i++) { - for (int j = 0; j < adjacencyMatrix.length; j++) { - if (i == nodeIndex && adjacencyMatrix[i][j] != null) adjacencyMatrix[i][j].tryToVisit(); - else if (adjacencyMatrix[i][j] != null) adjacencyMatrix[i][j].tryToProcess(); - } - } - } - private int findCheapestEdge(int node) { - int index = -1; - Double cheapest = Double.POSITIVE_INFINITY; - for (int i = 0; i < adjacencyMatrix.length; i++) { - if (i != node && adjacencyMatrix[node][i] != null && adjacencyMatrix[node][i].getWeight() < cheapest && vertices[i].getWeight() == Double.POSITIVE_INFINITY) { - index = i; - cheapest = adjacencyMatrix[node][i].getWeight(); - } - } - return index; - } - - private void resetEdgeColoring() { - for (int i = 0; i < adjacencyMatrix.length; i++) { - for (int j = 0; j < adjacencyMatrix.length; j++) { - if (adjacencyMatrix[i][j] != null) { - adjacencyMatrix[i][j].setMarking(UNVISITED); - } - } - } - } - - private void colorAllVisitedEdges() { - for (com.example.algorithm.Graph.GraphEdge[] matrix : adjacencyMatrix) { - for (int j = 0; j < adjacencyMatrix.length; j++) { - if (matrix[j] != null && matrix[j].getMarking() == GraphEdge.VISITED) { - matrix[j].finish(); - } - } - } } } diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/AllShortestPath/BellmanFord/BellmanFordService.java b/algorithm/src/main/java/com/example/algorithm/Graph/AllShortestPath/BellmanFord/BellmanFordService.java index aade6d1..f2b85ce 100644 --- a/algorithm/src/main/java/com/example/algorithm/Graph/AllShortestPath/BellmanFord/BellmanFordService.java +++ b/algorithm/src/main/java/com/example/algorithm/Graph/AllShortestPath/BellmanFord/BellmanFordService.java @@ -16,7 +16,7 @@ public class BellmanFordService extends AllShortestPathService { @Override public GraphResponse step(String graphString) throws JSONException { AllShortestPathGraph graph = new AllShortestPathGraph(graphString); - return graph.bellmanFord(); + return new GraphResponse(graph); } @Override diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/AllShortestPath/Dijkstra/AllShortestPathDijkstraService.java b/algorithm/src/main/java/com/example/algorithm/Graph/AllShortestPath/Dijkstra/AllShortestPathDijkstraService.java index 1eeb122..addb6a9 100644 --- a/algorithm/src/main/java/com/example/algorithm/Graph/AllShortestPath/Dijkstra/AllShortestPathDijkstraService.java +++ b/algorithm/src/main/java/com/example/algorithm/Graph/AllShortestPath/Dijkstra/AllShortestPathDijkstraService.java @@ -16,7 +16,7 @@ public class AllShortestPathDijkstraService extends AllShortestPathService { @Override public GraphResponse step(String graphString) throws JSONException { AllShortestPathGraph graph = new AllShortestPathGraph(graphString); - return graph.dijkstra(); + return new GraphResponse(graph); } @Override diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/Graph.java b/algorithm/src/main/java/com/example/algorithm/Graph/Graph.java index d4d2776..bd2f63b 100644 --- a/algorithm/src/main/java/com/example/algorithm/Graph/Graph.java +++ b/algorithm/src/main/java/com/example/algorithm/Graph/Graph.java @@ -1,31 +1,58 @@ package com.example.algorithm.Graph; +import lombok.Getter; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.util.ArrayList; + +@Getter public class Graph { protected GraphEdge[][] adjacencyMatrix; + protected GraphNode[] vertexList; public Graph(String graphJSON) throws JSONException { JSONObject graph = new JSONObject(graphJSON); - convJSONtoAdjacencyMatrix(graph.getJSONArray("edges")); + constructFromJSONVertexList(graph.getJSONArray("vertices")); + constructFromJSONEdgeList(graph.getJSONArray("edges")); + } + + private void constructFromJSONVertexList(JSONArray jsonVertexList) throws JSONException { + vertexList = new GraphNode[jsonVertexList.length()]; + + for (int i = 0; i < jsonVertexList.length(); i++) { + JSONObject vertex = jsonVertexList.getJSONObject(i); + + vertexList[i] = new GraphNode(vertex); + } + } + + private void constructFromJSONEdgeList(JSONArray jsonEdgeList) throws JSONException { + adjacencyMatrix = new GraphEdge[vertexList.length][vertexList.length]; + + for (int i=0; i < jsonEdgeList.length(); i++) { + JSONObject edge = jsonEdgeList.getJSONObject(i); + int from = getVertexId(edge.getString("from")); + int to = getVertexId(edge.getString("to")); + String id = edge.getString("id"); + + Double weight = null; + if (edge.has("weight") && !edge.isNull("weight")) { + weight = edge.getDouble("weight"); + } + + adjacencyMatrix[from][to] = new GraphEdge(GraphEdge.UNVISITED, weight, id); + } } - private void convJSONtoAdjacencyMatrix(JSONArray adjMatrixJSON) throws JSONException { - adjacencyMatrix = new GraphEdge[adjMatrixJSON.length()][adjMatrixJSON.length()]; - for (int i = 0; i < adjMatrixJSON.length(); i++) { - for (int j = 0; j < adjMatrixJSON.getJSONArray(i).length(); j++) { - try { - if (adjMatrixJSON.getJSONArray(i).getJSONObject(j).getString("weight").equals("null")) { - adjacencyMatrix[i][j] = new GraphEdge(adjMatrixJSON.getJSONArray(i).getJSONObject(j).getInt("marking")); - } else { - adjacencyMatrix[i][j] = new GraphEdge(adjMatrixJSON.getJSONArray(i).getJSONObject(j).getInt("marking"), adjMatrixJSON.getJSONArray(i).getJSONObject(j).getDouble("weight")); - } - } catch (JSONException e) { - adjacencyMatrix[i][j] = null; - } + public int getVertexId(String name) { + for (int i = 0; i < vertexList.length; i++) { + if (vertexList[i].getValue().equals(name)) { + return i; } } + + throw new IndexOutOfBoundsException("Vertex not found"); } } diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/GraphEdge.java b/algorithm/src/main/java/com/example/algorithm/Graph/GraphEdge.java index 2d73aa0..a553f06 100644 --- a/algorithm/src/main/java/com/example/algorithm/Graph/GraphEdge.java +++ b/algorithm/src/main/java/com/example/algorithm/Graph/GraphEdge.java @@ -1,5 +1,6 @@ package com.example.algorithm.Graph; +import lombok.Getter; import lombok.Setter; public class GraphEdge { @@ -8,45 +9,27 @@ public class GraphEdge { public static final int ON_FINAL_PATH = 2; public static final int PROCESSED = 3; - @Setter + @Getter private int marking; + @Getter private Double weight; + @Getter + private String id; - public GraphEdge(int marking) { + public GraphEdge(int marking, Double weight, String id) { this.marking = marking; - this.weight = null; + this.weight = weight; } - public GraphEdge(int marking, double weight) { - this.marking = marking; - this.weight = Double.valueOf(weight); + public void visit() { + marking = VISITED; } - public boolean tryToVisit() { - if (marking != VISITED) { - marking = VISITED; - return false; - } else { - return true; - } + public void process() { + marking = PROCESSED; } - public boolean tryToProcess() { - if (marking == VISITED) { - marking = PROCESSED; - return true; - } - return false; - } public void finish() { marking = ON_FINAL_PATH; } - - public int getMarking() { - return marking; - } - - public Double getWeight() { - return weight; - } } diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/GraphNode.java b/algorithm/src/main/java/com/example/algorithm/Graph/GraphNode.java index de00235..d66e8b5 100644 --- a/algorithm/src/main/java/com/example/algorithm/Graph/GraphNode.java +++ b/algorithm/src/main/java/com/example/algorithm/Graph/GraphNode.java @@ -2,6 +2,8 @@ import lombok.Getter; import lombok.Setter; +import org.json.JSONException; +import org.json.JSONObject; public class GraphNode { @Getter @@ -12,8 +14,21 @@ public class GraphNode { @Setter String value; - public GraphNode(String value, Double weight) { - this.value = value; - this.weight = weight; + @Getter + String id; + + public GraphNode(JSONObject object) throws JSONException { + this.value = object.getString("value"); + this.id = object.getString("id"); + + if (object.has("weight") && !object.isNull("weight")) { + this.weight = object.getDouble("weight"); + } + } + + public GraphNode(GraphNode graphNode) { + this.weight = graphNode.weight; + this.value = graphNode.value; + this.id = graphNode.id; } } diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/GraphResponse.java b/algorithm/src/main/java/com/example/algorithm/Graph/GraphResponse.java index 5d106c0..2bb00b5 100644 --- a/algorithm/src/main/java/com/example/algorithm/Graph/GraphResponse.java +++ b/algorithm/src/main/java/com/example/algorithm/Graph/GraphResponse.java @@ -1,22 +1,50 @@ package com.example.algorithm.Graph; +import lombok.Getter; + +import java.util.ArrayList; + +@Getter +class EdgeListEntry { + String id; + + String from; + String to; + Double weight; + Integer marking; + + public EdgeListEntry(String from, String to, GraphEdge edge) { + this.from = from; + this.to = to; + this.weight = edge.getWeight(); + this.marking = edge.getMarking(); + this.id = edge.getId(); + } +} + public class GraphResponse { - private GraphEdge[][] edges; + @Getter + private EdgeListEntry[] edges; + @Getter private GraphNode[] vertices; - public GraphResponse(GraphEdge[][] adjacencyMatrix) { - this.edges = adjacencyMatrix; - } - public GraphResponse(GraphEdge[][] adjacencyMatrix, GraphNode[] nodes) { - this.edges = adjacencyMatrix; - this.vertices = nodes; - } + public GraphResponse(Graph graph) { + ArrayList edgeList = new ArrayList<>(); + for (int i=0; i < graph.adjacencyMatrix.length; ++i) { + for (int j=0; j < graph.adjacencyMatrix[i].length; ++j) { + if (graph.adjacencyMatrix[i][j] != null) { + String from = graph.vertexList[i].getValue(); + String to = graph.vertexList[j].getValue(); + edgeList.add(new EdgeListEntry(from, to, graph.adjacencyMatrix[i][j])); + } + } + } + this.edges = edgeList.toArray(new EdgeListEntry[0]); - public GraphEdge[][] getEdges() { - return edges; - } + vertices = new GraphNode[graph.vertexList.length]; - public GraphNode[] getVertices() { - return vertices; + for (int i=0; i < graph.vertexList.length; ++i) { + vertices[i] = new GraphNode(graph.vertexList[i]); + } } } diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/MinimalSpanningTree/JarnikPrim/JarnikPrimService.java b/algorithm/src/main/java/com/example/algorithm/Graph/MinimalSpanningTree/JarnikPrim/JarnikPrimService.java index 7e3527a..4acffbb 100644 --- a/algorithm/src/main/java/com/example/algorithm/Graph/MinimalSpanningTree/JarnikPrim/JarnikPrimService.java +++ b/algorithm/src/main/java/com/example/algorithm/Graph/MinimalSpanningTree/JarnikPrim/JarnikPrimService.java @@ -15,7 +15,7 @@ public class JarnikPrimService extends MinimalSpanningTreeService { public GraphResponse step(String graphString) throws JSONException { MinimalSpanningTreeGraph graph = new MinimalSpanningTreeGraph(graphString); - return graph.jarnikPrim(); + return new GraphResponse(graph); } @Override diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/MinimalSpanningTree/Kruskal/KruskalService.java b/algorithm/src/main/java/com/example/algorithm/Graph/MinimalSpanningTree/Kruskal/KruskalService.java index 1a99ad3..e6ea8a3 100644 --- a/algorithm/src/main/java/com/example/algorithm/Graph/MinimalSpanningTree/Kruskal/KruskalService.java +++ b/algorithm/src/main/java/com/example/algorithm/Graph/MinimalSpanningTree/Kruskal/KruskalService.java @@ -15,7 +15,7 @@ public class KruskalService extends MinimalSpanningTreeService { @Override public GraphResponse step(String graphString) throws JSONException { MinimalSpanningTreeGraph graph = new MinimalSpanningTreeGraph(graphString); - return graph.kruskal(); + return new GraphResponse(graph); } @Override diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/MinimalSpanningTree/MinimalSpanningTreeGraph.java b/algorithm/src/main/java/com/example/algorithm/Graph/MinimalSpanningTree/MinimalSpanningTreeGraph.java index 66cb137..6e1d42c 100644 --- a/algorithm/src/main/java/com/example/algorithm/Graph/MinimalSpanningTree/MinimalSpanningTreeGraph.java +++ b/algorithm/src/main/java/com/example/algorithm/Graph/MinimalSpanningTree/MinimalSpanningTreeGraph.java @@ -15,114 +15,6 @@ public class MinimalSpanningTreeGraph extends Graph { public MinimalSpanningTreeGraph(String graphJSON) throws JSONException { super(graphJSON); JSONObject graph = new JSONObject(graphJSON); - if (graph.has("start")) { - start = graph.getInt("start"); - } else { - start = 0; - } - } - - public GraphResponse jarnikPrim() { - Boolean[] connectedNodes = new Boolean[adjacencyMatrix.length]; - Arrays.fill(connectedNodes, false); - connectedNodes[start] = true; - jarnikPrimRecurisve(connectedNodes); - return new GraphResponse(adjacencyMatrix); - } - - private void jarnikPrimRecurisve(Boolean[] connectedNodes) { - Double cheapestEdge = Double.POSITIVE_INFINITY; - int cheapestEdgeIndex = -1; - int cheapestEdgeIndexFrom = -1; - for (int i = 0; i < adjacencyMatrix.length; i++) { - int cheapestNodetemp = findCheapestNotConnectedNode(connectedNodes, i); - if (connectedNodes[i] && cheapestNodetemp >= 0 && adjacencyMatrix[i][cheapestNodetemp].getWeight() < cheapestEdge) { - cheapestEdge = adjacencyMatrix[i][cheapestNodetemp].getWeight(); - cheapestEdgeIndex = cheapestNodetemp; - cheapestEdgeIndexFrom = i; - } - } - connectedNodes[cheapestEdgeIndex] = true; - - boolean isVisisted = adjacencyMatrix[cheapestEdgeIndexFrom][cheapestEdgeIndex].tryToVisit() || adjacencyMatrix[cheapestEdgeIndex][cheapestEdgeIndexFrom].tryToVisit(); - if (Arrays.stream(connectedNodes).filter((elem) -> !elem).count() == 0) { - colorAllEdges(); - return; - } - - if (isVisisted) { - jarnikPrimRecurisve(connectedNodes); - } - - - - } - - private int findCheapestNotConnectedNode(Boolean[] connectedNodes, int node) { - Double cheapestEdge = Double.POSITIVE_INFINITY; - int cheapestEdgeIndex = -1; - for (int i = 0; i < adjacencyMatrix.length; i++) { - if (!connectedNodes[i] && adjacencyMatrix[node][i] != null && adjacencyMatrix[node][i].getWeight() < cheapestEdge) { - cheapestEdge = adjacencyMatrix[node][i].getWeight(); - cheapestEdgeIndex = i; - } - } - return cheapestEdgeIndex; - } - - private void colorAllEdges() { - for (int i = 0; i < adjacencyMatrix.length; i++) { - for (int j = 0; j < adjacencyMatrix.length; j++) { - if (adjacencyMatrix[i][j] != null && adjacencyMatrix[i][j].getMarking() == GraphEdge.VISITED) { - adjacencyMatrix[i][j].finish(); - } - } - } - } - - public GraphResponse kruskal() { - PriorityQueue edges = new PriorityQueue<>((e1,e2) -> Double.compare(adjacencyMatrix[e1[0]][e1[1]].getWeight(), adjacencyMatrix[e2[0]][e2[1]].getWeight())); - - for (int i = 0; i < adjacencyMatrix.length; i++) { - for (int j = 0; j < adjacencyMatrix.length; j++) { - if (adjacencyMatrix[i][j] != null) { - Integer[] temp = {i, j}; - edges.add(temp); - } - } - } - int[] connectedComponents = new int[adjacencyMatrix.length]; - for (int i = 0; i < adjacencyMatrix.length; i++) { - connectedComponents[i] = i; - } - kruskalRecurisve(edges, connectedComponents); - return new GraphResponse(adjacencyMatrix); - } - - private void kruskalRecurisve(PriorityQueue edges, int[] connectedComponents) { - if (edges.size() == 0) { - colorAllEdges(); - return; - } - - Integer[] edge = edges.poll(); - if (connectedComponents[edge[0]] != connectedComponents[edge[1]]) { - boolean isVisisted = adjacencyMatrix[edge[0]][edge[1]].tryToVisit() || adjacencyMatrix[edge[1]][edge[0]].tryToVisit(); - mergeComponents(edge[0], edge[1], connectedComponents); - if (isVisisted) { - kruskalRecurisve(edges, connectedComponents); - } - } else { - kruskalRecurisve(edges, connectedComponents); - } - } - - private void mergeComponents(int a, int b, int[] connectedComponents) { - int valueToChange = connectedComponents[b]; - for (int i = 0; i < connectedComponents.length; i++) { - if (connectedComponents[i] == valueToChange) { - connectedComponents[i] = connectedComponents[a]; - } - } + start = graph.getInt("start"); } } diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/BreadthFirstSearch/BreadthFirstSearchService.java b/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/BreadthFirstSearch/BreadthFirstSearchService.java deleted file mode 100644 index 874222b..0000000 --- a/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/BreadthFirstSearch/BreadthFirstSearchService.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.example.algorithm.Graph.PathFinding.BreadthFirstSearch; - -import com.example.algorithm.Explanation.Explanation; -import com.example.algorithm.Graph.GraphResponse; -import com.example.algorithm.Graph.PathFinding.PathFindingGraph; -import com.example.algorithm.Graph.PathFinding.PathFindingService; -import org.json.JSONException; -import org.springframework.stereotype.Service; -import org.springframework.util.ResourceUtils; - -import java.io.IOException; -import java.nio.file.Files; - -@Service -public class BreadthFirstSearchService extends PathFindingService { - @Override - public GraphResponse step(String graphString) throws JSONException { - PathFindingGraph graph = new PathFindingGraph(graphString); - return graph.breadthFirstSearch(); - } - - @Override - public Explanation getExplanation() throws IOException { - String explanation = new String(Files.readAllBytes(ResourceUtils.getFile("classpath:explanations/bfs.txt").toPath())); - return new Explanation(explanation); - } -} diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/BreadthFirstSearchService.java b/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/BreadthFirstSearchService.java new file mode 100644 index 0000000..debb9ab --- /dev/null +++ b/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/BreadthFirstSearchService.java @@ -0,0 +1,71 @@ +package com.example.algorithm.Graph.PathFinding; + +import com.example.algorithm.Explanation.Explanation; +import com.example.algorithm.Graph.GraphResponse; +import org.json.JSONException; +import org.springframework.stereotype.Service; +import org.springframework.util.ResourceUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.*; + +@Service +public class BreadthFirstSearchService extends PathFindingService { + @Override + public GraphResponse[] execute(String graphString) throws JSONException { + PathFindingGraph graph = new PathFindingGraph(graphString); + + Deque> nextToProcess = new ArrayDeque<>(); + ArrayList startProcessingList = new ArrayList<>(); + startProcessingList.add(graph.getStart()); + nextToProcess.add(startProcessingList); + + boolean[] visited = new boolean[graph.getVertexList().length]; + ArrayList steps = new ArrayList<>(); + steps.add(new GraphResponse(graph)); + + recursiveBreathFirstSearch(steps, graph, nextToProcess, visited); + + return steps.toArray(new GraphResponse[0]); + } + + @Override + public Explanation getExplanation() throws IOException { + String explanation = new String(Files.readAllBytes(ResourceUtils.getFile("classpath:explanations/bfs.txt").toPath())); + return new Explanation(explanation); + } + + private void recursiveBreathFirstSearch(ArrayList steps, PathFindingGraph graph, Deque> nextToProcessQueue, boolean[] visited) { + if (nextToProcessQueue.isEmpty()) { + return; + } + + boolean progress = false; + + List nextToProcess = nextToProcessQueue.poll(); + int indexNextToProcess = nextToProcess.getLast(); + + for (int i=0; i < graph.getAdjacencyMatrix()[indexNextToProcess].length; ++i) { + if (!visited[i] && graph.getAdjacencyMatrix()[indexNextToProcess][i] != null) { + visited[i] = true; + ArrayList newNextToProcess = new ArrayList<>(nextToProcess); + newNextToProcess.add(i); + nextToProcessQueue.add(newNextToProcess); + + graph.getAdjacencyMatrix()[indexNextToProcess][i].visit(); + progress = true; + + if (i == graph.getEnd()) { + colorFinishedPath(steps, nextToProcess, graph); + return; + } + } + } + + if (progress) { + steps.add(new GraphResponse(graph)); + } + recursiveBreathFirstSearch(steps, graph, nextToProcessQueue, visited); + } +} diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DepthFirstSearch/DepthFirstSearchService.java b/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DepthFirstSearch/DepthFirstSearchService.java deleted file mode 100644 index 988fd8e..0000000 --- a/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DepthFirstSearch/DepthFirstSearchService.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.example.algorithm.Graph.PathFinding.DepthFirstSearch; - -import com.example.algorithm.Explanation.Explanation; -import com.example.algorithm.Graph.GraphResponse; -import com.example.algorithm.Graph.PathFinding.PathFindingGraph; -import com.example.algorithm.Graph.PathFinding.PathFindingService; -import org.json.JSONException; -import org.springframework.stereotype.Service; -import org.springframework.util.ResourceUtils; - -import java.io.IOException; -import java.nio.file.Files; - -@Service -public class DepthFirstSearchService extends PathFindingService { - @Override - public GraphResponse step(String graphString) throws JSONException { - PathFindingGraph graph = new PathFindingGraph(graphString); - return graph.depthFirstSearch(); - } - - @Override - public Explanation getExplanation() throws IOException { - String explanation = new String(Files.readAllBytes(ResourceUtils.getFile("classpath:explanations/dfs.txt").toPath())); - return new Explanation(explanation); - } -} diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DepthFirstSearchService.java b/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DepthFirstSearchService.java new file mode 100644 index 0000000..fa60f36 --- /dev/null +++ b/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DepthFirstSearchService.java @@ -0,0 +1,60 @@ +package com.example.algorithm.Graph.PathFinding; + +import com.example.algorithm.Explanation.Explanation; +import com.example.algorithm.Graph.GraphResponse; +import org.json.JSONException; +import org.springframework.stereotype.Service; +import org.springframework.util.ResourceUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.PriorityQueue; + +@Service +public class DepthFirstSearchService extends PathFindingService { + @Override + public GraphResponse[] execute(String graphString) throws JSONException { + PathFindingGraph graph = new PathFindingGraph(graphString); + + ArrayList steps = new ArrayList<>(); + boolean[] visited = new boolean[graph.getVertexList().length]; + + recursiveDepthFirstSearch(steps, graph, visited, graph.getStart()); + + return steps.toArray(new GraphResponse[0]); + } + + @Override + public Explanation getExplanation() throws IOException { + String explanation = new String(Files.readAllBytes(ResourceUtils.getFile("classpath:explanations/dfs.txt").toPath())); + return new Explanation(explanation); + } + + private boolean recursiveDepthFirstSearch(ArrayList steps, PathFindingGraph graph, boolean[] visited, int current) { + if (graph.getEnd() == current) { + return true; + } + + for (int i=0; i < graph.getAdjacencyMatrix()[current].length; i++) { + if (visited[i]) { + continue; + } else { + visited[i] = true; + } + + + graph.getAdjacencyMatrix()[current][i].visit(); + steps.add(new GraphResponse(graph)); + + boolean finished = recursiveDepthFirstSearch(steps, graph, visited, i); + if (finished) { + graph.getAdjacencyMatrix()[current][i].finish(); + steps.add(new GraphResponse(graph)); + return true; + } + } + + return false; + } +} diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DijkstraAlgorithm/DijkstraService.java b/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DijkstraAlgorithm/DijkstraService.java deleted file mode 100644 index 163496a..0000000 --- a/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DijkstraAlgorithm/DijkstraService.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.example.algorithm.Graph.PathFinding.DijkstraAlgorithm; - -import com.example.algorithm.Explanation.Explanation; -import com.example.algorithm.Graph.GraphResponse; -import com.example.algorithm.Graph.PathFinding.PathFindingGraph; -import com.example.algorithm.Graph.PathFinding.PathFindingService; -import org.json.JSONException; -import org.springframework.stereotype.Service; -import org.springframework.util.ResourceUtils; - -import java.io.IOException; -import java.nio.file.Files; - -@Service -public class DijkstraService extends PathFindingService { - @Override - public GraphResponse step(String graphString) throws JSONException { - PathFindingGraph graph = new PathFindingGraph(graphString); - return graph.dijkstraAlgorithm(); - } - - @Override - public Explanation getExplanation() throws IOException { - String explanation = new String(Files.readAllBytes(ResourceUtils.getFile("classpath:explanations/dijkstra.txt").toPath())); - return new Explanation(explanation); - } -} diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DijkstraService.java b/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DijkstraService.java new file mode 100644 index 0000000..933c29f --- /dev/null +++ b/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/DijkstraService.java @@ -0,0 +1,74 @@ +package com.example.algorithm.Graph.PathFinding; + +import com.example.algorithm.Explanation.Explanation; +import com.example.algorithm.Graph.GraphResponse; +import org.json.JSONException; +import org.springframework.stereotype.Service; +import org.springframework.util.ResourceUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +@Service +public class DijkstraService extends PathFindingService { + @Override + public GraphResponse[] execute(String graphString) throws JSONException { + PathFindingGraph graph = new PathFindingGraph(graphString); + + ArrayList steps = new ArrayList<>(); + PriorityQueue nextToProcess = new PriorityQueue<>(Comparator.comparingDouble(value -> value.cost)); + List temp = new ArrayList<>(); + temp.add(graph.getStart()); + nextToProcess.add(new DijkstraElement(0.0, temp)); + + double[] cost = new double[graph.getVertexList().length]; + dijkstra(steps, nextToProcess, graph, cost); + + return steps.toArray(new GraphResponse[0]); + } + + @Override + public Explanation getExplanation() throws IOException { + String explanation = new String(Files.readAllBytes(ResourceUtils.getFile("classpath:explanations/dijkstra.txt").toPath())); + return new Explanation(explanation); + } + + class DijkstraElement { + double cost; + List path; + + public DijkstraElement(double cost, List path) { + this.cost = cost; + this.path = path; + } + } + + private void dijkstra(ArrayList steps, PriorityQueue nextToProcess, PathFindingGraph graph, double[] cost) { + while (!nextToProcess.isEmpty()) { + DijkstraElement current = nextToProcess.poll(); + + int currentIndex = current.path.getLast(); + if (currentIndex == graph.getEnd()) { + colorFinishedPath(steps, current.path, graph); + return; + } + + for (int i=0; i < graph.getAdjacencyMatrix()[currentIndex].length; i++) { + if (graph.getAdjacencyMatrix()[currentIndex][i] != null && + cost[i] > graph.getAdjacencyMatrix()[currentIndex][i].getWeight() + current.cost) { + + double newCost = graph.getAdjacencyMatrix()[currentIndex][i].getWeight() + current.cost; + cost[i] = newCost; + graph.getAdjacencyMatrix()[currentIndex][i].visit(); + + steps.add(new GraphResponse(graph)); + + List newPath = new ArrayList<>(current.path); + nextToProcess.add(new DijkstraElement(newCost, newPath)); + } + } + } + } +} diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/PathFindingGraph.java b/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/PathFindingGraph.java index 0be4151..58e9e0f 100644 --- a/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/PathFindingGraph.java +++ b/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/PathFindingGraph.java @@ -1,171 +1,19 @@ package com.example.algorithm.Graph.PathFinding; import com.example.algorithm.Graph.Graph; -import com.example.algorithm.Graph.GraphResponse; +import lombok.Getter; import org.json.JSONException; import org.json.JSONObject; -import java.util.*; - +@Getter public class PathFindingGraph extends Graph { - private int start, end; + private final int start; + private final int end; public PathFindingGraph(String graphJSON) throws JSONException { super(graphJSON); JSONObject graph = new JSONObject(graphJSON); - start = graph.getInt("start"); - end = graph.getInt("end"); - } - - public GraphResponse breadthFirstSearch() { - Queue> todo = new ArrayDeque<>(); - ArrayDeque temp = new ArrayDeque<>(); - temp.add(start); - todo.add(temp); - Status[] status = new Status[adjacencyMatrix.length]; - Arrays.fill(status, Status.notVisited); - status[start] = Status.inQueue; - breadthFirstSearchRecursive(todo, status); - return new GraphResponse(adjacencyMatrix); - } - - private void breadthFirstSearchRecursive(Queue> todo, Status[] status) { - if (todo.isEmpty()) return; - - ArrayDeque current = todo.poll(); - for (int i = 0; i < status.length; i++) { - if (status[i] == Status.notVisited && adjacencyMatrix[current.getLast()][i] != null) { - ArrayDeque currentUpdate = current.clone(); - - if (i == end) { - finishFoundedPath(currentUpdate); - return; - } - if (adjacencyMatrix[currentUpdate.getLast()][i].tryToVisit()) { - currentUpdate.add(i); - todo.add(currentUpdate); - status[i] = Status.inQueue; - } else { - return; - } - } - } - status[current.getLast()] = Status.finished; - breadthFirstSearchRecursive(todo, status); - } - - public GraphResponse depthFirstSearch() { - Stack> todo = new Stack<>(); - ArrayDeque temp = new ArrayDeque<>(); - temp.add(start); - todo.add(temp); - Status[] status = new Status[adjacencyMatrix.length]; - Arrays.fill(status, Status.notVisited); - status[start] = Status.inQueue; - depthFirstSearchRecursive(todo, status); - return new GraphResponse(adjacencyMatrix); - } - - private boolean depthFirstSearchRecursive(Stack> todo, Status[] status) { - if (todo.isEmpty()) return true; - - ArrayDeque current = todo.pop(); - for (int i = 0; i < status.length; i++) { - ArrayDeque currentUpdated = current.clone(); - if (status[i] == Status.notVisited && adjacencyMatrix[current.getLast()][i] != null) { - - status[i] = Status.inQueue; - - if (i == end) { - finishFoundedPath(currentUpdated); - return true; - } - - if (!adjacencyMatrix[current.getLast()][i].tryToVisit()) { - return true; - } else { - currentUpdated.add(i); - Stack> todoTemp = new Stack<>(); - todoTemp.addAll(todo); - todoTemp.add(currentUpdated); - if (depthFirstSearchRecursive(todoTemp, status)) return true; - } - } - } - return false; - } - - public GraphResponse dijkstraAlgorithm() { - ArrayDeque pathToStart = new ArrayDeque<>(); - pathToStart.add(start); - ArrayDeque[] pathsAlreadyKnown = new ArrayDeque[adjacencyMatrix.length]; - Arrays.fill(pathsAlreadyKnown, null); - pathsAlreadyKnown[start] = pathToStart; - Double[] initCosts = new Double[adjacencyMatrix.length]; - Arrays.fill(initCosts, Double.POSITIVE_INFINITY); - initCosts[start] = 0.; - dijkstraAlgorithmRecursive(initCosts, pathsAlreadyKnown); - return new GraphResponse(adjacencyMatrix); - } - - private void dijkstraAlgorithmRecursive(Double[] reachingCosts, ArrayDeque[] pathToVertices) { - int indexCheapestNode = -1; - int indexVertexToCheapestNode = -1; - Double cheapestCosts = Double.POSITIVE_INFINITY; - for (int i = 0; i < adjacencyMatrix.length; i++) { - if (findCheapestEdge(i, reachingCosts) >= 0 && reachingCosts[i] + adjacencyMatrix[i][findCheapestEdge(i, reachingCosts)].getWeight() < cheapestCosts) { - cheapestCosts = reachingCosts[i] + adjacencyMatrix[i][findCheapestEdge(i, reachingCosts)].getWeight(); - indexCheapestNode = findCheapestEdge(i, reachingCosts); - indexVertexToCheapestNode = i; - } - } - - if (indexCheapestNode == -1) { - return; - } - - reachingCosts[indexCheapestNode] = reachingCosts[indexVertexToCheapestNode] + adjacencyMatrix[indexVertexToCheapestNode][indexCheapestNode].getWeight(); - - ArrayDeque currentUpdated = pathToVertices[indexVertexToCheapestNode].clone(); - currentUpdated.add(indexCheapestNode); - pathToVertices[indexCheapestNode] = currentUpdated; - - if (indexCheapestNode == end) { - colorPath(pathToVertices[end]); - return; - } - - if (adjacencyMatrix[indexVertexToCheapestNode][indexCheapestNode].tryToVisit()) { - dijkstraAlgorithmRecursive(reachingCosts, pathToVertices); - } - } - - private int findCheapestEdge(int node, Double[] reachingCosts) { - int index = -1; - Double cheapest = Double.POSITIVE_INFINITY; - for (int i = 0; i < adjacencyMatrix.length; i++) { - if (i != node && adjacencyMatrix[node][i] != null && adjacencyMatrix[node][i].getWeight() < cheapest && reachingCosts[i] == Double.POSITIVE_INFINITY) { - index = i; - cheapest = adjacencyMatrix[node][i].getWeight(); - } - } - return index; - } - - private void colorPath(ArrayDeque path) { - while (path.size() > 1) { - Integer first = path.poll(); - Integer second = path.getFirst(); - adjacencyMatrix[first][second].finish(); - } - } - - private void finishFoundedPath(ArrayDeque currentUpdate) { - currentUpdate.add(end); - colorPath(currentUpdate); - } - - enum Status { - finished, inQueue, notVisited + start = getVertexId(graph.getString("start")); + end = getVertexId(graph.getString("end")); } } diff --git a/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/PathFindingService.java b/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/PathFindingService.java index e55fdd3..d4e399f 100644 --- a/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/PathFindingService.java +++ b/algorithm/src/main/java/com/example/algorithm/Graph/PathFinding/PathFindingService.java @@ -5,8 +5,26 @@ import org.json.JSONException; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; public abstract class PathFindingService { - public abstract GraphResponse step(String graph) throws JSONException; + public abstract GraphResponse[] execute(String graph) throws JSONException; public abstract Explanation getExplanation() throws IOException; + + + protected void colorFinishedPath(ArrayList steps, List path, PathFindingGraph graph) { + if (path.size() < 2) { + return; + } + + int prevNode = path.getFirst(); + for (int i=1; i < path.size(); ++i) { + int currentNode = path.get(i); + graph.getAdjacencyMatrix()[prevNode][currentNode].finish(); + prevNode = currentNode; + } + + steps.add(new GraphResponse(graph)); + } } diff --git a/visualalgorithm/src/components/Visualisation/GraphVisualisation.jsx b/visualalgorithm/src/components/Visualisation/GraphVisualisation.jsx index 28ab96e..c935c04 100644 --- a/visualalgorithm/src/components/Visualisation/GraphVisualisation.jsx +++ b/visualalgorithm/src/components/Visualisation/GraphVisualisation.jsx @@ -9,7 +9,8 @@ const GraphVisualisation = props => { const [explanation, setExplanation] = useState(null); const [graph, graphDispatch] = useReducer(graphReducer, { vertices: [], - edges: [] + edges: [], + directed: true }); const [svgDimension, setSvgDimension] = useState({ width: 0, height: 0}); @@ -19,11 +20,22 @@ const GraphVisualisation = props => { const lastMovement = useRef(Date.now()); + function processVertices(newVertices, oldVertices) { + return newVertices.map(vertex => { + return { + ...vertex, + ...oldVertices.find(v => v.id === vertex.id) + } + }) + } + function graphReducer(graph, graphAction) { switch(graphAction.type) { case 'redraw': { - graph.vertices = graphAction.vertices; + graph.vertices = processVertices(graphAction.vertices, graph.vertices); graph.edges = graphAction.edges + console.log(graph.vertices); + console.log(graph.edges); return {vertices: graph.vertices, edges: graph.edges}; } case 'addVertex': { @@ -82,6 +94,23 @@ const GraphVisualisation = props => { } }, []); + const processMarking = (marking) => { + switch (marking) { + case 0: return { + color: "black", + stroke: "white", + } + case 1: return { + color: "blue", + stroke: "black", + } + case 2: return { + color: "red", + stroke: "black", + } + } + } + const convertVertex = (item) => { let text = item.weight !== undefined ? item.value + "|" + item.weight : item.value; return { } const convertEdge = (item) => { - if (item.from === item.to && item.from != null) { - return convertSelfEdge(item); + let colors = processMarking(item.marking); + + let edge = { + ...colors, + ...item, + } + + if (edge.from === edge.to && edge.from != null) { + return convertSelfEdge(edge); } - let vertexFrom = getVertex(item.from); - let vertexTo = getVertex(item.to); + let vertexFrom = getVertex(edge.from); + let vertexTo = getVertex(edge.to); - if (item.directed) { - return convertDirectedEdge(item, vertexFrom, vertexTo) + if (edge.directed) { + return convertDirectedEdge(edge, vertexFrom, vertexTo) } else { - return convertUndirectedEdge(item, vertexFrom, vertexTo); + return convertUndirectedEdge(edge, vertexFrom, vertexTo); } } diff --git a/visualalgorithm/src/components/control/GraphControl.jsx b/visualalgorithm/src/components/control/GraphControl.jsx index 449c82d..306d5ea 100644 --- a/visualalgorithm/src/components/control/GraphControl.jsx +++ b/visualalgorithm/src/components/control/GraphControl.jsx @@ -12,10 +12,6 @@ const GraphControl = (props) => { const START_COLOR = "Aqua"; const END_COLOR = "Chartreuse"; - const MARKED_EDGE_LINE_COLOR = "blue"; - const FINAL_EDGE_LINE_COLOR = "red"; - const PROCESSED_EDGE_LINE_COLOR = "gray"; - const DEFAULT_LINE_COLOR = "white" const DEFAULT_TEXT_COLOR = "black"; @@ -23,11 +19,14 @@ const GraphControl = (props) => { const markedNodes = useRef([]); const addVal = useRef(null); - const prev = useRef([]); + + const graphSteps = useRef([]); + const currentStep = useRef(undefined); const start = useRef(undefined); const end = useRef(undefined); + const weight = useRef(0); const [modalIsOpen, setModalIsOpen] = useState(true); @@ -103,8 +102,7 @@ const GraphControl = (props) => { from: from, to: to, stroke: DEFAULT_LINE_COLOR, - id: from + "-" + to, - directed: true + id: from + "-" + to } }) } else { @@ -141,100 +139,8 @@ const GraphControl = (props) => { } } - async function updatePreviousStack() { - let newPrevObj = { - vertices: props.graph.vertices, - edges: props.graph.edges, - start: start, - end: end - } - console.log(newPrevObj) - prev.current.push(newPrevObj); - console.log(prev.current) - } - - const getEdgeStateDependingOnColor = (edge) => { - switch (edge.stroke) { - case DEFAULT_LINE_COLOR: return 0; - case MARKED_EDGE_LINE_COLOR: return 1; - case FINAL_EDGE_LINE_COLOR: return 2; - case PROCESSED_EDGE_LINE_COLOR: return 3; - } - } - - const getAdjacencyMatrix = () => { - let n = props.graph.vertices.length; - let adj = Array.from({ length: n }, () => Array(n).fill(null)); - props.graph.edges.forEach(edge => { - const fromIndex = props.graph.vertices.findIndex(v => v.id === edge.from) - const toIndex = props.graph.vertices.findIndex(v => v.id === edge.to) - adj[fromIndex][toIndex] = { - marking: getEdgeStateDependingOnColor(edge), - weight: edge.weight - }; - }); - return adj; - } - - const getVertexList = () => { - return props.graph.vertices.map(v => { - return { - value: v.value, - weight: v.weight === "∞" ? "Infinity" : (v.weight === "-∞" ? "-Infinity" : v.weight) - } - }) - } - - const updateEdge = (from, to, updatedEdge) => { - let newEdgeColor = DEFAULT_LINE_COLOR; - - switch(updatedEdge.marking) { - case 0: newEdgeColor = DEFAULT_LINE_COLOR; - break; - case 1: newEdgeColor = MARKED_EDGE_LINE_COLOR; - break; - case 2: newEdgeColor = FINAL_EDGE_LINE_COLOR; - break; - case 3: newEdgeColor = PROCESSED_EDGE_LINE_COLOR; - - } - - const edgeId = props.graph.edges.find(e => e.from === from && e.to === to).id; - - props.graphDispatch({ - type: 'changeEdgeProperties', - updateEdge: (e => { - if (e.id === edgeId) e.stroke = newEdgeColor; - return e; - }) - }); - } - - const updateVertices = (updatedVertices) => { - props.graphDispatch({ - type: 'changeVertexProperties', - updateVertices: (vertices => { - for (let vertexId = 0; vertexId < updatedVertices.length; ++vertexId) { - if (updatedVertices[vertexId].value !== vertices[vertexId].value) { - vertices[vertexId].value = updatedVertices[vertexId].value; - } - if (updatedVertices[vertexId].weight !== vertices[vertexId].weight) { - if (updatedVertices[vertexId].weight === "Infinity") { - vertices[vertexId].weight = "∞"; - } else if (updatedVertices[vertexId].weight === "-Infinity") { - vertices[vertexId].weight = "-∞"; - } else { - vertices[vertexId].weight = updatedVertices[vertexId].weight; - } - } - } - return vertices; - }) - }); - } - const resetEdgeColor = () => { - prev.current = []; + currentStep.current = undefined; props.graphDispatch({ type: 'changeEdgeProperties', updateEdge: (e => { @@ -257,23 +163,38 @@ const GraphControl = (props) => { } const next = () => { + if (currentStep.current !== undefined) { + if (currentStep.current >= graphSteps.current.length) { + return; + } + + currentStep.current++; + console.log(graphSteps) - const adjMatrix = getAdjacencyMatrix(); - const vertexList = getVertexList(); + let nextState = graphSteps.current[currentStep.current]; + props.graphDispatch({ + type: 'redraw', + vertices: nextState.vertices, + edges: nextState.edges + }) + start.current = nextState.start; + end.current = nextState.end; + return; + } function createGraphFromJSON(response) { - if (response.edges != null) { - for (let from = 0; from < response.edges.length; from++) { - for (let to = 0; to < response.edges.length; to++) { - if (response.edges[from][to] !== adjMatrix[from][to]) { - updateEdge(vertexList[from].value, vertexList[to].value, response.edges[from][to]) - } - } - } - } - if (response.vertices != null) { - updateVertices(response.vertices) - } + graphSteps.current = response; + + currentStep.current = 1; + + let nextState = graphSteps.current[currentStep.current]; + props.graphDispatch({ + type: 'redraw', + vertices: nextState.vertices, + edges: nextState.edges + }) + start.current = nextState.start; + end.current = nextState.end; } sendRequest({ @@ -283,33 +204,29 @@ const GraphControl = (props) => { 'Content-Type': 'application/json' }, body: { - edges: adjMatrix, - start: props.graph.vertices.findIndex(v => v.value === start.current), - vertices: vertexList, - end: props.graph.vertices.findIndex(v => v.value === end.current) + edges: props.graph.edges, + start: start.current, + vertices: props.graph.vertices, + end: end.current } }, (response => { - updatePreviousStack().then(() => createGraphFromJSON(response)); + createGraphFromJSON(response); })); } const previous = () => { - if (prev.current.length < 1) return; + if (currentStep.current <= 0) return; + + currentStep.current--; - let previousState = prev.current[prev.current.length - 1]; - console.log(prev) - console.log(prev.current.length - 1) - console.log(previousState) + let previousState = graphSteps.current[currentStep.current]; props.graphDispatch({ type: 'redraw', vertices: previousState.vertices, edges: previousState.edges }) - console.log(props.graph) start.current = previousState.start; end.current = previousState.end; - - prev.current.pop(); } @@ -324,7 +241,6 @@ const GraphControl = (props) => { vertices[i].textFill = textColor; } } - console.log(vertices) return vertices; } })