Obtaining nice layouts programmatically
Closed this issue · 3 comments
Hi,
I'd like to be able to layout a graph in the same nice way the default UI does when autoLayout
is true
.
I tried to mimick what LayoutRunner
does, by:
Creating a Graph
, adding it as a sink for a RandomEuclideanGenerator
, then creating a SpringBox
layout, and adding the graph as a sink for the graph.
Once done, I generate 400 nodes using generator.nextEvents()
. At each generation, I also run:
while (layout.stabilization < 1) {
layout.compute()
}
(it's Kotlin code, but I can translate to Java if it's clearer for you)
This seems similar to what LayoutRunner does (besides taking a nap between multiple compute() calls, but I guess that is not to make the system uselessly slow when reshaping the displayed graph).
However, I must be doing something very wrong, as my layout appears to be:
while the autolayout for the same graph (exactly the same, I'm seeding the rng):
The only difference I can see from code is a call to Graph.replay()
done from the UI, but the documentation says to avoid doing so. What's the suggested method to achieve a good layout manually?
You just have to define a new instance of the layout algorithm you like and use it :
SpringBox l = new SpringBox();
Then you can define the parameter of the layout, like the force or the stabilization point :
l.setStabilizationLimit(0);
But please keep in mind that if you want to use your instance of layout algorithm, you need to create your viewer before the display. And that implies to build your own UI. Here an easy example :
SpringBox l = new SpringBox(); // The layout algorithm
l.setStabilizationLimit(0);
Viewer viewer = new Viewer(graph, Viewer.ThreadingModel.GRAPH_IN_GUI_THREAD);
viewer.enableAutoLayout(l); // Add the layout algorithm to the viewer
// Build your UI
add(viewer.addDefaultView(false), BorderLayout.CENTER); // Your class should extends JFrame
setSize(800, 600);
setVisible(true);
I solved this way:
SingleGraph("asda").also { graph ->
val layout = SpringBox(false, Random(1))
with(LobsterGenerator(2, 10)) {
addNodeLabels(false)
setRandomSeed(0)
addSink(graph)
addSink(layout)
layout.addSink(graph)
layout.quality = 1.0
begin()
(0..nodes).forEach { _ ->
nextEvents()
}
end()
}
while (layout.stabilization < 1) {
layout.compute()
}
graph.display(false)
}
I got a question though. Nodes have properties "xyz", "x", and "y". The first two values of "xyz" do not seem to match the values of "x" and "y". Values of "xyz" seem to be what the display system is using.
I wonder what "x" and "y" mean.