Giter VIP home page Giter VIP logo

gs-ui-swing's People

Contributors

amimeyet avatar gsavin avatar hichbra avatar pigne avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

gs-ui-swing's Issues

allow setting custom camera

I noticed that edges are only found if you click right in the middle of them.
I really would like to be able to click (or mouse over) anywhere on the edge in order to find it.
As a workaround, I would like to set my own custom camera that has my own implementation of edgeContains.
Unfortunately, this does not seem possible because there is not setCamera method on the defaultView, only getCamera.
I would like to be able to do this:

viewer.getDefaultView.setCamera(customCamera)

Fill Nodes with images

I would like to use GraphStream to display a tree where the nodes are images.
None of the commands I found in the tutorials work for me. Colouring the nodes with the same commands is no problem. The Url works without any problems in another context.
Unfortunately, I don't see the error even after trying around for a while.

Graph graph = new SingleGraph("Tutorial 1");
Node tmpNode = graph.addNode("A" );
graph.addNode("B" );
graph.addNode("C" );
graph.addEdge("AB", "A", "B");
graph.addEdge("BC", "B", "C");
graph.addEdge("CA", "C", "A");
System.setProperty("org.graphstream.ui", "swing");

tmpNode.addAttribute("ui.style", "fill-color: rgb(0,100,255);"); //works fine
graph.addAttribute("ui.stylesheet", "node { fill-color: rgb(0,100,255); }"); //works fine

tmpNode.addAttribute("ui.style", "fill-image: url('D:/Studium/Scaffold/scaffoldTest/scaffoldTestOutput/Test11/Tree/TreeTest0Level0.png');"); //does not work
graph.addAttribute("ui.stylesheet", "node { fill-mode: image-scaled; fill-image: url('D:/Studium/Scaffold/scaffoldTest/scaffoldTestOutput/Test11/Tree/TreeTest0Level0.png'); }"); //does not work

graph.display();

Greetings
Julian

cannot create image with swing backend: initImage is never called in the constructor

I have the following bug when I try to create an image :

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.awt.Graphics2D.getTransform()" because "this.g2" is null
        at org.graphstream.ui.swing.BackendJ2D.prepareNewFrame(BackendJ2D.java:107)
        at org.graphstream.ui.swing.SwingGraphRenderer.render(SwingGraphRenderer.java:223)
        at org.graphstream.ui.swing.util.SwingFileSinkImages.render(SwingFileSinkImages.java:70)
        at org.graphstream.stream.file.FileSinkImages.outputNewImage(FileSinkImages.java:548)
        at org.graphstream.stream.file.FileSinkImages.outputNewImage(FileSinkImages.java:515)

I think the problem is that the function initImage (that create g2d) is never called in the constructor:

https://github.com/graphstream/gs-ui-swing/blob/67888fae1f2bbc71b4d9ab50bdfd20e7ea391a67/src/org/graphstream/ui/swing/util/SwingFileSinkImages.java#L78C1-L82C3

DefaultView is not abstract and does not override abstract method enableMouseOptions()

Hello, tried to compile it against the master branch of gs libraries and I have got this error:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile (default-compile) on project gs-ui-swing: Compilation failure [ERROR] /Users/xxx/Development/Tests/GraphStream/gs-ui-swing/src/org/graphstream/ui/swing_viewer/DefaultView.java:[114,8] org.graphstream.ui.swing_viewer.DefaultView is not abstract and does not override abstract method enableMouseOptions() in org.graphstream.ui.view.View
regards
john

Problem of synchronization between a GraphStream's View and JTextField

