Graph View
* added simple Window for graph play * added new function to interface * all tests passes * end graph
This commit is contained in:
BIN
.gitignore
vendored
BIN
.gitignore
vendored
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,488 +1,500 @@
|
||||
package berack96.sim.util.graph;
|
||||
|
||||
import berack96.sim.util.graph.visit.Dijkstra;
|
||||
import berack96.sim.util.graph.visit.Tarjan;
|
||||
import berack96.sim.util.graph.visit.VisitInfo;
|
||||
import berack96.sim.util.graph.visit.VisitStrategy;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* 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>
|
||||
* 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 <W> the weight of the edges
|
||||
* @author Berack96
|
||||
*/
|
||||
public class MapGraph<V, W extends Number> implements Graph<V, W> {
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* If an edge exist, then it's weight is returned
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
private Map<V, Dijkstra<V, W>> dijkstra = null;
|
||||
|
||||
@Override
|
||||
public boolean isCyclic() {
|
||||
return stronglyConnectedComponents().size() != numberOfVertices();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDAG() {
|
||||
return !isCyclic();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vertex<V> getVertex(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
return new Vertex<>(this, vertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addVertex(V vertex) throws NullPointerException {
|
||||
checkNull(vertex);
|
||||
graphChanged();
|
||||
edges.put(vertex, new HashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addVertexIfAbsent(V vertex) throws NullPointerException {
|
||||
if (contains(vertex))
|
||||
return false;
|
||||
addVertex(vertex);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAllVertices(Collection<V> vertices) throws NullPointerException {
|
||||
checkNull(vertices);
|
||||
vertices.forEach(this::addVertexIfAbsent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeVertex(V vertex) throws NullPointerException {
|
||||
if (contains(vertex)) {
|
||||
graphChanged();
|
||||
edges.remove(vertex);
|
||||
edges.forEach((v, map) -> map.remove(vertex));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllVertex() {
|
||||
graphChanged();
|
||||
edges.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(V vertex) throws NullPointerException {
|
||||
checkNull(vertex);
|
||||
return edges.containsKey(vertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
checkNull(mark);
|
||||
|
||||
Set<Object> set = marked.computeIfAbsent(vertex, (m) -> new HashSet<>());
|
||||
set.add(mark);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
checkNull(mark);
|
||||
marked.get(vertex).remove(mark);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unMark(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
marked.get(vertex).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Object> getMarks(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
return marked.computeIfAbsent(vertex, (m) -> new HashSet<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unMarkAll(Object mark) {
|
||||
checkNull(mark);
|
||||
marked.forEach((v, m) -> m.remove(mark));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unMarkAll() {
|
||||
marked.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex1);
|
||||
checkNullAndExist(vertex2);
|
||||
checkNull(weight);
|
||||
|
||||
graphChanged();
|
||||
return edges.get(vertex1).put(vertex2, weight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public W addEdge(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException {
|
||||
return addEdge(edge.getSource(), edge.getDestination(), edge.getWeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException {
|
||||
addVertexIfAbsent(vertex1);
|
||||
addVertexIfAbsent(vertex2);
|
||||
return addEdge(vertex1, vertex2, weight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public W addEdgeAndVertices(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException {
|
||||
return addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAllEdges(Collection<Edge<V, W>> edges) throws NullPointerException {
|
||||
edges.forEach((edge) -> addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public W getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex1);
|
||||
checkNullAndExist(vertex2);
|
||||
|
||||
return edges.get(vertex1).get(vertex2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex1);
|
||||
checkNullAndExist(vertex2);
|
||||
|
||||
graphChanged();
|
||||
edges.get(vertex1).remove(vertex2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
|
||||
graphChanged();
|
||||
edges.forEach((v, map) -> map.remove(vertex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
|
||||
graphChanged();
|
||||
edges.put(vertex, new HashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
removeVertex(vertex);
|
||||
addVertex(vertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllEdge() {
|
||||
graphChanged();
|
||||
edges.forEach((v, map) -> map.clear());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException {
|
||||
return (contains(vertex1) && contains(vertex2)) && edges.get(vertex1).get(vertex2) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> vertices() {
|
||||
return new HashSet<>(edges.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Edge<V, W>> edges() {
|
||||
Set<Edge<V, W>> allEdges = new HashSet<>();
|
||||
edges.forEach((source, map) -> map.forEach((destination, weight) -> allEdges.add(new Edge<>(source, destination, weight))));
|
||||
return allEdges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Edge<V, W>> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
|
||||
Set<Edge<V, W>> set = new HashSet<>();
|
||||
edges.forEach((source, map) -> map.forEach((destination, weight) -> {
|
||||
if (destination.equals(vertex) || source.equals(vertex))
|
||||
set.add(new Edge<>(source, destination, weight));
|
||||
}));
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Edge<V, W>> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
Collection<Edge<V, W>> collection = new HashSet<>();
|
||||
edges.forEach((source, edge) -> {
|
||||
if (edge.get(vertex) != null)
|
||||
collection.add(new Edge<>(source, vertex, edge.get(vertex)));
|
||||
});
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Edge<V, W>> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
Collection<Edge<V, W>> collection = new HashSet<>();
|
||||
edges.get(vertex).forEach((dest, weight) -> collection.add(new Edge<>(vertex, dest, weight)));
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
|
||||
return new HashSet<>(edges.get(vertex).keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
|
||||
Set<V> set = new HashSet<>();
|
||||
edges.forEach((v, map) -> {
|
||||
if (map.containsKey(vertex)) set.add(v);
|
||||
});
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
|
||||
AtomicInteger sum = new AtomicInteger();
|
||||
edges.forEach((v, map) -> {
|
||||
if (map.containsKey(vertex))
|
||||
sum.getAndIncrement();
|
||||
});
|
||||
|
||||
return sum.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
|
||||
return edges.get(vertex).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int degree(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
return degreeIn(vertex) + degreeOut(vertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int numberOfVertices() {
|
||||
return edges.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int numberOfEdges() {
|
||||
AtomicInteger sum = new AtomicInteger(0);
|
||||
edges.forEach((v, map) -> sum.getAndAdd(map.size()));
|
||||
|
||||
return sum.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisitInfo<V> visit(V source, VisitStrategy<V, W> strategy, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
|
||||
return strategy.visit(this, source, visit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graph<V, W> transpose() {
|
||||
Graph<V, W> graph = new MapGraph<>();
|
||||
for (V vertex : edges.keySet())
|
||||
graph.addVertex(vertex);
|
||||
|
||||
edges.forEach((source, map) -> map.forEach((destination, weight) -> graph.addEdge(destination, source, weight)));
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<V> topologicalSort() throws UnsupportedOperationException {
|
||||
if (!isDAG())
|
||||
throw new UnsupportedOperationException(NOT_DAG);
|
||||
return getTarjan().getTopologicalSort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Collection<V>> stronglyConnectedComponents() {
|
||||
return getTarjan().getSCC();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graph<V, W> subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException {
|
||||
Graph<V, W> sub = new MapGraph<>();
|
||||
Set<V> vertices = new HashSet<>();
|
||||
|
||||
int finalDepth = depth > 0 ? depth : 0;
|
||||
VisitStrategy<V, W> strategy = (graph, sourceVertex, visit) -> {
|
||||
int currentDepth = 0;
|
||||
final LinkedList<Map.Entry<V, Integer>> toVisitChildren = new LinkedList<>();
|
||||
toVisitChildren.add(new AbstractMap.SimpleEntry<>(sourceVertex, 0));
|
||||
vertices.add(source);
|
||||
|
||||
while (!toVisitChildren.isEmpty() && currentDepth + 1 <= finalDepth) {
|
||||
final Map.Entry<V, Integer> current = toVisitChildren.removeFirst();
|
||||
currentDepth = current.getValue() + 1;
|
||||
final int finalCurrentDepth = currentDepth;
|
||||
|
||||
for (V child : graph.getChildren(current.getKey()))
|
||||
if (!vertices.contains(child)) {
|
||||
toVisitChildren.addLast(new AbstractMap.SimpleEntry<>(child, finalCurrentDepth));
|
||||
vertices.add(child);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
strategy.visit(this, source, null);
|
||||
|
||||
sub.addAllVertices(vertices);
|
||||
for (V vertex : vertices)
|
||||
getEdgesOut(vertex).forEach((edge) -> {
|
||||
try {
|
||||
sub.addEdge(edge);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
});
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graph<V, W> subGraph(final Object marker) {
|
||||
final Graph<V, W> graph = new MapGraph<>();
|
||||
final Set<V> allVertices = new HashSet<>();
|
||||
|
||||
marked.forEach((vertex, mark) -> {
|
||||
if (mark.contains(marker) || (marker == null && !mark.isEmpty()))
|
||||
allVertices.add(vertex);
|
||||
});
|
||||
|
||||
if (marker == null) {
|
||||
Collection<V> toAdd = graph.vertices();
|
||||
toAdd.removeAll(allVertices);
|
||||
allVertices.clear();
|
||||
allVertices.addAll(toAdd);
|
||||
}
|
||||
|
||||
graph.addAllVertices(allVertices);
|
||||
for (V vertex : graph.vertices())
|
||||
getEdgesOut(vertex).forEach((edge) -> {
|
||||
try {
|
||||
graph.addEdge(edge);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
});
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Edge<V, W>> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
|
||||
checkNullAndExist(source);
|
||||
checkNullAndExist(destination);
|
||||
|
||||
Dijkstra<V, W> dijkstra = getDijkstra(source);
|
||||
List<Edge<V, W>> path = dijkstra.getLastDistance().get(destination);
|
||||
if (path == null)
|
||||
throw new UnsupportedOperationException(NOT_CONNECTED);
|
||||
return new ArrayList<>(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<V, List<Edge<V, W>>> distance(V source) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(source);
|
||||
return new HashMap<>(getDijkstra(source).getLastDistance());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return edges.keySet().iterator();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple function that set all the memory vars at null if the graph changed
|
||||
*/
|
||||
private void graphChanged() {
|
||||
tarjan = null;
|
||||
dijkstra = null;
|
||||
}
|
||||
|
||||
private Dijkstra<V, W> getDijkstra(V source) {
|
||||
if (dijkstra == null)
|
||||
dijkstra = new HashMap<>();
|
||||
if (dijkstra.get(source) == null) {
|
||||
Dijkstra<V, W> newDijkstra = new Dijkstra<>();
|
||||
newDijkstra.visit(this, source, null);
|
||||
dijkstra.put(source, newDijkstra);
|
||||
}
|
||||
|
||||
return dijkstra.get(source);
|
||||
}
|
||||
|
||||
private Tarjan<V, W> getTarjan() {
|
||||
if (tarjan == null) {
|
||||
tarjan = new Tarjan<>();
|
||||
tarjan.visit(this, null, null);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
package berack96.sim.util.graph;
|
||||
|
||||
import berack96.sim.util.graph.visit.Dijkstra;
|
||||
import berack96.sim.util.graph.visit.Tarjan;
|
||||
import berack96.sim.util.graph.visit.VisitInfo;
|
||||
import berack96.sim.util.graph.visit.VisitStrategy;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* 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>
|
||||
* 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 <W> the weight of the edges
|
||||
* @author Berack96
|
||||
*/
|
||||
public class MapGraph<V, W extends Number> implements Graph<V, W> {
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* If an edge exist, then it's weight is returned
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
private Map<V, Dijkstra<V, W>> dijkstra = null;
|
||||
|
||||
@Override
|
||||
public boolean isCyclic() {
|
||||
return stronglyConnectedComponents().size() != numberOfVertices();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDAG() {
|
||||
return !isCyclic();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vertex<V> getVertex(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
return new Vertex<>(this, vertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addVertex(V vertex) throws NullPointerException {
|
||||
checkNull(vertex);
|
||||
graphChanged();
|
||||
edges.put(vertex, new HashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addVertexIfAbsent(V vertex) throws NullPointerException {
|
||||
if (contains(vertex))
|
||||
return false;
|
||||
addVertex(vertex);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAllVertices(Collection<V> vertices) throws NullPointerException {
|
||||
checkNull(vertices);
|
||||
vertices.forEach(this::addVertexIfAbsent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeVertex(V vertex) throws NullPointerException {
|
||||
if (contains(vertex)) {
|
||||
graphChanged();
|
||||
edges.remove(vertex);
|
||||
edges.forEach((v, map) -> map.remove(vertex));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllVertex() {
|
||||
graphChanged();
|
||||
edges.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(V vertex) throws NullPointerException {
|
||||
checkNull(vertex);
|
||||
return edges.containsKey(vertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
checkNull(mark);
|
||||
|
||||
Set<Object> set = marked.computeIfAbsent(vertex, (m) -> new HashSet<>());
|
||||
set.add(mark);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
checkNull(mark);
|
||||
marked.get(vertex).remove(mark);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unMark(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
marked.get(vertex).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> getMarkedWith(Object mark) throws NullPointerException {
|
||||
checkNull(mark);
|
||||
Collection<V> ret = new HashSet<V>();
|
||||
marked.forEach((v, set) -> {
|
||||
if(set.contains(mark))
|
||||
ret.add(v);
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Object> getMarks(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
return marked.computeIfAbsent(vertex, (m) -> new HashSet<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unMarkAll(Object mark) {
|
||||
checkNull(mark);
|
||||
marked.forEach((v, m) -> m.remove(mark));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unMarkAll() {
|
||||
marked.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex1);
|
||||
checkNullAndExist(vertex2);
|
||||
checkNull(weight);
|
||||
|
||||
graphChanged();
|
||||
return edges.get(vertex1).put(vertex2, weight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public W addEdge(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException {
|
||||
return addEdge(edge.getSource(), edge.getDestination(), edge.getWeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException {
|
||||
addVertexIfAbsent(vertex1);
|
||||
addVertexIfAbsent(vertex2);
|
||||
return addEdge(vertex1, vertex2, weight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public W addEdgeAndVertices(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException {
|
||||
return addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAllEdges(Collection<Edge<V, W>> edges) throws NullPointerException {
|
||||
edges.forEach((edge) -> addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public W getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex1);
|
||||
checkNullAndExist(vertex2);
|
||||
|
||||
return edges.get(vertex1).get(vertex2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex1);
|
||||
checkNullAndExist(vertex2);
|
||||
|
||||
graphChanged();
|
||||
edges.get(vertex1).remove(vertex2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
|
||||
graphChanged();
|
||||
edges.forEach((v, map) -> map.remove(vertex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
|
||||
graphChanged();
|
||||
edges.put(vertex, new HashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
removeVertex(vertex);
|
||||
addVertex(vertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllEdge() {
|
||||
graphChanged();
|
||||
edges.forEach((v, map) -> map.clear());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException {
|
||||
return (contains(vertex1) && contains(vertex2)) && edges.get(vertex1).get(vertex2) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> vertices() {
|
||||
return new HashSet<>(edges.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Edge<V, W>> edges() {
|
||||
Set<Edge<V, W>> allEdges = new HashSet<>();
|
||||
edges.forEach((source, map) -> map.forEach((destination, weight) -> allEdges.add(new Edge<>(source, destination, weight))));
|
||||
return allEdges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Edge<V, W>> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
|
||||
Set<Edge<V, W>> set = new HashSet<>();
|
||||
edges.forEach((source, map) -> map.forEach((destination, weight) -> {
|
||||
if (destination.equals(vertex) || source.equals(vertex))
|
||||
set.add(new Edge<>(source, destination, weight));
|
||||
}));
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Edge<V, W>> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
Collection<Edge<V, W>> collection = new HashSet<>();
|
||||
edges.forEach((source, edge) -> {
|
||||
if (edge.get(vertex) != null)
|
||||
collection.add(new Edge<>(source, vertex, edge.get(vertex)));
|
||||
});
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Edge<V, W>> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
Collection<Edge<V, W>> collection = new HashSet<>();
|
||||
edges.get(vertex).forEach((dest, weight) -> collection.add(new Edge<>(vertex, dest, weight)));
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
|
||||
return new HashSet<>(edges.get(vertex).keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
|
||||
Set<V> set = new HashSet<>();
|
||||
edges.forEach((v, map) -> {
|
||||
if (map.containsKey(vertex)) set.add(v);
|
||||
});
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
|
||||
AtomicInteger sum = new AtomicInteger();
|
||||
edges.forEach((v, map) -> {
|
||||
if (map.containsKey(vertex))
|
||||
sum.getAndIncrement();
|
||||
});
|
||||
|
||||
return sum.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(vertex);
|
||||
|
||||
return edges.get(vertex).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int degree(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
return degreeIn(vertex) + degreeOut(vertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int numberOfVertices() {
|
||||
return edges.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int numberOfEdges() {
|
||||
AtomicInteger sum = new AtomicInteger(0);
|
||||
edges.forEach((v, map) -> sum.getAndAdd(map.size()));
|
||||
|
||||
return sum.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisitInfo<V> visit(V source, VisitStrategy<V, W> strategy, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
|
||||
return strategy.visit(this, source, visit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graph<V, W> transpose() {
|
||||
Graph<V, W> graph = new MapGraph<>();
|
||||
for (V vertex : edges.keySet())
|
||||
graph.addVertex(vertex);
|
||||
|
||||
edges.forEach((source, map) -> map.forEach((destination, weight) -> graph.addEdge(destination, source, weight)));
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<V> topologicalSort() throws UnsupportedOperationException {
|
||||
if (!isDAG())
|
||||
throw new UnsupportedOperationException(NOT_DAG);
|
||||
return getTarjan().getTopologicalSort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Collection<V>> stronglyConnectedComponents() {
|
||||
return getTarjan().getSCC();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graph<V, W> subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException {
|
||||
Graph<V, W> sub = new MapGraph<>();
|
||||
Set<V> vertices = new HashSet<>();
|
||||
|
||||
int finalDepth = depth > 0 ? depth : 0;
|
||||
VisitStrategy<V, W> strategy = (graph, sourceVertex, visit) -> {
|
||||
int currentDepth = 0;
|
||||
final LinkedList<Map.Entry<V, Integer>> toVisitChildren = new LinkedList<>();
|
||||
toVisitChildren.add(new AbstractMap.SimpleEntry<>(sourceVertex, 0));
|
||||
vertices.add(source);
|
||||
|
||||
while (!toVisitChildren.isEmpty() && currentDepth + 1 <= finalDepth) {
|
||||
final Map.Entry<V, Integer> current = toVisitChildren.removeFirst();
|
||||
currentDepth = current.getValue() + 1;
|
||||
final int finalCurrentDepth = currentDepth;
|
||||
|
||||
for (V child : graph.getChildren(current.getKey()))
|
||||
if (!vertices.contains(child)) {
|
||||
toVisitChildren.addLast(new AbstractMap.SimpleEntry<>(child, finalCurrentDepth));
|
||||
vertices.add(child);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
strategy.visit(this, source, null);
|
||||
|
||||
sub.addAllVertices(vertices);
|
||||
for (V vertex : vertices)
|
||||
getEdgesOut(vertex).forEach((edge) -> {
|
||||
try {
|
||||
sub.addEdge(edge);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
});
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graph<V, W> subGraph(final Object marker) {
|
||||
final Graph<V, W> graph = new MapGraph<>();
|
||||
final Set<V> allVertices = new HashSet<>();
|
||||
|
||||
marked.forEach((vertex, mark) -> {
|
||||
if (mark.contains(marker) || (marker == null && !mark.isEmpty()))
|
||||
allVertices.add(vertex);
|
||||
});
|
||||
|
||||
if (marker == null) {
|
||||
Collection<V> toAdd = graph.vertices();
|
||||
toAdd.removeAll(allVertices);
|
||||
allVertices.clear();
|
||||
allVertices.addAll(toAdd);
|
||||
}
|
||||
|
||||
graph.addAllVertices(allVertices);
|
||||
for (V vertex : graph.vertices())
|
||||
getEdgesOut(vertex).forEach((edge) -> {
|
||||
try {
|
||||
graph.addEdge(edge);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
});
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Edge<V, W>> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
|
||||
checkNullAndExist(source);
|
||||
checkNullAndExist(destination);
|
||||
|
||||
Dijkstra<V, W> dijkstra = getDijkstra(source);
|
||||
List<Edge<V, W>> path = dijkstra.getLastDistance().get(destination);
|
||||
if (path == null)
|
||||
throw new UnsupportedOperationException(NOT_CONNECTED);
|
||||
return new ArrayList<>(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<V, List<Edge<V, W>>> distance(V source) throws NullPointerException, IllegalArgumentException {
|
||||
checkNullAndExist(source);
|
||||
return new HashMap<>(getDijkstra(source).getLastDistance());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return edges.keySet().iterator();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple function that set all the memory vars at null if the graph changed
|
||||
*/
|
||||
private void graphChanged() {
|
||||
tarjan = null;
|
||||
dijkstra = null;
|
||||
}
|
||||
|
||||
private Dijkstra<V, W> getDijkstra(V source) {
|
||||
if (dijkstra == null)
|
||||
dijkstra = new HashMap<>();
|
||||
if (dijkstra.get(source) == null) {
|
||||
Dijkstra<V, W> newDijkstra = new Dijkstra<>();
|
||||
newDijkstra.visit(this, source, null);
|
||||
dijkstra.put(source, newDijkstra);
|
||||
}
|
||||
|
||||
return dijkstra.get(source);
|
||||
}
|
||||
|
||||
private Tarjan<V, W> getTarjan() {
|
||||
if (tarjan == null) {
|
||||
tarjan = new Tarjan<>();
|
||||
tarjan.visit(this, null, null);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.util.function.Consumer;
|
||||
* @param <V> the vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class Vertex<V> {
|
||||
|
||||
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 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();
|
||||
return graph.visit(vertex, (VisitStrategy<V, Number>) strategy, visit);
|
||||
}
|
||||
|
||||
162
src/berack96/sim/util/graph/view/GraphInfo.java
Normal file
162
src/berack96/sim/util/graph/view/GraphInfo.java
Normal 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());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
27
src/berack96/sim/util/graph/view/GraphListener.java
Normal file
27
src/berack96/sim/util/graph/view/GraphListener.java
Normal 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();
|
||||
}
|
||||
174
src/berack96/sim/util/graph/view/GraphPanel.java
Normal file
174
src/berack96/sim/util/graph/view/GraphPanel.java
Normal 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));
|
||||
}
|
||||
|
||||
}
|
||||
65
src/berack96/sim/util/graph/view/GraphWindow.java
Normal file
65
src/berack96/sim/util/graph/view/GraphWindow.java
Normal 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;
|
||||
}
|
||||
}
|
||||
29
src/berack96/sim/util/graph/view/GraphicalView.java
Normal file
29
src/berack96/sim/util/graph/view/GraphicalView.java
Normal 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);
|
||||
}
|
||||
112
src/berack96/sim/util/graph/view/VisitListener.java
Normal file
112
src/berack96/sim/util/graph/view/VisitListener.java
Normal 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) {
|
||||
}
|
||||
}
|
||||
22
src/berack96/sim/util/graph/view/edge/EdgeComponent.java
Normal file
22
src/berack96/sim/util/graph/view/edge/EdgeComponent.java
Normal 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);
|
||||
}
|
||||
}
|
||||
24
src/berack96/sim/util/graph/view/edge/EdgeIntListener.java
Normal file
24
src/berack96/sim/util/graph/view/edge/EdgeIntListener.java
Normal 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]+", ""));
|
||||
}
|
||||
}
|
||||
128
src/berack96/sim/util/graph/view/edge/EdgeListener.java
Normal file
128
src/berack96/sim/util/graph/view/edge/EdgeListener.java
Normal 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) {
|
||||
}
|
||||
}
|
||||
109
src/berack96/sim/util/graph/view/edge/EdgeView.java
Normal file
109
src/berack96/sim/util/graph/view/edge/EdgeView.java
Normal 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;
|
||||
}
|
||||
}
|
||||
59
src/berack96/sim/util/graph/view/stuff/Arrow.java
Normal file
59
src/berack96/sim/util/graph/view/stuff/Arrow.java
Normal 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));
|
||||
}
|
||||
}
|
||||
15
src/berack96/sim/util/graph/view/vertex/VertexComponent.java
Normal file
15
src/berack96/sim/util/graph/view/vertex/VertexComponent.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
91
src/berack96/sim/util/graph/view/vertex/VertexListener.java
Normal file
91
src/berack96/sim/util/graph/view/vertex/VertexListener.java
Normal 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) {
|
||||
}
|
||||
}
|
||||
42
src/berack96/sim/util/graph/view/vertex/VertexView.java
Normal file
42
src/berack96/sim/util/graph/view/vertex/VertexView.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
VisitInfo<V> info = new VisitInfo<>(source);
|
||||
Queue<QueueEntry> queue = new PriorityQueue<>();
|
||||
Map<V, Integer> dist = new HashMap<>();
|
||||
Map<V, Double> dist = new HashMap<>();
|
||||
Map<V, V> prev = new HashMap<>();
|
||||
|
||||
this.source = source;
|
||||
dist.put(source, 0); // Initialization
|
||||
queue.add(new QueueEntry(source, 0));
|
||||
dist.put(source, 0.0); // Initialization
|
||||
queue.add(new QueueEntry(source, 0.0));
|
||||
|
||||
while (!queue.isEmpty()) { // The main loop
|
||||
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) -> {
|
||||
V child = edge.getDestination();
|
||||
info.setDiscovered(child);
|
||||
int alt = dist.get(u.entry) + edge.getWeight().intValue();
|
||||
Integer distCurrent = dist.get(child);
|
||||
double alt = dist.get(u.entry) + edge.getWeight().doubleValue();
|
||||
Double distCurrent = dist.get(child);
|
||||
if (distCurrent == null || alt < distCurrent) {
|
||||
dist.put(child, alt);
|
||||
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> {
|
||||
final V entry;
|
||||
final Integer weight;
|
||||
final Double weight;
|
||||
|
||||
QueueEntry(V entry, Integer weight) {
|
||||
QueueEntry(V entry, Double weight) {
|
||||
this.entry = entry;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
try {
|
||||
return ((QueueEntry) obj).entry.equals(entry);
|
||||
@@ -101,7 +102,8 @@ public class Dijkstra<V, W extends Number> implements VisitDistance<V, W> {
|
||||
|
||||
@Override
|
||||
public int compareTo(QueueEntry queueEntry) {
|
||||
return this.weight - queueEntry.weight;
|
||||
double ret = this.weight - queueEntry.weight;
|
||||
return ret==0? 0: ret<0? -1:1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,5 +25,5 @@ public interface VisitDistSourceDest<V, W extends Number> extends VisitStrategy<
|
||||
* @throws NullPointerException if one of the vertex is null
|
||||
* @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;
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
package berack96.sim.util.graph.visit;
|
||||
|
||||
import berack96.sim.util.graph.Graph;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* This class is used for define some strategy for the visit of a graph.
|
||||
*
|
||||
* @param <V> The Object that represent a vertex
|
||||
* @param <W> The Object that represent the edge (more specifically the weight of the edge)
|
||||
* @author Berack96
|
||||
*/
|
||||
public interface VisitStrategy<V, W extends Number> {
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* 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 source the source of the visit
|
||||
* @param visit the function to apply at each vertex when they are visited
|
||||
* @return an info of the view
|
||||
* @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 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;
|
||||
}
|
||||
package berack96.sim.util.graph.visit;
|
||||
|
||||
import berack96.sim.util.graph.Graph;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* This class is used for define some strategy for the visit of a graph.
|
||||
*
|
||||
* @param <V> The Object that represent a vertex
|
||||
* @param <W> The Object that represent the edge (more specifically the weight of the edge)
|
||||
* @author Berack96
|
||||
*/
|
||||
public interface VisitStrategy<V, W extends Number> {
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* 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 source the source of the visit
|
||||
* @param visit the function to apply at each vertex when they are visited
|
||||
* @return an info of the view
|
||||
* @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 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;
|
||||
}
|
||||
|
||||
@@ -918,6 +918,7 @@ public class TestGraph {
|
||||
shouldThrow(nullException, () -> graph.unMark("1", null));
|
||||
shouldThrow(nullException, () -> graph.unMark(null, "blue"));
|
||||
shouldThrow(nullException, () -> graph.unMarkAll(null));
|
||||
shouldThrow(nullException, () -> graph.getMarkedWith(null));
|
||||
|
||||
shouldThrow(notException, () -> graph.mark("324", "yellow"));
|
||||
shouldThrow(notException, () -> graph.unMark("32423"));
|
||||
@@ -966,30 +967,54 @@ public class TestGraph {
|
||||
graph.mark("2", "mark");
|
||||
graph.mark("3", "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("3"), "mark2");
|
||||
shouldContain(graph.getMarkedWith("mark"), "2", "1");
|
||||
shouldContain(graph.getMarkedWith("mark2"), "1", "3");
|
||||
shouldContain(graph.getMarkedWith(3), "1");
|
||||
|
||||
graph.unMark("1", "mark");
|
||||
shouldContain(graph.getMarks("1"), "mark2");
|
||||
shouldContain(graph.getMarks("1"), "mark2", 3);
|
||||
shouldContain(graph.getMarks("2"), "mark");
|
||||
shouldContain(graph.getMarks("3"), "mark2");
|
||||
shouldContain(graph.getMarkedWith("mark"), "2");
|
||||
shouldContain(graph.getMarkedWith("mark2"), "1", "3");
|
||||
shouldContain(graph.getMarkedWith(3), "1");
|
||||
|
||||
graph.unMarkAll("mark2");
|
||||
shouldContain(graph.getMarks("1"));
|
||||
shouldContain(graph.getMarks("1"), 3);
|
||||
shouldContain(graph.getMarks("2"), "mark");
|
||||
shouldContain(graph.getMarks("3"));
|
||||
|
||||
shouldContain(graph.getMarkedWith("mark"), "2");
|
||||
shouldContain(graph.getMarkedWith("mark2"));
|
||||
shouldContain(graph.getMarkedWith(3), "1");
|
||||
|
||||
graph.unMark("1", "mark");
|
||||
graph.unMark("2", "mark2");
|
||||
shouldContain(graph.getMarks("1"));
|
||||
shouldContain(graph.getMarks("1"), 3);
|
||||
shouldContain(graph.getMarks("2"), "mark");
|
||||
shouldContain(graph.getMarks("3"));
|
||||
shouldContain(graph.getMarkedWith("mark"), "2");
|
||||
shouldContain(graph.getMarkedWith("mark2"));
|
||||
shouldContain(graph.getMarkedWith(3), "1");
|
||||
|
||||
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("2"));
|
||||
shouldContain(graph.getMarks("3"));
|
||||
shouldContain(graph.getMarkedWith("mark"));
|
||||
shouldContain(graph.getMarkedWith("mark2"));
|
||||
shouldContain(graph.getMarkedWith(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user