|
|
1.1 ! root 1: #import "draw.h" ! 2: ! 3: /* ! 4: * This line is just a stub to get genstrings to generate ! 5: * a .strings file entry for the name of this type of Graphic. ! 6: * The name is used in the Undo New <Whatever> menu item. ! 7: * ! 8: * NXLocalString("Polygon", NULL, "Name of the tool that draws polygons, i.e., the %s of the New %s operation.") ! 9: */ ! 10: ! 11: @implementation Polygon ! 12: ! 13: + initialize ! 14: /* ! 15: * This bumps the class version so that we can compatibly read ! 16: * old Graphic objects out of an archive. ! 17: */ ! 18: { ! 19: [Polygon setVersion:1]; ! 20: return self; ! 21: } ! 22: ! 23: + cursor ! 24: /* ! 25: * The cursor inherited from Scribble is a pencil. ! 26: * That's not very appropriate, so CrossCursor is used instead. ! 27: */ ! 28: { ! 29: return CrossCursor; ! 30: } ! 31: ! 32: static void getRectFromBBox(NXRect *r, float x1, float y1, float x2, float y2) ! 33: /* ! 34: * Takes two points (x1, y1) and (x2, y2) and updates the r rect to ! 35: * equal that bounding box. ! 36: */ ! 37: { ! 38: r->size.width = x1 - x2; ! 39: r->size.height = y1 - y2; ! 40: if (r->size.width < 0.0) { ! 41: r->origin.x = x2 + r->size.width; ! 42: r->size.width = 0.0 - r->size.width; ! 43: } else r->origin.x = x2; ! 44: if (r->size.height < 0.0) { ! 45: r->origin.y = y2 + r->size.height; ! 46: r->size.height = 0.0 - r->size.height; ! 47: } else r->origin.y = y2; ! 48: } ! 49: ! 50: /* ! 51: * This class probably is probably not implemented in the optimal way, ! 52: * but it shows how an existing implementation (i.e. Scribble) can be ! 53: * used to implement some other object. ! 54: * ! 55: * This method creates a polygon. The user must drag out each segment of ! 56: * the polygon clicking to make a corner, finally ending with a double click. ! 57: * ! 58: * Start by getting the starting point of the polygon from the mouse down ! 59: * event passed in the event parameter (if the ALT key is not down, then we ! 60: * will close the path even if the user does not explicitly do so). ! 61: * ! 62: * Next, we initialize a chunk of space for the points to be stored in ! 63: * and initialize point[0] and point[1] to be the starting point (since the ! 64: * first thing in the userpath is a moveto). We also initialize our bounding ! 65: * box to contain only that point. ! 66: * ! 67: * p represents the last point the user moved the mouse to. We initialize it ! 68: * to start before entering the tracking loop. ! 69: * ! 70: * Inside the loop, last represents the last point the user confirmed (by ! 71: * clicking) as opposed to p, the last point the user moved to. We update ! 72: * last every time we start the segment tracking loop (the inner, ! 73: * while (event->type != NX_MOUSEUP) loop). ! 74: * ! 75: * In the segment tracking loop, r represents the rectangle which must be ! 76: * redrawn to get rid of the last time we drew the segment we are currently ! 77: * tracking. After we [view drawSelf:&r :1] to clear out the last segment, ! 78: * we recalculate the value of r for the next time around the loop. Finally, ! 79: * we draw ourselves (i.e. all the other segments besides the one we are ! 80: * currently tracking) and then draw the segment we are currently tracking. ! 81: * ! 82: * After tracking the segment, we check to see if we are done. ! 83: * We are finished if any of the following are true: ! 84: * 1. The last segment the user created was smaller than a gridSpacing. ! 85: * 2. The user clicked on the starting point (thereby closing the path). ! 86: * 3. The mouse down is outside the view's bounds. ! 87: * 4. A kit defined or system defined event comes through. ! 88: * ! 89: * If we are not done (or we need to close the path), then we store the ! 90: * new point pair into points (reallocating our points ! 91: * and userPathOps arrays if we are out of room). We then update our bounding ! 92: * box to reflect the new point and update our bounds to equal our bounding ! 93: * box. If we aren't done, we look for the next mouse down to begin the ! 94: * tracking of another segment. ! 95: * ! 96: * After we are finished with all segments, we check to be sure that we have ! 97: * at least two segments (one segment is a line, not a polygon). If the ! 98: * path is closed, then we need at least three segments. If we have the ! 99: * requisite number of segments, then we reallocate our arrays to fit exactly ! 100: * our number of points and return YES. Otherwise, we free the storage of ! 101: * those arrays and clean up any drawing we did and return NO. ! 102: */ ! 103: ! 104: #define POLYGON_MASK (NX_MOUSEDRAGGEDMASK|NX_MOUSEUPMASK) ! 105: #define END_POLYGON_MASK (NX_KITDEFINEDMASK|NX_MOUSEDOWNMASK|NX_SYSDEFINEDMASK) ! 106: ! 107: - (BOOL)create:(NXEvent *)event in:view ! 108: { ! 109: float *pptr; ! 110: NXRect r, viewBounds; ! 111: NXPoint start, last, p; ! 112: Window *window = [view window]; ! 113: BOOL closepath, done = NO, resend = NO; ! 114: float grid = (float)[view gridSpacing]; ! 115: int windowNum = event->window, arrow = 0; ! 116: ! 117: if (![view gridIsEnabled]) ! 118: grid = 1.0; ! 119: ! 120: gFlags.initialized = YES; ! 121: if (gFlags.arrow && gFlags.arrow != ARROW_AT_START) { ! 122: arrow = gFlags.arrow; ! 123: gFlags.arrow = (gFlags.arrow == ARROW_AT_END) ? 0 : ARROW_AT_START; ! 124: } ! 125: ! 126: start = event->location; ! 127: [view convertPoint:&start fromView:nil]; ! 128: [view grid:&start]; ! 129: ! 130: [view getVisibleRect:&viewBounds]; ! 131: ! 132: closepath = (event->flags & NX_ALTERNATEMASK) ? NO : YES; ! 133: ! 134: length = 0; ! 135: [self allocateChunk]; ! 136: pptr = points; ! 137: *pptr++ = bbox[0] = bbox[2] = start.x; ! 138: *pptr++ = bbox[1] = bbox[3] = start.y; ! 139: userPathOps[0] = dps_moveto; ! 140: ! 141: [view lockFocus]; ! 142: ! 143: [self setLineColor]; ! 144: PSsetlinewidth(linewidth); ! 145: ! 146: p = start; ! 147: event = [NXApp getNextEvent:POLYGON_MASK]; ! 148: while (!done) { ! 149: last = p; ! 150: if (event->type == NX_MOUSEDOWN) { ! 151: if (event->data.mouse.click > 1) { ! 152: done = YES; ! 153: [NXApp getNextEvent:NX_MOUSEUPMASK]; ! 154: } else if (event->window != windowNum) { ! 155: done = YES; ! 156: resend = YES; ! 157: } else { ! 158: p = event->location; ! 159: [view convertPoint:&p fromView:nil]; ! 160: done = !NXMouseInRect(&p, &viewBounds, NO); ! 161: resend = YES; ! 162: } ! 163: } else if (event->type == NX_KITDEFINED || ! 164: event->type == NX_SYSDEFINED) { ! 165: done = YES; ! 166: resend = YES; ! 167: } ! 168: if (!done) { ! 169: while (event->type != NX_MOUSEUP) { ! 170: p = event->location; ! 171: [view convertPoint:&p fromView:nil]; ! 172: [view grid:&p]; ! 173: [view drawSelf:&r :1]; ! 174: getRectFromBBox(&r, p.x, p.y, last.x, last.y); ! 175: [view scrollPointToVisible:&p]; ! 176: NXInsetRect(&r, -2.0, -2.0); ! 177: [self draw]; ! 178: PSmoveto(last.x, last.y); ! 179: PSlineto(p.x, p.y); ! 180: PSstroke(); ! 181: [window flushWindow]; ! 182: event = [NXApp getNextEvent:POLYGON_MASK]; ! 183: } ! 184: if (fabs(p.x-start.x) <= grid && fabs(p.y-start.y) <= grid) { ! 185: done = YES; ! 186: closepath = YES; ! 187: } ! 188: } ! 189: if (!done || (closepath && length > 1)) { ! 190: if (done) p = start; ! 191: length++; ! 192: if (!(length % CHUNK_SIZE)) [self allocateChunk]; ! 193: *pptr++ = p.x - last.x; ! 194: *pptr++ = p.y - last.y; ! 195: if (p.x < bbox[0]) bbox[0] = p.x; ! 196: if (p.x > bbox[2]) bbox[2] = p.x; ! 197: if (p.y < bbox[1]) bbox[1] = p.y; ! 198: if (p.y > bbox[3]) bbox[3] = p.y; ! 199: getRectFromBBox(&bounds, bbox[0], bbox[1], bbox[2], bbox[3]); ! 200: if (!done) event = [NXApp getNextEvent:END_POLYGON_MASK]; ! 201: } ! 202: } ! 203: ! 204: [view unlockFocus]; ! 205: ! 206: if (resend) DPSPostEvent(event, 1); ! 207: if (arrow) gFlags.arrow = arrow; ! 208: ! 209: if (length > (closepath ? 2 : 1)) { ! 210: points = NX_ZONEREALLOC([self zone], points, float, (length+1) << 1); ! 211: userPathOps = NX_ZONEREALLOC([self zone], userPathOps, char, length+1); ! 212: return YES; ! 213: } else { ! 214: NX_FREE(points); points = NULL; ! 215: NX_FREE(userPathOps); userPathOps = NULL; ! 216: [view drawSelf:[self getExtendedBounds:&r] :1]; ! 217: return NO; ! 218: } ! 219: } ! 220: ! 221: - (Graphic *)colorAcceptorAt:(const NXPoint *)point ! 222: { ! 223: if ([self hit:point]) return self; ! 224: return nil; ! 225: } ! 226: ! 227: @end ! 228:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.