Hello,
I created a JFrame composed of one View and two JTextField:
1
The View displays five nodes: four are just landmarks and are not expected to be moved by the user ("fixed_*"), and one is there to be moved by the user ("unfixed"). The two JTextField show the coordinates of the "unfixed" node. Both the View and the JTextField have to be synchronized with each other. Indeed, when the user moves the "unfixed" node in the View, then the two JTextField have to be updated accordingly :
2
Reciprocally, when the user modifies the coordinate in one of the JTextField, then the View has to be updated accordingly too:
3
Here are four test cases:

  • Test case 1: the user runs the application and moves the "unfixed" node in the View.
  • Test case 2: the user runs the application and modifies the coordinate in one of the JTextField.
  • Test case 3: the user runs the application, modifies the coordinate in one of the JTextField, and then moves the "unfixed" node in the View.
  • Test case 4: the user runs the application, moves the "unfixed" node in the View, and then modifies the coordinate in one of the JTextField.

Test cases 1, 2 and 3 work fine, but test case 4 doesn't work. Actually, in test case 4, once the user moved the "unfixed" node in the View, then the modification of the coordinate in one of the JTextField doesn't update the View.

I tried to analyze what differs between the execution of test cases 3 and 4. To do so, I printed the name of the current thread at different places of the code. I saw that modifications through the JTextField are run on thread "AWT-EventQueue-0" (this is the Event Dispatch Thread of Swing, isn't it?), and that modifications through the View are run on thread "Thread-0". In my implementation, "Thread-0" is the thread on which I run the GraphStream's pumping loop, to wait for events occurred in the GraphStream's Viewer thread and to copy back them inside "Thread-0". From what I understood of GraphStream's documentation:

  • the GraphStream's Viewer always runs in the Event Dispatch Thread (EDT) of Swing,
  • the GraphStream's Graph associated to a GraphStream's Viewer can be accessed either from the EDT or from another thread, depending on the GraphStream's ThreadingModel used,
  • it is more powerful to access the GraphStream's Graph from another thread than the EDT, because it allows running algorithms on the graph in parallel with the GraphStream's Viewer.

Did I understand well the documentation?
In my implementation, I chose to access the GraphStream's Graph from another thread than the Swing one. Thus, I deduce from my previous runs of test cases 3 and 4 that:

  • the update of the GraphStream's View from the EDT doesn't prevent a future update of the GraphStream's View from "Thread-0" (test case 3),
  • but the update of the GraphStream's View from "Thread-0" blocks any future update of the GraphStream's View from the EDT (test case 4).

I have the impression that I'm doing something wrong with all these threads. Could you help me please?

I tried to make a minimum working example (MWE) to reproduce my problem. Here is the content of the Java source file NodeSyncTest.java:

package mwe;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;

import org.apache.commons.lang3.math.NumberUtils;
import org.graphstream.graph.Graph;
import org.graphstream.graph.Node;
import org.graphstream.graph.implementations.MultiGraph;
import org.graphstream.ui.graphicGraph.GraphicGraph;
import org.graphstream.ui.graphicGraph.GraphicNode;
import org.graphstream.ui.swing_viewer.SwingViewer;
import org.graphstream.ui.view.View;
import org.graphstream.ui.view.Viewer;
import org.graphstream.ui.view.ViewerListener;
import org.graphstream.ui.view.ViewerPipe;

class NodeSyncTest {
	
	public static void main(String[] args) {
		System.out.println("NodeSyncTest.main : " + Thread.currentThread().getName());
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			@Override
            public void run() {
				System.out.println("SwingUtilities.invokeLater.Runnable.run : " + Thread.currentThread().getName());
				new NodeSyncTest();
            }
        });
	}
	
	NodeSyncTest() {
				
		Map<String, MyNode> myNodes = Map.of(
			    "fixed_top_left", new MyNode(-2, 2),
			    "fixed_top_right", new MyNode(2, 2),
			    "fixed_bottom_left", new MyNode(-2, -2),
			    "fixed_bottom_right", new MyNode(2, -2),
			    "unfixed", new MyNode(0, 0)
			);

		GraphStreamControl graphStreamControl = new GraphStreamControl(myNodes);
		
		JTextFieldsControl jTextFieldsControl = new JTextFieldsControl(myNodes);
		
		graphStreamControl.jTextFieldsControl = jTextFieldsControl;
		jTextFieldsControl.graphStreamControl = graphStreamControl;
		
		graphStreamControl.fillGraphStreamGraph();
		
		JFrame mainDialog = new JFrame();
		
		mainDialog.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		mainDialog.setSize(300, 300);

		mainDialog.getContentPane().add((Component) graphStreamControl.view, BorderLayout.CENTER);
		mainDialog.getContentPane().add(jTextFieldsControl.panel, BorderLayout.SOUTH);
		
		mainDialog.setLocationRelativeTo(null);
		
		mainDialog.setVisible(true);
		
		graphStreamControl.startPumpLoop();
	}
	
	 class GraphStreamControl {
		 
		 Map<String, MyNode> myNodes;
		 
		 MyNode myUnfixedNode;
		 
		 Graph graphStreamGraph;
		 
		 Viewer viewer;
		 
		 View view;
		 
		 ViewerPipe viewerPipe;
		 
		 JTextFieldsControl jTextFieldsControl;
		
		 GraphStreamControl(Map<String, MyNode> myNodes) {
			 
			this.myNodes = myNodes;
			
			myUnfixedNode = myNodes.get("unfixed");
						
			System.setProperty("org.graphstream.ui", "swing");
			
			graphStreamGraph = new MultiGraph("");
			
			viewer = new SwingViewer(graphStreamGraph, Viewer.ThreadingModel.GRAPH_IN_ANOTHER_THREAD);
			viewer.disableAutoLayout();
			view = viewer.addDefaultView(false);
			viewerPipe = viewer.newViewerPipe();
			viewerPipe.addSink(graphStreamGraph);
			
			viewerPipe.addViewerListener(new ViewerListener() {

				@Override
				public void viewClosed(String viewName) {}

				@Override
				public void buttonPushed(String id) {}

				@Override
				public void buttonReleased(String id) {
					System.out.println("ViewerListener.buttonReleased : " + Thread.currentThread().getName());
					if ("unfixed".equals(id)) {
						GraphicGraph graphicGraph = viewer.getGraphicGraph();
						GraphicNode unfixedGraphStreamNode = (GraphicNode) graphicGraph.getNode("unfixed");
						myUnfixedNode.x = unfixedGraphStreamNode.getX();
						myUnfixedNode.y = unfixedGraphStreamNode.getY();
						jTextFieldsControl.update();
					}
				}

				@Override
				public void mouseOver(String id) {}

				@Override
				public void mouseLeft(String id) {}

			});

		}
		 
		public void fillGraphStreamGraph() {
			for (var entry : myNodes.entrySet()) {
			    String nodeId = entry.getKey();
			    MyNode myNode = entry.getValue();
			    Node graphStreamNode = graphStreamGraph.addNode(nodeId);
			    graphStreamNode.setAttribute("xy", myNode.x, myNode.y);
			    graphStreamNode.setAttribute("ui.label", nodeId);
			    graphStreamNode.setAttribute("ui.style", "text-alignment: under;");
			}
			
		}

		void startPumpLoop() {
			new Thread(() -> {
				System.out.println("GraphStreamControl.startPumpLoop : " + Thread.currentThread().getName());
				while (true) {
					try {
						viewerPipe.blockingPump();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}).start();
		}

		void update() {
			GraphicGraph graphicGraph = viewer.getGraphicGraph();
			GraphicNode unfixedGraphStreamNode = (GraphicNode) graphicGraph.getNode("unfixed");
			unfixedGraphStreamNode.setAttribute("xy", myUnfixedNode.x, myUnfixedNode.y);	
		}
		
	}
	 
	class JTextFieldsControl {
		
		Map<String, MyNode> myNodes;
		
		MyNode myUnfixedNode;
		
		JPanel panel;
		
		JTextField xTextField;
		
		JTextField yTextField;
		
		GraphStreamControl graphStreamControl;
		
		JTextFieldsControl(Map<String, MyNode> myNodes) {
			
			this.myNodes = myNodes;
			
			myUnfixedNode = myNodes.get("unfixed");
									
			panel = new JPanel(new GridLayout(1, 4));
			
			JLabel xLabel = new JLabel("X:", SwingConstants.RIGHT);
			xLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
			panel.add(xLabel);
			xTextField = new JTextField(3);
			xTextField.setHorizontalAlignment(SwingConstants.RIGHT);
			xTextField.setText(Double.toString(myUnfixedNode.x));
			xTextField.getCaret().setDot(0);
			xTextField.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent event) {
					System.out.println("JTextFieldsControl - actionPerformed on xTextField : " + Thread.currentThread().getName());
					String xNodeString = xTextField.getText();
					double xNodeDouble = NumberUtils.toDouble(xNodeString);
					myUnfixedNode.x = xNodeDouble;
					graphStreamControl.update();
					
				}
			});
			panel.add(xTextField);
			
			JLabel yLabel = new JLabel("Y:", SwingConstants.RIGHT);
			yLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
			panel.add(yLabel);
			yTextField = new JTextField(3);
			yTextField.setHorizontalAlignment(SwingConstants.RIGHT);
			yTextField.setText(Double.toString(myUnfixedNode.y));
			yTextField.getCaret().setDot(0);
			yTextField.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent event) {
					System.out.println("JTextFieldsControl - actionPerformed on yTextField : " + Thread.currentThread().getName());
					String yNodeString = yTextField.getText();
					double yNodeDouble = NumberUtils.toDouble(yNodeString);
					myUnfixedNode.y = yNodeDouble;
					graphStreamControl.update();
				}
			});
			panel.add(yTextField);
		}
		
		void update() {
			String xNodeString = Double.toString(myUnfixedNode.x);
			xTextField.setText(xNodeString);
			xTextField.getCaret().setDot(0);
			String yNodeString = Double.toString(myUnfixedNode.y);
			yTextField.setText(yNodeString);
			yTextField.getCaret().setDot(0);
		}
	}
	
	class MyNode {
		double x;
		double y;
		MyNode(double x, double y) {
			this.x = x;
			this.y = y;
		}
	}
	
}

