From cdc183754fb36c181079dfc6c1cb0e8b30b88c5f Mon Sep 17 00:00:00 2001 From: Giacomo Bertolazzi <20015159@studenti.uniupo.it> Date: Tue, 26 Feb 2019 21:58:53 +0100 Subject: [PATCH] Graph View * added simple Window for graph play * added new function to interface * all tests passes * end graph --- .gitignore | Bin 4620 -> 2360 bytes src/berack96/sim/util/graph/Graph.java | 1064 +++++++++-------- src/berack96/sim/util/graph/MapGraph.java | 988 +++++++-------- src/berack96/sim/util/graph/Vertex.java | 4 +- .../sim/util/graph/view/GraphInfo.java | 162 +++ .../sim/util/graph/view/GraphListener.java | 27 + .../sim/util/graph/view/GraphPanel.java | 174 +++ .../sim/util/graph/view/GraphWindow.java | 65 + .../sim/util/graph/view/GraphicalView.java | 29 + .../sim/util/graph/view/VisitListener.java | 112 ++ .../util/graph/view/edge/EdgeComponent.java | 22 + .../util/graph/view/edge/EdgeIntListener.java | 24 + .../util/graph/view/edge/EdgeListener.java | 128 ++ .../sim/util/graph/view/edge/EdgeView.java | 109 ++ .../sim/util/graph/view/stuff/Arrow.java | 59 + .../graph/view/vertex/VertexComponent.java | 15 + .../graph/view/vertex/VertexIntListener.java | 24 + .../graph/view/vertex/VertexListener.java | 91 ++ .../util/graph/view/vertex/VertexView.java | 42 + .../sim/util/graph/visit/Dijkstra.java | 20 +- .../util/graph/visit/VisitDistSourceDest.java | 2 +- .../sim/util/graph/visit/VisitStrategy.java | 60 +- test/berack96/test/sim/TestGraph.java | 35 +- 23 files changed, 2203 insertions(+), 1053 deletions(-) create mode 100644 src/berack96/sim/util/graph/view/GraphInfo.java create mode 100644 src/berack96/sim/util/graph/view/GraphListener.java create mode 100644 src/berack96/sim/util/graph/view/GraphPanel.java create mode 100644 src/berack96/sim/util/graph/view/GraphWindow.java create mode 100644 src/berack96/sim/util/graph/view/GraphicalView.java create mode 100644 src/berack96/sim/util/graph/view/VisitListener.java create mode 100644 src/berack96/sim/util/graph/view/edge/EdgeComponent.java create mode 100644 src/berack96/sim/util/graph/view/edge/EdgeIntListener.java create mode 100644 src/berack96/sim/util/graph/view/edge/EdgeListener.java create mode 100644 src/berack96/sim/util/graph/view/edge/EdgeView.java create mode 100644 src/berack96/sim/util/graph/view/stuff/Arrow.java create mode 100644 src/berack96/sim/util/graph/view/vertex/VertexComponent.java create mode 100644 src/berack96/sim/util/graph/view/vertex/VertexIntListener.java create mode 100644 src/berack96/sim/util/graph/view/vertex/VertexListener.java create mode 100644 src/berack96/sim/util/graph/view/vertex/VertexView.java diff --git a/.gitignore b/.gitignore index c2b7620a8d676dbd11ee7603d326654cab62645d..408ed1e56f7f88e8bea84a1c8e9e7bc4ef6e8cd8 100644 GIT binary patch literal 2360 zcmZuzOK;;g5WXAme;{^&B6Tde?QV-Ux5P6}vY9v{nyO_7sceQ5l%BMOiO{b=8zz z*Y(^=5;gOZlVtuyS%U{gI;$jGhA5R~+(`Bfiq>j8de5F<0}hOUD^`Y3`>WYZr9jV_ zk9BQb$e$q;Z6>*})vOdVFj@37?t&5;{Oq@1{`mdl$3H*(H6D@6uO3|HYY=Lq1oI(o zHsevQB=Ff}GJCb|#n)UwzOVE^9=Lz;XR_$|7IGhWP#$BQA4??y*2tX#Kk(#B&=`?Y z`2+?W4rMklcPfzyE) z>m7z0o@gt07xRdU|E_O)pJy=0#_&psGig+};MEojJUftu;XAeENQ}O+PmW6sgIu?* z;LKDjY;=ONg|F3VIrGSOVA!h)CF3#JOjSe;QuD51f4TC)O}1L1)BIBk=uc*6vb9$4F+oUyM4saat|ncAde z$kQOxoZb`MDPTvYgK&~7qT}JU(@89vKk^rZPE`2Fim0^ACfIh;ezaz5+19 zz|4FRG8(GWpzAL4PW|Ej>H4U^gXZ2VZpbc6x%Ld1N9T$8b8VlagQHaf%|Wec5WBIZ z^yZHHQa3c1oie+?Dt@$a4wd|jDB5Z*ddoHJiAZZ7To2LUJV$(Lz)eHbJCLV7oSbfW z;c!ZZ29)NVR5%G8+E3sER;Z9Ehc40DdxGx~PdeNWD-%~2t;`Skua^vWn&MjhiyaeO zl!>ng7p=t4T57Cm)2vZLy!cr891W?Pc}p*RRuDLCgYl?P2D=oh8hFaE5CZti@3x^0sF=bO$Sx z-*n=RG+$^z=>}Ifl3KENcS5TghfVWnM9FY-%+~hBt={_x3FW!{eQ7& zLTL?MV#IOQIYTB)^16|*>joeh&u>@#)p0SO%`Yz*x&v+NcDgBB86@_AVHjf`VjWqV&!(w)u*&9oB}@i~jVs?{kh1 zJB!zl%Cgy+Io}`endRSqwo;KgX_ju&INhXDt3y34(yms6beX2McaZkdzMg*7`GNKp zqM7J(De70+J5I}Vn(ot)ag1%Rn=aBk^+Y?9tZUnQqn)|w8%YA3U2%0pIe4Jyq<4}s zlNQe3=JhAJj)Nt?;59Z4JN7x^u5m3qdPg`^G@p|S0JpT12$ zr61D|`uv<;r(Y5lhL!I{gN^8&>uD(7GvhC`gTH6`K;70ZksCkHq6rlFAJ>m5L$t>Q{HXU0P=sD@@so2fm&o8q*IXCjS5X|MaaUMpq#B$~ZOJD;wGk-C&TUMn)g zS~S* z^~hNL5AuML)bzE^Mq1G~c?Z3+%G}bPTSdJV=FwP^-9IqCP?R_ULx=Vo{fG^Lz}I?$ zi#o(lII8YxYgMH8^4(GMTIY0Vp?I9>w~9cAUyOG5})ErE6yM3p^(OSb+Tt)9Mkq6Mb&^fn}$f@tlEb zixr-QPO2R$I+jkK>b#Wxg{+84^^~XmT6G3mhV(<-HI8)ySeJnH6Z4YVr3OP^s}|QB z0YYj=esI$X%QBn7^VIA8@huan)h+Y22+Uo!V0ALXmqSf&9wPy^|#hj$vL%ke#6+5FSj*Eskr;VjfHdl zGBMq9dMTdmF!@mUdjXVQRtcZ#5806`>m7ApKI6etNsR4iWeP3H|NQHc5S;**4X~imIn`2Tw=&O3YNtaJcIw7yG^%;bp zH>xkXJpkAdi@Q46H2rtr5Qu&-3_|0*Gku761C!X<5^qw%oXLnMZ5k6T9?~X z)<7ldG5WHpmvXwVMO#&|cj$T*`7N!vyXml)H!XFe|Ch!sFCQ%GTl_#1{zJkq|5b?T N-Om1Rz|5hl{sY^=@%aD% diff --git a/src/berack96/sim/util/graph/Graph.java b/src/berack96/sim/util/graph/Graph.java index 44e2747..5ae7afa 100644 --- a/src/berack96/sim/util/graph/Graph.java +++ b/src/berack96/sim/util/graph/Graph.java @@ -1,518 +1,546 @@ -package berack96.sim.util.graph; - -import berack96.sim.util.graph.visit.VisitInfo; -import berack96.sim.util.graph.visit.VisitStrategy; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -/** - * An interface for the graphs.
- * This interface is used for the graphs with Directed edges.
- * A directed edge between V1 and V2 is an edge that has V1 as source and V2 as destination.
- * - * @param The Object that represent a vertex - * @param The Object that represent the edge (more specifically the weight of the edge) - * @author Berack96 - */ -public interface Graph extends Iterable { - - String NOT_DAG = "The graph is not a DAG"; - String NOT_CONNECTED = "The source vertex doesn't have a path that reach the destination"; - String PARAM_NULL = "The parameter must not be null"; - String VERTEX_NOT_CONTAINED = "The vertex must be contained in the graph"; - - /** - * Tells if the graph has some cycle.
- * A cycle is detected if visiting the graph G starting from V1 (that is any of the vertex of G), - * the visit can return to V1 in any point. - * - * @return true if has cycle, false otherwise - */ - boolean isCyclic(); - - /** - * Tells if the graph has the property of DAG (Directed Acyclic Graph).
- * A graph is a DAG only if absent of any cycle. ( see {@link #isCyclic()} ) - * - * @return true if is a DAG, false otherwise - */ - boolean isDAG(); - - /** - * Get an instance of the vertex linked with this graph.
- * For more info see {@link Vertex} - * - * @param vertex the vertex - * @return a vertex - * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if the vertex is not contained in the graph - */ - Vertex getVertex(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * Add the vertex to the graph. If it's already in the graph it will be replaced.
- * Of course the vertex added will have no edge to any other vertex nor form any other vertex. - * - * @param vertex the vertex to add - * @throws NullPointerException if the vertex is null - */ - void addVertex(V vertex) throws NullPointerException; - - /** - * Add the specified vertex to the graph only if the graph doesn't contains it.
- * The graph contains a vertex only if the method {@link #contains(V)} returns true. - * - * @param vertex the vertex to add - * @return true if the vertex is added, false if the graph contains the vertex and therefore the new one is not added - * @throws NullPointerException if the vertex is null - */ - boolean addVertexIfAbsent(V vertex) throws NullPointerException; - - /** - * Add all the vertices contained in the collection to the graph.
- * If a vertex is contained in the collection and in the graph is ignored and it will not be replaced.
- * Null vertices will be ignored and they will not be added to the graph. - * - * @param vertices a collection of the vertices to add - * @throws NullPointerException if the set is null - */ - void addAllVertices(Collection vertices) throws NullPointerException; - - /** - * Remove the selected vertex from the graph.
- * After this method's call the vertex will be no longer present in the graph, and nether all his edges. - * - * @param vertex the vertex to remove - * @throws NullPointerException if the vertex is null - */ - void removeVertex(V vertex) throws IllegalArgumentException; - - /** - * Remove all the vertex contained in the graph.
- * After this method's call the graph will be empty; no vertices nor edges. - */ - void removeAllVertex(); - - /** - * Check if the vertex passed is contained in the graph or not.
- * The vertex V1 is contained in the graph G, if and only if:
- * exist V2 in G such that V2.equals(V1) - * - * @param vertex the vertex to check - * @return true if the vertex is contained, false otherwise - * @throws NullPointerException if the vertex is null - */ - boolean contains(V vertex) throws NullPointerException; - - /** - * Add to the specified vertex the mark passed.
- * A vertex can have multiple marker. - * - * @param vertex the vertex to mark - * @param mark the mark to add - * @throws NullPointerException if one of the param is null - * @throws IllegalArgumentException if the vertex is not contained in the graph - */ - void mark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException; - - /** - * Remove the selected mark from the vertex.
- * - * @param vertex the vertex where remove the mark - * @param mark the mark to remove - * @throws NullPointerException if a param is null - * @throws IllegalArgumentException if the vertex is not contained in the graph - */ - void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException; - - /** - * Unmark the vertex selected.
- * After this call the vertex will not have any marked object to himself. - * - * @param vertex the vertex - * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if the vertex is not contained in the graph - */ - void unMark(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * Get all the marker of this vertex.
- * If the vertex doesn't have any mark, then it will return an empty set.
- * Note: this set is linked to the marked vertex, so any changes to the set returned are reflected to the graph. - * - * @param vertex the vertex - * @return all the mark to the vertex or an empty collection if none - * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if the vertex is not contained in the graph - */ - Collection getMarks(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * Remove the selected mark from all the vertices - * - * @param mark the mark to remove - * @throws NullPointerException if the mark is null - */ - void unMarkAll(Object mark) throws NullPointerException; - - /** - * Remove all the marker to all the vertex.
- * After this call the {@link #getMarks(Object)} applied to any vertex will return an empty set - */ - void unMarkAll(); - - /** - * Add an edge between the two vertex.
- * The edge will be created from the vertex V1 and the vertex V2
- * This method will overwrite any existing edge between the two vertex.
- * If there was a previous edge then it is returned - * - * @param vertex1 a vertex of the graph - * @param vertex2 a vertex of the graph - * @param weight the weight of the edge - * @return null or the previous weight of the edge if there was already one - * @throws NullPointerException if one of the parameter is null - * @throws IllegalArgumentException if one of the vertex is not contained in the graph - */ - W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException; - - /** - * Add an edge between the two vertex.
- * The edge will be created from the vertex source of the edge and the vertex destination of it
- * This method will overwrite any existing edge between the two vertex.
- * If there was a previous edge then it is returned - * - * @param edge the edge to add - * @return null or the previous weight of the edge if there was already one - * @throws NullPointerException if one of the parameter is null - * @throws IllegalArgumentException if one of the vertex is not contained in the graph - */ - W addEdge(Edge edge) throws NullPointerException, IllegalArgumentException; - - /** - * This particular function add an edge to the graph.
- * If one of the two, or both vertices aren't contained in the graph, then the vertices will be added.
- * The edge will be created from the vertex V1 and the vertex V2
- * This method will overwrite any existing edge between the two vertex.
- * If there was a previous edge then it is returned - * - * @param vertex1 a vertex of the graph - * @param vertex2 a vertex of the graph - * @param weight the weight of the edge - * @return null or the previous weight of the edge if there was already one - * @throws NullPointerException if one of the parameter is null - */ - W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException; - - /** - * This particular function add an edge to the graph.
- * If one of the two, or both vertices of the edge aren't contained in the graph, then the vertices will be added.
- * The edge will be created from the vertex source of the edge and the vertex destination of it
- * This method will overwrite any existing edge between the two vertex.
- * If there was a previous edge then it is returned - * - * @param edge the edge to add - * @return null or the previous weight of the edge if there was already one - * @throws NullPointerException if one of the parameter is null - */ - W addEdgeAndVertices(Edge edge) throws NullPointerException, IllegalArgumentException; - - /** - * Add all the edges of the collection to the graph.
- * If one of the two, or both vertices aren't contained in the graph, then the vertices will be added.
- * Any null edges will be ignored.
- * This method will overwrite any existing edge between the two vertex. - * - * @param edges the edges to add - * @throws NullPointerException if the set is null - */ - void addAllEdges(Collection> edges) throws NullPointerException; - - /** - * Get the weight of the selected edge.
- * If the edge doesn't exist, then null is returned - * - * @param vertex1 a vertex of the graph - * @param vertex2 a vertex of the graph - * @return the weight previously set, or null if the edge doesn't exist - * @throws NullPointerException if one of the parameters is null - * @throws IllegalArgumentException if one of the vertex is not contained in the graph - */ - W getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException; - - /** - * Remove the edge between the two vertex.
- * If the edge doesn't exist, then this call does nothing.
- * After this method's call it will be no longer possible to travel from V1 to V2, nether from V2 to V1. - * - * @param vertex1 a vertex of the graph - * @param vertex2 a vertex of the graph - * @throws NullPointerException if one of the parameters is null - * @throws IllegalArgumentException if one of the vertex is not contained in the graph - */ - void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException; - - /** - * Remove all the edges that goes in the vertex.
- * After this method's call it will be no longer possible travel to this vertex. - * - * @param vertex a vertex of the graph - * @throws NullPointerException if one of the parameters is null - * @throws IllegalArgumentException if one of the vertex is not contained in the graph - */ - void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * Remove all the edges that start from this vertex.
- * After this method's call it will be no longer possible travel to any vertex from this one. - * - * @param vertex a vertex of the graph - * @throws NullPointerException if one of the parameters is null - * @throws IllegalArgumentException if one of the vertex is not contained in the graph - */ - void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * Remove all edges form a particular vertex of the graph.
- * After this method's call the selected vertex will have 0 edges.
- * It will be no longer possible to reach this vertex from any other vertex, and vice versa. - * - * @param vertex a vertex of the graph - * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if one of the vertex is not contained in the graph - */ - void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * Remove all the edges of the graph.
- * After this method's call the graph will have only vertices, and no edge. - */ - void removeAllEdge(); - - /** - * Check if the edge between the two vertex passed is contained in the graph or not.
- * An edge between V1 and V2 is contained in the graph if and only if i can travel from V1 to V2.
- * If one of the two vertices is not contained in the graph, then even the edge isn't - * - * @param vertex1 a vertex of the graph - * @param vertex2 a vertex of the graph - * @return true if the edge is contained, false otherwise - * @throws NullPointerException if one of the parameters is null - */ - boolean containsEdge(V vertex1, V vertex2) throws NullPointerException; - - /** - * Get all the vertices in the graph.
- * If the graph doesn't contains vertices, it'll return an empty collection.
- * Note that this collection is completely different the object used for the vertices, so any modification to this collection will not change the graph. - * - * @return an array that include all the vertices - */ - Collection vertices(); - - /** - * Get all the edges in the graph.
- * If the graph doesn't contains edges, it'll return an empty collection.
- * Note that this collection is completely different than the object used for the edges, so any modification to this collection will not change the graph. - * - * @return a collection that include all the edges - */ - Collection> edges(); - - /** - * Retrieve all the edges of a particular vertex.
- * Note: the edges that are returned are the one that goes IN this vertex AND the edges that goes OUT of it. - * - * @param vertex a vertex of the graph - * @return a collection of edges - * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if the vertex is not contained in the graph - */ - Collection> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * Retrieve all the edges of a particular vertex.
- * Note: the edges that are returned are the one that have this vertex as destination and another as source. - * - * @param vertex a vertex of the graph - * @return a collection of edges - * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if the vertex is not contained in the graph - */ - Collection> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * Retrieve all the edges that goes OUT of a particular vertex.
- * Note: the edges that are returned are the one that have this vertex as source and another one as destination. - * - * @param vertex a vertex of the graph - * @return a collection of edges - * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if the vertex is not contained in the graph - */ - Collection> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * Get all the vertices that are children of the vertex passed as parameter.
- * The vertices V(0-N) that are 'children' of a vertex V1, are all the vertices that have an edge - * where V1 is the source of that edge. - * - * @param vertex the source vertex - * @return an array of vertices - * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if the vertex is not contained in the graph - */ - Collection getChildren(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * Get all the vertices that have the vertex passed as their child.
- * Basically is the opposite of {@link #getChildren(Object)} - * - * @param vertex a vertex of the graph - * @return an array of ancestors of the vertex - * @throws NullPointerException if one of the parameters is null - * @throws IllegalArgumentException if one of the vertex is not contained in the graph - */ - Collection getAncestors(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * Tells the degree of all the edges that goes to this vertex.
- * Basically, it'll count how many edge towards himself it have. - * - * @param vertex a vertex of the graph - * @return the in degree of the vertex - * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if the vertex is not contained in the graph - */ - int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * Tells the degree of all the edges that goes form this vertex to others.
- * Basically, it'll count how many edge towards any other vertex it have. - * - * @param vertex a vertex of the graph - * @return the out degree of the vertex - * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if the vertex is not contained in the graph - */ - int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * Tells the degree of a vertex.
- * The degree of a vertex is the quantity of edges that have.
- * Basically, it'll count how many edge it have. - * - * @param vertex a vertex of the graph - * @return the degree of the vertex - * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if the vertex is not contained in the graph - */ - int degree(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * Tells how many vertices are in the graph. - * - * @return the number of vertices - */ - int numberOfVertices(); - - /** - * Tells how many edges are in the graph. - * - * @return the number of edges - */ - int numberOfEdges(); - - /** - * Visit the graph accordingly to the strategy that is passed.
- * This method visit the graph from the source to all the vertex that are reachable form the source.
- * Some strategy can accept a source vertex null, because they visit all the graph anyway. - * - * @param source the source vertex of the visit - * @param strategy the algorithm for visiting the graph - * @param visit the function to apply at each vertex - * @throws NullPointerException if one of the parameter is null (except the consumer) - * @throws IllegalArgumentException if the vertex is not in the graph - */ - VisitInfo visit(V source, VisitStrategy strategy, Consumer visit) throws NullPointerException, IllegalArgumentException; - - /** - * This method will create a new Graph that is the transposed version of the original.
- * At the end of this method the new graph will have all the edges inverted in orientation.
- * Example: if the graph G contains (V1, V2, V3) as vertex, and (V1->V2, V3->V2) as edges, the transpose graph G' will contain (V1, V2, V3) as vertex, and (V2->V1, V2->V3) as edges. - * - * @return a transposed graph of this instance - */ - Graph transpose(); - - /** - * If the current graph is a DAG, it returns a topological sort of this graph.
- * A topological ordering of a graph is a linear ordering of its vertices such that for every directed edge (V1, V2) from vertex V1 to vertex V2, V2 comes before V1 in the ordering. - * - * @return an array containing the topological order of the vertices - * @throws UnsupportedOperationException if the graph is not a DAG (see {@link #isDAG()}) - */ - List topologicalSort() throws UnsupportedOperationException; - - /** - * The strongly connected components or diconnected components of an arbitrary directed graph form a partition into subgraphs that are themselves strongly connected. - * - * @return a collection containing the strongly connected components - */ - Collection> stronglyConnectedComponents(); - - /** - * Get a sub-graph of the current one based on the maximum depth that is given.
- * If the depth is 1 then only the source and it's children will be in the sub-graph.
- * If the depth is 2 then only the source, it's children and it's children of it's children will be in the sub-graph.
- * And so on.
- * Of course the sub-graph will contain the edges that link the vertices, but only the one selected. - * - * @param source the source vertex - * @param depth the maximum depth (must be a positive number, if >=0 a graph containing only the source is returned) - * @return a sub-graph of the original - * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if the vertex is not contained - */ - Graph subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException; - - /** - * Get a sub-graph of the current one with only the vertex marked with the selected marker.
- * Each vertex will have all his edges, but only the ones with the destination marked with the same marker.
- * If the marker is null then the returning graph will have all the vertices that are not marked by any marker.
- * If the graph doesn't contain any vertex with that marker then an empty graph is returned. - * - * @param marker the marker - * @return a sub-graph of the current graph - */ - Graph subGraph(Object marker); - - /** - * Get the minimum path from the source vertex to the destination vertex.
- * If the source vertex can't reach the destination, then an exception is thrown. - * - * @param source the vertex where to start - * @param destination the destination chosen - * @return an ordered list of edges from source to destination that represent the minimum path between the two vertices - * @throws NullPointerException if one of the parameter is null (except the consumer) - * @throws IllegalArgumentException if the vertex is not in the graph - * @throws UnsupportedOperationException if from the source it's not possible to reach the destination - */ - List> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException; - - /** - * Get the minimum path from the source vertex to all the possible reachable vertices. - * - * @param source the vertex where to start - * @return a map containing all the possible reachable vertices from the source and the minimum path to reach them - * @throws NullPointerException if one of the parameter is null (except the consumer) - * @throws IllegalArgumentException if the vertex is not in the graph - */ - Map>> distance(V source) throws NullPointerException, IllegalArgumentException; - - // TODO maybe -> STATIC saveOnFile(orString) INSTANCE loadFromFile(orString), but need JSON parser - // TODO maybe, but i don't think so... STATIC DISTANCE V* -> V* -} +package berack96.sim.util.graph; + +import berack96.sim.util.graph.visit.VisitInfo; +import berack96.sim.util.graph.visit.VisitStrategy; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +/** + * An interface for the graphs.
+ * This interface is used for the graphs with Directed edges.
+ * A directed edge between V1 and V2 is an edge that has V1 as source and V2 as destination.
+ * + * @param The Object that represent a vertex + * @param The Object that represent the edge (more specifically the weight of the edge) + * @author Berack96 + */ +public interface Graph extends Iterable { + + String NOT_DAG = "The graph is not a DAG"; + String NOT_CONNECTED = "The source vertex doesn't have a path that reach the destination"; + String PARAM_NULL = "The parameter must not be null"; + String VERTEX_NOT_CONTAINED = "The vertex must be contained in the graph"; + + /** + * Tells if the graph has some cycle.
+ * A cycle is detected if visiting the graph G starting from V1 (that is any of the vertex of G), + * the visit can return to V1 in any point. + * + * @return true if has cycle, false otherwise + */ + boolean isCyclic(); + + /** + * Tells if the graph has the property of DAG (Directed Acyclic Graph).
+ * A graph is a DAG only if absent of any cycle. ( see {@link #isCyclic()} ) + * + * @return true if is a DAG, false otherwise + */ + boolean isDAG(); + + /** + * Get an instance of the vertex linked with this graph.
+ * For more info see {@link Vertex} + * + * @param vertex the vertex + * @return a vertex + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + Vertex getVertex(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Add the vertex to the graph. If it's already in the graph it will be replaced.
+ * Of course the vertex added will have no edge to any other vertex nor form any other vertex. + * + * @param vertex the vertex to add + * @throws NullPointerException if the vertex is null + */ + void addVertex(V vertex) throws NullPointerException; + + /** + * Add the specified vertex to the graph only if the graph doesn't contains it.
+ * The graph contains a vertex only if the method {@link #contains(V)} returns true. + * + * @param vertex the vertex to add + * @return true if the vertex is added, false if the graph contains the vertex and therefore the new one is not added + * @throws NullPointerException if the vertex is null + */ + boolean addVertexIfAbsent(V vertex) throws NullPointerException; + + /** + * Add all the vertices contained in the collection to the graph.
+ * If a vertex is contained in the collection and in the graph is ignored and it will not be replaced.
+ * Null vertices will be ignored and they will not be added to the graph. + * + * @param vertices a collection of the vertices to add + * @throws NullPointerException if the set is null + */ + void addAllVertices(Collection vertices) throws NullPointerException; + + /** + * Remove the selected vertex from the graph.
+ * After this method's call the vertex will be no longer present in the graph, and nether all his edges. + * + * @param vertex the vertex to remove + * @throws NullPointerException if the vertex is null + */ + void removeVertex(V vertex) throws IllegalArgumentException; + + /** + * Remove all the vertex contained in the graph.
+ * After this method's call the graph will be empty; no vertices nor edges. + */ + void removeAllVertex(); + + /** + * Check if the vertex passed is contained in the graph or not.
+ * The vertex V1 is contained in the graph G, if and only if:
+ * exist V2 in G such that V2.equals(V1) + * + * @param vertex the vertex to check + * @return true if the vertex is contained, false otherwise + * @throws NullPointerException if the vertex is null + */ + boolean contains(V vertex) throws NullPointerException; + + /** + * Add to the specified vertex the mark passed.
+ * A vertex can have multiple marker. + * + * @param vertex the vertex to mark + * @param mark the mark to add + * @throws NullPointerException if one of the param is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + void mark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException; + + /** + * Remove the selected mark from the vertex.
+ * + * @param vertex the vertex where remove the mark + * @param mark the mark to remove + * @throws NullPointerException if a param is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException; + + /** + * Unmark the vertex selected.
+ * After this call the vertex will not have any marked object to himself. + * + * @param vertex the vertex + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + void unMark(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Get all the vertices that are marked with the specific mark passed.
+ * If there aren't vertices with that mark then it is returned an empty set.
+ * Note: depending on the implementation, modifying the returned collection
+ * could affect the graph behavior and the changes could be reflected to the graph. + * + * @param mark the mark + * @return all the vertices that are marked with that specific mark + * @throws NullPointerException if the mark is null + */ + Collection getMarkedWith(Object mark) throws NullPointerException; + + /** + * Get all the marker of this vertex.
+ * If the vertex doesn't have any mark, then it will return an empty set.
+ * Note: depending on the implementation, modifying the returned collection
+ * could affect the graph behavior and the changes could be reflected to the graph. + * + * @param vertex the vertex + * @return all the mark to the vertex or an empty collection if none + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + Collection getMarks(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Remove the selected mark from all the vertices + * + * @param mark the mark to remove + * @throws NullPointerException if the mark is null + */ + void unMarkAll(Object mark) throws NullPointerException; + + /** + * Remove all the marker to all the vertex.
+ * After this call the {@link #getMarks(Object)} applied to any vertex will return an empty set + */ + void unMarkAll(); + + /** + * Add an edge between the two vertex.
+ * The edge will be created from the vertex V1 and the vertex V2
+ * This method will overwrite any existing edge between the two vertex.
+ * If there was a previous edge then it is returned + * + * @param vertex1 a vertex of the graph + * @param vertex2 a vertex of the graph + * @param weight the weight of the edge + * @return null or the previous weight of the edge if there was already one + * @throws NullPointerException if one of the parameter is null + * @throws IllegalArgumentException if one of the vertex is not contained in the graph + */ + W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException; + + /** + * Add an edge between the two vertex.
+ * The edge will be created from the vertex source of the edge and the vertex destination of it
+ * This method will overwrite any existing edge between the two vertex.
+ * If there was a previous edge then it is returned + * + * @param edge the edge to add + * @return null or the previous weight of the edge if there was already one + * @throws NullPointerException if one of the parameter is null + * @throws IllegalArgumentException if one of the vertex is not contained in the graph + */ + W addEdge(Edge edge) throws NullPointerException, IllegalArgumentException; + + /** + * This particular function add an edge to the graph.
+ * If one of the two, or both vertices aren't contained in the graph, then the vertices will be added.
+ * The edge will be created from the vertex V1 and the vertex V2
+ * This method will overwrite any existing edge between the two vertex.
+ * If there was a previous edge then it is returned + * + * @param vertex1 a vertex of the graph + * @param vertex2 a vertex of the graph + * @param weight the weight of the edge + * @return null or the previous weight of the edge if there was already one + * @throws NullPointerException if one of the parameter is null + */ + W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException; + + /** + * This particular function add an edge to the graph.
+ * If one of the two, or both vertices of the edge aren't contained in the graph, then the vertices will be added.
+ * The edge will be created from the vertex source of the edge and the vertex destination of it
+ * This method will overwrite any existing edge between the two vertex.
+ * If there was a previous edge then it is returned + * + * @param edge the edge to add + * @return null or the previous weight of the edge if there was already one + * @throws NullPointerException if one of the parameter is null + */ + W addEdgeAndVertices(Edge edge) throws NullPointerException, IllegalArgumentException; + + /** + * Add all the edges of the collection to the graph.
+ * If one of the two, or both vertices aren't contained in the graph, then the vertices will be added.
+ * Any null edges will be ignored.
+ * This method will overwrite any existing edge between the two vertex. + * + * @param edges the edges to add + * @throws NullPointerException if the set is null + */ + void addAllEdges(Collection> edges) throws NullPointerException; + + /** + * Get the weight of the selected edge.
+ * If the edge doesn't exist, then null is returned + * + * @param vertex1 a vertex of the graph + * @param vertex2 a vertex of the graph + * @return the weight previously set, or null if the edge doesn't exist + * @throws NullPointerException if one of the parameters is null + * @throws IllegalArgumentException if one of the vertex is not contained in the graph + */ + W getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException; + + /** + * Remove the edge between the two vertex.
+ * If the edge doesn't exist, then this call does nothing.
+ * After this method's call it will be no longer possible to travel from V1 to V2, nether from V2 to V1. + * + * @param vertex1 a vertex of the graph + * @param vertex2 a vertex of the graph + * @throws NullPointerException if one of the parameters is null + * @throws IllegalArgumentException if one of the vertex is not contained in the graph + */ + void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException; + + /** + * Remove all the edges that goes in the vertex.
+ * After this method's call it will be no longer possible travel to this vertex. + * + * @param vertex a vertex of the graph + * @throws NullPointerException if one of the parameters is null + * @throws IllegalArgumentException if one of the vertex is not contained in the graph + */ + void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Remove all the edges that start from this vertex.
+ * After this method's call it will be no longer possible travel to any vertex from this one. + * + * @param vertex a vertex of the graph + * @throws NullPointerException if one of the parameters is null + * @throws IllegalArgumentException if one of the vertex is not contained in the graph + */ + void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Remove all edges form a particular vertex of the graph.
+ * After this method's call the selected vertex will have 0 edges.
+ * It will be no longer possible to reach this vertex from any other vertex, and vice versa. + * + * @param vertex a vertex of the graph + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if one of the vertex is not contained in the graph + */ + void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Remove all the edges of the graph.
+ * After this method's call the graph will have only vertices, and no edge. + */ + void removeAllEdge(); + + /** + * Check if the edge between the two vertex passed is contained in the graph or not.
+ * An edge between V1 and V2 is contained in the graph if and only if i can travel from V1 to V2.
+ * If one of the two vertices is not contained in the graph, then even the edge isn't + * + * @param vertex1 a vertex of the graph + * @param vertex2 a vertex of the graph + * @return true if the edge is contained, false otherwise + * @throws NullPointerException if one of the parameters is null + */ + boolean containsEdge(V vertex1, V vertex2) throws NullPointerException; + + /** + * Get all the vertices in the graph.
+ * If the graph doesn't contains vertices, it'll return an empty collection.
+ * Note: depending on the implementation, modifying the returned collection
+ * could affect the graph behavior and the changes could be reflected to the graph. + * + * @return an array that include all the vertices + */ + Collection vertices(); + + /** + * Get all the edges in the graph.
+ * If the graph doesn't contains edges, it'll return an empty collection.
+ * Note: depending on the implementation, modifying the returned collection
+ * could affect the graph behavior and the changes could be reflected to the graph. + * + * @return a collection that include all the edges + */ + Collection> edges(); + + /** + * Retrieve all the edges of a particular vertex.
+ * Note: the edges that are returned are the one that goes IN this vertex AND the edges that goes OUT of it.
+ * Note2: depending on the implementation, modifying the returned collection
+ * could affect the graph behavior and the changes could be reflected to the graph. + * + * @param vertex a vertex of the graph + * @return a collection of edges + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + Collection> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Retrieve all the edges of a particular vertex.
+ * Note: the edges that are returned are the one that have this vertex as destination and another as source.
+ * Note2: depending on the implementation, modifying the returned collection
+ * could affect the graph behavior and the changes could be reflected to the graph. + * + * @param vertex a vertex of the graph + * @return a collection of edges + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + Collection> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Retrieve all the edges that goes OUT of a particular vertex.
+ * Note: the edges that are returned are the one that have this vertex as source and another one as destination.
+ * Note2: depending on the implementation, modifying the returned collection
+ * could affect the graph behavior and the changes could be reflected to the graph. + * + * @param vertex a vertex of the graph + * @return a collection of edges + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + Collection> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Get all the vertices that are children of the vertex passed as parameter.
+ * The vertices V(0-N) that are 'children' of a vertex V1, are all the vertices that have an edge + * where V1 is the source of that edge.
+ * Note: depending on the implementation, modifying the returned collection
+ * could affect the graph behavior and the changes could be reflected to the graph. + * + * @param vertex the source vertex + * @return an array of vertices + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + Collection getChildren(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Get all the vertices that have the vertex passed as their child.
+ * Basically is the opposite of {@link #getChildren(Object)}
+ * Note: depending on the implementation, modifying the returned collection
+ * could affect the graph behavior and the changes could be reflected to the graph. + * + * @param vertex a vertex of the graph + * @return an array of ancestors of the vertex + * @throws NullPointerException if one of the parameters is null + * @throws IllegalArgumentException if one of the vertex is not contained in the graph + */ + Collection getAncestors(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Tells the degree of all the edges that goes to this vertex.
+ * Basically, it'll count how many edge towards himself it have. + * + * @param vertex a vertex of the graph + * @return the in degree of the vertex + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Tells the degree of all the edges that goes form this vertex to others.
+ * Basically, it'll count how many edge towards any other vertex it have. + * + * @param vertex a vertex of the graph + * @return the out degree of the vertex + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Tells the degree of a vertex.
+ * The degree of a vertex is the quantity of edges that have.
+ * Basically, it'll count how many edge it have. + * + * @param vertex a vertex of the graph + * @return the degree of the vertex + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + int degree(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Tells how many vertices are in the graph. + * + * @return the number of vertices + */ + int numberOfVertices(); + + /** + * Tells how many edges are in the graph. + * + * @return the number of edges + */ + int numberOfEdges(); + + /** + * Visit the graph accordingly to the strategy that is passed.
+ * This method visit the graph from the source to all the vertex that are reachable form the source.
+ * Some strategy can accept a source vertex null, because they visit all the graph anyway. + * + * @param source the source vertex of the visit + * @param strategy the algorithm for visiting the graph + * @param visit the function to apply at each vertex + * @throws NullPointerException if one of the parameter is null (except the consumer) + * @throws IllegalArgumentException if the vertex is not in the graph + */ + VisitInfo visit(V source, VisitStrategy strategy, Consumer visit) throws NullPointerException, IllegalArgumentException; + + /** + * This method will create a new Graph that is the transposed version of the original.
+ * At the end of this method the new graph will have all the edges inverted in orientation.
+ * Example: if the graph G contains (V1, V2, V3) as vertex, and (V1->V2, V3->V2) as edges, + * the transpose graph G' will contain (V1, V2, V3) as vertex, and (V2->V1, V2->V3) as edges. + * + * @return a transposed graph of this instance + */ + Graph transpose(); + + /** + * If the current graph is a DAG, it returns a topological sort of this graph.
+ * A topological ordering of a graph is a linear ordering of its vertices such that for + * every directed edge (V1, V2) from vertex V1 to vertex V2, V2 comes before V1 in the ordering. + * + * @return an array containing the topological order of the vertices + * @throws UnsupportedOperationException if the graph is not a DAG (see {@link #isDAG()}) + */ + List topologicalSort() throws UnsupportedOperationException; + + /** + * The strongly connected components or disconnected components of an arbitrary directed graph + * form a partition into subgraphs that are themselves strongly connected. + * + * @return a collection containing the strongly connected components + */ + Collection> stronglyConnectedComponents(); + + /** + * Get a sub-graph of the current one based on the maximum depth that is given.
+ * If the depth is 1 then only the source and it's children will be in the sub-graph.
+ * If the depth is 2 then only the source, it's children and it's children of it's children will be in the sub-graph.
+ * And so on.
+ * Of course the sub-graph will contain the edges that link the vertices, but only the one selected. + * + * @param source the source vertex + * @param depth the maximum depth (must be a positive number, if >=0 a graph containing only the source is returned) + * @return a sub-graph of the original + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained + */ + Graph subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException; + + /** + * Get a sub-graph of the current one with only the vertex marked with the selected marker.
+ * Each vertex will have all his edges, but only the ones with the destination marked with the same marker.
+ * If the marker is null then the returning graph will have all the vertices that are not marked by any marker.
+ * If the graph doesn't contain any vertex with that marker then an empty graph is returned. + * + * @param marker the marker + * @return a sub-graph of the current graph + */ + Graph subGraph(Object marker); + + /** + * Get the minimum path from the source vertex to the destination vertex.
+ * If the source vertex can't reach the destination, then an exception is thrown. + * + * @param source the vertex where to start + * @param destination the destination chosen + * @return an ordered list of edges from source to destination that represent the minimum path between the two vertices + * @throws NullPointerException if one of the parameter is null (except the consumer) + * @throws IllegalArgumentException if the vertex is not in the graph + * @throws UnsupportedOperationException if from the source it's not possible to reach the destination + */ + List> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException; + + /** + * Get the minimum path from the source vertex to all the possible reachable vertices. + * + * @param source the vertex where to start + * @return a map containing all the possible reachable vertices from the source and the minimum path to reach them + * @throws NullPointerException if one of the parameter is null (except the consumer) + * @throws IllegalArgumentException if the vertex is not in the graph + */ + Map>> distance(V source) throws NullPointerException, IllegalArgumentException; + + // TODO maybe -> STATIC saveOnFile(orString) INSTANCE loadFromFile(orString), but need JSON parser + // TODO maybe, but i don't think so... STATIC DISTANCE V* -> V* +} diff --git a/src/berack96/sim/util/graph/MapGraph.java b/src/berack96/sim/util/graph/MapGraph.java index d13b04d..6e50cce 100644 --- a/src/berack96/sim/util/graph/MapGraph.java +++ b/src/berack96/sim/util/graph/MapGraph.java @@ -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
- * More specifically it utilizes a Map containing all the vertices mapped to all their edges
- * 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.
- * This happen if the HashMap is not reallocated. So in the end each operation of adding or removing has O(n) - * - * @param the vertices - * @param the weight of the edges - * @author Berack96 - */ -public class MapGraph implements Graph { - - /** - * Map that contains the edges from a vertex to another
- * The first vertex is the vertex where start the edge, the second one is where the edge goes
- * If an edge exist, then it's weight is returned - */ - private final Map> edges = new HashMap<>(); - - /** - * Map that contains the vertex as key and all the marker as the value associated with that vertex. - */ - private final Map> 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 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> dijkstra = null; - - @Override - public boolean isCyclic() { - return stronglyConnectedComponents().size() != numberOfVertices(); - } - - @Override - public boolean isDAG() { - return !isCyclic(); - } - - @Override - public Vertex 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 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 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 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 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 edge) throws NullPointerException, IllegalArgumentException { - return addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight()); - } - - @Override - public void addAllEdges(Collection> 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 vertices() { - return new HashSet<>(edges.keySet()); - } - - @Override - public Set> edges() { - Set> allEdges = new HashSet<>(); - edges.forEach((source, map) -> map.forEach((destination, weight) -> allEdges.add(new Edge<>(source, destination, weight)))); - return allEdges; - } - - @Override - public Set> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - Set> 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> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - Collection> 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> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - Collection> collection = new HashSet<>(); - edges.get(vertex).forEach((dest, weight) -> collection.add(new Edge<>(vertex, dest, weight))); - - return collection; - } - - @Override - public Set getChildren(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - return new HashSet<>(edges.get(vertex).keySet()); - } - - @Override - public Set getAncestors(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - Set 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 visit(V source, VisitStrategy strategy, Consumer visit) throws NullPointerException, IllegalArgumentException { - return strategy.visit(this, source, visit); - } - - @Override - public Graph transpose() { - Graph 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 topologicalSort() throws UnsupportedOperationException { - if (!isDAG()) - throw new UnsupportedOperationException(NOT_DAG); - return getTarjan().getTopologicalSort(); - } - - @Override - public Collection> stronglyConnectedComponents() { - return getTarjan().getSCC(); - } - - @Override - public Graph subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException { - Graph sub = new MapGraph<>(); - Set vertices = new HashSet<>(); - - int finalDepth = depth > 0 ? depth : 0; - VisitStrategy strategy = (graph, sourceVertex, visit) -> { - int currentDepth = 0; - final LinkedList> toVisitChildren = new LinkedList<>(); - toVisitChildren.add(new AbstractMap.SimpleEntry<>(sourceVertex, 0)); - vertices.add(source); - - while (!toVisitChildren.isEmpty() && currentDepth + 1 <= finalDepth) { - final Map.Entry 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 subGraph(final Object marker) { - final Graph graph = new MapGraph<>(); - final Set allVertices = new HashSet<>(); - - marked.forEach((vertex, mark) -> { - if (mark.contains(marker) || (marker == null && !mark.isEmpty())) - allVertices.add(vertex); - }); - - if (marker == null) { - Collection 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> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException { - checkNullAndExist(source); - checkNullAndExist(destination); - - Dijkstra dijkstra = getDijkstra(source); - List> path = dijkstra.getLastDistance().get(destination); - if (path == null) - throw new UnsupportedOperationException(NOT_CONNECTED); - return new ArrayList<>(path); - } - - @Override - public Map>> distance(V source) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(source); - return new HashMap<>(getDijkstra(source).getLastDistance()); - } - - @Override - public Iterator 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 getDijkstra(V source) { - if (dijkstra == null) - dijkstra = new HashMap<>(); - if (dijkstra.get(source) == null) { - Dijkstra newDijkstra = new Dijkstra<>(); - newDijkstra.visit(this, source, null); - dijkstra.put(source, newDijkstra); - } - - return dijkstra.get(source); - } - - private Tarjan 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
+ * More specifically it utilizes a Map containing all the vertices mapped to all their edges
+ * 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.
+ * This happen if the HashMap is not reallocated. So in the end each operation of adding or removing has O(n) + * + * @param the vertices + * @param the weight of the edges + * @author Berack96 + */ +public class MapGraph implements Graph { + + /** + * Map that contains the edges from a vertex to another
+ * The first vertex is the vertex where start the edge, the second one is where the edge goes
+ * If an edge exist, then it's weight is returned + */ + private final Map> edges = new HashMap<>(); + + /** + * Map that contains the vertex as key and all the marker as the value associated with that vertex. + */ + private final Map> 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 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> dijkstra = null; + + @Override + public boolean isCyclic() { + return stronglyConnectedComponents().size() != numberOfVertices(); + } + + @Override + public boolean isDAG() { + return !isCyclic(); + } + + @Override + public Vertex 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 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 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 getMarkedWith(Object mark) throws NullPointerException { + checkNull(mark); + Collection ret = new HashSet(); + marked.forEach((v, set) -> { + if(set.contains(mark)) + ret.add(v); + }); + + return ret; + } + + @Override + public Set 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 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 edge) throws NullPointerException, IllegalArgumentException { + return addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight()); + } + + @Override + public void addAllEdges(Collection> 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 vertices() { + return new HashSet<>(edges.keySet()); + } + + @Override + public Set> edges() { + Set> allEdges = new HashSet<>(); + edges.forEach((source, map) -> map.forEach((destination, weight) -> allEdges.add(new Edge<>(source, destination, weight)))); + return allEdges; + } + + @Override + public Set> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException { + checkNullAndExist(vertex); + + Set> 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> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException { + checkNullAndExist(vertex); + Collection> 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> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException { + checkNullAndExist(vertex); + Collection> collection = new HashSet<>(); + edges.get(vertex).forEach((dest, weight) -> collection.add(new Edge<>(vertex, dest, weight))); + + return collection; + } + + @Override + public Set getChildren(V vertex) throws NullPointerException, IllegalArgumentException { + checkNullAndExist(vertex); + + return new HashSet<>(edges.get(vertex).keySet()); + } + + @Override + public Set getAncestors(V vertex) throws NullPointerException, IllegalArgumentException { + checkNullAndExist(vertex); + + Set 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 visit(V source, VisitStrategy strategy, Consumer visit) throws NullPointerException, IllegalArgumentException { + return strategy.visit(this, source, visit); + } + + @Override + public Graph transpose() { + Graph 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 topologicalSort() throws UnsupportedOperationException { + if (!isDAG()) + throw new UnsupportedOperationException(NOT_DAG); + return getTarjan().getTopologicalSort(); + } + + @Override + public Collection> stronglyConnectedComponents() { + return getTarjan().getSCC(); + } + + @Override + public Graph subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException { + Graph sub = new MapGraph<>(); + Set vertices = new HashSet<>(); + + int finalDepth = depth > 0 ? depth : 0; + VisitStrategy strategy = (graph, sourceVertex, visit) -> { + int currentDepth = 0; + final LinkedList> toVisitChildren = new LinkedList<>(); + toVisitChildren.add(new AbstractMap.SimpleEntry<>(sourceVertex, 0)); + vertices.add(source); + + while (!toVisitChildren.isEmpty() && currentDepth + 1 <= finalDepth) { + final Map.Entry 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 subGraph(final Object marker) { + final Graph graph = new MapGraph<>(); + final Set allVertices = new HashSet<>(); + + marked.forEach((vertex, mark) -> { + if (mark.contains(marker) || (marker == null && !mark.isEmpty())) + allVertices.add(vertex); + }); + + if (marker == null) { + Collection 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> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException { + checkNullAndExist(source); + checkNullAndExist(destination); + + Dijkstra dijkstra = getDijkstra(source); + List> path = dijkstra.getLastDistance().get(destination); + if (path == null) + throw new UnsupportedOperationException(NOT_CONNECTED); + return new ArrayList<>(path); + } + + @Override + public Map>> distance(V source) throws NullPointerException, IllegalArgumentException { + checkNullAndExist(source); + return new HashMap<>(getDijkstra(source).getLastDistance()); + } + + @Override + public Iterator 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 getDijkstra(V source) { + if (dijkstra == null) + dijkstra = new HashMap<>(); + if (dijkstra.get(source) == null) { + Dijkstra newDijkstra = new Dijkstra<>(); + newDijkstra.visit(this, source, null); + dijkstra.put(source, newDijkstra); + } + + return dijkstra.get(source); + } + + private Tarjan 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); + } +} diff --git a/src/berack96/sim/util/graph/Vertex.java b/src/berack96/sim/util/graph/Vertex.java index 3e29425..d369555 100644 --- a/src/berack96/sim/util/graph/Vertex.java +++ b/src/berack96/sim/util/graph/Vertex.java @@ -15,7 +15,7 @@ import java.util.function.Consumer; * @param the vertex * @author Berack96 */ -@SuppressWarnings("unchecked") +@SuppressWarnings({"unchecked", "rawtypes"}) public class Vertex { public static final String REMOVED = "The vertex is no longer in the graph"; @@ -240,7 +240,7 @@ public class Vertex { * @throws NullPointerException if the strategy is null * @throws UnsupportedOperationException if the vertex is not in the graph anymore */ - public VisitInfo visit(final VisitStrategy strategy, final Consumer visit) throws NullPointerException, UnsupportedOperationException { + public VisitInfo visit(final VisitStrategy strategy, final Consumer visit) throws NullPointerException, UnsupportedOperationException { throwIfNotContained(); return graph.visit(vertex, (VisitStrategy) strategy, visit); } diff --git a/src/berack96/sim/util/graph/view/GraphInfo.java b/src/berack96/sim/util/graph/view/GraphInfo.java new file mode 100644 index 0000000..7794dca --- /dev/null +++ b/src/berack96/sim/util/graph/view/GraphInfo.java @@ -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 extends JPanel { + + private static final long serialVersionUID = 1L; + + private final Map> visits; + + public GraphInfo(GraphPanel graphPanel, VertexListener vListener, EdgeListener eListener, Set> 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 components = new LinkedList<>(); + JLabel selected = new JLabel(); + JComboBox comboBox = new JComboBox<>(); + comboBox.addItemListener(e -> { + if (e.getStateChange() == ItemEvent.SELECTED) { + try { + String clazz = (String) e.getItem(); + VisitListener 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 strategy: visits) { + String clazz = strategy.getClass().getSimpleName(); + VisitListener 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 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()); + } + } + }); + } +} diff --git a/src/berack96/sim/util/graph/view/GraphListener.java b/src/berack96/sim/util/graph/view/GraphListener.java new file mode 100644 index 0000000..364d289 --- /dev/null +++ b/src/berack96/sim/util/graph/view/GraphListener.java @@ -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(); +} diff --git a/src/berack96/sim/util/graph/view/GraphPanel.java b/src/berack96/sim/util/graph/view/GraphPanel.java new file mode 100644 index 0000000..b547089 --- /dev/null +++ b/src/berack96/sim/util/graph/view/GraphPanel.java @@ -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 extends Component { + + private static final long serialVersionUID = 1L; + private final GraphicalView> vertexRender; + private final GraphicalView> edgeRender; + + private final Container vertices = new Container(); + private final Container edges = new Container(); + + private final Graph graph = new MapGraph<>(); + private final Set observers = new HashSet<>(); + + private GraphListener old = null; + + public GraphPanel(GraphicalView> vertexRender, GraphicalView> edgeRender) { + this.vertexRender = vertexRender; + this.edgeRender = edgeRender; + } + + public Graph 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 component = getVertexAt(center); + + if (component == null) { + VertexComponent 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 component = getVertexAt(center); + component.vertex.remove(); + vertices.remove(component); + } catch (Exception ignore) {} + } + + public void moveVertex(VertexComponent vertex, Point destination) { + Rectangle rectangle = vertexRender.getBox(vertex, destination); + vertex.setLocation(rectangle.x, rectangle.y); + } + + public void addEdge(VertexComponent source, VertexComponent dest, W weight) { + try { + Point center = new Point(Math.abs(source.getX() - dest.getY()), Math.abs(source.getY() - dest.getY())); + EdgeComponent 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 source, VertexComponent dest) { + try { + graph.removeEdge(source.vertex.getValue(), dest.vertex.getValue()); + EdgeComponent toRemove = null; + for (Component c : edges.getComponents()) { + EdgeComponent edge = (EdgeComponent) c; + if (edge.source.equals(source) && edge.destination.equals(dest)) + toRemove = edge; + } + edges.remove(toRemove); + + } catch (Exception ignore) {} + } + + public void modEdge(VertexComponent source, VertexComponent dest, W weight) { + removeEdge(source, dest); + addEdge(source, dest, weight); + } + + public VertexComponent getVertexAt(Point point) { + Component component = vertices.getComponentAt(point); + return component instanceof VertexComponent ? (VertexComponent) component : null; + } + + public EdgeComponent getEdgeAt(Point point) { + Component component = edges.getComponentAt(point); + return component instanceof EdgeComponent ? (EdgeComponent) 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> toRemove = new HashSet<>(); + for (Component component : edges.getComponents()) { + EdgeComponent edge = (EdgeComponent) component; + Vertex source = edge.source.vertex; + Vertex 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 vertex = (VertexComponent) 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)); + } + +} diff --git a/src/berack96/sim/util/graph/view/GraphWindow.java b/src/berack96/sim/util/graph/view/GraphWindow.java new file mode 100644 index 0000000..f64c821 --- /dev/null +++ b/src/berack96/sim/util/graph/view/GraphWindow.java @@ -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 extends JFrame { + + private static final long serialVersionUID = 1L; + + public static void main(String[] args) { + GraphPanel panel = new GraphPanel<>(new VertexView<>(), new EdgeView<>()); + GraphWindow 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 graphPanel; + private final GraphInfo infoPanel; + + private GraphWindow(GraphPanel graphPanel, VertexListener vListener, EdgeListener eListener) { + this.setTitle("Grafo"); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setLayout(new BorderLayout()); + + Set> 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 getGraphPanel() { + return graphPanel; + } +} diff --git a/src/berack96/sim/util/graph/view/GraphicalView.java b/src/berack96/sim/util/graph/view/GraphicalView.java new file mode 100644 index 0000000..9d29e2f --- /dev/null +++ b/src/berack96/sim/util/graph/view/GraphicalView.java @@ -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 the object to paint + * @author Berack96 + */ +public interface GraphicalView { + /** + * 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); +} diff --git a/src/berack96/sim/util/graph/view/VisitListener.java b/src/berack96/sim/util/graph/view/VisitListener.java new file mode 100644 index 0000000..9dd40a3 --- /dev/null +++ b/src/berack96/sim/util/graph/view/VisitListener.java @@ -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 implements GraphListener { + + private final GraphPanel panel; + private final VisitStrategy strategy; + private final Set timers = new HashSet<>(); + + private static int refreshTime = 1000; + + public VisitListener(GraphPanel panel, VisitStrategy 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 "" + + "Start a visit by pressing
" + + "with the mouse SX on the root vertex
" + + ""; + } + + @Override + public void mousePressed(MouseEvent e) { + this.remove(); + + Graph graph = panel.getGraph(); + graph.unMarkAll(); + panel.repaint(); + + if (e.getButton() == MouseEvent.BUTTON1) + try { + VertexComponent vertex = panel.getVertexAt(e.getPoint()); + AtomicInteger count = new AtomicInteger(0); + VisitInfo 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) { + } +} diff --git a/src/berack96/sim/util/graph/view/edge/EdgeComponent.java b/src/berack96/sim/util/graph/view/edge/EdgeComponent.java new file mode 100644 index 0000000..cd52048 --- /dev/null +++ b/src/berack96/sim/util/graph/view/edge/EdgeComponent.java @@ -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 extends Component { + private static final long serialVersionUID = 1L; + + public final VertexComponent source; + public final VertexComponent destination; + public final W weight; + public final Edge edge; + + public EdgeComponent(VertexComponent source, VertexComponent destination, W weight) { + this.source = source; + this.destination = destination; + this.weight = weight; + this.edge = new Edge<>(source.vertex.getValue(), destination.vertex.getValue(), weight); + } +} diff --git a/src/berack96/sim/util/graph/view/edge/EdgeIntListener.java b/src/berack96/sim/util/graph/view/edge/EdgeIntListener.java new file mode 100644 index 0000000..03c61e1 --- /dev/null +++ b/src/berack96/sim/util/graph/view/edge/EdgeIntListener.java @@ -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 extends EdgeListener { + + public EdgeIntListener(GraphPanel graphPanel) { + super(graphPanel); + } + + @Override + public void remove() {} + + @Override + protected Integer buildNewEdge(Vertex vertex, Vertex vertex1) { + return (int) (Math.random() * 100); + } + + @Override + protected Integer buildEdgeFrom(String string) { + return Integer.parseInt(string.replaceAll("[^0-9]+", "")); + } +} diff --git a/src/berack96/sim/util/graph/view/edge/EdgeListener.java b/src/berack96/sim/util/graph/view/edge/EdgeListener.java new file mode 100644 index 0000000..fee506a --- /dev/null +++ b/src/berack96/sim/util/graph/view/edge/EdgeListener.java @@ -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 implements GraphListener { + + private final GraphPanel graphPanel; + private final AtomicReference> componentPressed = new AtomicReference<>(); + private final AtomicReference buttonPressed = new AtomicReference<>(); + private final AtomicReference> edge = new AtomicReference<>(); + private final StringBuilder string = new StringBuilder(); + + public EdgeListener(GraphPanel graphPanel) { + this.graphPanel = graphPanel; + } + + protected abstract W buildNewEdge(Vertex vertex, Vertex vertex1); + + protected abstract W buildEdgeFrom(String string); + + @Override + public String getDescription() { + return "" + + "Modify edges with:
" + + "mouse SX on vertex to another ==> add
" + + "mouse DX on edge ==> change weigth
" + + "(only numbers allowed)" + + ""; + } + + @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 source = componentPressed.get(); + VertexComponent 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) { + } +} diff --git a/src/berack96/sim/util/graph/view/edge/EdgeView.java b/src/berack96/sim/util/graph/view/edge/EdgeView.java new file mode 100644 index 0000000..0c1fbf0 --- /dev/null +++ b/src/berack96/sim/util/graph/view/edge/EdgeView.java @@ -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 implements GraphicalView> { + + private static final Font FONT = new Font("Papyrus", Font.BOLD, 14); + + @Override + public Rectangle getBox(EdgeComponent 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 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 marksD = edge.destination.vertex.getMarks(); + Collection 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; + } +} diff --git a/src/berack96/sim/util/graph/view/stuff/Arrow.java b/src/berack96/sim/util/graph/view/stuff/Arrow.java new file mode 100644 index 0000000..92b9ab7 --- /dev/null +++ b/src/berack96/sim/util/graph/view/stuff/Arrow.java @@ -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)); + } +} diff --git a/src/berack96/sim/util/graph/view/vertex/VertexComponent.java b/src/berack96/sim/util/graph/view/vertex/VertexComponent.java new file mode 100644 index 0000000..8cd00c0 --- /dev/null +++ b/src/berack96/sim/util/graph/view/vertex/VertexComponent.java @@ -0,0 +1,15 @@ +package berack96.sim.util.graph.view.vertex; + +import berack96.sim.util.graph.Vertex; + +import java.awt.*; + +public class VertexComponent extends Component { + private static final long serialVersionUID = 1L; + + public final Vertex vertex; + + public VertexComponent(Vertex vertex) { + this.vertex = vertex; + } +} diff --git a/src/berack96/sim/util/graph/view/vertex/VertexIntListener.java b/src/berack96/sim/util/graph/view/vertex/VertexIntListener.java new file mode 100644 index 0000000..79c6d8e --- /dev/null +++ b/src/berack96/sim/util/graph/view/vertex/VertexIntListener.java @@ -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 { + + private Integer counter = 0; + + public VertexIntListener(GraphPanel panel) { + super(panel); + } + + @Override + public void remove() {} + + @Override + protected Integer buildNewVertex(Graph graph) { + if(graph.numberOfVertices() == 0) + counter = 0; + counter++; + return counter - 1; + } +} diff --git a/src/berack96/sim/util/graph/view/vertex/VertexListener.java b/src/berack96/sim/util/graph/view/vertex/VertexListener.java new file mode 100644 index 0000000..9d3fad2 --- /dev/null +++ b/src/berack96/sim/util/graph/view/vertex/VertexListener.java @@ -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 implements GraphListener { + + protected final GraphPanel panel; + private final AtomicReference> componentPressed = new AtomicReference<>(); + + public VertexListener(GraphPanel panel) { + this.panel = panel; + } + + protected abstract V buildNewVertex(Graph graph); + + @Override + public String getDescription() { + return "" + + "Modify vertex with:
" + + "mouse SX ==> add
" + + "mouse SX on vertex ==> move
" + + "mouse DX ==> remove
" + + ""; + } + + @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) { + } +} diff --git a/src/berack96/sim/util/graph/view/vertex/VertexView.java b/src/berack96/sim/util/graph/view/vertex/VertexView.java new file mode 100644 index 0000000..a5b6803 --- /dev/null +++ b/src/berack96/sim/util/graph/view/vertex/VertexView.java @@ -0,0 +1,42 @@ +package berack96.sim.util.graph.view.vertex; + +import berack96.sim.util.graph.view.GraphicalView; + +import java.awt.*; + +public class VertexView implements GraphicalView> { + + private static final Font FONT = new Font("Comic Sans MS", Font.BOLD, 17); + private static final int PADDING = 6; + + @Override + public Rectangle getBox(VertexComponent 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 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); + } +} diff --git a/src/berack96/sim/util/graph/visit/Dijkstra.java b/src/berack96/sim/util/graph/visit/Dijkstra.java index d40cf37..ced62d1 100644 --- a/src/berack96/sim/util/graph/visit/Dijkstra.java +++ b/src/berack96/sim/util/graph/visit/Dijkstra.java @@ -32,12 +32,12 @@ public class Dijkstra implements VisitDistance { public VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException { VisitInfo info = new VisitInfo<>(source); Queue queue = new PriorityQueue<>(); - Map dist = new HashMap<>(); + Map dist = new HashMap<>(); Map 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 implements VisitDistance { 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 implements VisitDistance { private class QueueEntry implements Comparable { 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 implements VisitDistance { @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; } } } diff --git a/src/berack96/sim/util/graph/visit/VisitDistSourceDest.java b/src/berack96/sim/util/graph/visit/VisitDistSourceDest.java index 697ff57..df7a4b4 100644 --- a/src/berack96/sim/util/graph/visit/VisitDistSourceDest.java +++ b/src/berack96/sim/util/graph/visit/VisitDistSourceDest.java @@ -25,5 +25,5 @@ public interface VisitDistSourceDest 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> distance(Graph graph, V source, V destination) throws NullPointerException, IllegalArgumentException; + List> distance(Graph graph, V source, V destination) throws NullPointerException, IllegalArgumentException; } diff --git a/src/berack96/sim/util/graph/visit/VisitStrategy.java b/src/berack96/sim/util/graph/visit/VisitStrategy.java index b3455b2..abde231 100644 --- a/src/berack96/sim/util/graph/visit/VisitStrategy.java +++ b/src/berack96/sim/util/graph/visit/VisitStrategy.java @@ -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 The Object that represent a vertex - * @param The Object that represent the edge (more specifically the weight of the edge) - * @author Berack96 - */ -public interface VisitStrategy { - - /** - * With this the graph will be visited accordingly to the strategy of the visit.
- * Some strategy can accept a source vertex null, because they visit all the graph anyway.
- * 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 visit(Graph graph, V source, Consumer 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 The Object that represent a vertex + * @param The Object that represent the edge (more specifically the weight of the edge) + * @author Berack96 + */ +public interface VisitStrategy { + + /** + * With this the graph will be visited accordingly to the strategy of the visit.
+ * Some strategy can accept a source vertex null, because they visit all the graph anyway.
+ * 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 visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException; +} diff --git a/test/berack96/test/sim/TestGraph.java b/test/berack96/test/sim/TestGraph.java index 1b53131..a5334a5 100644 --- a/test/berack96/test/sim/TestGraph.java +++ b/test/berack96/test/sim/TestGraph.java @@ -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