Graph View

* added simple Window for graph play
* added new function to interface
* all tests passes
* end graph
This commit is contained in:
2019-02-26 21:58:53 +01:00
parent b441871de0
commit cdc183754f
23 changed files with 2203 additions and 1053 deletions

BIN
.gitignore vendored

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -1,488 +1,500 @@
package berack96.sim.util.graph; package berack96.sim.util.graph;
import berack96.sim.util.graph.visit.Dijkstra; import berack96.sim.util.graph.visit.Dijkstra;
import berack96.sim.util.graph.visit.Tarjan; import berack96.sim.util.graph.visit.Tarjan;
import berack96.sim.util.graph.visit.VisitInfo; import berack96.sim.util.graph.visit.VisitInfo;
import berack96.sim.util.graph.visit.VisitStrategy; import berack96.sim.util.graph.visit.VisitStrategy;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
* Graph that uses HashMap for vertices and edges<br> * Graph that uses HashMap for vertices and edges<br>
* More specifically it utilizes a Map containing all the vertices mapped to all their edges<br> * More specifically it utilizes a Map containing all the vertices mapped to all their edges<br>
* Technically this version of the graph combine the fast adding/removing of the edges of the Matrix implementation, * Technically this version of the graph combine the fast adding/removing of the edges of the Matrix implementation,
* with the low memory and fast adding/removing of vertices of the Linked List implementation.<br> * with the low memory and fast adding/removing of vertices of the Linked List implementation.<br>
* This happen if the HashMap is not reallocated. So in the end each operation of adding or removing has O(n) * This happen if the HashMap is not reallocated. So in the end each operation of adding or removing has O(n)
* *
* @param <V> the vertices * @param <V> the vertices
* @param <W> the weight of the edges * @param <W> the weight of the edges
* @author Berack96 * @author Berack96
*/ */
public class MapGraph<V, W extends Number> implements Graph<V, W> { public class MapGraph<V, W extends Number> implements Graph<V, W> {
/** /**
* Map that contains the edges from a vertex to another<br> * Map that contains the edges from a vertex to another<br>
* The first vertex is the vertex where start the edge, the second one is where the edge goes<br> * The first vertex is the vertex where start the edge, the second one is where the edge goes<br>
* If an edge exist, then it's weight is returned * If an edge exist, then it's weight is returned
*/ */
private final Map<V, Map<V, W>> edges = new HashMap<>(); private final Map<V, Map<V, W>> edges = new HashMap<>();
/** /**
* Map that contains the vertex as key and all the marker as the value associated with that vertex. * Map that contains the vertex as key and all the marker as the value associated with that vertex.
*/ */
private final Map<V, Set<Object>> marked = new HashMap<>(); private final Map<V, Set<Object>> marked = new HashMap<>();
/** /**
* Need this variable for not calculating each time the SCC or the cyclic part if the graph doesn't change * Need this variable for not calculating each time the SCC or the cyclic part if the graph doesn't change
*/ */
private Tarjan<V, W> tarjan = null; private Tarjan<V, W> tarjan = null;
/** /**
* Need this variable for not calculating each time the distance from a vertex to all his destinations if the graph doesn't change * Need this variable for not calculating each time the distance from a vertex to all his destinations if the graph doesn't change
*/ */
private Map<V, Dijkstra<V, W>> dijkstra = null; private Map<V, Dijkstra<V, W>> dijkstra = null;
@Override @Override
public boolean isCyclic() { public boolean isCyclic() {
return stronglyConnectedComponents().size() != numberOfVertices(); return stronglyConnectedComponents().size() != numberOfVertices();
} }
@Override @Override
public boolean isDAG() { public boolean isDAG() {
return !isCyclic(); return !isCyclic();
} }
@Override @Override
public Vertex<V> getVertex(V vertex) throws NullPointerException, IllegalArgumentException { public Vertex<V> getVertex(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex); checkNullAndExist(vertex);
return new Vertex<>(this, vertex); return new Vertex<>(this, vertex);
} }
@Override @Override
public void addVertex(V vertex) throws NullPointerException { public void addVertex(V vertex) throws NullPointerException {
checkNull(vertex); checkNull(vertex);
graphChanged(); graphChanged();
edges.put(vertex, new HashMap<>()); edges.put(vertex, new HashMap<>());
} }
@Override @Override
public boolean addVertexIfAbsent(V vertex) throws NullPointerException { public boolean addVertexIfAbsent(V vertex) throws NullPointerException {
if (contains(vertex)) if (contains(vertex))
return false; return false;
addVertex(vertex); addVertex(vertex);
return true; return true;
} }
@Override @Override
public void addAllVertices(Collection<V> vertices) throws NullPointerException { public void addAllVertices(Collection<V> vertices) throws NullPointerException {
checkNull(vertices); checkNull(vertices);
vertices.forEach(this::addVertexIfAbsent); vertices.forEach(this::addVertexIfAbsent);
} }
@Override @Override
public void removeVertex(V vertex) throws NullPointerException { public void removeVertex(V vertex) throws NullPointerException {
if (contains(vertex)) { if (contains(vertex)) {
graphChanged(); graphChanged();
edges.remove(vertex); edges.remove(vertex);
edges.forEach((v, map) -> map.remove(vertex)); edges.forEach((v, map) -> map.remove(vertex));
} }
} }
@Override @Override
public void removeAllVertex() { public void removeAllVertex() {
graphChanged(); graphChanged();
edges.clear(); edges.clear();
} }
@Override @Override
public boolean contains(V vertex) throws NullPointerException { public boolean contains(V vertex) throws NullPointerException {
checkNull(vertex); checkNull(vertex);
return edges.containsKey(vertex); return edges.containsKey(vertex);
} }
@Override @Override
public void mark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException { public void mark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex); checkNullAndExist(vertex);
checkNull(mark); checkNull(mark);
Set<Object> set = marked.computeIfAbsent(vertex, (m) -> new HashSet<>()); Set<Object> set = marked.computeIfAbsent(vertex, (m) -> new HashSet<>());
set.add(mark); set.add(mark);
} }
@Override @Override
public void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException { public void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex); checkNullAndExist(vertex);
checkNull(mark); checkNull(mark);
marked.get(vertex).remove(mark); marked.get(vertex).remove(mark);
} }
@Override @Override
public void unMark(V vertex) throws NullPointerException, IllegalArgumentException { public void unMark(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex); checkNullAndExist(vertex);
marked.get(vertex).clear(); marked.get(vertex).clear();
} }
@Override @Override
public Set<Object> getMarks(V vertex) throws NullPointerException, IllegalArgumentException { public Collection<V> getMarkedWith(Object mark) throws NullPointerException {
checkNullAndExist(vertex); checkNull(mark);
return marked.computeIfAbsent(vertex, (m) -> new HashSet<>()); Collection<V> ret = new HashSet<V>();
} marked.forEach((v, set) -> {
if(set.contains(mark))
@Override ret.add(v);
public void unMarkAll(Object mark) { });
checkNull(mark);
marked.forEach((v, m) -> m.remove(mark)); return ret;
} }
@Override @Override
public void unMarkAll() { public Set<Object> getMarks(V vertex) throws NullPointerException, IllegalArgumentException {
marked.clear(); checkNullAndExist(vertex);
} return marked.computeIfAbsent(vertex, (m) -> new HashSet<>());
}
@Override
public W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException { @Override
checkNullAndExist(vertex1); public void unMarkAll(Object mark) {
checkNullAndExist(vertex2); checkNull(mark);
checkNull(weight); marked.forEach((v, m) -> m.remove(mark));
}
graphChanged();
return edges.get(vertex1).put(vertex2, weight); @Override
} public void unMarkAll() {
marked.clear();
@Override }
public W addEdge(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException {
return addEdge(edge.getSource(), edge.getDestination(), edge.getWeight()); @Override
} public W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex1);
@Override checkNullAndExist(vertex2);
public W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException { checkNull(weight);
addVertexIfAbsent(vertex1);
addVertexIfAbsent(vertex2); graphChanged();
return addEdge(vertex1, vertex2, weight); return edges.get(vertex1).put(vertex2, weight);
} }
@Override @Override
public W addEdgeAndVertices(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException { public W addEdge(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException {
return addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight()); return addEdge(edge.getSource(), edge.getDestination(), edge.getWeight());
} }
@Override @Override
public void addAllEdges(Collection<Edge<V, W>> edges) throws NullPointerException { public W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException {
edges.forEach((edge) -> addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight())); addVertexIfAbsent(vertex1);
} addVertexIfAbsent(vertex2);
return addEdge(vertex1, vertex2, weight);
@Override }
public W getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex1); @Override
checkNullAndExist(vertex2); public W addEdgeAndVertices(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException {
return addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight());
return edges.get(vertex1).get(vertex2); }
}
@Override
@Override public void addAllEdges(Collection<Edge<V, W>> edges) throws NullPointerException {
public void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException { edges.forEach((edge) -> addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight()));
checkNullAndExist(vertex1); }
checkNullAndExist(vertex2);
@Override
graphChanged(); public W getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
edges.get(vertex1).remove(vertex2); checkNullAndExist(vertex1);
} checkNullAndExist(vertex2);
@Override return edges.get(vertex1).get(vertex2);
public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException { }
checkNullAndExist(vertex);
@Override
graphChanged(); public void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
edges.forEach((v, map) -> map.remove(vertex)); checkNullAndExist(vertex1);
} checkNullAndExist(vertex2);
@Override graphChanged();
public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException { edges.get(vertex1).remove(vertex2);
checkNullAndExist(vertex); }
graphChanged(); @Override
edges.put(vertex, new HashMap<>()); public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException {
} checkNullAndExist(vertex);
@Override graphChanged();
public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException { edges.forEach((v, map) -> map.remove(vertex));
checkNullAndExist(vertex); }
removeVertex(vertex);
addVertex(vertex); @Override
} public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
@Override
public void removeAllEdge() { graphChanged();
graphChanged(); edges.put(vertex, new HashMap<>());
edges.forEach((v, map) -> map.clear()); }
}
@Override
@Override public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException {
public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException { checkNullAndExist(vertex);
return (contains(vertex1) && contains(vertex2)) && edges.get(vertex1).get(vertex2) != null; removeVertex(vertex);
} addVertex(vertex);
}
@Override
public Set<V> vertices() { @Override
return new HashSet<>(edges.keySet()); public void removeAllEdge() {
} graphChanged();
edges.forEach((v, map) -> map.clear());
@Override }
public Set<Edge<V, W>> edges() {
Set<Edge<V, W>> allEdges = new HashSet<>(); @Override
edges.forEach((source, map) -> map.forEach((destination, weight) -> allEdges.add(new Edge<>(source, destination, weight)))); public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException {
return allEdges; return (contains(vertex1) && contains(vertex2)) && edges.get(vertex1).get(vertex2) != null;
} }
@Override @Override
public Set<Edge<V, W>> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException { public Set<V> vertices() {
checkNullAndExist(vertex); return new HashSet<>(edges.keySet());
}
Set<Edge<V, W>> set = new HashSet<>();
edges.forEach((source, map) -> map.forEach((destination, weight) -> { @Override
if (destination.equals(vertex) || source.equals(vertex)) public Set<Edge<V, W>> edges() {
set.add(new Edge<>(source, destination, weight)); Set<Edge<V, W>> allEdges = new HashSet<>();
})); edges.forEach((source, map) -> map.forEach((destination, weight) -> allEdges.add(new Edge<>(source, destination, weight))));
return set; return allEdges;
} }
@Override @Override
public Collection<Edge<V, W>> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException { public Set<Edge<V, W>> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex); checkNullAndExist(vertex);
Collection<Edge<V, W>> collection = new HashSet<>();
edges.forEach((source, edge) -> { Set<Edge<V, W>> set = new HashSet<>();
if (edge.get(vertex) != null) edges.forEach((source, map) -> map.forEach((destination, weight) -> {
collection.add(new Edge<>(source, vertex, edge.get(vertex))); if (destination.equals(vertex) || source.equals(vertex))
}); set.add(new Edge<>(source, destination, weight));
}));
return collection; return set;
} }
@Override @Override
public Collection<Edge<V, W>> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException { public Collection<Edge<V, W>> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex); checkNullAndExist(vertex);
Collection<Edge<V, W>> collection = new HashSet<>(); Collection<Edge<V, W>> collection = new HashSet<>();
edges.get(vertex).forEach((dest, weight) -> collection.add(new Edge<>(vertex, dest, weight))); edges.forEach((source, edge) -> {
if (edge.get(vertex) != null)
return collection; collection.add(new Edge<>(source, vertex, edge.get(vertex)));
} });
@Override return collection;
public Set<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException { }
checkNullAndExist(vertex);
@Override
return new HashSet<>(edges.get(vertex).keySet()); public Collection<Edge<V, W>> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException {
} checkNullAndExist(vertex);
Collection<Edge<V, W>> collection = new HashSet<>();
@Override edges.get(vertex).forEach((dest, weight) -> collection.add(new Edge<>(vertex, dest, weight)));
public Set<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex); return collection;
}
Set<V> set = new HashSet<>();
edges.forEach((v, map) -> { @Override
if (map.containsKey(vertex)) set.add(v); public Set<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException {
}); checkNullAndExist(vertex);
return set;
} return new HashSet<>(edges.get(vertex).keySet());
}
@Override
public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException { @Override
checkNullAndExist(vertex); public Set<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
AtomicInteger sum = new AtomicInteger();
edges.forEach((v, map) -> { Set<V> set = new HashSet<>();
if (map.containsKey(vertex)) edges.forEach((v, map) -> {
sum.getAndIncrement(); if (map.containsKey(vertex)) set.add(v);
}); });
return set;
return sum.get(); }
}
@Override
@Override public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException {
public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException { checkNullAndExist(vertex);
checkNullAndExist(vertex);
AtomicInteger sum = new AtomicInteger();
return edges.get(vertex).size(); edges.forEach((v, map) -> {
} if (map.containsKey(vertex))
sum.getAndIncrement();
@Override });
public int degree(V vertex) throws NullPointerException, IllegalArgumentException {
return degreeIn(vertex) + degreeOut(vertex); return sum.get();
} }
@Override @Override
public int numberOfVertices() { public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException {
return edges.size(); checkNullAndExist(vertex);
}
return edges.get(vertex).size();
@Override }
public int numberOfEdges() {
AtomicInteger sum = new AtomicInteger(0); @Override
edges.forEach((v, map) -> sum.getAndAdd(map.size())); public int degree(V vertex) throws NullPointerException, IllegalArgumentException {
return degreeIn(vertex) + degreeOut(vertex);
return sum.get(); }
}
@Override
@Override public int numberOfVertices() {
public VisitInfo<V> visit(V source, VisitStrategy<V, W> strategy, Consumer<V> visit) throws NullPointerException, IllegalArgumentException { return edges.size();
return strategy.visit(this, source, visit); }
}
@Override
@Override public int numberOfEdges() {
public Graph<V, W> transpose() { AtomicInteger sum = new AtomicInteger(0);
Graph<V, W> graph = new MapGraph<>(); edges.forEach((v, map) -> sum.getAndAdd(map.size()));
for (V vertex : edges.keySet())
graph.addVertex(vertex); return sum.get();
}
edges.forEach((source, map) -> map.forEach((destination, weight) -> graph.addEdge(destination, source, weight)));
@Override
return graph; public VisitInfo<V> visit(V source, VisitStrategy<V, W> strategy, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
} return strategy.visit(this, source, visit);
}
@Override
public List<V> topologicalSort() throws UnsupportedOperationException { @Override
if (!isDAG()) public Graph<V, W> transpose() {
throw new UnsupportedOperationException(NOT_DAG); Graph<V, W> graph = new MapGraph<>();
return getTarjan().getTopologicalSort(); for (V vertex : edges.keySet())
} graph.addVertex(vertex);
@Override edges.forEach((source, map) -> map.forEach((destination, weight) -> graph.addEdge(destination, source, weight)));
public Collection<Collection<V>> stronglyConnectedComponents() {
return getTarjan().getSCC(); return graph;
} }
@Override @Override
public Graph<V, W> subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException { public List<V> topologicalSort() throws UnsupportedOperationException {
Graph<V, W> sub = new MapGraph<>(); if (!isDAG())
Set<V> vertices = new HashSet<>(); throw new UnsupportedOperationException(NOT_DAG);
return getTarjan().getTopologicalSort();
int finalDepth = depth > 0 ? depth : 0; }
VisitStrategy<V, W> strategy = (graph, sourceVertex, visit) -> {
int currentDepth = 0; @Override
final LinkedList<Map.Entry<V, Integer>> toVisitChildren = new LinkedList<>(); public Collection<Collection<V>> stronglyConnectedComponents() {
toVisitChildren.add(new AbstractMap.SimpleEntry<>(sourceVertex, 0)); return getTarjan().getSCC();
vertices.add(source); }
while (!toVisitChildren.isEmpty() && currentDepth + 1 <= finalDepth) { @Override
final Map.Entry<V, Integer> current = toVisitChildren.removeFirst(); public Graph<V, W> subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException {
currentDepth = current.getValue() + 1; Graph<V, W> sub = new MapGraph<>();
final int finalCurrentDepth = currentDepth; Set<V> vertices = new HashSet<>();
for (V child : graph.getChildren(current.getKey())) int finalDepth = depth > 0 ? depth : 0;
if (!vertices.contains(child)) { VisitStrategy<V, W> strategy = (graph, sourceVertex, visit) -> {
toVisitChildren.addLast(new AbstractMap.SimpleEntry<>(child, finalCurrentDepth)); int currentDepth = 0;
vertices.add(child); final LinkedList<Map.Entry<V, Integer>> toVisitChildren = new LinkedList<>();
} toVisitChildren.add(new AbstractMap.SimpleEntry<>(sourceVertex, 0));
} vertices.add(source);
return null;
}; while (!toVisitChildren.isEmpty() && currentDepth + 1 <= finalDepth) {
final Map.Entry<V, Integer> current = toVisitChildren.removeFirst();
strategy.visit(this, source, null); currentDepth = current.getValue() + 1;
final int finalCurrentDepth = currentDepth;
sub.addAllVertices(vertices);
for (V vertex : vertices) for (V child : graph.getChildren(current.getKey()))
getEdgesOut(vertex).forEach((edge) -> { if (!vertices.contains(child)) {
try { toVisitChildren.addLast(new AbstractMap.SimpleEntry<>(child, finalCurrentDepth));
sub.addEdge(edge); vertices.add(child);
} catch (Exception ignored) { }
} }
}); return null;
};
return sub;
} strategy.visit(this, source, null);
@Override sub.addAllVertices(vertices);
public Graph<V, W> subGraph(final Object marker) { for (V vertex : vertices)
final Graph<V, W> graph = new MapGraph<>(); getEdgesOut(vertex).forEach((edge) -> {
final Set<V> allVertices = new HashSet<>(); try {
sub.addEdge(edge);
marked.forEach((vertex, mark) -> { } catch (Exception ignored) {
if (mark.contains(marker) || (marker == null && !mark.isEmpty())) }
allVertices.add(vertex); });
});
return sub;
if (marker == null) { }
Collection<V> toAdd = graph.vertices();
toAdd.removeAll(allVertices); @Override
allVertices.clear(); public Graph<V, W> subGraph(final Object marker) {
allVertices.addAll(toAdd); final Graph<V, W> graph = new MapGraph<>();
} final Set<V> allVertices = new HashSet<>();
graph.addAllVertices(allVertices); marked.forEach((vertex, mark) -> {
for (V vertex : graph.vertices()) if (mark.contains(marker) || (marker == null && !mark.isEmpty()))
getEdgesOut(vertex).forEach((edge) -> { allVertices.add(vertex);
try { });
graph.addEdge(edge);
} catch (Exception ignored) { if (marker == null) {
} Collection<V> toAdd = graph.vertices();
}); toAdd.removeAll(allVertices);
allVertices.clear();
return graph; allVertices.addAll(toAdd);
} }
@Override graph.addAllVertices(allVertices);
public List<Edge<V, W>> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException { for (V vertex : graph.vertices())
checkNullAndExist(source); getEdgesOut(vertex).forEach((edge) -> {
checkNullAndExist(destination); try {
graph.addEdge(edge);
Dijkstra<V, W> dijkstra = getDijkstra(source); } catch (Exception ignored) {
List<Edge<V, W>> path = dijkstra.getLastDistance().get(destination); }
if (path == null) });
throw new UnsupportedOperationException(NOT_CONNECTED);
return new ArrayList<>(path); return graph;
} }
@Override @Override
public Map<V, List<Edge<V, W>>> distance(V source) throws NullPointerException, IllegalArgumentException { public List<Edge<V, W>> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
checkNullAndExist(source); checkNullAndExist(source);
return new HashMap<>(getDijkstra(source).getLastDistance()); checkNullAndExist(destination);
}
Dijkstra<V, W> dijkstra = getDijkstra(source);
@Override List<Edge<V, W>> path = dijkstra.getLastDistance().get(destination);
public Iterator<V> iterator() { if (path == null)
return edges.keySet().iterator(); throw new UnsupportedOperationException(NOT_CONNECTED);
} return new ArrayList<>(path);
}
/** @Override
* Simple function that set all the memory vars at null if the graph changed public Map<V, List<Edge<V, W>>> distance(V source) throws NullPointerException, IllegalArgumentException {
*/ checkNullAndExist(source);
private void graphChanged() { return new HashMap<>(getDijkstra(source).getLastDistance());
tarjan = null; }
dijkstra = null;
} @Override
public Iterator<V> iterator() {
private Dijkstra<V, W> getDijkstra(V source) { return edges.keySet().iterator();
if (dijkstra == null) }
dijkstra = new HashMap<>();
if (dijkstra.get(source) == null) {
Dijkstra<V, W> newDijkstra = new Dijkstra<>(); /**
newDijkstra.visit(this, source, null); * Simple function that set all the memory vars at null if the graph changed
dijkstra.put(source, newDijkstra); */
} private void graphChanged() {
tarjan = null;
return dijkstra.get(source); dijkstra = null;
} }
private Tarjan<V, W> getTarjan() { private Dijkstra<V, W> getDijkstra(V source) {
if (tarjan == null) { if (dijkstra == null)
tarjan = new Tarjan<>(); dijkstra = new HashMap<>();
tarjan.visit(this, null, null); if (dijkstra.get(source) == null) {
} Dijkstra<V, W> newDijkstra = new Dijkstra<>();
newDijkstra.visit(this, source, null);
return tarjan; dijkstra.put(source, newDijkstra);
} }
private void checkNull(Object object) { return dijkstra.get(source);
if (object == null) }
throw new NullPointerException(PARAM_NULL);
} private Tarjan<V, W> getTarjan() {
if (tarjan == null) {
private void checkNullAndExist(V vertex) { tarjan = new Tarjan<>();
checkNull(vertex); tarjan.visit(this, null, null);
if (!edges.containsKey(vertex)) }
throw new IllegalArgumentException(VERTEX_NOT_CONTAINED);
} return tarjan;
} }
private void checkNull(Object object) {
if (object == null)
throw new NullPointerException(PARAM_NULL);
}
private void checkNullAndExist(V vertex) {
checkNull(vertex);
if (!edges.containsKey(vertex))
throw new IllegalArgumentException(VERTEX_NOT_CONTAINED);
}
}

