Vainolo's Blog

Extending org.eclipse.draw2d.Shape – A Short Experience

leave a comment

I am creating a GEF editor for the Object Process Methodology modeling language. The modeling language uses an isosceles triangle, resembling the one that is provided by the org.eclipse.draw2d.Triangle class. Problem is the Triangle provided by the framework cannot be extended or customized (it is always draw having base=2*height, inside the bounds of the figure).
So heck, I had to implement my own triangle class, based on the implementation provided by the framework. Just like the Triangle class, I extended the org.eclipse.draw2d.Shape class, implemented the fillShape() and outlineShape() methods (as the javacode for the class said I should do), and also the validate method, like in the org.eclipse.drawd2.Triangle class. I fired up my application and surely, my new non-isosceles triangle showed on screen. But if I moved it disappeared, appearing again when I did another operation that required a refresh on the triangle (like moving another figure above the triangle).
Looking at the original Triangle implementation, it had another overridden method which was not mentioned anywhere and I did not implement: primTranslate. Well so it happens that the setBounds method in the org.eclipse.draw2d.Figure class uses the primTranslate method to move the figure when the bounds of the figure are moved to a new origin point. So I went and coded my own primTranslate method and now everything is working fine.
So what did I learn today? First, I learned that if you extend the org.eclipse.draw2d.Shape class you should most probably override the primTranslate method. Second, extending classes is not always straightforward and must be done with care, because the base classes may have a lot of underlying functionality that is not documented. Third, you should always explain how your class should be overridden, specially if your class is part of a large framework and you expect the users of the framework to override it.
And if you want, here is the code of the generic triangle class:

package com.vainolo.phd.opm.gef.editor.figure;

import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.Shape;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;

/**
 * A triangle that uses all of its bounds to draw an isosceles triangle
 * in the figure's bounds, like this:
 * 
 *		______
 *     |  /\  |
 *     | /  \ | (bounds shown as surrounding rectangle). 
 *     |/____\|
 * 
 * The implementation is based on the {@link org.eclipse.draw2d.Triangle} implementation.
 * 
 * @author vainolo
 *
 */
public final class Triangle extends Shape {
	/** The points of the triangle. */
	protected PointList triangle = new PointList(3);
	
	/**
	 * {@inheritDoc}
	 */
	@Override public void primTranslate(int dx, int dy) {
		super.primTranslate(dx, dy);
		triangle.translate(dx, dy);
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override protected void outlineShape(Graphics graphics) {
		graphics.drawPolygon(triangle);
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override protected void fillShape(Graphics graphics) {
		graphics.fillPolygon(triangle);
	}

	/**
	 * Validates the figure, drawing a vertical isosceles triangle filling the 
	 * figure's bounds.
	 */
	@Override public void validate() {
		super.validate();
		bounds = getBounds().getCopy();
		Point top = new Point(bounds.x+bounds.width/2, bounds.y);
		Point left = new Point(bounds.x, bounds.y+bounds.height);
		Point right = new Point(bounds.x+bounds.width, bounds.y+bounds.height);
		triangle.removeAllPoints();
		triangle.addPoint(top);
		triangle.addPoint(left);
		triangle.addPoint(right);
	}
}

9/8/2011 – Update

Seems that my triangle code is not completely correct. While it draws filled triangles correctly triangles that are not filled are shown without the bottom line. Reading the code of org.eclipse.draw2d.Triangle I came with the following changes:

  1. Shrink and resize the bounds of the containing rectangle before creating the triangle (that is how it is done in their implementation).
  2. STOP using the bounds variable in the validate functions, specially since I am changing it.
    @Override public void validate() {
        super.validate();
        Rectangle r = getBounds().getCopy();
        r.shrink(getInsets());
        r.resize(-1, -1);
        Point top = new Point(r.x+r.width/2, r.y);
        Point left = new Point(r.x, r.y+r.height);
        Point right = new Point(r.x+r.width, r.y+r.height);
        triangle.removeAllPoints();
        triangle.addPoint(top);
        triangle.addPoint(left);
        triangle.addPoint(right);
    }

Written by vainolo

July 20th, 2011 at 11:38 am

Posted in Programming

Tagged with , , ,

Leave a Reply

%d bloggers like this: