Skip to content

Commit

Permalink
Version 2.0.0-rc1. Shapes (partially addresses #4, #34 and #37), size…
Browse files Browse the repository at this point in the history
…s, providers, annotations and minor improvements.
  • Loading branch information
brunomnsilva committed Mar 19, 2024
1 parent ee09a7f commit 9acd594
Show file tree
Hide file tree
Showing 29 changed files with 1,317 additions and 135 deletions.
30 changes: 19 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,27 @@ through a [force-directed algorithm](https://en.wikipedia.org/wiki/Force-directe

### What's new?

- (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):

- `ForceDirectedSpringSystemLayoutStrategy`: this is the original implementation for the automatic placement, through a spring system;
- `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.
This is a *release candidate* version, as it hasn't been thoroughly tested yet. Feedback is appreciated.

- (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.
Although one full version number higher than the previous *stable* version (1.1.0), existing applications are expected to work with this library version without significant changes.

- (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.
:warning: The only exception is the necessary use of `SmartStylableNode.setStyleInline(...)` instead of `SmartStylableNode.setStyle(...)` to correctly apply inline styles to nodes (vertices and edges). Css classes are set the same way as before.

- (0.9.4) You can manually alter a vertex position on the panel at anytime, through `SmartGraphPanel.setVertexPosition(Vertex<V> v)`; see the example at `com.brunomnsilva.smartgraph.example`.
- (2.0.0-rc1) Shapes, sizes, providers, annotations and minor improvements:

- (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.
- Different shapes can be used to represent vertices, namely circles, stars and regular polygons (from triangles to dodecagons);
- The default shape can be specified with the `vertex.shape` property in `smartgraph.properties`
- Can be set/changed at runtime through a `SmartShapeTypeProvider` or `SmartShapeTypeSource` annotation.

- (0.9.4) You can now style labels and arrows individually.
- The radius of the shape (enclosing circle) used to represent a vertex can be set/changed at runtime through a `SmartRadiusProvider` or `SmartRadiusSource` annotation.

- Updated shapes and radii are only reflected in the visualization after calling `SmartGraphPanel.update()` or `SmartGraphPanel.updateAndWait()`.

- Improvements:
- When dragging nodes, they will be kept within the panel's bounds.
- The look of curved edges has been improved.

See the [wiki](https://github.com/brunomnsilva/JavaFXSmartGraph/wiki) for the complete changelist.

### Using the library

Expand Down Expand Up @@ -216,7 +223,7 @@ graphView.setVertexDoubleClickAction(graphVertex -> {
graphView.setEdgeDoubleClickAction(graphEdge -> {
System.out.println("Edge contains element: " + graphEdge.getUnderlyingEdge().element());
//dynamically change the style, can also be done for a vertex
graphEdge.setStyle("-fx-stroke: black; -fx-stroke-width: 2;");
graphEdge.setStyleInline("-fx-stroke: black; -fx-stroke-width: 2;");
});
```

Expand All @@ -234,7 +241,8 @@ You can set the graph visualization properties in the `smartgraph.properties` fi
# Vertex related configurations
#
vertex.allow-user-move = true
vertex.radius = 15
vertex.radius = 15
vertex.shape = circle
vertex.tooltip = true
vertex.label = false

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

<groupId>com.brunomnsilva</groupId>
<artifactId>smartgraph</artifactId>
<version>1.1.1</version>
<version>2.0.0-rc1</version>
<packaging>jar</packaging>

<name>${project.groupId}:${project.artifactId}</name>
Expand Down
35 changes: 31 additions & 4 deletions smartgraph.properties
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,40 @@
################################################################################
# javafxgraph.properties
#
# ATTENTION: do not leave any trailing spaces after 'true' or 'false' values
# These properties can be used to override the default values, which are:
#
# vertex.allow-user-move = true (boolean)
# vertex.radius = 15 (in pixels)
# vertex.shape = circle (string)
# vertex.tooltip = true (boolean)
# vertex.label = true (boolean)
# edge.tooltip = true (boolean)
# edge.label = true (boolean)
# edge.arrow = true (boolean)
# edge.arrowsize = 5 (in pixels)
#
# Allowed/available vertex shape names (string without quotes) are:
# - circle
# - star
# - triangle
# - square
# - pentagon
# - hexagon
# - heptagon
# - octagon
# - nonagon
# - decagon
# - hendecagon
# - dodecagon
#
# ATTENTION: do not leave any trailing spaces after 'true' or 'false' values
################################################################################

# Vertex related configurations
#
vertex.allow-user-move = true
vertex.radius = 15
vertex.radius = 20
vertex.shape = star
vertex.tooltip = true
vertex.label = true

Expand All @@ -46,12 +72,13 @@ edge.arrow = true
# size in pixels (side of a triangle); only for directed graphs
edge.arrowsize = 5

# Force-directed layout related configurations
#
# Notice: deprecated since version 1.1.0 Force directed layout strategies are now
# instantiated and can be swapped at runtime, per the Strategy design pattern.
# The parameters are passed as arguments or one can use the default ones described
# in the javadoc documentation.
#
# Force-directed layout related configurations
#
# -- You should experiment with different values for your
# -- particular problem, knowing that not all will achieve
# -- a stable state
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/com/brunomnsilva/smartgraph/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public void start(Stage ignored) {
This can be done at any time afterwards.
*/
if (g.numVertices() > 0) {
graphView.getStylableVertex("A").setStyle("-fx-fill: gold; -fx-stroke: brown;");
graphView.getStylableVertex("A").setStyleInline("-fx-fill: gold; -fx-stroke: brown;");
}

/*
Expand Down Expand Up @@ -110,9 +110,9 @@ public void start(Stage ignored) {
graphView.setEdgeDoubleClickAction(graphEdge -> {
System.out.println("Edge contains element: " + graphEdge.getUnderlyingEdge().element());
//dynamically change the style when clicked
graphEdge.setStyle("-fx-stroke: black; -fx-stroke-width: 3;");
graphEdge.setStyleInline("-fx-stroke: black; -fx-stroke-width: 3;");

graphEdge.getStylableArrow().setStyle("-fx-stroke: black; -fx-stroke-width: 3;");
graphEdge.getStylableArrow().setStyleInline("-fx-stroke: black; -fx-stroke-width: 3;");

//uncomment to see edges being removed after click
//Edge<String, String> underlyingEdge = graphEdge.getUnderlyingEdge();
Expand Down Expand Up @@ -250,7 +250,7 @@ private void continuously_test_adding_elements(Graph<String, String> g, SmartGra
//color new vertices
SmartStylableNode stylableVertex = graphView.getStylableVertex(vertexId);
if(stylableVertex != null) {
stylableVertex.setStyle("-fx-fill: orange;");
stylableVertex.setStyleInline("-fx-fill: orange;");
}
} else {
Vertex<String> existing1 = get_random_vertex(g);
Expand Down
33 changes: 31 additions & 2 deletions src/main/java/com/brunomnsilva/smartgraph/example/City.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
package com.brunomnsilva.smartgraph.example;

import com.brunomnsilva.smartgraph.graphview.SmartLabelSource;
import com.brunomnsilva.smartgraph.graphview.SmartRadiusSource;
import com.brunomnsilva.smartgraph.graphview.SmartShapeTypeSource;

/**
* A simple class to represent a city in an example usage of the library.
Expand Down Expand Up @@ -53,7 +55,6 @@ public void setName(String name) {
this.name = name;
}


public float getPopulation() {
return population;
}
Expand All @@ -67,5 +68,33 @@ public String toString() {
return "City{" + "name=" + name + ", population=" + population + '}';
}


@SmartShapeTypeSource
public String modelShape() {
if(this.name == "Tokyo") {
return "star";
}

return "circle";
}

@SmartRadiusSource
public Double modelRadius() {
return convertToLogScale(Double.valueOf(String.valueOf(this.population)));
}

private static double convertToLogScale(double value) {
// Define input range
double minValue = 1;
double maxValue = 40;

// Define output range
double minOutputValue = 15;
double maxOutputValue = 40;

// Map the input value to the output range using logarithmic function
double mappedValue = (Math.log(value) - Math.log(minValue)) / (Math.log(maxValue) - Math.log(minValue));

// Map the mapped value to the output range
return minOutputValue + mappedValue * (maxOutputValue - minOutputValue);
}
}
18 changes: 12 additions & 6 deletions src/main/java/com/brunomnsilva/smartgraph/example/ExampleMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@
package com.brunomnsilva.smartgraph.example;

import com.brunomnsilva.smartgraph.containers.SmartGraphDemoContainer;
import com.brunomnsilva.smartgraph.graph.Graph;
import com.brunomnsilva.smartgraph.graph.GraphEdgeList;
import com.brunomnsilva.smartgraph.graph.Digraph;
import com.brunomnsilva.smartgraph.graph.DigraphEdgeList;
import com.brunomnsilva.smartgraph.graph.Vertex;
import com.brunomnsilva.smartgraph.graphview.SmartCircularSortedPlacementStrategy;
import com.brunomnsilva.smartgraph.graphview.SmartGraphPanel;
import com.brunomnsilva.smartgraph.graphview.SmartGraphProperties;
import com.brunomnsilva.smartgraph.graphview.SmartGraphVertex;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
Expand All @@ -45,7 +46,7 @@ public class ExampleMain extends Application {
@Override
public void start(Stage ignored) {

Graph<City, Distance> distances = new GraphEdgeList<>();
Digraph<City, Distance> distances = new DigraphEdgeList<>();

Vertex<City> prague = distances.insertVertex(new City("Prague", 1.3f));
Vertex<City> tokyo = distances.insertVertex(new City("Tokyo", 37.5f));
Expand All @@ -64,7 +65,7 @@ public void start(Stage ignored) {
distances.insertEdge(beijing, london, new Distance(8132));

/* Only Java 15 allows for multi-line strings. */
String customProps = "edge.label = true" + "\n" + "edge.arrow = false";
String customProps = "edge.label = true" + "\n" + "edge.arrow = true";

SmartGraphProperties properties = new SmartGraphProperties(customProps);

Expand Down Expand Up @@ -92,10 +93,15 @@ public void start(Stage ignored) {
graphView.setVertexPosition(helsinky, 924, 100);
graphView.setVertexPosition(london, 200, 668);
graphView.setVertexPosition(prague, 824, 668);
graphView.setVertexPosition(tokyo, 512, 300);
graphView.setVertexPosition(tokyo, 512, 200);
graphView.setVertexPosition(newYork, 512, 400);

graphView.getStylableLabel(tokyo).setStyle("-fx-stroke: red; -fx-fill: red;");
graphView.getStylableLabel(tokyo).setStyleInline("-fx-stroke: green; -fx-fill: green;");

graphView.setVertexDoubleClickAction((SmartGraphVertex<City> graphVertex) -> {
graphVertex.addStyleClass("myVertex");
});

}

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ protected Point2D computeForceBetween(SmartGraphVertexNode<V> v, SmartGraphVerte

Point2D vPosition = v.getUpdatedPosition();
Point2D wPosition = w.getUpdatedPosition();
double distance = vPosition.distance(wPosition);
double distance = vPosition.distance(wPosition) - (v.getRadius() + w.getRadius());
Point2D forceDirection = wPosition.subtract(vPosition).normalize();

if (distance < 1) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* The MIT License
*
* JavaFXSmartGraph | Copyright 2024 [email protected]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package com.brunomnsilva.smartgraph.graphview;

import javafx.beans.property.DoubleProperty;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Shape;

/**
* This class represents a circle shape with a specified radius.
*
* @author brunomnsilva
*/
public class ShapeCircle implements ShapeWithRadius<Circle> {

private final Circle surrogate;

/**
* Creates a circle shape.
* @param x the x-center coordinate
* @param y the y-center coordinate
* @param radius the radius of the circle
*/
public ShapeCircle(double x, double y, double radius) {
Args.requireNonNegative(x, "x");
Args.requireNonNegative(y, "y");
Args.requireNonNegative(radius, "radius");

this.surrogate = new Circle(x, y, radius);
}

@Override
public Shape getShape() {
return surrogate;
}

@Override
public DoubleProperty centerXProperty() {
return surrogate.centerXProperty();
}

@Override
public DoubleProperty centerYProperty() {
return surrogate.centerYProperty();
}

@Override
public DoubleProperty radiusProperty() {
return surrogate.radiusProperty();
}

@Override
public double getRadius() {
return surrogate.getRadius();
}

@Override
public void setRadius(double radius) {
Args.requireNonNegative(radius, "radius");

// Only update if different
if(Double.compare(this.getRadius(), radius) != 0) {
surrogate.setRadius(radius);
}
}
}
Loading

0 comments on commit 9acd594

Please sign in to comment.