From a39ee72836add0ef97f95fa038376f4f152cab3e Mon Sep 17 00:00:00 2001 From: Barry G Becker Date: Sun, 5 May 2024 08:49:42 -0700 Subject: [PATCH] 22 - Add optional control point to ConnectorSkeleton for curved edges --- .../ui/viewer_swing/test/TestCurvedEdges.java | 115 ++++++++++++++++++ .../ui/viewer_swing/test/TestManySprite.java | 2 +- .../ui/swing/renderer/ConnectorSkeleton.java | 9 ++ .../ui/swing/renderer/shape/Connector.java | 3 + .../swing/advancedShapes/CubicCurveShape.java | 65 +++++----- 5 files changed, 165 insertions(+), 29 deletions(-) create mode 100644 src-test/org/graphstream/ui/viewer_swing/test/TestCurvedEdges.java diff --git a/src-test/org/graphstream/ui/viewer_swing/test/TestCurvedEdges.java b/src-test/org/graphstream/ui/viewer_swing/test/TestCurvedEdges.java new file mode 100644 index 0000000..e1e35fa --- /dev/null +++ b/src-test/org/graphstream/ui/viewer_swing/test/TestCurvedEdges.java @@ -0,0 +1,115 @@ +/* + * This file is part of GraphStream . + * + * GraphStream is a library whose purpose is to handle static or dynamic + * graph, create them from scratch, file or any source and display them. + * + * This program is free software distributed under the terms of two licenses, the + * CeCILL-C license that fits European law, and the GNU Lesser General Public + * License. You can use, modify and/ or redistribute the software under the terms + * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following + * URL or under the terms of the GNU LGPL as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms. + */ + +/** + * @author Antoine Dutot + * @author Guilhelm Savin + * @author Hicham Brahimi + */ + +package org.graphstream.ui.viewer_swing.test; + +import org.graphstream.graph.Edge; +import org.graphstream.graph.Graph; +import org.graphstream.graph.implementations.MultiGraph; +import org.graphstream.ui.geom.Point3; + +public class TestCurvedEdges { + public static void main(String[] args) { + (new TestCurvedEdges()).run(); + } + + private void run() { + System.setProperty("org.graphstream.ui", "org.graphstream.ui.swing.util.Display"); + Graph graph = new MultiGraph( "TestSprites" ); + + populateGraph(graph); + + graph.setAttribute("ui.stylesheet", styleSheet); + graph.setAttribute("ui.antialias"); + graph.display(false); + } + + private void populateGraph(Graph graph) { + Edge edge; + graph.addNode("A"); + graph.addNode("B"); + graph.addNode("C"); + + graph.addEdge("AB", "A", "B", true); + graph.addEdge("BA", "B", "A", true); + graph.addEdge("BC", "B", "C", true); + graph.addEdge("CB", "C", "B", true); + graph.addEdge("CA", "C", "A", true); + graph.addEdge("AC", "A", "C", true); + + // Replacing node D with the modelling of an intersection + graph.addNode("D1"); + graph.addNode("D2"); + graph.addNode("D3"); + graph.addNode("D4"); + graph.addNode("D5"); + graph.addNode("D6"); + + graph.getNode("A").setAttribute("xyz", -1500d, -1100d, 0.0 ); + graph.getNode("B").setAttribute("xyz", 1500d, -1100d, 0.0 ); + graph.getNode("C").setAttribute("xyz", 100d, 1500d, 0.0 ); + graph.getNode("D1").setAttribute("xyz", -100d, 300d, 0.0 ); + graph.getNode("D2").setAttribute("xyz", 100d, 300d, 0.0 ); + graph.getNode("D3").setAttribute("xyz", 250d, -100d, 0.0 ); + graph.getNode("D4").setAttribute("xyz", 200d, -300d, 0.0 ); + graph.getNode("D5").setAttribute("xyz", -200d, -300d, 0.0 ); + graph.getNode("D6").setAttribute("xyz", -250d, -100d, 0.0 ); + + edge = graph.addEdge("CD1", "C", "D1", true); + edge.setAttribute("ui.control-point", new Point3(-100.0d, 500.0d, 0.0)); + graph.addEdge("D2C", "D2", "C", true); + + edge = graph.addEdge("BD3", "B", "D3", true); + edge.setAttribute("ui.control-point", new Point3(1000.0d, -600.0d, 0.0)); + edge = graph.addEdge("D4B", "D4", "B", true); + edge.setAttribute("ui.control-point", new Point3(1000.0d, -600.0d, 0.0)); + + edge = graph.addEdge("AD5", "A", "D5", true); + edge.setAttribute("ui.control-point", new Point3(-1000.0d, -600.0d, 0.0)); + edge = graph.addEdge("D6A", "D6", "A", true); + edge.setAttribute("ui.control-point", new Point3(-700.0d, -300.0d, 0.0)); + + edge = graph.addEdge("D1D6", "D1", "D6", true); + edge.setAttribute("ui.control-point", new Point3(-100.0d, 100.0d, 0.0)); + edge = graph.addEdge("D1D4", "D1", "D4", true); + edge.setAttribute("ui.control-point", new Point3(0.0d, -0.0d, 0.0)); + edge = graph.addEdge("D5D2", "D5", "D2", true); + edge.setAttribute("ui.control-point", new Point3(0.0d, 0.0d, 0.0)); + edge = graph.addEdge("D3D2", "D3", "D2", true); + edge.setAttribute("ui.control-point", new Point3(100.0d, 100.0d, 0.0)); + edge = graph.addEdge("D3D6", "D3", "D6", true); + edge.setAttribute("ui.control-point", new Point3(0.0d, 0.0d, 0.0)); + edge = graph.addEdge("D5D4", "D5", "D4", true); + edge.setAttribute("ui.control-point", new Point3(0.0d, -240.0d, 0.0)); + } + + String styleSheet = "edge { shape: cubic-curve; }"; +} diff --git a/src-test/org/graphstream/ui/viewer_swing/test/TestManySprite.java b/src-test/org/graphstream/ui/viewer_swing/test/TestManySprite.java index 2464c13..8bf5386 100644 --- a/src-test/org/graphstream/ui/viewer_swing/test/TestManySprite.java +++ b/src-test/org/graphstream/ui/viewer_swing/test/TestManySprite.java @@ -119,7 +119,7 @@ public void buttonReleased( String id ) {} private void moveSprites() { sprites.forEach( s -> ((TestSprite)s).move() ); } - + private void addSprites() { sprites = new SpriteManager( graph ); diff --git a/src/org/graphstream/ui/swing/renderer/ConnectorSkeleton.java b/src/org/graphstream/ui/swing/renderer/ConnectorSkeleton.java index 5554ac6..a60c9f9 100644 --- a/src/org/graphstream/ui/swing/renderer/ConnectorSkeleton.java +++ b/src/org/graphstream/ui/swing/renderer/ConnectorSkeleton.java @@ -59,6 +59,7 @@ public class ConnectorSkeleton extends Skeleton implements AttributeUtils, org.g private boolean isACurve; private int aMulti; private boolean isALoop; + private Point3 controlPoint; public ConnectorSkeleton() { this.points = new EdgePoints(2); @@ -114,6 +115,10 @@ public void setPoly(Object aSetOfPoints) { lengths = null ; } } + + public void setControlPoint(Point3 controlPoint) { + this.controlPoint = controlPoint; + } public void setPoly(Point3[] aSetOfPoints) { if (points == null || points.size() != aSetOfPoints.length) { @@ -193,6 +198,10 @@ public Point3 to() { public Point3 from() { return points.get(0); } + + public Point3 getControlPoint() { + return controlPoint; + } /** * Total length of the polyline defined by the points. diff --git a/src/org/graphstream/ui/swing/renderer/shape/Connector.java b/src/org/graphstream/ui/swing/renderer/shape/Connector.java index 4fc74eb..711b03b 100644 --- a/src/org/graphstream/ui/swing/renderer/shape/Connector.java +++ b/src/org/graphstream/ui/swing/renderer/shape/Connector.java @@ -113,6 +113,9 @@ public void configureConnectorForElement(DefaultCamera2D camera, GraphicEdge ele if(element.hasAttribute("ui.points")) { skel.setPoly(element.getAttribute("ui.points")); } else { + if(element.hasAttribute("ui.control-point")) { + skel.setControlPoint(element.getAttribute("ui.control-point", Point3.class)); + } positionForLinesAndCurves( skel, element.from.getStyle(), element.from, element.to, element.multi, element.getGroup() ); } diff --git a/src/org/graphstream/ui/swing/renderer/shape/swing/advancedShapes/CubicCurveShape.java b/src/org/graphstream/ui/swing/renderer/shape/swing/advancedShapes/CubicCurveShape.java index 856bddc..2f83527 100644 --- a/src/org/graphstream/ui/swing/renderer/shape/swing/advancedShapes/CubicCurveShape.java +++ b/src/org/graphstream/ui/swing/renderer/shape/swing/advancedShapes/CubicCurveShape.java @@ -31,16 +31,17 @@ package org.graphstream.ui.swing.renderer.shape.swing.advancedShapes; -import java.awt.Graphics2D; -import java.awt.geom.Path2D; - +import org.graphstream.ui.geom.Point3; import org.graphstream.ui.geom.Vector2; import org.graphstream.ui.graphicGraph.GraphicElement; import org.graphstream.ui.swing.Backend; -import org.graphstream.ui.view.camera.DefaultCamera2D; import org.graphstream.ui.swing.renderer.Skeleton; import org.graphstream.ui.swing.renderer.shape.swing.ShowCubics; import org.graphstream.ui.swing.renderer.shape.swing.baseShapes.LineConnectorShape; +import org.graphstream.ui.view.camera.DefaultCamera2D; + +import java.awt.Graphics2D; +import java.awt.geom.Path2D; public class CubicCurveShape extends LineConnectorShape { ShowCubics showCubics ; @@ -56,12 +57,12 @@ public void make(Backend backend, DefaultCamera2D camera) { } private void make(DefaultCamera2D camera, double sox, double soy, double swx, double swy) { - if (skel.multi() > 1 || skel.isLoop()) // is a loop or a multi edge - makeMultiOrLoop(camera, sox, soy, swx, swy); - else if(skel.isPoly() && skel.size() == 4) - makeFromPoints(camera, sox, soy, swx, swy); // has points positions + if (skel.multi() > 1 || skel.isLoop()) // is a loop or a multi edge + makeMultiOrLoop(camera, sox, soy, swx, swy); + else if (skel.isPoly() && skel.size() == 4) + makeFromPoints(camera, sox, soy, swx, swy); // has points positions else - makeSingle(camera, sox, soy, swx, swy); // is a single edge. + makeSingle(camera, sox, soy, swx, swy); // is a single edge. } private void makeMultiOrLoop(DefaultCamera2D camera, double sox, double soy, double swx, double swy) { @@ -132,24 +133,32 @@ private void makeSingle(DefaultCamera2D camera, double sox, double soy, double s Vector2 mainDir = new Vector2(skel.from(), skel.to()); double length = mainDir.length(); double angle = mainDir.y() / length; - double c1x = 0.0; - double c1y = 0.0; - double c2x = 0.0; - double c2y = 0.0; - - if (angle > 0.707107f || angle < -0.707107f) { - // North or south. - c1x = fromx + mainDir.x() / 2; - c2x = c1x; - c1y = fromy; - c2y = toy; - } - else { - // East or west. - c1x = fromx; - c2x = tox; - c1y = fromy + mainDir.y() / 2; - c2y = c1y; + double c1x; + double c1y; + double c2x; + double c2y; + + if (skel.getControlPoint() != null) { + Point3 controlPoint = skel.getControlPoint(); + c1x = controlPoint.x; + c1y = controlPoint.y; + c2x = controlPoint.x; + c2y = controlPoint.y; + } else { + if (angle > 0.707107f || angle < -0.707107f) { + // North or south. + c1x = fromx + mainDir.x() / 2; + c1y = fromy; + c2x = c1x; + c2y = toy; + } + else { + // East or west. + c1x = fromx; + c1y = fromy + mainDir.y() / 2; + c2x = tox; + c2y = c1y; + } } theShape.reset(); @@ -192,7 +201,7 @@ public void render(Backend bck, DefaultCamera2D camera, GraphicElement element, // g.setStroke( s ); // g.setColor( c ); // showCtrlPoints( g, camera ) - // } + // } } @Override