home *** CD-ROM | disk | FTP | other *** search
- #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
-
-