package graph; import OurApplication.OurLogElement; import logging.LogElementList; import visualizationElements.Edge; import visualizationElements.EdgeStyle; import visualizationElements.Vertex; import java.awt.Color; import java.util.HashMap; import java.util.Objects; import java.util.PriorityQueue; import java.util.Vector; /** * Represents a directed graph with vertex and edge markings. This class extends the Graph class and * provides additional functionality for directed graphs, including visualization and logging capabilities. * * @param the type of vertex marking * @param the type of edge marking */ public class DirectedGraph extends Graph { // ATTRIBUTE private visualizationElements.Graph screenGraph; private LogElementList logList; // KONSTRUKTOREN /** * Constructs an empty directed graph. */ public DirectedGraph() { super(); this.screenGraph = new visualizationElements.Graph(new Vector(), new Vector(), true, EdgeStyle.Direct); this.logList = new LogElementList(); } /** * Constructs a directed graph from a string representation. * * @param s the string representation of the graph */ public DirectedGraph(String s) { super(s); this.screenGraph = new visualizationElements.Graph(new Vector(), new Vector(), true, EdgeStyle.Direct); this.logList = new LogElementList(); } // GET-ER /** * Returns the screen graph associated with this directed graph. * * @return the screen graph */ public visualizationElements.Graph getScreenGraph() { return this.screenGraph; } /** * Returns a copy of the screen graph associated with this directed graph. * * @return a copy of the screen graph */ public visualizationElements.Graph getScreenGraphCopy() { visualizationElements.Graph graphCopy = new visualizationElements.Graph(new Vector(), new Vector(), true, EdgeStyle.Direct); Vector copiedVertexes = new Vector<>(); Vector copiedEdges = new Vector<>(); for (visualizationElements.Vertex vertexCopy : this.screenGraph.getVertexes()) { visualizationElements.Vertex newCopiedVertex = new visualizationElements.Vertex(vertexCopy.getXpos(), vertexCopy.getYpos(), vertexCopy.getMarking(), vertexCopy.getColor()); copiedVertexes.add(newCopiedVertex); graphCopy.setVertexes(copiedVertexes); } for (visualizationElements.Edge edgeCopy : this.screenGraph.getEdges()) { visualizationElements.Edge newCopiedEdge = new visualizationElements.Edge(edgeCopy.getSource(), edgeCopy.getDestination(), edgeCopy.getMarking(), edgeCopy.getColor()); copiedEdges.add(newCopiedEdge); graphCopy.setEdges(copiedEdges); } return graphCopy; } /** * Returns the log list associated with this directed graph. * * @return the log list */ public LogElementList getLogList() { return this.logList; } // HINZUFÜGEN /** * Adds an edge to the graph. * * @param e the edge to be added */ public void addEdge(MarkedEdge e) { super.addEdge(e); this.screenGraph.getEdges().add(e.getScreenEdge()); } /** * Adds a vertex to the graph. * * @param n the vertex to be added */ public void addVertex(MarkedVertex n) { super.addVertex(n); this.screenGraph.getVertexes().add(n.getScreenVertex()); } // LÖSCHEN /** * Removes an edge from the graph. * * @param e the edge to be removed */ public void removeEdge(MarkedEdge e) { super.removeEdge(e); this.screenGraph.getEdges().remove(e.getScreenEdge()); } /** * Removes a vertex from the graph. * * @param n the vertex to be removed */ public void removeVertex(MarkedVertex n) { super.removeVertex(n); this.screenGraph.getVertexes().remove(n.getScreenVertex()); } // KNOTEN EIGENSCHAFTEN /** * Checks if two vertices are strongly adjacent (i.e., there is a bidirectional edge between them). * * @param n1 the first vertex * @param n2 the second vertex * @return true if the vertices are strongly adjacent, false otherwise */ public boolean areStrongAdjacent(MarkedVertex n1, MarkedVertex n2) { boolean n1ton2 = false; boolean n2ton1 = false; for (MarkedEdge i: this.getAllEdges()) { if (i.getSource() == n1 && i.getDestination() == n2) { n1ton2 = true; } else if (i.getSource() == n2 && i.getDestination() == n1) { n2ton1 = true; } } return (n1ton2 && n2ton1); } /** * Checks if two vertices are strongly adjacent (i.e., there is a bidirectional edge between them) by their names. * * @param s1 the name of the first vertex * @param s2 the name of the second vertex * @return true if the vertices are strongly adjacent, false otherwise * @throws NameDoesNotExistException if one of the vertices does not exist */ public boolean areStrongAdjacent(String s1, String s2) throws NameDoesNotExistException { MarkedVertex n1 = null; MarkedVertex n2 = null; for (MarkedVertex i: this.getAllVertexes()) { if (Objects.equals(i.getName(), s1)) { n1 = i; } else if (Objects.equals(i.getName(), s2)) { n2 = i; } } if (n1 == null || n2 == null) { throw new NameDoesNotExistException("One of the Vertexes might not exist"); } else { return areStrongAdjacent(n1, n2); } } /** * Returns the in-degree of a vertex (i.e., the number of edges directed towards the vertex). * * @param n the vertex * @return the in-degree of the vertex */ public int inDegree(MarkedVertex n) { int degree = 0; for (MarkedEdge i: this.getAllEdges()) { if (i.getDestination() == n) { degree += 1; } } return degree; } /** * Returns the in-degree of a vertex by its name. * * @param s the name of the vertex * @return the in-degree of the vertex * @throws NameDoesNotExistException if the vertex does not exist */ public int inDegree(String s) throws NameDoesNotExistException{ for (MarkedVertex i: this.getAllVertexes()) { if (Objects.equals(i.getName(), s)) { return inDegree(i); } } throw new NameDoesNotExistException("One of the Vertexes might not exist"); } /** * Returns the out-degree of a vertex (i.e., the number of edges directed away from the vertex). * * @param n the vertex * @return the out-degree of the vertex */ public int outDegree(MarkedVertex n) { int degree = 0; for (MarkedEdge i: this.getAllEdges()) { if (i.getSource() == n) { degree += 1; } } return degree; } /** * Returns the out-degree of a vertex by its name. * * @param s the name of the vertex * @return the out-degree of the vertex * @throws NameDoesNotExistException if the vertex does not exist */ public int outDegree(String s) throws NameDoesNotExistException{ for (MarkedVertex i: this.getAllVertexes()) { if (Objects.equals(i.getName(), s)) { return outDegree(i); } } throw new NameDoesNotExistException("One of the Vertexes might not exist"); } /** * Returns a vector of predecessor vertices (i.e., vertices with edges directed towards the specified vertex). * * @param n the vertex * @return a vector of predecessor vertices */ public Vector> getPredecessors(MarkedVertex n) { Vector> predecessors = new Vector<>(); for (MarkedEdge i: this.getAllEdges()) { if (i.getDestination() == n) { predecessors.add((MarkedVertex) i.getSource()); } } return predecessors; } /** * Returns a vector of successor vertices (i.e., vertices with edges directed away from the specified vertex). * * @param n the vertex * @return a vector of successor vertices */ public Vector> getSuccessors(MarkedVertex n) { Vector> successors = new Vector<>(); for (MarkedEdge i: this.getAllEdges()) { if (i.getSource() == n) { successors.add((MarkedVertex) i.getDestination()); } } return successors; } // AUFGABE 2 /** * Finds the shortest path between two vertices using Dijkstra's algorithm. * * @param n1 the starting vertex * @param n2 the ending vertex * @return the shortest distance from n1 to n2, or -1 if no path is found */ public int getShortestPathDijkstra(MarkedVertex n1, MarkedVertex n2) { // Erstellt Hashmap um Distanz von Startnoten zu jedem Knoten auf dem Graph zu tracken // Erstellt Hashmap um zu tracken welche Knoten schon besucht wurden // Erstelle Hashmap um Vorgängerknoten zu tracken // Initialisierung aller Distanzen auf UNENDLICH (= -1) // Initialisierung, dass kein Knoten besucht wurde // Initialisierung aller Vorgänger auf null HashMap, Integer> distance = new HashMap<>(); HashMap, Boolean> visited = new HashMap<>(); HashMap, MarkedVertex> predecessors = new HashMap<>(); for (MarkedVertex i: this.getAllVertexes()) { distance.put(i, -1); visited.put(i, false); predecessors.put(i, null); } // Erstelle Schlange wo die nächsten Verbindungen drin sind PriorityQueue> queue = new PriorityQueue<>(new WrapperComparator()); // Distanz zu Startknoten auf 0 // Weg zu Startknoten in die Schlange aufnehmen distance.put(n1, 0); queue.add(new WrapperElement<>(n1, 0)); // Variable, die Distanz zwischen aktuellem Knoten und Nachfolger speichert int dist = 0; // Zähler für LogList int step = 0; // String für den Description Inhalt String textDescription; // Färben der Start und Ziel Knoten für Visualisierung + hinzufügen zur LogList n1.getScreenVertex().setColor(Color.RED); n2.getScreenVertex().setColor(Color.RED); textDescription = "Startknoten: " + n1.getScreenVertex().getMarking() + ", Endknoten: " + n2.getScreenVertex().getMarking(); this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy())); while (!queue.isEmpty()) { // Den nächsten Knoten, der am wenigsten kostet, besuchen WrapperElement nextVertex = queue.poll(); // Falls Knoten schon besucht if(!visited.get(nextVertex.getElement())){ // Knoten als besucht makieren visited.put(nextVertex.getElement(), true); textDescription = "Visit " + nextVertex.getElement().getName(); // Logging System.out.println(textDescription); if (nextVertex.getElement().getScreenVertex().getColor() != Color.RED) { nextVertex.getElement().getScreenVertex().setColor(Color.BLUE); } this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy())); } // Wenn Weg gefunden, brich ab if (nextVertex.getElement() == n2) { MarkedVertex colorroute = n2; while (colorroute != null) { textDescription = colorroute.getName(); System.out.println(textDescription); colorroute.getScreenVertex().setColor(Color.green); this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy())); colorroute = predecessors.get(colorroute); } //zurücksetzten der Färbungen this.clearScreenGraphColor(); return distance.get(n2); } // Gehe von diesem Knoten aus alle erreichbaren Knoten durch for (MarkedVertex i: this.getSuccessors(nextVertex.getElement())) { // Kante finde, die den jetzigen und nächsten Knoten verbindet for (MarkedEdge j: this.getAllEdges()) { if (j.getSource() == nextVertex.getElement() && j.getDestination() == i) { // Berechne Distanz zu nächstem Knoten EdgeWeightMarking marking = (EdgeWeightMarking) j.getMarking(); dist = distance.get(nextVertex.getElement()) + marking.getWeight(); break; } } // Wenn es schon einen kürzeren Weg zum Knoten gibt, überspringen if ((distance.get(i) <= dist && distance.get(i) != -1) || visited.get(i)) { continue; } // Vorgänger aktualisieren predecessors.put(i, nextVertex.getElement()); // Aktualisiere Distanz von Start zu nächstem Knoten distance.put(i, dist); // Logging textDescription = "Add " + i.getName() + " with " + dist + " weight to queue."; System.out.println(textDescription); if (i.getScreenVertex().getColor() != Color.RED) { i.getScreenVertex().setColor(Color.YELLOW); } this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy())); // Nehme nächsten Knoten in die Queue auf queue.add(new WrapperElement<>(i, dist)); } } //zurücksetzten der Färbungen this.clearScreenGraphColor(); MarkedVertex colorroute = n2; while (colorroute != null) { textDescription = colorroute.getName(); System.out.println(textDescription); colorroute.getScreenVertex().setColor(Color.green); this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy())); colorroute = predecessors.get(colorroute); } System.out.println("Done"); // Gibt Distanz zu gefragtem Knoten zurück return distance.get(n2); } /** * Finds the shortest path between two vertices using the A* algorithm. * * @param n1 the starting vertex * @param n2 the ending vertex * @return the shortest distance from n1 to n2, or -1 if no path is found */ public double getShortestPathAStar(MarkedVertex n1, MarkedVertex n2) { // Erstellt Hashmap um Distanz von Startnoten zu jedem Knoten auf dem Graph zu tracken // Erstellt Hashmap um zu tracken welche Knoten schon besucht wurden // Erstelle Hashmap um Vorgängerknoten zu tracken // Initialisierung aller Distanzen auf UNENDLICH (= -1) // Initialisierung, dass kein Knoten besucht wurde // Initialisierung aller Vorgänger auf null HashMap, Double> distance = new HashMap<>(); HashMap, Boolean> visited = new HashMap<>(); HashMap, MarkedVertex> predecessors = new HashMap<>(); for (MarkedVertex i: this.getAllVertexes()) { distance.put(i, -1.0); visited.put(i, false); predecessors.put(i, null); } // Erstelle Schlange wo die nächsten Verbindungen drin sind PriorityQueue> queue = new PriorityQueue<>(new WrapperComparator()); // Distanz zu Startknoten auf 0 // Weg zu Startknoten in die Schlange aufnehmen distance.put(n1, 0.0); queue.add(new WrapperElement<>(n1, 0)); // Variable, die Distanz zwischen aktuellem Knoten und Nachfolger speichert double dist = 0; // Variable, die Distanz zwischen dem potenziell nächsten Knoten und dem Zielknoten speichert double airDist = 0; // Variable, die Distanz zwischen dem aktuellen Knoten bis zum Endknoten speichert double distToFinish = 0; // Zähler für LogList int step = 0; // String für den Description Inhalt String textDescription; // Färben der Start und Ziel Knoten für Visualisierung + hinzufügen zur LogList n1.getScreenVertex().setColor(Color.RED); n2.getScreenVertex().setColor(Color.RED); textDescription = "Startknoten: " + n1.getScreenVertex().getMarking() + ", Endknoten: " + n2.getScreenVertex().getMarking(); this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy())); while (!queue.isEmpty()) { // Den nächsten Knoten, der am wenigsten kostet, besuchen WrapperElement nextVertex = queue.poll(); // Falls Knoten schon besucht if(!visited.get(nextVertex.getElement())){ // Knoten als besucht makieren visited.put(nextVertex.getElement(), true); textDescription = "Visit " + nextVertex.getElement().getName(); // Logging System.out.println(textDescription); if (nextVertex.getElement().getScreenVertex().getColor() != Color.RED) { nextVertex.getElement().getScreenVertex().setColor(Color.BLUE); } this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy())); } // Wenn Weg gefunden, brich ab if (nextVertex.getElement() == n2) { MarkedVertex colorroute = n2; while (colorroute != null) { textDescription = colorroute.getName(); System.out.println(textDescription); colorroute.getScreenVertex().setColor(Color.green); this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy())); colorroute = predecessors.get(colorroute); } //zurücksetzten der Färbungen this.clearScreenGraphColor(); return distance.get(n2); } // Gehe von diesem Knoten aus alle erreichbaren Knoten durch for (MarkedVertex i: this.getSuccessors(nextVertex.getElement())) { // Kante finde, die den jetzigen und nächsten Knoten verbindet for (MarkedEdge j: this.getAllEdges()) { if (j.getSource() == nextVertex.getElement() && j.getDestination() == i) { //Berechnung der Heuristik über die Luftdistanz des nächsten Knoten zum Zielknoten airDist = Math.sqrt(Math.pow((i.getCords()[0] - n2.getCords()[0]), 2) + Math.pow((i.getCords()[1] - n2.getCords()[1]), 2)) / 100; // Berechne Distanz zu nächstem Knoten EdgeWeightMarking marking = (EdgeWeightMarking) j.getMarking(); dist = distance.get(nextVertex.getElement()) + marking.getWeight(); distToFinish = distance.get(nextVertex.getElement()) + marking.getWeight() + airDist; break; } } // Wenn es schon einen kürzeren Weg zum Knoten gibt, überspringen if ((distance.get(i) <= dist && distance.get(i) != -1) || visited.get(i)) { continue; } // Vorgänger aktualisieren predecessors.put(i, nextVertex.getElement()); // Aktualisiere Distanz von Start zu nächstem Knoten distance.put(i, dist); // Logging textDescription = "Add " + i.getName() + " with " + distToFinish + " weight to queue."; System.out.println(textDescription); if (i.getScreenVertex().getColor() != Color.RED) { i.getScreenVertex().setColor(Color.YELLOW); } this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy())); // Nehme nächsten Knoten in die Queue auf queue.add(new WrapperElement<>(i, distToFinish)); } } //zurücksetzten der Färbungen this.clearScreenGraphColor(); MarkedVertex colorroute = n2; while (colorroute != null) { textDescription = colorroute.getName(); System.out.println(textDescription); colorroute.getScreenVertex().setColor(Color.green); this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy())); colorroute = predecessors.get(colorroute); } System.out.println("Done"); // Gibt Distanz zu gefragtem Knoten zurück return distance.get(n2); } public void createLoglistElement() { this.logList.add(new OurLogElement(0, "", 0, this.getScreenGraphCopy())); } }