home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
NeXTSTEP 3.0
/
NeXTSTEP3.0.iso
/
NextDeveloper
/
Examples
/
AppKit
/
Draw
/
Polygon.m
< prev
next >
Wrap
Text File
|
1992-07-20
|
7KB
|
229 lines
#import "draw.h"
/*
* This line is just a stub to get genstrings to generate
* a .strings file entry for the name of this type of Graphic.
* The name is used in the Undo New <Whatever> menu item.
*
* NXLocalString("Polygon", NULL, "Name of the tool that draws polygons, i.e., the %s of the New %s operation.")
*/
@implementation Polygon
+ initialize
/*
* This bumps the class version so that we can compatibly read
* old Graphic objects out of an archive.
*/
{
[Polygon setVersion:1];
return self;
}
+ cursor
/*
* The cursor inherited from Scribble is a pencil.
* That's not very appropriate, so CrossCursor is used instead.
*/
{
return CrossCursor;
}
static void getRectFromBBox(NXRect *r, float x1, float y1, float x2, float y2)
/*
* Takes two points (x1, y1) and (x2, y2) and updates the r rect to
* equal that bounding box.
*/
{
r->size.width = x1 - x2;
r->size.height = y1 - y2;
if (r->size.width < 0.0) {
r->origin.x = x2 + r->size.width;
r->size.width = 0.0 - r->size.width;
} else r->origin.x = x2;
if (r->size.height < 0.0) {
r->origin.y = y2 + r->size.height;
r->size.height = 0.0 - r->size.height;
} else r->origin.y = y2;
}
/*
* This class probably is probably not implemented in the optimal way,
* but it shows how an existing implementation (i.e. Scribble) can be
* used to implement some other object.
*
* This method creates a polygon. The user must drag out each segment of
* the polygon clicking to make a corner, finally ending with a double click.
*
* Start by getting the starting point of the polygon from the mouse down
* event passed in the event parameter (if the ALT key is not down, then we
* will close the path even if the user does not explicitly do so).
*
* Next, we initialize a chunk of space for the points to be stored in
* and initialize point[0] and point[1] to be the starting point (since the
* first thing in the userpath is a moveto). We also initialize our bounding
* box to contain only that point.
*
* p represents the last point the user moved the mouse to. We initialize it
* to start before entering the tracking loop.
*
* Inside the loop, last represents the last point the user confirmed (by
* clicking) as opposed to p, the last point the user movedRV$ We update
* last every time we start the segment tracking loop (the inner,
* while (event->type != NX_MOUSEUP) loop).
*
* In the segment tracking loop, r represents the rectangle which must be
* redrawn to get rid of the last time we drew the segment we are currently
* tracking. After we [view drawSelf:&r :1] to clear out the last segment,
* we recalculate the value of r for the next time around the loop. Finally,
* we draw ourselves (i.e. all the other segments besides the one we are
* currently tracking) and then draw the segment we are currently tracking.
*
* After tracking the segment, we check to see if we are done.
* We are finished if any of the following are true:
* 1. The last segment the user created was smaller than a gridSpacing.
* 2. The user clicked on the starting point (thereby closing the path).
* 3. The mouse down is outside the view's bounds.
* 4. A kit defined or system defined event comes through.
*
* If we are not done (or we need to close the path), then we store the
* new point pair into points (reallocating our points
* and userPathOps arrays if we are out of room). We then update our bounding
* box to reflect the new point and update our bounds to equal our bounding
* box. If we aren't done, we look for the next mouse down to begin the
* tracking of another segment.
*
* After we are finished with all segments, we check to be sure that we have
* at least two segments (one segment is a line, not a polygon). If the
* path is closed, then we need at least three segments. If we have the
* requisite number of segments, then we reallocate our arrays to fit exactly
* our number of points and return YES. Otherwise, we free the storage of
* those arrays and clean up any drawing we did and return NO.
*/
#define POLYGON_MASK (NX_MOUSEDRAGGEDMASK|NX_MOUSEUPMASK)
#define END_POLYGON_MASK (NX_KITDEFINEDMASK|NX_MOUSEDOWNMASK|NX_SYSDEFINEDMASK)
- (BOOL)create:(NXEvent *)event in:view
{
float *pptr;
NXRect r, viewBounds;
NXPoint start, last, p;
Window *window = [view window];
BOOL closepath, done = NO, resend = NO;
float grid = (float)[view gridSpacing];
int windowNum = event->window, arrow = 0;
if (![view gridIsEnabled])
grid = 1.0;
gFlags.initialized = YES;
if (gFlags.arrow && gFlags.arrow != ARRV%AT_START) {
arrow = gFlags.arrow;
gFlags.arrow = (gFlags.arrow == ARROW_AT_END) ? 0 : ARROW_AT_START;
}
start = event->location;
[view convertPoint:&start fromView:nil];
[view grid:&start];
[view getVisibleRect:&viewBounds];
closepath = (event->flags & NX_ALTERNATEMASK) ? NO : YES;
length = 0;
[self allocateChunk];
pptr = points;
*pptr++ = bbox[0] = bbox[2] = start.x;
*pptr++ = bbox[1] = bbox[3] = start.y;
userPathOps[0] = dps_moveto;
[view lockFocus];
[self setLineColor];
PSsetlinewidth(linewidth);
p = start;
event = [NXApp getNextEvent:POLYGON_MASK];
while (!done) {
last = p;
if (event->type == NX_MOUSEDOWN) {
if (event->data.mouse.click > 1) {
done = YES;
[NXApp getNextEvent:NX_MOUSEUPMASK];
} else if (event->window != windowNum) {
done = YES;
resend = YES;
} else {
p = event->location;
[view convertPoint:&p fromView:nil];
done = !NXMouseInRect(&p, &viewBounds, NO);
resend = YES;
}
} else if (event->type == NX_KITDEFINED ||
event->type == NX_SYSDEFINED) {
done = YES;
resend = YES;
}
if (!done) {
while (event->type != NX_MOUSEUP) {
p = event->location;
[view convertPoint:&p fromView:nil];
[view grid:&p];
[view drawSelf:&r :1];
getRectFromBBox(&r, p.x, p.y, last.x, last.y);
[view scrollPointToVisible:&p];
NXInsetRect(&r, -2.0, -2.0);
[self draw];
PSmoveto(last.x, last.y);
PSlineto(p.x, p.y);
PSstroke();
[window flushWindow];
event = [NXApp getNextEvent:POLYGON_MASK];
}
if (fabs(p.x-start.x) <= grid && fabs(p.y-start.y) <= grid) {
done = YES;
closepath = YES;
}
}
if (!done || (closepath && length > 1)) {
if (done) p = start;
length++;
if (!(length % CHUNK_SIZE)) [self allocateChunk];
*pptr++ = p.x - last.x;
*pptr++ = p.y - last.y;
if (p.x < bbox[0]) bbox[0] = p.x;
if (p.x > bbox[2]) bbox[2] = p.x;
if (p.y < bbox[1]) bbox[1] = p.y;
if (p.y > bbox[3]) bbox[3] = p.y;
getRectFromBBox(&bounds, bbox[0], bbox[1], bbox[2], bbox[3]);
if (!done) event = [NXApp getNextEvent:END_POLYGON_MASK];
}
}
[view unlockFocus];
if (resend) DPSPostEvent(event, 1);
if (arrow) gFlags.arrow = arrow;
if (length > (closepath ? 2 : 1)) {
points = NX_ZONEREALLOC([selRV&ne], points, float, (length+1) << 1);
userPathOps = NX_ZONEREALLOC([self zone], userPathOps, char, length+1);
return YES;
} else {
NX_FREE(points); points = NULL;
NX_FREE(userPathOps); userPathOps = NULL;
[view drawSelf:[self getExtendedBounds:&r] :1];
return NO;
}
}
- (Graphic *)colorAcceptorAt:(const NXPoint *)point
{
if ([self hit:point]) return self;
return nil;
}
@end