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;
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);
}
}

View File

@@ -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);
}

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 {
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;
}
}
}

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 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;
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;
}

View File

@@ -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