Nov 97 - Tips
Volume Number: 13 (1997)
Issue Number: 11
Column Tag: Tips & Tidbits
by Steve Sisak
Here is an easy technique for determining if a point lies within a given graphic object of arbitrary complexity: simply draw the object into an offscreen bitmap, then check if the desired pixel has been changed.
In more detail, the steps are:
- Create an offscreen black-and-white bitmap, one pixel high by 16 pixels wide (actually it only needs to be one pixel wide, but QuickDraw insists that you allocate an even number of bytes).
- Erase this bitmap to white.
- Draw the shape into the bitmap in solid black.
- Test the desired pixel to see if it has been set to black.
Here is some actual code that shows you how to hit test a polygon in this way. First, an auxiliary routine to aid the setup of an offscreen black-and-white GrafPort (since GWorlds are overkill for this purpose):
PROCEDURE SetBitsTo
(
VAR TheBits : BitMap
);
(* sets the portBits of the current grafPort, also
changing the portRect, clipRgn, visRgn to match
the bounds of the bitmap. *)
VAR
CurPort : GrafPtr;
BEGIN
SetPortBits(TheBits);
MovePortTo(0, 0);
SetOrigin(TheBits.bounds.left, TheBits.bounds.top);
PortSize
(
TheBits.bounds.right - TheBits.bounds.left,
TheBits.bounds.bottom - TheBits.bounds.top
);
GetPort(CurPort);
ClipRect(CurPort^.portRect);
CopyRgn(CurPort^.clipRgn, CurPort^.visRgn)
END SetBitsTo;
Then, the actual routine that hit tests a point against a polygon:
PROCEDURE PtInPoly
(
pt : Point;
poly : PolyHandle
) : BOOLEAN;
(* is the specified point within the given polygon. *)
VAR
PreviousPort : GrafPtr;
TempPort : GrafPort;
TempBits : BitMap;
TempBitsWord : ShortCard; (* a 1-by-16 offscreen bitmap *)
BEGIN
GetPort(PreviousPort);
OpenPort(ADR(TempPort));
TempBits.bounds.top := pt.v;
TempBits.bounds.bottom := pt.v + 1;
TempBits.bounds.left := pt.h - 15;
(* so desired pixel is in bit 0 *)
TempBits.bounds.right := pt.h + 1;
TempBits.rowBytes := 2;
TempBits.baseAddr := ADR(TempBitsWord);
TempBitsWord := 0; (* set all pixels to white *)
SetBitsTo(TempBits);
PaintPoly(poly); (* draw polygon in black *)
ClosePort(ADR(TempPort));
SetPort(PreviousPort);
RETURN
ODD(TempBitsWord)
END PtInPoly;
It shouldn't be hard to see how to adapt the basic idea to other sorts of graphic objects, or even to other graphics architectures besides QuickDraw.
Lawrence D'Oliveiro
ldo@geek-central.gen.nz