and here is the one of the the Maven POM file pom.xml to build an executable JAR including all dependencies:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>mwe</groupId>
	<artifactId>mwe</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<name>MWE</name>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>21</maven.compiler.source>
		<maven.compiler.target>21</maven.compiler.target>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.14.0</version>
		</dependency>
		<dependency>
			<groupId>org.graphstream</groupId>
			<artifactId>gs-core</artifactId>
			<version>2.0</version>
		</dependency>
		<dependency>
			<groupId>org.graphstream</groupId>
			<artifactId>gs-ui-swing</artifactId>
			<version>2.0</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<!-- Build an executable JAR -->
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jar-plugin</artifactId>
				<version>3.1.0</version>
				<configuration>
					<archive>
						<manifest>
							<addClasspath>true</addClasspath>
							<!-- here we specify that we want to use the main
							method within the App class -->
							<mainClass>mwe.NodeSyncTest</mainClass>
						</manifest>
					</archive>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-assembly-plugin</artifactId>
				<configuration>
					<archive>
						<manifest>
							<mainClass>mwe.NodeSyncTest</mainClass>
						</manifest>
					</archive>
					<descriptorRefs>
						<descriptorRef>jar-with-dependencies</descriptorRef>
					</descriptorRefs>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

To use these two files, just create a folder mwe/, put NodeSyncTest.java into mwe/src/main/java/mwe/ and pom.xml into mwe/, and, in mwe/, run
mvn compile assembly:single
and
java -jar target/mwe-0.0.1-SNAPSHOT-jar-with-dependencies.jar
Here is the full project folder:
MWE.zip

