Coordinate Systems
Draw2d provides a flexible coordinate with simple defaults. Different
coordinate systems are required by certain optimizations and features provided
by draw2d. A coordinate system is nothing more than an adjustment made to the
Draw2d Graphics
when painting children (coordinate systems have
no affect on the way a figure paints itself). For example, it is possible to
translate or zoom a Graphics. Both of these operations affect subsequent paint
calls. To match what you see, operations like searching for a figure at a point
must respect such coordinate changes.
The default coordinate system is very simple, it is the same for every
figure. So it doesn't matter if a figure is parented by another figure, you
could compare the bounding boxes directly to each other. When the figures paint,
they all paint in the same coordinates, and when hit testing is performed, the
location is not modified when searching recursively through the children. This
coordinate system is called absolute. If a figure uses absolute
coordinates, when it moves, it must also translate its children by the same
amount. "Absolute" may not be the best choice of words. "Inherited" is probably
more accurate, since children are inheriting the parent's coordinate system,
which could be anything.
The opposite of absolute (or inherited) coordinates is relative (also
local). In a relative coordinate system, the bounds of children are
relative to the client area of their parent. The client area is the parent's
bounds unless the parent has insets. When a figure with relative coordinates is
moved, the children come along for free, without any changes to their bounds.
Relative vs. Absolute
The default coordinate system is absolute and is often left unchanged. A
figure can easily change the coordinate system used for its children by
overriding useLocalCoordinates(). The following table shows some possible
reasons to choose either.
Task
|
Absolute Coordinates
|
Relative Coordinates
|
Translate/move a figure |
The figure and all of its children must be translated, which can be
expensive in extreme cases. |
Only the figure's bounds must be updated. The children move for
free. |
Hit-test / determine repaint regions |
No adjustments are needed to coordinates. |
Some simple math is used to adjust coordinates and rectangles
to/from the coordinate system's origin. |
Observe the figure's "location" on the Canvas |
A FigureListener can be used if the entire parent chain is using
absolute coordinates. But this guarantee is rare. |
A FigureListener and CoordinateListener must be used. You must call
translateToAsbolute on the figure being observed to get its canvas
coordinates. |
Determine the bounds of a parent based on the bounds of the children |
Easy - after the children have been position, the parent can then
figure out what its bounds should be. |
Extremely hard, since updating the parent's bounds will cause the
children to "move". |
Other Coordinate Systems
Zooming and scrolling are done specially by the viewport and scalable layered
pane figures provided with Draw2d. Scrolling a drawing is quite common, and it
would be silly if doing this required updating the bounding rectangle of every
figure in the drawing. As an optimization, a viewport can be constructed with
virtual scrolling, which is implemented by translating the coordinate
system's origin.
Zoom is done by scaling the coordinate system about the origin.
Working with Absolute Coordinates
The only types of locations which can be passed around and compared in a
meaningful way are absolute. Here, "absolute" means the top-most parent (or
root) figure's coordinate system. When Draw2d is used with SWT Canvas, an
absolute location is the location on that Canvas. To get the location on the
Display (i.e. the monitor), you would then have to use the utilities on SWT's
Display to convert from a Control to the Display.
To convert to and from absolute coordinates, utility methods are defined on
IFigure. These methods will convert any object implementing the
Translatable
interface (historical note: zoom was not in the first draw2d release so
translation was the only type of converting necessary).
- IFigure#translateToAbsolute(Translatable) - converts from the receiver's
coordinates to absolute coordinates. Note that the receiver's coordinate
system is the system in which the receiver is placed, and not the
coordinates in which it places its children.
- IFigure#translateToRelative(Translatable) - converts from absolute
coordinates to the receiver's coordinates.
The above methods are implemented recursively by walking up the parent chain
and performing the conversion for each parent. It's not easy to extend the
behavior of a recursive method, so the actual conversions are factored out into
separate methods. These methods are also public and are called in some
situations.
- IFigure#translateToParent(Translatable) - converts from the coordinate
system defined by the receiver. If the receiver uses
absolute/inherited coordinates, no conversion is performed.
- IFigure#translateFromParent(Translatable) - converts to the coordinate
system defined by the receiver.
Example: How to place a Shell on top of a Figure
Given a figure that is displayed on a Canvas, how do we popup a shell
such that it is in exactly the same location on the screen?
Solution:
First, take the figure's location and convert it to the canvas. Note
that the figure's bounds are returned by reference, so you need to
create a copy to avoid modifying the original bounds. Next, create an
SWT Rectangle and convert it to the display's coordinates:
public void example1(Shell shell, Figure figure, Canvas canvas) {
org.eclipse.draw2d.geometry.Rectangle r;
org.eclipse.swt.graphics.Rectangle swtRect;
r = figure.getBounds().getCopy();
figure.translateToAbsolute(r);
swtRect = new org.eclipse.swt.graphics.Rectangle(r.x, r.y, r.width, r.height);
shell.setBounds(canvas.getDisplay().map(canvas, null, swtRect));
}
Example: How to Position a Connection using an Anchor
Given a ConnectionAnchor, what is the proper way to set the endpoint
of a connection?
Solution:
A connection anchor is a helper object that returns a Point in
absolute coordinates. The connection figure can be inside a figure such
as a viewport or layer that has its own coordinate system. The following
code makes the necessary conversion:
public void example2(Connection connection,
ConnectionAnchor sourceAnchor, Point reference)
{
Point anchorpoint = sourceAnchor.getLocation(reference);
connection.translateToRelative(anchorpoint);
PointList list = connection.getPoints();
list.setPoint(anchorpoint, 0);
connection.setPoints(list);
}