Polygons and Regions
Volume Number: 3
Issue Number: 5
Column Tag: ABC's of C
Polygons and Regions as Quickdraw Objects 
By Bob Gordon, Contributing Editor, Minneapolis, MN
Stars! The reason we have stars this month is because Jane, the seven year old
who hangs out around here, wanted them. We also have triangles, pentagons, hexagons,
and a large H-like thing. The point of all this was to use QuickDraw functions to draw
polygons and regions. These functions were integrated into the program two columns
back so we now have one function that can draw rectangles, rounded rectangles, arcs,
ovals, lines, polygons, and regions. The program is fairly straight forward, but it took
a while to get it to work correctly. At this point, our program is a fairly complex, and
complete implementation of some of the more advanced features of quickdraw. The
major operations of quickdraw are supported including Frame, Paint, Erase and
Invert. The only one missing is Fill. Implementing these functions for polygons and
regions extends the program to the most general quickdraw constructions. The next
level of complexity would be to implement pictures. Figure 1 shows some of the paint
type drawings we can construct.
We have also improved on our Mac user interface design. We now support desk
accessories. Since this opens up the possibility of having a second window obscure our
drawing, we also have to implement update events to restore the drawing after a DA has
covered part of our window. So we need an update event as well. Since our program
draws directly to the screen, what we have is a kind of MacPaint approach, where even
though our quickdraw objects are generalized polygons and regions, after they are
drawn, our screen is simply a bit map. This lends itself to an obvious approach for
update events: simply copy the window to an off-screen bit map and then use copybits
to update the window. This is the approach we have used this month. Other
improvements to the user interface might be to made our window growable and to add
multi-window capability. Improvements to our "paint" program would be to make it
into a "draw" program where each object on the screen retains it's "object-oriented
nature rather than becoming a bit map. This would also require a more complex update
function to re-draw the screen from the object definitions, rather than from the saved
bip map image. We will look at this approach next month as we generalize our
quickdraw objects even further.
C Review
One of the most confusing things about C is the use of pointers and handles from a
Pascal machine definition. Let us try to review how this is done. Suppose we have an
event record declared as theEvent and we wish to pass this to a subroutine. This is
normally done by passing the address of the event record structure by using the &
function. Hence, we might do something like Foo(&theEvent). This would pass the
address of theEvent to the routine Foo. Now what do we do in Foo? The answer is we
prepare the variable with which we accept this address:
Foo(myEvent)
Event Record *myEvent;
What this means is that myEvent is a pointer to an event record and it is this
address that is being passed to Foo; *myEvent is the actual event record, dereferenced
from myEvent. Hence throughout Foo, we are using the pointer to our event record.
This means it must be dereferenced first before being used to access fields of myEvent.
There are two ways to do this in C:
point = (*myEvent).where;
point = myEvent->where;
The same approach extends to handles as the next example shows:
rect = (**teRecHandle).viewRect;
rect = (*teRecHandle)->viewRect;
Notice that if we try to use something like
point = myEvent.where;
within Foo, the Lightspeed compiler will return a message saying that "where" is not a
field of myEvent. This is because myEvent is a pointer to theEvent, not our event
record structure itself.
This is futher complicated by situations where we must pass the data structure
itself, and not a pointer to it. Normally this is not allowed in C. However, "Macinized
C compilers allow us to pass the actual data by calling Foo(theEvent) instead of calling
Foo(&theEvent). This often leads to another problem in C. When calling routines that
modify the variables passed to it as VAR variables, we must pass the address of the
variable as in "&theEvent", rather than simply "theEvent". This often leads to
compiler errors when we fail to insert the required & for a VAR parameter. In pascal,
the programmer does not need a different syntax for VAR values so you don't have to
pay much attention to which parameters are VAR's and which are not. When you have
this error, you'll get a message that says something like wrong size for a pascal
variable. This is a clue that the & is missing in the call. An example of this is the
SetPort and GetPort trap calls which are defined as:
SetPort(gp)
GrafPtr gp;
GetPort(gp)
GrafPtr *gp;
Now when we call these routines, for SetPort we pass the window pointer for our
window as in:
SetPort(myWindowPtr);
But when we call GetPort to save the current port in a temporary window
pointer, we pass the address of our variable as in the following because it is a VAR
parameter:
GetPort(&myTempPtr);
For some reason, very few C books, even the Macintosh ones, do not fully explain
pointers and handles in C in relation to pointers and handles in Pascal, from which all
the toolbox trap calls are defined. For more on this subject, and on memory
management, see chapter six on memory management in our book, Using the Macintosh
Toolbox with C by Sybex, which we are more or less following in this series.
Fig. 1 Polygons & Regions in this month's paint program
My first idea for polygons was to develop a function that would collect points for a
polygon on the fly. After selecting "Open Poly" from a menu, you would use the mouse
to place points on the screen, lines would connect them, and after selecting "Close
Poly," you could reproduce the new polygon at will with the all purpose drawing
function, that is the central feature of our program. I got part of it written when I
discovered that it would not work: I kept getting some bomb. I think this happened
because after a polygon is opened it collects all line drawing information until the
polygon is closed. This apparently includes such things as the lines involved in menus
so doing almost anything created a multitude of lines. I did not investigate further, but
decided to simply create some new shapes (the aforementioned pentagons, hexagons,
and stars) that we could draw. This may imply that a pallette of drawing commands,
maintained by the list manager would be more appropriate. This would be another