[Contents] [Index] [Help] [Retrace] [Browse <] [Browse >]

CPLY (0x43504C59)       /* Closed polygon */
OPLY (0x4F504C59)       /* Open polygon */

Polygons are the basic components of almost all 2D objects in the DR2D
FORM.  Lines, squares, circles, and arcs are all examples of DR2D
polygons.  There are two types of DR2D polygons, the open polygon (OPLY)
and the closed polygon (CPLY).  The difference between a closed and open
polygon is that the computer adds a line segment connecting the endpoints
of a closed polygon so that it is a continuous path.  An open polygon's
endpoints do not have to meet, like the endpoints of a line segment.

    struct POLYstruct {
        ULONG       ID;
        ULONG       Size;
        USHORT      NumPoints;
        IEEE        PolyPoints[2*NumPoints];
    };

The NumPoints field contains the number of points in the polygon and the
PolyPoints array contains the (X, Y) coordinates of the points of the
non-curved parts of polygons.  The even index elements are X coordinates
and the odd index elements are Y coordinates.

[ figure 3 - bezier curves 

DR2D uses Bezier cubic sections, or cubic splines, to describe curves in
polygons.  A set of four coordinates (P1 through P4) defines the shape of
a cubic spline.  The first coordinate (P1) is the point where the curve
begins.  The line from the first to the second coordinate (P1 to P2) is
tangent to the curve at the first point.  The line from P3 to P4 is
tangent to the cubic section, where it ends at P4.

The coordinates describing the cubic section are stored in the
PolyPoints[] array with the coordinates of the normal points.  DR2D
inserts an indicator point before a set of cubic section points to
differentiate a normal point from the points that describe a curve. An
indicator point has an X value of 0xFFFFFFFF.  The indicator point's Y
value is a bit field.  If this bit field's low-order bit is set, the
points that follow the indicator point make up a cubic section.

The second lowest order bit in the indicator point's bit field is the
MOVETO flag.  If this bit is set, the point (or set of cubic section
points) starts a new polygon, or subpolygon.  This subpolygon will appear
to be completely separate from other polygons but there is an important
connection between a polygon and its subpolygon. Subpolygons make it
possible to create holes in polygons.  An example of a polygon with a hole
is the letter ``O''.  The ``O'' is a filled circular polygon with a
smaller circular polygon within it.  The reason the inner polygon isn't
covered up when the outer polygon is filled is that DR2D fills are done
using the even-odd rule.

The even-odd rule determines if a point is ``inside'' a polygon by drawing
a ray outward from that point and counting the number of path segments the
ray crosses.  If the number is even, the point is outside the object and
shouldn't be filled.  Conversely, an odd number of crossings means the
point is inside and should be filled.  DR2D only applies the even-odd rule
to a polygon and its subpolygons, so no other objects are considered in
the calculations.

Taliesin, Inc. supplied the following algorithm to illustrate the format
of DR2D polygons.  OPLYs, CPLYs, AROWs, and ProVector's outline fonts all
use the same format:

    typedef union {
        IEEE num;
        LONG bits;
    } Coord;

    #define INDICATOR       0xFFFFFFFF
    #define IND_SPLINE      0x00000001
    #define IND_MOVETO      0x00000002

    /* A common pitfall in attempts to support DR2D has
            been to fail to recognize the case when an
            INDICATOR point indicates the following
            coordinate to be the first point of BOTH a
            Bezier cubic and a sub-polygon, ie. the
            value of the flag = (IND_CURVE | IND_MOVETO) */

    Coord   Temp0, Temp1;
    int     FirstPoint, i, Increment;

    /* Initialize the path */
    NewPath();
    FirstPoint = 1;

    /* Draw the path */
    i = 0;
    while( i < NumPoints ) {
        Temp0.num = PolyPoints[2*i];    Temp1.num = PolyPoints[2*i + 1];
        if( Temp0.bits == INDICATOR ) {
            /* Increment past the indicator */
            Increment = 1;
            if( Temp1.bits & IND_MOVETO ) {
                /* Close and fill, if appropriate */
                if( ID == CPLY ) {
                    FillPath();
                }
                else {
                    StrokePath();
                }

                /* Set up the new path */
                NewPath();
                FirstPoint = 1;
            }
            if( Temp1.bits & IND_CURVE ) {
                /* The next 4 points are Bezier cubic control points */
                if( FirstPoint )
                    MoveTo( PolyPoints[2*i + 2], PolyPoints[2*i + 3] );
                else
                    LineTo( PolyPoints[2*i + 2], PolyPoints[2*i + 3] );
                CurveTo(    PolyPoints[2*i + 4], PolyPoints[2*i + 5],
                            PolyPoints[2*i + 6], PolyPoints[2*i + 7],
                            PolyPoints[2*i + 8], PolyPoints[2*i + 9] );
                FirstPoint = 0;
                /* Increment past the control points */
                Increment += 4;
            }
        }
        else {
            if( FirstPoint )
                MoveTo(     PolyPoints[2*i], PolyPoints[2*i + 1] );
            else
                LineTo(     PolyPoints[2*i], PolyPoints[2*i + 1] );
            FirstPoint = 0;

            /* Increment past the last endpoint */
            Increment = 1;
        }

        /* Add the increment */
        i += Increment;
    }

    /* Close the last path */
    if( ID == CPLY ) {
        FillPath();
    }
    else {
        StrokePath();
    }