{@link #INTERRUPTED} - Represents an interrupted process
18 | *
{@link #SUCCESS} - Represents a successful process
19 | *
{@link #ERROR} - Represents an error during a process
20 | *
21 | *
22 | *
Available edge directions:
23 | *
24 | *
{@link #BIDIRECTIONAL} - Represents a bidirectional edge
25 | *
{@link #NATURAL_DIRECTION} - Represents an edge in the natural direction (first to second)
26 | *
{@link #OPPOSITE_DIRECTION} - Represents an edge in the opposite direction (second to first)
27 | *
28 | *
29 | * @author vittoripiotti
30 | */
31 | public class Constants {
32 |
33 | /**
34 | * Constant representing an interrupted process.
35 | */
36 | public static final int INTERRUPTED = 2147483602;
37 |
38 | /**
39 | * Constant representing a successful process.
40 | */
41 | public static final int SUCCESS = 2147483601;
42 |
43 | /**
44 | * Constant representing an error during process.
45 | */
46 | public static final int ERROR = 2147483600;
47 |
48 | /**
49 | * Constant representing bidirectional edge.
50 | */
51 | public static final int BIDIRECTIONAL = 0;
52 |
53 | /**
54 | * Constant representing edge in natural direction.
55 | */
56 | public static final int NATURAL_DIRECTION = 1;
57 |
58 |
59 | /**
60 | * Constant representing edge in opposite direction.
61 | */
62 | public static final int OPPOSITE_DIRECTION = 2;
63 |
64 |
65 | /**
66 | * Constant representing the max number of nodes.
67 | */
68 | public static final int MAX_NODES = 26;
69 |
70 |
71 |
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/callbacks/AdjustPositionCallback.java:
--------------------------------------------------------------------------------
1 | package com.vittoriopiotti.pathgraph.callbacks;
2 |
3 | /**
4 | * A callback interface for adjusting positions without parameters.
5 | *
6 | * @author vittoriopiotti
7 | */
8 | public interface AdjustPositionCallback extends GenericCallback {
9 |
10 | @Override
11 | default void handle(Object... params) {
12 | handle();
13 | }
14 |
15 | /**
16 | * Handles the position adjustment.
17 | */
18 | void handle();
19 | }
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/callbacks/BackgroundCallback.java:
--------------------------------------------------------------------------------
1 | package com.vittoriopiotti.pathgraph.callbacks;
2 |
3 | import javafx.scene.input.MouseEvent;
4 |
5 |
6 | /**
7 | * A callback interface for handling mouse events.
8 | *
9 | * @author vittoriopiotti
10 | */
11 | public interface BackgroundCallback extends GenericCallback {
12 |
13 | @Override
14 | default void handle(Object... params) {
15 | if (params.length > 0 && params[0] instanceof MouseEvent) {
16 | handle((MouseEvent) params[0]);
17 | }
18 | }
19 |
20 | /**
21 | * Handles a mouse event.
22 | *
23 | * @param event the mouse event
24 | */
25 | void handle(MouseEvent event);
26 | }
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/callbacks/ContextMenuCallback.java:
--------------------------------------------------------------------------------
1 | package com.vittoriopiotti.pathgraph.callbacks;
2 |
3 |
4 |
5 | /**
6 | * A callback interface for handling context menu events.
7 | *
8 | * @author vittoriopiotti
9 | */
10 | public interface ContextMenuCallback extends GenericCallback {
11 |
12 | @Override
13 | default void handle(Object... params) {
14 | handle();
15 | }
16 |
17 | /**
18 | * Handles the context menu event.
19 | */
20 | void handle();
21 | }
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/callbacks/EdgeCallback.java:
--------------------------------------------------------------------------------
1 | package com.vittoriopiotti.pathgraph.callbacks;
2 |
3 | import javafx.scene.input.MouseEvent;
4 |
5 | /**
6 | * A callback interface for handling edge events with mouse and characters.
7 | *
8 | * @author vittoriopiotti
9 | */
10 | public interface EdgeCallback extends GenericCallback {
11 |
12 | @Override
13 | default void handle(Object... params) {
14 | if (params.length == 3 && (params[0] == null || params[0] instanceof MouseEvent ) && params[1] instanceof Character && params[2] instanceof Character) {
15 | handle((MouseEvent) params[0], (Character) params[1], (Character) params[2]);
16 | }
17 |
18 | }
19 |
20 | /**
21 | * Handles the edge event.
22 | *
23 | * @param event the mouse event
24 | * @param from the character representing the starting node
25 | * @param to the character representing the ending node
26 | */
27 | void handle(MouseEvent event, Character from, Character to);
28 | }
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/callbacks/GenericCallback.java:
--------------------------------------------------------------------------------
1 | package com.vittoriopiotti.pathgraph.callbacks;
2 |
3 | /**
4 | * A functional interface for handling generic callbacks with variable parameters.
5 | *
6 | * @author vittoriopiotti
7 | */
8 | @FunctionalInterface
9 | public interface GenericCallback {
10 |
11 | /**
12 | * Handles a callback with variable parameters.
13 | *
14 | * @param params the parameters for the callback
15 | */
16 | void handle(Object... params);
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/callbacks/NodeCallback.java:
--------------------------------------------------------------------------------
1 | package com.vittoriopiotti.pathgraph.callbacks;
2 |
3 | import javafx.scene.input.MouseEvent;
4 |
5 | /**
6 | * A callback interface for handling node events with mouse and a character.
7 | *
8 | * @author vittoriopiotti
9 | */
10 | public interface NodeCallback extends GenericCallback {
11 |
12 | @Override
13 | default void handle(Object... params) {
14 | if (params.length == 2 && params[0] instanceof MouseEvent && params[1] instanceof Character) {
15 | handle((MouseEvent) params[0], (Character) params[1]);
16 | }
17 | }
18 |
19 | /**
20 | * Handles the node event.
21 | *
22 | * @param event the mouse event
23 | * @param node the character representing the node
24 | */
25 | void handle(MouseEvent event, Character node);
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/callbacks/ZoomCallback.java:
--------------------------------------------------------------------------------
1 | package com.vittoriopiotti.pathgraph.callbacks;
2 |
3 | /**
4 | * A callback interface for handling zoom events with a zoom level.
5 | *
6 | * @author vittoriopiotti
7 | */
8 | public interface ZoomCallback extends GenericCallback {
9 |
10 | @Override
11 | default void handle(Object... params) {
12 | if (params.length == 1 && params[0] instanceof Double) {
13 | handle((Double) params[0]);
14 | }
15 | }
16 |
17 | /**
18 | * Handles the zoom event.
19 | *
20 | * @param zoomLevel the zoom level
21 | */
22 | void handle(Double zoomLevel);
23 | }
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/dto/ConnectionDTO.java:
--------------------------------------------------------------------------------
1 | /*
2 | * PathGraph v1.0.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/releases/tag/1.0.0)
3 | * PathGraph | Copyright 2024 Vittorio Piotti
4 | * Licensed under GPL v3.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt)
5 | */
6 |
7 | package com.vittoriopiotti.pathgraph.dto;
8 |
9 | /**
10 | * Represents a connection in a graph with a label and a cost.
11 | * This class is used to transfer connection data between different layers of an application.
12 | *
13 | * @author vittoriopiotti
14 | */
15 |
16 | @SuppressWarnings("all")
17 | public class ConnectionDTO {
18 |
19 | private final char label;
20 | private final int cost;
21 |
22 | /**
23 | * Constructs a new ConnectionDTO with the specified label and cost.
24 | *
25 | * @param label the character representing the label of the connection
26 | * @param cost the integer representing the cost associated with the connection
27 | */
28 | public ConnectionDTO(char label, int cost) {
29 | this.label = label;
30 | this.cost = cost;
31 | }
32 |
33 | /**
34 | * Returns the label of the connection.
35 | *
36 | * @return the character label of the connection
37 | */
38 | public char getLabel() {
39 | return label;
40 | }
41 |
42 |
43 |
44 | /**
45 | * Returns the cost of the connection.
46 | *
47 | * @return the integer cost of the connection
48 | */
49 | public int getCost() {
50 | return cost;
51 | }
52 |
53 |
54 | /**
55 | * Returns a string representation of the ConnectionDTO.
56 | *
57 | * @return a string in the format "ConnectionDTO{label=X, cost=Y}"
58 | */
59 | @Override
60 | public String toString() {
61 | return "ConnectionDTO{" +
62 | "label=" + label +
63 | ", cost=" + cost +
64 | '}';
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/dto/EdgeDTO.java:
--------------------------------------------------------------------------------
1 | /*
2 | * PathGraph v1.0.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/releases/tag/1.0.0)
3 | * PathGraph | Copyright 2024 Vittorio Piotti
4 | * Licensed under GPL v3.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt)
5 | */
6 |
7 | package com.vittoriopiotti.pathgraph.dto;
8 |
9 | import com.vittoriopiotti.pathgraph.app.Constants;
10 |
11 | /**
12 | * The EdgeDTO class represents a data transfer object for an edge in a graph.
13 | * It encapsulates the details of the edge, including its endpoints, cost,
14 | * and whether it is directed (arrowed). This class is typically used to
15 | * transfer edge data between different layers of an application.
16 | *
17 | * @author vittoriopiotti
18 | */
19 | public class EdgeDTO {
20 |
21 | /**
22 | * The character representing the starting point of the edge.
23 | */
24 | private final char from;
25 |
26 | /**
27 | * The character representing the endpoint of the edge.
28 | */
29 | private final char to;
30 |
31 | /**
32 | * The integer representing the cost associated with the edge.
33 | */
34 | private final int cost;
35 |
36 | /**
37 | * A boolean indicating whether the edge is directed (arrowed).
38 | */
39 | private final boolean isArrowed;
40 |
41 | /**
42 | * Constructs a new EdgeDTO with the specified start point, end point,
43 | * cost, and direction indicator.
44 | *
45 | * @param from the character representing the starting point of the edge
46 | * @param to the character representing the endpoint of the edge
47 | * @param cost the integer representing the cost associated with the edge
48 | * @param isArrowed a boolean indicating whether the edge is directed
49 | */
50 | public EdgeDTO(char from, char to, int cost, boolean isArrowed) {
51 | this.from = from;
52 | this.to = to;
53 | this.cost = cost;
54 | this.isArrowed = isArrowed;
55 | }
56 |
57 | /**
58 | * Constructs a new EdgeDTO with the specified start point, end point,
59 | * cost, and direction. The direction determines whether the edge is
60 | * considered directed or not.
61 | *
62 | * @param from the character representing the starting point of the edge
63 | * @param to the character representing the endpoint of the edge
64 | * @param cost the integer representing the cost associated with the edge
65 | * @param dir an integer indicating the direction of the edge
66 | * (see AppConstants for direction constants)
67 | */
68 | public EdgeDTO(char from, char to, int cost, int dir) {
69 | if (dir == Constants.OPPOSITE_DIRECTION) {
70 | this.from = to;
71 | this.to = from;
72 | } else {
73 | this.from = from;
74 | this.to = to;
75 | }
76 | this.cost = cost;
77 | this.isArrowed = dir != Constants.BIDIRECTIONAL;
78 | }
79 |
80 | /**
81 | * Returns the starting point of the edge.
82 | *
83 | * @return the character representing the starting point of the edge
84 | */
85 | public char getFrom() {
86 | return from;
87 | }
88 |
89 | /**
90 | * Returns the endpoint of the edge.
91 | *
92 | * @return the character representing the endpoint of the edge
93 | */
94 | public char getTo() {
95 | return to;
96 | }
97 |
98 | /**
99 | * Returns the cost of the edge.
100 | *
101 | * @return the integer cost of the edge
102 | */
103 | public int getCost() {
104 | return cost;
105 | }
106 |
107 | /**
108 | * Returns whether the edge is directed (arrowed).
109 | *
110 | * @return true if the edge is directed; false otherwise
111 | */
112 | public boolean getIsArrowed() {
113 | return isArrowed;
114 | }
115 |
116 |
117 | /**
118 | * Returns a string representation of the EdgeDTO.
119 | *
120 | * @return a string in the format "EdgeDTO{from=X, to=Y, cost=Z, isArrowed=W}"
121 | * where X is the starting point, Y is the endpoint, Z is the cost, and W indicates if the edge is directed.
122 | */
123 | @Override
124 | public String toString() {
125 | return "EdgeDTO{" +
126 | "from=" + from +
127 | ", to=" + to +
128 | ", cost=" + cost +
129 | ", isArrowed=" + isArrowed +
130 | '}';
131 | }
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/dto/NodeDTO.java:
--------------------------------------------------------------------------------
1 | /*
2 | * PathGraph v1.0.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/releases/tag/1.0.0)
3 | * PathGraph | Copyright 2024 Vittorio Piotti
4 | * Licensed under GPL v3.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt)
5 | */
6 |
7 |
8 | package com.vittoriopiotti.pathgraph.dto;
9 |
10 | /**
11 | * Represents a node in the graph with a unique label.
12 | * This class is used to transfer node data between different layers of an application.
13 | *
14 | * @author vittoriopiotti
15 | */
16 | @SuppressWarnings("all")
17 | public class NodeDTO {
18 |
19 | private final char label;
20 |
21 | /**
22 | * Constructs a NodeDTO with the specified label.
23 | *
24 | * @param label the label of the node
25 | */
26 | public NodeDTO(char label) {
27 | this.label = label;
28 | }
29 |
30 | /**
31 | * Returns the label of the node.
32 | *
33 | * @return the label of the node
34 | */
35 | public char getLabel() {
36 | return label;
37 | }
38 |
39 | /**
40 | * Returns a string representation of the NodeDTO.
41 | *
42 | * @return a string representing the node label
43 | */
44 | @Override
45 | public String toString() {
46 | return "NodeDTO{" +
47 | "label=" + label +
48 | '}';
49 | }
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/example/Example.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 |
8 | /*
9 | * PathGraph v1.0.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/releases/tag/1.0.0)
10 | * PathGraph | Copyright 2024 Vittorio Piotti
11 | * Licensed under GPL v3.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt)
12 | */
13 |
14 | package com.vittoriopiotti.pathgraph.example;
15 |
16 |
17 | import javafx.scene.Scene;
18 | import javafx.scene.layout.*;
19 | import javafx.stage.Stage;
20 | import com.vittoriopiotti.pathgraph.app.PathGraphUI;
21 |
22 |
23 | import javafx.application.Application;
24 | import javafx.scene.paint.Color;
25 | import javafx.scene.layout.BorderPane;
26 |
27 |
28 | public class Example extends Application {
29 |
30 |
31 | @Override
32 | public void start(Stage primaryStage) {
33 |
34 | /* 1. Create javafx window */
35 | BorderPane root = new BorderPane();
36 | root.setBackground(Background.fill(Color.web("#dee2e6")));
37 | Scene scene = new Scene(root, 750, 550);
38 | primaryStage.setScene(scene);
39 |
40 | /* 2. Visualize primary stage */
41 | primaryStage.show();
42 |
43 |
44 | /* 3. Create PathGraph object*/
45 | PathGraphUI pg = new PathGraphUI(primaryStage, scene);
46 |
47 | /* 4. Add PathGraph in a container */
48 | root.setCenter(pg);
49 |
50 | /* 4. Setup PathGraph */
51 | pg.setup();
52 |
53 | /* 4. Configure PathGraph */
54 | pg.setUI(false,false,false,false,false,false );
55 |
56 | pg.enableListenersGraph(true);
57 | pg.enableListenersPane(true);
58 | pg.setAutomaticLayout(true);
59 |
60 | /* 5. Make Graphs with PathGraph*/
61 | pg.newNode('A');
62 | pg.newNode('B');
63 | pg.newNode('C');
64 | pg.newEdge('A', 'B', 1);
65 | pg.newEdge('C', 'A', 2);
66 | pg.newEdge('C', 'A', 3);
67 |
68 |
69 | }
70 |
71 | public static void main(String[] args) {
72 | launch(args);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graph/Digraph.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 |
8 | /*
9 | * PathGraph v1.0.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/releases/tag/1.0.0)
10 | * PathGraph | Copyright 2024 Vittorio Piotti
11 | * Licensed under GPL v3.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt)
12 | */
13 |
14 | package com.vittoriopiotti.pathgraph.graph;
15 |
16 | import java.util.Collection;
17 |
18 | /**
19 | * A directed graph (or digraph) is a graph that is made up of a set of vertices
20 | * connected by edges, where the edges have a direction associated with them.
21 | *
22 | * A directed-edge leaves the outbound vertex
23 | * towards the inbound vertex and this changes the reasoning behind some
24 | * methods of the {@link Graph} interface, which are overridden in this interface
25 | * to provide different documentation of expected behavior.
26 | *
27 | * @param Type of element stored at a vertex
28 | * @param Type of element stored at an edge
29 | *
30 | * @see Graph
31 | * @see Edge
32 | * @see Vertex
33 | *
34 | * @author brunomsilva
35 | *
Modified by vittoriopiotti
36 | */
37 | public interface Digraph extends Graph {
38 |
39 | /**
40 | * Returns a vertex's incident edges as a collection.
41 | *
42 | * Incident edges are all edges that have vertex inbound as the
43 | * inbound vertex, i.e., the edges "entering" vertex inbound.
44 | * If there are no incident edges, e.g., an isolated vertex,
45 | * returns an empty collection.
46 | *
47 | * @param inbound vertex for which to obtain the incident edges
48 | *
49 | * @return collection of edges
50 | *
51 | * @author brunomsilva
52 | */
53 | @Override
54 | Collection> incidentEdges(Vertex inbound)
55 | throws InvalidVertexException;
56 |
57 | /**
58 | * Returns a vertex's outbound edges as a collection.
59 | *
60 | * Incident edges are all edges that have vertex outbound as the
61 | * outbound vertex, i.e., the edges "leaving" vertex outbound.
62 | * If there are no outbound edges, e.g., an isolated vertex,
63 | * returns an empty collection.
64 | *
65 | * @param outbound vertex for which to obtain the outbound edges
66 | *
67 | * @return collection of edges
68 | *
69 | * @throws InvalidVertexException if outbound is an invalid vertex for the graph
70 | *
71 | * @author brunomsilva
72 | */
73 | Collection> outboundEdges(Vertex outbound)
74 | throws InvalidVertexException;
75 |
76 |
77 | /**
78 | * Evaluates whether two vertices are adjacent, i.e., there exists some
79 | * directed-edge connecting outbound and inbound.
80 | *
81 | * The existing edge must be directed as outbound --> inbound.
82 | *
83 | * If, for example, there exists only an edge outbound <-- inbound,
84 | * they are not considered adjacent.
85 | *
86 | * @param outbound outbound vertex
87 | * @param inbound inbound vertex
88 | *
89 | * @return true if they are adjacent, false otherwise.
90 | *
91 | * @throws InvalidVertexException if outbound or
92 | * inbound
93 | * are invalid vertices for the graph
94 | *
95 | * @author brunomsilva
96 | */
97 | @Override
98 | boolean areAdjacent(Vertex outbound, Vertex inbound)
99 | throws InvalidVertexException;
100 |
101 | /**
102 | * Inserts a new edge with a given element between two existing vertices and
103 | * returns its (the edge's) reference.
104 | *
105 | * @param outbound the outbound vertex from which the edge originates
106 | * @param inbound the inbound vertex to which the edge points
107 | * @param edgeElement the element to store in the new edge
108 | * @param cost the label text associated with the edge
109 | * @param direction the direction of the edge (e.g., 0 for bidirectional, 1 for one direction, 2 for the opposite)
110 | *
111 | * @return the reference for the newly created edge
112 | *
113 | * @throws InvalidVertexException if outbound or
114 | * inbound
115 | * are invalid vertices for the graph
116 | *
117 | * @throws InvalidEdgeException if there already exists an edge
118 | * containing edgeElement
119 | * according to the equality of
120 | * {@link Object#equals(java.lang.Object)}
121 | * method.
122 | *
123 | * @author brunomsilva
124 | *
Modified by vittoriopiotti
125 | */
126 | @Override
127 | Edge insertEdge(Vertex outbound, Vertex inbound, E edgeElement, int cost, int direction)
128 | throws InvalidVertexException, InvalidEdgeException;
129 |
130 |
131 |
132 | /**
133 | * Inserts a new edge with a given element between two existing vertices and
134 | * return its (the edge's) reference.
135 | *
136 | * @param outboundElement outbound vertex's stored element
137 | * @param inboundElement inbound vertex's stored element
138 | * @param edgeElement element to store in the new edge
139 | * @param cost the label text associated with the edge
140 | * @param direction the direction of the edge (e.g., 0 for bidirectional, 1 for one direction, 2 for the opposite)
141 | *
142 | * @return the reference for the newly created edge
143 | *
144 | * @throws InvalidVertexException if outboundElement or
145 | * inboundElement
146 | * are not found in any vertices of the graph
147 | * according to the equality of
148 | * {@link Object#equals(java.lang.Object) }
149 | * method.
150 | *
151 | * @throws InvalidEdgeException if there already exists an edge
152 | * containing edgeElement
153 | * according to the equality of
154 | * {@link Object#equals(java.lang.Object) }
155 | * method.
156 | *
157 | * @author brunomsilva
158 | *
Modified by vittoriopiotti
159 | */
160 | @Override
161 | Edge insertEdge(V outboundElement, V inboundElement, E edgeElement,int cost,int direction)
162 | throws InvalidVertexException, InvalidEdgeException;
163 |
164 |
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graph/DigraphEdgeList.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 |
8 | /*
9 | * PathGraph v1.0.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/releases/tag/1.0.0)
10 | * PathGraph | Copyright 2024 Vittorio Piotti
11 | * Licensed under GPL v3.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt)
12 | */
13 |
14 | package com.vittoriopiotti.pathgraph.graph;
15 |
16 | import java.util.*;
17 |
18 |
19 | /**
20 | * Implementation of a digraph that adheres to the {@link Digraph} interface.
21 | *
22 | * Does not allow duplicates of stored elements through equals criteria.
23 | *
24 | * @param Type of element stored at a vertex
25 | * @param Type of element stored at an edge
26 | *
27 | * @author brunomnsilva
28 | *
Modified by vittoriopiotti
29 | */
30 | public class DigraphEdgeList implements Digraph {
31 |
32 | /* inner classes are defined at the end of the class, so are the auxiliary methods
33 | */
34 | private final Map> vertices;
35 | private final Map> edges;
36 |
37 | /**
38 | * Default constructor that initializes an empty digraph.
39 | */
40 | public DigraphEdgeList() {
41 | this.vertices = new HashMap<>();
42 | this.edges = new HashMap<>();
43 | }
44 |
45 | @Override
46 | public synchronized Collection> incidentEdges(Vertex inbound) throws InvalidVertexException {
47 | checkVertex(inbound);
48 |
49 | List> incidentEdges = new ArrayList<>();
50 | for (Edge edge : edges.values()) {
51 |
52 | if (((MyEdge) edge).getInbound() == inbound) {
53 | incidentEdges.add(edge);
54 | }
55 | }
56 | return incidentEdges;
57 | }
58 |
59 | @Override
60 | public synchronized Collection> outboundEdges(Vertex outbound) throws InvalidVertexException {
61 | checkVertex(outbound);
62 |
63 | List> outboundEdges = new ArrayList<>();
64 | for (Edge edge : edges.values()) {
65 |
66 | if (((MyEdge) edge).getOutbound() == outbound) {
67 | outboundEdges.add(edge);
68 | }
69 | }
70 | return outboundEdges;
71 | }
72 |
73 | @Override
74 | public boolean areAdjacent(Vertex outbound, Vertex inbound) throws InvalidVertexException {
75 | //we allow loops, so we do not check if outbound == inbound
76 | checkVertex(outbound);
77 | checkVertex(inbound);
78 |
79 | /* find and edge that goes outbound ---> inbound */
80 | for (Edge edge : edges.values()) {
81 | if (((MyEdge) edge).getOutbound() == outbound && ((MyEdge) edge).getInbound() == inbound) {
82 | return true;
83 | }
84 | }
85 | return false;
86 | }
87 |
88 | @Override
89 | public synchronized Edge insertEdge(Vertex outbound, Vertex inbound, E edgeElement,int cost,int direction) throws InvalidVertexException, InvalidEdgeException {
90 | if (existsEdgeWith(edgeElement)) {
91 | throw new InvalidEdgeException("There's already an edge with this element.");
92 | }
93 |
94 | MyVertex outVertex = checkVertex(outbound);
95 | MyVertex inVertex = checkVertex(inbound);
96 |
97 | MyEdge newEdge = new MyEdge(edgeElement, outVertex, inVertex,cost,direction);
98 |
99 | edges.put(edgeElement, newEdge);
100 |
101 | return newEdge;
102 | }
103 |
104 | @Override
105 | public synchronized Edge insertEdge(V outboundElement, V inboundElement, E edgeElement,int cost,int direction) throws InvalidVertexException, InvalidEdgeException {
106 | if (existsEdgeWith(edgeElement)) {
107 | throw new InvalidEdgeException("There's already an edge with this element.");
108 | }
109 |
110 | if (!existsVertexWith(outboundElement)) {
111 | throw new InvalidVertexException("No vertex contains " + outboundElement);
112 | }
113 | if (!existsVertexWith(inboundElement)) {
114 | throw new InvalidVertexException("No vertex contains " + inboundElement);
115 | }
116 |
117 | MyVertex outVertex = vertexOf(outboundElement);
118 | MyVertex inVertex = vertexOf(inboundElement);
119 |
120 | MyEdge newEdge = new MyEdge(edgeElement, outVertex, inVertex,cost,direction);
121 |
122 | edges.put(edgeElement, newEdge);
123 |
124 | return newEdge;
125 | }
126 |
127 | @Override
128 | public int numVertices() {
129 | return vertices.size();
130 | }
131 |
132 | @Override
133 | public int numEdges() {
134 | return edges.size();
135 | }
136 |
137 | @Override
138 | public synchronized Collection> vertices() {
139 | return new ArrayList<>(vertices.values());
140 | }
141 |
142 | @Override
143 | public synchronized Collection> edges() {
144 | return new ArrayList<>(edges.values());
145 | }
146 |
147 | @Override
148 | public synchronized Vertex opposite(Vertex v, Edge e) throws InvalidVertexException, InvalidEdgeException {
149 | checkVertex(v);
150 | MyEdge edge = checkEdge(e);
151 |
152 | if (!edge.contains(v)) {
153 | return null; /* this edge does not connect vertex v */
154 | }
155 |
156 | if (edge.vertices()[0] == v) {
157 | return edge.vertices()[1];
158 | } else {
159 | return edge.vertices()[0];
160 | }
161 |
162 | }
163 |
164 | @Override
165 | public synchronized Vertex insertVertex(V vElement) throws InvalidVertexException {
166 | if (existsVertexWith(vElement)) {
167 | throw new InvalidVertexException("There's already a vertex with this element.");
168 | }
169 |
170 | MyVertex newVertex = new MyVertex(vElement);
171 |
172 | vertices.put(vElement, newVertex);
173 |
174 | return newVertex;
175 | }
176 |
177 | @Override
178 | public synchronized V removeVertex(Vertex v) throws InvalidVertexException {
179 | checkVertex(v);
180 |
181 | V element = v.element();
182 |
183 | //remove incident edges
184 | Collection> inOutEdges = incidentEdges(v);
185 | inOutEdges.addAll(outboundEdges(v));
186 |
187 | for (Edge edge : inOutEdges) {
188 | edges.remove(edge.element());
189 | }
190 |
191 | vertices.remove(v.element());
192 |
193 | return element;
194 | }
195 |
196 | @Override
197 | public synchronized E removeEdge(Edge e) throws InvalidEdgeException {
198 | checkEdge(e);
199 |
200 | E element = e.element();
201 | edges.remove(e.element());
202 |
203 | return element;
204 | }
205 |
206 | @Override
207 | public V replace(Vertex v, V newElement) throws InvalidVertexException {
208 | if (existsVertexWith(newElement)) {
209 | throw new InvalidVertexException("There's already a vertex with this element.");
210 | }
211 |
212 | MyVertex vertex = checkVertex(v);
213 |
214 | V oldElement = vertex.element;
215 | vertex.element = newElement;
216 |
217 | return oldElement;
218 | }
219 |
220 | @Override
221 | public E replace(Edge e, E newElement) throws InvalidEdgeException {
222 | if (existsEdgeWith(newElement)) {
223 | throw new InvalidEdgeException("There's already an edge with this element.");
224 | }
225 |
226 | MyEdge edge = checkEdge(e);
227 |
228 | E oldElement = edge.element;
229 | edge.element = newElement;
230 |
231 | return oldElement;
232 | }
233 |
234 | private MyVertex vertexOf(V vElement) {
235 | for (Vertex v : vertices.values()) {
236 | if (v.element().equals(vElement)) {
237 | return (MyVertex) v;
238 | }
239 | }
240 | return null;
241 | }
242 |
243 | private boolean existsVertexWith(V vElement) {
244 | return vertices.containsKey(vElement);
245 | }
246 |
247 | private boolean existsEdgeWith(E edgeElement) {
248 | return edges.containsKey(edgeElement);
249 | }
250 |
251 | @Override
252 | public String toString() {
253 | StringBuilder sb = new StringBuilder(
254 | String.format("Graph with %d vertices and %d edges:\n", numVertices(), numEdges())
255 | );
256 |
257 | sb.append("--- Vertices: \n");
258 | for (Vertex v : vertices.values()) {
259 | sb.append("\t").append(v.toString()).append("\n");
260 | }
261 | sb.append("\n--- Edges: \n");
262 | for (Edge e : edges.values()) {
263 | sb.append("\t").append(e.toString()).append("\n");
264 | }
265 | return sb.toString();
266 | }
267 |
268 | private class MyVertex implements Vertex {
269 |
270 | V element;
271 |
272 | public MyVertex(V element) {
273 | this.element = element;
274 | }
275 |
276 | @Override
277 | public V element() {
278 | return this.element;
279 | }
280 |
281 | @Override
282 | public String toString() {
283 | return "Vertex{" + element + '}';
284 | }
285 | }
286 |
287 | private class MyEdge implements Edge {
288 |
289 | E element;
290 | Vertex vertexOutbound;
291 | Vertex vertexInbound;
292 |
293 | int cost;
294 | int direction;
295 | public MyEdge(E element, Vertex vertexOutbound, Vertex vertexInbound,int cost, int direction) {
296 | this.element = element;
297 | this.vertexOutbound = vertexOutbound;
298 | this.vertexInbound = vertexInbound;
299 | this.cost = cost;
300 | this.direction = direction;
301 | }
302 |
303 | @Override
304 | public E element() {
305 | return this.element;
306 | }
307 |
308 |
309 | public boolean contains(Vertex v) {
310 | return (vertexOutbound == v || vertexInbound == v);
311 | }
312 | @Override
313 | public int getCost(){
314 | return cost;
315 | }
316 | @Override
317 | public int getDirection(){
318 | return direction;
319 | }
320 | @Override
321 | public void setDirection(int direction){
322 | this.direction = direction;
323 | }
324 |
325 | @Override
326 | public Vertex[] vertices() {
327 | @SuppressWarnings("unchecked")
328 | Vertex[] vertices = new Vertex[2];
329 | vertices[0] = vertexOutbound;
330 | vertices[1] = vertexInbound;
331 |
332 | return vertices;
333 | }
334 |
335 | @Override
336 | public String toString() {
337 | return "Edge{{" + element + "}, vertexOutbound=" + vertexOutbound.toString()
338 | + ", vertexInbound=" + vertexInbound.toString() + '}';
339 | }
340 |
341 | public Vertex getOutbound() {
342 | return vertexOutbound;
343 | }
344 |
345 | public Vertex getInbound() {
346 | return vertexInbound;
347 | }
348 | }
349 |
350 | /**
351 | * Checks whether a given vertex is valid and belongs to this graph.
352 | *
353 | * @param v the vertex to check
354 | * @return the reference of the vertex, with cast to the underlying implementation of {@link Vertex}
355 | * @throws InvalidVertexException if the vertex is null or does not belong to this graph
356 | */
357 | private MyVertex checkVertex(Vertex v) throws InvalidVertexException {
358 | if(v == null) throw new InvalidVertexException("Null vertex.");
359 |
360 | MyVertex vertex;
361 | try {
362 | vertex = (MyVertex) v;
363 | } catch (ClassCastException e) {
364 | throw new InvalidVertexException("Not a vertex.");
365 | }
366 |
367 | if (!vertices.containsKey(vertex.element)) {
368 | throw new InvalidVertexException("Vertex does not belong to this graph.");
369 | }
370 |
371 | return vertex;
372 | }
373 |
374 | private MyEdge checkEdge(Edge e) throws InvalidEdgeException {
375 | if(e == null) throw new InvalidEdgeException("Null edge.");
376 |
377 | MyEdge edge;
378 | try {
379 | edge = (MyEdge) e;
380 | } catch (ClassCastException ex) {
381 | throw new InvalidVertexException("Not an adge.");
382 | }
383 |
384 | if (!edges.containsKey(edge.element)) {
385 | throw new InvalidEdgeException("Edge does not belong to this graph.");
386 | }
387 |
388 | return edge;
389 | }
390 |
391 | }
392 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graph/Edge.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 |
8 | /*
9 | * PathGraph v1.0.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/releases/tag/1.0.0)
10 | * PathGraph | Copyright 2024 Vittorio Piotti
11 | * Licensed under GPL v3.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt)
12 | */
13 |
14 | package com.vittoriopiotti.pathgraph.graph;
15 |
16 | /**
17 | * An edge connects two {@link Vertex} of type V and stores
18 | * an element of type E.
19 | *
20 | * The edge may be used in oriented and non-oriented graphs.
21 | *
22 | * @param Type of value stored in the edge
23 | * @param Type of value stored in the vertices that this edge connects.
24 | *
25 | * @see Graph
26 | * @see Digraph
27 | *
28 | * @author brunomnsilva
29 | *
Modified by vittoriopiotti
30 | */
31 | public interface Edge {
32 |
33 | /**
34 | * Returns the element stored in the edge.
35 | *
36 | * @return stored element
37 | *
38 | * @author brunomnsilva
39 | */
40 | E element();
41 |
42 | /**
43 | * Returns and array of size 2, with references for both vertices at the ends
44 | * of an edge.
45 | *
46 | * In a {@link Digraph} the reference at {@code vertices()[0]} must be that
47 | * of the outbound vertex and at {@code vertices()[1]} that of the inbound
48 | * vertex.
49 | *
50 | * @return an array of length 2, containing the vertices at both ends.
51 | *
52 | * @author brunomnsilva
53 | */
54 | Vertex[] vertices();
55 |
56 |
57 | /**
58 | * Returns the cost associated with this edge.
59 | *
60 | * @return the cost of the edge
61 | *
62 | * @author vittoriopiotti
63 | */
64 | int getCost();
65 |
66 |
67 | /**
68 | * Returns the direction of the edge.
69 | *
70 | * In a directed graph, this value can be used to determine if the edge
71 | * points from the first vertex to the second or vice versa.
72 | *
73 | * @return the direction of the edge
74 | *
75 | * @author vittoriopiotti
76 | */
77 | int getDirection();
78 |
79 |
80 |
81 | /**
82 | * Sets the direction of the edge.
83 | *
84 | * This method allows to specify the direction the edge should have.
85 | * In a directed graph, this typically determines which vertex is the start
86 | * and which is the end.
87 | *
88 | * @param direction the direction to set for the edge
89 | *
90 | * @author vittoriopiotti
91 | */
92 | void setDirection(int direction);
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graph/Graph.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 |
8 | /*
9 | * PathGraph v1.0.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/releases/tag/1.0.0)
10 | * PathGraph | Copyright 2024 Vittorio Piotti
11 | * Licensed under GPL v3.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt)
12 | */
13 |
14 | package com.vittoriopiotti.pathgraph.graph;
15 | import com.vittoriopiotti.pathgraph.app.Constants;
16 |
17 | import java.util.Collection;
18 |
19 | /**
20 | * A graph is made up of a set of vertices connected by edges, where the edges
21 | * have no direction associated with them, i.e., they establish a two-way connection.
22 | *
23 | * @param Type of element stored at a vertex
24 | * @param Type of element stored at an edge
25 | *
26 | * @see Edge
27 | * @see Vertex
28 | *
29 | * @author brunomnsilva
30 | *
Modified by vittoriopiotti
31 | */
32 | public interface Graph {
33 |
34 | /**
35 | * Returns the total number of vertices of the graph.
36 | *
37 | * @return total number of vertices
38 | *
39 | * @author brunomnsilva
40 | */
41 | int numVertices();
42 |
43 | /**
44 | * Returns the total number of edges of the graph.
45 | *
46 | * @return total number of vertices
47 | *
48 | * @author brunomnsilva
49 | */
50 | int numEdges();
51 |
52 | /**
53 | * Returns the vertices of the graph as a collection.
54 | *
55 | * If there are no vertices, returns an empty collection.
56 | *
57 | * @return collection of vertices
58 | *
59 | * @author brunomnsilva
60 | */
61 | Collection> vertices();
62 |
63 | /**
64 | * Returns the edges of the graph as a collection.
65 | *
66 | * If there are no edges, returns an empty collection.
67 | *
68 | * @return collection of edges
69 | *
70 | * @author brunomnsilva
71 | */
72 | Collection> edges();
73 |
74 | /**
75 | * Returns a vertex's incident edges as a collection.
76 | *
77 | * Incident edges are all edges that are connected to vertex v.
78 | * If there are no incident edges, e.g., an isolated vertex,
79 | * returns an empty collection.
80 | *
81 | * @param v vertex for which to obtain the incident edges
82 | *
83 | * @return collection of edges
84 | *
85 | * @throws InvalidVertexException if the vertex is invalid for the graph
86 | *
87 | * @author brunomnsilva
88 | */
89 | Collection> incidentEdges(Vertex v)
90 | throws InvalidVertexException;
91 |
92 | /**
93 | * Given vertex v, return the opposite vertex at the other end
94 | * of edge e.
95 | *
96 | * If both v and e are valid, but e
97 | * is not connected to v, returns null.
98 | *
99 | * @param v vertex on one end of e
100 | * @param e edge connected to v
101 | * @return opposite vertex along e
102 | *
103 | * @throws InvalidVertexException if the vertex is invalid for the graph
104 | * @throws InvalidEdgeException if the edge is invalid for the graph
105 | *
106 | * @author brunomnsilva
107 | */
108 | Vertex opposite(Vertex v, Edge e)
109 | throws InvalidVertexException, InvalidEdgeException;
110 |
111 | /**
112 | * Evaluates whether two vertices are adjacent, i.e., there exists some
113 | * edge connecting u and v.
114 | *
115 | * @param u a vertex
116 | * @param v another vertex
117 | *
118 | * @return true if they are adjacent, false otherwise.
119 | *
120 | * @throws InvalidVertexException if u or v
121 | * are invalid vertices for the graph
122 | *
123 | * @author brunomnsilva
124 | */
125 | boolean areAdjacent(Vertex u, Vertex v)
126 | throws InvalidVertexException;
127 |
128 | /**
129 | * Inserts a new vertex with a given element, returning its reference.
130 | *
131 | * @param vElement the element to store at the vertex
132 | *
133 | * @return the reference of the newly created vertex
134 | *
135 | * @throws InvalidVertexException if there already exists a vertex
136 | * containing vElement
137 | * according to the equality of
138 | * {@link Object#equals(java.lang.Object) }
139 | * method.
140 | *
141 | * @author brunomnsilva
142 | */
143 | Vertex insertVertex(V vElement)
144 | throws InvalidVertexException;
145 |
146 | /**
147 | * Inserts a new edge with a given element between two existing vertices and
148 | * returns its (the edge's) reference.
149 | *
150 | * @param u a vertex
151 | * @param v another vertex
152 | * @param edgeElement the element to store in the new edge
153 | * @param cost the cost associated with the edge
154 | * @param direction the direction of the edge, which can be one of:
155 | * {@link Constants#BIDIRECTIONAL},
156 | * {@link Constants#DIRECTION_FIRST},
157 | * or {@link Constants#DIRECTION_SECOND}
158 | *
159 | * @return the reference for the newly created edge
160 | *
161 | * @throws InvalidVertexException if u or v
162 | * are invalid vertices for the graph
163 | *
164 | * @throws InvalidEdgeException if there already exists an edge
165 | * containing edgeElement
166 | * according to the equality of
167 | * {@link Object#equals(java.lang.Object)}
168 | * method.
169 | *
170 | * @author brunomnsilva
171 | *
Modified by vittoriopiotti
172 | */
173 | Edge insertEdge(Vertex u, Vertex v, E edgeElement,int cost,int direction)
174 | throws InvalidVertexException, InvalidEdgeException;
175 |
176 |
177 | /**
178 | * Inserts a new edge with a given element between two existing vertices and
179 | * returns its (the edge's) reference.
180 | *
181 | * @param vElement1 a vertex's stored element
182 | * @param vElement2 another vertex's stored element
183 | * @param edgeElement the element to store in the new edge
184 | * @param cost the cost associated with the edge
185 | * @param direction the direction of the edge, which can be one of:
186 | * {@link Constants#BIDIRECTIONAL},
187 | * {@link Constants#DIRECTION_FIRST},
188 | * or {@link Constants#DIRECTION_SECOND}
189 | *
190 | * @return the reference for the newly created edge
191 | *
192 | * @throws InvalidVertexException if vElement1 or
193 | * vElement2
194 | * are not found in any vertices of the graph
195 | * according to the equality of
196 | * {@link Object#equals(java.lang.Object)}
197 | * method.
198 | *
199 | * @throws InvalidEdgeException if there already exists an edge
200 | * containing edgeElement
201 | * according to the equality of
202 | * {@link Object#equals(java.lang.Object)}
203 | * method.
204 | *
205 | * @author brunomnsilva
206 | *
Modified by vittoriopiotti
207 | */
208 | Edge insertEdge(V vElement1, V vElement2, E edgeElement,int cost,int direction)
209 | throws InvalidVertexException, InvalidEdgeException;
210 |
211 | /**
212 | * Removes a vertex, along with all of its incident edges, and returns the element
213 | * stored at the removed vertex.
214 | *
215 | * @param v vertex to remove
216 | *
217 | * @return element stored at the removed vertex
218 | *
219 | * @throws InvalidVertexException if v is an invalid vertex for the graph
220 | *
221 | * @author brunomnsilva
222 | */
223 | V removeVertex(Vertex v) throws InvalidVertexException;
224 |
225 | /**
226 | * Removes an edge and return its element.
227 | *
228 | * @param e edge to remove
229 | *
230 | * @return element stored at the removed edge
231 | *
232 | * @throws InvalidEdgeException if e is an invalid edge for the graph.
233 | *
234 | * @author brunomnsilva
235 | */
236 | E removeEdge(Edge e) throws InvalidEdgeException;
237 |
238 | /**
239 | * Replaces the element of a given vertex with a new element and returns the
240 | * previous element stored at v.
241 | *
242 | * @param v vertex to replace its element
243 | * @param newElement new element to store in v
244 | *
245 | * @return previous element previously stored in v
246 | *
247 | * @throws InvalidVertexException if the vertex v is invalid for the graph, or;
248 | * if there already exists another vertex containing
249 | * the element newElement
250 | * according to the equality of
251 | * {@link Object#equals(java.lang.Object) }
252 | * method.
253 | *
254 | * @author brunomnsilva
255 | */
256 | V replace(Vertex v, V newElement) throws InvalidVertexException;
257 |
258 | /**
259 | * Replaces the element of a given edge with a new element and returns the
260 | * previous element stored at e.
261 | *
262 | * @param e edge to replace its element
263 | * @param newElement new element to store in e
264 | *
265 | * @return previous element previously stored in e
266 | *
267 | * @throws InvalidEdgeException if the edge e is invalid for the graph, or;
268 | * if there already exists another edge containing
269 | * the element newElement
270 | * according to the equality of
271 | * {@link Object#equals(java.lang.Object)}
272 | * method.
273 | *
274 | * @author brunomnsilva
275 | */
276 | E replace(Edge e, E newElement) throws InvalidEdgeException;
277 | }
278 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graph/GraphEdgeList.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 |
8 | /*
9 | * PathGraph v1.0.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/releases/tag/1.0.0)
10 | * PathGraph | Copyright 2024 Vittorio Piotti
11 | * Licensed under GPL v3.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt)
12 | */
13 |
14 | package com.vittoriopiotti.pathgraph.graph;
15 |
16 | import java.util.*;
17 |
18 | /**
19 | * ADT Graph implementation that stores a collection of edges (and vertices) and
20 | * where each edge contains the references for the vertices it connects.
21 | *
22 | * Does not allow duplicates of stored elements through equals criteria.
23 | *
24 | * @param Type of element stored at a vertex
25 | * @param Type of element stored at an edge
26 | *
27 | * @author brunomnsilva
28 | */
29 | public class GraphEdgeList implements Graph {
30 |
31 | /* inner classes are defined at the end of the class, so are the auxiliary methods
32 | */
33 | private final Map> vertices;
34 | private final Map> edges;
35 |
36 | /**
37 | * Default constructor that initializes an empty graph.
38 | */
39 | public GraphEdgeList() {
40 | this.vertices = new HashMap<>();
41 | this.edges = new HashMap<>();
42 | }
43 |
44 | @Override
45 | public int numVertices() {
46 | return vertices.size();
47 | }
48 |
49 | @Override
50 | public int numEdges() {
51 | return edges.size();
52 | }
53 |
54 | @Override
55 | public Collection> vertices() {
56 | return new ArrayList<>(vertices.values());
57 | }
58 |
59 | @Override
60 | public Collection> edges() {
61 | return new ArrayList<>(edges.values());
62 | }
63 |
64 | @Override
65 | public Collection> incidentEdges(Vertex v) throws InvalidVertexException {
66 |
67 | checkVertex(v);
68 |
69 | List> incidentEdges = new ArrayList<>();
70 | for (Edge edge : edges.values()) {
71 |
72 | if (((MyEdge) edge).contains(v)) {
73 | /* edge.vertices()[0] == v || edge.vertices()[1] == v */
74 | incidentEdges.add(edge);
75 | }
76 |
77 | }
78 |
79 | return incidentEdges;
80 | }
81 |
82 | @Override
83 | public Vertex opposite(Vertex v, Edge e) throws InvalidVertexException, InvalidEdgeException {
84 | checkVertex(v);
85 | MyEdge edge = checkEdge(e);
86 |
87 | if (!edge.contains(v)) {
88 | return null; /* this edge does not connect vertex v */
89 | }
90 |
91 | if (edge.vertices()[0] == v) {
92 | return edge.vertices()[1];
93 | } else {
94 | return edge.vertices()[0];
95 | }
96 |
97 | }
98 |
99 | @Override
100 | public synchronized boolean areAdjacent(Vertex u, Vertex v) throws InvalidVertexException {
101 | //we allow loops, so we do not check if u == v
102 | checkVertex(v);
103 | checkVertex(u);
104 |
105 | /* find and edge that contains both u and v */
106 | for (Edge edge : edges.values()) {
107 | if (((MyEdge) edge).contains(u) && ((MyEdge) edge).contains(v)) {
108 | return true;
109 | }
110 | }
111 | return false;
112 | }
113 |
114 | @Override
115 | public synchronized Vertex insertVertex(V vElement) throws InvalidVertexException {
116 | if (existsVertexWith(vElement)) {
117 | throw new InvalidVertexException("There's already a vertex with this element.");
118 | }
119 |
120 | MyVertex newVertex = new MyVertex(vElement);
121 |
122 | vertices.put(vElement, newVertex);
123 |
124 | return newVertex;
125 | }
126 |
127 | @Override
128 | public synchronized Edge insertEdge(Vertex u, Vertex v, E edgeElement,int cost,int direction)
129 | throws InvalidVertexException, InvalidEdgeException {
130 |
131 | if (existsEdgeWith(edgeElement)) {
132 | throw new InvalidEdgeException("There's already an edge with this element.");
133 | }
134 |
135 | MyVertex outVertex = checkVertex(u);
136 | MyVertex inVertex = checkVertex(v);
137 |
138 | MyEdge newEdge = new MyEdge(edgeElement, outVertex, inVertex,cost,direction);
139 |
140 | edges.put(edgeElement, newEdge);
141 |
142 | return newEdge;
143 |
144 | }
145 |
146 | @Override
147 | public synchronized Edge insertEdge(V vElement1, V vElement2, E edgeElement,int cost,int direction)
148 | throws InvalidVertexException, InvalidEdgeException {
149 |
150 | if (existsEdgeWith(edgeElement)) {
151 | throw new InvalidEdgeException("There's already an edge with this element.");
152 | }
153 |
154 | if (!existsVertexWith(vElement1)) {
155 | throw new InvalidVertexException("No vertex contains " + vElement1);
156 | }
157 | if (!existsVertexWith(vElement2)) {
158 | throw new InvalidVertexException("No vertex contains " + vElement2);
159 | }
160 |
161 | MyVertex outVertex = vertexOf(vElement1);
162 | MyVertex inVertex = vertexOf(vElement2);
163 |
164 | MyEdge newEdge = new MyEdge(edgeElement, outVertex, inVertex,cost,direction);
165 |
166 | edges.put(edgeElement, newEdge);
167 |
168 | return newEdge;
169 |
170 | }
171 |
172 | @Override
173 | public synchronized V removeVertex(Vertex v) throws InvalidVertexException {
174 | checkVertex(v);
175 |
176 | V element = v.element();
177 |
178 | //remove incident edges
179 | Iterable> incidentEdges = incidentEdges(v);
180 | for (Edge edge : incidentEdges) {
181 | edges.remove(edge.element());
182 | }
183 |
184 | vertices.remove(v.element());
185 |
186 | return element;
187 | }
188 |
189 | @Override
190 | public synchronized E removeEdge(Edge e) throws InvalidEdgeException {
191 | checkEdge(e);
192 |
193 | E element = e.element();
194 | edges.remove(e.element());
195 |
196 | return element;
197 | }
198 |
199 | @Override
200 | public V replace(Vertex v, V newElement) throws InvalidVertexException {
201 | if (existsVertexWith(newElement)) {
202 | throw new InvalidVertexException("There's already a vertex with this element.");
203 | }
204 |
205 | MyVertex vertex = checkVertex(v);
206 |
207 | V oldElement = vertex.element;
208 | vertex.element = newElement;
209 |
210 | return oldElement;
211 | }
212 |
213 | @Override
214 | public E replace(Edge e, E newElement) throws InvalidEdgeException {
215 | if (existsEdgeWith(newElement)) {
216 | throw new InvalidEdgeException("There's already an edge with this element.");
217 | }
218 |
219 | MyEdge edge = checkEdge(e);
220 |
221 | E oldElement = edge.element;
222 | edge.element = newElement;
223 |
224 | return oldElement;
225 | }
226 |
227 | private MyVertex vertexOf(V vElement) {
228 | for (Vertex v : vertices.values()) {
229 | if (v.element().equals(vElement)) {
230 | return (MyVertex) v;
231 | }
232 | }
233 | return null;
234 | }
235 |
236 | private boolean existsVertexWith(V vElement) {
237 | return vertices.containsKey(vElement);
238 | }
239 |
240 | private boolean existsEdgeWith(E edgeElement) {
241 | return edges.containsKey(edgeElement);
242 | }
243 |
244 | @Override
245 | public String toString() {
246 | StringBuilder sb = new StringBuilder(
247 | String.format("Graph with %d vertices and %d edges:\n", numVertices(), numEdges())
248 | );
249 |
250 | sb.append("--- Vertices: \n");
251 | for (Vertex v : vertices.values()) {
252 | sb.append("\t").append(v.toString()).append("\n");
253 | }
254 | sb.append("\n--- Edges: \n");
255 | for (Edge e : edges.values()) {
256 | sb.append("\t").append(e.toString()).append("\n");
257 | }
258 | return sb.toString();
259 | }
260 |
261 | class MyVertex implements Vertex {
262 |
263 | V element;
264 |
265 | public MyVertex(V element) {
266 | this.element = element;
267 | }
268 |
269 | @Override
270 | public V element() {
271 | return this.element;
272 | }
273 |
274 | @Override
275 | public String toString() {
276 | return "Vertex{" + element + '}';
277 | }
278 | }
279 |
280 | class MyEdge implements Edge {
281 |
282 | E element;
283 | Vertex vertexOutbound;
284 | Vertex vertexInbound;
285 | int cost;
286 | int direction;
287 |
288 | public MyEdge(E element, Vertex vertexOutbound, Vertex vertexInbound,int cost,int direction) {
289 | this.element = element;
290 | this.vertexOutbound = vertexOutbound;
291 | this.vertexInbound = vertexInbound;
292 | this.cost = cost;
293 | this.direction = direction;
294 | }
295 |
296 | @Override
297 | public E element() {
298 | return this.element;
299 | }
300 |
301 |
302 | public boolean contains(Vertex v) {
303 | return (vertexOutbound == v || vertexInbound == v);
304 | }
305 |
306 | @Override
307 | public int getCost(){
308 | return cost;
309 | }
310 |
311 | @Override
312 | public int getDirection(){
313 | return direction;
314 | }
315 |
316 | @Override
317 | public void setDirection(int direction){
318 | this.direction = direction;
319 | }
320 |
321 |
322 | @Override
323 | public Vertex[] vertices() {
324 | @SuppressWarnings("unchecked")
325 | Vertex[] vertices = new Vertex[2];
326 | vertices[0] = vertexOutbound;
327 | vertices[1] = vertexInbound;
328 |
329 | return vertices;
330 | }
331 |
332 | @Override
333 | public String toString() {
334 | return "Edge{{" + element + "}, vertexOutbound=" + vertexOutbound.toString()
335 | + ", vertexInbound=" + vertexInbound.toString() + '}';
336 | }
337 | }
338 |
339 | /**
340 | * Checks whether a given vertex is valid (i.e., not null) and belongs to this graph
341 | *
342 | * @param v vertex to check
343 | * @return the reference of the vertex
344 | * @throws InvalidVertexException if the vertex is invalid
345 | */
346 | private MyVertex checkVertex(Vertex v) throws InvalidVertexException {
347 | if(v == null) throw new InvalidVertexException("Null vertex.");
348 |
349 | MyVertex vertex;
350 | try {
351 | vertex = (MyVertex) v;
352 | } catch (ClassCastException e) {
353 | throw new InvalidVertexException("Not a vertex.");
354 | }
355 |
356 | if (!vertices.containsKey(vertex.element)) {
357 | throw new InvalidVertexException("Vertex does not belong to this graph.");
358 | }
359 |
360 | return vertex;
361 | }
362 |
363 | private MyEdge checkEdge(Edge e) throws InvalidEdgeException {
364 | if(e == null) throw new InvalidEdgeException("Null edge.");
365 |
366 | MyEdge edge;
367 | try {
368 | edge = (MyEdge) e;
369 | } catch (ClassCastException ex) {
370 | throw new InvalidVertexException("Not an adge.");
371 | }
372 |
373 | if (!edges.containsKey(edge.element)) {
374 | throw new InvalidEdgeException("Edge does not belong to this graph.");
375 | }
376 |
377 | return edge;
378 | }
379 | }
380 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graph/InvalidEdgeException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 |
8 | /*
9 | * PathGraph v1.0.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/releases/tag/1.0.0)
10 | * PathGraph | Copyright 2024 Vittorio Piotti
11 | * Licensed under GPL v3.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt)
12 | */
13 |
14 | package com.vittoriopiotti.pathgraph.graph;
15 |
16 | /**
17 | * Thrown when using an invalid edge in calls of methods in {@link Graph}
18 | * and {@link Digraph} implementations.
19 | *
20 | * @see Graph
21 | * @see Digraph
22 | *
23 | * @author brunomnsilva
24 | */
25 | public class InvalidEdgeException extends RuntimeException {
26 |
27 | /**
28 | * Constructs a new InvalidEdgeException with a default error message.
29 | */
30 | public InvalidEdgeException() {
31 | super("The edge is invalid or does not belong to this graph.");
32 | }
33 |
34 | /**
35 | * Constructs a new InvalidEdgeException with the specified error message.
36 | *
37 | * @param message the error message to display
38 | */
39 | public InvalidEdgeException(String message) {
40 | super(message);
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graph/InvalidVertexException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 |
8 | /*
9 | * PathGraph v1.0.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/releases/tag/1.0.0)
10 | * PathGraph | Copyright 2024 Vittorio Piotti
11 | * Licensed under GPL v3.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt)
12 | */
13 | package com.vittoriopiotti.pathgraph.graph;
14 |
15 | /**
16 | * Thrown when using an invalid vertex in calls of methods in {@link Graph}
17 | * and {@link Digraph} implementations.
18 | *
19 | * @see Graph
20 | * @see Digraph
21 | *
22 | * @author brunomnsilva
23 | */
24 | public class InvalidVertexException extends RuntimeException {
25 |
26 | /**
27 | * Constructs a new InvalidVertexException with a default error message.
28 | */
29 | public InvalidVertexException() {
30 | super("The vertex is invalid or does not belong to this graph.");
31 | }
32 |
33 | /**
34 | * Constructs a new InvalidVertexException with the specified error message.
35 | *
36 | * @param message the error message to display
37 | */
38 | public InvalidVertexException(String message) {
39 | super(message);
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graph/Vertex.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 |
8 | /*
9 | * PathGraph v1.0.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/releases/tag/1.0.0)
10 | * PathGraph | Copyright 2024 Vittorio Piotti
11 | * Licensed under GPL v3.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt)
12 | */
13 | package com.vittoriopiotti.pathgraph.graph;
14 |
15 | /**
16 | * A vertex contains an element of type V and is used both in
17 | * graphs and digraphs.
18 | *
19 | * @param Type of value stored in the vertex.
20 | *
21 | * @see Graph
22 | * @see Digraph
23 | *
24 | * @author brunomnsilva
25 | */
26 | public interface Vertex {
27 |
28 | /**
29 | * Returns the element stored in the vertex.
30 | *
31 | * @return stored element
32 | */
33 | V element();
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graphview/Args.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 |
8 |
9 | package com.vittoriopiotti.pathgraph.graphview;
10 |
11 | /**
12 | * The Args class provides a collection of static methods for checking method parameters and throwing
13 | * IllegalArgumentExceptions if the parameters do not meet the specified requirements.
14 | *
15 | * @author brunomnsilva
16 | */
17 | public final class Args {
18 |
19 | /**
20 | * Checks if the specified parameter is null and throws an IllegalArgumentException if it is.
21 | *
22 | * @param param the parameter to check for null
23 | * @param name the name of the parameter being checked
24 | * @throws IllegalArgumentException if the specified parameter is null
25 | */
26 | public static void requireNotNull(Object param, String name) {
27 | if (param == null) {
28 | throw new IllegalArgumentException(String.format("Require '%s' to be not null.", name));
29 | }
30 | }
31 |
32 | /**
33 | * Checks if a specified double value is greater than a specified minimum value and throws an IllegalArgumentException
34 | * if it is not. Uses a scaled comparison to avoid floating-point precision errors.
35 | *
36 | * @param value the double value to check
37 | * @param name the name of the double value being checked
38 | * @param minValue the minimum value that the specified double value must be greater than
39 | * @throws IllegalArgumentException if the specified double value is less than or equal to the specified minimum value
40 | */
41 | public static void requireGreaterThan(double value, String name, double minValue) {
42 | if(value <= minValue) {
43 | throw new IllegalArgumentException(String.format("Require '%s' (%f) to be greater than %f.", name, value, minValue));
44 | }
45 | }
46 |
47 | /**
48 | * Checks if a specified double value is non-negative and throws an IllegalArgumentException if it is not.
49 | *
50 | * @param value the double value to check
51 | * @param name the name of the double value being checked
52 | * @throws IllegalArgumentException if the specified double value is negative
53 | */
54 | public static void requireNonNegative(double value, String name) {
55 | if (value < 0.0) {
56 | throw new IllegalArgumentException(String.format("Require '%s' (%f) to be non-negative.", name, value));
57 | }
58 | }
59 |
60 | /**
61 | * This method checks if a value falls within a specified range.
62 | * If the value is less than the lower bound or greater than the upper bound, an IllegalArgumentException is thrown.
63 | * @param value the value to check
64 | * @param name the name of the value being checked
65 | * @param lowerBound the lower bound of the range (inclusive)
66 | * @param upperBound the upper bound of the range (inclusive)
67 | * @throws IllegalArgumentException if the value is outside the specified range
68 | */
69 | public static void requireInRange(double value, String name, double lowerBound, double upperBound) {
70 | if (value < lowerBound || value > upperBound) {
71 | throw new IllegalArgumentException(String.format("Require '%s' (%f) to be in range [%f, %f].", name, value, lowerBound, upperBound));
72 | }
73 | }
74 |
75 | /**
76 | * Checks if the given double value is finite (i.e., not NaN or infinite).
77 | * @param value the double value to check for finiteness
78 | * @param name the name of the value being checked
79 | * @throws IllegalArgumentException if the value is not finite
80 | */
81 | public static void requireFinite(double value, String name) {
82 | if (!Double.isFinite(value)) {
83 | throw new IllegalArgumentException(String.format("Require '%s' (%f) to be finite.", name, value));
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graphview/ForceDirectedLayoutStrategy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 |
8 | package com.vittoriopiotti.pathgraph.graphview;
9 |
10 | import javafx.geometry.Point2D;
11 |
12 | import java.util.Collection;
13 |
14 | /**
15 | * A representation of a force directed layout "strategy" used during automatic layout of nodes in a {@link SmartGraphPanel}.
16 | *
17 | * Implementing classes should compute attractive and repulsive forces according to some algorithm.
18 | * Typically, if two graph nodes are not adjacent the force should be dominated by the repulsive force.
19 | *
20 | * See: Wikipedia - Force-directed graph drawing
21 | *
22 | * @param The generic type of {@link SmartGraphVertexNode}, i.e., the nodes of a {@link SmartGraphPanel}.
23 | *
24 | * @author brunomnsilva
25 | */
26 | public abstract class ForceDirectedLayoutStrategy {
27 |
28 | /**
29 | * This method must compute forces between all graph nodes. Typically, repelling forces exist between all nodes (similarly to particles
30 | * with the same polarity), but attractive forces only exist between adjacent nodes (nodes that are connected).
31 | *
32 | * The default behavior is to iterate over all distinct pairs of nodes and compute
33 | * their combined forces (attractive and repulsive), by calling {@link #computeForceBetween(SmartGraphVertexNode, SmartGraphVertexNode, double, double)}.
34 | *
35 | * Other strategies that rely on some link of global metrics should override this method.
36 | *
37 | * @param nodes the current nodes of the graph
38 | * @param panelWidth the graph panel's width
39 | * @param panelHeight the graph panel's height
40 | */
41 | public void computeForces(Collection> nodes, double panelWidth, double panelHeight) {
42 | for (SmartGraphVertexNode v : nodes) {
43 | for (SmartGraphVertexNode w : nodes) {
44 | if(v == w) continue;
45 |
46 | Point2D force = computeForceBetween(v, w, panelWidth, panelHeight);
47 | v.addForceVector(force.getX(), force.getY());
48 | }
49 | }
50 | }
51 |
52 | /**
53 | * Computes a force vector between two nodes. The force vector is the result of the attractive and repulsive force between the two.
54 | *
55 | * @param v a node
56 | * @param w another node
57 | * @param panelWidth the graph panel's width
58 | * @param panelHeight the graph panel's height
59 | * @return the force vector
60 | */
61 | protected abstract Point2D computeForceBetween(SmartGraphVertexNode v, SmartGraphVertexNode w, double panelWidth, double panelHeight);
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graphview/ForceDirectedSpringGravityLayoutStrategy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 | package com.vittoriopiotti.pathgraph.graphview;
8 |
9 | import javafx.geometry.Point2D;
10 |
11 | import java.util.Collection;
12 |
13 | /**
14 | * An implementation of a spring system layout strategy with gravity towards the center.
15 | *
16 | * Applies the same spring system as {@link ForceDirectedSpringSystemLayoutStrategy} but with added gravitational pull
17 | * towards the center of the panel. Even with bipartite graphs, they will not repel each other to the edges of the panel.
18 | *
19 | * Parameters:
20 | *
21 | * Repulsive force (> 0): Recommended [1, 50]. Default 25. The strength of the repulsive force between nodes.
22 | * Higher values result in greater repulsion.
23 | *
24 | * Attraction force (> 0): Recommended [1, 5]. Default 3. The strength of the attractive force between connected nodes.
25 | * Higher values result in stronger attraction. Careful, because larger values may not produce stable states.
26 | *
27 | * Attraction scale (> 0): Recommended [1, ?]. Default 10. The scale factor for attraction.
28 | * It determines the effectiveness of the attraction force based on the distance between connected nodes.
29 | *
30 | * Acceleration: Mandatory [0, 1]. Default 0.8. The acceleration factor applied to node movements.
31 | * Higher values result in faster movements.
32 | *
33 | * Gravity (> 0): Recommended [0, 0.1]. Default 0.01. The higher the value, the more dominant the gravitation "pull" is.
34 | * Careful, because larger values may not produce stable states. The gravity force is applied after
35 | * the attraction/repulsive forces between nodes are computed. We don't want this force to dominate the layout placement.
36 | *
37 | * @param The generic type of {@link SmartGraphVertexNode}, i.e., the nodes of a {@link SmartGraphPanel}.
38 | *
39 | * @author brunomnsilva
40 | */
41 | public class ForceDirectedSpringGravityLayoutStrategy extends ForceDirectedSpringSystemLayoutStrategy {
42 |
43 | private final double gravity;
44 |
45 | /**
46 | * Constructs a new instance of ForceDirectedSpringGravityLayoutStrategy with default parameters, namely:
47 | *
48 | * repulsiveForce = 25, attractionForce = 3, attractionScale = 10, acceleration = 0.8 and gravity = 0.01.
49 | */
50 | public ForceDirectedSpringGravityLayoutStrategy() {
51 | super();
52 | this.gravity = 0.01;
53 | }
54 |
55 | /**
56 | * Constructs a new instance of ForceDirectedSpringGravityLayoutStrategy with the specified parameters.
57 | *
58 | * @param repulsiveForce The strength of the repulsive force between nodes. Higher values result in greater repulsion.
59 | * @param attractionForce The strength of the attractive force between connected nodes. Higher values result in stronger attraction.
60 | * @param attractionScale The scale factor for attraction. It determines the effectiveness of the attraction force based on the distance between connected nodes.
61 | * @param acceleration The acceleration factor applied to node movements. Higher values result in faster movements.
62 | * @param gravity The strength of the gravity force applied to all nodes, attracting them towards the center of the layout area.
63 | */
64 | public ForceDirectedSpringGravityLayoutStrategy(double repulsiveForce, double attractionForce, double attractionScale,
65 | double acceleration, double gravity) {
66 | super(repulsiveForce, attractionForce, attractionScale, acceleration);
67 |
68 | Args.requireGreaterThan(gravity, "gravity", 0);
69 | Args.requireInRange(gravity, "gravity", 0, 1);
70 | this.gravity = gravity;
71 | }
72 |
73 | @Override
74 | public void computeForces(Collection> nodes, double panelWidth, double panelHeight) {
75 | // Attractive and repulsive forces
76 | for (SmartGraphVertexNode v : nodes) {
77 | for (SmartGraphVertexNode w : nodes) {
78 | if(v == w) continue;
79 |
80 | Point2D force = computeForceBetween(v, w, panelWidth, panelHeight);
81 | v.addForceVector(force.getX(), force.getY());
82 | }
83 | }
84 |
85 | // Gravitational pull towards the center for all nodes
86 | double centerX = panelWidth / 2;
87 | double centerY = panelHeight / 2;
88 |
89 | for (SmartGraphVertexNode v : nodes) {
90 | Point2D curPosition = v.getUpdatedPosition();
91 | Point2D forceCenter = new Point2D(centerX - curPosition.getX(), centerY - curPosition.getY())
92 | .multiply(gravity);
93 |
94 | v.addForceVector(forceCenter.getX(), forceCenter.getY());
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graphview/ForceDirectedSpringSystemLayoutStrategy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 | package com.vittoriopiotti.pathgraph.graphview;
8 |
9 | import javafx.geometry.Point2D;
10 |
11 | /**
12 | * An implementation of a spring system layout strategy. This strategy allows to freely move the graph along
13 | * the panel, but if you have a bipartite graph, the sub-graphs will repel each other to the edges of the panel.
14 | *
15 | * Parameters:
16 | *
17 | * Repulsive force (> 0): Recommended [1, 50]. Default 25. The strength of the repulsive force between nodes.
18 | * Higher values result in greater repulsion.
19 | *
20 | * Attraction force (> 0): Recommended [1, 5]. Default 3. The strength of the attractive force between connected nodes.
21 | * Higher values result in stronger attraction. Careful, because larger values may not produce stable states.
22 | *
23 | * Attraction scale (> 0): Recommended [1, ?]. Default 10. The scale factor for attraction.
24 | * It determines the effectiveness of the attraction force based on the distance between connected nodes.
25 | *
26 | * Acceleration: Mandatory [0, 1]. Default 0.8. The acceleration factor applied to node movements.
27 | * Higher values result in faster movements.
28 | *
29 | * @param The generic type of {@link SmartGraphVertexNode}, i.e., the nodes of a {@link SmartGraphPanel}.
30 | *
31 | * @author brunomnsilva
32 | */
33 | public class ForceDirectedSpringSystemLayoutStrategy extends ForceDirectedLayoutStrategy {
34 |
35 | private final double repulsiveForce;
36 | private final double attractionForce;
37 | private final double attractionScale;
38 | private final double acceleration;
39 |
40 | /* just a scaling factor so all parameters are, at most, two-digit numbers. */
41 | private static final double A_THOUSAND = 1000;
42 |
43 | /**
44 | * Constructs a new instance of ForceDirectedSpringGravityLayoutStrategy with default parameters, namely:
45 | *
46 | * repulsiveForce = 25, attractionForce = 3, attractionScale = 10 and acceleration = 0.8.
47 | */
48 | public ForceDirectedSpringSystemLayoutStrategy() {
49 | this.repulsiveForce = 25;
50 | this.attractionForce = 3;
51 | this.attractionScale = 10;
52 | this.acceleration = 0.8;
53 | }
54 |
55 | /**
56 | * Constructs a new instance of ForceDirectedSpringGravityLayoutStrategy with the specified parameters.
57 | *
58 | * @param repulsiveForce The strength of the repulsive force between nodes. Higher values result in greater repulsion.
59 | * @param attractionForce The strength of the attractive force between connected nodes. Higher values result in stronger attraction.
60 | * @param attractionScale The scale factor for attraction. It determines the effectiveness of the attraction force based on the distance between connected nodes.
61 | * @param acceleration The acceleration factor applied to node movements. Higher values result in faster movements.
62 | */
63 | public ForceDirectedSpringSystemLayoutStrategy(double repulsiveForce, double attractionForce, double attractionScale, double acceleration) {
64 | Args.requireGreaterThan(repulsiveForce, "repulsiveForce", 0);
65 | Args.requireGreaterThan(attractionForce, "attractionForce", 0);
66 | Args.requireGreaterThan(attractionScale, "attractionScale", 0);
67 | Args.requireGreaterThan(acceleration, "acceleration", 0);
68 | Args.requireInRange(acceleration, "acceleration", 0, 1);
69 |
70 | this.repulsiveForce = repulsiveForce;
71 | this.attractionForce = attractionForce;
72 | this.attractionScale = attractionScale;
73 | this.acceleration = acceleration;
74 | }
75 |
76 | @Override
77 | protected Point2D computeForceBetween(SmartGraphVertexNode v, SmartGraphVertexNode w, double panelWidth, double panelHeight) {
78 | // The panel's width and height are not used in this strategy
79 | // This allows to freely move the graph to a particular region in the panel;
80 | // On the other hand, e.g., in a bipartite graph the two sub-graphs will repel each other to the edges of the panel
81 |
82 | Point2D vPosition = v.getUpdatedPosition();
83 | Point2D wPosition = w.getUpdatedPosition();
84 | double distance = vPosition.distance(wPosition) - (v.getRadius() + w.getRadius());
85 | Point2D forceDirection = wPosition.subtract(vPosition).normalize();
86 |
87 | if (distance < 1) {
88 | distance = 1;
89 | }
90 |
91 | // attractive force
92 | Point2D attraction;
93 | if(v.isAdjacentTo(w)) {
94 | double attraction_factor = attractionForce * Math.log(distance / attractionScale);
95 | attraction = forceDirection.multiply(attraction_factor);
96 | } else {
97 | attraction = new Point2D(0,0);
98 | }
99 |
100 | // repelling force
101 | double repulsive_factor = repulsiveForce * A_THOUSAND / (distance * distance);
102 | Point2D repulsion = forceDirection.multiply(-repulsive_factor);
103 |
104 | // combine forces
105 | Point2D totalForce = new Point2D(attraction.getX() + repulsion.getX(),
106 | attraction.getY() + repulsion.getY());
107 |
108 | return totalForce.multiply(acceleration);
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graphview/ShapeCircle.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 | package com.vittoriopiotti.pathgraph.graphview;
8 |
9 | import javafx.beans.property.DoubleProperty;
10 | import javafx.scene.shape.Circle;
11 | import javafx.scene.shape.Shape;
12 |
13 | /**
14 | * This class represents a circle shape with a specified radius.
15 | *
16 | * @author brunomnsilva
17 | */
18 | public class ShapeCircle implements ShapeWithRadius {
19 |
20 | private final Circle surrogate;
21 |
22 | /**
23 | * Creates a circle shape.
24 | * @param x the x-center coordinate
25 | * @param y the y-center coordinate
26 | * @param radius the radius of the circle
27 | */
28 | public ShapeCircle(double x, double y, double radius) {
29 | Args.requireNonNegative(x, "x");
30 | Args.requireNonNegative(y, "y");
31 | Args.requireNonNegative(radius, "radius");
32 |
33 | this.surrogate = new Circle(x, y, radius);
34 | }
35 |
36 | @Override
37 | public Shape getShape() {
38 | return surrogate;
39 | }
40 |
41 | @Override
42 | public DoubleProperty centerXProperty() {
43 | return surrogate.centerXProperty();
44 | }
45 |
46 | @Override
47 | public DoubleProperty centerYProperty() {
48 | return surrogate.centerYProperty();
49 | }
50 |
51 | @Override
52 | public DoubleProperty radiusProperty() {
53 | return surrogate.radiusProperty();
54 | }
55 |
56 | @Override
57 | public double getRadius() {
58 | return surrogate.getRadius();
59 | }
60 |
61 | @Override
62 | public void setRadius(double radius) {
63 | Args.requireNonNegative(radius, "radius");
64 |
65 | // Only update if different
66 | if(Double.compare(this.getRadius(), radius) != 0) {
67 | surrogate.setRadius(radius);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graphview/ShapeFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 | package com.vittoriopiotti.pathgraph.graphview;
8 |
9 | /**
10 | * A factory class for creating instances of shapes with a specified center coordinates and radius.
11 | *
12 | * @author brunomnsilva
13 | */
14 | public class ShapeFactory {
15 |
16 | /**
17 | * Creates a new instance of a shape with the specified type, center coordinates, and radius.
18 | *
19 | * @param type The type of shape to create. Supported types are "star", "circle", "triangle",
20 | * "square", "pentagon", "hexagon", "heptagon", "octagon", "nonagon", "decagon",
21 | * "hendecagon", and "dodecagon".
22 | * @param x The center X coordinate of the shape.
23 | * @param y The center Y coordinate of the shape.
24 | * @param radius The radius of the shape.
25 | * @return An instance of a shape with the specified parameters.
26 | * @throws IllegalArgumentException If the provided type is not recognized or if the center coordinates
27 | * or radius are negative.
28 | */
29 | public static ShapeWithRadius> create(String type, double x, double y, double radius) {
30 | Args.requireNonNegative(x, "x");
31 | Args.requireNonNegative(y, "y");
32 | Args.requireNonNegative(radius, "radius");
33 |
34 | type = type.trim().toLowerCase();
35 |
36 | return switch (type) {
37 | case "star" -> new ShapeStar(x, y, radius);
38 | case "circle" -> new ShapeCircle(x, y, radius);
39 | case "triangle" -> new ShapeRegularPolygon(x, y, radius, 3);
40 | case "square" -> new ShapeRegularPolygon(x, y, radius, 4);
41 | case "pentagon" -> new ShapeRegularPolygon(x, y, radius, 5);
42 | case "hexagon" -> new ShapeRegularPolygon(x, y, radius, 6);
43 | case "heptagon" -> new ShapeRegularPolygon(x, y, radius, 7);
44 | case "octagon" -> new ShapeRegularPolygon(x, y, radius, 8);
45 | case "nonagon" -> new ShapeRegularPolygon(x, y, radius, 9);
46 | case "decagon" -> new ShapeRegularPolygon(x, y, radius, 10);
47 | case "hendecagon" -> new ShapeRegularPolygon(x, y, radius, 11);
48 | case "dodecagon" -> new ShapeRegularPolygon(x, y, radius, 12);
49 | default -> throw new IllegalArgumentException("Invalid shape type. See javadoc for available shapes.");
50 | };
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graphview/ShapeRegularPolygon.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 | package com.vittoriopiotti.pathgraph.graphview;
8 |
9 | import javafx.beans.property.DoubleProperty;
10 | import javafx.beans.property.SimpleDoubleProperty;
11 | import javafx.scene.shape.Polygon;
12 | import javafx.scene.shape.Shape;
13 |
14 | /**
15 | * This class represents a regular polygon shape with a specified number of sides and radius.
16 | *
17 | * @author brunomnsilva
18 | */
19 | public class ShapeRegularPolygon implements ShapeWithRadius {
20 |
21 | protected final int numberSides;
22 | protected final DoubleProperty centerX, centerY;
23 | protected final DoubleProperty radius;
24 |
25 | protected final Polygon surrogate;
26 |
27 | /**
28 | * Creates a regular polygon shape with numberSides
29 | * @param x the x-center coordinate
30 | * @param y the y-center coordinate
31 | * @param radius the radius of the enclosed circle
32 | * @param numberSides the number of sides of the polygon
33 | */
34 | public ShapeRegularPolygon(double x, double y, double radius, int numberSides) {
35 | Args.requireNonNegative(x, "x");
36 | Args.requireNonNegative(y, "y");
37 | Args.requireNonNegative(radius, "radius");
38 | Args.requireGreaterThan(numberSides, "numberSides", 2);
39 |
40 | this.surrogate = new Polygon();
41 |
42 | this.numberSides = numberSides;
43 |
44 | this.centerX = new SimpleDoubleProperty(x);
45 | this.centerY = new SimpleDoubleProperty(y);
46 |
47 | this.centerX.addListener((observable, oldValue, newValue) -> updatePolygon());
48 | this.centerY.addListener((observable, oldValue, newValue) -> updatePolygon());
49 |
50 | this.radius = new SimpleDoubleProperty( radius );
51 | this.radius.addListener((observable, oldValue, newValue) -> updatePolygon());
52 |
53 | updatePolygon();
54 | }
55 |
56 | protected void updatePolygon() {
57 | surrogate.getPoints().clear();
58 |
59 | double cx = centerX.doubleValue();
60 | double cy = centerY.doubleValue();
61 |
62 | double startAngle = Math.PI / (numberSides % 2 == 0 ? numberSides : 2);
63 |
64 | double radius = getRadius();
65 |
66 | for (int i = 0; i < numberSides; i++) {
67 | double angle = startAngle + 2 * Math.PI * i / numberSides;
68 | double px = cx - radius * Math.cos(angle);
69 | double py = cy - radius * Math.sin(angle);
70 | surrogate.getPoints().addAll(px, py);
71 | }
72 | }
73 |
74 | /**
75 | * Returns the number of sides of the polygon.
76 | * @return the number of sides of the polygon
77 | */
78 | @SuppressWarnings("unused")
79 | public int getNumberSides() {
80 | return numberSides;
81 | }
82 |
83 | @Override
84 | public Shape getShape() {
85 | return this.surrogate;
86 | }
87 |
88 | @Override
89 | public DoubleProperty centerXProperty() {
90 | return this.centerX;
91 | }
92 |
93 | @Override
94 | public DoubleProperty centerYProperty() {
95 | return this.centerY;
96 | }
97 |
98 | @Override
99 | public DoubleProperty radiusProperty() {
100 | return this.radius;
101 | }
102 |
103 | @Override
104 | public double getRadius() {
105 | return this.radius.doubleValue();
106 | }
107 |
108 | @Override
109 | public void setRadius(double radius) {
110 | Args.requireNonNegative(radius, "radius");
111 |
112 | this.radius.set(radius);
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graphview/ShapeStar.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 | package com.vittoriopiotti.pathgraph.graphview;
8 |
9 | /**
10 | * This class represents a five-point star shape inscribed within a specified radius.
11 | *
12 | * @author brunomnsilva
13 | */
14 | public class ShapeStar extends ShapeRegularPolygon {
15 |
16 | /**
17 | * Creates a new star shape enclosed in a circle of radius.
18 | * @param x the x-center coordinate
19 | * @param y the y-center coordinate
20 | * @param radius the radius of the enclosed circle
21 | */
22 | public ShapeStar(double x, double y, double radius) {
23 | super(x, y, radius, 10);
24 | }
25 |
26 | @Override
27 | protected void updatePolygon() {
28 | surrogate.getPoints().clear();
29 |
30 | double cx = centerX.doubleValue();
31 | double cy = centerY.doubleValue();
32 |
33 | double startAngle = Math.PI / 2;
34 |
35 | double radius = getRadius();
36 | double innerRadius = radius / 2;
37 |
38 | for (int i = 0; i < numberSides; i++) {
39 | double angle = startAngle + 2 * Math.PI * i / numberSides;
40 |
41 | double radiusToggle = (i % 2 == 0 ? radius : innerRadius);
42 | double px = cx - radiusToggle * Math.cos(angle);
43 | double py = cy - radiusToggle * Math.sin(angle);
44 | surrogate.getPoints().addAll(px, py);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graphview/ShapeWithRadius.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 | package com.vittoriopiotti.pathgraph.graphview;
8 |
9 | import javafx.beans.property.DoubleProperty;
10 | import javafx.scene.shape.Shape;
11 |
12 | /**
13 | * This interface represents a shape with a radius, providing methods to access and modify
14 | * properties related to the center coordinates and radius of the shape.
15 | *
16 | * @param The type of the concrete underlying shape.
17 | *
18 | * @author brunomnsilva
19 | */
20 | @SuppressWarnings("unused")
21 | public interface ShapeWithRadius {
22 |
23 |
24 | /**
25 | * Returns the shape instance associated with this object.
26 | *
27 | * @return The shape instance.
28 | */
29 | Shape getShape();
30 |
31 | /**
32 | * Returns the property representing the center X coordinate of the shape.
33 | *
34 | * @return The property representing the center X coordinate.
35 | */
36 | DoubleProperty centerXProperty();
37 |
38 | /**
39 | * Returns the property representing the center Y coordinate of the shape.
40 | *
41 | * @return The property representing the center Y coordinate.
42 | */
43 | DoubleProperty centerYProperty();
44 |
45 | /**
46 | * Returns the property representing the radius of the shape.
47 | *
48 | * @return The property representing the radius of the shape.
49 | */
50 | DoubleProperty radiusProperty();
51 |
52 | /**
53 | * Returns the radius of the shape.
54 | *
55 | * @return The radius of the shape.
56 | */
57 | double getRadius();
58 |
59 | /**
60 | * Sets the radius of the shape to the specified value.
61 | *
62 | * @param radius The new radius value.
63 | */
64 | void setRadius(double radius);
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartArrow.java:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaFXSmartGraph v2.0.0 (https://github.com/brunomnsilva/JavaFXSmartGraph/releases/tag/v2.0.0)
3 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com
4 | * Licensed under MIT (https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt)
5 | */
6 |
7 |
8 | /*
9 | * PathGraph v1.0.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/releases/tag/1.0.0)
10 | * PathGraph | Copyright 2024 Vittorio Piotti
11 | * Licensed under GPL v3.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt)
12 | */
13 |
14 | package com.vittoriopiotti.pathgraph.graphview;
15 |
16 | import javafx.scene.shape.LineTo;
17 | import javafx.scene.shape.MoveTo;
18 | import javafx.scene.shape.Path;
19 |
20 | /**
21 | * A shape of an arrow to be attached to a {@link SmartArrow}.
22 | *
23 | * @author brunomnsilva
24 | *
Modified by vittoriopiotti
25 | */
26 | public class SmartArrow extends Path implements SmartStylableNode {
27 |
28 | public static final int FILL_ARROW = 1;
29 | public static final int ARROW = 2;
30 |
31 |
32 |
33 | private final double size;
34 | private final SmartStyleProxy styleProxy;
35 |
36 | /**
37 | * Default constructor.
38 | *
39 | * @param size determines the size of the arrow (side of the triangle in pixels)
40 | * @param arrowStyle determines the style of the arrow (filled or unfilled)
41 | *
42 | * @author brunomnsilva
43 | *