TweetFollow Us on Twitter

September 96 - Graphical Truffles: A Library for Traversing Paths

Graphical Truffles: A Library for Traversing Paths

Daniel I. Lipton

The QuickDraw GX graphics system is based on shape objects that are used by reference. An application creates a shape object such as a path or a polygon by passing in data that represents the geometric points of the shape to be drawn or otherwise manipulated. The QuickDraw GX graphics system then stores this information in its internal database and returns to the application a reference to the shape object. This reference is then passed to the various QuickDraw GX routines that perform operations on the shape.

Since the QuickDraw GX graphics system is maintaining the original data in its database, the application often won't keep this data around. Also, an application may not even have created the geometric points in the first place, as in the case of converting text into a path.

It's often desirable, for a variety of purposes, for an application to retrieve the geometric information from a shape object. Given the richness of geometric information that these objects can contain, it can be a nontrivial task to read back the information. This column describes a C library that any application can incorporate for the purpose of traversing the geometric information in QuickDraw GX paths.

WHAT'S IN A QUICKDRAW GX PATH OBJECT

The QuickDraw GX graphics system provides several types of graphics primitives with which to create visual content: lines, curves, polygons, paths, typography, and bitmaps. In this system, curves are quadratic Béziers that can be defined by three control points, the middle point being off the curve and the other two being on. Figure 1 depicts a single quadratic curve segment.

Figure 1. A QuickDraw GX quadratic curve segment

A QuickDraw GX path object is just a conglomeration of curve and line segments, resulting from an array of points. The object can contain multiple contours, each contour being a group of connected segments.

The question arises, if we look at a specific point in a path structure, whether the point is part of a line segment or part of a curve segment. The answer is that in addition to the points themselves, a path contains an array of flags, one for each point, indicating whether the point is on or off the path. To represent a single contour for a path object, QuickDraw GX uses the gxPath data structure:

struct gxPath {
   long               vectors;
   long               controlBits[1];
   struct gxPoint      vector[1];
};
typedef struct gxPath gxPath;
The vectors field is an integer that specifies the number of points in the contour, the controlBits field is a bit array representing the on-curve/off-curve flags, and the vector field is an array of points for the contour.

To represent a path object, QuickDraw GX uses the gxPaths data structure:

struct gxPaths {
   long               contours;
   struct gxPath      contour[1];
};
typedef struct gxPaths gxPaths;
The contours field is an integer specifying the number of contours, and the contour field is an array of gxPath structures, one for each contour.

Hence we have enough information to figure out what the points mean. If we see two on-path points in a row, we know that represents a line. If we see an on-path point followed by an off-path point followed by an on-path point, we know that's a quadratic curve segment.

So to read the QuickDraw GX path object to determine the actual shape, all we have to do is get a point and then get the next point. According to the previous description, it would be safe to assume that the very first point in a contour is an on-path point. Then, if the next one were also on the path, we'd have a line; if it were off, we'd know that we'd have to read a third one (which by definition would have to be on) and we'd have a curve.

The only trouble is that those assumptions aren't necessarily true. The design of QuickDraw GX could have restricted applications to using only those patterns of on-path/off-path points, disallowing two consecutive off-path points and requiring the first point and the last point in a contour to be on the path; however, it didn't.

In the interest of saving memory, QuickDraw GX allows two consecutive off-path points to imply a middle on-path point -- known as an implicit point -- exactly halfway between the off-path points. An example of this is shown in Figure 2.

Figure 2. A QuickDraw GX quadratic path

For each implicit point there's a memory savings of 8 bytes in QuickDraw GX. This allows us to define geometries in less space than would be required in other popular graphics models that use cubic Bézier curves without implicit points, but it does complicate traversing the path.

QuickDraw GX also allows the first or the last point of a contour to be off the curve, in the case where the contour is closed. This further complicates path traversal.

THE SHAPEWALKER LIBRARY

