ProjektGraph/graph/UndirectedGraph.java
i23007 deaffff595 Merge branch 'main' of https://gitea.hb.dhbw-stuttgart.de/i23024/ProjektGraph
# Conflicts:
#	graph/DirectedGraph.java
#	graph/Graph.java
#	graph/NameDoesNotExistException.java
#	graph/UndirectedGraph.java
#	out/production/ProjektGraph/graph/Graph.class
#	out/production/ProjektGraph/graph/WrapperComparator.class
#	out/production/ProjektGraph/graph/WrapperElement.class
2024-07-08 17:51:11 +02:00

491 lines
19 KiB
Java

package graph;
import OurApplication.OurLogElement;
import logging.LogElementList;
import visualizationElements.Edge;
import visualizationElements.EdgeStyle;
import visualizationElements.Vertex;
import java.awt.*;
import java.util.HashMap;
import java.util.Objects;
import java.util.PriorityQueue;
import java.util.Vector;
/**
* Represents an undirected graph with vertices marked by T and edges marked by U.
* Inherits from the Graph class and provides additional functionality specific to undirected graphs.
*
* @param <T> Type of marking for vertices in the graph.
* @param <U> Type of marking for edges in the graph.
*/
public class UndirectedGraph<T extends VertexMarking, U extends EdgeMarking> extends Graph<T, U> {
// ATTRIBUTE
private visualizationElements.Graph screenGraph;
private LogElementList<OurLogElement> logList;
// KONSTRUKTOREN
/**
* Constructs an empty undirected graph with default properties.
*/
public UndirectedGraph() {
super();
this.screenGraph = new visualizationElements.Graph(new Vector<Vertex>(), new Vector<Edge>(), false, EdgeStyle.Direct);
this.logList = new LogElementList<>();
}
/**
* Constructs an undirected graph with a specified name.
*
* @param s The name of the graph.
*/
public UndirectedGraph(String s) {
super(s);
this.screenGraph = new visualizationElements.Graph(new Vector<Vertex>(), new Vector<Edge>(), false, EdgeStyle.Direct);
this.logList = new LogElementList<>();
}
// GET-ER
/**
* Retrieves the visualization graph associated with this undirected graph.
*
* @return The visualization graph.
*/
public visualizationElements.Graph getScreenGraph() {
return this.screenGraph;
}
/**
* Retrieves the log list containing logging elements related to operations on this graph.
*
* @return The log element list.
*/
public LogElementList<OurLogElement> getLogList() {
return this.logList;
}
/**
* Creates and retrieves a deep copy of the visualization graph associated with this undirected graph.
*
* @return A deep copy of the visualization graph.
*/
public visualizationElements.Graph getScreenGraphCopy() {
visualizationElements.Graph graphCopy = new visualizationElements.Graph(new Vector<Vertex>(), new Vector<Edge>(), false, EdgeStyle.Direct);
Vector<visualizationElements.Vertex> copiedVertexes = new Vector<>();
Vector<visualizationElements.Edge> 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;
}
// HINZUFÜGEN
/**
* Adds an edge to the undirected graph and updates the associated visualization graph.
*
* @param e The edge to be added.
*/
public void addEdge(MarkedEdge<U> e) {
super.addEdge(e);
this.screenGraph.getEdges().add(e.getScreenEdge());
}
/**
* Adds a vertex to the undirected graph and updates the associated visualization graph.
*
* @param n The vertex to be added.
*/
public void addVertex(MarkedVertex<T> n) {
super.addVertex(n);
this.screenGraph.getVertexes().add(n.getScreenVertex());
}
// LÖSCHEN
/**
* Removes an edge from the undirected graph and updates the associated visualization graph.
*
* @param e The edge to be removed.
*/
public void removeEdge(MarkedEdge<U> e) {
super.removeEdge(e);
this.screenGraph.getEdges().remove(e.getScreenEdge());
}
/**
* Removes a vertex from the undirected graph and updates the associated visualization graph.
*
* @param n The vertex to be removed.
*/
public void removeVertex(MarkedVertex<T> n) {
super.removeVertex(n);
this.screenGraph.getVertexes().remove(n.getScreenVertex());
}
// KNOTEN EIGENSCHAFTEN
/**
* Returns the degree of a specified vertex in the undirected graph.
* The degree of a vertex is the number of edges incident to it.
*
* @param n The vertex for which to determine the degree.
* @return The degree of the vertex.
*/
public int degree(MarkedVertex<T> n) {
int degree = 0;
for (MarkedEdge<U> i: this.getAllEdges()) {
if (i.getSource() == n) {
degree += 1;
}
if (i.getDestination() == n) {
degree += 1;
}
}
return degree;
}
/**
* Returns the degree of a specified vertex by its name in the undirected graph.
*
* @param s The name of the vertex for which to determine the degree.
* @return The degree of the vertex.
* @throws NameDoesNotExistException If the vertex with the specified name does not exist in the graph.
*/
public int degree(String s) throws NameDoesNotExistException {
for (MarkedVertex<T> i: this.getAllVertexes()) {
if (Objects.equals(i.getName(), s)) {
return degree(i);
}
}
throw new NameDoesNotExistException("One of the Vertexes might not exist");
}
/**
* Returns a vector containing all neighbors (adjacent vertices) of a specified vertex in the undirected graph.
*
* @param n The vertex for which to retrieve neighbors.
* @return A vector of neighboring vertices.
*/
public Vector<MarkedVertex<T>> getNeighbours(MarkedVertex<T> n) {
Vector<MarkedVertex<T>> neighbours = new Vector<>();
for (MarkedEdge<U> i: this.getAllEdges()) {
if (i.getSource() == n && !neighbours.contains(i.getDestination())) {
neighbours.add((MarkedVertex<T>) i.getDestination());
} else if (i.getDestination() == n && !neighbours.contains(i.getSource())) {
neighbours.add((MarkedVertex<T>) i.getSource());
}
}
return neighbours;
}
/**
* Computes the shortest path between two vertices using Dijkstra's algorithm in the undirected graph.
*
* @param n1 The starting vertex of the shortest path.
* @param n2 The ending vertex of the shortest path.
* @return The length of the shortest path between n1 and n2.
*/
public int getShortestPathDijkstra(MarkedVertex<T> n1, MarkedVertex<T> 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<MarkedVertex<T>, Integer> distance = new HashMap<>();
HashMap<MarkedVertex<T>, Boolean> visited = new HashMap<>();
HashMap<MarkedVertex<T>, MarkedVertex<T>> predecessors = new HashMap<>();
for (MarkedVertex<T> 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<WrapperElement<T>> queue = new PriorityQueue<>(new WrapperComparator<T>());
// 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<T> 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<T> 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<T> i: this.getNeighbours(nextVertex.getElement())) {
// Kante finde, die den jetzigen und nächsten Knoten verbindet
for (MarkedEdge<U> 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<T> 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);
}
/**
* Computes the shortest path between two vertices using A* algorithm in the undirected graph.
*
* @param n1 The starting vertex of the shortest path.
* @param n2 The ending vertex of the shortest path.
* @return The length of the shortest path between n1 and n2.
*/
public double getShortestPathAStar(MarkedVertex<T> n1, MarkedVertex<T> 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<MarkedVertex<T>, Double> distance = new HashMap<>();
HashMap<MarkedVertex<T>, Boolean> visited = new HashMap<>();
HashMap<MarkedVertex<T>, MarkedVertex<T>> predecessors = new HashMap<>();
for (MarkedVertex<T> 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<WrapperElement<T>> queue = new PriorityQueue<>(new WrapperComparator<T>());
// 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<T> 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<T> 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<T> i: this.getNeighbours(nextVertex.getElement())) {
// Kante finde, die den jetzigen und nächsten Knoten verbindet
for (MarkedEdge<U> 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;
}
// 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<T> 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);
}
}