View File

@@ -15,7 +15,7 @@ import java.util.function.Consumer;
* @param <V> the vertex * @param <V> the vertex
* @author Berack96 * @author Berack96
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings({"unchecked", "rawtypes"})
public class Vertex<V> { public class Vertex<V> {
public static final String REMOVED = "The vertex is no longer in the graph"; public static final String REMOVED = "The vertex is no longer in the graph";
@@ -240,7 +240,7 @@ public class Vertex<V> {
* @throws NullPointerException if the strategy is null * @throws NullPointerException if the strategy is null
* @throws UnsupportedOperationException if the vertex is not in the graph anymore * @throws UnsupportedOperationException if the vertex is not in the graph anymore
*/ */
public VisitInfo<V> visit(final VisitStrategy strategy, final Consumer<V> visit) throws NullPointerException, UnsupportedOperationException { public VisitInfo<V> visit(final VisitStrategy strategy, final Consumer<V> visit) throws NullPointerException, UnsupportedOperationException {
throwIfNotContained(); throwIfNotContained();
return graph.visit(vertex, (VisitStrategy<V, Number>) strategy, visit); return graph.visit(vertex, (VisitStrategy<V, Number>) strategy, visit);
} }

View File

@@ -0,0 +1,162 @@
package berack96.sim.util.graph.view;
import berack96.sim.util.graph.Graph;
import berack96.sim.util.graph.view.edge.EdgeListener;
import berack96.sim.util.graph.view.vertex.VertexListener;
import berack96.sim.util.graph.visit.VisitStrategy;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.util.*;
import java.util.List;
public class GraphInfo<V, W extends Number> extends JPanel {
private static final long serialVersionUID = 1L;
private final Map<String, VisitListener<V>> visits;
public GraphInfo(GraphPanel<V, W> graphPanel, VertexListener<V> vListener, EdgeListener<V, W> eListener, Set<VisitStrategy<V, W>> visits) {
this.visits = new HashMap<>();
/* ZERO (DESCRIPTION) */
JLabel listenerDescription = new JLabel();
JPanel panelDescription = new JPanel();
panelDescription.setOpaque(false);
panelDescription.add(listenerDescription);
/* FIRST (GRAPH INFO) */
JLabel vNumber = new JLabel(String.valueOf(graphPanel.getGraph().numberOfVertices()));
JLabel eNumber = new JLabel(String.valueOf(graphPanel.getGraph().numberOfEdges()));
JLabel gCyclic = new JLabel(String.valueOf(graphPanel.getGraph().isCyclic()));
List<Component> components = new LinkedList<>();
JLabel selected = new JLabel();
JComboBox<String> comboBox = new JComboBox<>();
comboBox.addItemListener(e -> {
if (e.getStateChange() == ItemEvent.SELECTED) {
try {
String clazz = (String) e.getItem();
VisitListener<V> listener = this.visits.get(clazz);
selected.setText(listener != null? "visit":"nothing");
listenerDescription.setText(listener != null? listener.getDescription():"");
graphPanel.getGraph().unMarkAll();
graphPanel.repaint();
graphPanel.setGraphListener(listener);
} catch (Exception ignore) {}
}
});
comboBox.addItem("None");
for(VisitStrategy<V, W> strategy: visits) {
String clazz = strategy.getClass().getSimpleName();
VisitListener<V> visit = new VisitListener<>(graphPanel, strategy);
comboBox.addItem(clazz);
this.visits.put(clazz, visit);
}
components.add(new JLabel("Visit Strategy: "));
components.add(comboBox);
components.add(new JLabel("Selected modality: "));
components.add(selected);
components.add(new JLabel("Vertex Number: "));
components.add(vNumber);
components.add(new JLabel("Edge Number: "));
components.add(eNumber);
components.add(new JLabel("Is Cyclic: "));
components.add(gCyclic);
JPanel panelInfo = new JPanel();
panelInfo.setOpaque(false);
panelInfo.setBorder(BorderFactory.createLineBorder(Color.RED));
panelInfo.setLayout(new GridLayout(components.size()/2, 2, 2*2, 2*2));
components.forEach(panelInfo::add);
components.clear();
/* SECOND (VERTEX) */
JLabel vVertex = new JLabel();
JLabel vEdgesNumber = new JLabel();
JLabel vEdgesInNumber = new JLabel();
JLabel vEdgesOutNumber = new JLabel();
JButton modVertex = new JButton("Modify Vertices");
modVertex.addActionListener(a -> {
comboBox.setSelectedIndex(0);
listenerDescription.setText(vListener.getDescription());
graphPanel.setGraphListener(vListener);
graphPanel.getGraph().unMarkAll();
graphPanel.repaint();
selected.setText("vertices");
});
JButton modEdge = new JButton("Modify Edges");
modEdge.addActionListener(a -> {
comboBox.setSelectedIndex(0);
listenerDescription.setText(eListener.getDescription());
graphPanel.setGraphListener(eListener);
graphPanel.getGraph().unMarkAll();
graphPanel.repaint();
selected.setText("edges");
});
components.add(modVertex);
components.add(modEdge);
components.add(new JLabel("Vertex name: "));
components.add(vVertex);
components.add(new JLabel("Edges: "));
components.add(vEdgesNumber);
components.add(new JLabel("Edges IN: "));
components.add(vEdgesInNumber);
components.add(new JLabel("Edges OUT: "));
components.add(vEdgesOutNumber);
JPanel panelVertex = new JPanel();
panelVertex.setOpaque(false);
panelVertex.setLayout(new GridLayout(components.size()/2, 2, 2*2, 2*2));
components.forEach(panelVertex::add);
/* Save/Load
JPanel panelSave = new JPanel(); */
/* ADDING COMPONENTS */
this.setBackground(Color.LIGHT_GRAY);
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
this.setOpaque(true);
this.setBorder(BorderFactory.createSoftBevelBorder(BevelBorder.RAISED, Color.GRAY, Color.DARK_GRAY));
this.add(panelDescription);
this.add(panelInfo);
this.add(panelVertex);
this.add(Box.createVerticalGlue());
modVertex.doClick();
graphPanel.addObserver((o, arg) -> {
Graph<V, W> graph = graphPanel.getGraph();
if(arg.equals(graph)) {
vNumber.setText(String.valueOf(graph.numberOfVertices()));
eNumber.setText(String.valueOf(graph.numberOfEdges()));
gCyclic.setText(String.valueOf(graph.isCyclic()));
/* There should be only one */
for(V v : graph.getMarkedWith("selected")) {
int inE = graph.getEdgesIn(v).size();
int outE = graph.getEdgesOut(v).size();
vEdgesInNumber.setText(String.valueOf(inE));
vEdgesOutNumber.setText(String.valueOf(outE));
vEdgesNumber.setText(String.valueOf(inE + outE));
vVertex.setText(v.toString());
}
}
});
}
}

View File

@@ -0,0 +1,27 @@
package berack96.sim.util.graph.view;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
/**
* An interface for creating a listener of the Graph.
*
* @author Berack96
*/
public interface GraphListener extends MouseListener, MouseMotionListener, KeyListener {
/**
* Remove the listener to the graph.
* This function is called when the listener is removed to the graph.
* Here you could remove any other thing that you have done.
*/
public void remove();
/**
* Get the description of this listener, in a way to interact with the user.
*
* @return a string describing the functionalities of this listener
*/
public String getDescription();
}

View File

@@ -0,0 +1,174 @@
package berack96.sim.util.graph.view;
import berack96.sim.util.graph.Graph;
import berack96.sim.util.graph.MapGraph;
import berack96.sim.util.graph.Vertex;
import berack96.sim.util.graph.view.edge.EdgeComponent;
import berack96.sim.util.graph.view.vertex.VertexComponent;
import java.awt.*;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Collection;
import java.util.HashSet;
import java.util.Observer;
import java.util.Set;
@SuppressWarnings("unchecked")
public class GraphPanel<V, W extends Number> extends Component {
private static final long serialVersionUID = 1L;
private final GraphicalView<VertexComponent<V>> vertexRender;
private final GraphicalView<EdgeComponent<V, W>> edgeRender;
private final Container vertices = new Container();
private final Container edges = new Container();
private final Graph<V, W> graph = new MapGraph<>();
private final Set<Observer> observers = new HashSet<>();
private GraphListener old = null;
public GraphPanel(GraphicalView<VertexComponent<V>> vertexRender, GraphicalView<EdgeComponent<V, W>> edgeRender) {
this.vertexRender = vertexRender;
this.edgeRender = edgeRender;
}
public Graph<V, W> getGraph() {
return graph;
}
public void setGraphListener(GraphListener listener) {
if(old != null)
old.remove();
for (MouseListener l : getMouseListeners())
removeMouseListener(l);
for (MouseMotionListener l : getMouseMotionListeners())
removeMouseMotionListener(l);
for (KeyListener l : getKeyListeners())
removeKeyListener(l);
old = listener;
addMouseListener(listener);
addMouseMotionListener(listener);
addKeyListener(listener);
}
public void addVertex(Point center, V vertex) {
VertexComponent<V> component = getVertexAt(center);
if (component == null) {
VertexComponent<V> v = new VertexComponent<>(new Vertex<>(graph, vertex));
if (!graph.contains(vertex)) {
v.vertex.addIfAbsent();
v.setBounds(vertexRender.getBox(v, center));
vertices.add(v);
}
}
}
public void removeVertex(Point center) {
try {
VertexComponent<V> component = getVertexAt(center);
component.vertex.remove();
vertices.remove(component);
} catch (Exception ignore) {}
}
public void moveVertex(VertexComponent<V> vertex, Point destination) {
Rectangle rectangle = vertexRender.getBox(vertex, destination);
vertex.setLocation(rectangle.x, rectangle.y);
}
public void addEdge(VertexComponent<V> source, VertexComponent<V> dest, W weight) {
try {
Point center = new Point(Math.abs(source.getX() - dest.getY()), Math.abs(source.getY() - dest.getY()));
EdgeComponent<V, W> edgeComponent = new EdgeComponent<>(source, dest, weight);
edgeComponent.setBounds(edgeRender.getBox(edgeComponent, center));
edges.add(edgeComponent);
graph.addEdge(edgeComponent.edge);
} catch (Exception ignore) {}
}
public void removeEdge(VertexComponent<V> source, VertexComponent<V> dest) {
try {
graph.removeEdge(source.vertex.getValue(), dest.vertex.getValue());
EdgeComponent<V, W> toRemove = null;
for (Component c : edges.getComponents()) {
EdgeComponent<V, W> edge = (EdgeComponent<V, W>) c;
if (edge.source.equals(source) && edge.destination.equals(dest))
toRemove = edge;
}
edges.remove(toRemove);
} catch (Exception ignore) {}
}
public void modEdge(VertexComponent<V> source, VertexComponent<V> dest, W weight) {
removeEdge(source, dest);
addEdge(source, dest, weight);
}
public VertexComponent<V> getVertexAt(Point point) {
Component component = vertices.getComponentAt(point);
return component instanceof VertexComponent ? (VertexComponent<V>) component : null;
}
public EdgeComponent<V, W> getEdgeAt(Point point) {
Component component = edges.getComponentAt(point);
return component instanceof EdgeComponent ? (EdgeComponent<V, W>) component : null;
}
public void addObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void setBounds(int x, int y, int width, int height) {
super.setBounds(x, y, width, height);
vertices.setBounds(x, y, width, height);
edges.setBounds(x, y, width, height);
}
@Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Collection<EdgeComponent<V, W>> toRemove = new HashSet<>();
for (Component component : edges.getComponents()) {
EdgeComponent<V, W> edge = (EdgeComponent<V, W>) component;
Vertex<V> source = edge.source.vertex;
Vertex<V> dest = edge.destination.vertex;
if (source.isStillContained() && dest.isStillContained() && graph.containsEdge(source.getValue(), dest.getValue())) {
Point center = new Point(edge.getX() + edge.getWidth() / 2, edge.getY() + edge.getHeight() / 2);
edge.setBounds(edgeRender.getBox(edge, center));
edgeRender.paint((Graphics2D) g2.create(), edge, center);
}
else
toRemove.add(edge);
}
toRemove.forEach(edges::remove);
for (Component component : vertices.getComponents()) {
VertexComponent<V> vertex = (VertexComponent<V>) component;
if (graph.contains(vertex.vertex.getValue())) {
Point center = new Point(vertex.getX() + vertex.getWidth() / 2, vertex.getY() + vertex.getHeight() / 2);
vertexRender.paint((Graphics2D) g2.create(), vertex, center);
}
}
updateObservers();
}
private void updateObservers() {
observers.forEach(observer -> observer.update(null, this.graph));
}
}

View File

@@ -0,0 +1,65 @@
package berack96.sim.util.graph.view;
import berack96.sim.util.graph.view.edge.EdgeIntListener;
import berack96.sim.util.graph.view.edge.EdgeListener;
import berack96.sim.util.graph.view.edge.EdgeView;
import berack96.sim.util.graph.view.vertex.VertexIntListener;
import berack96.sim.util.graph.view.vertex.VertexListener;
import berack96.sim.util.graph.view.vertex.VertexView;
import berack96.sim.util.graph.visit.*;
import javax.swing.*;
import java.awt.*;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* This class is the Window that appear for building the graph and playing around with it
*
* @author Berack96
*/
public class GraphWindow<V, W extends Number> extends JFrame {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
GraphPanel<Integer, Integer> panel = new GraphPanel<>(new VertexView<>(), new EdgeView<>());
GraphWindow<Integer, Integer> win = new GraphWindow<>(panel, new VertexIntListener(panel), new EdgeIntListener<>(panel));
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); // full screen
dim.setSize(dim.width / 2, dim.height / 2);
win.setSize(dim);
win.setLocationRelativeTo(null); //centered
win.visitRefresh(500);
win.setVisible(true);
}
private final GraphPanel<V, W> graphPanel;
private final GraphInfo<V, W> infoPanel;
private GraphWindow(GraphPanel<V, W> graphPanel, VertexListener<V> vListener, EdgeListener<V, W> eListener) {
this.setTitle("Grafo");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(new BorderLayout());
Set<VisitStrategy<V, W>> strats = new LinkedHashSet<>();
strats.add(new DFS<>());
strats.add(new BFS<>());
strats.add(new Dijkstra<>());
strats.add(new Tarjan<>());
this.infoPanel = new GraphInfo<>(graphPanel, vListener, eListener, strats);
this.graphPanel = graphPanel;
this.add(infoPanel, BorderLayout.EAST);
this.add(graphPanel);
}
public void visitRefresh(int millisec) {
VisitListener.changeRefresh(millisec);
}
public GraphPanel<V, W> getGraphPanel() {
return graphPanel;
}
}