EDIT: I just posted my question on Stack Overflow (https://stackoverflow.com/q/77913500/3890496) to reach more readers. I will update this issue if I get answer from there.

Problem with GraphicElement.setAttribute("xy", ...)

Hello,
I created a JFrame composed of one View and two JTextField:
1
The View displays five nodes: four are just landmarks and are not expected to be moved by the user ("fixed_*"), and one is there to be moved by the user ("unfixed"). The two JTextField show the coordinates of the "unfixed" node. Both the View and the JTextField have to be synchronized with each other. Indeed, when the user moves the "unfixed" node in the View, then the two JTextField have to be updated accordingly :
2
Reciprocally, when the user modifies the coordinate in one of the JTextField, then the View has to be updated accordingly too:
3

Here is the code:

package mwe;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingWorker;

import org.apache.commons.lang3.math.NumberUtils;
import org.graphstream.graph.Graph;
import org.graphstream.graph.Node;
import org.graphstream.graph.implementations.MultiGraph;
import org.graphstream.ui.graphicGraph.GraphicGraph;
import org.graphstream.ui.graphicGraph.GraphicNode;
import org.graphstream.ui.swing_viewer.SwingViewer;
import org.graphstream.ui.view.View;
import org.graphstream.ui.view.Viewer;
import org.graphstream.ui.view.ViewerListener;
import org.graphstream.ui.view.ViewerPipe;

class NodeSyncTest2 {
	
	public static void main(String[] args) {
		new NodeSyncTest2();
	}
	
	NodeSyncTest2() {
				
		Map<String, MyNode> myNodes = Map.of(
			    "fixed_top_left", new MyNode(-2, 2),
			    "fixed_top_right", new MyNode(2, 2),
			    "fixed_bottom_left", new MyNode(-2, -2),
			    "fixed_bottom_right", new MyNode(2, -2),
			    "unfixed", new MyNode(1, 2)
			);

		GraphStreamControl graphStreamControl = new GraphStreamControl(myNodes);
		
		JTextFieldsControl jTextFieldsControl = new JTextFieldsControl(myNodes);
		
		graphStreamControl.jTextFieldsControl = jTextFieldsControl;
		jTextFieldsControl.graphStreamControl = graphStreamControl;
		
		graphStreamControl.fillGraphStreamGraph();
		
		JFrame mainDialog = new JFrame();
		
		mainDialog.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		mainDialog.setSize(300, 300);

		mainDialog.getContentPane().add((Component) graphStreamControl.view, BorderLayout.CENTER);
		mainDialog.getContentPane().add(jTextFieldsControl.panel, BorderLayout.SOUTH);
		
		mainDialog.setLocationRelativeTo(null);
		
		mainDialog.setVisible(true);
		
		graphStreamControl.startPumpLoop();
	}
	
	 class GraphStreamControl {
		 
		 Map<String, MyNode> myNodes;
		 
		 MyNode myUnfixedNode;
		 
		 Graph graphStreamGraph;
		 
		 Viewer viewer;
		 
		 View view;
		 
		 ViewerPipe viewerPipe;
		 
		 JTextFieldsControl jTextFieldsControl;
		
		 GraphStreamControl(Map<String, MyNode> myNodes) {
			 
			this.myNodes = myNodes;
			
			myUnfixedNode = myNodes.get("unfixed");
						
			System.setProperty("org.graphstream.ui", "swing");
			
			graphStreamGraph = new MultiGraph("");
			
			viewer = new SwingViewer(graphStreamGraph, Viewer.ThreadingModel.GRAPH_IN_GUI_THREAD);
			viewer.disableAutoLayout();
			view = viewer.addDefaultView(false);
			viewerPipe = viewer.newViewerPipe();
			viewerPipe.addSink(graphStreamGraph);
			
			viewerPipe.addViewerListener(new ViewerListener() {

				@Override
				public void viewClosed(String viewName) {}

				@Override
				public void buttonPushed(String id) {}

				@Override
				public void buttonReleased(String id) {
					if ("unfixed".equals(id)) {
						GraphicGraph graphicGraph = viewer.getGraphicGraph();
						GraphicNode unfixedGraphStreamNode = (GraphicNode) graphicGraph.getNode("unfixed");
						myUnfixedNode.x = unfixedGraphStreamNode.getX();
						myUnfixedNode.y = unfixedGraphStreamNode.getY();
						jTextFieldsControl.update();
					}
				}

				@Override
				public void mouseOver(String id) {}

				@Override
				public void mouseLeft(String id) {}

			});

		}
		 
		public void fillGraphStreamGraph() {
			for (var entry : myNodes.entrySet()) {
			    String nodeId = entry.getKey();
			    MyNode myNode = entry.getValue();
			    Node graphStreamNode = graphStreamGraph.addNode(nodeId);
			    
			    // graphStreamNode.setAttribute("x", myNode.x); // WORKS! 
			    // graphStreamNode.setAttribute("y", myNode.y); // WORKS!
			    
			    // graphStreamNode.setAttribute("xyz", myNode.x, myNode.y, 0.0); // WORKS!
			    
			    graphStreamNode.setAttribute("xy", myNode.x, myNode.y); // WORKS! 
			    
			    graphStreamNode.setAttribute("ui.label", nodeId);
			    graphStreamNode.setAttribute("ui.style", "text-alignment: under;");
			}
			
		}

		void startPumpLoop() {

			SwingWorker<Void, Void> swingWorker = new SwingWorker<Void, Void>() {

				@Override
				protected Void doInBackground() throws Exception {
					while (true) {
						try {
							viewerPipe.blockingPump();
						} catch (InterruptedException e) {
							e.printStackTrace();
							return null;
						}
					}
				}
			};
			swingWorker.execute();
		}

		void update() {
			Node unfixedGraphStreamNode = graphStreamGraph.getNode("unfixed");
			
		    // unfixedGraphStreamNode.setAttribute("x", myUnfixedNode.x); // WORKS! 
		    // unfixedGraphStreamNode.setAttribute("y", myUnfixedNode.y); // WORKS! 
		    
		    // unfixedGraphStreamNode.setAttribute("xyz", myUnfixedNode.x, myUnfixedNode.y, 0.0); // WORKS! 
		    
		    unfixedGraphStreamNode.setAttribute("xy", myUnfixedNode.x, myUnfixedNode.y); // DOES NOT WORK!
		}
		
	}
	 
	class JTextFieldsControl {
		
		Map<String, MyNode> myNodes;
		
		MyNode myUnfixedNode;
		
		JPanel panel;
		
		JTextField xTextField;
		
		JTextField yTextField;
		
		GraphStreamControl graphStreamControl;
		
		JTextFieldsControl(Map<String, MyNode> myNodes) {
			
			this.myNodes = myNodes;
			
			myUnfixedNode = myNodes.get("unfixed");
									
			panel = new JPanel(new GridLayout(1, 4));
			
			JLabel xLabel = new JLabel("X:", SwingConstants.RIGHT);
			xLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
			panel.add(xLabel);
			xTextField = new JTextField(3);
			xTextField.setHorizontalAlignment(SwingConstants.RIGHT);
			xTextField.setText(Double.toString(myUnfixedNode.x));
			xTextField.getCaret().setDot(0);
			xTextField.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent event) {
					String xNodeString = xTextField.getText();
					double xNodeDouble = NumberUtils.toDouble(xNodeString);
					myUnfixedNode.x = xNodeDouble;
					graphStreamControl.update();
					
				}
			});
			panel.add(xTextField);
			
			JLabel yLabel = new JLabel("Y:", SwingConstants.RIGHT);
			yLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
			panel.add(yLabel);
			yTextField = new JTextField(3);
			yTextField.setHorizontalAlignment(SwingConstants.RIGHT);
			yTextField.setText(Double.toString(myUnfixedNode.y));
			yTextField.getCaret().setDot(0);
			yTextField.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent event) {
					String yNodeString = yTextField.getText();
					double yNodeDouble = NumberUtils.toDouble(yNodeString);
					myUnfixedNode.y = yNodeDouble;
					graphStreamControl.update();
				}
			});
			panel.add(yTextField);
		}
		
		void update() {
			String xNodeString = Double.toString(myUnfixedNode.x);
			xTextField.setText(xNodeString);
			xTextField.getCaret().setDot(0);
			String yNodeString = Double.toString(myUnfixedNode.y);
			yTextField.setText(yNodeString);
			yTextField.getCaret().setDot(0);
		}
	}
	
	class MyNode {
		double x;
		double y;
		MyNode(double x, double y) {
			this.x = x;
			this.y = y;
		}
	}
	
}

