ftp.nice.ch/pub/next/science/mathematics/HippoDraw.2.0.s.tar.gz#/HippoDraw/Hippo.bproj/Draw.subproj/Polygon.m

This is Polygon.m in view mode; [Download] [Up]

#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 moved to.  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 != ARROW_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([self zone], 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

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.