├── .gitignore ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── assets ├── digraph_automatic_layout.png ├── digraph_circle_placement.png ├── graph_automatic_layout.png ├── graph_circle_placement.png └── smartgraph_realtime.gif ├── pom.xml ├── repository-smartgraph.png ├── smartgraph.css ├── smartgraph.properties ├── squares.jpg ├── src └── main │ └── java │ ├── com │ └── brunomnsilva │ │ └── smartgraph │ │ ├── Main.java │ │ ├── containers │ │ ├── ContentZoomPane.java │ │ ├── ContentZoomScrollPane.java │ │ └── SmartGraphDemoContainer.java │ │ ├── example │ │ ├── City.java │ │ ├── Distance.java │ │ └── ExampleMain.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 │ │ ├── SmartGraphEdgeNode.java │ │ ├── SmartGraphPanel.java │ │ ├── SmartGraphProperties.java │ │ ├── SmartGraphVertex.java │ │ ├── SmartGraphVertexNode.java │ │ ├── SmartLabel.java │ │ ├── SmartLabelProvider.java │ │ ├── SmartLabelSource.java │ │ ├── SmartLabelledNode.java │ │ ├── SmartPlacementStrategy.java │ │ ├── SmartRadiusProvider.java │ │ ├── SmartRadiusSource.java │ │ ├── SmartRandomPlacementStrategy.java │ │ ├── SmartShapeTypeProvider.java │ │ ├── SmartShapeTypeSource.java │ │ ├── SmartStylableNode.java │ │ ├── SmartStyleProxy.java │ │ ├── UtilitiesBindings.java │ │ ├── UtilitiesJavaFX.java │ │ └── UtilitiesPoint2D.java │ └── module-info.java └── version.properties /.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ IDEA files 2 | .idea 3 | *.iml 4 | *.ipr 5 | *.iws 6 | 7 | # Netbeans IDE files 8 | nbproject/ 9 | releases/ 10 | build.xml 11 | 12 | # Compiled classes 13 | target/ 14 | 15 | # Maven files 16 | pom.xml.tag 17 | pom.xml.releaseBackup 18 | pom.xml.versionsBackup 19 | pom.xml.next 20 | release.properties 21 | dependency-reduced-pom.xml 22 | buildNumber.properties 23 | .mvn/timing.properties 24 | .mvn/wrapper/maven-wrapper.jar 25 | .mvn/wrapper/maven-wrapper.properties 26 | 27 | # Log files 28 | *.log 29 | log/ 30 | logs/ 31 | logging/ 32 | 33 | # IDE-specific files 34 | *.backup.* 35 | *.bak 36 | *.swp 37 | 38 | #Ignore folders 39 | /test/ 40 | /build/ 41 | /dist/ 42 | **/javadoc/ 43 | 44 | # Hanging html files 45 | *.html 46 | 47 | # Compiled class file 48 | *.class 49 | 50 | #Java Network Launch Protocol 51 | *.jnlp 52 | 53 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 54 | hs_err_pid* 55 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Changelog 2 | 3 | - (2.3.0) Notable changes: 4 | 5 | - 👍 Edges only span the distance between vertex boundaries (instead of centers). This yields a nicer visualization when vertices are made transparent. 6 | 7 | - (2.2.0) Notable changes: 8 | 9 | - ⚠️ Bump minimum JDK to 11 and define library as a Java Module; 10 | - 👍 Robust algorithm for vertex spawning; 11 | - Propagation of hover events to labels and arrows (can be styled); 12 | - 🎉 Improve edge readability (issue #43) by changing the label underlying representation to a StackPane. Labels now accept CSS styles for Pane (background) and Text (the label itself). 13 | - ⚠️ Fixed a regression that affected the movement of vertices. 14 | - Other small fixes and improvements. 15 | 16 | - (2.1.0) Notable changes: 17 | 18 | - Improved edge and label rendering; 19 | - Fix arrow "z-order" placement in some situations. 20 | - Fix arrow misalignment on self-loops (issue \#40). 21 | - Fix parallel edge spacing (issue \#40). 22 | - Parallel edge spacing, including self-loops, is kept as compact as possible when inserting/removing, without loosing visual reference. 23 | 24 | - Fix visibility of methods necessary to create custom placement strategies. 25 | 26 | - Improved `ContentZoomScrollPane` with clipping of any overflow. 27 | 28 | - Bring vertex and label to front, while dragging. 29 | 30 | - (2.0.0) 🎉 Minor fixes and stable version release. 31 | 32 | - (2.0.0-rc2) Several minor improvements, including: 33 | 34 | - Example on how to use a background image for a vertex, see issue \#34. 35 | 36 | - Styles applied to edges are propagated to their respective arrows, see issue \#31. 37 | 38 | - (2.0.0-rc1) Shapes, sizes, providers, annotations and minor improvements: 39 | 40 | - Different shapes can be used to represent vertices, namely circles, stars and regular polygons (from triangles to dodecagons); 41 | - The default shape can be specified with the `vertex.shape` property in `smartgraph.properties` 42 | - Can be set/changed at runtime through a `SmartShapeTypeProvider` or `SmartShapeTypeSource` annotation. 43 | 44 | - The radius of the shape (enclosing circle) used to represent a vertex can be set/changed at runtime through a `SmartRadiusProvider` or `SmartRadiusSource` annotation. 45 | 46 | - Updated shapes and radii are only reflected in the visualization after calling `SmartGraphPanel.update()` or `SmartGraphPanel.updateAndWait()`. 47 | 48 | - Improvements: 49 | - When dragging nodes, they will be kept within the panel's bounds. 50 | - The look of curved edges has been improved. 51 | 52 | - (1.1.0) Automatic layout is now performed through an instantiated *strategy*. There are two available (but the pattern allows for the user to devise others): 53 | 54 | - `ForceDirectedSpringSystemLayoutStrategy`: this is the original implementation for the automatic placement, through a spring system; 55 | - `ForceDirectedSpringGravityLayoutStrategy`: (**new**) this is a variant of the spring system implementation, but with a gravity pull towards the center of the panel. This is now the default strategy and has the advantage of not repelling isolated vertices and/or bipartite graphs to the edges of the panel. 56 | 57 | - (1.0.0) Package now available through [Maven Central](https://central.sonatype.com/namespace/com.brunomnsilva). The library seems stable, after dozens of college projects of my students have used it. Hence, the version was bumped to 1.0.0. 58 | 59 | - (0.9.4) You can now annotate a method with `@SmartLabelSource` within a model class to provide the displayed label for a vertex/edge; see the example at `com.brunomnsilva.smartgraph.example`. If no annotation is present, then the `toString()` method is used to obtain the label's text. 60 | 61 | - (0.9.4) You can manually alter a vertex position on the panel at anytime, through `SmartGraphPanel.setVertexPosition(Vertex v)`; see the example at `com.brunomnsilva.smartgraph.example`. 62 | 63 | - (0.9.4) You can override specific default properties by using a *String* parameter to the `SmartGraphProperties` constructor; see the example at `com.brunomnsilva.smartgraph.example`. This is useful if you want to display visually different graphs within the same application. 64 | 65 | - (0.9.4) You can now style labels and arrows individually. -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | JavaFXSmartGraph | Copyright 2019 - 2025 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 | -------------------------------------------------------------------------------- /assets/digraph_automatic_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunomnsilva/JavaFXSmartGraph/1dc06865c4b758fe86f086844d511b2fce5b4365/assets/digraph_automatic_layout.png -------------------------------------------------------------------------------- /assets/digraph_circle_placement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunomnsilva/JavaFXSmartGraph/1dc06865c4b758fe86f086844d511b2fce5b4365/assets/digraph_circle_placement.png -------------------------------------------------------------------------------- /assets/graph_automatic_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunomnsilva/JavaFXSmartGraph/1dc06865c4b758fe86f086844d511b2fce5b4365/assets/graph_automatic_layout.png -------------------------------------------------------------------------------- /assets/graph_circle_placement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunomnsilva/JavaFXSmartGraph/1dc06865c4b758fe86f086844d511b2fce5b4365/assets/graph_circle_placement.png -------------------------------------------------------------------------------- /assets/smartgraph_realtime.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunomnsilva/JavaFXSmartGraph/1dc06865c4b758fe86f086844d511b2fce5b4365/assets/smartgraph_realtime.gif -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 29 | 4.0.0 30 | 31 | com.brunomnsilva 32 | smartgraph 33 | 2.3.0 34 | jar 35 | 36 | ${project.groupId}:${project.artifactId} 37 | A generic (Java FX) graph visualization library that can automatically arrange the vertices' locations through a force-directed algorithm in real-time. 38 | https://github.com/brunomnsilva/JavaFXSmartGraph 39 | 40 | 41 | UTF-8 42 | 11 43 | 11 44 | 45 | 46 | 47 | 48 | org.openjfx 49 | javafx-base 50 | 21.0.1 51 | 52 | 53 | org.openjfx 54 | javafx-graphics 55 | 21.0.1 56 | 57 | 58 | org.openjfx 59 | javafx-controls 60 | 21.0.1 61 | 62 | 63 | 64 | 65 | 66 | MIT License 67 | https://opensource.org/license/mit/ 68 | 69 | 70 | 71 | 72 | 73 | Bruno Silva 74 | brunomnsilva@gmail.com 75 | brunomnsilva 76 | http://www.brunomnsilva.com 77 | 78 | 79 | 80 | 81 | scm:git:git://github.com/brunomnsilva/JavaFXSmartGraph.git 82 | scm:git:ssh://github.com:brunomnsilva/JavaFXSmartGraph.git 83 | http://github.com/brunomnsilva/JavaFXSmartGraph/tree/master 84 | 85 | 86 | 87 | 88 | ossrh 89 | https://s01.oss.sonatype.org/content/repositories/snapshots 90 | 91 | 92 | ossrh 93 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 94 | 95 | 96 | 97 | 98 | 99 | 100 | org.apache.maven.plugins 101 | maven-compiler-plugin 102 | 3.11.0 103 | 104 | 11 105 | 11 106 | 107 | 108 | 109 | org.openjfx 110 | javafx-maven-plugin 111 | 0.0.8 112 | 113 | 114 | 115 | default-cli 116 | 117 | com.brunomnsilva.smartgraph.Main 118 | app 119 | app 120 | app 121 | true 122 | true 123 | true 124 | 125 | 126 | 127 | 128 | example 129 | 130 | com.brunomnsilva.smartgraph.example.ExampleMain 131 | app 132 | app 133 | app 134 | true 135 | true 136 | true 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | org.sonatype.plugins 145 | nexus-staging-maven-plugin 146 | 1.6.13 147 | true 148 | 149 | ossrh 150 | https://s01.oss.sonatype.org/ 151 | true 152 | 153 | 30 154 | 155 | 156 | 157 | 158 | org.apache.maven.plugins 159 | maven-source-plugin 160 | 3.3.0 161 | 162 | 163 | attach-sources 164 | 165 | jar-no-fork 166 | 167 | 168 | 169 | 170 | 171 | org.apache.maven.plugins 172 | maven-javadoc-plugin 173 | 3.6.2 174 | 175 | 176 | attach-javadocs 177 | 178 | jar 179 | 180 | 181 | 182 | 183 | 184 | org.apache.maven.plugins 185 | maven-gpg-plugin 186 | 3.2.2 187 | 188 | 189 | sign-artifacts 190 | verify 191 | 192 | sign 193 | 194 | 195 | 196 | ${gpg.keyname} 197 | ${gpg.keyname} 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /repository-smartgraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunomnsilva/JavaFXSmartGraph/1dc06865c4b758fe86f086844d511b2fce5b4365/repository-smartgraph.png -------------------------------------------------------------------------------- /smartgraph.css: -------------------------------------------------------------------------------- 1 | /* 2 | STYLESHEET FOR SMARTGRAPH 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 is a Pane, which is an extension of Region; 9 | - A vertex is of type Shape; 10 | - The edges are of type CubicLine (Shape); 11 | - The labels are of type Pane (background) + Text (accept both sets of properties); 12 | - The arrows are of type Path. 13 | 14 | This should help you understand which styles you can apply to each type of elements. 15 | */ 16 | 17 | .graph { 18 | -fx-background-color: #F4FFFB; 19 | /* you can use -fx-background-image to set a background image */ 20 | } 21 | 22 | .vertex { 23 | -fx-stroke-width: 3; 24 | -fx-stroke: #61B5F1; 25 | -fx-stroke-type: inside; /* you should keep this if using arrows */ 26 | -fx-fill: #B1DFF7; 27 | } 28 | 29 | .vertex:hover { /* pseudo-classes also work */ 30 | /*-fx-cursor: default; */ /* You can use this style to override the hand/move cursors while hovering. */ 31 | -fx-stroke-width: 4; 32 | } 33 | 34 | .vertex-label { 35 | -fx-font: bold 8pt "sans-serif"; 36 | -fx-padding: 5px; 37 | } 38 | 39 | .vertex-label:hover { 40 | -fx-background-color: white; 41 | -fx-background-radius: 5; 42 | -fx-border-color: black; 43 | } 44 | 45 | .edge { 46 | -fx-stroke-width: 2; 47 | -fx-stroke: #FF6D66; 48 | -fx-stroke-dash-array: 2 5 2 5; /* remove for clean lines */ 49 | -fx-fill: transparent; /* important to keep for curved edges */ 50 | -fx-stroke-line-cap: round; 51 | -fx-opacity: 0.8; 52 | } 53 | 54 | .edge:hover { 55 | -fx-stroke-width: 3; 56 | } 57 | 58 | .edge-label { 59 | -fx-font: normal 5pt "sans-serif"; 60 | -fx-fill: white; 61 | -fx-background-color: red; 62 | -fx-background-radius: 0; 63 | -fx-border-color: black; 64 | -fx-padding: 2px; 65 | } 66 | 67 | .edge-label:hover { 68 | -fx-font: bold 5pt "sans-serif"; 69 | -fx-background-radius: 5; 70 | -fx-border-radius: 5; 71 | } 72 | 73 | /* Since version 2.0.0-rc2 this style is cumulatively applied to arrows, after the "edge" class. 74 | * Use to, e.g., to remove the dash effect (that will not look good in arrows). 75 | * Afterwards, styles applied to the edges are propagated to the respective arrows. You can, however, apply 76 | * specific styles to the arrows programmatically. See example programs. 77 | */ 78 | .arrow { 79 | -fx-stroke-dash-array: none; 80 | } 81 | 82 | /* Custom vertex class. If you use node.setStyleClass("myVertex"), any previous styling 83 | * will be overwritten. If you use node.addStyleClass("myVertex"), the styles are applied 84 | * cumulatively; in the later case, any properties not wanted from the default "vertex" class 85 | * must be overwritten. 86 | */ 87 | .myVertex { 88 | -fx-stroke-width: 4; 89 | -fx-stroke: green; 90 | -fx-stroke-type: inside; /* you should keep this if using arrows */ 91 | -fx-fill: yellowgreen; 92 | -fx-opacity: 0.5; 93 | } 94 | 95 | /* Custom edge class. The same above logic applies to edges. 96 | */ 97 | .myEdge { 98 | -fx-stroke-width: 2; 99 | -fx-stroke: red; 100 | -fx-opacity: 1; 101 | -fx-fill: transparent; /* important to keep for curved edges */ 102 | } -------------------------------------------------------------------------------- /smartgraph.properties: -------------------------------------------------------------------------------- 1 | # 2 | # The MIT License 3 | # 4 | # JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | # 24 | 25 | ################################################################################ 26 | # javafxgraph.properties 27 | # 28 | # These properties can be used to override the default values, which are: 29 | # 30 | # vertex.allow-user-move = true (boolean) 31 | # vertex.radius = 15 (in pixels) 32 | # vertex.shape = circle (string) 33 | # vertex.tooltip = true (boolean) 34 | # vertex.label = true (boolean) 35 | # edge.tooltip = true (boolean) 36 | # edge.label = true (boolean) 37 | # edge.arrow = true (boolean) 38 | # edge.arrowsize = 5 (in pixels) 39 | # 40 | # Allowed/available vertex shape names (string without quotes) are: 41 | # - circle 42 | # - star 43 | # - triangle 44 | # - square 45 | # - pentagon 46 | # - hexagon 47 | # - heptagon 48 | # - octagon 49 | # - nonagon 50 | # - decagon 51 | # - hendecagon 52 | # - dodecagon 53 | # 54 | # ATTENTION: do not leave any trailing spaces after 'true' or 'false' values 55 | ################################################################################ 56 | 57 | # Vertex related configurations 58 | # 59 | vertex.allow-user-move = true 60 | vertex.radius = 20 61 | vertex.shape = circle 62 | vertex.tooltip = true 63 | vertex.label = true 64 | 65 | # Edge related configurations 66 | # 67 | edge.tooltip = true 68 | edge.label = true 69 | # only makes sense if displaying a directed graph 70 | edge.arrow = true 71 | 72 | # size in pixels (side of a triangle); only for directed graphs 73 | edge.arrowsize = 5 74 | 75 | # Notice: deprecated since version 1.1.0 Force directed layout strategies are now 76 | # instantiated and can be swapped at runtime, per the Strategy design pattern. 77 | # The parameters are passed as arguments or one can use the default ones described 78 | # in the javadoc documentation. 79 | # 80 | # Force-directed layout related configurations 81 | # 82 | # -- You should experiment with different values for your 83 | # -- particular problem, knowing that not all will achieve 84 | # -- a stable state 85 | layout.repulsive-force = 25000 86 | layout.attraction-force = 30 87 | layout.attraction-scale = 10 88 | -------------------------------------------------------------------------------- /squares.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunomnsilva/JavaFXSmartGraph/1dc06865c4b758fe86f086844d511b2fce5b4365/squares.jpg -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph; 25 | 26 | import com.brunomnsilva.smartgraph.containers.SmartGraphDemoContainer; 27 | import com.brunomnsilva.smartgraph.graph.*; 28 | import com.brunomnsilva.smartgraph.graphview.*; 29 | import javafx.application.Application; 30 | import javafx.scene.Scene; 31 | import javafx.stage.Stage; 32 | import javafx.stage.StageStyle; 33 | 34 | import java.util.Random; 35 | import java.util.logging.Level; 36 | import java.util.logging.Logger; 37 | 38 | /** 39 | * Class that provides an example of using the library. 40 | * 41 | * @author brunomnsilva 42 | */ 43 | public class Main extends Application { 44 | 45 | private volatile boolean running; 46 | 47 | @Override 48 | public void start(Stage ignored) { 49 | 50 | Graph g = build_sample_digraph(); 51 | //Graph g = build_flower_graph(); 52 | System.out.println(g); 53 | 54 | SmartPlacementStrategy initialPlacement = new SmartCircularSortedPlacementStrategy(); 55 | //SmartPlacementStrategy initialPlacement = new SmartRandomPlacementStrategy(); 56 | 57 | ForceDirectedLayoutStrategy automaticPlacementStrategy = new ForceDirectedSpringGravityLayoutStrategy<>(); 58 | //ForceDirectedLayoutStrategy automaticPlacementStrategy = new ForceDirectedSpringSystemLayoutStrategy<>(); 59 | 60 | SmartGraphPanel graphView = new SmartGraphPanel<>(g, initialPlacement, automaticPlacementStrategy); 61 | 62 | /* 63 | After creating, you can change the styling of some element. 64 | This can be done at any time afterwards. 65 | */ 66 | if (g.numVertices() > 0) { 67 | graphView.getStylableVertex("A").setStyleInline("-fx-fill: gold; -fx-stroke: brown;"); 68 | } 69 | 70 | /* 71 | Basic usage: 72 | Use SmartGraphDemoContainer if you want zoom capabilities and automatic layout toggling 73 | */ 74 | //Scene scene = new Scene(graphView, 1024, 768); 75 | Scene scene = new Scene(new SmartGraphDemoContainer(graphView), 1024, 768); 76 | 77 | Stage stage = new Stage(StageStyle.DECORATED); 78 | stage.setTitle("JavaFX SmartGraph Visualization"); 79 | stage.setMinHeight(500); 80 | stage.setMinWidth(800); 81 | stage.setScene(scene); 82 | stage.show(); 83 | 84 | 85 | // Programmatically define the shape of a vertex. Uncomment to test 86 | /*graphView.setVertexShapeTypeProvider(new SmartShapeTypeProvider() { 87 | @Override 88 | public String valueFor(String vertexElement) { 89 | if(vertexElement.equalsIgnoreCase("V03")) return "triangle"; 90 | return "circle"; 91 | } 92 | });*/ 93 | 94 | /* 95 | IMPORTANT: Must call init() after scene is displayed, so we can have width and height values 96 | to initially place the vertices according to the placement strategy. 97 | */ 98 | graphView.init(); 99 | 100 | /* 101 | Bellow you can see how to attach actions for when vertices and edges are double-clicked 102 | */ 103 | graphView.setVertexDoubleClickAction((SmartGraphVertex graphVertex) -> { 104 | System.out.println("Vertex contains element: " + graphVertex.getUnderlyingVertex().element()); 105 | 106 | //toggle different styling 107 | if( !graphVertex.removeStyleClass("myVertex") ) { 108 | /* for the golden vertex, this is necessary to clear the inline 109 | css class. Otherwise, it has priority. Test and uncomment. */ 110 | //graphVertex.setStyle(null); 111 | 112 | graphVertex.addStyleClass("myVertex"); 113 | } 114 | 115 | //want fun? uncomment below with automatic layout 116 | //g.removeVertex(graphVertex.getUnderlyingVertex()); 117 | //graphView.update(); 118 | }); 119 | 120 | graphView.setEdgeDoubleClickAction(graphEdge -> { 121 | System.out.println("Edge contains element: " + graphEdge.getUnderlyingEdge().element()); 122 | //dynamically change the style when clicked; style propagated to the arrows 123 | graphEdge.setStyleClass("myEdge"); 124 | 125 | // can apply different styling to the arrows programmatically. 126 | // graphEdge.getStylableArrow().setStyleClass("arrow"); 127 | 128 | //uncomment to see edges being removed after click 129 | //Edge underlyingEdge = graphEdge.getUnderlyingEdge(); 130 | //g.removeEdge(underlyingEdge); 131 | //graphView.update(); 132 | }); 133 | 134 | /* 135 | Should proceed with automatic layout or keep original placement? 136 | If using SmartGraphDemoContainer you can toggle this in the UI 137 | */ 138 | //graphView.setAutomaticLayout(true); 139 | 140 | /* 141 | Uncomment lines to test adding of new elements 142 | */ 143 | /*continuously_test_adding_elements(g, graphView); 144 | stage.setOnCloseRequest(event -> { 145 | running = false; 146 | });*/ 147 | } 148 | 149 | /** 150 | * @param args the command line arguments 151 | */ 152 | public static void main(String[] args) { 153 | launch(args); 154 | } 155 | 156 | private Graph build_sample_digraph() { 157 | 158 | Digraph g = new DigraphEdgeList<>(); 159 | 160 | g.insertVertex("A"); 161 | g.insertVertex("B"); 162 | g.insertVertex("C"); 163 | g.insertVertex("D"); 164 | g.insertVertex("E"); 165 | g.insertVertex("F"); 166 | 167 | g.insertEdge("A", "B", "AB"); 168 | g.insertEdge("B", "A", "AB2"); 169 | g.insertEdge("A", "C", "AC"); 170 | g.insertEdge("A", "D", "AD"); 171 | g.insertEdge("B", "C", "BC"); 172 | g.insertEdge("C", "D", "CD"); 173 | g.insertEdge("B", "E", "BE"); 174 | g.insertEdge("F", "D", "DF"); 175 | g.insertEdge("F", "D", "DF2"); 176 | 177 | //yep, its a loop! 178 | g.insertEdge("A", "A", "Loop"); 179 | 180 | return g; 181 | } 182 | 183 | private Graph build_flower_graph() { 184 | 185 | Graph g = new GraphEdgeList<>(); 186 | 187 | g.insertVertex("A"); 188 | g.insertVertex("B"); 189 | g.insertVertex("C"); 190 | g.insertVertex("D"); 191 | g.insertVertex("E"); 192 | g.insertVertex("F"); 193 | g.insertVertex("G"); 194 | 195 | g.insertEdge("A", "B", "1"); 196 | g.insertEdge("A", "C", "2"); 197 | g.insertEdge("A", "D", "3"); 198 | g.insertEdge("A", "E", "4"); 199 | g.insertEdge("A", "F", "5"); 200 | g.insertEdge("A", "G", "6"); 201 | 202 | g.insertVertex("H"); 203 | g.insertVertex("I"); 204 | g.insertVertex("J"); 205 | g.insertVertex("K"); 206 | g.insertVertex("L"); 207 | g.insertVertex("M"); 208 | g.insertVertex("N"); 209 | 210 | g.insertEdge("H", "I", "7"); 211 | g.insertEdge("H", "J", "8"); 212 | g.insertEdge("H", "K", "9"); 213 | g.insertEdge("H", "L", "10"); 214 | g.insertEdge("H", "M", "11"); 215 | g.insertEdge("H", "N", "12"); 216 | 217 | g.insertEdge("A", "H", "0"); 218 | 219 | //g.insertVertex("ISOLATED"); 220 | 221 | return g; 222 | } 223 | 224 | private static final Random random = new Random(/* seed to reproduce*/); 225 | 226 | private void continuously_test_adding_elements(Graph g, SmartGraphPanel graphView) { 227 | //update graph 228 | running = true; 229 | final long ITERATION_WAIT = 3000; //milliseconds 230 | 231 | Runnable r; 232 | r = () -> { 233 | int count = 0; 234 | 235 | try { 236 | Thread.sleep(5000); 237 | } catch (InterruptedException ex) { 238 | Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); 239 | } 240 | 241 | while (running) { 242 | try { 243 | Thread.sleep(ITERATION_WAIT); 244 | } catch (InterruptedException ex) { 245 | Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); 246 | } 247 | 248 | //generate new vertex with 2/3 probability, else connect two 249 | //existing 250 | String id = String.format("%02d", ++count); 251 | if (random.nextInt(3) < 2) { 252 | //add a new vertex connected to a random existing vertex 253 | Vertex existing = get_random_vertex(g); 254 | Vertex vertexId = g.insertVertex(("V" + id)); 255 | g.insertEdge(existing, vertexId, ("E" + id)); 256 | 257 | //this variant must be called to ensure the view has reflected the 258 | //underlying graph before styling a node immediately after. 259 | graphView.updateAndWait(); 260 | 261 | //color new vertices, uncomment 262 | //SmartStylableNode stylableVertex = graphView.getStylableVertex(vertexId); 263 | //if(stylableVertex != null) { 264 | // stylableVertex.setStyleInline("-fx-fill: orange;"); 265 | //} 266 | } else { 267 | Vertex existing1 = get_random_vertex(g); 268 | Vertex existing2 = get_random_vertex(g); 269 | g.insertEdge(existing1, existing2, ("E" + id)); 270 | 271 | graphView.update(); 272 | } 273 | 274 | 275 | } 276 | }; 277 | 278 | new Thread(r).start(); 279 | } 280 | 281 | private static Vertex get_random_vertex(Graph g) { 282 | 283 | int size = g.numVertices(); 284 | int rand = random.nextInt(size); 285 | Vertex existing = null; 286 | int i = 0; 287 | for (Vertex v : g.vertices()) { 288 | existing = v; 289 | if (i++ == rand) { 290 | break; 291 | } 292 | } 293 | return existing; 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/containers/ContentZoomPane.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.containers; 25 | 26 | import javafx.beans.property.DoubleProperty; 27 | import javafx.beans.property.ReadOnlyDoubleWrapper; 28 | import javafx.geometry.Bounds; 29 | import javafx.scene.Cursor; 30 | import javafx.scene.Node; 31 | import javafx.scene.input.MouseEvent; 32 | import javafx.scene.input.ScrollEvent; 33 | import javafx.scene.layout.BorderPane; 34 | import javafx.scene.shape.Rectangle; 35 | 36 | /** 37 | * This class provides zooming and panning for any JavaFX Node. 38 | *
39 | * Reacts to mouse scrolls and right-click mouse dragging (panning). 40 | *
41 | * The content node is out forward in the z-index, so it can react to mouse 42 | * events first. The node should consume any event not meant to propagate to 43 | * this pane. 44 | * 45 | * @deprecated This class will be removed in the final stable 2.0.0 version 46 | * in favor of ContentZoomScrollPane. 47 | * 48 | * @author brunomnsilva 49 | */ 50 | public class ContentZoomPane extends BorderPane { 51 | 52 | /** Minimum scale factor */ 53 | public static final double MIN_SCALE = 1; 54 | /** Maximum scale factor */ 55 | public static final double MAX_SCALE = 5; 56 | /** Scroll delta to apply to scale factor */ 57 | public static final double SCROLL_DELTA = 0.25; 58 | 59 | private final Node content; 60 | private final DoubleProperty scaleFactorProperty; 61 | private final double minScaleFactor, maxScaleFactor, deltaScaleFactor; 62 | 63 | /** 64 | * Creates a new instance of ContentZoomPane. 65 | * @param content pane to zoom and pan. 66 | */ 67 | public ContentZoomPane(Node content) { 68 | this(content, MIN_SCALE, MAX_SCALE, SCROLL_DELTA); 69 | } 70 | 71 | /** 72 | * Creates a new instance of ContentZoomPane. 73 | * @param content pane to zoom and pan. 74 | * @param minScaleFactor minimum scale factor for zoom 75 | * @param maxScaleFactor maximum scale factor for zoom 76 | * @param deltaScaleFactor delta scaling factor applied when zooming with the mouse 77 | */ 78 | public ContentZoomPane(Node content, double minScaleFactor, double maxScaleFactor, double deltaScaleFactor) { 79 | if (content == null) 80 | throw new IllegalArgumentException("Content cannot be null."); 81 | if (minScaleFactor <= 0 || maxScaleFactor <= 0 || deltaScaleFactor <= 0) 82 | throw new IllegalArgumentException("Scale factors must be >= 0."); 83 | if(minScaleFactor >= maxScaleFactor) 84 | throw new IllegalArgumentException("Requirement: minScaleFactor < maxScaleFactor."); 85 | 86 | content.toFront(); 87 | 88 | this.minScaleFactor = minScaleFactor; 89 | this.maxScaleFactor = maxScaleFactor; 90 | this.deltaScaleFactor = deltaScaleFactor; 91 | 92 | this.scaleFactorProperty = new ReadOnlyDoubleWrapper(minScaleFactor); 93 | 94 | setCenter(this.content = content); 95 | enablePanAndZoom(); 96 | enableClipping(); 97 | } 98 | 99 | /** 100 | * Scale (zoom) factor property. Can be bound to control the zoom of the panel. 101 | * @return the scale factor property 102 | */ 103 | public DoubleProperty scaleFactorProperty() { 104 | return scaleFactorProperty; 105 | } 106 | 107 | /** 108 | * Gets the minimum scaling factor allowed for zooming. 109 | * 110 | * @return the minimum scaling factor 111 | */ 112 | public double getMinScaleFactor() { 113 | return minScaleFactor; 114 | } 115 | 116 | /** 117 | * Gets the maximum scaling factor allowed for zooming. 118 | * 119 | * @return the maximum scaling factor 120 | */ 121 | public double getMaxScaleFactor() { 122 | return maxScaleFactor; 123 | } 124 | 125 | /** 126 | * Gets the delta scaling factor applied when zooming with the mouse. 127 | * 128 | * @return the delta scaling factor 129 | */ 130 | public double getDeltaScaleFactor() { 131 | return deltaScaleFactor; 132 | } 133 | 134 | 135 | /** 136 | * Processes mouse scroll-wheel and right-click drags to achieve the intended purposes. 137 | */ 138 | private void enablePanAndZoom() { 139 | 140 | setOnScroll((ScrollEvent event) -> { 141 | 142 | double direction = event.getDeltaY() >= 0 ? 1 : -1; 143 | 144 | double currentScale = scaleFactorProperty.getValue(); 145 | double computedScale = currentScale + direction * deltaScaleFactor; 146 | 147 | scaleContent(event.getX(), event.getY(), computedScale); 148 | 149 | //do not propagate event 150 | event.consume(); 151 | }); 152 | 153 | final DragContext sceneDragContext = new DragContext(); 154 | 155 | setOnMousePressed((MouseEvent event) -> { 156 | 157 | if (event.isSecondaryButtonDown()) { 158 | getScene().setCursor(Cursor.MOVE); 159 | 160 | sceneDragContext.mouseAnchorX = event.getX(); 161 | sceneDragContext.mouseAnchorY = event.getY(); 162 | 163 | sceneDragContext.translateAnchorX = content.getTranslateX(); 164 | sceneDragContext.translateAnchorY = content.getTranslateY(); 165 | 166 | } 167 | 168 | }); 169 | 170 | setOnMouseReleased((MouseEvent event) -> getScene().setCursor(Cursor.DEFAULT)); 171 | 172 | setOnMouseDragged((MouseEvent event) -> { 173 | if (event.isSecondaryButtonDown()) { 174 | 175 | // TranslateAnchorX and translateAnchorY are the current content.translateX/Y values 176 | // when the mouse was pressed 177 | double translateX = sceneDragContext.translateAnchorX + event.getX() - sceneDragContext.mouseAnchorX; 178 | double translateY = sceneDragContext.translateAnchorY + event.getY() - sceneDragContext.mouseAnchorY; 179 | 180 | translateContent(translateX, translateY); 181 | } 182 | }); 183 | } 184 | 185 | private void scaleContent(double pivotX, double pivotY, double scaleFactor) { 186 | double currentScale = content.getScaleX(); 187 | 188 | double computedScale = boundValue(scaleFactor, minScaleFactor, maxScaleFactor); 189 | 190 | if (currentScale != computedScale) { 191 | 192 | if (computedScale == 1) { 193 | 194 | // If scale will be 1, then the content should fit this panel 195 | translateContent(0, 0); 196 | content.setScaleX(1); 197 | content.setScaleY(1); 198 | 199 | } else { 200 | 201 | content.setScaleX(computedScale); 202 | content.setScaleY(computedScale); 203 | 204 | 205 | // This computes the translation needed so the zoom is performed at the mouse positioning location 206 | Bounds bounds = content.getBoundsInParent(); //content.localToScene(content.getBoundsInLocal()); 207 | double f = (computedScale / currentScale) - 1; 208 | 209 | if(bounds.getMinX() > 0) { 210 | pivotX = 0; 211 | } 212 | 213 | double dx = (pivotX - (bounds.getWidth() / 2 + bounds.getMinX())); 214 | double dy = (pivotY - (bounds.getHeight() / 2 + bounds.getMinY())); 215 | 216 | translateContent(content.getTranslateX() - f * dx, content.getTranslateY() - f * dy); 217 | 218 | System.out.println(content.getBoundsInParent()); 219 | } 220 | 221 | scaleFactorProperty.setValue(computedScale); 222 | 223 | } 224 | 225 | } 226 | 227 | private void translateContent(double translateX, double translateY) { 228 | // If the current scale is 1, then the content should fit the panel and no translation should 229 | // be performed/needed. Note that we always scale X and Y together. 230 | if(content.getScaleX() == 1) return; 231 | 232 | content.setTranslateX(translateX); 233 | content.setTranslateY(translateY); 234 | } 235 | 236 | private void enableClipping() { 237 | final Rectangle region = new Rectangle(); 238 | this.widthProperty().addListener((observableValue, oldValue, newValue) -> { 239 | region.setWidth(newValue.doubleValue()); 240 | setClip(region); 241 | }); 242 | 243 | this.heightProperty().addListener((observableValue, oldValue, newValue) -> { 244 | region.setHeight(newValue.doubleValue()); 245 | setClip(region); 246 | }); 247 | } 248 | 249 | /** 250 | * Helper method to keep a value within bounds 251 | * @param value the value to check 252 | * @param min minimum value 253 | * @param max maximum value 254 | * @return a value that is kept within [min, max] 255 | */ 256 | private static double boundValue(double value, double min, double max) { 257 | 258 | if (Double.compare(value, min) < 0) { 259 | return min; 260 | } 261 | 262 | if (Double.compare(value, max) > 0) { 263 | return max; 264 | } 265 | 266 | return value; 267 | } 268 | 269 | /** 270 | * Keeps track of mouse drag action. 271 | */ 272 | private static class DragContext { 273 | double mouseAnchorX; 274 | double mouseAnchorY; 275 | 276 | double translateAnchorX; 277 | double translateAnchorY; 278 | } 279 | 280 | } 281 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/containers/SmartGraphDemoContainer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.containers; 25 | 26 | import com.brunomnsilva.smartgraph.graphview.SmartGraphPanel; 27 | import javafx.geometry.Insets; 28 | import javafx.geometry.Orientation; 29 | import javafx.geometry.Pos; 30 | import javafx.scene.Node; 31 | import javafx.scene.control.*; 32 | import javafx.scene.layout.*; 33 | import javafx.scene.paint.Color; 34 | import javafx.scene.text.Text; 35 | 36 | /** 37 | * A simple container that provides zoom and toggling of automatic layout of a SmartGraphPanel instance. It can 38 | * also force update of the SmartGraphPanel instance for testing purposes. 39 | *
40 | * It shows the current zoom level with a slider control. 41 | * 42 | * @author brunomnsilva 43 | */ 44 | public class SmartGraphDemoContainer extends BorderPane { 45 | 46 | private final ContentZoomScrollPane contentZoomPane; 47 | 48 | /** 49 | * Creates a new instance of SmartGraphDemoContainer pane. 50 | * @param graphView the SmartGraphPanel instance to show and control. 51 | */ 52 | public SmartGraphDemoContainer(SmartGraphPanel graphView) { 53 | if(graphView == null) throw new IllegalArgumentException("View cannot be null."); 54 | 55 | setCenter( this.contentZoomPane = new ContentZoomScrollPane(graphView) ); 56 | Background background = new Background(new BackgroundFill(Color.WHITE, null, null)); 57 | 58 | setRight(createSidebar(this.contentZoomPane, background)); 59 | setBottom(createBottomBar(graphView, background)); 60 | } 61 | 62 | /** 63 | * Create bottom pane with automatic layout toggle, force update button and help. 64 | * @param view the SmartGraphPanel instance to control 65 | * @param bg the background to apply 66 | * @return the bottom pane 67 | */ 68 | private Node createBottomBar(SmartGraphPanel view, Background bg) { 69 | HBox bar = new HBox(20); 70 | bar.setAlignment(Pos.CENTER); 71 | bar.setPadding(new Insets(10)); 72 | bar.setBackground(bg); 73 | 74 | /* Create toggle to control automatic layout */ 75 | CheckBox automatic = new CheckBox("Automatic layout"); 76 | automatic.selectedProperty().bindBidirectional(view.automaticLayoutProperty()); 77 | 78 | /* Create button to force SmartGraphPanel update() */ 79 | Button btUpdate = new Button("Force update"); 80 | btUpdate.setOnAction(actionEvent -> view.update()); 81 | 82 | /* Create help */ 83 | Text helpLabel = new Text("?"); 84 | helpLabel.setStyle("-fx-font-size: 14px; -fx-font-weight: bold;"); 85 | 86 | // Create a Tooltip with the help message 87 | Tooltip tooltip = new Tooltip("Mouse wheel to zoom; left-click to drag and interact with nodes; drag area (any button) for panning."); 88 | 89 | // Attach the Tooltip to the Label 90 | Tooltip.install(helpLabel, tooltip); 91 | 92 | /* Add components */ 93 | bar.getChildren().addAll(automatic, 94 | new Separator(Orientation.VERTICAL), 95 | btUpdate, 96 | new Separator(Orientation.VERTICAL), 97 | helpLabel); 98 | 99 | return bar; 100 | } 101 | 102 | /** 103 | * Creates a sidebar with slider control pane to control the zoom level 104 | * @param zoomPane the ContentZoomPane instance to control 105 | * @param bg the background to apply 106 | * @return the side pane 107 | */ 108 | private Node createSidebar(ContentZoomScrollPane zoomPane, Background bg) { 109 | VBox paneSlider = new VBox(10); 110 | paneSlider.setAlignment(Pos.CENTER); 111 | paneSlider.setPadding(new Insets(10)); 112 | paneSlider.setSpacing(10); 113 | paneSlider.setBackground(bg); 114 | 115 | /* Create slider to control zoom level */ 116 | Slider slider = new Slider(zoomPane.getMinScaleFactor(), 117 | zoomPane.getMaxScaleFactor(), zoomPane.getMinScaleFactor()); 118 | 119 | slider.setOrientation(Orientation.VERTICAL); 120 | slider.setShowTickMarks(true); 121 | slider.setShowTickLabels(true); 122 | slider.setMajorTickUnit(zoomPane.getDeltaScaleFactor()); 123 | slider.setMinorTickCount(1); 124 | slider.setBlockIncrement(0.125f); 125 | slider.setSnapToTicks(true); 126 | 127 | slider.valueProperty().bind(zoomPane.scaleFactorProperty()); 128 | 129 | /* Add components */ 130 | paneSlider.getChildren().addAll(slider, new Text("Zoom")); 131 | 132 | return paneSlider; 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/example/City.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2023-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.example; 26 | 27 | import com.brunomnsilva.smartgraph.graphview.SmartLabelSource; 28 | import com.brunomnsilva.smartgraph.graphview.SmartRadiusSource; 29 | import com.brunomnsilva.smartgraph.graphview.SmartShapeTypeSource; 30 | 31 | import java.util.Objects; 32 | 33 | /** 34 | * A simple class to represent a city in an example usage of the library. 35 | * @author brunomnsilva 36 | */ 37 | public class City { 38 | private String name; 39 | private float population; 40 | 41 | /** 42 | * Constructor for City instances. 43 | * @param name name of the city 44 | * @param population population (in millions) 45 | */ 46 | public City(String name, float population) { 47 | this.name = name; 48 | this.population = population; 49 | } 50 | 51 | /** 52 | * Returns the name of the city. 53 | * @return the name of the city 54 | */ 55 | @SmartLabelSource 56 | public String getName() { 57 | return name; 58 | } 59 | 60 | /** 61 | * Setter for the name of the city. 62 | * @param name the name of the city 63 | */ 64 | public void setName(String name) { 65 | this.name = name; 66 | } 67 | 68 | /** 69 | * Returns the population of the city. 70 | * @return the population of the city 71 | */ 72 | public float getPopulation() { 73 | return population; 74 | } 75 | 76 | /** 77 | * Setter for the population of the city. 78 | * @param population the population of the city 79 | */ 80 | public void setPopulation(float population) { 81 | this.population = population; 82 | } 83 | 84 | @Override 85 | public String toString() { 86 | return "City{" + "name=" + name + ", population=" + population + '}'; 87 | } 88 | 89 | /** 90 | * Establishes the shape of the vertex to use when representing this city. 91 | * @return the name of the shape, see {@link com.brunomnsilva.smartgraph.graphview.ShapeFactory} 92 | */ 93 | @SmartShapeTypeSource 94 | public String modelShape() { 95 | if(Objects.equals(this.name, "Tokyo")) { 96 | return "star"; 97 | } 98 | 99 | return "circle"; 100 | } 101 | 102 | /** 103 | * Returns the radius of the vertex when representing this city. 104 | * @return the radius of the vertex 105 | */ 106 | @SmartRadiusSource 107 | public Double modelRadius() { 108 | return convertToLogScale(Double.parseDouble(String.valueOf(this.population))); 109 | } 110 | 111 | private static double convertToLogScale(double value) { 112 | // Define input range 113 | double minValue = 1; 114 | double maxValue = 40; 115 | 116 | // Define output range 117 | double minOutputValue = 15; 118 | double maxOutputValue = 40; 119 | 120 | // Map the input value to the output range using logarithmic function 121 | double mappedValue = (Math.log(value) - Math.log(minValue)) / (Math.log(maxValue) - Math.log(minValue)); 122 | 123 | // Map the mapped value to the output range 124 | return minOutputValue + mappedValue * (maxOutputValue - minOutputValue); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/example/Distance.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2023-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.example; 26 | 27 | import com.brunomnsilva.smartgraph.graphview.SmartLabelSource; 28 | 29 | /** 30 | * A simple class to represent a distance in an example usage of the library. 31 | * @author brunomnsilva 32 | */ 33 | public class Distance { 34 | private int distance; 35 | 36 | /** 37 | * Constructor for Distance instances. 38 | * @param distance the distance 39 | */ 40 | public Distance(int distance) { 41 | this.distance = distance; 42 | } 43 | 44 | /** 45 | * Returns the distance. 46 | * @return the distance 47 | */ 48 | public int getDistance() { 49 | return distance; 50 | } 51 | 52 | /** 53 | * Setter for the distance. 54 | * @param distance the distance. 55 | */ 56 | public void setDistance(int distance) { 57 | this.distance = distance; 58 | } 59 | 60 | /** 61 | * Establishes the text representation in the graph. 62 | * @return the text representation of the distance 63 | */ 64 | @SmartLabelSource 65 | public String getDisplayDistance() { 66 | /* If the above annotation is not present, the toString() 67 | will be used as the edge label. */ 68 | 69 | return distance + " km"; 70 | } 71 | 72 | @Override 73 | public String toString() { 74 | return "Distance{" + "distance=" + distance + '}'; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/example/ExampleMain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2023-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.example; 26 | 27 | import com.brunomnsilva.smartgraph.containers.SmartGraphDemoContainer; 28 | import com.brunomnsilva.smartgraph.graph.Graph; 29 | import com.brunomnsilva.smartgraph.graph.GraphEdgeList; 30 | import com.brunomnsilva.smartgraph.graph.Vertex; 31 | import com.brunomnsilva.smartgraph.graphview.SmartCircularSortedPlacementStrategy; 32 | import com.brunomnsilva.smartgraph.graphview.SmartGraphPanel; 33 | import com.brunomnsilva.smartgraph.graphview.SmartGraphProperties; 34 | import javafx.application.Application; 35 | import javafx.scene.Scene; 36 | import javafx.stage.Stage; 37 | import javafx.stage.StageStyle; 38 | 39 | /** 40 | * Example program that depicts some world cities and their distances. 41 | * @author brunomnsilva 42 | */ 43 | public class ExampleMain extends Application { 44 | 45 | @Override 46 | public void start(Stage ignored) { 47 | 48 | Graph distances = new GraphEdgeList<>(); 49 | 50 | Vertex prague = distances.insertVertex(new City("Prague", 1.3f)); 51 | Vertex tokyo = distances.insertVertex(new City("Tokyo", 37.5f)); 52 | Vertex beijing = distances.insertVertex(new City("Beijing", 21.5f)); 53 | Vertex newYork = distances.insertVertex(new City("New York", 19.5f)); 54 | Vertex london = distances.insertVertex(new City("London", 14.4f)); 55 | Vertex helsinky = distances.insertVertex(new City("Helsinky", 0.6f)); 56 | 57 | distances.insertEdge(tokyo, newYork, new Distance(10838)); 58 | distances.insertEdge(beijing, newYork, new Distance(11550)); 59 | distances.insertEdge(beijing, tokyo, new Distance(1303)); 60 | distances.insertEdge(london, newYork, new Distance(5567)); 61 | distances.insertEdge(london, prague, new Distance(1264)); 62 | distances.insertEdge(helsinky, tokyo, new Distance(7815)); 63 | distances.insertEdge(prague, helsinky, new Distance(1845)); 64 | distances.insertEdge(beijing, london, new Distance(8132)); 65 | 66 | /* Only Java 15 allows for multi-line strings. */ 67 | String customProps = "edge.label = true" + "\n" + "edge.arrow = true"; 68 | 69 | SmartGraphProperties properties = new SmartGraphProperties(customProps); 70 | 71 | SmartGraphPanel graphView = new SmartGraphPanel<>(distances, properties, new SmartCircularSortedPlacementStrategy()); 72 | 73 | Scene scene = new Scene(new SmartGraphDemoContainer(graphView), 1024, 768); 74 | 75 | Stage stage = new Stage(StageStyle.DECORATED); 76 | stage.setTitle("JavaFX SmartGraph City Distances"); 77 | stage.setMinHeight(500); 78 | stage.setMinWidth(800); 79 | stage.setScene(scene); 80 | stage.show(); 81 | 82 | graphView.init(); 83 | 84 | //graphView.setAutomaticLayout(true); 85 | 86 | /* You can manually place vertices at any time. However, these are 87 | absolute coordinates inside the container panel. 88 | Careful choice of a panel's background image can allow you to overlay 89 | the vertices over a, e.g., world map. 90 | */ 91 | graphView.setVertexPosition(beijing, 100, 100); 92 | graphView.setVertexPosition(helsinky, 924, 100); 93 | graphView.setVertexPosition(london, 200, 668); 94 | graphView.setVertexPosition(prague, 824, 668); 95 | graphView.setVertexPosition(tokyo, 512, 200); 96 | graphView.setVertexPosition(newYork, 512, 400); 97 | 98 | /* 99 | * This illustrates setting an image to the background of a node. 100 | * By default, the css class "vertex" is applied to all vertices. 101 | * Note that all inline styles have 102 | * priority over any properties set in css classes, even if they are applied cumulatively through 103 | * .addStyleClass(class). However, inline styles can be overwritten by using .setStyleInline(css); 104 | * also, when you use .setStyleClass(class), all previous styles will be discarded, including inline. 105 | */ 106 | graphView.getStylableVertex(tokyo).setStyleInline("-fx-fill: url(\"file:squares.jpg\");"); 107 | //graphVertex.setStyleInline("-fx-fill: red;"); //this will overwrite the property later on 108 | 109 | graphView.setVertexDoubleClickAction(graphVertex -> { 110 | graphVertex.setStyleClass("myVertex"); 111 | }); 112 | 113 | } 114 | 115 | /** 116 | * Main program. 117 | * @param args program arguments 118 | */ 119 | public static void main(String[] args) { 120 | launch(args); 121 | } 122 | } -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graph/Digraph.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graph; 26 | 27 | import java.util.Collection; 28 | 29 | /** 30 | * A directed graph (or digraph) is a graph that is made up of a set of vertices 31 | * connected by edges, where the edges have a direction associated with them. 32 | *
33 | * A directed-edge leaves the outbound vertex 34 | * towards the inbound vertex and this changes the reasoning behind some 35 | * methods of the {@link Graph} interface, which are overridden in this interface 36 | * to provide different documentation of expected behavior. 37 | * 38 | * @param Type of element stored at a vertex 39 | * @param Type of element stored at an edge 40 | * 41 | * @see Graph 42 | * @see Edge 43 | * @see Vertex 44 | */ 45 | public interface Digraph extends Graph { 46 | 47 | /** 48 | * Returns a vertex's incident edges as a collection. 49 | *
50 | * Incident edges are all edges that have vertex inbound as the 51 | * inbound vertex, i.e., the edges "entering" vertex inbound. 52 | * If there are no incident edges, e.g., an isolated vertex, 53 | * returns an empty collection. 54 | * 55 | * @param inbound vertex for which to obtain the incident edges 56 | * 57 | * @return collection of edges 58 | */ 59 | @Override 60 | Collection> incidentEdges(Vertex inbound) 61 | throws InvalidVertexException; 62 | 63 | /** 64 | * Returns a vertex's outbound edges as a collection. 65 | *
66 | * Incident edges are all edges that have vertex outbound as the 67 | * outbound vertex, i.e., the edges "leaving" vertex outbound. 68 | * If there are no outbound edges, e.g., an isolated vertex, 69 | * returns an empty collection. 70 | * 71 | * @param outbound vertex for which to obtain the outbound edges 72 | * 73 | * @return collection of edges 74 | * 75 | * @throws InvalidVertexException if outbound is an invalid vertex for the graph 76 | */ 77 | Collection> outboundEdges(Vertex outbound) 78 | throws InvalidVertexException; 79 | 80 | 81 | /** 82 | * Evaluates whether two vertices are adjacent, i.e., there exists some 83 | * directed-edge connecting outbound and inbound. 84 | *
85 | * The existing edge must be directed as outbound --> inbound. 86 | *
87 | * If, for example, there exists only an edge outbound <-- inbound, 88 | * they are not considered adjacent. 89 | * 90 | * @param outbound outbound vertex 91 | * @param inbound inbound vertex 92 | * 93 | * @return true if they are adjacent, false otherwise. 94 | * 95 | * @throws InvalidVertexException if outbound or 96 | * inbound 97 | * are invalid vertices for the graph 98 | */ 99 | @Override 100 | boolean areAdjacent(Vertex outbound, Vertex inbound) 101 | throws InvalidVertexException; 102 | 103 | /** 104 | * Inserts a new edge with a given element between two existing vertices and 105 | * return its (the edge's) reference. 106 | * 107 | * @param outbound outbound vertex 108 | * @param inbound inbound vertex 109 | * @param edgeElement the element to store in the new edge 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 | @Override 124 | Edge insertEdge(Vertex outbound, Vertex inbound, E edgeElement) 125 | throws InvalidVertexException, InvalidEdgeException; 126 | 127 | 128 | /** 129 | * Inserts a new edge with a given element between two existing vertices and 130 | * return its (the edge's) reference. 131 | * 132 | * @param outboundElement outbound vertex's stored element 133 | * @param inboundElement inbound vertex's stored element 134 | * @param edgeElement element to store in the new edge 135 | * 136 | * @return the reference for the newly created edge 137 | * 138 | * @throws InvalidVertexException if outboundElement or 139 | * inboundElement 140 | * are not found in any vertices of the graph 141 | * according to the equality of 142 | * {@link Object#equals(java.lang.Object) } 143 | * method. 144 | * 145 | * @throws InvalidEdgeException if there already exists an edge 146 | * containing edgeElement 147 | * according to the equality of 148 | * {@link Object#equals(java.lang.Object) } 149 | * method. 150 | */ 151 | @Override 152 | Edge insertEdge(V outboundElement, V inboundElement, E edgeElement) 153 | throws InvalidVertexException, InvalidEdgeException; 154 | 155 | 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graph/Edge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graph; 25 | 26 | /** 27 | * An edge connects two {@link Vertex} of type V and stores 28 | * an element of type E. 29 | *
30 | * The edge may be used in oriented and non-oriented graphs. 31 | * 32 | * @param Type of value stored in the edge 33 | * @param Type of value stored in the vertices that this edge connects. 34 | * 35 | * @see Graph 36 | * @see Digraph 37 | */ 38 | public interface Edge { 39 | 40 | /** 41 | * Returns the element stored in the edge. 42 | * 43 | * @return stored element 44 | */ 45 | E element(); 46 | 47 | /** 48 | * Returns and array of size 2, with references for both vertices at the ends 49 | * of an edge. 50 | *
51 | * In a {@link Digraph} the reference at {@code vertices()[0]} must be that 52 | * of the outbound vertex and at {@code vertices()[1]} that of the inbound 53 | * vertex. 54 | * 55 | * @return an array of length 2, containing the vertices at both ends. 56 | */ 57 | Vertex[] vertices(); 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graph/Graph.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graph; 25 | 26 | import java.util.Collection; 27 | 28 | /** 29 | * A graph is made up of a set of vertices connected by edges, where the edges 30 | * have no direction associated with them, i.e., they establish a two-way connection. 31 | * 32 | * @param Type of element stored at a vertex 33 | * @param Type of element stored at an edge 34 | * 35 | * @see Edge 36 | * @see Vertex 37 | */ 38 | public interface Graph { 39 | 40 | /** 41 | * Returns the total number of vertices of the graph. 42 | * 43 | * @return total number of vertices 44 | */ 45 | int numVertices(); 46 | 47 | /** 48 | * Returns the total number of edges of the graph. 49 | * 50 | * @return total number of vertices 51 | */ 52 | int numEdges(); 53 | 54 | /** 55 | * Returns the vertices of the graph as a collection. 56 | *
57 | * If there are no vertices, returns an empty collection. 58 | * 59 | * @return collection of vertices 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 | Collection> edges(); 71 | 72 | /** 73 | * Returns a vertex's incident edges as a collection. 74 | *
75 | * Incident edges are all edges that are connected to vertex v. 76 | * If there are no incident edges, e.g., an isolated vertex, 77 | * returns an empty collection. 78 | * 79 | * @param v vertex for which to obtain the incident edges 80 | * 81 | * @return collection of edges 82 | * 83 | * @throws InvalidVertexException if the vertex is invalid for the graph 84 | */ 85 | Collection> incidentEdges(Vertex v) 86 | throws InvalidVertexException; 87 | 88 | /** 89 | * Given vertex v, return the opposite vertex at the other end 90 | * of edge e. 91 | *
92 | * If both v and e are valid, but e 93 | * is not connected to v, returns null. 94 | * 95 | * @param v vertex on one end of e 96 | * @param e edge connected to v 97 | * @return opposite vertex along e 98 | * 99 | * @throws InvalidVertexException if the vertex is invalid for the graph 100 | * @throws InvalidEdgeException if the edge is invalid for the graph 101 | */ 102 | Vertex opposite(Vertex v, Edge e) 103 | throws InvalidVertexException, InvalidEdgeException; 104 | 105 | /** 106 | * Evaluates whether two vertices are adjacent, i.e., there exists some 107 | * edge connecting u and v. 108 | * 109 | * @param u a vertex 110 | * @param v another vertex 111 | * 112 | * @return true if they are adjacent, false otherwise. 113 | * 114 | * @throws InvalidVertexException if u or v 115 | * are invalid vertices for the graph 116 | */ 117 | boolean areAdjacent(Vertex u, Vertex v) 118 | throws InvalidVertexException; 119 | 120 | /** 121 | * Inserts a new vertex with a given element, returning its reference. 122 | * 123 | * @param vElement the element to store at the vertex 124 | * 125 | * @return the reference of the newly created vertex 126 | * 127 | * @throws InvalidVertexException if there already exists a vertex 128 | * containing vElement 129 | * according to the equality of 130 | * {@link Object#equals(java.lang.Object) } 131 | * method. 132 | * 133 | */ 134 | Vertex insertVertex(V vElement) 135 | throws InvalidVertexException; 136 | 137 | /** 138 | * Inserts a new edge with a given element between two existing vertices and 139 | * return its (the edge's) reference. 140 | * 141 | * @param u a vertex 142 | * @param v another vertex 143 | * @param edgeElement the element to store in the new edge 144 | * 145 | * @return the reference for the newly created edge 146 | * 147 | * @throws InvalidVertexException if u or v 148 | * are invalid vertices for the graph 149 | * 150 | * @throws InvalidEdgeException if there already exists an edge 151 | * containing edgeElement 152 | * according to the equality of 153 | * {@link Object#equals(java.lang.Object) } 154 | * method. 155 | */ 156 | Edge insertEdge(Vertex u, Vertex v, E edgeElement) 157 | throws InvalidVertexException, InvalidEdgeException; 158 | 159 | 160 | /** 161 | * Inserts a new edge with a given element between two existing vertices and 162 | * return its (the edge's) reference. 163 | * 164 | * @param vElement1 a vertex's stored element 165 | * @param vElement2 another vertex's stored element 166 | * @param edgeElement the element to store in the new edge 167 | * 168 | * @return the reference for the newly created edge 169 | * 170 | * @throws InvalidVertexException if vElement1 or 171 | * vElement2 172 | * are not found in any vertices of the graph 173 | * according to the equality of 174 | * {@link Object#equals(java.lang.Object) } 175 | * method. 176 | * 177 | * @throws InvalidEdgeException if there already exists an edge 178 | * containing edgeElement 179 | * according to the equality of 180 | * {@link Object#equals(java.lang.Object) } 181 | * method. 182 | */ 183 | Edge insertEdge(V vElement1, V vElement2, E edgeElement) 184 | throws InvalidVertexException, InvalidEdgeException; 185 | 186 | /** 187 | * Removes a vertex, along with all of its incident edges, and returns the element 188 | * stored at the removed vertex. 189 | * 190 | * @param v vertex to remove 191 | * 192 | * @return element stored at the removed vertex 193 | * 194 | * @throws InvalidVertexException if v is an invalid vertex for the graph 195 | */ 196 | V removeVertex(Vertex v) throws InvalidVertexException; 197 | 198 | /** 199 | * Removes an edge and return its element. 200 | * 201 | * @param e edge to remove 202 | * 203 | * @return element stored at the removed edge 204 | * 205 | * @throws InvalidEdgeException if e is an invalid edge for the graph. 206 | */ 207 | E removeEdge(Edge e) throws InvalidEdgeException; 208 | 209 | /** 210 | * Replaces the element of a given vertex with a new element and returns the 211 | * previous element stored at v. 212 | * 213 | * @param v vertex to replace its element 214 | * @param newElement new element to store in v 215 | * 216 | * @return previous element previously stored in v 217 | * 218 | * @throws InvalidVertexException if the vertex v is invalid for the graph, or; 219 | * if there already exists another vertex containing 220 | * the element newElement 221 | * according to the equality of 222 | * {@link Object#equals(java.lang.Object) } 223 | * method. 224 | */ 225 | V replace(Vertex v, V newElement) throws InvalidVertexException; 226 | 227 | /** 228 | * Replaces the element of a given edge with a new element and returns the 229 | * previous element stored at e. 230 | * 231 | * @param e edge to replace its element 232 | * @param newElement new element to store in e 233 | * 234 | * @return previous element previously stored in e 235 | * 236 | * @throws InvalidEdgeException if the edge e is invalid for the graph, or; 237 | * if there already exists another edge containing 238 | * the element newElement 239 | * according to the equality of 240 | * {@link Object#equals(java.lang.Object)} 241 | * method. 242 | */ 243 | E replace(Edge e, E newElement) throws InvalidEdgeException; 244 | } 245 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graph/GraphEdgeList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graph; 25 | 26 | import java.util.*; 27 | 28 | /** 29 | * ADT Graph implementation that stores a collection of edges (and vertices) and 30 | * where each edge contains the references for the vertices it connects. 31 | *
32 | * Does not allow duplicates of stored elements through equals criteria. 33 | * 34 | * @param Type of element stored at a vertex 35 | * @param Type of element stored at an edge 36 | * 37 | * @author brunomnsilva 38 | */ 39 | public class GraphEdgeList implements Graph { 40 | 41 | /* inner classes are defined at the end of the class, so are the auxiliary methods 42 | */ 43 | private final Map> vertices; 44 | private final Map> edges; 45 | 46 | /** 47 | * Default constructor that initializes an empty graph. 48 | */ 49 | public GraphEdgeList() { 50 | this.vertices = new HashMap<>(); 51 | this.edges = new HashMap<>(); 52 | } 53 | 54 | @Override 55 | public int numVertices() { 56 | return vertices.size(); 57 | } 58 | 59 | @Override 60 | public int numEdges() { 61 | return edges.size(); 62 | } 63 | 64 | @Override 65 | public Collection> vertices() { 66 | return new ArrayList<>(vertices.values()); 67 | } 68 | 69 | @Override 70 | public Collection> edges() { 71 | return new ArrayList<>(edges.values()); 72 | } 73 | 74 | @Override 75 | public Collection> incidentEdges(Vertex v) throws InvalidVertexException { 76 | 77 | checkVertex(v); 78 | 79 | List> incidentEdges = new ArrayList<>(); 80 | for (Edge edge : edges.values()) { 81 | 82 | if (((MyEdge) edge).contains(v)) { 83 | /* edge.vertices()[0] == v || edge.vertices()[1] == v */ 84 | incidentEdges.add(edge); 85 | } 86 | 87 | } 88 | 89 | return incidentEdges; 90 | } 91 | 92 | @Override 93 | public Vertex opposite(Vertex v, Edge e) throws InvalidVertexException, InvalidEdgeException { 94 | checkVertex(v); 95 | MyEdge edge = checkEdge(e); 96 | 97 | if (!edge.contains(v)) { 98 | return null; /* this edge does not connect vertex v */ 99 | } 100 | 101 | if (edge.vertices()[0] == v) { 102 | return edge.vertices()[1]; 103 | } else { 104 | return edge.vertices()[0]; 105 | } 106 | 107 | } 108 | 109 | @Override 110 | public synchronized boolean areAdjacent(Vertex u, Vertex v) throws InvalidVertexException { 111 | //we allow loops, so we do not check if u == v 112 | checkVertex(v); 113 | checkVertex(u); 114 | 115 | /* find and edge that contains both u and v */ 116 | for (Edge edge : edges.values()) { 117 | if (((MyEdge) edge).contains(u) && ((MyEdge) edge).contains(v)) { 118 | return true; 119 | } 120 | } 121 | return false; 122 | } 123 | 124 | @Override 125 | public synchronized Vertex insertVertex(V vElement) throws InvalidVertexException { 126 | if (existsVertexWith(vElement)) { 127 | throw new InvalidVertexException("There's already a vertex with this element."); 128 | } 129 | 130 | MyVertex newVertex = new MyVertex(vElement); 131 | 132 | vertices.put(vElement, newVertex); 133 | 134 | return newVertex; 135 | } 136 | 137 | @Override 138 | public synchronized Edge insertEdge(Vertex u, Vertex v, E edgeElement) 139 | throws InvalidVertexException, InvalidEdgeException { 140 | 141 | if (existsEdgeWith(edgeElement)) { 142 | throw new InvalidEdgeException("There's already an edge with this element."); 143 | } 144 | 145 | MyVertex outVertex = checkVertex(u); 146 | MyVertex inVertex = checkVertex(v); 147 | 148 | MyEdge newEdge = new MyEdge(edgeElement, outVertex, inVertex); 149 | 150 | edges.put(edgeElement, newEdge); 151 | 152 | return newEdge; 153 | 154 | } 155 | 156 | @Override 157 | public synchronized Edge insertEdge(V vElement1, V vElement2, E edgeElement) 158 | throws InvalidVertexException, InvalidEdgeException { 159 | 160 | if (existsEdgeWith(edgeElement)) { 161 | throw new InvalidEdgeException("There's already an edge with this element."); 162 | } 163 | 164 | if (!existsVertexWith(vElement1)) { 165 | throw new InvalidVertexException("No vertex contains " + vElement1); 166 | } 167 | if (!existsVertexWith(vElement2)) { 168 | throw new InvalidVertexException("No vertex contains " + vElement2); 169 | } 170 | 171 | MyVertex outVertex = vertexOf(vElement1); 172 | MyVertex inVertex = vertexOf(vElement2); 173 | 174 | MyEdge newEdge = new MyEdge(edgeElement, outVertex, inVertex); 175 | 176 | edges.put(edgeElement, newEdge); 177 | 178 | return newEdge; 179 | 180 | } 181 | 182 | @Override 183 | public synchronized V removeVertex(Vertex v) throws InvalidVertexException { 184 | checkVertex(v); 185 | 186 | V element = v.element(); 187 | 188 | //remove incident edges 189 | Iterable> incidentEdges = incidentEdges(v); 190 | for (Edge edge : incidentEdges) { 191 | edges.remove(edge.element()); 192 | } 193 | 194 | vertices.remove(v.element()); 195 | 196 | return element; 197 | } 198 | 199 | @Override 200 | public synchronized E removeEdge(Edge e) throws InvalidEdgeException { 201 | checkEdge(e); 202 | 203 | E element = e.element(); 204 | edges.remove(e.element()); 205 | 206 | return element; 207 | } 208 | 209 | @Override 210 | public V replace(Vertex v, V newElement) throws InvalidVertexException { 211 | if (existsVertexWith(newElement)) { 212 | throw new InvalidVertexException("There's already a vertex with this element."); 213 | } 214 | 215 | MyVertex vertex = checkVertex(v); 216 | 217 | V oldElement = vertex.element; 218 | vertex.element = newElement; 219 | 220 | return oldElement; 221 | } 222 | 223 | @Override 224 | public E replace(Edge e, E newElement) throws InvalidEdgeException { 225 | if (existsEdgeWith(newElement)) { 226 | throw new InvalidEdgeException("There's already an edge with this element."); 227 | } 228 | 229 | MyEdge edge = checkEdge(e); 230 | 231 | E oldElement = edge.element; 232 | edge.element = newElement; 233 | 234 | return oldElement; 235 | } 236 | 237 | private MyVertex vertexOf(V vElement) { 238 | for (Vertex v : vertices.values()) { 239 | if (v.element().equals(vElement)) { 240 | return (MyVertex) v; 241 | } 242 | } 243 | return null; 244 | } 245 | 246 | private boolean existsVertexWith(V vElement) { 247 | return vertices.containsKey(vElement); 248 | } 249 | 250 | private boolean existsEdgeWith(E edgeElement) { 251 | return edges.containsKey(edgeElement); 252 | } 253 | 254 | @Override 255 | public String toString() { 256 | StringBuilder sb = new StringBuilder( 257 | String.format("Graph with %d vertices and %d edges:\n", numVertices(), numEdges()) 258 | ); 259 | 260 | sb.append("--- Vertices: \n"); 261 | for (Vertex v : vertices.values()) { 262 | sb.append("\t").append(v.toString()).append("\n"); 263 | } 264 | sb.append("\n--- Edges: \n"); 265 | for (Edge e : edges.values()) { 266 | sb.append("\t").append(e.toString()).append("\n"); 267 | } 268 | return sb.toString(); 269 | } 270 | 271 | class MyVertex implements Vertex { 272 | 273 | V element; 274 | 275 | public MyVertex(V element) { 276 | this.element = element; 277 | } 278 | 279 | @Override 280 | public V element() { 281 | return this.element; 282 | } 283 | 284 | @Override 285 | public String toString() { 286 | return "Vertex{" + element + '}'; 287 | } 288 | } 289 | 290 | class MyEdge implements Edge { 291 | 292 | E element; 293 | Vertex vertexOutbound; 294 | Vertex vertexInbound; 295 | 296 | public MyEdge(E element, Vertex vertexOutbound, Vertex vertexInbound) { 297 | this.element = element; 298 | this.vertexOutbound = vertexOutbound; 299 | this.vertexInbound = vertexInbound; 300 | } 301 | 302 | @Override 303 | public E element() { 304 | return this.element; 305 | } 306 | 307 | public boolean contains(Vertex v) { 308 | return (vertexOutbound == v || vertexInbound == v); 309 | } 310 | 311 | @Override 312 | public Vertex[] vertices() { 313 | Vertex[] vertices = new Vertex[2]; 314 | vertices[0] = vertexOutbound; 315 | vertices[1] = vertexInbound; 316 | 317 | return vertices; 318 | } 319 | 320 | @Override 321 | public String toString() { 322 | return "Edge{{" + element + "}, vertexOutbound=" + vertexOutbound.toString() 323 | + ", vertexInbound=" + vertexInbound.toString() + '}'; 324 | } 325 | } 326 | 327 | /** 328 | * Checks whether a given vertex is valid (i.e., not null) and belongs to this graph 329 | * 330 | * @param v vertex to check 331 | * @return the reference of the vertex 332 | * @throws InvalidVertexException if the vertex is invalid 333 | */ 334 | private MyVertex checkVertex(Vertex v) throws InvalidVertexException { 335 | if(v == null) throw new InvalidVertexException("Null vertex."); 336 | 337 | MyVertex vertex; 338 | try { 339 | vertex = (MyVertex) v; 340 | } catch (ClassCastException e) { 341 | throw new InvalidVertexException("Not a vertex."); 342 | } 343 | 344 | if (!vertices.containsKey(vertex.element)) { 345 | throw new InvalidVertexException("Vertex does not belong to this graph."); 346 | } 347 | 348 | return vertex; 349 | } 350 | 351 | private MyEdge checkEdge(Edge e) throws InvalidEdgeException { 352 | if(e == null) throw new InvalidEdgeException("Null edge."); 353 | 354 | MyEdge edge; 355 | try { 356 | edge = (MyEdge) e; 357 | } catch (ClassCastException ex) { 358 | throw new InvalidVertexException("Not an adge."); 359 | } 360 | 361 | if (!edges.containsKey(edge.element)) { 362 | throw new InvalidEdgeException("Edge does not belong to this graph."); 363 | } 364 | 365 | return edge; 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graph/InvalidEdgeException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graph; 25 | 26 | /** 27 | * Thrown when using an invalid edge in calls of methods in {@link Graph} 28 | * and {@link Digraph} implementations. 29 | * 30 | * @see Graph 31 | * @see Digraph 32 | */ 33 | public class InvalidEdgeException extends RuntimeException { 34 | 35 | /** 36 | * Constructs a new InvalidEdgeException with a default error message. 37 | */ 38 | public InvalidEdgeException() { 39 | super("The edge is invalid or does not belong to this graph."); 40 | } 41 | 42 | /** 43 | * Constructs a new InvalidEdgeException with the specified error message. 44 | * 45 | * @param message the error message to display 46 | */ 47 | public InvalidEdgeException(String message) { 48 | super(message); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graph/InvalidVertexException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graph; 25 | 26 | /** 27 | * Thrown when using an invalid vertex in calls of methods in {@link Graph} 28 | * and {@link Digraph} implementations. 29 | * 30 | * @see Graph 31 | * @see Digraph 32 | */ 33 | public class InvalidVertexException extends RuntimeException { 34 | 35 | /** 36 | * Constructs a new InvalidVertexException with a default error message. 37 | */ 38 | public InvalidVertexException() { 39 | super("The vertex is invalid or does not belong to this graph."); 40 | } 41 | 42 | /** 43 | * Constructs a new InvalidVertexException with the specified error message. 44 | * 45 | * @param message the error message to display 46 | */ 47 | public InvalidVertexException(String message) { 48 | super(message); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graph/Vertex.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graph; 25 | 26 | /** 27 | * A vertex contains an element of type V and is used both in 28 | * graphs and digraphs. 29 | * 30 | * @param Type of value stored in the vertex. 31 | * 32 | * @see Graph 33 | * @see Digraph 34 | */ 35 | public interface Vertex { 36 | 37 | /** 38 | * Returns the element stored in the vertex. 39 | * 40 | * @return stored element 41 | */ 42 | V element(); 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/Args.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | /** 28 | * The Args class provides a collection of static methods for checking method parameters and throwing 29 | * IllegalArgumentExceptions if the parameters do not meet the specified requirements. 30 | * 31 | * @author brunomnsilva 32 | */ 33 | public final class Args { 34 | 35 | /** 36 | * Checks if the specified parameter is null and throws an IllegalArgumentException if it is. 37 | * 38 | * @param param the parameter to check for null 39 | * @param name the name of the parameter being checked 40 | * @throws IllegalArgumentException if the specified parameter is null 41 | */ 42 | public static void requireNotNull(Object param, String name) { 43 | if (param == null) { 44 | throw new IllegalArgumentException(String.format("Require '%s' to be not null.", name)); 45 | } 46 | } 47 | 48 | /** 49 | * Checks if a specified double value is greater than a specified minimum value and throws an IllegalArgumentException 50 | * if it is not. Uses a scaled comparison to avoid floating-point precision errors. 51 | * 52 | * @param value the double value to check 53 | * @param name the name of the double value being checked 54 | * @param minValue the minimum value that the specified double value must be greater than 55 | * @throws IllegalArgumentException if the specified double value is less than or equal to the specified minimum value 56 | */ 57 | public static void requireGreaterThan(double value, String name, double minValue) { 58 | if(value <= minValue) { 59 | throw new IllegalArgumentException(String.format("Require '%s' (%f) to be greater than %f.", name, value, minValue)); 60 | } 61 | } 62 | 63 | /** 64 | * Checks if a specified double value is non-negative and throws an IllegalArgumentException if it is not. 65 | * 66 | * @param value the double value to check 67 | * @param name the name of the double value being checked 68 | * @throws IllegalArgumentException if the specified double value is negative 69 | */ 70 | public static void requireNonNegative(double value, String name) { 71 | if (value < 0.0) { 72 | throw new IllegalArgumentException(String.format("Require '%s' (%f) to be non-negative.", name, value)); 73 | } 74 | } 75 | 76 | /** 77 | * This method checks if a value falls within a specified range. 78 | * If the value is less than the lower bound or greater than the upper bound, an IllegalArgumentException is thrown. 79 | * @param value the value to check 80 | * @param name the name of the value being checked 81 | * @param lowerBound the lower bound of the range (inclusive) 82 | * @param upperBound the upper bound of the range (inclusive) 83 | * @throws IllegalArgumentException if the value is outside the specified range 84 | */ 85 | public static void requireInRange(double value, String name, double lowerBound, double upperBound) { 86 | if (value < lowerBound || value > upperBound) { 87 | throw new IllegalArgumentException(String.format("Require '%s' (%f) to be in range [%f, %f].", name, value, lowerBound, upperBound)); 88 | } 89 | } 90 | 91 | /** 92 | * Checks if the given double value is finite (i.e., not NaN or infinite). 93 | * @param value the double value to check for finiteness 94 | * @param name the name of the value being checked 95 | * @throws IllegalArgumentException if the value is not finite 96 | */ 97 | public static void requireFinite(double value, String name) { 98 | if (!Double.isFinite(value)) { 99 | throw new IllegalArgumentException(String.format("Require '%s' (%f) to be finite.", name, value)); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/ForceDirectedLayoutStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | import javafx.geometry.Point2D; 28 | 29 | import java.util.Collection; 30 | 31 | /** 32 | * A representation of a force directed layout "strategy" used during automatic layout of nodes in a {@link SmartGraphPanel}. 33 | *
34 | * Implementing classes should compute attractive and repulsive forces according to some algorithm. 35 | * Typically, if two graph nodes are not adjacent the force should be dominated by the repulsive force. 36 | *
37 | * See: Wikipedia - Force-directed graph drawing 38 | * 39 | * @param The generic type of {@link SmartGraphVertexNode}, i.e., the nodes of a {@link SmartGraphPanel}. 40 | */ 41 | public abstract class ForceDirectedLayoutStrategy { 42 | 43 | /** 44 | * This method must compute forces between all graph nodes. Typically, repelling forces exist between all nodes (similarly to particles 45 | * with the same polarity), but attractive forces only exist between adjacent nodes (nodes that are connected). 46 | *
47 | * The default behavior is to iterate over all distinct pairs of nodes and compute 48 | * their combined forces (attractive and repulsive), by calling {@link #computeForceBetween(SmartGraphVertexNode, SmartGraphVertexNode, double, double)}. 49 | *
50 | * Other strategies that rely on some link of global metrics should override this method. 51 | * 52 | * @param nodes the current nodes of the graph 53 | * @param panelWidth the graph panel's width 54 | * @param panelHeight the graph panel's height 55 | */ 56 | public void computeForces(Collection> nodes, double panelWidth, double panelHeight) { 57 | for (SmartGraphVertexNode v : nodes) { 58 | for (SmartGraphVertexNode w : nodes) { 59 | if(v == w) continue; 60 | 61 | Point2D force = computeForceBetween(v, w, panelWidth, panelHeight); 62 | v.addForceVector(force.getX(), force.getY()); 63 | } 64 | } 65 | } 66 | 67 | /** 68 | * Computes a force vector between two nodes. The force vector is the result of the attractive and repulsive force between the two. 69 | * 70 | * @param v a node 71 | * @param w another node 72 | * @param panelWidth the graph panel's width 73 | * @param panelHeight the graph panel's height 74 | * @return the force vector 75 | */ 76 | protected abstract Point2D computeForceBetween(SmartGraphVertexNode v, SmartGraphVertexNode w, double panelWidth, double panelHeight); 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/ForceDirectedSpringGravityLayoutStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | import javafx.geometry.Point2D; 28 | 29 | import java.util.Collection; 30 | 31 | /** 32 | * An implementation of a spring system layout strategy with gravity towards the center. 33 | *
34 | * Applies the same spring system as {@link ForceDirectedSpringSystemLayoutStrategy} but with added gravitational pull 35 | * towards the center of the panel. Even with bipartite graphs, they will not repel each other to the edges of the panel. 36 | *
37 | * Parameters: 38 | *
39 | * Repulsive force (> 0): Recommended [1, 50]. Default 25. The strength of the repulsive force between nodes. 40 | * Higher values result in greater repulsion. 41 | *
42 | * Attraction force (> 0): Recommended [1, 5]. Default 3. The strength of the attractive force between connected nodes. 43 | * Higher values result in stronger attraction. Careful, because larger values may not produce stable states. 44 | *
45 | * Attraction scale (> 0): Recommended [1, ?]. Default 10. The scale factor for attraction. 46 | * It determines the effectiveness of the attraction force based on the distance between connected nodes. 47 | *
48 | * Acceleration: Mandatory ]0, 1]. Default 0.8. The acceleration factor applied to node movements. 49 | * Higher values result in faster movements. 50 | *
51 | * Gravity (> 0): Recommended ]0, 0.1]. Default 0.01. The higher the value, the more dominant the gravitation "pull" is. 52 | * Careful, because larger values may not produce stable states. The gravity force is applied after 53 | * the attraction/repulsive forces between nodes are computed. We don't want this force to dominate the layout placement. 54 | * 55 | * @param The generic type of {@link SmartGraphVertexNode}, i.e., the nodes of a {@link SmartGraphPanel}. 56 | */ 57 | public class ForceDirectedSpringGravityLayoutStrategy extends ForceDirectedSpringSystemLayoutStrategy { 58 | 59 | private final double gravity; 60 | 61 | /** 62 | * Constructs a new instance of ForceDirectedSpringGravityLayoutStrategy with default parameters, namely: 63 | *
64 | * repulsiveForce = 25, attractionForce = 3, attractionScale = 10, acceleration = 0.8 and gravity = 0.01. 65 | */ 66 | public ForceDirectedSpringGravityLayoutStrategy() { 67 | super(); 68 | this.gravity = 0.01; 69 | } 70 | 71 | /** 72 | * Constructs a new instance of ForceDirectedSpringGravityLayoutStrategy with the specified parameters. 73 | * 74 | * @param repulsiveForce The strength of the repulsive force between nodes. Higher values result in greater repulsion. 75 | * @param attractionForce The strength of the attractive force between connected nodes. Higher values result in stronger attraction. 76 | * @param attractionScale The scale factor for attraction. It determines the effectiveness of the attraction force based on the distance between connected nodes. 77 | * @param acceleration The acceleration factor applied to node movements. Higher values result in faster movements. 78 | * @param gravity The strength of the gravity force applied to all nodes, attracting them towards the center of the layout area. 79 | */ 80 | public ForceDirectedSpringGravityLayoutStrategy(double repulsiveForce, double attractionForce, double attractionScale, 81 | double acceleration, double gravity) { 82 | super(repulsiveForce, attractionForce, attractionScale, acceleration); 83 | 84 | Args.requireGreaterThan(gravity, "gravity", 0); 85 | Args.requireInRange(gravity, "gravity", 0, 1); 86 | this.gravity = gravity; 87 | } 88 | 89 | @Override 90 | public void computeForces(Collection> nodes, double panelWidth, double panelHeight) { 91 | // Attractive and repulsive forces 92 | for (SmartGraphVertexNode v : nodes) { 93 | for (SmartGraphVertexNode w : nodes) { 94 | if(v == w) continue; 95 | 96 | Point2D force = computeForceBetween(v, w, panelWidth, panelHeight); 97 | v.addForceVector(force.getX(), force.getY()); 98 | } 99 | } 100 | 101 | // Gravitational pull towards the center for all nodes 102 | double centerX = panelWidth / 2; 103 | double centerY = panelHeight / 2; 104 | 105 | for (SmartGraphVertexNode v : nodes) { 106 | Point2D curPosition = v.getUpdatedPosition(); 107 | Point2D forceCenter = new Point2D(centerX - curPosition.getX(), centerY - curPosition.getY()) 108 | .multiply(gravity); 109 | 110 | v.addForceVector(forceCenter.getX(), forceCenter.getY()); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/ForceDirectedSpringSystemLayoutStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | import javafx.geometry.Point2D; 28 | 29 | /** 30 | * An implementation of a spring system layout strategy. This strategy allows to freely move the graph along 31 | * the panel, but if you have a bipartite graph, the sub-graphs will repel each other to the edges of the panel. 32 | *
33 | * Parameters: 34 | *
35 | * Repulsive force (> 0): Recommended [1, 50]. Default 25. The strength of the repulsive force between nodes. 36 | * Higher values result in greater repulsion. 37 | *
38 | * Attraction force (> 0): Recommended [1, 5]. Default 3. The strength of the attractive force between connected nodes. 39 | * Higher values result in stronger attraction. Careful, because larger values may not produce stable states. 40 | *
41 | * Attraction scale (> 0): Recommended [1, ?]. Default 10. The scale factor for attraction. 42 | * It determines the effectiveness of the attraction force based on the distance between connected nodes. 43 | *
44 | * Acceleration: Mandatory ]0, 1]. Default 0.8. The acceleration factor applied to node movements. 45 | * Higher values result in faster movements. 46 | * 47 | * @param The generic type of {@link SmartGraphVertexNode}, i.e., the nodes of a {@link SmartGraphPanel}. 48 | */ 49 | public class ForceDirectedSpringSystemLayoutStrategy extends ForceDirectedLayoutStrategy { 50 | 51 | private final double repulsiveForce; 52 | private final double attractionForce; 53 | private final double attractionScale; 54 | private final double acceleration; 55 | 56 | /* just a scaling factor so all parameters are, at most, two-digit numbers. */ 57 | private static final double A_THOUSAND = 1000; 58 | 59 | /** 60 | * Constructs a new instance of ForceDirectedSpringGravityLayoutStrategy with default parameters, namely: 61 | *
62 | * repulsiveForce = 25, attractionForce = 3, attractionScale = 10 and acceleration = 0.8. 63 | */ 64 | public ForceDirectedSpringSystemLayoutStrategy() { 65 | this.repulsiveForce = 25; 66 | this.attractionForce = 3; 67 | this.attractionScale = 10; 68 | this.acceleration = 0.8; 69 | } 70 | 71 | /** 72 | * Constructs a new instance of ForceDirectedSpringGravityLayoutStrategy with the specified parameters. 73 | * 74 | * @param repulsiveForce The strength of the repulsive force between nodes. Higher values result in greater repulsion. 75 | * @param attractionForce The strength of the attractive force between connected nodes. Higher values result in stronger attraction. 76 | * @param attractionScale The scale factor for attraction. It determines the effectiveness of the attraction force based on the distance between connected nodes. 77 | * @param acceleration The acceleration factor applied to node movements. Higher values result in faster movements. 78 | */ 79 | public ForceDirectedSpringSystemLayoutStrategy(double repulsiveForce, double attractionForce, double attractionScale, double acceleration) { 80 | Args.requireGreaterThan(repulsiveForce, "repulsiveForce", 0); 81 | Args.requireGreaterThan(attractionForce, "attractionForce", 0); 82 | Args.requireGreaterThan(attractionScale, "attractionScale", 0); 83 | Args.requireGreaterThan(acceleration, "acceleration", 0); 84 | Args.requireInRange(acceleration, "acceleration", 0, 1); 85 | 86 | this.repulsiveForce = repulsiveForce; 87 | this.attractionForce = attractionForce; 88 | this.attractionScale = attractionScale; 89 | this.acceleration = acceleration; 90 | } 91 | 92 | @Override 93 | protected Point2D computeForceBetween(SmartGraphVertexNode v, SmartGraphVertexNode w, double panelWidth, double panelHeight) { 94 | // The panel's width and height are not used in this strategy 95 | // This allows to freely move the graph to a particular region in the panel; 96 | // On the other hand, e.g., in a bipartite graph the two sub-graphs will repel each other to the edges of the panel 97 | 98 | Point2D vPosition = v.getUpdatedPosition(); 99 | Point2D wPosition = w.getUpdatedPosition(); 100 | double distance = vPosition.distance(wPosition) - (v.getRadius() + w.getRadius()); 101 | Point2D forceDirection = wPosition.subtract(vPosition).normalize(); 102 | 103 | if (distance < 1) { 104 | distance = 1; 105 | } 106 | 107 | // attractive force 108 | Point2D attraction; 109 | if(v.isAdjacentTo(w)) { 110 | double attraction_factor = attractionForce * Math.log(distance / attractionScale); 111 | attraction = forceDirection.multiply(attraction_factor); 112 | } else { 113 | attraction = new Point2D(0,0); 114 | } 115 | 116 | // repelling force 117 | double repulsive_factor = repulsiveForce * A_THOUSAND / (distance * distance); 118 | Point2D repulsion = forceDirection.multiply(-repulsive_factor); 119 | 120 | // combine forces 121 | Point2D totalForce = new Point2D(attraction.getX() + repulsion.getX(), 122 | attraction.getY() + repulsion.getY()); 123 | 124 | return totalForce.multiply(acceleration); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/ShapeCircle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | import javafx.beans.property.DoubleProperty; 28 | import javafx.scene.shape.Circle; 29 | import javafx.scene.shape.Shape; 30 | 31 | /** 32 | * This class represents a circle shape with a specified radius. 33 | * 34 | * @author brunomnsilva 35 | */ 36 | public class ShapeCircle implements ShapeWithRadius { 37 | 38 | private final Circle surrogate; 39 | 40 | /** 41 | * Creates a circle shape. 42 | * @param x the x-center coordinate 43 | * @param y the y-center coordinate 44 | * @param radius the radius of the circle 45 | */ 46 | public ShapeCircle(double x, double y, double radius) { 47 | Args.requireNonNegative(x, "x"); 48 | Args.requireNonNegative(y, "y"); 49 | Args.requireNonNegative(radius, "radius"); 50 | 51 | this.surrogate = new Circle(x, y, radius); 52 | } 53 | 54 | @Override 55 | public Shape getShape() { 56 | return surrogate; 57 | } 58 | 59 | @Override 60 | public DoubleProperty centerXProperty() { 61 | return surrogate.centerXProperty(); 62 | } 63 | 64 | @Override 65 | public DoubleProperty centerYProperty() { 66 | return surrogate.centerYProperty(); 67 | } 68 | 69 | @Override 70 | public DoubleProperty radiusProperty() { 71 | return surrogate.radiusProperty(); 72 | } 73 | 74 | @Override 75 | public double getRadius() { 76 | return surrogate.getRadius(); 77 | } 78 | 79 | @Override 80 | public void setRadius(double radius) { 81 | Args.requireNonNegative(radius, "radius"); 82 | 83 | // Only update if different 84 | if(Double.compare(this.getRadius(), radius) != 0) { 85 | surrogate.setRadius(radius); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/ShapeFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | /** 28 | * A factory class for creating instances of shapes with a specified center coordinates and radius. 29 | * 30 | * @author brunomnsilva 31 | */ 32 | public class ShapeFactory { 33 | 34 | /** 35 | * Creates a new instance of a shape with the specified type, center coordinates, and radius. 36 | * 37 | * @param type The type of shape to create. Supported types are "star", "circle", "triangle", 38 | * "square", "pentagon", "hexagon", "heptagon", "octagon", "nonagon", "decagon", 39 | * "hendecagon", and "dodecagon". 40 | * @param x The center X coordinate of the shape. 41 | * @param y The center Y coordinate of the shape. 42 | * @param radius The radius of the shape. 43 | * @return An instance of a shape with the specified parameters. 44 | * @throws IllegalArgumentException If the provided type is not recognized or if the center coordinates 45 | * or radius are negative. 46 | */ 47 | public static ShapeWithRadius create(String type, double x, double y, double radius) { 48 | Args.requireNonNegative(x, "x"); 49 | Args.requireNonNegative(y, "y"); 50 | Args.requireNonNegative(radius, "radius"); 51 | 52 | type = type.trim().toLowerCase(); 53 | 54 | switch(type) { 55 | case "star": return new ShapeStar(x, y, radius); 56 | case "circle": return new ShapeCircle(x, y, radius); 57 | case "triangle": return new ShapeRegularPolygon(x, y, radius, 3); 58 | case "square": return new ShapeRegularPolygon(x, y, radius, 4); 59 | case "pentagon": return new ShapeRegularPolygon(x, y, radius, 5); 60 | case "hexagon": return new ShapeRegularPolygon(x, y, radius, 6); 61 | case "heptagon": return new ShapeRegularPolygon(x, y, radius, 7); 62 | case "octagon": return new ShapeRegularPolygon(x, y, radius, 8); 63 | case "nonagon": return new ShapeRegularPolygon(x, y, radius, 9); 64 | case "decagon": return new ShapeRegularPolygon(x, y, radius, 10); 65 | case "hendecagon": return new ShapeRegularPolygon(x, y, radius, 11); 66 | case "dodecagon": return new ShapeRegularPolygon(x, y, radius, 12); 67 | 68 | default: throw new IllegalArgumentException("Invalid shape type. See javadoc for available shapes."); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/ShapeRegularPolygon.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | import javafx.beans.property.DoubleProperty; 28 | import javafx.beans.property.SimpleDoubleProperty; 29 | import javafx.scene.shape.Polygon; 30 | import javafx.scene.shape.Shape; 31 | 32 | /** 33 | * This class represents a regular polygon shape with a specified number of sides and radius. 34 | * 35 | * @author brunomnsilva 36 | */ 37 | public class ShapeRegularPolygon implements ShapeWithRadius { 38 | 39 | protected final int numberSides; 40 | protected final DoubleProperty centerX, centerY; 41 | protected final DoubleProperty radius; 42 | 43 | protected final Polygon surrogate; 44 | 45 | /** 46 | * Creates a regular polygon shape with numberSides 47 | * @param x the x-center coordinate 48 | * @param y the y-center coordinate 49 | * @param radius the radius of the enclosed circle 50 | * @param numberSides the number of sides of the polygon 51 | */ 52 | public ShapeRegularPolygon(double x, double y, double radius, int numberSides) { 53 | Args.requireNonNegative(x, "x"); 54 | Args.requireNonNegative(y, "y"); 55 | Args.requireNonNegative(radius, "radius"); 56 | Args.requireGreaterThan(numberSides, "numberSides", 2); 57 | 58 | this.surrogate = new Polygon(); 59 | 60 | this.numberSides = numberSides; 61 | 62 | this.centerX = new SimpleDoubleProperty(x); 63 | this.centerY = new SimpleDoubleProperty(y); 64 | 65 | this.centerX.addListener((observable, oldValue, newValue) -> updatePolygon()); 66 | this.centerY.addListener((observable, oldValue, newValue) -> updatePolygon()); 67 | 68 | this.radius = new SimpleDoubleProperty( radius ); 69 | this.radius.addListener((observable, oldValue, newValue) -> updatePolygon()); 70 | 71 | updatePolygon(); 72 | } 73 | 74 | protected void updatePolygon() { 75 | surrogate.getPoints().clear(); 76 | 77 | double cx = centerX.doubleValue(); 78 | double cy = centerY.doubleValue(); 79 | 80 | double startAngle = Math.PI / (numberSides % 2 == 0 ? numberSides : 2); 81 | 82 | double radius = getRadius(); 83 | 84 | for (int i = 0; i < numberSides; i++) { 85 | double angle = startAngle + 2 * Math.PI * i / numberSides; 86 | double px = cx - radius * Math.cos(angle); 87 | double py = cy - radius * Math.sin(angle); 88 | surrogate.getPoints().addAll(px, py); 89 | } 90 | } 91 | 92 | /** 93 | * Returns the number of sides of the polygon. 94 | * @return the number of sides of the polygon 95 | */ 96 | public int getNumberSides() { 97 | return numberSides; 98 | } 99 | 100 | @Override 101 | public Shape getShape() { 102 | return this.surrogate; 103 | } 104 | 105 | @Override 106 | public DoubleProperty centerXProperty() { 107 | return this.centerX; 108 | } 109 | 110 | @Override 111 | public DoubleProperty centerYProperty() { 112 | return this.centerY; 113 | } 114 | 115 | @Override 116 | public DoubleProperty radiusProperty() { 117 | return this.radius; 118 | } 119 | 120 | @Override 121 | public double getRadius() { 122 | return this.radius.doubleValue(); 123 | } 124 | 125 | @Override 126 | public void setRadius(double radius) { 127 | Args.requireNonNegative(radius, "radius"); 128 | 129 | this.radius.set(radius); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/ShapeStar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | /** 28 | * This class represents a five-point star shape inscribed within a specified radius. 29 | * 30 | * @author brunomnsilva 31 | */ 32 | public class ShapeStar extends ShapeRegularPolygon { 33 | 34 | /** 35 | * Creates a new star shape enclosed in a circle of radius. 36 | * @param x the x-center coordinate 37 | * @param y the y-center coordinate 38 | * @param radius the radius of the enclosed circle 39 | */ 40 | public ShapeStar(double x, double y, double radius) { 41 | super(x, y, radius, 10); 42 | } 43 | 44 | @Override 45 | protected void updatePolygon() { 46 | surrogate.getPoints().clear(); 47 | 48 | double cx = centerX.doubleValue(); 49 | double cy = centerY.doubleValue(); 50 | 51 | double startAngle = Math.PI / 2; 52 | 53 | double radius = getRadius(); 54 | double innerRadius = radius / 2; 55 | 56 | for (int i = 0; i < numberSides; i++) { 57 | double angle = startAngle + 2 * Math.PI * i / numberSides; 58 | 59 | double radiusToggle = (i % 2 == 0 ? radius : innerRadius); 60 | double px = cx - radiusToggle * Math.cos(angle); 61 | double py = cy - radiusToggle * Math.sin(angle); 62 | surrogate.getPoints().addAll(px, py); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/ShapeWithRadius.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | import javafx.beans.property.DoubleProperty; 28 | import javafx.scene.shape.Shape; 29 | 30 | /** 31 | * This interface represents a shape with a radius, providing methods to access and modify 32 | * properties related to the center coordinates and radius of the shape. 33 | * 34 | * @param The type of the concrete underlying shape. 35 | * 36 | * @author brunomnsilva 37 | */ 38 | public interface ShapeWithRadius { 39 | 40 | /** 41 | * Returns the shape instance associated with this object. 42 | * 43 | * @return The shape instance. 44 | */ 45 | Shape getShape(); 46 | 47 | /** 48 | * Returns the property representing the center X coordinate of the shape. 49 | * 50 | * @return The property representing the center X coordinate. 51 | */ 52 | DoubleProperty centerXProperty(); 53 | 54 | /** 55 | * Returns the property representing the center Y coordinate of the shape. 56 | * 57 | * @return The property representing the center Y coordinate. 58 | */ 59 | DoubleProperty centerYProperty(); 60 | 61 | /** 62 | * Returns the property representing the radius of the shape. 63 | * 64 | * @return The property representing the radius of the shape. 65 | */ 66 | DoubleProperty radiusProperty(); 67 | 68 | /** 69 | * Returns the radius of the shape. 70 | * 71 | * @return The radius of the shape. 72 | */ 73 | double getRadius(); 74 | 75 | /** 76 | * Sets the radius of the shape to the specified value. 77 | * 78 | * @param radius The new radius value. 79 | */ 80 | void setRadius(double radius); 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartArrow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graphview; 25 | 26 | import javafx.scene.shape.LineTo; 27 | import javafx.scene.shape.MoveTo; 28 | import javafx.scene.shape.Path; 29 | 30 | /** 31 | * A shape of an arrow to be attached to a {@link SmartGraphEdge}. 32 | * 33 | * @author brunomnsilva 34 | */ 35 | public class SmartArrow extends Path implements SmartStylableNode { 36 | 37 | /* Styling proxy */ 38 | private final SmartStyleProxy styleProxy; 39 | 40 | /** 41 | * Default constructor. 42 | * 43 | * @param size determines the size of the arrow (side of the triangle in pixels) 44 | */ 45 | public SmartArrow(double size) { 46 | 47 | /* Create this arrow shape */ 48 | getElements().add(new MoveTo(0, 0)); 49 | getElements().add(new LineTo(-size, size)); 50 | getElements().add(new MoveTo(0, 0)); 51 | getElements().add(new LineTo(-size, -size)); 52 | 53 | /* Add the corresponding css class */ 54 | styleProxy = new SmartStyleProxy(this); 55 | styleProxy.addStyleClass("edge"); // the same style as the edge 56 | styleProxy.addStyleClass("arrow"); // it can initially be styled through this class 57 | } 58 | 59 | @Override 60 | public void setStyleInline(String css) { 61 | styleProxy.setStyleInline(css); 62 | } 63 | 64 | @Override 65 | public void setStyleClass(String cssClass) { 66 | styleProxy.setStyleClass(cssClass); 67 | } 68 | 69 | @Override 70 | public void addStyleClass(String cssClass) { 71 | styleProxy.addStyleClass(cssClass); 72 | } 73 | 74 | @Override 75 | public boolean removeStyleClass(String cssClass) { 76 | return styleProxy.removeStyleClass(cssClass); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartCircularSortedPlacementStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graphview; 25 | 26 | import javafx.geometry.Point2D; 27 | 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | 31 | /** 32 | * Places vertices around a circle, ordered by the underlying 33 | * vertices {@code element.toString() value}. 34 | * 35 | * @see SmartPlacementStrategy 36 | * 37 | * @author brunomnsilva 38 | */ 39 | public class SmartCircularSortedPlacementStrategy implements SmartPlacementStrategy { 40 | 41 | private static final int RADIUS_PADDING = 4; 42 | 43 | @Override 44 | public void place(double width, double height, SmartGraphPanel smartGraphPanel) { 45 | // Sort vertices by their label 46 | List> vertices = new ArrayList<>(smartGraphPanel.getSmartVertices()); 47 | 48 | vertices.sort((v1, v2) -> { 49 | V e1 = v1.getUnderlyingVertex().element(); 50 | V e2 = v2.getUnderlyingVertex().element(); 51 | return smartGraphPanel.getVertexLabelFor(e1).compareTo(smartGraphPanel.getVertexLabelFor(e2)); 52 | }); 53 | 54 | //place first vertex at north position, others in clockwise manner 55 | Point2D center = new Point2D(width / 2, height / 2); 56 | int N = vertices.size(); 57 | double angleIncrement = -360f / N; 58 | boolean first = true; 59 | Point2D p = null; 60 | 61 | for (SmartGraphVertex vertex : vertices) { 62 | 63 | if (first) { 64 | //verify the smallest width and height. 65 | if(width > height) 66 | p = new Point2D(center.getX(), 67 | center.getY() - height / 2 + vertex.getRadius() * RADIUS_PADDING); 68 | else 69 | p = new Point2D(center.getX(), 70 | center.getY() - width / 2 + vertex.getRadius() * RADIUS_PADDING); 71 | 72 | first = false; 73 | } else { 74 | p = UtilitiesPoint2D.rotate(p, center, angleIncrement); 75 | } 76 | 77 | vertex.setPosition(p.getX(), p.getY()); 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartGraphEdge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graphview; 25 | 26 | import com.brunomnsilva.smartgraph.graph.Edge; 27 | import com.brunomnsilva.smartgraph.graph.Vertex; 28 | 29 | /** 30 | * A graph edge visually connects two {@link Vertex} of type V. 31 | *
32 | * Concrete edge implementations used by {@link SmartGraphPanel} should 33 | * implement this interface as this type is the only one exposed to the user. 34 | * 35 | * @param Type stored in the underlying edge 36 | * @param Type of connecting vertex 37 | * 38 | * @see Vertex 39 | * @see SmartGraphPanel 40 | * 41 | * @author brunomnsilva 42 | */ 43 | public interface SmartGraphEdge extends SmartStylableNode { 44 | 45 | /** 46 | * Returns the underlying (stored reference) graph edge. 47 | * 48 | * @return edge reference 49 | * 50 | * @see SmartGraphPanel 51 | */ 52 | Edge getUnderlyingEdge(); 53 | 54 | /** 55 | * Returns the inbound vertex of the edge. 56 | * @return the inbound vertex of the edge. 57 | */ 58 | SmartGraphVertex getInbound(); 59 | 60 | /** 61 | * Returns the outbound vertex of the edge. 62 | * @return the outbound vertex of the edge. 63 | */ 64 | SmartGraphVertex getOutbound(); 65 | 66 | /** 67 | * Returns the attached arrow of the edge, for styling purposes. 68 | *
69 | * The arrows are only used with directed graphs. 70 | * 71 | * @return arrow reference; null if does not exist. 72 | */ 73 | SmartStylableNode getStylableArrow(); 74 | 75 | /** 76 | * Returns the label node for further styling. 77 | * 78 | * @return the label node. 79 | */ 80 | SmartStylableNode getStylableLabel(); 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartGraphProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graphview; 25 | 26 | import java.io.ByteArrayInputStream; 27 | import java.io.FileInputStream; 28 | import java.io.IOException; 29 | import java.io.InputStream; 30 | import java.util.Properties; 31 | import java.util.logging.Level; 32 | import java.util.logging.Logger; 33 | 34 | /** 35 | * Properties used by {@link SmartGraphPanel}. Default file is given by 36 | * the {@link #DEFAULT_FILE} property. 37 | * 38 | * @see SmartGraphPanel 39 | * @see SmartGraphVertex 40 | * @see SmartGraphEdge 41 | * 42 | * @author brunomnsilva 43 | */ 44 | public class SmartGraphProperties { 45 | 46 | private static final boolean DEFAULT_VERTEX_ALLOW_USER_MOVE = true; 47 | private static final String PROPERTY_VERTEX_ALLOW_USER_MOVE = "vertex.allow-user-move"; 48 | 49 | private static final double DEFAULT_VERTEX_RADIUS = 15; 50 | private static final String PROPERTY_VERTEX_RADIUS = "vertex.radius"; 51 | 52 | private static final String DEFAULT_VERTEX_SHAPE = "circle"; 53 | private static final String PROPERTY_VERTEX_SHAPE = "vertex.shape"; 54 | 55 | private static final boolean DEFAULT_VERTEX_USE_TOOLTIP = true; 56 | private static final String PROPERTY_VERTEX_USE_TOOLTIP = "vertex.tooltip"; 57 | 58 | private static final boolean DEFAULT_VERTEX_USE_LABEL = true; 59 | private static final String PROPERTY_VERTEX_USE_LABEL = "vertex.label"; 60 | 61 | private static final boolean DEFAULT_EDGE_USE_TOOLTIP = true; 62 | private static final String PROPERTY_EDGE_USE_TOOLTIP = "edge.tooltip"; 63 | 64 | private static final boolean DEFAULT_EDGE_USE_LABEL = true; 65 | private static final String PROPERTY_EDGE_USE_LABEL = "edge.label"; 66 | 67 | private static final boolean DEFAULT_EDGE_USE_ARROW = true; 68 | private static final String PROPERTY_EDGE_USE_ARROW = "edge.arrow"; 69 | 70 | private static final int DEFAULT_ARROW_SIZE = 5; 71 | private static final String PROPERTY_ARROW_SIZE = "edge.arrowsize"; 72 | 73 | private static final double DEFAULT_REPULSION_FORCE = 25000; 74 | private static final String PROPERTY_REPULSION_FORCE = "layout.repulsive-force"; 75 | 76 | private static final double DEFAULT_ATTRACTION_FORCE = 30; 77 | private static final String PROPERTY_ATTRACTION_FORCE = "layout.attraction-force"; 78 | 79 | private static final double DEFAULT_ATTRACTION_SCALE = 10; 80 | private static final String PROPERTY_ATTRACTION_SCALE = "layout.attraction-scale"; 81 | 82 | private static final String DEFAULT_FILE = "smartgraph.properties"; 83 | private final Properties properties; 84 | 85 | /** 86 | * Uses default properties file. 87 | */ 88 | public SmartGraphProperties() { 89 | properties = new Properties(); 90 | 91 | try { 92 | properties.load(new FileInputStream(DEFAULT_FILE)); 93 | } catch (IOException ex) { 94 | String msg = String.format("The default %s was not found. Using default values.", DEFAULT_FILE); 95 | Logger.getLogger(SmartGraphProperties.class.getName()).log(Level.WARNING, msg); 96 | } 97 | } 98 | 99 | /** 100 | * Reads properties from the desired input stream. 101 | * 102 | * @param inputStream input stream from where to read the properties 103 | */ 104 | public SmartGraphProperties(InputStream inputStream) { 105 | properties = new Properties(); 106 | try { 107 | properties.load(inputStream); 108 | } catch (IOException ex) { 109 | String msg = "The file provided by the input stream does not exist. Using default values."; 110 | Logger.getLogger(SmartGraphProperties.class.getName()).log(Level.WARNING, msg); 111 | } 112 | } 113 | 114 | /** 115 | * Reads properties from the desired string. 116 | * @param content string from where to read the properties 117 | */ 118 | public SmartGraphProperties(String content) { 119 | properties = new Properties(); 120 | try { 121 | InputStream targetStream = new ByteArrayInputStream(content.getBytes()); 122 | properties.load(targetStream); 123 | } catch (IOException ex) { 124 | String msg = "The string contents could not be loaded. Using default values."; 125 | Logger.getLogger(SmartGraphProperties.class.getName()).log(Level.WARNING, msg); 126 | } 127 | } 128 | 129 | /** 130 | * Returns a property that indicates whether a vertex can be moved freely 131 | * by the user. 132 | * 133 | * @return corresponding property value 134 | */ 135 | public boolean getVertexAllowUserMove() { 136 | return getBooleanProperty(PROPERTY_VERTEX_ALLOW_USER_MOVE, DEFAULT_VERTEX_ALLOW_USER_MOVE); 137 | } 138 | 139 | /** 140 | * Returns a property that indicates the radius of each vertex. 141 | * 142 | * @return corresponding property value 143 | */ 144 | public double getVertexRadius() { 145 | return getDoubleProperty(PROPERTY_VERTEX_RADIUS, DEFAULT_VERTEX_RADIUS); 146 | } 147 | 148 | /** 149 | * Returns a property that indicates the shape of each vertex. 150 | * 151 | * @return corresponding property value 152 | */ 153 | public String getVertexShape() { 154 | return getStringProperty(PROPERTY_VERTEX_SHAPE, DEFAULT_VERTEX_SHAPE); 155 | } 156 | 157 | /** 158 | * Returns a property that indicates the repulsion force to use in the 159 | * automatic force-based layout. 160 | * 161 | * @deprecated since version 1.1 162 | * 163 | * @return corresponding property value 164 | */ 165 | public double getRepulsionForce() { 166 | return getDoubleProperty(PROPERTY_REPULSION_FORCE, DEFAULT_REPULSION_FORCE); 167 | } 168 | 169 | /** 170 | * Returns a property that indicates the attraction force to use in the 171 | * automatic force-based layout. 172 | * 173 | * @deprecated since version 1.1 174 | * 175 | * @return corresponding property value 176 | */ 177 | public double getAttractionForce() { 178 | return getDoubleProperty(PROPERTY_ATTRACTION_FORCE, DEFAULT_ATTRACTION_FORCE); 179 | } 180 | 181 | /** 182 | * Returns a property that indicates the attraction scale to use in the 183 | * automatic force-based layout. 184 | * 185 | * @deprecated since version 1.1 186 | * 187 | * @return corresponding property value 188 | */ 189 | public double getAttractionScale() { 190 | return getDoubleProperty(PROPERTY_ATTRACTION_SCALE, DEFAULT_ATTRACTION_SCALE); 191 | } 192 | 193 | /** 194 | * Returns a property that indicates whether a vertex has a tooltip installed. 195 | * 196 | * @return corresponding property value 197 | */ 198 | public boolean getUseVertexTooltip() { 199 | return getBooleanProperty(PROPERTY_VERTEX_USE_TOOLTIP, DEFAULT_VERTEX_USE_TOOLTIP); 200 | } 201 | 202 | /** 203 | * Returns a property that indicates whether a vertex has a {@link SmartLabel} 204 | * attached to it. 205 | * 206 | * @return corresponding property value 207 | */ 208 | public boolean getUseVertexLabel() { 209 | return getBooleanProperty(PROPERTY_VERTEX_USE_LABEL, DEFAULT_VERTEX_USE_LABEL); 210 | } 211 | 212 | /** 213 | * Returns a property that indicates whether an edge has a tooltip installed. 214 | * 215 | * @return corresponding property value 216 | */ 217 | public boolean getUseEdgeTooltip() { 218 | return getBooleanProperty(PROPERTY_EDGE_USE_TOOLTIP, DEFAULT_EDGE_USE_TOOLTIP); 219 | } 220 | 221 | /** 222 | * Returns a property that indicates whether an edge has a {@link SmartLabel} 223 | * attached to it. 224 | * 225 | * @return corresponding property value 226 | */ 227 | public boolean getUseEdgeLabel() { 228 | return getBooleanProperty(PROPERTY_EDGE_USE_LABEL, DEFAULT_EDGE_USE_LABEL); 229 | } 230 | 231 | /** 232 | * Returns a property that indicates whether a {@link SmartArrow} should be 233 | * attached to an edge. 234 | * 235 | * @return corresponding property value 236 | */ 237 | public boolean getUseEdgeArrow() { 238 | return getBooleanProperty(PROPERTY_EDGE_USE_ARROW, DEFAULT_EDGE_USE_ARROW); 239 | } 240 | 241 | /** 242 | * Returns a property that indicates the size of the {@link SmartArrow}. 243 | * 244 | * @return corresponding property value 245 | */ 246 | public double getEdgeArrowSize() { 247 | return getDoubleProperty(PROPERTY_ARROW_SIZE, DEFAULT_ARROW_SIZE); 248 | } 249 | 250 | 251 | private double getDoubleProperty(String propertyName, double defaultValue) { 252 | String p = properties.getProperty(propertyName, Double.toString(defaultValue)); 253 | try { 254 | return Double.parseDouble(p); 255 | } catch (NumberFormatException e) { 256 | System.err.printf("Error in reading property %s: %s", propertyName, e.getMessage()); 257 | return defaultValue; 258 | } 259 | 260 | } 261 | 262 | private boolean getBooleanProperty(String propertyName, boolean defaultValue) { 263 | String p = properties.getProperty(propertyName, Boolean.toString(defaultValue)); 264 | try { 265 | return Boolean.parseBoolean(p); 266 | } catch (NumberFormatException e) { 267 | System.err.printf("Error in reading property %s: %s", propertyName, e.getMessage()); 268 | return defaultValue; 269 | } 270 | } 271 | 272 | private String getStringProperty(String propertyName, String defaultValue) { 273 | return properties.getProperty(propertyName, defaultValue); 274 | } 275 | 276 | /** 277 | * Test program. 278 | * @param args not used 279 | */ 280 | public static void main(String[] args) { 281 | SmartGraphProperties props = new SmartGraphProperties(); 282 | 283 | System.out.println("Prop vertex radius: " + props.getVertexRadius()); 284 | System.out.println("Prop vertex shape: " + props.getVertexShape()); 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartGraphVertex.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graphview; 25 | 26 | import com.brunomnsilva.smartgraph.graph.Graph; 27 | import com.brunomnsilva.smartgraph.graph.Vertex; 28 | 29 | /** 30 | * Abstracts the internal representation and behavior of a visualized graph vertex. 31 | * 32 | * @param Type stored in the underlying vertex 33 | * 34 | * @see SmartGraphPanel 35 | * 36 | * @author brunomnsilva 37 | */ 38 | public interface SmartGraphVertex extends SmartStylableNode { 39 | 40 | /** 41 | * Returns the underlying (stored reference) graph vertex. 42 | * 43 | * @return vertex reference 44 | * 45 | * @see Graph 46 | */ 47 | Vertex getUnderlyingVertex(); 48 | 49 | /** 50 | * Sets the position of this vertex in panel coordinates. 51 | *
52 | * Apart from its usage in the {@link SmartGraphPanel}, this method 53 | * should only be called when implementing {@link SmartPlacementStrategy}. 54 | * 55 | * @param x x-coordinate for the vertex 56 | * @param y y-coordinate for the vertex 57 | */ 58 | void setPosition(double x, double y); 59 | 60 | /** 61 | * Return the center x-coordinate of this vertex in panel coordinates. 62 | * 63 | * @return x-coordinate of the vertex 64 | */ 65 | double getPositionCenterX(); 66 | 67 | /** 68 | * Return the center y-coordinate of this vertex in panel coordinates. 69 | * 70 | * @return y-coordinate of the vertex 71 | */ 72 | double getPositionCenterY(); 73 | 74 | /** 75 | * Returns the circle radius used to represent this vertex. 76 | * 77 | * @return circle radius 78 | */ 79 | double getRadius(); 80 | 81 | /** 82 | * Returns the label node for further styling. 83 | * 84 | * @return the label node. 85 | */ 86 | SmartStylableNode getStylableLabel(); 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartLabel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graphview; 25 | 26 | import javafx.beans.property.DoubleProperty; 27 | import javafx.beans.property.ReadOnlyDoubleProperty; 28 | import javafx.beans.property.SimpleDoubleProperty; 29 | import javafx.scene.layout.StackPane; 30 | import javafx.scene.text.Text; 31 | 32 | /** 33 | * A label contains text and can be attached to any {@link SmartLabelledNode}. 34 | *
35 | * This class extends from {@link Text} and is allowed any corresponding 36 | * css formatting. 37 | * 38 | * @author brunomnsilva 39 | */ 40 | public class SmartLabel extends StackPane implements SmartStylableNode { 41 | 42 | private final Text text; 43 | 44 | /** Proxy used for node styling */ 45 | private final SmartStyleProxy paneStyleProxy; 46 | private final SmartStyleProxy textStyleProxy; 47 | 48 | /** The width used for layout calculations. */ 49 | private final DoubleProperty layoutWidth; 50 | 51 | /** The height used for layout calculations. */ 52 | private final DoubleProperty layoutHeight; 53 | 54 | 55 | /** 56 | * Default constructor. 57 | * @param label the text of the SmartLabel. 58 | */ 59 | public SmartLabel(String label) { 60 | this(0, 0, label); 61 | } 62 | 63 | /** 64 | * Constructor that accepts an initial position. 65 | * @param x initial x coordinate 66 | * @param y initial y coordinate 67 | * @param label the text of the SmartLabel. 68 | */ 69 | public SmartLabel(double x, double y, String label) { 70 | super(); 71 | 72 | this.text = new Text(label); 73 | this.textStyleProxy = new SmartStyleProxy(text); 74 | getChildren().add(text); 75 | 76 | //Set initial position 77 | setLayoutX(x); 78 | setLayoutY(y); 79 | 80 | this.paneStyleProxy = new SmartStyleProxy(this); 81 | 82 | this.layoutWidth = new SimpleDoubleProperty( ); 83 | this.layoutHeight = new SimpleDoubleProperty( ); 84 | 85 | enableBoundsListener(); 86 | propagateHoverEffectToAttachments(); 87 | } 88 | 89 | /** 90 | * Defines the X coordinate of the SmartLabel's top-left corner in its parent's coordinate space. 91 | * This property is analogous to the 'x' property of a standalone Text node for positioning 92 | * and directly manipulates the {@code layoutXProperty} of this StackPane. 93 | * 94 | * @return the property for the x coordinate. 95 | */ 96 | public final DoubleProperty xProperty() { 97 | return layoutXProperty(); 98 | } 99 | 100 | /** 101 | * Defines the Y coordinate of the SmartLabel's top-left corner in its parent's coordinate space. 102 | * This property is analogous to the 'y' property of a standalone Text node for positioning 103 | * and directly manipulates the {@code layoutYProperty} of this StackPane. 104 | * Note: For Text, 'y' often refers to the baseline; here it's the top edge. 105 | * 106 | * @return the property for the y coordinate. 107 | */ 108 | public final DoubleProperty yProperty() { 109 | return layoutYProperty(); 110 | } 111 | 112 | /** 113 | * Returns the read-only property representing the layout width of this label. 114 | * 115 | * @return the read-only property representing the layout width 116 | */ 117 | public ReadOnlyDoubleProperty layoutWidthProperty() { 118 | return layoutWidth; 119 | } 120 | 121 | /** 122 | * Returns the read-only property representing the layout height of this label. 123 | * 124 | * @return the read-only property representing the layout height 125 | */ 126 | public ReadOnlyDoubleProperty layoutHeightProperty() { 127 | return layoutHeight; 128 | } 129 | 130 | /** 131 | * Gets the text content of this SmartLabel. 132 | * @return the text content. 133 | */ 134 | public String getText() { 135 | return text.getText(); 136 | } 137 | 138 | /** 139 | * Use instead of {@link #setText(String)} to allow for correct layout adjustments and label placement. 140 | * @param newLabel the text to display on the label 141 | * @deprecated Since version 2.2.0. Keeping for compatibility until removed. 142 | */ 143 | public void setText_(String newLabel) { 144 | setText(newLabel); 145 | } 146 | 147 | /** 148 | * Sets the text content of this SmartLabel. 149 | * @param newLabel the new text content. 150 | */ 151 | public void setText(String newLabel) { 152 | if(this.text.getText().compareTo(newLabel) != 0) { 153 | this.text.setText(newLabel); 154 | } 155 | } 156 | 157 | @Override 158 | public void setStyleInline(String css) { 159 | textStyleProxy.setStyleInline(css); 160 | paneStyleProxy.setStyleInline(css); 161 | } 162 | 163 | @Override 164 | public void setStyleClass(String cssClass) { 165 | textStyleProxy.setStyleClass(cssClass); 166 | paneStyleProxy.setStyleClass(cssClass); 167 | } 168 | 169 | @Override 170 | public void addStyleClass(String cssClass) { 171 | textStyleProxy.addStyleClass(cssClass); 172 | paneStyleProxy.addStyleClass(cssClass); 173 | } 174 | 175 | @Override 176 | public boolean removeStyleClass(String cssClass) { 177 | boolean res1 = paneStyleProxy.removeStyleClass(cssClass); 178 | boolean res2 = paneStyleProxy.removeStyleClass(cssClass); 179 | return (res1 || res2); 180 | } 181 | 182 | private void enableBoundsListener() { 183 | layoutBoundsProperty().addListener((observableValue, oldValue, newValue) -> { 184 | if(newValue != null) { 185 | if(Double.compare(layoutWidth.doubleValue(), newValue.getWidth()) != 0) { 186 | layoutWidth.set(newValue.getWidth()); 187 | } 188 | if(Double.compare(layoutHeight.doubleValue(), newValue.getHeight()) != 0) { 189 | layoutHeight.set(newValue.getHeight()); 190 | } 191 | } 192 | }); 193 | } 194 | 195 | private void propagateHoverEffectToAttachments() { 196 | this.hoverProperty().addListener((observable, oldValue, newValue) -> { 197 | 198 | // Propagate to text node 199 | if(text != null && newValue) { 200 | UtilitiesJavaFX.triggerMouseEntered(text); 201 | 202 | } else if(text != null) { //newValue is false, hover ended 203 | UtilitiesJavaFX.triggerMouseExited(text); 204 | } 205 | }); 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartLabelProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | /** 28 | * A provider interface for generating labels. 29 | * 30 | * @param the type of the elements for which labels are generated 31 | */ 32 | public interface SmartLabelProvider { 33 | 34 | /** 35 | * Returns the label for the specified element. 36 | *
37 | * The returned value is expected to be non-null. 38 | * 39 | * @param element the element for which the label is generated 40 | * @return the label for the specified element 41 | */ 42 | String valueFor(T element); 43 | } -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartLabelSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2023-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | import java.lang.annotation.ElementType; 28 | import java.lang.annotation.Retention; 29 | import java.lang.annotation.RetentionPolicy; 30 | import java.lang.annotation.Target; 31 | 32 | /** 33 | * Method annotation to override an element's label provider. 34 | *
35 | * The annotated method must return a value, otherwise an exception will be thrown. 36 | *
37 | * By default, the text label is obtained from the toString method if this 38 | * annotation is not present in any other class method; this is also the case 39 | * with String and other boxed-types, e.g., Integer, Double, etc. 40 | *
41 | * If multiple annotations exist, the behavior is undefined. 42 | * 43 | * @author brunomnsilva 44 | */ 45 | @Retention(RetentionPolicy.RUNTIME) 46 | @Target(ElementType.METHOD) 47 | public @interface SmartLabelSource { 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartLabelledNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graphview; 25 | 26 | /** 27 | * A node to which a {@link SmartLabel} can be attached. 28 | * 29 | * @author brunomnsilva 30 | */ 31 | public interface SmartLabelledNode { 32 | 33 | /** 34 | * Own and bind the label position to the desired position. 35 | * 36 | * @param label text label node 37 | */ 38 | void attachLabel(SmartLabel label); 39 | 40 | /** 41 | * Returns the attached text label, if any. 42 | * 43 | * @return the text label reference or null if no label is attached 44 | */ 45 | SmartLabel getAttachedLabel(); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartPlacementStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graphview; 25 | 26 | /** 27 | * Contains the method that should be implemented when creating new vertex placement 28 | * strategies. 29 | * 30 | * @author brunomnsilva 31 | */ 32 | public interface SmartPlacementStrategy { 33 | 34 | /** 35 | * Implementations of placement strategies must implement this interface. 36 | *
37 | * Should use the {@link SmartGraphVertex#setPosition(double, double) } method to place individual vertices. 38 | * 39 | * @param Generic type for element stored at vertices. 40 | * @param Generic type for element stored at edges. 41 | * @param width Width of the area in which to place the vertices. 42 | * @param height Height of the area in which to place the vertices. 43 | * @param smartGraphPanel Reference to the {@link SmartGraphPanel} whose internal vertices are to be placed. 44 | * The vertices to be placed can be obtained through {@link SmartGraphPanel#getSmartVertices()} 45 | * 46 | */ 47 | void place(double width, double height, SmartGraphPanel smartGraphPanel); 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartRadiusProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | /** 28 | * A provider interface for generating radii values. 29 | * 30 | * @param the type of the elements for which radii are generated 31 | */ 32 | public interface SmartRadiusProvider { 33 | 34 | /** 35 | * Returns the radius for the specified element. 36 | *
37 | * The returned value is expected to be positive. 38 | * 39 | * @param vertexElement the element for which the radius is generated 40 | * @return the radius for the specified element 41 | */ 42 | double valueFor(T vertexElement); 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartRadiusSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2023-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | import java.lang.annotation.ElementType; 28 | import java.lang.annotation.Retention; 29 | import java.lang.annotation.RetentionPolicy; 30 | import java.lang.annotation.Target; 31 | 32 | /** 33 | * Method annotation to override an element's vertex shape radius. 34 | *
35 | * The annotated method must return a value (Double) with a non-negative value, otherwise an exception will be thrown. 36 | *
37 | * By default, the shape radius is defined in "smartgraph.properties" or in a custom properties file. 38 | *
39 | * If multiple annotations exist, the behavior is undefined. 40 | * 41 | * @author brunomnsilva 42 | */ 43 | @Retention(RetentionPolicy.RUNTIME) 44 | @Target(ElementType.METHOD) 45 | public @interface SmartRadiusSource { 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartRandomPlacementStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graphview; 25 | 26 | import java.util.Random; 27 | 28 | /** 29 | * Scatters the vertices randomly. 30 | * 31 | * @see SmartPlacementStrategy 32 | * 33 | * @author brunomnsilva 34 | */ 35 | public class SmartRandomPlacementStrategy implements SmartPlacementStrategy { 36 | 37 | @Override 38 | public void place(double width, double height, SmartGraphPanel smartGraphPanel) { 39 | 40 | Random rand = new Random(); 41 | 42 | for (SmartGraphVertex vertex : smartGraphPanel.getSmartVertices()) { 43 | 44 | double x = rand.nextDouble() * width; 45 | double y = rand.nextDouble() * height; 46 | 47 | vertex.setPosition(x, y); 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartShapeTypeProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | /** 28 | * A provider interface for generating shape types. 29 | * 30 | * @param the type of the elements for which shape types are generated 31 | */ 32 | public interface SmartShapeTypeProvider { 33 | /** 34 | * Returns the shape type for the specified element. 35 | *
36 | * The returned value is expected to be non-null and a valid type, see {@link ShapeFactory}. 37 | * 38 | * @param vertexElement the element for which the shape type is generated 39 | * @return the shape type for the specified element 40 | */ 41 | String valueFor(T vertexElement); 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartShapeTypeSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2023-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | import java.lang.annotation.ElementType; 28 | import java.lang.annotation.Retention; 29 | import java.lang.annotation.RetentionPolicy; 30 | import java.lang.annotation.Target; 31 | 32 | /** 33 | * Method annotation to override an element's vertex shape type representation. 34 | *
35 | * The annotated method must return a value (String) with a valid type (see {@link ShapeFactory}), otherwise an exception will be thrown. 36 | *
37 | * By default, a circle is used for the vertex shape representation. 38 | *
39 | * If multiple annotations exist, the behavior is undefined. 40 | * 41 | * @author brunomnsilva 42 | */ 43 | @Retention(RetentionPolicy.RUNTIME) 44 | @Target(ElementType.METHOD) 45 | public @interface SmartShapeTypeSource { 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartStylableNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graphview; 25 | 26 | /** 27 | * A stylable node can have its css properties changed at runtime. 28 | *
29 | * All Java FX nodes used by {@link SmartGraphPanel} to represent graph entities 30 | * should implement this interface. 31 | * 32 | * @see SmartGraphPanel 33 | * 34 | * @author brunomnsilva 35 | */ 36 | public interface SmartStylableNode { 37 | 38 | /** 39 | * Applies cumulatively the css inline styles to the node. 40 | *
41 | * These inline JavaFX styles have higher priority and are not overwritten by 42 | * any css classes set by {@link SmartStylableNode#addStyleClass(java.lang.String) }. 43 | * But will be discarded if you use {@link SmartStylableNode#setStyleClass(java.lang.String) } 44 | *
45 | * If you need to clear any previously set inline styles, use 46 | * .setStyleInline(null). Not that this will clear all inline styles previously applied. 47 | * 48 | * @param css styles 49 | */ 50 | void setStyleInline(String css); 51 | 52 | /** 53 | * Applies the CSS styling defined in class selector cssClass. 54 | *
55 | * The cssClass string must not contain a preceding dot, e.g., 56 | * "myClass" instead of ".myClass". 57 | *
58 | * The CSS Class must be defined in smartgraph.css file or 59 | * in the custom provided stylesheet. 60 | *
61 | * The expected behavior is to remove all current styling before 62 | * applying the class css. 63 | * 64 | * @param cssClass name of the CSS class. 65 | */ 66 | void setStyleClass(String cssClass); 67 | 68 | /** 69 | * Applies cumulatively the CSS styling defined in class selector 70 | * cssClass. 71 | *
72 | * The CSS Class must be defined in smartgraph.css file or 73 | * in the custom provided stylesheet. 74 | *
75 | * The cumulative operation will overwrite any existing styling elements 76 | * previously defined for previous classes. 77 | * 78 | * @param cssClass name of the CSS class. 79 | */ 80 | void addStyleClass(String cssClass); 81 | 82 | /** 83 | * Removes a previously cssClass existing CSS styling. 84 | *
85 | * Given styles can be added sequentially, the removal of a css class 86 | * will be a removal that keeps the previous ordering of kept styles. 87 | * 88 | * @param cssClass name of the CSS class. 89 | * 90 | * @return true if successful; false if cssClass wasn't 91 | * previously set. 92 | */ 93 | boolean removeStyleClass(String cssClass); 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/SmartStyleProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2023-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | import javafx.scene.Node; 28 | import javafx.scene.shape.Shape; 29 | 30 | /** 31 | * This class acts as a proxy for styling of nodes. 32 | *
33 | * It essentially groups all the logic, avoiding code duplicate. 34 | *
35 | * Classes that have this behavior can delegate the method calls to an instance 36 | * of this class. 37 | * 38 | * @author brunomnsilva 39 | */ 40 | public class SmartStyleProxy implements SmartStylableNode { 41 | 42 | private Node client; 43 | 44 | /** 45 | * Creates a new style proxy for a shape client. 46 | * @param client the shape client 47 | */ 48 | public SmartStyleProxy(Node client) { 49 | this.client = client; 50 | } 51 | 52 | /** 53 | * Changes the shape client of this proxy. 54 | * @param client the new shape client 55 | */ 56 | public void setClient(Node client) { 57 | this.client = client; 58 | } 59 | 60 | @Override 61 | public void setStyleInline(String css) { 62 | client.setStyle(css); 63 | } 64 | 65 | @Override 66 | public void setStyleClass(String cssClass) { 67 | client.getStyleClass().clear(); 68 | client.setStyle(null); 69 | client.getStyleClass().add(cssClass); 70 | } 71 | 72 | @Override 73 | public void addStyleClass(String cssClass) { 74 | client.getStyleClass().add(cssClass); 75 | } 76 | 77 | @Override 78 | public boolean removeStyleClass(String cssClass) { 79 | return client.getStyleClass().remove(cssClass); 80 | } 81 | 82 | /** 83 | * Copies all the styles and classes (currently applied) of source to destination. 84 | * @param source the shape whose styles are to be copied 85 | * @param destination the shape that receives the copied styles 86 | */ 87 | protected static void copyStyling(Shape source, Shape destination) { 88 | destination.setStyle(source.getStyle()); 89 | destination.getStyleClass().addAll(source.getStyleClass()); 90 | } 91 | 92 | /* 93 | 94 | // This may be used in the future. 95 | 96 | public void removeStyleInlineProperty(String cssProperty) { 97 | // Get the current inline style 98 | String currentStyle = client.getStyle(); 99 | 100 | // Split the style into individual property declarations 101 | String[] styleProperties = currentStyle.split(";"); 102 | 103 | // Reconstruct the style without the -fx-fill property 104 | StringBuilder newStyle = new StringBuilder(); 105 | for (String property : styleProperties) { 106 | // Split each property into key-value pair 107 | String[] keyValue = property.split(":"); 108 | if (keyValue.length == 2) { 109 | // Check if the property is -fx-fill, if not, add it to the new style 110 | String key = keyValue[0].trim(); 111 | if (!key.equals(cssProperty)) { 112 | newStyle.append(property).append(";"); 113 | } 114 | } 115 | } 116 | 117 | // Apply the modified inline style 118 | client.setStyle(newStyle.toString()); 119 | }*/ 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/UtilitiesBindings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graphview; 25 | 26 | import javafx.beans.binding.DoubleBinding; 27 | import javafx.beans.value.ObservableDoubleValue; 28 | 29 | import static javafx.beans.binding.Bindings.createDoubleBinding; 30 | 31 | /** 32 | * Some {@link Math} operations implemented as bindings. 33 | * 34 | * @author brunomnsilva 35 | */ 36 | public class UtilitiesBindings { 37 | 38 | /** 39 | * Binding for {@link java.lang.Math#atan2(double, double)} 40 | * 41 | * @param y the ordinate coordinate 42 | * @param x the abscissa coordinate 43 | * @return the theta component of the point 44 | * (rtheta) 45 | * in polar coordinates that corresponds to the point 46 | * (xy) in Cartesian coordinates. 47 | */ 48 | public static DoubleBinding atan2(final ObservableDoubleValue y, final ObservableDoubleValue x) { 49 | return createDoubleBinding(() -> Math.atan2(y.get(), x.get()), y, x); 50 | } 51 | 52 | /** 53 | * Binding for {@link java.lang.Math#toDegrees(double)} 54 | * 55 | * @param angRad an angle, in radians 56 | * @return the measurement of the angle {@code angRad} 57 | * in degrees. 58 | */ 59 | public static DoubleBinding toDegrees(final ObservableDoubleValue angRad) { 60 | return createDoubleBinding(() -> Math.toDegrees(angRad.get()), angRad); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/UtilitiesJavaFX.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2023-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package com.brunomnsilva.smartgraph.graphview; 26 | 27 | import javafx.geometry.BoundingBox; 28 | import javafx.geometry.Bounds; 29 | import javafx.geometry.Point2D; 30 | import javafx.scene.Node; 31 | import javafx.scene.Parent; 32 | import javafx.scene.input.MouseButton; 33 | import javafx.scene.input.MouseEvent; 34 | 35 | import java.util.List; 36 | 37 | /** 38 | * Utility methods for JavaFX. 39 | * 40 | * @author brunomnsilva 41 | */ 42 | public class UtilitiesJavaFX { 43 | /** 44 | * Determines the closest node that resides in the x,y scene position, if any. 45 | *
46 | * Obtained from here 47 | * 48 | * @param node parent node 49 | * @param sceneX x-coordinate of picking point 50 | * @param sceneY y-coordinate of picking point 51 | * 52 | * @return topmost node containing (sceneX, sceneY) point 53 | */ 54 | public static Node pick(Node node, double sceneX, double sceneY) { 55 | Point2D p = node.sceneToLocal(sceneX, sceneY, true /* rootScene */); 56 | 57 | // check if the given node has the point inside it, or else we drop out 58 | if (!node.contains(p)) { 59 | return null; 60 | } 61 | 62 | // at this point we know that _at least_ the given node is a valid 63 | // answer to the given point, so we will return that if we don't find 64 | // a better child option 65 | if (node instanceof Parent && !(node instanceof SmartGraphVertexNode)) { 66 | // we iterate through all children in reverse order, and stop when we find a match. 67 | // We do this as we know the elements at the end of the list have a higher 68 | // z-order, and are therefore the better match, compared to children that 69 | // might also intersect (but that would be underneath the element). 70 | Node bestMatchingChild = null; 71 | List children = ((Parent) node).getChildrenUnmodifiable(); 72 | for (int i = children.size() - 1; i >= 0; i--) { 73 | Node child = children.get(i); 74 | p = child.sceneToLocal(sceneX, sceneY, true /* rootScene */); 75 | if (child.isVisible() && !child.isMouseTransparent() && child.contains(p)) { 76 | bestMatchingChild = child; 77 | break; 78 | } 79 | } 80 | 81 | if (bestMatchingChild != null) { 82 | return pick(bestMatchingChild, sceneX, sceneY); 83 | } 84 | } 85 | 86 | return node; 87 | } 88 | 89 | 90 | /** 91 | * Combines the bounds of the specified JavaFX nodes into a single bounding box. 92 | * This method calculates the minimum and maximum coordinates of all the nodes 93 | * and returns a bounding box that encompasses all of them. 94 | * 95 | * @param nodes the JavaFX nodes whose bounds need to be combined 96 | * @return the combined bounding box containing the bounds of all specified nodes, 97 | * or {@code null} if the input array is empty or {@code null} 98 | */ 99 | public static Bounds combineBounds(Node... nodes) { 100 | if (nodes == null || nodes.length == 0) { 101 | return null; 102 | } 103 | 104 | double minX = Double.POSITIVE_INFINITY; 105 | double minY = Double.POSITIVE_INFINITY; 106 | double maxX = Double.NEGATIVE_INFINITY; 107 | double maxY = Double.NEGATIVE_INFINITY; 108 | 109 | // Iterate through all nodes to find the combined bounds 110 | for (Node node : nodes) { 111 | Bounds nodeBounds = node.getLayoutBounds(); 112 | minX = Math.min(minX, nodeBounds.getMinX()); 113 | minY = Math.min(minY, nodeBounds.getMinY()); 114 | maxX = Math.max(maxX, nodeBounds.getMaxX()); 115 | maxY = Math.max(maxY, nodeBounds.getMaxY()); 116 | } 117 | 118 | // Create a new Bounds object using the calculated minimum and maximum values 119 | return new BoundingBox(minX, minY, 0, maxX - minX, maxY - minY, 0); 120 | } 121 | 122 | /** 123 | * Computes the union of multiple {@link Bounds} objects, returning the smallest 124 | * {@link BoundingBox} that fully contains all given bounds. 125 | * 126 | *

If the input array is {@code null} or empty, this method returns {@code null}.

127 | * 128 | * @param bounds an array of {@link Bounds} objects to be merged 129 | * @return a {@link BoundingBox} that represents the union of all input bounds, 130 | * or {@code null} if the input is {@code null} or empty 131 | */ 132 | public static Bounds union(Bounds... bounds) { 133 | if (bounds == null || bounds.length == 0) { 134 | return null; 135 | } 136 | 137 | double minX = Double.POSITIVE_INFINITY; 138 | double minY = Double.POSITIVE_INFINITY; 139 | double maxX = Double.NEGATIVE_INFINITY; 140 | double maxY = Double.NEGATIVE_INFINITY; 141 | 142 | // Iterate through all nodes to find the combined bounds 143 | for (Bounds box : bounds) { 144 | minX = Math.min(minX, box.getMinX()); 145 | minY = Math.min(minY, box.getMinY()); 146 | maxX = Math.max(maxX, box.getMaxX()); 147 | maxY = Math.max(maxY, box.getMaxY()); 148 | } 149 | 150 | // Create a new Bounds object using the calculated minimum and maximum values 151 | return new BoundingBox(minX, minY, 0, maxX - minX, maxY - minY, 0); 152 | } 153 | 154 | /** 155 | * Simulates a mouse entered event on a JavaFX node. 156 | * 157 | * @param node The node to simulate the event on. If null, nothing happens. 158 | */ 159 | public static void triggerMouseEntered(Node node) { 160 | if(node == null) return; 161 | 162 | node.fireEvent(new MouseEvent(MouseEvent.MOUSE_ENTERED, 0, 0, 0, 0, 163 | MouseButton.NONE, 0, true, true, true, true, 164 | true, true, true, true, true, 165 | true, null)); 166 | } 167 | 168 | /** 169 | * Simulates a mouse exited event on a JavaFX node. 170 | * 171 | * @param node The node to simulate the event on. If null, nothing happens. 172 | */ 173 | public static void triggerMouseExited(Node node) { 174 | if(node == null) return; 175 | 176 | node.fireEvent(new MouseEvent(MouseEvent.MOUSE_EXITED, 0, 0, 0, 0, 177 | MouseButton.NONE, 0, true, true, true, true, 178 | true, true, true, true, true, 179 | true, null)); 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /src/main/java/com/brunomnsilva/smartgraph/graphview/UtilitiesPoint2D.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package com.brunomnsilva.smartgraph.graphview; 25 | 26 | import javafx.geometry.Point2D; 27 | 28 | /** 29 | * Class with utility methods for Point2D instances. 30 | * 31 | * @author brunomnsilva 32 | */ 33 | public class UtilitiesPoint2D { 34 | 35 | /** 36 | * Rotate a point around a pivot point by a specific degrees amount 37 | * @param point point to rotate 38 | * @param pivot pivot point 39 | * @param angleDegrees rotation degrees 40 | * @return rotated point 41 | */ 42 | public static Point2D rotate(final Point2D point, final Point2D pivot, final double angleDegrees) { 43 | double angleRadians = Math.toRadians(angleDegrees); // Convert angle to radians 44 | 45 | double sin = Math.sin(angleRadians); 46 | double cos = Math.cos(angleRadians); 47 | 48 | // Translate the point relative to the pivot 49 | double translatedX = point.getX() - pivot.getX(); 50 | double translatedY = point.getY() - pivot.getY(); 51 | 52 | // Apply rotation using trigonometric functions 53 | double rotatedX = translatedX * cos - translatedY * sin; 54 | double rotatedY = translatedX * sin + translatedY * cos; 55 | 56 | // Translate the rotated point back to the original position 57 | rotatedX += pivot.getX(); 58 | rotatedY += pivot.getY(); 59 | 60 | return new Point2D(rotatedX, rotatedY); 61 | } 62 | 63 | /** 64 | * Calculates the third vertex point that forms a triangle with segment AB as the base and C equidistant to A and B; 65 | * angleDegrees is the angle formed between A and C. 66 | * 67 | * @param pointA the point a 68 | * @param pointB the point b 69 | * @param angleDegrees desired angle (in degrees) 70 | * @return the point c 71 | */ 72 | public static Point2D calculateTriangleBetween(final Point2D pointA, final Point2D pointB, final double angleDegrees) { 73 | // Calculate the midpoint of AB 74 | Point2D midpointAB = pointA.midpoint(pointB); 75 | 76 | // Calculate the perpendicular bisector of AB 77 | double slopeAB = (pointB.getY() - pointA.getY()) / (pointB.getX() - pointA.getX()); 78 | double perpendicularSlope = -1 / slopeAB; 79 | 80 | // Handle special cases where the perpendicular bisector is vertical or horizontal 81 | if (Double.isInfinite(perpendicularSlope)) { 82 | double yC = midpointAB.getY() + Math.tan(Math.toRadians(angleDegrees)) * midpointAB.getX(); 83 | return new Point2D(midpointAB.getX(), yC); 84 | } else if (perpendicularSlope == 0) { 85 | return new Point2D(pointA.getX(), midpointAB.getY()); 86 | } 87 | 88 | // Calculate the angle between AB and the x-axis 89 | double angleAB = Math.toDegrees(Math.atan2(pointB.getY() - pointA.getY(), pointB.getX() - pointA.getX())); 90 | 91 | // Calculate the angle between AB and AC 92 | double angleAC = angleAB + angleDegrees; 93 | 94 | // Calculate the coordinates of point C 95 | double distanceAC = pointA.distance(midpointAB) / Math.cos(Math.toRadians(angleDegrees)); 96 | double xC = pointA.getX() + distanceAC * Math.cos(Math.toRadians(angleAC)); 97 | double yC = perpendicularSlope * (xC - midpointAB.getX()) + midpointAB.getY(); 98 | 99 | return new Point2D(xC, yC); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The SmartGraph module provides graph data structures and visualizations for JavaFX. 3 | */ 4 | module com.brunomnsilva.smartgraph { 5 | requires javafx.controls; 6 | requires javafx.graphics; 7 | requires javafx.base; 8 | requires java.logging; 9 | 10 | exports com.brunomnsilva.smartgraph; 11 | exports com.brunomnsilva.smartgraph.containers; 12 | exports com.brunomnsilva.smartgraph.graph; 13 | exports com.brunomnsilva.smartgraph.graphview; 14 | exports com.brunomnsilva.smartgraph.example; 15 | } -------------------------------------------------------------------------------- /version.properties: -------------------------------------------------------------------------------- 1 | # 2 | # The MIT License 3 | # 4 | # JavaFXSmartGraph | Copyright 2019-2024 brunomnsilva@gmail.com 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | # 24 | 25 | project.version=2.3.0 26 | --------------------------------------------------------------------------------