and here is the one of the Maven POM file pom.xml to build an executable JAR including all dependencies:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>mwe</groupId>
	<artifactId>mwe</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<name>MWE</name>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>21</maven.compiler.source>
		<maven.compiler.target>21</maven.compiler.target>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.14.0</version>
		</dependency>
		<dependency>
			<groupId>org.graphstream</groupId>
			<artifactId>gs-core</artifactId>
			<version>2.0</version>
		</dependency>
		<dependency>
			<groupId>org.graphstream</groupId>
			<artifactId>gs-ui-swing</artifactId>
			<version>2.0</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<!-- Build an executable JAR -->
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jar-plugin</artifactId>
				<version>3.1.0</version>
				<configuration>
					<archive>
						<manifest>
							<addClasspath>true</addClasspath>
							<!-- here we specify that we want to use the main
							method within the App class -->
							<mainClass>mwe.NodeSyncTest2</mainClass>
						</manifest>
					</archive>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-assembly-plugin</artifactId>
				<configuration>
					<archive>
						<manifest>
							<mainClass>mwe.NodeSyncTest2</mainClass>
						</manifest>
					</archive>
					<descriptorRefs>
						<descriptorRef>jar-with-dependencies</descriptorRef>
					</descriptorRefs>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

(to use these two files, just create a folder mwe/, put NodeSyncTest2.java into mwe/src/main/java/mwe/ and pom.xml into mwe/, and, in mwe/, run
mvn compile assembly:single
and
java -jar target/mwe-0.0.1-SNAPSHOT-jar-with-dependencies.jar ;
here is the full project folder: MWE.zip)

