├── CODE_OF_CONDUCT.txt ├── LICENSE-JavaFXSmartGraph.txt ├── LICENSE.txt ├── LICENSE_Bootstrap.txt ├── PathGraph-JavaFX-1.0.9-Preview.jar ├── PathGraph-JavaFX-1.0.9-javadoc.jar ├── PathGraph-JavaFX-1.0.9-javadoc.zip ├── PathGraph-JavaFX-1.0.9-sources.jar ├── PathGraph-JavaFX-1.0.9-sources.zip ├── PathGraph-JavaFX-1.0.9.jar ├── README.md ├── SECURITY.md ├── SECURITY.txt ├── pom.xml └── src └── main ├── java ├── com │ └── vittoriopiotti │ │ └── pathgraph │ │ ├── App.java │ │ ├── Main.java │ │ ├── app │ │ ├── Constants.java │ │ ├── PathGraph.java │ │ └── PathGraphUI.java │ │ ├── callbacks │ │ ├── AdjustPositionCallback.java │ │ ├── BackgroundCallback.java │ │ ├── ContextMenuCallback.java │ │ ├── EdgeCallback.java │ │ ├── GenericCallback.java │ │ ├── NodeCallback.java │ │ └── ZoomCallback.java │ │ ├── dto │ │ ├── ConnectionDTO.java │ │ ├── EdgeDTO.java │ │ ├── GraphDTO.java │ │ └── NodeDTO.java │ │ ├── example │ │ └── Example.java │ │ ├── graph │ │ ├── Digraph.java │ │ ├── DigraphEdgeList.java │ │ ├── Edge.java │ │ ├── Graph.java │ │ ├── GraphEdgeList.java │ │ ├── InvalidEdgeException.java │ │ ├── InvalidVertexException.java │ │ └── Vertex.java │ │ ├── graphview │ │ ├── Args.java │ │ ├── ForceDirectedLayoutStrategy.java │ │ ├── ForceDirectedSpringGravityLayoutStrategy.java │ │ ├── ForceDirectedSpringSystemLayoutStrategy.java │ │ ├── ShapeCircle.java │ │ ├── ShapeFactory.java │ │ ├── ShapeRegularPolygon.java │ │ ├── ShapeStar.java │ │ ├── ShapeWithRadius.java │ │ ├── SmartArrow.java │ │ ├── SmartCircularSortedPlacementStrategy.java │ │ ├── SmartGraphEdge.java │ │ ├── SmartGraphEdgeBase.java │ │ ├── SmartGraphEdgeCurve.java │ │ ├── SmartGraphEdgeLine.java │ │ ├── SmartGraphPanel.java │ │ ├── SmartGraphProperties.java │ │ ├── SmartGraphVertex.java │ │ ├── SmartGraphVertexNode.java │ │ ├── SmartLabel.java │ │ ├── SmartLabelProvider.java │ │ ├── SmartLabelSource.java │ │ ├── SmartLabelledNode.java │ │ ├── SmartPlacementStrategy.java │ │ ├── SmartRadiusProvider.java │ │ ├── SmartRadiusSource.java │ │ ├── SmartShapeTypeProvider.java │ │ ├── SmartShapeTypeSource.java │ │ ├── SmartStylableNode.java │ │ ├── SmartStyleProxy.java │ │ ├── UtilitiesBindings.java │ │ └── UtilitiesPoint2D.java │ │ └── utilities │ │ ├── SvgConstants.java │ │ ├── UtilitiesCapture.java │ │ └── UtilitiesUI.java └── module-info.java └── resources ├── META-INF └── MANIFEST.MF ├── smartgraph.css ├── smartgraph.properties └── styles.css /CODE_OF_CONDUCT.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /LICENSE-JavaFXSmartGraph.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | JavaFXSmartGraph | Copyright 2019 - 2024 brunomnsilva@gmail.com. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE_Bootstrap.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2024 The Bootstrap Authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /PathGraph-JavaFX-1.0.9-Preview.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioPiotti/PathGraph-JavaFX/85129beb00a7673c8974f216b738df34ad7c9d6b/PathGraph-JavaFX-1.0.9-Preview.jar -------------------------------------------------------------------------------- /PathGraph-JavaFX-1.0.9-javadoc.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioPiotti/PathGraph-JavaFX/85129beb00a7673c8974f216b738df34ad7c9d6b/PathGraph-JavaFX-1.0.9-javadoc.jar -------------------------------------------------------------------------------- /PathGraph-JavaFX-1.0.9-javadoc.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioPiotti/PathGraph-JavaFX/85129beb00a7673c8974f216b738df34ad7c9d6b/PathGraph-JavaFX-1.0.9-javadoc.zip -------------------------------------------------------------------------------- /PathGraph-JavaFX-1.0.9-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioPiotti/PathGraph-JavaFX/85129beb00a7673c8974f216b738df34ad7c9d6b/PathGraph-JavaFX-1.0.9-sources.jar -------------------------------------------------------------------------------- /PathGraph-JavaFX-1.0.9-sources.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioPiotti/PathGraph-JavaFX/85129beb00a7673c8974f216b738df34ad7c9d6b/PathGraph-JavaFX-1.0.9-sources.zip -------------------------------------------------------------------------------- /PathGraph-JavaFX-1.0.9.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vittorioPiotti/PathGraph-JavaFX/85129beb00a7673c8974f216b738df34ad7c9d6b/PathGraph-JavaFX-1.0.9.jar -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /SECURITY.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.vittoriopiotti 8 | PathGraph-JavaFX 9 | 1.0.9 10 | 11 | 12 | 13 | PathGraph-JavaFX 14 | Library with all the tools necessary to create and work both path and walk graphs in a stable and simple way. Create graphs with nodes, edges, and associated costs, in a stable interface to manage, and interact with the graphs. 15 | https://github.com/vittorioPiotti/PathGraph-JavaFX 16 | 17 | 18 | UTF-8 19 | 5.10.2 20 | 22 21 | 17 22 | 17 23 | 24 | 25 | 26 | 27 | GNU General Public License, Version 3.0 28 | https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt 29 | This license applies to the original PathGraph-JavaFX code. 30 | repo 31 | 32 | 33 | MIT License 34 | https://github.com/brunomnsilva/JavaFXSmartGraph/blob/master/LICENSE.txt 35 | repo 36 | This license applies to the original JavaFXSmartGraph code. 37 | 38 | 39 | MIT License 40 | https://github.com/twbs/icons/blob/main/LICENSE 41 | repo 42 | This license applies to the Bootstrap SVG icons. 43 | 44 | 45 | 46 | 47 | 48 | vittorioPiotti 49 | Vittorio Piotti 50 | vittoriopiotti.it@gmail.com 51 | 52 | 53 | 54 | 55 | scm:git:git://github.com/vittorioPiotti/PathGraph-JavaFX.git 56 | scm:git:ssh://github.com:vittorioPiotti/PathGraph-JavaFX.git 57 | https://github.com/vittorioPiotti/PathGraph-JavaFX 58 | 59 | 60 | 61 | 62 | github 63 | PathGraph-JavaFX 64 | https://maven.pkg.github.com/vittorioPiotti/PathGraph-JavaFX 65 | 66 | 67 | 68 | 69 | 70 | org.openjfx 71 | javafx-controls 72 | ${javafx.version} 73 | 74 | 75 | org.openjfx 76 | javafx-fxml 77 | ${javafx.version} 78 | 79 | 80 | org.openjfx 81 | javafx-swing 82 | ${javafx.version} 83 | 84 | 85 | org.junit.jupiter 86 | junit-jupiter-api 87 | ${junit.version} 88 | test 89 | 90 | 91 | org.junit.jupiter 92 | junit-jupiter-engine 93 | ${junit.version} 94 | test 95 | 96 | 97 | 98 | 99 | 100 | 101 | org.apache.maven.plugins 102 | maven-compiler-plugin 103 | 3.11.0 104 | 105 | 17 106 | 17 107 | 108 | 109 | 110 | org.openjfx 111 | javafx-maven-plugin 112 | 0.0.8 113 | 114 | 115 | 116 | default-cli 117 | 118 | com.example.demo1/com.example.demo1.HelloApplication 119 | app 120 | app 121 | app 122 | true 123 | true 124 | true 125 | 126 | 127 | 128 | 129 | 130 | org.apache.maven.plugins 131 | maven-assembly-plugin 132 | 3.6.0 133 | 134 | 135 | jar-with-dependencies 136 | 137 | 138 | 139 | com.vittoriopiotti.pathgraph.Main 140 | 141 | 142 | 143 | 144 | 145 | make-assembly 146 | 147 | 148 | single 149 | 150 | 151 | 152 | package 153 | 154 | 155 | 156 | 157 | 158 | 159 | org.sonatype.central 160 | central-publishing-maven-plugin 161 | 0.6.0 162 | true 163 | 164 | central 165 | 166 | 167 | 168 | org.apache.maven.plugins 169 | maven-javadoc-plugin 170 | 3.3.0 171 | 172 | 173 | attach-javadocs 174 | 175 | jar 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | org.apache.maven.plugins 185 | maven-source-plugin 186 | 3.2.1 187 | 188 | 189 | attach-source 190 | 191 | jar-no-fork 192 | 193 | 194 | 195 | 196 | 197 | org.apache.maven.plugins 198 | maven-gpg-plugin 199 | 1.6 200 | 201 | 202 | sign-artifacts 203 | verify 204 | 205 | sign 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | org.openjfx 214 | javafx-maven-plugin 215 | 0.0.8 216 | 217 | 218 | 219 | run 220 | 221 | 222 | io.github.vittoriopiotti.pathgraph.Main 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/App.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; 15 | import com.vittoriopiotti.pathgraph.app.Constants; 16 | import com.vittoriopiotti.pathgraph.callbacks.*; 17 | import javafx.application.Application; 18 | import javafx.scene.Node; 19 | import javafx.scene.Scene; 20 | import javafx.scene.control.*; 21 | import javafx.scene.input.MouseButton; 22 | import javafx.scene.layout.*; 23 | import javafx.scene.paint.Color; 24 | import javafx.scene.layout.Background; 25 | import javafx.stage.Stage; 26 | import javafx.scene.layout.BorderPane; 27 | import com.vittoriopiotti.pathgraph.app.PathGraphUI; 28 | import com.vittoriopiotti.pathgraph.utilities.SvgConstants; 29 | import com.vittoriopiotti.pathgraph.utilities.UtilitiesUI; 30 | 31 | 32 | public class App extends Application { 33 | 34 | 35 | @Override 36 | public void start(Stage primaryStage) { 37 | 38 | /* 1. Create javafx window */ 39 | BorderPane root = new BorderPane(); 40 | root.setBackground(Background.fill(Color.web("#dee2e6"))); 41 | Scene scene = new Scene(root, 750, 550); 42 | primaryStage.setScene(scene); 43 | 44 | /* 2. Visualize primary stage */ 45 | primaryStage.show(); 46 | 47 | 48 | /* 3. Create PathGraph object*/ 49 | PathGraphUI pg = new PathGraphUI( 50 | primaryStage, 51 | scene 52 | ); 53 | 54 | 55 | /* 4. Add PathGraph in a container */ 56 | ScrollPane page = UtilitiesUI.createPage( 57 | primaryStage, 58 | /* window name */ 59 | "PathGraph", 60 | /* min width main pane */ 61 | 400, 62 | /* max width main pane */ 63 | 1280, 64 | /* min height main pane */ 65 | 400, 66 | /* max height main pane */ 67 | 640, 68 | /* graph to add */ 69 | new VBox(pg), 70 | /* social icon footer */ 71 | SvgConstants.SVG_GITHUB, 72 | /* social name footer */ 73 | "Github", 74 | /* copyrights */ 75 | "© 2024 · Vittorio Piotti ", 76 | /* link to site */ 77 | "https://github.com/vittorioPiotti" 78 | ); 79 | root.setCenter(page); 80 | pg.setup().thenRun(() -> { 81 | pg.newNode('A'); 82 | pg.newNode('B'); 83 | pg.newNode('C'); 84 | pg.newEdge('A', 'B', 1); 85 | pg.newEdge('C', 'A', 1, false); 86 | pg.newEdge('C', 'A', 1, Constants.NATURAL_DIRECTION); 87 | 88 | 89 | }); 90 | 91 | } 92 | 93 | public static void main(String[] args) { 94 | launch(args); 95 | } 96 | } 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/Main.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 | 15 | package com.vittoriopiotti.pathgraph; 16 | 17 | public class Main { 18 | 19 | public static void main(String[] args){ 20 | App.main(args); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/app/Constants.java: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * PathGraph v1.0.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/releases/tag/1.0.0) 4 | * PathGraph | Copyright 2024 Vittorio Piotti 5 | * Licensed under GPL v3.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt) 6 | */ 7 | 8 | 9 | package com.vittoriopiotti.pathgraph.app; 10 | 11 | /** 12 | * This class defines various constants used throughout the application 13 | * to represent different states and directions of edges. 14 | * 15 | *