You can use the ShapeWalker library (which is included on this issue's CD) to avoid having to write a huge blob of code to deal with all those points and flags discussed above. It allows an application to pass in a QuickDraw GX shape object and be sent back (via callbacks) each line and curve segment in the shape. All implicit points are resolved by the library, so the client sees only complete line or curve segments.

The header file to be used with the library defines types for four callbacks and a prototype for the ShapeWalker function:

// Function is called to move to a new point
// (start new contour).
typedef Boolean (*TpwMovetoProc)(gxPoint *p,
                  void* refcon);

// Function is called to draw a line from
// current point to p.
typedef Boolean (*TpwLinetoProc)(gxPoint *p,
                  void* refcon);

// Function is called to draw a curve from
// current point (which will be p[0]) through 
// p[1] to p[2].
typedef Boolean (*TpwCurvetoProc)(gxPoint p[3],
                  void* refcon);

// Function is called to close a contour.
typedef Boolean (*TpwClosepathProc)
                  (void* refcon);

// Return result will be true if path walking
// was terminated by one of the callbacks.
Boolean ShapeWalker(gxShape theShape,
                     TpwMovetoProc DoMoveto,
                     TpwLinetoProc DoLineto,
                     TpwCurvetoProc DoCurveto, 
                     TpwClosepathProc DoClosepath,
                     void* refcon);
When using the library, you provide four callbacks and a refcon. Each callback will get passed the refcon and possibly point information. It's suggested that the client maintain whatever state information is necessary for the purpose at hand. The refcon can be a pointer to a structure containing the state information. One typical component of such information that most clients would need is the notion of the current point. The current point is the piece of the path we've looked at most recently, representative of the state of processing the shape. This current point should be updated as segments come through the callbacks. (We'll see this in a moment in our sample application.)

Each callback must also return a result of type Boolean, giving the client a mechanism for causing the library to terminate traversal of the shape before completion. Return false and the shape walker will continue on to the next segment; return true and it will terminate early. This can be used to catch errors in processing the points, or to terminate processing if you've finished with the shape before the last point is reached.

The four callbacks are as follows:

  • DoMoveto -- This procedure is called at the start of each new contour in the shape. It gets passed a single point and the refcon. The point identifies the location of the beginning of the contour. If the client is maintaining a current point via the refcon, it should be updated to the point passed in.

  • DoLineto -- This procedure is called for each line segment in the contour. It gets passed a single point and the refcon. The point represents the end point of the line segment. The start point of the line segment is whatever point we last saw; that will be the current point if one is being maintained. If the client is maintaining a current point via the refcon, it should be updated to the point passed in.

  • DoCurveto -- This procedure is called for each quadratic curve segment in the contour. It gets passed an array of three points and the refcon. The three points correspond to the control points of the curve. The first point in the array will be the current point if one is being maintained. The current point should then be updated to reflect the third point in the array.

  • DoClosepath -- This procedure is called at the end of every contour if the QuickDraw GX shape is closed (has the gxClosedFrameFill shape fill attribute). Closing a contour implies connecting the last point in the contour (whatever the current point is when this function is called) with the first point in the contour (the point passed into the DoMoveto function).
The code shown in Listing 1 is a sample application (SamplePathWalker.c) that converts a piece of text to a path and then uses the ShapeWalker library to read the points from the result. In this example the callback procedures are used only to print out the points in the segments, but of course they can be used to do a lot of other things as well.



Listing 1. Sample application using the ShapeWalker library
// The following structure is used to maintain a state while
// walking a shape. 
typedef struct {
   gxPoint         currentPoint;      // current point
   gxPoint         firstPoint;        // first point in contour
} TestWalkRec;

#define fix2float(x) ((double)x / 65536.0)

Boolean   TestMoveto(gxPoint *p, TestWalkRec* pWalk);
Boolean   TestMoveto(gxPoint *p, TestWalkRec* pWalk)
{
   printf("Begin new contour: %f, %f\r\n", fix2float(p->x),
            fix2float(p->y));
   pWalk->currentPoint.x = p->x;
   pWalk->currentPoint.y = p->y;
   pWalk->firstPoint.x = p->x;
   pWalk->firstPoint.y = p->y;
   return (false);
}

Boolean   TestLineto(gxPoint *p, TestWalkRec* pWalk);
Boolean   TestLineto(gxPoint *p, TestWalkRec* pWalk)
{
   printf("Line from %f, %f to %f, %f\r\n",
            fix2float(pWalk->currentPoint.x),
            fix2float(pWalk->currentPoint.y),
            fix2float(p->x), fix2float(p->y));
   pWalk->currentPoint.x = p->x;
   pWalk->currentPoint.y = p->y;
   return (false);
}
Boolean   TestCurveto(gxPoint p[3], TestWalkRec* pWalk);
Boolean   TestCurveto(gxPoint p[3], TestWalkRec* pWalk)
{
   printf("Curve from %f, %f through %f, %f, to %f, %f\r\n",
            fix2float(p[0].x), fix2float(p[0].y),
            fix2float(p[1].x), fix2float(p[1].y),
            fix2float(p[2].x), fix2float(p[2].y));
   pWalk->currentPoint.x = p[2].x;
   pWalk->currentPoint.y = p[2].y;
   return (false);
}

Boolean TestClosepath(TestWalkRec* pWalk);
Boolean TestClosepath(TestWalkRec* pWalk)
{
   printf("Closing the contour\r\n\r\n");
   pWalk->currentPoint.x = pWalk->firstPoint.x;
   pWalk->currentPoint.y = pWalk->firstPoint.y;
   return (false);
}

main()
{
   gxShape         theShape;
   gxPoint         location = {ff(100), ff(100)};
   TestWalkRec      walker;
   Boolean         result;
   
   theShape = GXNewText(5, "Hello", &location);
   GXSetShapeTextSize(theShape, ff(50));
   GXSetShapeType(theShape, gxPathType);
   GXSetShapeFill(theShape, gxClosedFrameFill);

   result = ShapeWalker(theShape, TestMoveto, TestLineto,
                         TestCurveto, TestClosepath, &walker);
   GXDisposeShape(theShape);
}

WALKING THE PATH

The files PathWalking.h and PathWalking.c are all that are required to use the ShapeWalker library in your application (for the sake of brevity, PathWalking.c isn't shown in this column). This library should make it easy for your application to process QuickDraw GX path objects. For completeness, the library will also process curve objects, line objects, rectangle objects, and polygon objects in a similar manner. All other shape types will result in the posting of the "illegal_type_for_shape" graphics error. (Graphics errors can be polled with the GXGetGraphicsError function.) The ShapeWalker library is actually based on the same code used by QuickDraw GX in its built-in GX-to-PostScript translator for printing. The library's versatility means that its uses in your application are limited only by your imagination, so get creative and try it out!

DANIEL I. LIPTON (daniel_lipton@powertalk.apple.com) has worked at Apple for seven years and is one of the original QuickDraw GX team members. In his spare time, Dan runs a small business repairing perpetual motion machines.*

Thanks to Dave Hersey, Ingrid Kelly, and Dave Polaschek for reviewing this column.*

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

DiskCatalogMaker 7.2.7 - Catalog your di...
DiskCatalogMaker is a simple disk management tool which catalogs disks. Simple, light-weight, and fast Finder-like intuitive look and feel Super-fast search algorithm Can compress catalog data for... Read more
Iridient Developer 3.2.1 - Powerful imag...
Iridient Developer (was RAW Developer) is a powerful image-conversion application designed specifically for OS X. Iridient Developer gives advanced photographers total control over every aspect of... Read more
BusyContacts 1.2.7 - Fast, efficient con...
BusyContacts is a contact manager for OS X that makes creating, finding, and managing contacts faster and more efficient. It brings to contact management the same power, flexibility, and sharing... Read more
MegaSeg 6.0.5 - Professional DJ and radi...
MegaSeg is a complete solution for pro audio/video DJ mixing, radio automation, and music scheduling with rock-solid performance and an easy-to-use design. Mix with visual waveforms and Magic... Read more
BusyContacts 1.2.7 - Fast, efficient con...
BusyContacts is a contact manager for OS X that makes creating, finding, and managing contacts faster and more efficient. It brings to contact management the same power, flexibility, and sharing... Read more
MegaSeg 6.0.5 - Professional DJ and radi...
MegaSeg is a complete solution for pro audio/video DJ mixing, radio automation, and music scheduling with rock-solid performance and an easy-to-use design. Mix with visual waveforms and Magic... Read more
Iridient Developer 3.2.1 - Powerful imag...
Iridient Developer (was RAW Developer) is a powerful image-conversion application designed specifically for OS X. Iridient Developer gives advanced photographers total control over every aspect of... Read more
iFFmpeg 6.6.1 - Convert multimedia files...
iFFmpeg is a comprehensive media tool to convert movie, audio and media files between formats. The FFmpeg command line instructions can be very hard to master/understand, so iFFmpeg does all the hard... Read more
Tor Browser Bundle 7.0.11 - Anonymize We...
The Tor Browser Bundle is an easy-to-use portable package of Tor, Vidalia, Torbutton, and a Firefox fork preconfigured to work together out of the box. It contains a modified copy of Firefox that... Read more
iShowU Instant 1.2.4 - Full-featured scr...
iShowU Instant gives you real-time screen recording like you've never seen before! It is the fastest, most feature-filled real-time screen capture tool from shinywhitebox yet. All of the features you... Read more

Latest Forum Discussions

See All

Reigns: Her Majesty guide - how to use e...
Ruling a kingdom isn't easy--doubly so for a queen whose every decision is questioned by the other factions seeking a slice of power. Reigns: Her Majesty builds on the original game's swipey tactics, adding items that you can use to move the story... | Read more »
The best new games we played this week -...
Friday has crept up on us once again, so it's time to honor the best new games we've played over the past few days. This past week was a pretty exciting one, with the debut of lots of beautiful new indies and some familiar faces returning to the... | Read more »
Portal Knights guide- beginner tips and...
Portal Knights is finally making the jump to iOS and Android, and it's already climbing the ranks to become the next big MMO experience on mobile. This sprawling sandbox game will let you pursue any adventure you wish, whether you want to sling... | Read more »
Reigns: Her Majesty guide - how to swipe...
Reigns: Her Majesty is storming the App Store this week, bringing more tinder-esque kingdom building to eager players everywhere. If you've played the original Reigns, you'll know that leading a kingdom is never easy. It's a careful balancing act... | Read more »
Getting Over It (Games)
Getting Over It 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: A game I madeFor a certain kind of person To hurt them. • Climb up an enormous mountain with nothing but a hammer and a pot.•... | Read more »
Reigns: Her Majesty (Games)
Reigns: Her Majesty 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: | Read more »
Pocket Legends Adventures guide - how to...
Pocket Legends Adventures is a fun action adventure RPG that takes control when you want it to, but also opens itself for player input, too, if you're looking to tkae a more active role in combat. Regardless of play style, the game can be quite... | Read more »
Portal Knights (Games)
Portal Knights 1.2.4 Device: iOS Universal Category: Games Price: $4.99, Version: 1.2.4 (iTunes) Description: Craft your adventure. Forge your hero. Become the ultimate Portal Knight! | Read more »
Ashworld (Games)
Ashworld 1.3 Device: iOS Universal Category: Games Price: $3.99, Version: 1.3 (iTunes) Description: | Read more »
The best deals on the App Store this wee...
Another week means another roundup of quality bargain games. This week's list features a few games we haven't featured before in our weekly roundups. If you're on the prowl for new games, now's an optimal time to try something completely new for... | Read more »

Price Scanner via MacPrices.net

Beats by Dr. Dre – BeatsX Earphones on sale f...
Best Buy has BeatsX Earphones on sale for $109, $40 off, on their online store. Sale price for online orders only. Choose free store pickup, if available, or choose free shipping. Read more
10″ 64GB WiFi Apple iPad Pros on sale for $59...
MacMall has 10.5″ 64GB Apple iPad Pros on sale for $599 including free shipping. That’s $50 off MSRP and among the lowest prices available for these iPads from any Apple reseller. Read more
15″ 2.2GHz MacBook Pros on sale for $200-$300...
B&H Photo has the 15″ 2.2GHz MacBook Pro available for $200 off MSRP including free shipping plus NY & NJ sales tax only: – 15″ 2.2GHz MacBook Pro (MJLQ2LL/A): $1799 $200 off MSRP Apple has... Read more
Holiday sale: 15″ MacBook Pros for $200-$420...
MacMall has 15″ MacBook Pros on sale for $220-$300 off MSRP, each including free shipping: – 15″ 2.8GHz MacBook Pro Space Gray (MPTR2LL/A): $2179, $220 off MSRP – 15″ 2.8GHz MacBook Pro Silver (... Read more
Holiday sale: 13″ MacBook Airs for up to $150...
B&H Photo has 13″ MacBook Airs on sale for $100 off MSRP as part of their Holiday sale. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 13″ 1.8GHz/128GB MacBook... Read more
The best Holiday sale prices on 13″ MacBook P...
B&H Photo has 13″ MacBook Pros on sale this weekend, with models available for $100-$150 off MSRP. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 13-inch 2.3GHz... Read more
Holiday sale: 27″ Apple iMacs for $150-$200 o...
MacMall has 27″ iMacs on sale for $150-$200 off MSRP as part of their Holiday sale, each including free shipping: – 27″ 3.8GHz iMac (MNED2LL/A): $2099 $200 off MSRP – 27″ 3.5GHz iMac (MNEA2LL/A): $... Read more
Save on clearance 2016 15″ MacBook Pros, up t...
B&H Photo has clearance 2016 15″ MacBook Pros available for up to $700 off original MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: – 15″ 2.7GHz Touch Bar MacBook Pro... Read more
Apple offers Certified Refurbished 9.7″ iPads...
Apple has Certified Refurbished 9.7″ WiFi iPads available for $50-$70 off the cost of new models. An Apple one-year warranty is included with each iPad, and shipping is free: – 9″ 32GB WiFi iPad: $... Read more
Save $200-$240 on a 12″ Apple MacBook with Ce...
Apple has Certified Refurbished 2017 12″ Retina MacBooks available for $200-$240 off the cost of new models. Apple will include a standard one-year warranty with each MacBook, and shipping is free.... Read more

Jobs Board

*Apple* Retail - Multiple Positions - Apple,...
Job Description:SalesSpecialist - Retail Customer Service and SalesTransform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description:SalesSpecialist - Retail Customer Service and SalesTransform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Information Security - Security Data...
# Apple Information Security - Security Data Analyst Job Number: 113119545 Austin, Texas, United States Posted: 10-Nov-2017 Weekly Hours: 40.00 **Job Summary** This Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.