I am using Jung to draw a graph. In my case, a node can have both multiple parents and multiple child.
Since, a node can have multiple parents, I can not use a Tree layout to get my graph drawn as a Tree.
Thus, I am using a DAGLayout. However, in DAGLayout, the plotting is bottom-up.
To resolve this, I have rotated the graph 180 degrees to plot it from top to bottom.
Here is the code:
// generate layout
Layout<SimpleTaskDomain, Integer> layout = new DAGLayout<SimpleTaskDomain, Integer>(g);
// fix layout size
layout.setSize(new Dimension(700, 700));
VisualizationViewer<SimpleTaskDomain, Integer> vv = new VisualizationViewer<SimpleTaskDomain, Integer>(layout);
vv.setBackground(Color.white);
vv.setPreferredSize(new Dimension(800, 800));
vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<SimpleTaskDomain>());
Dimension d = layout.getSize();
Point2D center = new Point2D.Double(d.width / 2, d.height / 2);
vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).rotate(Math.PI, center);
vv.setVertexToolTipTransformer(vv.getRenderContext().getVertexLabelTransformer());
// vv.getRenderContext().setEdgeShapeTransformer(new
// EdgeShape.Orthogonal<SimpleTaskDomain, Integer>());
vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line<SimpleTaskDomain, Integer>());
VertexFontTransformer<SimpleTaskDomain> vff = new VertexFontTransformer<SimpleTaskDomain>();
vff.setBold(true);
vv.getRenderContext().setVertexFontTransformer(vff);
Transformer<SimpleTaskDomain, Paint> vertexPaint = new Transformer<SimpleTaskDomain, Paint>() {
public Paint transform(final SimpleTaskDomain i) {
return Color.GREEN;
}
};
vv.getRenderContext().setVertexFillPaintTransformer(vertexPaint);
Transformer<SimpleTaskDomain, Shape> vertexSize = new Transformer<SimpleTaskDomain, Shape>() {
public Shape transform(final SimpleTaskDomain i) {
Rectangle2D.Double rectangle = new Rectangle2D.Double(-40, -20, 80, 40);
return AffineTransform.getScaleInstance(2, 2).createTransformedShape(rectangle);
}
};
vv.getRenderContext().setVertexShapeTransformer(vertexSize);
vv.getRenderer().getVertexLabelRenderer().setPosition(Position.CNTR);
I am changing shape of the vertices to a rectangle instead of default circle.
However, in the output graph, the vertices are overlapping.
How can I control the distance between vertices such that the overlap does not happen.
I want a tree like structure with proper distance between the vertices.
Thanks for reading!
Your question says "DagLayout", but your code sample says "FRLayout".
In any event, DagLayout doesn't let you specify node distance.
I suggest you follow the general outline in MinimumSpanningTreeDemo:
(1) Extract the appropriate spanning tree from your graph.
(2) Lay out the spanning tree using TreeLayout (which allows you to specify spacing).
(3) Use the layout positions from the Tree Layout to create a new StaticLayout instance for your original graph, and visualize using that layout.
Related
Disclaimer: I am using Java and Javafx 11. Just putting it out there :)
I am in the process of trying to create an Interpreter for Logo, but have run into a roadblock. You see, I defaulted to using a canvas to display all the things I needed as that is fitting for what I am doing. However, I did not account for the fact that my Turtle needed to move.
private void drawTurtle()
{
vertices[0] = new Vector2(position.x, position.y + 15); // The three points that make the triangle that is the turtle
vertices[1] = new Vector2(position.x - 15, position.y);
vertices[2] = new Vector2(position.x + 15, position.y);
vertices[1] = Renderer.rotatePoint(vertices[1], position, rotation); // applying rotation to vertices
vertices[2] = Renderer.rotatePoint(vertices[2], position, rotation);
vertices[0] = Renderer.rotatePoint(vertices[0], position, rotation);
Renderer.drawLine(vertices[2], vertices[1], currentPen); // drawing the vertices
Renderer.drawLine(vertices[2], vertices[0], currentPen);
Renderer.drawLine(vertices[1], vertices[0], currentPen);
}
Trails left due to rotating the turtle in realtime.
In order to achieve this without leaving "trails", I tried to erase the existing turtle by drawing with a white pen over it. That gave me... weird results.
This is after rotating the turtle 360 degrees.
Then I came across a post here on SO talking about how I should use a Line object on a Pane if I wanted to move stuff. And well, I tried combining it with a canvas to make a CanvasPane:
public class CanvasPane extends Pane
{
public final Canvas canvas;
public CanvasPane(double width, double height)
{
setWidth(width);
setHeight(height);
canvas = new Canvas(width, height);
getChildren().add(canvas);
canvas.widthProperty().bind(this.widthProperty()); // Change this so this canvas does not scale with the pane, and its size is constant.
canvas.heightProperty().bind(this.heightProperty());
}
}
And added line objects to this so I can then edit their start and end values to make the turtle move, but I got nothing out of it, no line to show, and I am quite confused and don't know what to do. Nothing on the great internet helped my either, so I am now asking this question to see if anyone has ideas on how I can move my turtle flawlessly. And no, I can't use clearRect()
TLDR: My turtle leaves trails when moving on a canvas, and using Line and Pane doesn't work, and I can't use clearRect() on my canvas. Help!
Use one Pane to hold both the Canvas Node and your "turtle" Node.
Canvas canvas = new Canvas(640, 480);
Shape turtle = new Polygon(); // fill in the points
Pane p = new Pane(canvas, turtle);
Now you can control the position of the turtle node by either setting the layout coordinates or applying a translation. As it was added last, it will be drawn over the Canvas. (You could also use a StackPane to make that layering more explicit.)
I'm making an animation that involves moving buttons representing data travelling in a network. So far, I've been able to move the buttons to different locations at different stages of the animation by updating their LayoutX and LayoutY fields, and by using sequential transitions.
But now I'm trying to get the button, named blue, to move in a diagonal line from its position at a stage to "router 3," which is up and to the right of the button. The exact Layout coordinates of the destination are 426(x) and 364(y), and the Layout coordinates of the starting position are 309(x) and 585(y). I've been trying to use moveTo with LineTo to get the button to the aforementioned coordinates, but it's proving difficult for two reasons:
First, I use a transition to get to the starting coordinates, so the actual coordinates of the button by the time it reaches the starting position are LayoutX: 14, LayoutY: 445, TranslateX: 295, TranslateY: 140.
I try and rectify this with the code:
blue.setLayoutX(blue.getLayoutX() + blue.getTranslateX());
blue.setLayoutY(blue.getLayoutY() + blue.getTranslateY());
blue.setTranslateX(0);
blue.setTranslateY(0);
And then define the path for blue to follow as:
Path path = new Path();
MoveTo start = new MoveTo();
start.setX(blue.getTranslateX());
start.setY(blue.getTranslateY());
path.getElements().add(start);
path.getElements().add(new LineTo(125.0f, -220.0f));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(1000));
pathTransition.setPath(path);
pathTransition.setNode(blue);
pathTransition.setCycleCount((int) 1f);
pathTransition.setAutoReverse(false);
pathTransition.play();
But this seems like a cumbersome workaround. For instance, the next stage of this program has the buttons traversing a network of multiple "routers" according to Dijkstra's algorithm, and I'd like to be able to just have a line path defined that goes straight to the next router, without having to fiddle with Translation and Layout coordinates at every stage. Alternatively, Swing, for instance, can repaint a circle while its coordinates are updated pixel by pixel, so as to make a diagonal animation and to stop after reaching a certain position. Is this possible using JavaFX?
Secondly, Even when this animation as it is implemented begins, the button seems to "jump back" slightly, as though the start of the path to be traveled is a couple pixels down and to the left of where the button actually is, even though as far as I can tell I've specified the start of the path to be exactly where the button is before the animation begins. Is there a particular reason for this?
Thank you for any help related to my question; It's my first time using StackOverflow. I was pretty sure I'd scoured for an answer to this kind of question quite thoroughly but I'm sorry if this is a duplicate.
That's a known problem, you can read about it in the Create a path transition with absolute coordinates for a StackPane object thread.
Unfortunately you have to work around that problem, there is no solution inside JavaFX. Fortunately the solution is easy, just use a modified version of MoveTo and LineTo.
Here's a modified example code for you. Just click on a position in your scene and the node will move there without "jumping".
public class PathTransitionExample extends Application {
PathTransition transition;
#Override
public void start(Stage primaryStage) throws Exception {
Group root = new Group();
// create movable object
Rectangle rect = new Rectangle(50, 50);
rect.setStroke(Color.BLUE);
rect.setFill(Color.BLUE.deriveColor(1, 1, 1, 0.3));
rect.relocate(100, 80);
root.getChildren().add(rect);
Label label = new Label("Click on scene to set destination");
label.relocate(0, 0);
root.getChildren().add(label);
// init transition
transition = new PathTransition();
transition.setNode(rect);
transition.setDuration(Duration.seconds(2));
Scene scene = new Scene(root, 1024, 768);
scene.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<Event>() {
#Override
public void handle(Event event) {
transition.stop();
setPositionFixed( rect);
double toX = ((MouseEvent) event).getX();
double toY = ((MouseEvent) event).getY();
Path path = new Path();
path.getElements().add(new MoveToAbs(rect));
path.getElements().add(new LineToAbs(rect, toX, toY));
transition.setPath(path);
transition.play();
}
// change layout to current position, reset translate
private void setPositionFixed( Node node) {
double x = rect.getLayoutX() + rect.getTranslateX();
double y = rect.getLayoutY() + rect.getTranslateY();
rect.relocate(x, y);
rect.setTranslateX(0);
rect.setTranslateY(0);
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
public static class MoveToAbs extends MoveTo {
public MoveToAbs(Node node) {
super(node.getLayoutBounds().getWidth() / 2, node.getLayoutBounds().getHeight() / 2);
}
public MoveToAbs(Node node, double x, double y) {
super(x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
}
}
public static class LineToAbs extends LineTo {
public LineToAbs(Node node, double x, double y) {
super(x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
}
}
public static void main(String[] args) {
launch(args);
}
}
I have created a java program of which starts with 1 vertex and from there it adds one vertice and 2 edges per cycle. It uses the Static Layout
Layout<Number, Number> staticLayout = new StaticLayout<Number, Number>(g, layout);
vv = new VisualizationViewer<Number, Number>(staticLayout, new Dimension(550, 550));
This is going to sound very un-technical, but the graph just doesn't look random enough, basically what i mean by this, is that every time it gets run they always seems to cluster a lot all the way around the edges of the graph, while very few get anywhere near the center. My program typically uses 100 generated verties and i will end up with half a dozen in the center and the others all round the edges.
Below is a random example that i just created just now.
Perhaps if someone could confirm that this is actually random, or if not if there is a way to get around this problem or if I've set something up wrong. As i wish to have the nodes as random as possible.
Any help would be appreciated.
Thanks
Below is the relevant code to the applet. involving its set up.
public class AnimatingAddNodeDemo extends JApplet {
//create a graph
Graph<Number, Number> ig = Graphs.synchronizedUndirectedGraph(new UndirectedSparseMultigraph<Number, Number>());
ObservableGraph<Number, Number> og = new ObservableGraph<Number, Number>(ig);
og.addGraphEventListener(new GraphEventListener<Number, Number>() {
public void handleGraphEvent(GraphEvent<Number, Number> evt) {
//System.err.println("got " + evt);
}
});
this.g = og;
//create a graphdraw
layout = new FRLayout<Number, Number>(g);
layout.setSize(new Dimension(600, 600));
setSize(700, 700);
Relaxer relaxer = new VisRunner((IterativeContext) layout);
relaxer.stop();
relaxer.prerelax();
Layout<Number, Number> staticLayout = new StaticLayout<Number, Number>(g, layout);
vv = new VisualizationViewer<Number, Number>(staticLayout, new Dimension(550, 550));
JRootPane rp = this.getRootPane();
rp.putClientProperty("defeatSystemEventQueueCheck", Boolean.TRUE);
getContentPane().setLayout(new BorderLayout());
}
Integer v_prev = null;
public void process() {
vv.getRenderContext().getPickedVertexState().clear();
vv.getRenderContext().getPickedEdgeState().clear();
try {
if (g.getVertexCount() < 100) {
//add a vertex
Integer v1 = nodeCount;
g.addVertex(v1);
nodeCount++;
System.out.println("adding vertex " + v1);
vv.getRenderContext().getPickedVertexState().pick(v1, true);
j.setText(myText);
// wire it to some edges
if (v_prev != null) {
Integer edge = edgeCount;
//vv.getRenderContext().getPickedEdgeState().pick(edge, true);
// let's connect to a random vertex, too!
int rand = (int) (Math.random() * (edgeCount-1)); // because there is a 0 node
while (v1.equals(rand)) {
System.out.println("avoided connecting to myself");
rand = (int) (Math.random() * (edgeCount-1)); // because there is a 0 node
}
edgeCount++;
g.addEdge(edge, rand, v1); //add an edge called var1, between the nodes var2 and var3
vv.getRenderContext().getPickedEdgeState().pick(edge, true);
System.out.println("Adding edge " + edge + " between " + rand + " & " + v1 + "()");
}
v_prev = v1;
layout.initialize();
Relaxer relaxer = new VisRunner((IterativeContext) layout);
relaxer.stop();
relaxer.prerelax();
vv.getRenderContext().getMultiLayerTransformer().setToIdentity();
vv.repaint();
} else {
done = true;
}
} catch (Exception e) {
System.out.println(e);
}
}
public static void main(String[] args) {
AnimatingAddNodeDemo and = new AnimatingAddNodeDemo();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(and);
and.init();
and.start();
frame.pack();
//frame.setVisible(true);
}
}
The reason your graph isn't random likely stems from the fact that you are passing a FRLayout to the constructor.
layout = new FRLayout<Number, Number>(g);
// ...
Layout<Number, Number> staticLayout = new StaticLayout<Number, Number>(g, layout);
You could make your own random layout class by extending AbstractLayout. But, according to the JavaDoc, StaticLayout will randomly layout nodes if you exclude the second constructor argument.
Layout<Number, Number> staticLayout = new StaticLayout(Number, Number>(g);
I Didn't get to a conclusion on whether or not it is random. So instead when i create each vertex i decided to set the particular co-ordinate of the vertex using layout.setLocation(v1, x, y)
With making x and y using math.random() and multiplying it by the width and height of my applet.
Therefore i now know that it is random.
EDIT
This actually seemed to work, however it actually did not, I had to remove the FRLayout.
It turns out FRLayout will not let you set your own locations because of what the algorithm does.
FRLayout is a force directed Layout that will reposition the vertices
according to the topology of the graph.
So i therefore changed the FRLayout to StaticLayout, removed a few things that worked only with FRLayout and it works correctly now.
I have 2 ellipses on a pane, one has a rotation transformation applied to it (the rotation point not being the ellipse itself obviously :)), the other doesn't. Now I need to draw a line from the center of the transformed ellipse to the center of the untransformed ellipse.
So I need the coordinates of the transformed ellipse, is there a way to retrieve those?
(I need them for other calculations besides the line drawing too)
Use localToParent method. Example:
#Override
public void start(Stage stage) {
stage.setTitle(VersionInfo.getRuntimeVersion());
Group root = new Group();
// ellypsis with center in 100,100
Arc ellypsis = ArcBuilder.create().centerX(100).centerY(100).length(360).radiusX(100).radiusY(50).fill(Color.TRANSPARENT).stroke(Color.RED).build();
// rotate
ellypsis.getTransforms().add(new Rotate(50, 50, 45));
// find out where is 100,100 in rotated ellypsis
Point2D localToParent = ellypsis.localToParent(100,100);
// draw line from that point
Line line = new Line(localToParent.getX(), localToParent.getY(), 200, 200);
root.getChildren().addAll(ellypsis, line);
stage.setScene(new Scene(root, 300, 250));
stage.show();
}
Given the lack of code provided in the question, I will give you a mathematical answer :)
Say you have ellipse1 which center is X1 and elipse2 which center is X2. The transformation gives you the following
elipse1 --> f(elipse1) = elipse1'
if you want to know the transformed ellipse ellipse1' center coordinates (X1') just do the following:
X1 --> f(X1) = X1`
So, in a nutshell, just apply the same transformation to your orginal point X1 and you will get the transformed coordinates X1'. Now all what you have to do is draw a line from X1' to X2
I need to draw a circle around a vertex in JUNG. The circle is defined by the vertex as center and a given radius r.
Something like this, I guess. This will give you points for circle with given radius. To adjust resolution of points change x+=0.01 to a bigger/smaller value as needed. To move circle centre to an arbitrary point (p,q), just add it to (x,y), that is plot(x+p,y+q);.
double radius = 3;
for (double x = -radius; x <= radius; x += 0.01) {
double y = Math.sqrt(radius * radius - x * x);
plot(x, y);//top half of the circle
plot(x, -y);//bottom half of the circle
}
EDIT: It appears that JUNG is not really an XY-plot but a network/graph framework. So all you need is to layout your points in a circle using one of provided layouts. CircleLayout and KKLayout seem to do the trick, though CircleLayout gives strange results for when there are many nodes. Here's complete sample code:
//Graph holder
Graph<Integer, String> graph = new SparseMultigraph<Integer, String>();
//Create graph with this many nodes and edges
int nodes = 30;
for (int i = 1; i <= nodes; i++) {
graph.addVertex(i);
//connect this vertext to vertex+1 to create an edge between them.
//Last vertex is connected to the first one, hence the i%nodes
graph.addEdge("Edge-" + i, i, (i % nodes) + 1);
}
//This will automatically layout nodes into a circle.
//You can also try CircleLayout class
Layout<Integer, String> layout = new KKLayout<Integer, String>(graph);
layout.setSize(new Dimension(300, 300));
//Thing that draws the graph onto JFrame
BasicVisualizationServer<Integer, String> vv = new BasicVisualizationServer<Integer, String>(layout);
vv.setPreferredSize(new Dimension(350, 350)); // Set graph dimensions
JFrame frame = new JFrame("Circle Graph");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(vv);
frame.pack();
frame.setVisible(true);
I have picked SparseMultiGraph because that's what was in JUNG tutorial. There are other types of graphs, but I am not sure what the difference is.
You could also use a StaticLayout that can take (x,y) vertices, then use my original code to plot the points, but that would not be as elegant for JUNG framework. Depends on what your requirements are, however.