Reputation: 359
I'm developing a Java application and I'm using the JUNG library.
In my application I first create a DelegateTree
and draw it to the screen:
public static GraphZoomScrollPane generateTree(Tree tree,
GraphicalUserInterface gui) {
/* Create a new tree */
edu.uci.ics.jung.graph.Tree<Node, Edge> graphTree = new DelegateTree<Node, Edge>();
/* Add all nodes and vertices to the tree */
graphTree.addVertex(tree.getRoot());
addChildren(tree.getRoot(), graphTree);
/* Create the visualization */
TreeLayout<Node, Edge> treeLayout = new TreeLayout<Node, Edge>(graphTree);
VisualizationViewer<Node, Edge> vv = new VisualizationViewer<Node, Edge>(treeLayout);
vv.setBackground(Color.WHITE);
vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller<Edge>());
vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line<Node, Edge>());
vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<Node>());
vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.S);
vv.addGraphMouseListener(new ClickNode(gui, vv));
final DefaultModalGraphMouse<Node, Edge> graphMouse = new DefaultModalGraphMouse<Node, Edge>();
graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING);
vv.setGraphMouse(graphMouse);
return new GraphZoomScrollPane(vv);
}
Afterwards the user is able to add new children to the leaves of my tree. But when I just do
graphTree.addEdge(edge, parent, child);
and then redraw the VisualizationViewer
, the visualization lost the 'Tree' structure. It just adds the child somewhere above the parent and all other children of that new child right on top of it.
Is there a better way to dynamically add children to the leaves of my tree? Or do I have to use something else for redrawing instead of just vv.repaint()
?
Any help would really be appreciated.
An example of what happens:
http://www.dylankiss.be/JUNGExample.PNG
Starting with just the root (OUTLOOK), after adding 3 children (Leaf, Leaf, Leaf) with different edges (sunny, overcast, rainy), they just appear on top of each other.
EDIT: This is the addChildren()
method.
private static void addChildren(Node node, edu.uci.ics.jung.graph.Tree<Node, Edge> tree) {
for (int i = 0; i < node.getChildren().size(); i++) {
tree.addEdge(new Edge(node.getChildren().get(i).getParentValue()), node, node.getChildren().get(i));
addChildren(node.getChildren().get(i), tree);
}
}
EDIT 2: This is the part of an AWT ActionListener where I add new children to the tree.
while (there are still edges to be added) {
value = name of new edge;
child = new Node(this.m_node, value);
this.m_node.addChild(child);
graphTree.addEdge(new Edge(value), this.m_node, child);
}
Upvotes: 3
Views: 1734
Reputation: 197
Posting the method that is in charge of adding new edges would help here :)
But at first glance, it seems that you are adding 3 different edges between the same two nodes (OUTLOOK and Leaf). I'm guessing you are doing this (or the equivalent with Node and Edge instances):
graphTree.addChild("sunny", "OUTLOOK", "Leaf");
graphTree.addChild("overcast", "OUTLOOK", "Leaf");
graphTree.addChild("rainy", "OUTLOOK", "Leaf");
In this case, as JUNG graphs maintain unicity of nodes, you end up with only two nodes, and 3 different edges between them. When JUNG tries to display this graph, you will get the two nodes and the 3 overlapping edges as you used EdgeShape.Line.
If you original goal was indeed to set 3 different edges between two nodes, try using different edge shapes to avoid overlapping and get a better rendering, e.g. EdgeShape.BentLine or such.
If you wanted 3 different nodes, you will have to use 3 different names, or 3 different Node instances which are not equals.
Good luck :)
EDIT:
Following your comment, I took a look at the TreeLayout sources, and there is a small issue which makes it impossible to dynamically update the layout.
To fix the problem, use this class instead:
import edu.uci.ics.jung.algorithms.layout.TreeLayout;
import java.awt.Point;
import java.util.Collection;
import edu.uci.ics.jung.graph.Forest;
import edu.uci.ics.jung.graph.util.TreeUtils;
public class DynamicTreeLayout<V, E>
extends TreeLayout<V, E>
{
public DynamicTreeLayout(Forest<V, E> g) {
this(g, DEFAULT_DISTX, DEFAULT_DISTY);
}
public DynamicTreeLayout(Forest<V, E> g, int distx) {
this(g, distx, DEFAULT_DISTY);
}
public DynamicTreeLayout(Forest<V, E> g, int distx, int disty) {
super(g, distx, disty);
}
protected void buildTree() {
alreadyDone.clear(); // This was missing and prevented the layout to update positions
this.m_currentPoint = new Point(20, 20);
Collection<V> roots = TreeUtils.getRoots(graph);
if (roots.size() > 0 && graph != null) {
calculateDimensionX(roots);
for (V v : roots) {
calculateDimensionX(v);
m_currentPoint.x += this.basePositions.get(v) / 2 + this.distX;
buildTree(v, this.m_currentPoint.x);
}
}
}
private int calculateDimensionX(V v) {
int localSize = 0;
int childrenNum = graph.getSuccessors(v).size();
if (childrenNum != 0) {
for (V element : graph.getSuccessors(v)) {
localSize += calculateDimensionX(element) + distX;
}
}
localSize = Math.max(0, localSize - distX);
basePositions.put(v, localSize);
return localSize;
}
private int calculateDimensionX(Collection<V> roots) {
int localSize = 0;
for (V v : roots) {
int childrenNum = graph.getSuccessors(v).size();
if (childrenNum != 0) {
for (V element : graph.getSuccessors(v)) {
localSize += calculateDimensionX(element) + distX;
}
}
localSize = Math.max(0, localSize - distX);
basePositions.put(v, localSize);
}
return localSize;
}
}
You will also need to add the following if you want the layout to be updated and the viewer to be repainted for each modification of your graph:
layout.setGraph(g);
vv.repaint();
Upvotes: 3