My issue: in the method GraphStreamControl.update(), the use of

unfixedGraphStreamNode.setAttribute("x", myUnfixedNode.x);
unfixedGraphStreamNode.setAttribute("y", myUnfixedNode.y);

or of

unfixedGraphStreamNode.setAttribute("xyz", myUnfixedNode.x, myUnfixedNode.y, 0.0);

updates the graph correctly. On the contrary, the use of

unfixedGraphStreamNode.setAttribute("xy", myUnfixedNode.x, myUnfixedNode.y);

doesn't update the graph. Why ?

Also, in the method GraphStreamControl.fillGraphStreamGraph(), each of

unfixedGraphStreamNode.setAttribute("x", myUnfixedNode.x);
unfixedGraphStreamNode.setAttribute("y", myUnfixedNode.y);

,

unfixedGraphStreamNode.setAttribute("xyz", myUnfixedNode.x, myUnfixedNode.y, 0.0);

and

unfixedGraphStreamNode.setAttribute("xy", myUnfixedNode.x, myUnfixedNode.y);

perfectly works. Why?

Allow more control over cubic-curve styled edges

Currently, when the edges of a graph are styled with
shape: cubic-curve;
they are curved in a seemingly arbitrary way and it rarely looks how I want.
For example, here is a graph where the edges are styled with cubic-curve:
image
Note that there are discontinuities at the nodes because the Bezier control vectors are either horizontal or vertical and determined by the angle between the source and destination vertices.

What I propose is a simple backward compatible change that would allow optionally specifying a control point for an edge that would determine the quadratic Bezier to use for its curvature like this:
image

If an edge has the "ui.control-point" attribute set, then the edge renderer will draw a quadratic Bezier curve using that control point. The implementation is simple, and will give a lot of extra control. I will make a PR.

After adding the control point, I can get the curved edges to look they way that I want.
image

Labels with multiple lines don't work

When doing something like node.setAttribute("label", "Line 1\nLine2") I expected to get Line 1 and Line 2 on two separate lines. Instead I got the lines concatenated on the same line (Line1Line2). This works as expected with the JavaFX renderer but not with the Swing renderer.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.