View File

@@ -0,0 +1,29 @@
package berack96.sim.util.graph.view;
import java.awt.*;
/**
* An interface for divide the "hitbox" and the "paint" of the various items
*
* @param <O> the object to paint
* @author Berack96
*/
public interface GraphicalView<O> {
/**
* Box where the object is sensible at listeners (like Hitbox)
*
* @param obj the object to draw
* @param center the center point of the object
* @return a rectangle where the object is sensible to the listeners
*/
Rectangle getBox(O obj, Point center);
/**
* The paint function, aka the part where you can draw things (like Mesh)
*
* @param g2 the graphics object used for painting
* @param obj the object to paint
* @param center the center point of the object
*/
void paint(Graphics2D g2, O obj, Point center);
}

View File

@@ -0,0 +1,112 @@
package berack96.sim.util.graph.view;
import berack96.sim.util.graph.Graph;
import berack96.sim.util.graph.view.vertex.VertexComponent;
import berack96.sim.util.graph.visit.VisitInfo;
import berack96.sim.util.graph.visit.VisitStrategy;
import javax.swing.*;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
public class VisitListener<V> implements GraphListener {
private final GraphPanel<V, ?> panel;
private final VisitStrategy<V, ?> strategy;
private final Set<Timer> timers = new HashSet<>();
private static int refreshTime = 1000;
public VisitListener(GraphPanel<V, ?> panel, VisitStrategy<V, ?> strategy) {
this.panel = panel;
this.strategy = strategy;
}
public static void changeRefresh(int millisec) {
refreshTime = millisec;
}
@Override
public void remove() {
timers.forEach(t -> t.stop());
timers.clear();
}
@Override
public String getDescription() {
return "<html>"
+ "Start a visit by pressing<br />"
+ "with the mouse SX on the root vertex<br />"
+ "</html>";
}
@Override
public void mousePressed(MouseEvent e) {
this.remove();
Graph<V, ?> graph = panel.getGraph();
graph.unMarkAll();
panel.repaint();
if (e.getButton() == MouseEvent.BUTTON1)
try {
VertexComponent<V> vertex = panel.getVertexAt(e.getPoint());
AtomicInteger count = new AtomicInteger(0);
VisitInfo<V> info = vertex.vertex.visit(strategy, null);
info.forEach(v -> {
final boolean visited = v.timeVisited == count.get();
Timer timer = new Timer(count.getAndIncrement() * refreshTime, e1 -> {
if (visited && v.parent !=null)
graph.mark(v.vertex, v.parent);
graph.mark(v.vertex, visited ? "visited" : "discovered");
panel.repaint();
});
timers.add(timer);
timer.setRepeats(false);
timer.start();
});
} catch (Exception ignore) {}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseMoved(MouseEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}

View File

@@ -0,0 +1,22 @@
package berack96.sim.util.graph.view.edge;
import berack96.sim.util.graph.Edge;
import berack96.sim.util.graph.view.vertex.VertexComponent;
import java.awt.*;
public class EdgeComponent<V, W extends Number> extends Component {
private static final long serialVersionUID = 1L;
public final VertexComponent<V> source;
public final VertexComponent<V> destination;
public final W weight;
public final Edge<V, W> edge;
public EdgeComponent(VertexComponent<V> source, VertexComponent<V> destination, W weight) {
this.source = source;
this.destination = destination;
this.weight = weight;
this.edge = new Edge<>(source.vertex.getValue(), destination.vertex.getValue(), weight);
}
}

View File

@@ -0,0 +1,24 @@
package berack96.sim.util.graph.view.edge;
import berack96.sim.util.graph.Vertex;
import berack96.sim.util.graph.view.GraphPanel;
public class EdgeIntListener<V> extends EdgeListener<V, Integer> {
public EdgeIntListener(GraphPanel<V, Integer> graphPanel) {
super(graphPanel);
}
@Override
public void remove() {}
@Override
protected Integer buildNewEdge(Vertex<V> vertex, Vertex<V> vertex1) {
return (int) (Math.random() * 100);
}
@Override
protected Integer buildEdgeFrom(String string) {
return Integer.parseInt(string.replaceAll("[^0-9]+", ""));
}
}

View File

@@ -0,0 +1,128 @@
package berack96.sim.util.graph.view.edge;
import berack96.sim.util.graph.Vertex;
import berack96.sim.util.graph.view.GraphListener;
import berack96.sim.util.graph.view.GraphPanel;
import berack96.sim.util.graph.view.vertex.VertexComponent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.concurrent.atomic.AtomicReference;
public abstract class EdgeListener<V, W extends Number> implements GraphListener {
private final GraphPanel<V, W> graphPanel;
private final AtomicReference<VertexComponent<V>> componentPressed = new AtomicReference<>();
private final AtomicReference<Integer> buttonPressed = new AtomicReference<>();
private final AtomicReference<EdgeComponent<V, W>> edge = new AtomicReference<>();
private final StringBuilder string = new StringBuilder();
public EdgeListener(GraphPanel<V, W> graphPanel) {
this.graphPanel = graphPanel;
}
protected abstract W buildNewEdge(Vertex<V> vertex, Vertex<V> vertex1);
protected abstract W buildEdgeFrom(String string);
@Override
public String getDescription() {
return "<html>"
+ "Modify edges with:<br />"
+ "mouse SX on vertex to another ==> add<br />"
+ "mouse DX on edge ==> change weigth<br />"
+ "(only numbers allowed)"
+ "</html>";
}
@Override
public void mousePressed(MouseEvent e) {
buttonPressed.set(e.getButton());
componentPressed.set(graphPanel.getVertexAt(e.getPoint()));
}
@Override
public void mouseReleased(MouseEvent e) {
try {
edge.get().source.vertex.unMark("modS");
edge.get().destination.vertex.unMark("modD");
edge.set(null);
} catch (Exception ignored) {}
if (buttonPressed.get() == MouseEvent.BUTTON1) {
try {
VertexComponent<V> source = componentPressed.get();
VertexComponent<V> destination = graphPanel.getVertexAt(e.getPoint());
if (!graphPanel.getGraph().containsEdge(source.vertex.getValue(), destination.vertex.getValue()))
graphPanel.addEdge(source, destination, buildNewEdge(source.vertex, destination.vertex));
} catch (Exception ignore) {
}
} else {
edge.set(graphPanel.getEdgeAt(e.getPoint()));
try {
edge.get().source.vertex.mark("modS");
edge.get().destination.vertex.mark("modD");
graphPanel.setFocusTraversalKeysEnabled(false);
graphPanel.requestFocusInWindow();
} catch (Exception ignored) {}
}
string.delete(0, string.length());
componentPressed.set(null);
graphPanel.repaint();
}
@Override
public void keyPressed(KeyEvent e) {
if (edge.get() != null && Character.isDigit(e.getKeyChar())) {
string.append(e.getKeyChar());
if (!edge.get().source.equals(edge.get().destination))
try {
graphPanel.modEdge(edge.get().source, edge.get().destination, buildEdgeFrom(string.toString()));
graphPanel.repaint();
} catch (Exception ignored) {
}
} else {
try {
edge.get().source.vertex.unMark("modS");
edge.get().destination.vertex.unMark("modD");
} catch (Exception ignored) {}
edge.set(null);
string.delete(0, string.length());
}
graphPanel.repaint();
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseMoved(MouseEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}

View File

@@ -0,0 +1,109 @@
package berack96.sim.util.graph.view.edge;
import berack96.sim.util.graph.view.GraphicalView;
import berack96.sim.util.graph.view.stuff.Arrow;
import java.awt.*;
import java.awt.geom.Point2D;
import java.util.Collection;
public class EdgeView<V, W extends Number> implements GraphicalView<EdgeComponent<V, W>> {
private static final Font FONT = new Font("Papyrus", Font.BOLD, 14);
@Override
public Rectangle getBox(EdgeComponent<V, W> edge, Point center) {
/* CALCULATING BOUNDS AND ARROW STARTING AND ENDING POINTS */
Point srcLoc = edge.source.getLocation();
Point desLoc = edge.destination.getLocation();
/* getting the radius */
int srcRadius = edge.source.getHeight() / 2;
int desRadius = edge.destination.getHeight() / 2;
/* centering the location */
srcLoc.translate(srcRadius, srcRadius);
desLoc.translate(desRadius, desRadius);
/* using vector for moving to the edge of the circumference and finding location of int box */
final Point.Double vector = getVector(srcLoc, desLoc);
/* CALCULATING THE NUMBER SPACE */
int boxDistance = (int) (srcLoc.distance(desLoc) / 2.7);
FontMetrics metrics = edge.getFontMetrics(FONT);
int dimString = metrics.stringWidth(edge.weight.toString());
int dimRect = Math.max(dimString, metrics.getHeight());
return new Rectangle(
(int) ((desLoc.x - (vector.x * boxDistance)) - (dimRect / 2)),
(int) ((desLoc.y - (vector.y * boxDistance)) - (dimRect / 2)),
dimRect, dimRect);
}
@Override
public void paint(Graphics2D g2, EdgeComponent<V, W> edge, Point center) {
/* CALCULATING BOUNDS AND ARROW STARTING AND ENDING POINTS */
Point srcLoc = edge.source.getLocation();
Point desLoc = edge.destination.getLocation();
/* getting the radius */
int srcRadius = edge.source.getHeight() / 2;
int desRadius = edge.destination.getHeight() / 2;
/* centering the location */
srcLoc.translate(srcRadius, srcRadius);
desLoc.translate(desRadius, desRadius);
/* using vector for moving to the edge of the circumference and finding location of int box */
final Point.Double vector = getVector(srcLoc, desLoc);
/* CALCULATING THE NUMBER SPACE */
int boxDistance = (int) (srcLoc.distance(desLoc) / 2.7);
FontMetrics metrics = edge.getFontMetrics(FONT);
int dimString = metrics.stringWidth(edge.weight.toString());
int dimRect = Math.max(dimString, metrics.getHeight());
Rectangle integerRect = new Rectangle(
(int) ((desLoc.x - (vector.x * boxDistance)) - (dimRect / 2)),
(int) ((desLoc.y - (vector.y * boxDistance)) - (dimRect / 2)),
dimRect, dimRect);
/* moving to a distance R to the center */
srcLoc.translate((int) (vector.x * srcRadius), (int) (vector.y * srcRadius));
desLoc.translate((int) (-vector.x * desRadius), (int) (-vector.y * desRadius));
/* THE COLOR OF THE ARROW */
Collection<Object> marksD = edge.destination.vertex.getMarks();
Collection<Object> marksS = edge.source.vertex.getMarks();
boolean isChild = marksD.contains(edge.source.vertex.getValue());
boolean selected = marksS.contains("selected");
boolean isMod = marksD.contains("modD") && marksS.contains("modS");
Color arrowColor = isChild || selected ? Color.RED : Color.BLACK;
Color boxColor = isChild || selected || isMod ? Color.ORANGE : Color.BLUE;
Color stringColor = isChild || selected || isMod ? Color.BLACK : Color.CYAN;
g2.setFont(FONT);
/* draw all */
g2.setColor(arrowColor);
//g2d.drawLine(arrowStart.x, arrowStart.y, arrowEnd.x, arrowEnd.y);
Polygon arrow = new Arrow(srcLoc, desLoc, 1, 10);
//g2d.draw(arrow);
g2.fillPolygon(arrow);
/* draw the integer space */
g2.setColor(boxColor);
g2.fill(integerRect);
g2.setColor(arrowColor);
g2.draw(integerRect);
g2.setColor(stringColor);
g2.drawString(edge.weight.toString(), (float) (integerRect.x + (dimRect - dimString) / 2), (float) (integerRect.y + (dimRect + metrics.getHeight() / 2) / 2));
}
private Point.Double getVector(Point a, Point b) {
final Point.Double vector = new Point2D.Double(b.x - a.x, b.y - a.y);
/* normalizing vector */
double length = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
if(length != 0) {
vector.x = vector.x / length;
vector.y = vector.y / length;
}
return vector;
}
}

View File

@@ -0,0 +1,59 @@
package berack96.sim.util.graph.view.stuff;
import java.awt.*;
/**
* Class that create a Polygon that has a shape of an arrow
*
* @author Berack96
*/
public class Arrow extends Polygon {
private static final long serialVersionUID = 1L;
/**
* Create an arrow
*
* @param start the starting point of your arrow (the base)
* @param end the ending point of your arrow (the head)
* @param size the size of the arrow base
* @param headSize the size of the arrow's head
*/
public Arrow(Point start, Point end, final int size, final int headSize) {
final Point.Double vector = new Point.Double(end.x - start.x, end.y - start.y);
/* vectors normalization */
double length = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
vector.x = vector.x / length;
vector.y = vector.y / length;
final Point headStart = new Point((int) (end.x - vector.x * headSize), (int) (end.y - vector.y * headSize));
/* rotating vector for the parallels */
double cs = Math.cos(Math.PI / 2);
double sn = Math.sin(Math.PI / 2);
double x = vector.x * cs - vector.y * sn;
double y = vector.x * sn + vector.y * cs;
vector.setLocation(x, y);
/* TODO here use some magic for create a curve arrow if vector.x == vector.y && vector.x == 0 */
/* building arrow starting from A to G */
/*
C
|\
| \
A--------B \
| D
G--------F /
| /
|/
E
*/
addPoint((int) (start.x - vector.x * size), (int) (start.y - vector.y * size));
addPoint((int) (start.x + vector.x * size), (int) (start.y + vector.y * size));
addPoint((int) (headStart.x + vector.x * size), (int) (headStart.y + vector.y * size));
addPoint((int) (headStart.x + vector.x * headSize), (int) (headStart.y + vector.y * headSize));
addPoint(end.x, end.y);
addPoint((int) (headStart.x - vector.x * headSize), (int) (headStart.y - vector.y * headSize));
addPoint((int) (headStart.x - vector.x * size), (int) (headStart.y - vector.y * size));
}
}

View File

@@ -0,0 +1,15 @@
package berack96.sim.util.graph.view.vertex;
import berack96.sim.util.graph.Vertex;
import java.awt.*;
public class VertexComponent<V> extends Component {
private static final long serialVersionUID = 1L;
public final Vertex<V> vertex;
public VertexComponent(Vertex<V> vertex) {
this.vertex = vertex;
}
}

View File

@@ -0,0 +1,24 @@
package berack96.sim.util.graph.view.vertex;
import berack96.sim.util.graph.Graph;
import berack96.sim.util.graph.view.GraphPanel;
public class VertexIntListener extends VertexListener<Integer> {
private Integer counter = 0;
public VertexIntListener(GraphPanel<Integer, ?> panel) {
super(panel);
}
@Override
public void remove() {}
@Override
protected Integer buildNewVertex(Graph<Integer, ?> graph) {
if(graph.numberOfVertices() == 0)
counter = 0;
counter++;
return counter - 1;
}
}

View File

@@ -0,0 +1,91 @@
package berack96.sim.util.graph.view.vertex;
import berack96.sim.util.graph.Graph;
import berack96.sim.util.graph.view.GraphListener;
import berack96.sim.util.graph.view.GraphPanel;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.concurrent.atomic.AtomicReference;
public abstract class VertexListener<V> implements GraphListener {
protected final GraphPanel<V, ?> panel;
private final AtomicReference<VertexComponent<V>> componentPressed = new AtomicReference<>();
public VertexListener(GraphPanel<V, ?> panel) {
this.panel = panel;
}
protected abstract V buildNewVertex(Graph<V, ?> graph);
@Override
public String getDescription() {
return "<html>"
+ "Modify vertex with:<br />"
+ "mouse SX ==> add<br />"
+ "mouse SX on vertex ==> move<br />"
+ "mouse DX ==> remove<br />"
+ "</html>";
}
@Override
public void mousePressed(MouseEvent e) {
try {
if (e.getButton() == MouseEvent.BUTTON1)
componentPressed.set(panel.getVertexAt(e.getPoint()));
} catch (Exception ignore) {}
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1 && componentPressed.get() == null)
panel.addVertex(e.getPoint(), buildNewVertex(panel.getGraph()));
else if (e.getButton() == MouseEvent.BUTTON3)
panel.removeVertex(e.getPoint());
panel.getGraph().unMarkAll("selected");
if(componentPressed.get() != null)
componentPressed.get().vertex.mark("selected");
panel.repaint();
componentPressed.set(null);
}
@Override
public void mouseDragged(MouseEvent e) {
if (componentPressed.get() != null) {
panel.moveVertex(componentPressed.get(), e.getPoint());
panel.repaint();
}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseMoved(MouseEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}

View File

@@ -0,0 +1,42 @@
package berack96.sim.util.graph.view.vertex;
import berack96.sim.util.graph.view.GraphicalView;
import java.awt.*;
public class VertexView<V> implements GraphicalView<VertexComponent<V>> {
private static final Font FONT = new Font("Comic Sans MS", Font.BOLD, 17);
private static final int PADDING = 6;
@Override
public Rectangle getBox(VertexComponent<V> obj, Point center) {
FontMetrics metrics = obj.getFontMetrics(FONT);
int stringPixels = metrics.stringWidth(obj.vertex.getValue().toString());
int size = Math.max(stringPixels, metrics.getHeight()) + 2 * PADDING;
return new Rectangle(center.x - size / 2, center.y - size / 2, size, size);
}
@Override
public void paint(Graphics2D g2, VertexComponent<V> obj, Point center) {
boolean discovered = obj.vertex.getMarks().contains("discovered");
boolean visited = obj.vertex.getMarks().contains("visited");
boolean selected = obj.vertex.getMarks().contains("selected");
FontMetrics metrics = obj.getFontMetrics(FONT);
int stringPixels = metrics.stringWidth(obj.vertex.getValue().toString());
int size = Math.max(stringPixels, metrics.getHeight()) + 2 * PADDING;
center.x = center.x - size / 2;
center.y = center.y - size / 2;
g2.setFont(FONT);
g2.setColor(visited || selected ? Color.RED : Color.ORANGE);
g2.fillOval(center.x, center.y, size, size);
g2.setColor(visited || discovered || selected ? Color.ORANGE : Color.YELLOW);
g2.fillOval(center.x + PADDING / 2, center.y + PADDING / 2, size - PADDING, size - PADDING);
g2.setColor(Color.BLACK);
g2.drawString(obj.vertex.getValue().toString(), center.x + PADDING + (size - 2 * PADDING - stringPixels) / 2, center.y + (size) / 2 + PADDING);
}
}

View File

@@ -32,12 +32,12 @@ public class Dijkstra<V, W extends Number> implements VisitDistance<V, W> {
public VisitInfo<V> visit(Graph<V, W> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException { public VisitInfo<V> visit(Graph<V, W> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
VisitInfo<V> info = new VisitInfo<>(source); VisitInfo<V> info = new VisitInfo<>(source);
Queue<QueueEntry> queue = new PriorityQueue<>(); Queue<QueueEntry> queue = new PriorityQueue<>();
Map<V, Integer> dist = new HashMap<>(); Map<V, Double> dist = new HashMap<>();
Map<V, V> prev = new HashMap<>(); Map<V, V> prev = new HashMap<>();
this.source = source; this.source = source;
dist.put(source, 0); // Initialization dist.put(source, 0.0); // Initialization
queue.add(new QueueEntry(source, 0)); queue.add(new QueueEntry(source, 0.0));
while (!queue.isEmpty()) { // The main loop while (!queue.isEmpty()) { // The main loop
QueueEntry u = queue.poll(); // Remove and return best vertex QueueEntry u = queue.poll(); // Remove and return best vertex
@@ -49,8 +49,8 @@ public class Dijkstra<V, W extends Number> implements VisitDistance<V, W> {
graph.getEdgesOut(u.entry).forEach((edge) -> { graph.getEdgesOut(u.entry).forEach((edge) -> {
V child = edge.getDestination(); V child = edge.getDestination();
info.setDiscovered(child); info.setDiscovered(child);
int alt = dist.get(u.entry) + edge.getWeight().intValue(); double alt = dist.get(u.entry) + edge.getWeight().doubleValue();
Integer distCurrent = dist.get(child); Double distCurrent = dist.get(child);
if (distCurrent == null || alt < distCurrent) { if (distCurrent == null || alt < distCurrent) {
dist.put(child, alt); dist.put(child, alt);
prev.put(child, u.entry); prev.put(child, u.entry);
@@ -83,14 +83,15 @@ public class Dijkstra<V, W extends Number> implements VisitDistance<V, W> {
private class QueueEntry implements Comparable<QueueEntry> { private class QueueEntry implements Comparable<QueueEntry> {
final V entry; final V entry;
final Integer weight; final Double weight;
QueueEntry(V entry, Integer weight) { QueueEntry(V entry, Double weight) {
this.entry = entry; this.entry = entry;
this.weight = weight; this.weight = weight;
} }
@Override @SuppressWarnings("unchecked")
@Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
try { try {
return ((QueueEntry) obj).entry.equals(entry); return ((QueueEntry) obj).entry.equals(entry);
@@ -101,7 +102,8 @@ public class Dijkstra<V, W extends Number> implements VisitDistance<V, W> {
@Override @Override
public int compareTo(QueueEntry queueEntry) { public int compareTo(QueueEntry queueEntry) {
return this.weight - queueEntry.weight; double ret = this.weight - queueEntry.weight;
return ret==0? 0: ret<0? -1:1;
} }
} }
} }

View File

@@ -25,5 +25,5 @@ public interface VisitDistSourceDest<V, W extends Number> extends VisitStrategy<
* @throws NullPointerException if one of the vertex is null * @throws NullPointerException if one of the vertex is null
* @throws IllegalArgumentException if one of the vertex is not contained in the graph * @throws IllegalArgumentException if one of the vertex is not contained in the graph
*/ */
List<Edge<V, W>> distance(Graph graph, V source, V destination) throws NullPointerException, IllegalArgumentException; List<Edge<V, W>> distance(Graph<V, W> graph, V source, V destination) throws NullPointerException, IllegalArgumentException;
} }

View File

@@ -1,30 +1,30 @@
package berack96.sim.util.graph.visit; package berack96.sim.util.graph.visit;
import berack96.sim.util.graph.Graph; import berack96.sim.util.graph.Graph;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
* This class is used for define some strategy for the visit of a graph. * This class is used for define some strategy for the visit of a graph.
* *
* @param <V> The Object that represent a vertex * @param <V> The Object that represent a vertex
* @param <W> The Object that represent the edge (more specifically the weight of the edge) * @param <W> The Object that represent the edge (more specifically the weight of the edge)
* @author Berack96 * @author Berack96
*/ */
public interface VisitStrategy<V, W extends Number> { public interface VisitStrategy<V, W extends Number> {
/** /**
* With this the graph will be visited accordingly to the strategy of the visit.<br> * With this the graph will be visited accordingly to the strategy of the visit.<br>
* Some strategy can accept a source vertex null, because they visit all the graph anyway.<br> * Some strategy can accept a source vertex null, because they visit all the graph anyway.<br>
* If you want to stop the visit of the graph, you just have to throw any exception in the visit function, but be sure to catch it * If you want to stop the visit of the graph, you just have to throw any exception in the visit function, but be sure to catch it
* *
* @param graph the graph to visit * @param graph the graph to visit
* @param source the source of the visit * @param source the source of the visit
* @param visit the function to apply at each vertex when they are visited * @param visit the function to apply at each vertex when they are visited
* @return an info of the view * @return an info of the view
* @throws NullPointerException if one of the arguments is null (only the consumers can be null) * @throws NullPointerException if one of the arguments is null (only the consumers can be null)
* @throws IllegalArgumentException if the source vertex is not in the graph * @throws IllegalArgumentException if the source vertex is not in the graph
* @throws UnsupportedOperationException in the case that the visit algorithm cannot be applied to the graph * @throws UnsupportedOperationException in the case that the visit algorithm cannot be applied to the graph
*/ */
VisitInfo<V> visit(Graph<V, W> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException; VisitInfo<V> visit(Graph<V, W> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException;
} }

View File

@@ -918,6 +918,7 @@ public class TestGraph {
shouldThrow(nullException, () -> graph.unMark("1", null)); shouldThrow(nullException, () -> graph.unMark("1", null));
shouldThrow(nullException, () -> graph.unMark(null, "blue")); shouldThrow(nullException, () -> graph.unMark(null, "blue"));
shouldThrow(nullException, () -> graph.unMarkAll(null)); shouldThrow(nullException, () -> graph.unMarkAll(null));
shouldThrow(nullException, () -> graph.getMarkedWith(null));
shouldThrow(notException, () -> graph.mark("324", "yellow")); shouldThrow(notException, () -> graph.mark("324", "yellow"));
shouldThrow(notException, () -> graph.unMark("32423")); shouldThrow(notException, () -> graph.unMark("32423"));
@@ -966,30 +967,54 @@ public class TestGraph {
graph.mark("2", "mark"); graph.mark("2", "mark");
graph.mark("3", "mark2"); graph.mark("3", "mark2");
graph.mark("1", "mark2"); graph.mark("1", "mark2");
shouldContain(graph.getMarks("1"), "mark", "mark2"); graph.mark("1", 3);
shouldContain(graph.getMarks("1"), "mark", "mark2", 3);
shouldContain(graph.getMarks("2"), "mark"); shouldContain(graph.getMarks("2"), "mark");
shouldContain(graph.getMarks("3"), "mark2"); shouldContain(graph.getMarks("3"), "mark2");
shouldContain(graph.getMarkedWith("mark"), "2", "1");
shouldContain(graph.getMarkedWith("mark2"), "1", "3");
shouldContain(graph.getMarkedWith(3), "1");
graph.unMark("1", "mark"); graph.unMark("1", "mark");
shouldContain(graph.getMarks("1"), "mark2"); shouldContain(graph.getMarks("1"), "mark2", 3);
shouldContain(graph.getMarks("2"), "mark"); shouldContain(graph.getMarks("2"), "mark");
shouldContain(graph.getMarks("3"), "mark2"); shouldContain(graph.getMarks("3"), "mark2");
shouldContain(graph.getMarkedWith("mark"), "2");
shouldContain(graph.getMarkedWith("mark2"), "1", "3");
shouldContain(graph.getMarkedWith(3), "1");
graph.unMarkAll("mark2"); graph.unMarkAll("mark2");
shouldContain(graph.getMarks("1")); shouldContain(graph.getMarks("1"), 3);
shouldContain(graph.getMarks("2"), "mark"); shouldContain(graph.getMarks("2"), "mark");
shouldContain(graph.getMarks("3")); shouldContain(graph.getMarks("3"));
shouldContain(graph.getMarkedWith("mark"), "2");
shouldContain(graph.getMarkedWith("mark2"));
shouldContain(graph.getMarkedWith(3), "1");
graph.unMark("1", "mark"); graph.unMark("1", "mark");
graph.unMark("2", "mark2"); graph.unMark("2", "mark2");
shouldContain(graph.getMarks("1")); shouldContain(graph.getMarks("1"), 3);
shouldContain(graph.getMarks("2"), "mark"); shouldContain(graph.getMarks("2"), "mark");
shouldContain(graph.getMarks("3")); shouldContain(graph.getMarks("3"));
shouldContain(graph.getMarkedWith("mark"), "2");
shouldContain(graph.getMarkedWith("mark2"));
shouldContain(graph.getMarkedWith(3), "1");
graph.unMark("2", "mark"); graph.unMark("2", "mark");
shouldContain(graph.getMarks("1"), 3);
shouldContain(graph.getMarks("2"));
shouldContain(graph.getMarks("3"));
shouldContain(graph.getMarkedWith("mark"));
shouldContain(graph.getMarkedWith("mark2"));
shouldContain(graph.getMarkedWith(3), "1");
graph.unMarkAll(3);
shouldContain(graph.getMarks("1")); shouldContain(graph.getMarks("1"));
shouldContain(graph.getMarks("2")); shouldContain(graph.getMarks("2"));
shouldContain(graph.getMarks("3")); shouldContain(graph.getMarks("3"));
shouldContain(graph.getMarkedWith("mark"));
shouldContain(graph.getMarkedWith("mark2"));
shouldContain(graph.getMarkedWith(3));
} }
@Test @Test