home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.0 / NeXTSTEP3.0.iso / NextDeveloper / Examples / AppKit / Draw / Polygon.m < prev    next >
Text File  |  1992-07-20  |  7KB  |  229 lines

  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 movedRV$  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 != ARRV%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([selRV&ne], 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.  
  229.