Available constants:

16 | * 21 | * 22 | *

Available edge directions:

23 | * 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 | *

Modified by vittoriopiotti

44 | */ 45 | public SmartArrow(double size, int arrowStyle) { 46 | getElements().add(new MoveTo(0, 0)); 47 | getElements().add(new LineTo(-size, size)); 48 | getElements().add(new MoveTo(0, 0)); 49 | getElements().add(new LineTo(-size, -size)); 50 | 51 | styleProxy = new SmartStyleProxy(this); 52 | styleProxy.addStyleClass("edge"); 53 | styleProxy.addStyleClass("arrow"); 54 | this.size = size; 55 | if (arrowStyle == FILL_ARROW) { 56 | setFillArrow(); 57 | } else { 58 | setArrow(); 59 | } 60 | 61 | } 62 | 63 | /** 64 | * Method to create a filled arrow 65 | * 66 | * @author vittoriopiotti 67 | */ 68 | private void setFillArrow() { 69 | double arrowWidth = size * 2; 70 | double arrowHeight = size * 1; 71 | getElements().clear(); 72 | getElements().add(new MoveTo(0, 0)); 73 | getElements().add(new LineTo(-arrowWidth, arrowHeight / 2)); 74 | getElements().add(new LineTo(-arrowWidth, -arrowHeight / 2)); 75 | getElements().add(new LineTo(0, 0)); 76 | } 77 | 78 | /** 79 | * Method to create an unfilled arrow 80 | * 81 | * @author brunomnsilva 82 | *

Modified by vittoriopiotti

83 | */ 84 | private void setArrow() { 85 | 86 | getElements().clear(); 87 | getElements().add(new MoveTo(0, 0)); 88 | getElements().add(new LineTo(-size, size)); 89 | getElements().add(new MoveTo(0, 0)); 90 | getElements().add(new LineTo(-size, -size)); 91 | } 92 | @Override 93 | public void setStyleInline(String css) { 94 | styleProxy.setStyleInline(css); 95 | } 96 | 97 | @Override 98 | public void setStyleClass(String cssClass) { 99 | styleProxy.setStyleClass(cssClass); 100 | } 101 | 102 | @Override 103 | public void addStyleClass(String cssClass) { 104 | styleProxy.addStyleClass(cssClass); 105 | } 106 | 107 | @Override 108 | public boolean removeStyleClass(String cssClass) { 109 | return styleProxy.removeStyleClass(cssClass); 110 | } 111 | 112 | 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartCircularSortedPlacementStrategy.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.ArrayList; 13 | import java.util.List; 14 | 15 | /** 16 | * Places vertices around a circle, ordered by the underlying 17 | * vertices {@code element.toString() value}. 18 | * 19 | * @see SmartPlacementStrategy 20 | * 21 | * @author brunomnsilva 22 | */ 23 | public class SmartCircularSortedPlacementStrategy implements SmartPlacementStrategy { 24 | 25 | private static final int RADIUS_PADDING = 4; 26 | 27 | @Override 28 | public void place(double width, double height, SmartGraphPanel boostSmartGraphPanel) { 29 | // Sort vertices by their label 30 | List> vertices = new ArrayList<>(boostSmartGraphPanel.getSmartVertices()); 31 | 32 | vertices.sort((v1, v2) -> { 33 | V e1 = v1.getUnderlyingVertex().element(); 34 | V e2 = v2.getUnderlyingVertex().element(); 35 | return boostSmartGraphPanel.getVertexLabelFor(e1).compareTo(boostSmartGraphPanel.getVertexLabelFor(e2)); 36 | }); 37 | 38 | //place first vertex at north position, others in clockwise manner 39 | Point2D center = new Point2D(width / 2, height / 2); 40 | int N = vertices.size(); 41 | double angleIncrement = -360f / N; 42 | boolean first = true; 43 | Point2D p = null; 44 | 45 | for (SmartGraphVertex vertex : vertices) { 46 | 47 | if (first) { 48 | //verify the smallest width and height. 49 | if(width > height) 50 | p = new Point2D(center.getX(), 51 | center.getY() - height / 2 + vertex.getRadius() * RADIUS_PADDING); 52 | else 53 | p = new Point2D(center.getX(), 54 | center.getY() - width / 2 + vertex.getRadius() * RADIUS_PADDING); 55 | 56 | first = false; 57 | } else { 58 | p = UtilitiesPoint2D.rotate(p, center, angleIncrement); 59 | } 60 | 61 | vertex.setPosition(p.getX(), p.getY()); 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartGraphEdge.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 | import com.vittoriopiotti.pathgraph.graph.Edge; 12 | import com.vittoriopiotti.pathgraph.graph.Vertex; 13 | 14 | /** 15 | * A graph edge visually connects two {@link Vertex} of type V. 16 | *
17 | * Concrete edge implementations used by {@link SmartGraphPanel} should 18 | * implement this interface as this type is the only one exposed to the user. 19 | * 20 | * @param Type stored in the underlying edge 21 | * @param Type of connecting vertex 22 | * 23 | * @see Vertex 24 | * @see SmartGraphPanel 25 | * 26 | * @author brunomnsilva 27 | */ 28 | public interface SmartGraphEdge extends SmartStylableNode { 29 | 30 | /** 31 | * Returns the underlying (stored reference) graph edge. 32 | * 33 | * @return edge reference 34 | * 35 | * @see SmartGraphPanel 36 | */ 37 | Edge getUnderlyingEdge(); 38 | 39 | /** 40 | * Returns the attached arrow of the edge, for styling purposes. 41 | *
42 | * The arrows are only used with directed graphs. 43 | * 44 | * @return arrow reference; null if does not exist. 45 | */ 46 | @SuppressWarnings("unused") 47 | SmartStylableNode getStylableArrow(); 48 | 49 | /** 50 | * Returns the label node for further styling. 51 | * 52 | * @return the label node. 53 | */ 54 | SmartStylableNode getStylableLabel(); 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartGraphEdgeBase.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.event.EventHandler; 17 | import javafx.scene.input.MouseEvent; 18 | import javafx.scene.shape.Rectangle; 19 | 20 | /** 21 | * A base interface for boost smart graph edges that visually connects two vertices and supports additional features. 22 | * 23 | * @param Type stored in the underlying edge 24 | * @param Type of connecting vertex 25 | * 26 | * @author brunomsilva 27 | *

Modified by vittoriopiotti

28 | */ 29 | public interface SmartGraphEdgeBase extends SmartGraphEdge, SmartLabelledNode { 30 | 31 | 32 | /** 33 | * Attaches a {@link SmartArrow} to this edge, binding its position/rotation. 34 | * 35 | * @param arrow arrow to attach 36 | * 37 | * @author brunomsilva 38 | */ 39 | void attachArrow(SmartArrow arrow); 40 | 41 | /** 42 | * Returns the attached {@link SmartArrow}, if any. 43 | * 44 | * @return reference of the attached arrow; null if none. 45 | * 46 | * @author brunomsilva 47 | */ 48 | SmartArrow getAttachedArrow(); 49 | 50 | /** 51 | * Applies a hover style to the edge. 52 | * This method changes the appearance of the edge when the mouse hovers over it. 53 | * 54 | * @author vittoriopiotti 55 | */ 56 | void applyHoverStyle(); 57 | 58 | /** 59 | * Reverts the edge to its default style. 60 | * This method resets any custom styles applied, returning the edge to its original appearance. 61 | * 62 | * @author vittoriopiotti 63 | */ 64 | void applyDefaultStyle(); 65 | 66 | /** 67 | * Sets the event handler for mouse click events. 68 | * 69 | * @param value The event handler to set. 70 | * @see EventHandler 71 | * @see MouseEvent 72 | * 73 | * @author vittoriopiotti 74 | */ 75 | void setOnMouseClicked_(EventHandler value); 76 | 77 | /** 78 | * Sets the label text for the edge based on the cost value. 79 | * 80 | * @param cost The text to be displayed as the label, representing the cost of the edge. 81 | * 82 | * @author vittoriopiotti 83 | */ 84 | void setCost(int cost); 85 | 86 | /** 87 | * Returns the cost associated with this edge. 88 | * 89 | * @return the cost of the edge 90 | * 91 | * @author vittoriopiotti 92 | */ 93 | int getCost(); 94 | 95 | /** 96 | * Applies a dash style to the edge. 97 | * This method modifies the appearance of the edge, setting a dashed pattern. 98 | * 99 | * @author vittoriopiotti 100 | */ 101 | void applyDashStyle(); 102 | 103 | /** 104 | * Resets the style of the edge to its default appearance. 105 | * This method clears any custom styling applied to the edge. 106 | * 107 | * @author vittoriopiotti 108 | */ 109 | void resetStyle(); 110 | 111 | /** 112 | * Returns the direction of the edge. 113 | *
114 | * In a directed graph, this value can be used to determine the orientation of the edge. 115 | * 116 | * @return the direction of the edge 117 | * 118 | * @author vittoriopiotti 119 | */ 120 | int getDirection(); 121 | 122 | /** 123 | * Sets the direction of the edge. 124 | *
125 | * This method allows specifying the direction of the edge in a directed graph. 126 | * 127 | * @param direction the direction to set for the edge 128 | * 129 | * @author vittoriopiotti 130 | */ 131 | void setDirection(int direction); 132 | 133 | /** 134 | * Returns the inbound vertex node of the edge. 135 | * 136 | * @return the inbound vertex node 137 | * 138 | * @see SmartGraphVertexNode 139 | * 140 | * @author vittoriopiotti 141 | */ 142 | SmartGraphVertexNode getInbound(); 143 | 144 | /** 145 | * Returns the outbound vertex node of the edge. 146 | * 147 | * @return the outbound vertex node 148 | * 149 | * @see SmartGraphVertexNode 150 | * 151 | * @author vittoriopiotti 152 | */ 153 | SmartGraphVertexNode getOutbound(); 154 | 155 | /** 156 | * Returns the attached background rectangle of the edge. 157 | * 158 | * @return the attached background rectangle 159 | * 160 | * @see Rectangle 161 | * 162 | * @author vittoriopiotti 163 | */ 164 | Rectangle getAttachedBackground(); 165 | 166 | /** 167 | * Attaches a background rectangle to the edge. 168 | * 169 | * @param attachedBackground the background rectangle to attach 170 | * 171 | * @see Rectangle 172 | * 173 | * @author vittoriopiotti 174 | */ 175 | void attachBackground(Rectangle attachedBackground); 176 | 177 | 178 | } 179 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartGraphEdgeLine.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.graphview; 14 | 15 | import javafx.event.EventHandler; 16 | import javafx.scene.shape.Rectangle; 17 | import com.vittoriopiotti.pathgraph.graph.Edge; 18 | import javafx.beans.binding.Bindings; 19 | import javafx.scene.input.MouseButton; 20 | import javafx.scene.input.MouseEvent; 21 | import javafx.scene.shape.Line; 22 | import javafx.scene.transform.Rotate; 23 | import javafx.scene.transform.Translate; 24 | 25 | 26 | /** 27 | * Implementation of a straight line edge. 28 | * 29 | * @param Type stored in the underlying edge 30 | * @param Type of connecting vertex 31 | * 32 | * @author brunomnsilva 33 | *

Modified by vittoriopiotti

34 | */ 35 | public class SmartGraphEdgeLine extends Line implements SmartGraphEdgeBase { 36 | 37 | private final Edge underlyingEdge; 38 | private int cost; 39 | 40 | private final SmartGraphVertexNode inbound; 41 | private final SmartGraphVertexNode outbound; 42 | 43 | private SmartLabel attachedLabel = null; 44 | private SmartArrow attachedArrow = null; 45 | private Rectangle attachedBackground = null; 46 | 47 | private int direction; 48 | /* Styling proxy */ 49 | private final SmartStyleProxy styleProxy; 50 | 51 | @Override 52 | public Rectangle getAttachedBackground(){ 53 | return attachedBackground; 54 | } 55 | @Override 56 | public void attachBackground(Rectangle attachedBackground){ 57 | this.attachedBackground = attachedBackground; 58 | } 59 | @Override 60 | public int getDirection(){ 61 | return direction; 62 | } 63 | 64 | @Override 65 | public void setDirection(int direction){ 66 | this.direction = direction; 67 | } 68 | /** 69 | * Constructs a SmartGraphEdgeLine representing an edge between two SmartGraphVertexNodes. 70 | * 71 | * @param edge the edge associated with this line 72 | * @param inbound the inbound SmartGraphVertexNode 73 | * @param outbound the outbound SmartGraphVertexNode 74 | */ 75 | public SmartGraphEdgeLine(Edge edge, SmartGraphVertexNode inbound, SmartGraphVertexNode outbound, int cost, int direction) { 76 | if( inbound == null || outbound == null) { 77 | throw new IllegalArgumentException("Cannot connect null vertices."); 78 | } 79 | this.direction = direction; 80 | this.inbound = inbound; 81 | this.outbound = outbound; 82 | this.cost = cost; 83 | this.underlyingEdge = edge; 84 | 85 | styleProxy = new SmartStyleProxy(this); 86 | styleProxy.addStyleClass("edge"); 87 | 88 | this.startXProperty().bind(outbound.centerXProperty()); 89 | this.startYProperty().bind(outbound.centerYProperty()); 90 | this.endXProperty().bind(inbound.centerXProperty()); 91 | this.endYProperty().bind(inbound.centerYProperty()); 92 | 93 | propagateHoverEffectToArrow(); 94 | 95 | 96 | } 97 | @Override 98 | public void setStyleInline(String css) { 99 | styleProxy.setStyleInline(css); 100 | if(attachedArrow != null) { 101 | attachedArrow.setStyleInline(css); 102 | } 103 | } 104 | 105 | @Override 106 | public void setStyleClass(String cssClass) { 107 | styleProxy.setStyleClass(cssClass); 108 | if(attachedArrow != null) { 109 | attachedArrow.setStyleClass(cssClass); 110 | } 111 | } 112 | 113 | @Override 114 | public SmartGraphVertexNode getInbound(){ 115 | return this.inbound; 116 | } 117 | 118 | @Override 119 | public SmartGraphVertexNode getOutbound(){ 120 | return this.outbound; 121 | } 122 | @Override 123 | public void addStyleClass(String cssClass) { 124 | styleProxy.addStyleClass(cssClass); 125 | if(attachedArrow != null) { 126 | attachedArrow.addStyleClass(cssClass); 127 | } 128 | } 129 | 130 | @Override 131 | public boolean removeStyleClass(String cssClass) { 132 | boolean result = styleProxy.removeStyleClass(cssClass); 133 | if(attachedArrow != null) { 134 | attachedArrow.removeStyleClass(cssClass); 135 | } 136 | return result; 137 | } 138 | 139 | @Override 140 | public void attachLabel(SmartLabel label) { 141 | this.attachedLabel = label; 142 | 143 | label.xProperty().bind(startXProperty().add(endXProperty()).divide(2).subtract(Bindings.divide(label.layoutWidthProperty(), 2))); 144 | label.yProperty().bind(startYProperty().add(endYProperty()).divide(2).add(Bindings.divide(label.layoutHeightProperty(), 1.5))); 145 | } 146 | 147 | @Override 148 | public SmartLabel getAttachedLabel() { 149 | return attachedLabel; 150 | } 151 | 152 | @Override 153 | public Edge getUnderlyingEdge() { 154 | return underlyingEdge; 155 | } 156 | @Override 157 | public void setOnMouseClicked_(EventHandler value) { 158 | this.addEventFilter(MouseEvent.MOUSE_CLICKED, value); 159 | } 160 | 161 | 162 | @Override 163 | public void attachArrow(SmartArrow arrow) { 164 | this.attachedArrow = arrow; 165 | 166 | 167 | /* attach arrow to line's endpoint */ 168 | arrow.translateXProperty().bind(endXProperty()); 169 | arrow.translateYProperty().bind(endYProperty()); 170 | 171 | /* rotate arrow around itself based on this line's angle */ 172 | Rotate rotation = new Rotate(); 173 | rotation.pivotXProperty().bind(translateXProperty()); 174 | rotation.pivotYProperty().bind(translateYProperty()); 175 | rotation.angleProperty().bind( UtilitiesBindings.toDegrees( 176 | UtilitiesBindings.atan2( endYProperty().subtract(startYProperty()), 177 | endXProperty().subtract(startXProperty())) 178 | )); 179 | 180 | arrow.getTransforms().add(rotation); 181 | 182 | /* add translation transform to put the arrow touching the circle's bounds */ 183 | Translate t = new Translate(0, 0); 184 | t.xProperty().bind( inbound.radiusProperty().negate() ); 185 | 186 | arrow.setOnMouseEntered(e -> { 187 | this.applyHoverStyle(); 188 | attachedLabel.applyHoverStyle(); 189 | }); 190 | arrow.setOnMouseExited(e -> { 191 | this.applyDefaultStyle(); 192 | attachedLabel.applyDefaultStyle(); 193 | }); 194 | 195 | 196 | 197 | arrow.getTransforms().add(t); 198 | } 199 | 200 | 201 | 202 | @Override 203 | public SmartArrow getAttachedArrow() { 204 | return this.attachedArrow; 205 | } 206 | 207 | @Override 208 | public SmartStylableNode getStylableArrow() { 209 | return this.attachedArrow; 210 | } 211 | 212 | @Override 213 | public SmartStylableNode getStylableLabel() { 214 | return this.attachedLabel; 215 | } 216 | 217 | @Override 218 | public void applyDashStyle() { 219 | 220 | 221 | if (!getStyleClass().contains("edge-dash")) { 222 | getStyleClass().remove("edge"); 223 | getStyleClass().remove("edge-hover"); 224 | getStyleClass().remove("edge-dash-hover"); 225 | getStyleClass().add("edge-dash"); 226 | 227 | } 228 | } 229 | 230 | @Override 231 | public void resetStyle(){ 232 | 233 | 234 | 235 | if (!getStyleClass().contains("edge")) { 236 | getStyleClass().remove("edge-dash"); 237 | getStyleClass().remove("edge-dash-hover"); 238 | getStyleClass().remove("edge-hover"); 239 | getStyleClass().add("edge"); 240 | 241 | } 242 | 243 | if (!attachedArrow.getStyleClass().contains("arrow")) { 244 | // Remove hover style class if it exists 245 | attachedArrow.getStyleClass().remove("arrow-hover"); 246 | 247 | // Add default style class 248 | attachedArrow.getStyleClass().add("arrow"); 249 | } 250 | 251 | 252 | if (!attachedLabel.getStyleClass().contains("edge-label")) { 253 | // Remove hover style class if it exists 254 | attachedLabel.getStyleClass().remove("edge-label-hover"); 255 | 256 | // Add default style class 257 | attachedLabel.getStyleClass().add("edge-label"); 258 | } 259 | } 260 | 261 | 262 | 263 | @Override 264 | public void applyHoverStyle() { 265 | if(getStyleClass().contains("edge")) { 266 | 267 | // Check if the current style class is not already 'edge-hover' 268 | if (!getStyleClass().contains("edge-hover")) { 269 | // Remove default style class if it exists 270 | getStyleClass().remove("edge"); 271 | 272 | // Add hover style class 273 | getStyleClass().add("edge-hover"); 274 | } 275 | 276 | if (!attachedArrow.getStyleClass().contains("arrow-hover")) { 277 | // Remove hover style class if it exists 278 | attachedArrow.getStyleClass().remove("arrow"); 279 | 280 | // Add default style class 281 | attachedArrow.getStyleClass().add("arrow-hover"); 282 | } 283 | 284 | if (!attachedLabel.getStyleClass().contains("edge-label-hover")) { 285 | // Remove hover style class if it exists 286 | attachedLabel.getStyleClass().remove("edge-label"); 287 | 288 | // Add default style class 289 | attachedLabel.getStyleClass().add("edge-label-hover"); 290 | } 291 | }else if(getStyleClass().contains("edge-dash")) { 292 | 293 | // Check if the current style class is not already 'edge-hover' 294 | if (!getStyleClass().contains("edge-dash-hover")) { 295 | // Remove default style class if it exists 296 | getStyleClass().remove("edge-dash"); 297 | 298 | // Add hover style class 299 | getStyleClass().add("edge-dash-hover"); 300 | } 301 | 302 | if (!attachedArrow.getStyleClass().contains("arrow-hover")) { 303 | // Remove hover style class if it exists 304 | attachedArrow.getStyleClass().remove("arrow"); 305 | 306 | // Add default style class 307 | attachedArrow.getStyleClass().add("arrow-hover"); 308 | } 309 | 310 | if (!attachedLabel.getStyleClass().contains("edge-label-hover")) { 311 | // Remove hover style class if it exists 312 | attachedLabel.getStyleClass().remove("edge-label"); 313 | 314 | // Add default style class 315 | attachedLabel.getStyleClass().add("edge-label-hover"); 316 | } 317 | } 318 | 319 | } 320 | 321 | @Override 322 | public void applyDefaultStyle() { 323 | 324 | if(getStyleClass().contains("edge-hover")) { 325 | // Check if the current style class is not already 'edge' 326 | if (!getStyleClass().contains("edge")) { 327 | // Remove hover style class if it exists 328 | getStyleClass().remove("edge-hover"); 329 | 330 | // Add default style class 331 | getStyleClass().add("edge"); 332 | } 333 | // Sovrascrivi lo stile inline 334 | if (!attachedArrow.getStyleClass().contains("arrow")) { 335 | // Remove hover style class if it exists 336 | attachedArrow.getStyleClass().remove("arrow-hover"); 337 | 338 | // Add default style class 339 | attachedArrow.getStyleClass().add("arrow"); 340 | } 341 | 342 | 343 | if (!attachedLabel.getStyleClass().contains("edge-label")) { 344 | // Remove hover style class if it exists 345 | attachedLabel.getStyleClass().remove("edge-label-hover"); 346 | 347 | // Add default style class 348 | attachedLabel.getStyleClass().add("edge-label"); 349 | } 350 | }else if(getStyleClass().contains("edge-dash-hover")) { 351 | // Check if the current style class is not already 'edge' 352 | if (!getStyleClass().contains("edge-dash")) { 353 | // Remove hover style class if it exists 354 | getStyleClass().remove("edge-dash-hover"); 355 | 356 | // Add default style class 357 | getStyleClass().add("edge-dash"); 358 | } 359 | // Sovrascrivi lo stile inline 360 | if (!attachedArrow.getStyleClass().contains("arrow")) { 361 | // Remove hover style class if it exists 362 | attachedArrow.getStyleClass().remove("arrow-hover"); 363 | 364 | // Add default style class 365 | attachedArrow.getStyleClass().add("arrow"); 366 | } 367 | 368 | 369 | if (!attachedLabel.getStyleClass().contains("edge-label")) { 370 | // Remove hover style class if it exists 371 | attachedLabel.getStyleClass().remove("edge-label-hover"); 372 | 373 | // Add default style class 374 | attachedLabel.getStyleClass().add("edge-label"); 375 | } 376 | } 377 | } 378 | 379 | @Override 380 | public int getCost(){ 381 | return cost; 382 | } 383 | 384 | @Override 385 | public void setCost(int text){ 386 | cost = text; 387 | attachedLabel.setText_(String.valueOf(cost)); 388 | } 389 | 390 | 391 | 392 | /** 393 | * @author brunomnsilva 394 | *

Modified by vittoriopiotti

395 | */ 396 | private void propagateHoverEffectToArrow() { 397 | this.hoverProperty().addListener((observable, oldValue, newValue) -> { 398 | if(attachedArrow != null && newValue) { 399 | attachedLabel.applyHoverStyle(); 400 | attachedArrow.fireEvent(new MouseEvent(MouseEvent.MOUSE_ENTERED, 0, 0, 0, 0, MouseButton.NONE, 0, true, true, true, true, true, true, true, true, true, true, null)); 401 | } else if(attachedArrow != null) { //newValue is false, hover ended 402 | attachedLabel.applyDefaultStyle(); 403 | attachedArrow.fireEvent(new MouseEvent(MouseEvent.MOUSE_EXITED, 0, 0, 0, 0, MouseButton.NONE, 0, true, true, true, true, true, true, true, true, true, true, null)); 404 | 405 | } 406 | }); 407 | } 408 | 409 | } 410 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartGraphProperties.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 java.io.ByteArrayInputStream; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.util.Objects; 14 | import java.util.Properties; 15 | import java.util.logging.Level; 16 | import java.util.logging.Logger; 17 | 18 | /** 19 | * Properties used by {@link SmartGraphPanel}. Default file is given by 20 | * the {@link #DEFAULT_FILE} property. 21 | * 22 | * @see SmartGraphPanel 23 | * @see SmartGraphVertex 24 | * @see SmartGraphEdge 25 | * 26 | * @author brunomnsilva 27 | */ 28 | public class SmartGraphProperties { 29 | 30 | private static final boolean DEFAULT_VERTEX_ALLOW_USER_MOVE = true; 31 | private static final String PROPERTY_VERTEX_ALLOW_USER_MOVE = "vertex.allow-user-move"; 32 | 33 | private static final double DEFAULT_VERTEX_RADIUS = 15; 34 | private static final String PROPERTY_VERTEX_RADIUS = "vertex.radius"; 35 | 36 | private static final String DEFAULT_VERTEX_SHAPE = "circle"; 37 | private static final String PROPERTY_VERTEX_SHAPE = "vertex.shape"; 38 | 39 | private static final boolean DEFAULT_VERTEX_USE_TOOLTIP = true; 40 | private static final String PROPERTY_VERTEX_USE_TOOLTIP = "vertex.tooltip"; 41 | 42 | private static final boolean DEFAULT_VERTEX_USE_LABEL = true; 43 | private static final String PROPERTY_VERTEX_USE_LABEL = "vertex.label"; 44 | 45 | private static final boolean DEFAULT_EDGE_USE_TOOLTIP = true; 46 | private static final String PROPERTY_EDGE_USE_TOOLTIP = "edge.tooltip"; 47 | 48 | private static final boolean DEFAULT_EDGE_USE_LABEL = true; 49 | private static final String PROPERTY_EDGE_USE_LABEL = "edge.label"; 50 | 51 | private static final boolean DEFAULT_EDGE_USE_ARROW = true; 52 | private static final String PROPERTY_EDGE_USE_ARROW = "edge.arrow"; 53 | 54 | private static final int DEFAULT_ARROW_SIZE = 5; 55 | private static final String PROPERTY_ARROW_SIZE = "edge.arrowsize"; 56 | 57 | private static final double DEFAULT_REPULSION_FORCE = 25000; 58 | private static final String PROPERTY_REPULSION_FORCE = "layout.repulsive-force"; 59 | 60 | private static final double DEFAULT_ATTRACTION_FORCE = 30; 61 | private static final String PROPERTY_ATTRACTION_FORCE = "layout.attraction-force"; 62 | 63 | private static final double DEFAULT_ATTRACTION_SCALE = 10; 64 | private static final String PROPERTY_ATTRACTION_SCALE = "layout.attraction-scale"; 65 | 66 | private static final String DEFAULT_FILE = "smartgraph.properties"; 67 | private final Properties properties; 68 | 69 | /** 70 | * Uses default properties file. 71 | */ 72 | public SmartGraphProperties() { 73 | properties = new Properties(); 74 | 75 | try { 76 | // Carica il file di proprietà dalle risorse 77 | InputStream input = Objects.requireNonNull(getClass().getResourceAsStream("/" + DEFAULT_FILE), 78 | "The default properties file was not found."); 79 | 80 | // Carica le proprietà dal file 81 | properties.load(input); 82 | } catch (NullPointerException | IOException ex) { 83 | String msg = String.format("The default %s was not found or couldn't be loaded. Using default values.", DEFAULT_FILE); 84 | Logger.getLogger(SmartGraphProperties.class.getName()).log(Level.WARNING, msg); 85 | } 86 | } 87 | /** 88 | * Reads properties from the desired input stream. 89 | * 90 | * @param inputStream input stream from where to read the properties 91 | */ 92 | public SmartGraphProperties(InputStream inputStream) { 93 | properties = new Properties(); 94 | try { 95 | properties.load(inputStream); 96 | } catch (IOException ex) { 97 | String msg = "The file provided by the input stream does not exist. Using default values."; 98 | Logger.getLogger(SmartGraphProperties.class.getName()).log(Level.WARNING, msg); 99 | } 100 | } 101 | 102 | /** 103 | * Reads properties from the desired string. 104 | * @param content string from where to read the properties 105 | */ 106 | public SmartGraphProperties(String content) { 107 | properties = new Properties(); 108 | try { 109 | InputStream targetStream = new ByteArrayInputStream(content.getBytes()); 110 | properties.load(targetStream); 111 | } catch (IOException ex) { 112 | String msg = "The string contents could not be loaded. Using default values."; 113 | Logger.getLogger(SmartGraphProperties.class.getName()).log(Level.WARNING, msg); 114 | } 115 | } 116 | 117 | /** 118 | * Returns a property that indicates whether a vertex can be moved freely 119 | * by the user. 120 | * 121 | * @return corresponding property value 122 | */ 123 | public boolean getVertexAllowUserMove() { 124 | return getBooleanProperty(PROPERTY_VERTEX_ALLOW_USER_MOVE, DEFAULT_VERTEX_ALLOW_USER_MOVE); 125 | } 126 | 127 | /** 128 | * Returns a property that indicates the radius of each vertex. 129 | * 130 | * @return corresponding property value 131 | */ 132 | public double getVertexRadius() { 133 | return getDoubleProperty(PROPERTY_VERTEX_RADIUS, DEFAULT_VERTEX_RADIUS); 134 | } 135 | 136 | /** 137 | * Returns a property that indicates the shape of each vertex. 138 | * 139 | * @return corresponding property value 140 | */ 141 | public String getVertexShape() { 142 | return getStringProperty(PROPERTY_VERTEX_SHAPE, DEFAULT_VERTEX_SHAPE); 143 | } 144 | 145 | /** 146 | * Returns a property that indicates the repulsion force to use in the 147 | * automatic force-based layout. 148 | * 149 | * @deprecated since version 1.1 150 | * 151 | * @return corresponding property value 152 | */ 153 | @Deprecated 154 | public double getRepulsionForce() { 155 | return getDoubleProperty(PROPERTY_REPULSION_FORCE, DEFAULT_REPULSION_FORCE); 156 | } 157 | 158 | /** 159 | * Returns a property that indicates the attraction force to use in the 160 | * automatic force-based layout. 161 | * 162 | * @deprecated since version 1.1 163 | * 164 | * @return corresponding property value 165 | */ 166 | @Deprecated 167 | public double getAttractionForce() { 168 | return getDoubleProperty(PROPERTY_ATTRACTION_FORCE, DEFAULT_ATTRACTION_FORCE); 169 | } 170 | 171 | /** 172 | * Returns a property that indicates the attraction scale to use in the 173 | * automatic force-based layout. 174 | * 175 | * @deprecated since version 1.1 176 | * 177 | * @return corresponding property value 178 | */ 179 | @Deprecated 180 | public double getAttractionScale() { 181 | return getDoubleProperty(PROPERTY_ATTRACTION_SCALE, DEFAULT_ATTRACTION_SCALE); 182 | } 183 | 184 | /** 185 | * Returns a property that indicates whether a vertex has a tooltip installed. 186 | * 187 | * @return corresponding property value 188 | */ 189 | public boolean getUseVertexTooltip() { 190 | return getBooleanProperty(PROPERTY_VERTEX_USE_TOOLTIP, DEFAULT_VERTEX_USE_TOOLTIP); 191 | } 192 | 193 | /** 194 | * Returns a property that indicates whether a vertex has a {@link SmartLabel} 195 | * attached to it. 196 | * 197 | * @return corresponding property value 198 | */ 199 | public boolean getUseVertexLabel() { 200 | return getBooleanProperty(PROPERTY_VERTEX_USE_LABEL, DEFAULT_VERTEX_USE_LABEL); 201 | } 202 | 203 | /** 204 | * Returns a property that indicates whether an edge has a tooltip installed. 205 | * 206 | * @return corresponding property value 207 | */ 208 | public boolean getUseEdgeTooltip() { 209 | return getBooleanProperty(PROPERTY_EDGE_USE_TOOLTIP, DEFAULT_EDGE_USE_TOOLTIP); 210 | } 211 | 212 | /** 213 | * Returns a property that indicates whether an edge has a {@link SmartLabel} 214 | * attached to it. 215 | * 216 | * @return corresponding property value 217 | */ 218 | public boolean getUseEdgeLabel() { 219 | return getBooleanProperty(PROPERTY_EDGE_USE_LABEL, DEFAULT_EDGE_USE_LABEL); 220 | } 221 | 222 | /** 223 | * Returns a property that indicates whether a {@link SmartArrow} should be 224 | * attached to an edge. 225 | * 226 | * @return corresponding property value 227 | */ 228 | public boolean getUseEdgeArrow() { 229 | return getBooleanProperty(PROPERTY_EDGE_USE_ARROW, DEFAULT_EDGE_USE_ARROW); 230 | } 231 | 232 | /** 233 | * Returns a property that indicates the size of the {@link SmartArrow}. 234 | * 235 | * @return corresponding property value 236 | */ 237 | public double getEdgeArrowSize() { 238 | return getDoubleProperty(PROPERTY_ARROW_SIZE, DEFAULT_ARROW_SIZE); 239 | } 240 | 241 | 242 | private double getDoubleProperty(String propertyName, double defaultValue) { 243 | String p = properties.getProperty(propertyName, Double.toString(defaultValue)); 244 | try { 245 | return Double.parseDouble(p); 246 | } catch (NumberFormatException e) { 247 | System.err.printf("Error in reading property %s: %s", propertyName, e.getMessage()); 248 | return defaultValue; 249 | } 250 | 251 | } 252 | 253 | private boolean getBooleanProperty(String propertyName, boolean defaultValue) { 254 | String p = properties.getProperty(propertyName, Boolean.toString(defaultValue)); 255 | try { 256 | return Boolean.parseBoolean(p); 257 | } catch (NumberFormatException e) { 258 | System.err.printf("Error in reading property %s: %s", propertyName, e.getMessage()); 259 | return defaultValue; 260 | } 261 | } 262 | 263 | @SuppressWarnings("all") 264 | private String getStringProperty(String propertyName, String defaultValue) { 265 | return properties.getProperty(propertyName, defaultValue); 266 | } 267 | 268 | /** 269 | * Test program. 270 | * @param args not used 271 | */ 272 | public static void main(String[] args) { 273 | SmartGraphProperties props = new SmartGraphProperties(); 274 | System.out.println("Prop vertex radius: " + props.getVertexRadius()); 275 | System.out.println("Prop vertex shape: " + props.getVertexShape()); 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartGraphVertex.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 com.vittoriopiotti.pathgraph.graph.Graph; 11 | import com.vittoriopiotti.pathgraph.graph.Vertex; 12 | 13 | /** 14 | * Abstracts the internal representation and behavior of a visualized graph vertex. 15 | * 16 | * @param Type stored in the underlying vertex 17 | * 18 | * @see SmartGraphPanel 19 | * 20 | * @author brunomnsilva 21 | */ 22 | public interface SmartGraphVertex extends SmartStylableNode { 23 | 24 | /** 25 | * Returns the underlying (stored reference) graph vertex. 26 | * 27 | * @return vertex reference 28 | * 29 | * @see Graph 30 | */ 31 | Vertex getUnderlyingVertex(); 32 | 33 | /** 34 | * Sets the position of this vertex in panel coordinates. 35 | *
36 | * Apart from its usage in the {@link SmartGraphPanel}, this method 37 | * should only be called when implementing {@link SmartPlacementStrategy}. 38 | * 39 | * @param x x-coordinate for the vertex 40 | * @param y y-coordinate for the vertex 41 | */ 42 | void setPosition(double x, double y); 43 | 44 | /** 45 | * Return the center x-coordinate of this vertex in panel coordinates. 46 | * 47 | * @return x-coordinate of the vertex 48 | */ 49 | double getPositionCenterX(); 50 | 51 | /** 52 | * Return the center y-coordinate of this vertex in panel coordinates. 53 | * 54 | * @return y-coordinate of the vertex 55 | */ 56 | double getPositionCenterY(); 57 | 58 | /** 59 | * Returns the circle radius used to represent this vertex. 60 | * 61 | * @return circle radius 62 | */ 63 | double getRadius(); 64 | 65 | /** 66 | * Returns the label node for further styling. 67 | * 68 | * @return the label node. 69 | */ 70 | SmartStylableNode getStylableLabel(); 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartLabel.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.graphview; 14 | 15 | 16 | import javafx.beans.property.DoubleProperty; 17 | import javafx.beans.property.ReadOnlyDoubleProperty; 18 | import javafx.beans.property.SimpleDoubleProperty; 19 | import javafx.scene.text.Text; 20 | 21 | /** 22 | * A label contains text and can be attached to any {@link SmartLabelledNode}. 23 | *
24 | * This class extends from {@link Text} and is allowed any corresponding 25 | * css formatting. 26 | * 27 | * @author brunomnsilva 28 | *

Modified by vittoriopiotti

29 | */ 30 | 31 | public class SmartLabel extends Text implements SmartStylableNode { 32 | 33 | private final SmartStyleProxy styleProxy; 34 | 35 | private final DoubleProperty layoutWidth; 36 | private final DoubleProperty layoutHeight; 37 | 38 | /** 39 | * Default constructor. 40 | * @param text the text of the SmartLabel. 41 | */ 42 | public SmartLabel(String text) { 43 | this(0, 0, text); 44 | } 45 | 46 | /** 47 | * Constructor that accepts an initial position. 48 | * @param x initial x coordinate 49 | * @param y initial y coordinate 50 | * @param text the text of the SmartLabel. 51 | */ 52 | public SmartLabel(double x, double y, String text) { 53 | super(x, y, text); 54 | styleProxy = new SmartStyleProxy(this); 55 | 56 | this.layoutWidth = new SimpleDoubleProperty( ); 57 | this.layoutHeight = new SimpleDoubleProperty( ); 58 | 59 | layoutBoundsProperty().addListener((observableValue, oldValue, newValue) -> { 60 | if(newValue != null) { 61 | if(Double.compare(layoutWidth.doubleValue(), newValue.getWidth()) != 0) { 62 | layoutWidth.set(newValue.getWidth()); 63 | } 64 | if(Double.compare(layoutHeight.doubleValue(), newValue.getHeight()) != 0) { 65 | layoutHeight.set(newValue.getHeight()); 66 | } 67 | } 68 | }); 69 | } 70 | /** 71 | * Applies the hover style to the label. 72 | * It removes the default style and adds the hover style class. 73 | * 74 | * @author vittoriopiotti 75 | */ 76 | public void applyHoverStyle() { 77 | // Check if the current style class is not already 'edge-label-hover' 78 | if (!getStyleClass().contains("edge-label-hover") && getStyleClass().contains("edge-label")) { 79 | // Remove default style class if it exists 80 | getStyleClass().remove("edge-label"); 81 | 82 | // Add hover style class 83 | getStyleClass().add("edge-label-hover"); 84 | } 85 | } 86 | 87 | /** 88 | * Applies the default style to the label. 89 | * It removes the hover style and adds the default style class. 90 | * 91 | * @author vittoriopiotti 92 | */ 93 | public void applyDefaultStyle() { 94 | // Check if the current style class is not already 'edge-label' 95 | if (!getStyleClass().contains("edge-label") && getStyleClass().contains("edge-label-hover")) { 96 | // Remove hover style class if it exists 97 | getStyleClass().remove("edge-label-hover"); 98 | 99 | // Add default style class 100 | getStyleClass().add("edge-label"); 101 | } 102 | } 103 | /** 104 | * Returns the read-only property representing the layout width of this label. 105 | * 106 | * @return the read-only property representing the layout width 107 | */ 108 | public ReadOnlyDoubleProperty layoutWidthProperty() { 109 | return layoutWidth; 110 | } 111 | 112 | /** 113 | * Returns the read-only property representing the layout height of this label. 114 | * 115 | * @return the read-only property representing the layout height 116 | */ 117 | public ReadOnlyDoubleProperty layoutHeightProperty() { 118 | return layoutHeight; 119 | } 120 | 121 | /** 122 | * Use instead of {@link #setText(String)} to allow for correct layout adjustments and label placement. 123 | * @param text the text to display on the label 124 | */ 125 | public void setText_(String text) { 126 | if(getText().compareTo(text) != 0) { 127 | setText(text); 128 | } 129 | } 130 | 131 | @Override 132 | public void setStyleInline(String css) { 133 | styleProxy.setStyleInline(css); 134 | } 135 | 136 | @Override 137 | public void setStyleClass(String cssClass) { 138 | styleProxy.setStyleClass(cssClass); 139 | } 140 | 141 | @Override 142 | public void addStyleClass(String cssClass) { 143 | styleProxy.addStyleClass(cssClass); 144 | } 145 | 146 | @Override 147 | public boolean removeStyleClass(String cssClass) { 148 | return styleProxy.removeStyleClass(cssClass); 149 | } 150 | 151 | } 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartLabelProvider.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 provider interface for generating labels. 11 | * 12 | * @param the type of the elements for which labels are generated 13 | * 14 | * @author brunomnsilva 15 | */ 16 | public interface SmartLabelProvider { 17 | 18 | /** 19 | * Returns the label for the specified element. 20 | *
21 | * The returned value is expected to be non-null. 22 | * 23 | * @param element the element for which the label is generated 24 | * @return the label for the specified element 25 | */ 26 | String valueFor(T element); 27 | } -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartLabelSource.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 java.lang.annotation.ElementType; 11 | import java.lang.annotation.Retention; 12 | import java.lang.annotation.RetentionPolicy; 13 | import java.lang.annotation.Target; 14 | 15 | /** 16 | * Method annotation to override an element's label provider. 17 | *
18 | * The annotated method must return a value, otherwise an exception will be thrown. 19 | *
20 | * By default, the text label is obtained from the toString method if this 21 | * annotation is not present in any other class method; this is also the case 22 | * with String and other boxed-types, e.g., Integer, Double, etc. 23 | *
24 | * If multiple annotations exist, the behavior is undefined. 25 | * 26 | * @author brunomnsilva 27 | */ 28 | @Retention(RetentionPolicy.RUNTIME) 29 | @Target(ElementType.METHOD) 30 | public @interface SmartLabelSource { 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartLabelledNode.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 node to which a {@link SmartLabel} can be attached. 11 | * 12 | * @author brunomnsilva 13 | */ 14 | public interface SmartLabelledNode { 15 | 16 | /** 17 | * Own and bind the label position to the desired position. 18 | * 19 | * @param label text label node 20 | */ 21 | void attachLabel(SmartLabel label); 22 | 23 | /** 24 | * Returns the attached text label, if any. 25 | * 26 | * @return the text label reference or null if no label is attached 27 | */ 28 | SmartLabel getAttachedLabel(); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartPlacementStrategy.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 | * Contains the method that should be implemented when creating new vertex placement 11 | * strategies. 12 | * 13 | * @author brunomnsilva 14 | */ 15 | public interface SmartPlacementStrategy { 16 | 17 | /** 18 | * Implementations of placement strategies must implement this interface. 19 | *
20 | * Should use the {@link SmartGraphVertex#setPosition(double, double) } method to place individual vertices. 21 | * 22 | * @param Generic type for element stored at vertices. 23 | * @param Generic type for element stored at edges. 24 | * @param width Width of the area in which to place the vertices. 25 | * @param height Height of the area in which to place the vertices. 26 | * @param smartGraphPanel Reference to the {@link SmartGraphPanel} whose internal vertices are to be placed. 27 | * The vertices to be placed can be obtained through {@link SmartGraphPanel#getSmartVertices()} 28 | * 29 | */ 30 | void place(double width, double height, SmartGraphPanel smartGraphPanel); 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartRadiusProvider.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 provider interface for generating radii values. 11 | * 12 | * @param the type of the elements for which radii are generated 13 | * 14 | * @author brunomnsilva 15 | */ 16 | public interface SmartRadiusProvider { 17 | 18 | /** 19 | * Returns the radius for the specified element. 20 | *
21 | * The returned value is expected to be positive. 22 | * 23 | * @param vertexElement the element for which the radius is generated 24 | * @return the radius for the specified element 25 | */ 26 | double valueFor(T vertexElement); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartRadiusSource.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 java.lang.annotation.ElementType; 10 | import java.lang.annotation.Retention; 11 | import java.lang.annotation.RetentionPolicy; 12 | import java.lang.annotation.Target; 13 | 14 | /** 15 | * Method annotation to override an element's vertex shape radius. 16 | *
17 | * The annotated method must return a value (Double) with a non-negative value, otherwise an exception will be thrown. 18 | *
19 | * By default, the shape radius is defined in "smartgraph.properties" or in a custom properties file. 20 | *
21 | * If multiple annotations exist, the behavior is undefined. 22 | * 23 | * @author brunomnsilva 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target(ElementType.METHOD) 27 | public @interface SmartRadiusSource { 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartShapeTypeProvider.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 provider interface for generating shape types. 11 | * 12 | * @param the type of the elements for which shape types are generated 13 | * 14 | * @author brunomnsilva 15 | */ 16 | public interface SmartShapeTypeProvider { 17 | /** 18 | * Returns the shape type for the specified element. 19 | *
20 | * The returned value is expected to be non-null and a valid type, see {@link ShapeFactory}. 21 | * 22 | * @param vertexElement the element for which the shape type is generated 23 | * @return the shape type for the specified element 24 | */ 25 | String valueFor(T vertexElement); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartShapeTypeSource.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 java.lang.annotation.ElementType; 10 | import java.lang.annotation.Retention; 11 | import java.lang.annotation.RetentionPolicy; 12 | import java.lang.annotation.Target; 13 | 14 | /** 15 | * Method annotation to override an element's vertex shape type representation. 16 | *
17 | * The annotated method must return a value (String) with a valid type (see {@link ShapeFactory}), otherwise an exception will be thrown. 18 | *
19 | * By default, a circle is used for the vertex shape representation. 20 | *
21 | * If multiple annotations exist, the behavior is undefined. 22 | * 23 | * @author brunomnsilva 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target(ElementType.METHOD) 27 | public @interface SmartShapeTypeSource { 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartStylableNode.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 stylable node can have its css properties changed at runtime. 11 | *
12 | * All Java FX nodes used by {@link SmartGraphPanel} to represent graph entities 13 | * should implement this interface. 14 | * 15 | * @see SmartGraphPanel 16 | * 17 | * @author brunomnsilva 18 | */ 19 | public interface SmartStylableNode { 20 | 21 | /** 22 | * Applies cumulatively the css inline styles to the node. 23 | *
24 | * These inline JavaFX styles have higher priority and are not overwritten by 25 | * any css classes set by {@link SmartStylableNode#addStyleClass(java.lang.String) }. 26 | * But will be discarded if you use {@link SmartStylableNode#setStyleClass(java.lang.String) } 27 | *
28 | * If you need to clear any previously set inline styles, use 29 | * .setStyleInline(null). Not that this will clear all inline styles previously applied. 30 | * 31 | * @param css styles 32 | */ 33 | void setStyleInline(String css); 34 | 35 | /** 36 | * Applies the CSS styling defined in class selector cssClass. 37 | *
38 | * The cssClass string must not contain a preceding dot, e.g., 39 | * "myClass" instead of ".myClass". 40 | *
41 | * The CSS Class must be defined in smartgraph.css file or 42 | * in the custom provided stylesheet. 43 | *
44 | * The expected behavior is to remove all current styling before 45 | * applying the class css. 46 | * 47 | * @param cssClass name of the CSS class. 48 | */ 49 | void setStyleClass(String cssClass); 50 | 51 | /** 52 | * Applies cumulatively the CSS styling defined in class selector 53 | * cssClass. 54 | *
55 | * The CSS Class must be defined in smartgraph.css file or 56 | * in the custom provided stylesheet. 57 | *
58 | * The cumulative operation will overwrite any existing styling elements 59 | * previously defined for previous classes. 60 | * 61 | * @param cssClass name of the CSS class. 62 | */ 63 | void addStyleClass(String cssClass); 64 | 65 | /** 66 | * Removes a previously cssClass existing CSS styling. 67 | *
68 | * Given styles can be added sequentially, the removal of a css class 69 | * will be a removal that keeps the previous ordering of kept styles. 70 | * 71 | * @param cssClass name of the CSS class. 72 | * 73 | * @return true if successful; false if cssClass wasn't 74 | * previously set. 75 | */ 76 | boolean removeStyleClass(String cssClass); 77 | 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/SmartStyleProxy.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.scene.shape.Shape; 11 | 12 | /** 13 | * This class acts as a proxy for styling of nodes. 14 | *
15 | * It essentially groups all the logic, avoiding code duplicate. 16 | *
17 | * Classes that have this behavior can delegate the method calls to an instance 18 | * of this class. 19 | * 20 | * @author brunomnsilva 21 | */ 22 | public class SmartStyleProxy implements SmartStylableNode { 23 | 24 | private Shape client; 25 | 26 | /** 27 | * Creates a new style proxy for a shape client. 28 | * @param client the shape client 29 | */ 30 | public SmartStyleProxy(Shape client) { 31 | this.client = client; 32 | } 33 | 34 | /** 35 | * Changes the shape client of this proxy. 36 | * @param client the new shape client 37 | */ 38 | public void setClient(Shape client) { 39 | this.client = client; 40 | } 41 | 42 | @Override 43 | public void setStyleInline(String css) { 44 | client.setStyle(css); 45 | } 46 | 47 | @Override 48 | public void setStyleClass(String cssClass) { 49 | client.getStyleClass().clear(); 50 | client.setStyle(null); 51 | client.getStyleClass().add(cssClass); 52 | } 53 | 54 | @Override 55 | public void addStyleClass(String cssClass) { 56 | client.getStyleClass().add(cssClass); 57 | } 58 | 59 | @Override 60 | public boolean removeStyleClass(String cssClass) { 61 | return client.getStyleClass().remove(cssClass); 62 | } 63 | 64 | 65 | /** 66 | * Copies all the styles and classes (currently applied) of source to destination. 67 | * @param source the shape whose styles are to be copied 68 | * @param destination the shape that receives the copied styles 69 | */ 70 | protected static void copyStyling(Shape source, Shape destination) { 71 | destination.setStyle(source.getStyle()); 72 | destination.getStyleClass().addAll(source.getStyleClass()); 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/UtilitiesBindings.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.binding.DoubleBinding; 10 | import javafx.beans.value.ObservableDoubleValue; 11 | 12 | import static javafx.beans.binding.Bindings.createDoubleBinding; 13 | 14 | /** 15 | * Some {@link Math} operations implemented as bindings. 16 | * 17 | * @author brunomnsilva 18 | */ 19 | public class UtilitiesBindings { 20 | 21 | /** 22 | * Binding for {@link java.lang.Math#atan2(double, double)} 23 | * 24 | * @param y the ordinate coordinate 25 | * @param x the abscissa coordinate 26 | * @return the theta component of the point 27 | * (rtheta) 28 | * in polar coordinates that corresponds to the point 29 | * (xy) in Cartesian coordinates. 30 | */ 31 | public static DoubleBinding atan2(final ObservableDoubleValue y, final ObservableDoubleValue x) { 32 | return createDoubleBinding(() -> Math.atan2(y.get(), x.get()), y, x); 33 | } 34 | 35 | /** 36 | * Binding for {@link java.lang.Math#toDegrees(double)} 37 | * 38 | * @param angRad an angle, in radians 39 | * @return the measurement of the angle {@code angRad} 40 | * in degrees. 41 | */ 42 | public static DoubleBinding toDegrees(final ObservableDoubleValue angRad) { 43 | return createDoubleBinding(() -> Math.toDegrees(angRad.get()), angRad); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/graphview/UtilitiesPoint2D.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 | * Class with utility methods for Point2D instances. 13 | * 14 | * @author brunomnsilva 15 | */ 16 | public class UtilitiesPoint2D { 17 | 18 | /** 19 | * Rotate a point around a pivot point by a specific degrees amount 20 | * @param point point to rotate 21 | * @param pivot pivot point 22 | * @param angleDegrees rotation degrees 23 | * @return rotated point 24 | */ 25 | public static Point2D rotate(final Point2D point, final Point2D pivot, final double angleDegrees) { 26 | double angleRadians = Math.toRadians(angleDegrees); // Convert angle to radians 27 | 28 | double sin = Math.sin(angleRadians); 29 | double cos = Math.cos(angleRadians); 30 | 31 | // Translate the point relative to the pivot 32 | double translatedX = point.getX() - pivot.getX(); 33 | double translatedY = point.getY() - pivot.getY(); 34 | 35 | // Apply rotation using trigonometric functions 36 | double rotatedX = translatedX * cos - translatedY * sin; 37 | double rotatedY = translatedX * sin + translatedY * cos; 38 | 39 | // Translate the rotated point back to the original position 40 | rotatedX += pivot.getX(); 41 | rotatedY += pivot.getY(); 42 | 43 | return new Point2D(rotatedX, rotatedY); 44 | } 45 | 46 | /** 47 | * Calculates the third vertex point that forms a triangle with segment AB as the base and C equidistant to A and B; 48 | * angleDegrees is the angle formed between A and C. 49 | * 50 | * @param pointA the point a 51 | * @param pointB the point b 52 | * @param angleDegrees desired angle (in degrees) 53 | * @return the point c 54 | */ 55 | public static Point2D calculateTriangleBetween(final Point2D pointA, final Point2D pointB, final double angleDegrees) { 56 | // Calculate the midpoint of AB 57 | Point2D midpointAB = pointA.midpoint(pointB); 58 | 59 | // Calculate the perpendicular bisector of AB 60 | double slopeAB = (pointB.getY() - pointA.getY()) / (pointB.getX() - pointA.getX()); 61 | double perpendicularSlope = -1 / slopeAB; 62 | 63 | // Handle special cases where the perpendicular bisector is vertical or horizontal 64 | if (Double.isInfinite(perpendicularSlope)) { 65 | double yC = midpointAB.getY() + Math.tan(Math.toRadians(angleDegrees)) * midpointAB.getX(); 66 | return new Point2D(midpointAB.getX(), yC); 67 | } else if (perpendicularSlope == 0) { 68 | return new Point2D(pointA.getX(), midpointAB.getY()); 69 | } 70 | 71 | // Calculate the angle between AB and the x-axis 72 | double angleAB = Math.toDegrees(Math.atan2(pointB.getY() - pointA.getY(), pointB.getX() - pointA.getX())); 73 | 74 | // Calculate the angle between AB and AC 75 | double angleAC = angleAB + angleDegrees; 76 | 77 | // Calculate the coordinates of point C 78 | double distanceAC = pointA.distance(midpointAB) / Math.cos(Math.toRadians(angleDegrees)); 79 | double xC = pointA.getX() + distanceAC * Math.cos(Math.toRadians(angleAC)); 80 | double yC = perpendicularSlope * (xC - midpointAB.getX()) + midpointAB.getY(); 81 | 82 | return new Point2D(xC, yC); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/utilities/SvgConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Bootstrap Icons v1.11.0 (https://blog.getbootstrap.com/2023/09/12/bootstrap-icons-1-11-0/) 3 | * Bootstrap | Copyright 2019-2024 The Bootstrap Authors 4 | * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE) 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.utilities; 15 | 16 | 17 | /** 18 | * This class contains static variable to simplify the access of icons. 19 | * Icons by Bootstrap ((link)) 20 | *

Available icons:

21 | *
    22 | *
  • {@link #SVG_GITHUB}
  • 23 | *
  • {@link #SVG_MINUS}
  • 24 | *
  • {@link #SVG_PLUS}>
  • 25 | *
  • {@link #SVG_PENCIL}
  • 26 | *
  • {@link #SVG_FILETYPE_JSON}
  • 27 | *
  • {@link #SVG_CHECK}
  • 28 | *
  • {@link #SVG_X_XL}
  • 29 | *
  • {@link #SVG_ARROW_EXPAND}
  • 30 | *
  • {@link #SVG_ERASER}
  • 31 | *
  • {@link #SVG_UPLOAD}
  • 32 | *
  • {@link #SVG_DOWNLOAD}
  • 33 | *
  • {@link #SVG_CAMERA}
  • 34 | *
  • {@link #SVG_GEO_ALT}
  • 35 | *
  • {@link #SVG_EYE}
  • 36 | *
  • {@link #SVG_DPAD}
  • 37 | *
  • {@link #SVG_ARROW_CLOCKWISE}
  • 38 | *
  • {@link #SVG_ARROW_REPEAT}
  • 39 | *
  • {@link #SVG_ZOOM_IN}
  • 40 | *
  • {@link #SVG_ZOOM_OUT}
  • 41 | *
  • {@link #SVG_ROCKET_TAKEOFF}
  • 42 | *
  • {@link #SVG_EYE_SLASH}
  • 43 | *
44 | * 45 | * @author vittoriopiotti 46 | */ 47 | public class SvgConstants { 48 | /** 49 | * Icon GITHUB 50 | * ((link)) 51 | */ 52 | public static final String SVG_GITHUB = "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8"; 53 | /** 54 | * Icon MINUS 55 | * ((link)) 56 | */ 57 | public static final String SVG_MINUS = "M2 8a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11A.5.5 0 0 1 2 8"; 58 | /** 59 | * Icon PLUS 60 | * ((link)) 61 | */ 62 | public static final String SVG_PLUS = "M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2"; 63 | /** 64 | * Icon PENCIL 65 | * ((link)) 66 | */ 67 | public static final String SVG_PENCIL = "M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"; 68 | /** 69 | * Icon FILETYPE JSON 70 | * ((link)) 71 | */ 72 | public static final String SVG_FILETYPE_JSON = "M14 4.5V11h-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5zM4.151 15.29a1.2 1.2 0 0 1-.111-.449h.764a.58.58 0 0 0 .255.384q.105.073.25.114.142.041.319.041.245 0 .413-.07a.56.56 0 0 0 .255-.193.5.5 0 0 0 .084-.29.39.39 0 0 0-.152-.326q-.152-.12-.463-.193l-.618-.143a1.7 1.7 0 0 1-.539-.214 1 1 0 0 1-.352-.367 1.1 1.1 0 0 1-.123-.524q0-.366.19-.639.192-.272.528-.422.337-.15.777-.149.456 0 .779.152.326.153.5.41.18.255.2.566h-.75a.56.56 0 0 0-.12-.258.6.6 0 0 0-.246-.181.9.9 0 0 0-.37-.068q-.324 0-.512.152a.47.47 0 0 0-.185.384q0 .18.144.3a1 1 0 0 0 .404.175l.621.143q.326.075.566.211a1 1 0 0 1 .375.358q.135.222.135.56 0 .37-.188.656a1.2 1.2 0 0 1-.539.439q-.351.158-.858.158-.381 0-.665-.09a1.4 1.4 0 0 1-.478-.252 1.1 1.1 0 0 1-.29-.375m-3.104-.033a1.3 1.3 0 0 1-.082-.466h.764a.6.6 0 0 0 .074.27.5.5 0 0 0 .454.246q.285 0 .422-.164.137-.165.137-.466v-2.745h.791v2.725q0 .66-.357 1.005-.355.345-.985.345a1.6 1.6 0 0 1-.568-.094 1.15 1.15 0 0 1-.407-.266 1.1 1.1 0 0 1-.243-.39m9.091-1.585v.522q0 .384-.117.641a.86.86 0 0 1-.322.387.9.9 0 0 1-.47.126.9.9 0 0 1-.47-.126.87.87 0 0 1-.32-.387 1.55 1.55 0 0 1-.117-.641v-.522q0-.386.117-.641a.87.87 0 0 1 .32-.387.87.87 0 0 1 .47-.129q.265 0 .47.129a.86.86 0 0 1 .322.387q.117.255.117.641m.803.519v-.513q0-.565-.205-.973a1.46 1.46 0 0 0-.59-.63q-.38-.22-.916-.22-.534 0-.92.22a1.44 1.44 0 0 0-.589.628q-.205.407-.205.975v.513q0 .562.205.973.205.407.589.626.386.217.92.217.536 0 .917-.217.384-.22.589-.626.204-.41.205-.973m1.29-.935v2.675h-.746v-3.999h.662l1.752 2.66h.032v-2.66h.75v4h-.656l-1.761-2.676z"; 73 | /** 74 | * Icon CHECK 75 | * ((link)) 76 | */ 77 | public static final String SVG_CHECK = "M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0"; 78 | /** 79 | * Icon X XL 80 | * ((link)) 81 | */ 82 | public static final String SVG_X_XL = "M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"; 83 | /** 84 | * Icon ARROW EXPAND 85 | * ((link)) 86 | */ 87 | public static final String SVG_ARROW_EXPAND = "M1 8a.5.5 0 0 1 .5-.5h13a.5.5 0 0 1 0 1h-13A.5.5 0 0 1 1 8ZM7.646.146a.5.5 0 0 1 .708 0l2 2a.5.5 0 0 1-.708.708L8.5 1.707V5.5a.5.5 0 0 1-1 0V1.707L6.354 2.854a.5.5 0 1 1-.708-.708l2-2ZM8 10a.5.5 0 0 1 .5.5v3.793l1.146-1.147a.5.5 0 0 1 .708.708l-2 2a.5.5 0 0 1-.708 0l-2-2a.5.5 0 0 1 .708-.708L7.5 14.293V10.5A.5.5 0 0 1 8 10Z"; 88 | /** 89 | * Icon ERASER 90 | * ((link)) 91 | */ 92 | public static final String SVG_ERASER = "M8.086 2.207a2 2 0 0 1 2.828 0l3.879 3.879a2 2 0 0 1 0 2.828l-5.5 5.5A2 2 0 0 1 7.879 15H5.12a2 2 0 0 1-1.414-.586l-2.5-2.5a2 2 0 0 1 0-2.828zm2.121.707a1 1 0 0 0-1.414 0L4.16 7.547l5.293 5.293 4.633-4.633a1 1 0 0 0 0-1.414zM8.746 13.547 3.453 8.254 1.914 9.793a1 1 0 0 0 0 1.414l2.5 2.5a1 1 0 0 0 .707.293H7.88a1 1 0 0 0 .707-.293z"; 93 | /** 94 | * Icon UPLOAD 95 | * ((link)) 96 | */ 97 | public static final String SVG_UPLOAD = ( 98 | "M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5" + 99 | "M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708z" 100 | ); 101 | /** 102 | * Icon DOWNLOAD 103 | * ((link)) 104 | */ 105 | public static final String SVG_DOWNLOAD = ( 106 | "M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5" + 107 | "M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708z" 108 | ); 109 | /** 110 | * Icon CAMERA 111 | * ((link)) 112 | */ 113 | public static final String SVG_CAMERA = ( 114 | "M15 12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h1.172a3 3 0 0 0 2.12-.879l.83-.828A1 1 0 0 1 6.827 3h2.344a1 1 0 0 1 .707.293l.828.828A3 3 0 0 0 12.828 5H14a1 1 0 0 1 1 1zM2 4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-1.172a2 2 0 0 1-1.414-.586l-.828-.828A2 2 0 0 0 9.172 2H6.828a2 2 0 0 0-1.414.586l-.828.828A2 2 0 0 1 3.172 4z" + 115 | "M8 11a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5m0 1a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7M3 6.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0" 116 | ); 117 | /** 118 | * Icon GEO ALT 119 | * ((link)) 120 | */ 121 | public static final String SVG_GEO_ALT = ( 122 | "M12.166 8.94c-.524 1.062-1.234 2.12-1.96 3.07A32 32 0 0 1 8 14.58a32 32 0 0 1-2.206-2.57c-.726-.95-1.436-2.008-1.96-3.07C3.304 7.867 3 6.862 3 6a5 5 0 0 1 10 0c0 .862-.305 1.867-.834 2.94M8 16s6-5.686 6-10A6 6 0 0 0 2 6c0 4.314 6 10 6 10" + 123 | "M8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4m0 1a3 3 0 1 0 0-6 3 3 0 0 0 0 6" 124 | ); 125 | /** 126 | * Icon EYE 127 | * ((link)) 128 | */ 129 | public static final String SVG_EYE = ( 130 | "M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8M1.173 8a13 13 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5s3.879 1.168 5.168 2.457A13 13 0 0 1 14.828 8q-.086.13-.195.288c-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5s-3.879-1.168-5.168-2.457A13 13 0 0 1 1.172 8z" + 131 | "M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5M4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0" 132 | ); 133 | 134 | /** 135 | * Icon DPAD 136 | * ((link)) 137 | */ 138 | public final static String SVG_DPAD = ( 139 | "m7.788 2.34-.799 1.278A.25.25 0 0 0 7.201 4h1.598a.25.25 0 0 0 .212-.382l-.799-1.279a.25.25 0 0 0-.424 0Zm0 11.32-.799-1.277A.25.25 0 0 1 7.201 12h1.598a.25.25 0 0 1 .212.383l-.799 1.278a.25.25 0 0 1-.424 0ZM3.617 9.01 2.34 8.213a.25.25 0 0 1 0-.424l1.278-.799A.25.25 0 0 1 4 7.201V8.8a.25.25 0 0 1-.383.212Zm10.043-.798-1.277.799A.25.25 0 0 1 12 8.799V7.2a.25.25 0 0 1 .383-.212l1.278.799a.25.25 0 0 1 0 .424Z" + 140 | "M6.5 0A1.5 1.5 0 0 0 5 1.5v3a.5.5 0 0 1-.5.5h-3A1.5 1.5 0 0 0 0 6.5v3A1.5 1.5 0 0 0 1.5 11h3a.5.5 0 0 1 .5.5v3A1.5 1.5 0 0 0 6.5 16h3a1.5 1.5 0 0 0 1.5-1.5v-3a.5.5 0 0 1 .5-.5h3A1.5 1.5 0 0 0 16 9.5v-3A1.5 1.5 0 0 0 14.5 5h-3a.5.5 0 0 1-.5-.5v-3A1.5 1.5 0 0 0 9.5 0zM6 1.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 .5.5v3A1.5 1.5 0 0 0 11.5 6h3a.5.5 0 0 1 .5.5v3a.5.5 0 0 1-.5.5h-3a1.5 1.5 0 0 0-1.5 1.5v3a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-3A1.5 1.5 0 0 0 4.5 10h-3a.5.5 0 0 1-.5-.5v-3a.5.5 0 0 1 .5-.5h3A1.5 1.5 0 0 0 6 4.5z" 141 | ); 142 | 143 | /** 144 | * Icon ARROW CLOCKWISE 145 | * ((link)) 146 | */ 147 | public static final String SVG_ARROW_CLOCKWISE = ( 148 | "M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z" + 149 | "M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z" 150 | ); 151 | /** 152 | * Icon ARROW REPEAT 153 | * ((link)) 154 | */ 155 | public static final String SVG_ARROW_REPEAT = ( 156 | "M11.534 7h3.932a.25.25 0 0 1 .192.41l-1.966 2.36a.25.25 0 0 1-.384 0l-1.966-2.36a.25.25 0 0 1 .192-.41m-11 2h3.932a.25.25 0 0 0 .192-.41L2.692 6.23a.25.25 0 0 0-.384 0L.342 8.59A.25.25 0 0 0 .534 9" + 157 | "M8 3c-1.552 0-2.94.707-3.857 1.818a.5.5 0 1 1-.771-.636A6.002 6.002 0 0 1 13.917 7H12.9A5 5 0 0 0 8 3M3.1 9a5.002 5.002 0 0 0 8.757 2.182.5.5 0 1 1 .771.636A6.002 6.002 0 0 1 2.083 9z" 158 | ); 159 | /** 160 | * Icon ZOOM IN 161 | * ((link)) 162 | */ 163 | public static final String SVG_ZOOM_IN = ( 164 | "M6.5 12a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11M13 6.5a6.5 6.5 0 1 1-13 0 6.5 6.5 0 0 1 13 0" + 165 | "M10.344 11.742q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1 6.5 6.5 0 0 1-1.398 1.4z" + 166 | "M6.5 3a.5.5 0 0 1 .5.5V6h2.5a.5.5 0 0 1 0 1H7v2.5a.5.5 0 0 1-1 0V7H3.5a.5.5 0 0 1 0-1H6V3.5a.5.5 0 0 1 .5-.5" 167 | ); 168 | /** 169 | * Icon ZOOM OUT 170 | * ((link)) 171 | */ 172 | public static final String SVG_ZOOM_OUT = ( 173 | "M6.5 12a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11M13 6.5a6.5 6.5 0 1 1-13 0 6.5 6.5 0 0 1 13 0" + 174 | "M10.344 11.742q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1 6.5 6.5 0 0 1-1.398 1.4z" + 175 | "M3 6.5a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5" 176 | ); 177 | /** 178 | * Icon ROCKET TAKEOFF 179 | * ((link)) 180 | */ 181 | public static final String SVG_ROCKET_TAKEOFF = ( 182 | "M9.752 6.193c.599.6 1.73.437 2.528-.362s.96-1.932.362-2.531c-.599-.6-1.73-.438-2.528.361-.798.8-.96 1.933-.362 2.532" + 183 | "M15.811 3.312c-.363 1.534-1.334 3.626-3.64 6.218l-.24 2.408a2.56 2.56 0 0 1-.732 1.526L8.817 15.85a.51.51 0 0 1-.867-.434l.27-1.899c.04-.28-.013-.593-.131-.956a9 9 0 0 0-.249-.657l-.082-.202c-.815-.197-1.578-.662-2.191-1.277-.614-.615-1.079-1.379-1.275-2.195l-.203-.083a10 10 0 0 0-.655-.248c-.363-.119-.675-.172-.955-.132l-1.896.27A.51.51 0 0 1 .15 7.17l2.382-2.386c.41-.41.947-.67 1.524-.734h.006l2.4-.238C9.005 1.55 11.087.582 12.623.208c.89-.217 1.59-.232 2.08-.188.244.023.435.06.57.093q.1.026.16.045c.184.06.279.13.351.295l.029.073a3.5 3.5 0 0 1 .157.721c.055.485.051 1.178-.159 2.065m-4.828 7.475.04-.04-.107 1.081a1.54 1.54 0 0 1-.44.913l-1.298 1.3.054-.38c.072-.506-.034-.993-.172-1.418a9 9 0 0 0-.164-.45c.738-.065 1.462-.38 2.087-1.006M5.205 5c-.625.626-.94 1.351-1.004 2.09a9 9 0 0 0-.45-.164c-.424-.138-.91-.244-1.416-.172l-.38.054 1.3-1.3c.245-.246.566-.401.91-.44l1.08-.107zm9.406-3.961c-.38-.034-.967-.027-1.746.163-1.558.38-3.917 1.496-6.937 4.521-.62.62-.799 1.34-.687 2.051.107.676.483 1.362 1.048 1.928.564.565 1.25.941 1.924 1.049.71.112 1.429-.067 2.048-.688 3.079-3.083 4.192-5.444 4.556-6.987.183-.771.18-1.345.138-1.713a3 3 0 0 0-.045-.283 3 3 0 0 0-.3-.041Z" + 184 | "M7.009 12.139a7.6 7.6 0 0 1-1.804-1.352A7.6 7.6 0 0 1 3.794 8.86c-1.102.992-1.965 5.054-1.839 5.18.125.126 3.936-.896 5.054-1.902Z" 185 | ); 186 | /** 187 | * Icon EYE SLASH 188 | * ((link)) 189 | */ 190 | public static final String SVG_EYE_SLASH = ( 191 | "M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7 7 0 0 0-2.79.588l.77.771A6 6 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13 13 0 0 1 14.828 8q-.086.13-.195.288c-.335.48-.83 1.12-1.465 1.755q-.247.248-.517.486z"+ 192 | "M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829zm-2.943 1.299.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829"+ 193 | "M3.35 5.47q-.27.24-.518.487A13 13 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7 7 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.709zm10.296 8.884-12-12 .708-.708 12 12z" 194 | ); 195 | } 196 | -------------------------------------------------------------------------------- /src/main/java/com/vittoriopiotti/pathgraph/utilities/UtilitiesCapture.java: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * PathGraph v1.0.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/releases/tag/1.0.0) 4 | * PathGraph | Copyright 2024 Vittorio Piotti 5 | * Licensed under GPL v3.0 (https://github.com/vittorioPiotti/PathGraph-JavaFX/blob/main/LICENSE.txt) 6 | */ 7 | 8 | 9 | package com.vittoriopiotti.pathgraph.utilities; 10 | 11 | import com.vittoriopiotti.pathgraph.app.Constants; 12 | import javafx.animation.FadeTransition; 13 | import javafx.animation.PauseTransition; 14 | import javafx.application.Platform; 15 | import javafx.embed.swing.SwingFXUtils; 16 | import javafx.scene.SnapshotParameters; 17 | import javafx.scene.image.WritableImage; 18 | import javafx.scene.layout.Pane; 19 | import javafx.scene.paint.Color; 20 | import javafx.scene.shape.Rectangle; 21 | import javafx.stage.FileChooser; 22 | import javafx.util.Duration; 23 | import javax.imageio.ImageIO; 24 | import java.io.File; 25 | import java.io.IOException; 26 | import javafx.scene.transform.Transform; 27 | 28 | import java.util.concurrent.CompletableFuture; 29 | 30 | /** 31 | * Utility class for capturing images from a JavaFX Pane and saving them as PNG files. 32 | * This class provides methods for taking snapshots of JavaFX components with optional 33 | * animation effects and handling the file saving process asynchronously. 34 | * 35 | * @author vittoriopiotti 36 | */ 37 | public class UtilitiesCapture { 38 | 39 | /** 40 | * Captures a PNG image of the specified Pane and saves it to a file asynchronously. 41 | * An optional flash animation effect can be displayed during the capture process. 42 | * 43 | * @param parent the Pane to capture as an image. 44 | * @param isAnimated indicates whether to show a flash effect during the capture. 45 | * @return integer status code: 46 | * - SUCCESS if the capture and saving process is successful, 47 | * - ERROR if an error occurs during the capture or saving, 48 | * - INTERRUPTED if the user cancels the file saving dialog. 49 | */ 50 | public static CompletableFuture takeScreenshot(Pane parent, boolean isAnimated) { 51 | double scaleFactor = 3.0; 52 | Rectangle flashEffect = new Rectangle(); 53 | flashEffect.setFill(Color.WHITE); 54 | flashEffect.setOpacity(0); 55 | parent.getChildren().add(flashEffect); 56 | flashEffect.widthProperty().bind(parent.widthProperty()); 57 | flashEffect.heightProperty().bind(parent.heightProperty()); 58 | 59 | CompletableFuture futureResult = new CompletableFuture<>(); 60 | 61 | if (isAnimated) { 62 | FadeTransition fadeIn = new FadeTransition(Duration.seconds(0.1), flashEffect); 63 | fadeIn.setFromValue(0); 64 | fadeIn.setToValue(1); 65 | PauseTransition pause = new PauseTransition(Duration.seconds(0.1)); 66 | FadeTransition fadeOut = new FadeTransition(Duration.seconds(0.5), flashEffect); 67 | fadeOut.setFromValue(1); 68 | fadeOut.setToValue(0); 69 | 70 | fadeIn.setOnFinished(event -> pause.play()); 71 | pause.setOnFinished(event -> fadeOut.play()); 72 | fadeOut.setOnFinished(event -> takeSnapshot(parent, scaleFactor).thenAccept(result -> { 73 | parent.getChildren().remove(flashEffect); 74 | futureResult.complete(result); 75 | })); 76 | fadeIn.play(); 77 | } else { 78 | takeSnapshot(parent, scaleFactor).thenAccept(result -> { 79 | parent.getChildren().remove(flashEffect); 80 | futureResult.complete(result); 81 | }); 82 | } 83 | 84 | return futureResult; 85 | } 86 | 87 | /** 88 | * Captures a PNG image of the specified Pane and saves it to a file asynchronously. 89 | * An optional flash animation effect can be displayed during the capture process. 90 | * Animation is active 91 | * 92 | * @param parent the Pane to capture as an image. 93 | * @return integer status code: 94 | * - SUCCESS if the capture and saving process is successful, 95 | * - ERROR if an error occurs during the capture or saving, 96 | * - INTERRUPTED if the user cancels the file saving dialog. 97 | */ 98 | public static CompletableFuture takeScreenshot(Pane parent) { 99 | return takeScreenshot(parent,true); 100 | } 101 | 102 | /** 103 | * Takes a snapshot of the specified Pane at a given scale factor and returns 104 | * the result as a CompletableFuture with a status code. 105 | * 106 | * @param parent the Pane to capture as an image. 107 | * @param scaleFactor the scale factor for the snapshot (e.g., for higher resolution). 108 | * @return a CompletableFuture that completes with an integer status code: 109 | * - SUCCESS if the snapshot and saving process is successful, 110 | * - ERROR if an error occurs during the capture or saving. 111 | */ 112 | private static CompletableFuture takeSnapshot(Pane parent, double scaleFactor) { 113 | CompletableFuture snapshotFuture = new CompletableFuture<>(); 114 | double width = parent.getBoundsInParent().getWidth(); 115 | double height = parent.getBoundsInParent().getHeight(); 116 | WritableImage highResImage = new WritableImage((int) (width * scaleFactor), (int) (height * scaleFactor)); 117 | SnapshotParameters params = new SnapshotParameters(); 118 | params.setTransform(Transform.scale(scaleFactor, scaleFactor)); 119 | 120 | parent.snapshot(snapshotResult -> { 121 | WritableImage snapshot = snapshotResult.getImage(); 122 | if (snapshot.getWidth() == 0 || snapshot.getHeight() == 0) { 123 | snapshotFuture.complete(Constants.ERROR); 124 | } else { 125 | saveImageToFile(parent, snapshot).thenAccept(snapshotFuture::complete); 126 | } 127 | return null; 128 | }, params, highResImage); 129 | 130 | return snapshotFuture; 131 | } 132 | 133 | /** 134 | * Saves the provided image to a file using a file chooser dialog. The method is executed 135 | * on the JavaFX Application Thread and returns the result asynchronously. 136 | * 137 | * @param parent the Pane that is used as the owner for the file chooser dialog. 138 | * @param image the image to save as a PNG file. 139 | * @return a CompletableFuture that completes with an integer status code: 140 | * - SUCCESS if the image is saved successfully, 141 | * - ERROR if an error occurs during the saving process, 142 | * - INTERRUPTED if the user cancels the file dialog. 143 | */ 144 | private static CompletableFuture saveImageToFile(Pane parent, WritableImage image) { 145 | CompletableFuture saveFuture = new CompletableFuture<>(); 146 | 147 | Platform.runLater(() -> { 148 | FileChooser fileChooser = new FileChooser(); 149 | fileChooser.setTitle("Save Image As..."); 150 | fileChooser.getExtensionFilters().add( 151 | new FileChooser.ExtensionFilter("PNG Image", "*.png") 152 | ); 153 | File file = fileChooser.showSaveDialog(parent.getScene().getWindow()); 154 | if (file != null) { 155 | try { 156 | ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file); 157 | saveFuture.complete(Constants.SUCCESS); 158 | } catch (IOException ignored) { 159 | saveFuture.complete(Constants.ERROR); 160 | } 161 | } else { 162 | saveFuture.complete(Constants.INTERRUPTED); 163 | } 164 | }); 165 | 166 | return saveFuture; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module com.vittoriopiotti.pathgraph { 2 | requires javafx.controls; 3 | requires javafx.fxml; 4 | 5 | requires java.logging; 6 | requires javafx.swing; 7 | 8 | opens com.vittoriopiotti.pathgraph to javafx.fxml; 9 | exports com.vittoriopiotti.pathgraph; 10 | 11 | 12 | 13 | 14 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: com.vittoriopiotti.pathgraph.Main 3 | 4 | -------------------------------------------------------------------------------- /src/main/resources/smartgraph.css: -------------------------------------------------------------------------------- 1 | /* 2 | STYLESHEET FOR BOOSTSMARTGRAPH VISUALIZATION. 3 | 4 | For your customization, please see: 5 | https://openjfx.io/javadoc/19/javafx.graphics/javafx/scene/doc-files/cssref.html 6 | 7 | And know that: 8 | - The drawing area itself is of type Pane, which is an extension of Region; 9 | - A vertex is of type Shape; 10 | - The edges are of type Line and CubicLine; 11 | - The labels are of type Text, and; 12 | - The arrows are of type Path. 13 | 14 | This should help you understand which styles you can apply. 15 | */ 16 | 17 | .graph { 18 | -fx-background-color: white; 19 | } 20 | 21 | .vertex { 22 | -fx-stroke-width: 3; 23 | -fx-fill: #008cff; 24 | -fx-view-order:2; 25 | } 26 | .vertex:hover { /* pseudo-classes also work */ 27 | /*-fx-cursor: default; */ /* You can use this style to override the hand/move cursors while hovering. */ 28 | -fx-stroke-width: 2; 29 | -fx-stroke: #61B5F1; 30 | -fx-stroke-type: inside; /* you should keep this if using arrows */ 31 | -fx-fill: #B1DFF7; 32 | } 33 | .vertex-deactivated-first { 34 | -fx-stroke-width: 3; 35 | -fx-fill: #4f5d75; 36 | -fx-view-order:2; 37 | } 38 | 39 | .vertex-deactivated { 40 | -fx-stroke-width: 3; 41 | -fx-fill: gray; 42 | -fx-view-order:2; 43 | } 44 | .vertex-hover { /* pseudo-classes also work */ 45 | /*-fx-cursor: default; */ /* You can use this style to override the hand/move cursors while hovering. */ 46 | -fx-stroke-width: 2; 47 | -fx-stroke: #ff8f4b; 48 | -fx-stroke-type: inside; /* you should keep this if using arrows */ 49 | -fx-fill: #ff8f4b; 50 | } 51 | .vertex-hover:hover { /* pseudo-classes also work */ 52 | /*-fx-cursor: default; */ /* You can use this style to override the hand/move cursors while hovering. */ 53 | -fx-stroke-width: 2; 54 | -fx-stroke: #ff9e00; 55 | -fx-stroke-type: inside; /* you should keep this if using arrows */ 56 | -fx-fill: #ffcb69; 57 | } 58 | 59 | 60 | 61 | .vertex-label { 62 | -fx-font: bold 12pt "sans-serif"; 63 | -fx-text-alignment: center; 64 | -fx-alignment: center; 65 | -fx-translate-y:-27; 66 | -fx-translate-z:-1; 67 | -fx-fill:white; 68 | } 69 | 70 | 71 | .edge-dash { 72 | -fx-stroke-width: 1; 73 | -fx-stroke: gray; 74 | -fx-fill: transparent; /* Importante per mantenere gli archi curvi */ 75 | -fx-stroke-line-cap: round; 76 | -fx-opacity: 0.8; 77 | -fx-stroke-dash-array: 5 5; /* Alterna un tratto di 5 unità e uno spazio di 5 unità */ 78 | } 79 | 80 | .edge-dash-hover { 81 | -fx-stroke-width: 2; 82 | -fx-stroke: gray; 83 | -fx-fill: transparent; /* Importante per mantenere gli archi curvi */ 84 | -fx-stroke-line-cap: round; 85 | -fx-opacity: 0.8; 86 | -fx-stroke-dash-array: 5 5; /* Alterna un tratto di 5 unità e uno spazio di 5 unità */ 87 | } 88 | 89 | .edge { 90 | -fx-stroke-width: 1; 91 | -fx-stroke: gray; 92 | -fx-fill: transparent; /* important to keep for curved edges */ 93 | -fx-stroke-line-cap: round; 94 | -fx-opacity: 0.8; 95 | -fx-stroke-dash-array: 0.1; 96 | 97 | } 98 | 99 | 100 | .edge-hover { 101 | -fx-stroke-width: 2; 102 | -fx-stroke: gray; 103 | -fx-fill: transparent; /* importante per mantenere gli angoli curvi */ 104 | -fx-stroke-line-cap: round; 105 | -fx-opacity: 0.8; 106 | -fx-stroke-dash-array: 0.1; 107 | } 108 | 109 | 110 | 111 | 112 | 113 | .edge-label { 114 | -fx-font: normal 9pt "sans-serif"; 115 | -fx-fill: black; /* Text color */ 116 | -fx-background-color: red; /* Background color */ 117 | -fx-padding: 2px; /* Padding around the text */ 118 | -fx-background-radius: 3px; /* Rounded corners for the background */ 119 | -fx-border-color: black; /* Optional: Add a border around the background */ 120 | -fx-border-width: 1px; /* Border thickness */ 121 | -fx-border-radius: 3px; /* Ensure the border follows the background's rounded corners */ 122 | } 123 | 124 | 125 | 126 | .edge-label-hover { 127 | -fx-font:bold 9pt "sans-serif"; 128 | 129 | } 130 | 131 | /* Since version 2.0.0-rc2 this style is cumulatively applied to arrows, after the "edge" class. 132 | * Use to, e.g., to remove the dash effect (that will not look good in arrows). 133 | * Afterwards, styles applied to the edges are propagated to the respective arrows. You can, however, apply 134 | * specific styles to the arrows programmatically. See example programs. 135 | */ 136 | .arrow { 137 | -fx-opacity: 1; 138 | -fx-fill: gray; 139 | -fx-stroke-width: 1; 140 | -fx-stroke-dash-array: 0.1; 141 | -fx-stroke: gray; 142 | } 143 | .arrow-hover { 144 | -fx-opacity: 1; 145 | -fx-fill: gray; 146 | -fx-stroke-width: 2; 147 | -fx-stroke-dash-array: 0.1; 148 | } 149 | 150 | /* Custom vertex class. If you use node.setStyleClass("myVertex"), any previous styling 151 | * will be overwritten. If you use node.addStyleClass("myVertex"), the styles are applied 152 | * cumulatively; in the later case, any properties not wanted from the default "vertex" class 153 | * must be overwritten. 154 | */ 155 | .myVertex { 156 | -fx-stroke-width: 4; 157 | -fx-stroke: green; 158 | -fx-stroke-type: inside; /* you should keep this if using arrows */ 159 | -fx-fill: yellowgreen; 160 | } 161 | 162 | /* Custom edge class. The same above logic applies to edges. 163 | */ 164 | .myEdge { 165 | -fx-stroke-width: 2; 166 | -fx-stroke: red; 167 | -fx-opacity: 1; 168 | -fx-fill: transparent; /* important to keep for curved edges */ 169 | } 170 | 171 | -------------------------------------------------------------------------------- /src/main/resources/smartgraph.properties: -------------------------------------------------------------------------------- 1 | # Vertex related configurations 2 | # 3 | vertex.allow-user-move = true 4 | vertex.radius = 15 5 | vertex.shape = circle 6 | vertex.tooltip = false 7 | vertex.label = true 8 | 9 | # Edge related configurations 10 | # 11 | edge.tooltip = false 12 | edge.label = true 13 | # only makes sense if displaying an oriented graph 14 | edge.arrow = true 15 | 16 | # (automatic) Force-directed layout related configurations 17 | # 18 | # Notice: deprecated since version 1.1. Force directed layout strategies are now 19 | # instantiated and can be swapped at runtime, per the Strategy design pattern. 20 | # The parameters are passed as arguments or one can use the default ones described 21 | # in the javadoc documentation. 22 | # -- You should experiment with different values for your 23 | # -- particular problem, knowing that not all will achieve 24 | # -- a stable state 25 | layout.repulsive-force = 25000 26 | layout.attraction-force = 30 27 | layout.attraction-scale = 10 -------------------------------------------------------------------------------- /src/main/resources/styles.css: -------------------------------------------------------------------------------- 1 | 2 | .context-menu { 3 | -fx-background-color: transparent; 4 | -fx-border: none; 5 | -fx-padding: 0px; 6 | -fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0), 0, 0, 0, 0); 7 | } 8 | 9 | 10 | 11 | .context-menu .menu-item { 12 | -fx-background-color: transparent; 13 | -fx-padding: 0px; 14 | } 15 | 16 | 17 | .context-menu .menu-item:hover { 18 | -fx-background-color: transparent; 19 | 20 | } 21 | 22 | .context-menu .menu-item:focused { 23 | -fx-background-color: transparent; 24 | } 25 | 26 | 27 | 28 | .slider .track { 29 | -fx-background-color: transparent; 30 | -fx-border-color: transparent; 31 | 32 | } 33 | .slider .thumb { 34 | -fx-background-color: white; 35 | -fx-border-color: #d3d4d5; 36 | -fx-border-width: 1; 37 | -fx-border-radius: 100%; /* Per rendere il pallino circolare */ 38 | -fx-background-radius: 100%; /* Per rendere anche lo sfondo circolare */ 39 | -fx-padding: 10px; /* Imposta un padding per la dimensione */ 40 | -fx-shape: null; /* Assicurati che il thumb sia circolare e non abbia una forma predefinita */ 41 | } 42 | 43 | 44 | --------------------------------------------------------------------------------