Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

GEF/GEF4/Geometry

Note to non-wiki readers: This documentation is generated from the Eclipse wiki - if you have corrections or additions it would be awesome if you added them in the original wiki page.

Introduction

This is the reference documentation of the GEF 4 Geometry API. You can use this component independent of the other GEF 4 components. The Geometry API provides classes to store geometric objects and to do geometric computations on those objects. It is developed to be intuitively usable. Nonetheless, it is of no harm to know the underlying design decisions which are explained in the following chapters.

Geometric Primitives

Angle, Dimension, and Point

The API uses a small number of "primitives" which are globally used to build up, introspect and modify complex objects. These primitives are the Point, Angle and Dimension classes. Presumably, the most important class of the three is the Point class, because you will encounter it very often while working with the API. Point objects represent a point in two dimensional space.(1) From a list of Point objects, you can build up most of the planar geometry objects:

Point p0 = new Point(); // defaults: x=0, y=0
Point p1 = new Point(5, 0);
Point p2 = new Point(0, 5);
Polygon triangle = new Polygon(p0, p1, p2);

Considering rotation and the angular relationship of two straight lines, Angle objects come into play. They abstract over the two commonly used angle units, degrees and radians. The user has to specify the unit of the value an Angle object is constructed from. Moreover, the user can read the value of an Angle object in either degrees or radians. Therefore, the use of Angle objects assures that correct values are used in calculations. This indirection is done due to an inconsistency of several APIs, for example, org.eclipse.swt.graphics.Transform vs. org.eclipse.draw2d.geometry.Transform.

// creates a 75% pie chart
Pie chart = new Pie(0, 0, 100, 100, Angle.fromDeg(15), Angle.fromDeg(270));

The Dimension class is the pendant of the org.eclipse.draw2d.geometry.Dimension class. It decouples the location and the width and height of a rectangular object.

Rectangle bounds = new Rectangle(
    new Point(50, 50),
    new Dimension(80, 20)
);

(1) For the purpose of imagination, you can assume the coordinate system to be originated in the top left corner of your drawing area, expanding to the right and to the bottom.

Planar Geometry

Interface hierarchy

This diagram depicts the interface hierarchy which underlies the individual geometry classes. It classifies the geometry classes mainly into either being ICurves or IShapes. An ICurve is a one dimensional geometry, i.e. the result that you get by drawing a continuous line with a pencil. It has a start and an end point and you can approximate it by a series of Bézier curves. On the other hand, an IShape is a two dimensional geometry, i.e. it continuously encloses a region on the drawing area, without holes. You can retrieve the outline of an IShape, which is an IPolyCurve, a special case of an ICurve. It defines a curve that is composed of multiple connected ICurves and its purpose is to be able to operate on them as a whole. Similarly and especially important for clipping is another set of planar geometries, the IPolyShapes. Other than the relationship between ICurve and IPolyCurve, an IPolyShape is not an IShape. An IPolyShape is a (possibly) non-continuous set of IShapes. An example for an IPolyShape is the Region. A Region is the area that results from composing multiple Rectangles. It corresponds to the SWT Region.

This interface hierarchy structures the geometry API. It describes how the individual classes are related to each other and how to transfer objects of one type to a number of objects of other types, which is the most important part of the geometry API.

The most general type in the hierarchy is the Path, because every geometric object can be transformed into it. Unfortunately, the Path is incompatible to the rest of the API in that it does not implement the different interfaces, it does not ensure a certain precision for the results of its test and manipulation methods, and it cannot be transferred back into compatible objects. Important functionality

As you can see in this diagram, an ICurve can be approximated by a number of BezierCurves using the toBezier() method. The outline of an IShape can be retrieved using its getOutline() method. Additionally, you can be split an IShape into a number of ICurves -- which form the outline -- using the getOutlineSegments() method. IPolyShape provides a getShapes() method to get the individual IShapes that are combined by the IPolyShape. It does provide a getOutlineSegments() method, too, which is used to split the IPolyShape into several ICurves. These transfer methods allow the decomposition of any geometric object into a bunch of BezierCurves:

BezierCurves[] fromCurve = curve.toBezier();
BezierCurves[] fromShape = shape.getOutline().toBezier();
ICurve[] fromPolyShape = polyShape.getOutlineSegments();
 
// okay, need to transfer the ICurves
List<BezierCurve> beziers = new ArrayList<BezierCurve>();
for (ICurve c : fromPolyShape)
    beziers.addAll(Arrays.asList(c.toBezier()));

An important part of a geometry API in general, is the possibility to test the relationship of two geometric objects. The GEF 4 Geometry API provides four methods that perform relation tests. Universally usable is the touches() method for planar objects. It tests if two objects have at least one point in common. Additionally, ICurves can be tested for points of intersection using the intersects() method and for an overlap using the overlaps() method, among each other. An IShape provides a contains() method to test if it fully contains a given planar object. Moreover, the point test is available for every geometric object. It tests if a given Point is incidental to the particular object. Supplementary to the intersects() test, a getIntersections() method is offered among ICurves. BezierCurves do also facilitate the extraction of overlapping segments via the getOverlap() method.

So, let us consider a few examples:

boolean contained = arc.contains(point);
boolean contained = pie.contains(point);
Point[] intersections = line1.getIntersections(line2);
Point[] intersections = line.getIntersections(polygon.getOutline());
Point[] intersections = polygon.getOutline().getIntersections(roundedRectangle.getOutline());
boolean contained = ellipse.contains(line);
boolean contained = rectangle.contains(ellipse);
boolean contained = region.contains(polygon);

Noticeably, the use of interfaces unifies similar operations on different types. Therefore, the fundamental interfaces (ICurve, IShape, etc.) are complemented by three transformation interfaces.

Affine transformations

You can either transform your geometric objects via instances of the AffineTransform class, or by using the short-cut methods provided by the ITranslatable, IScalable, and IRotatable interfaces. Transformations can either be directly applied to an object, modifying the object in-place, or to a copy of the original object. This distinction is represented by the names of the short-cut methods. All names starting with 'get' are applied to a copy of the original object. The other methods modify the object in-place.

Translating an object means moving the object. You can move an object in x- and y-direction. Scaling an object means resizing the object. You can individually scale the object in x- and y-direction. Additionally, scaling requires a relative Point to scale to/away from. If you omit this Point, the scaling method will appropriately choose the relative Point. Normally, this will be the center Point of the geometric object that you want to scale. Rotation is special in that not all geometric objects can be rotated in-place. Rectangles, for example, are always parallel to the x- and y-axes. That's why the IRotatable interface does only include the getRotated() short-cut methods which are not directly applied. However, some geometric objects do provide in-place rotation methods. As with scaling, rotation is performed around a relative Point. If you omit this Point, the rotation method will appropriately choose it. Normally, this will be the center Point of the geometric object that you want to rotate.

Polygon rhomb = new Rectangle(10, 10, 10, 10).getRotated(Angle.fromDeg(45));
PolyBezier slanted = new Ellipse(100, 100, 100, 50).getRotated(Angle.fromDeg(30));
Ring rotatedClippingArea = region.getRotated(Angle.fromDeg(300));

Inheritance hierarchy

This diagram depicts the inheritance hierarchy which underlies the individual geometry classes, parallel to the interface hierarchy. It classifies the classes by their construction type. Therefore, many operations can be generalized in a few abstract classes.

Line

  • extends: BezierCurve
  • implements: ICurve, ITranslatable, IScalable, IRotatable

A Line is the straight connection of two Points:

Line line = new Line(p0, p1);

As it inherits from the BezierCurve class, all the operations for BezierCurves are available for Line objects, too. Because of its frequent use, Line overrides many of those operations to provide faster implementations for the Line/Line and Line/Point cases (equals(), touches(), contains(), intersects(), overlaps(), getIntersections(), and many more). If you want to display a Line using SWT, you can use its toSWTPointArray() method as follows:

gc.drawPolyline(line.toSWTPointArray());

Rectangle

  • extends: AbstractRectangleBasedGeometry
  • implements: IShape, ITranslatable, IScalable, IRotatable

A Rectangle is the axes-parallel rectangle defined by a location (x- and y-coordinates) and a Dimension (width and height):

