How to draw a well-layouted tree w/ GEF/Draw2D

Imagine the following scenario: There is an Ecore meta-model of a Software Fault Tree. We want to create a model with EMFText and display it with a graphical editor using GEF. But, as things turn out, that is not as easy as it sounds.

Well, the first two parts, creating the model and the instantiation of it are pretty doable. Facing the third part, drawing the tree with GEF, some problems arise. The structure of GEF with EditParts, Models, Factories, Commands, Figures and so on isn’t very intuitive and tutorials are rare. Vainolos tutorial on building an editor with GEF and displaying models helpes a lot. Be careful, it uses another meta-modelling language. However, after a little try-and-error my tree was on the screen. But the layout was horrible, not balanced and misaligned.

Applying a balanced layout according to the number of it’s levels, subtrees and leafs wasn’t that easy. GEF doesn’t offer such a functionality (well, at least, i haven’t found one). Here, the problem is that GEF works with an ordered list of all elements of the modell. And without additional information1, a nice layout for the nodes of our Software Fault Tree isn’t computable. Therefore, no nice and balanced layout can be calculated. A workaround is needed.

After trying to implement a layouting algorithm that uses these queued elements and a rather complicated counting of levels, children, parents and so an (which worked, but wasn’t good looking), the following solution came to my mind:

First, we can build an internal representation of the tree (storing just ids and x,y-positions, this isn’t much overhead):

//In class SoftwareFaultTreeEditPart:
protected List getModelChildren() {

 ...

 List retVal = new ArrayList();
 SFTSoftwareFaultTree sft = (SFTSoftwareFaultTree) getModel();

 // traverse tree for nodes and connections
 startTreeAnalysis(retVal, sft);

 // build internal representation of the tree and the layout
 abegoLayouter.layout(sft);

 return retVal;
}

Second, we can use an open source layout algorithm2 to calculate the position of each tree element from this representation:

//In class AbegoLayouter:
public void layout(SFTSoftwareFaultTree sft) {
 //Required elements
 TreeForTreeLayout<SFTIdentifiable> tree = new SFTAsTreeForTreeLayout(sft.getRootFault());
 SFTNodeExtentProvider extents = new SFTNodeExtentProvider();
 DefaultConfiguration<SFTIdentifiable> config = new DefaultConfiguration<SFTIdentifiable>(50, 50);

 //representation and layout of the tree
 treeLayout = new TreeLayout<SFTIdentifiable>(tree, extents, config);

 //map for lookup
 setUpMap();
}

//Build map for lookup of coordinates
private void setUpMap() {
 for (Entry<SFTIdentifiable, Double> entry : treeLayout.getNodeBounds().entrySet()) {
  idToPartMap.put(entry.getKey().getId(), entry.getKey());
 }
}

Third, when GEF is going to draw the nodes of the tree, we just look for the position of this element in the map:

//In class BasicFaultFigure:
public SFTBasicFaultFigure(String id) {
 super();

 setLayoutManager(new XYLayout());

 this.setSize(new Dimension(ConstantsEnum.W_BASICFAULT, ConstantsEnum.H_BASICFAULT));
 Point location = abegoLayouter.getCoordinates(id);
 this.setLocation(location);
//...
}

//In class AbegoLayouter:
public Point getCoordinates(String key) {
 Map<SFTIdentifiable, Double> elements = treeLayout.getNodeBounds();
 Point res = new Point(0, 0);
 if (idToPartMap.containsKey(key)) {
 logger.trace(key + " recognized.");
 SFTIdentifiable tmp = idToPartMap.get(key);
  int x = (int) elements.get(tmp).getX();
  int y = (int) elements.get(tmp).getY();
  res.setX(x);
  res.setY(x);
 }
 else {
  logger.trace(key + " not recognized.");
 }
 return res;
}

Finally, the result is a nice and balanced tree:

b

1: The max-width, depth and other metrics of the tree and the subtrees
2: A nice and short example is provided!
Advertisements

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s