Rectangle rect = new Rectangle(x, y, w, h);

Rectangle objects are frequently used, that's why some operations are overridden to provide faster implementations for designated parameter types (equals(), contains(), touches()). If you want to display a Rectangle using SWT, you can use its toSWTRectangle() method as follows:

gc.drawRectangle(rect.toSWTRectangle());

Polyline

  • extends: AbstractPointListBasedGeometry
  • implements: IPolyCurve, ITranslatable, IScalable, IRotatable

A Polyline combines multiple Line segments to address them as a whole. Consecutive Line segments of a Polyline share at least one end Point. The outline of some of the IShape implementations can be represented by a Polyline (Rectangle and Polygon):

Polyline polyLine = new Polyline(new Line(p0, p1), new Line(p1, p2));
Polyline outline = Polygon.getOutline();

To render a Polyline with SWT, you can its toSWTPointArray() method as follows:

gc.drawPolyline(polyline.toSWTPointArray());

Polygon

  • extends: AbstractPointListBasedGeometry
  • implements: IShape, ITranslatable, IScalable, IRotatable

A Polygon represents a simple polygon, i.e one that does not have intersecting sides:

Polygon rhomb = new Polygon(0, 0, 1, -1, 2, 0, 1, 1);

If you need to process self-intersecting polygons, you can use the Ring instead.

A Polygon can be rendered with SWT using its toSWTPointArray() method as follows:

gc.drawPolyline(polygon.toSWTPointArray());

Ellipse

  • extends: AbstractRectangleBasedGeometry
  • implements: IShape, ITranslatable, IScalable, IRotatable

An Ellipse is the axes-symmetric oval that can be put into an axes-parallel rectangle:

Ellipse ellipse = new Ellipse(rect);

Therefore, rotating an Ellipse does not result in another Ellipse, but in a PolyBezier which approximates the rotated Ellipse:

PolyBezier rotatedEllipse = ellipse.getRotated(Angle.fromDeg(45));

If you want to draw an Ellipse using SWT, you can directly use the GC's drawOval() method as follows:

gc.drawOval((int) ellipse.getX(), (int) ellipse.getY(), (int) ellipse.getWidth(), (int) ellipse.getHeight());

Arc

  • extends: AbstractArcBasedGeometry (which extends AbstractRectangleBasedGeometry)
  • implements: ICurve, ITranslatable, IScalable, IRotatable

An Arc is an open segment of an Ellipse. Its constructors reflect this relationship:

Arc q1 = new Arc(ellipse, Angle.fromDeg(45), Angle.fromDeg(90));

Rotating an Arc does not necessarily result in another Arc, that's why you get a PolyBezier object

Pie

  • extends: AbstractArcBasedGeometry (which extends AbstractRectangleBasedGeometry)
  • implements: IShape, ITranslatable, IScalable, IRotatable

RoundedRectangle

  • extends: AbstractRectangleBasedGeometry
  • implements: IShape, ITranslatable, IScalable, IRotatable

QuadraticCurve

  • extends: BezierCurve
  • implements: ICurve, ITranslatable, IScalable, IRotatable

CubicCurve

  • extends: BezierCurve
  • implements: ICurve, ITranslatable, IScalable, IRotatable

BezierCurve

  • extends: AbstractGeometry
  • implements: ICurve, ITranslatable, IScalable, IRotatable

PolyBezier

  • extends: AbstractGeometry
  • implements: IPolyCurve, ITranslatable, IScalable, IRotatable

Region

  • extends: AbstractPolyShape
  • implements: IPolyShape, ITranslatable, IScalable, IRotatable

Ring

  • extends: AbstractPolyShape
  • implements: IPolyShape, ITranslatable, IScalable, IRotatable

Path

  • extends: AbstractGeometry

Euclidean Geometry

Vector and Straight

Projective Geometry

Vector3D and Straight3D

Affine transformations

Affine transformations

Conversions

Conversions

From Geometry to SWT

via the toSWT...() methods

AWT2Geometry

Geometry2AWT

SWT2AWT

Tutorial

This is the documentation of the GEF4 Geometry API. You can find a small tutorial for the API here.

Back to the top