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

  1. #import "draRV0
  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("Scribble", NULL, "Name of the tool that draws scribbles, i.e., the %s of the New %s operation.")
  9.  */
  10.  
  11. @implementation Scribble : Graphic
  12.  
  13. static NXPoint lastPoint;    /* used in creating only */
  14.  
  15. + initialize
  16. /*
  17.  * This bumps the class version so that we can compatibly read
  18.  * old Graphic objects out of an archive.
  19.  */
  20. {
  21.     [Scribble setVersion:1];
  22.     return self;
  23. }
  24.  
  25. + cursor
  26. /*
  27.  * A Scribble uses a pencil as its cursor.
  28.  */
  29. {
  30.     NXPoint spot;
  31.     static NXCursor *cursor = nil;
  32.  
  33.     if (!cursor) {
  34.     cursor = [NXCursor newFromImage:[NXImage newFromSection:"pencil.tiff"]];
  35.     spot.x = 0.0; spot.y = 15.0;
  36.     [cursor setHotSpot:&spot];
  37.     }
  38.  
  39.     return cursor ? cursor : [super cursor];
  40. }
  41.  
  42. - free
  43. {
  44.     NX_FREE(points);
  45.     NX_FREE(userPathOps);
  46.     return [super free];
  47. }
  48.  
  49. - allocateChunk
  50. /*
  51.  * The Scribble's storage is allocated in chunks.
  52.  * This allocates another chunk.
  53.  */
  54. {
  55.     int i, newSize;
  56.  
  57.     newSize = length + CHUNK_SIZE;
  58.     if (points) {
  59.     NX_ZONEREALLOC([self zone], points, float, newSize << 1);
  60.     NX_ZONEREALLOC([self zone], userPathOps, char, newSize);
  61.     } else {
  62.     NX_ZONEMALLOC([self zone], points, float, newSize << 1);
  63.     NX_ZONEMALLOC([self zone], userPathOps, char, newSize);
  64.     }
  65.     for (i = newSize - 1; i >= length; i--) {
  66.     userPathOps[i] = dps_rlineto;
  67.     }
  68.  
  69.     return self;
  70. }
  71.  
  72. - (float)naturalAspectRatio
  73. /*
  74.  * The Scribble's natural aspect ratio is the one it was created with.
  75.  */
  76. {
  77.     return (gFlags.initialized ? ((bbox[2]-bbox[0])/(bbox[3]-bbox[1])) : 0.0);
  78. }
  79.  
  80. - (int)moveCorner:(int)corner to:(const NXPoint *)point constrain:(BOOL)flag
  81. /*
  82.  * After the Scribble is created (gFlags.initialized == YES), this method
  83.  * just returns super's implementation.  During creation, every time the
  84.  * "corner" is moved, a new line segment is added to the Scribble and
  85.  * the bounding box is expanded if necessary.
  86.  */
  87. {
  88.     float *p;
  89.  
  90.     if (gFlags.initialized) {
  91.     return [super moveCorner:corner to:point constrain:flag];
  92.     }
  93.  
  94.     if (!(point->x - lastPoint.x || point->y - lastPoint.y)) return corner;
  95.  
  96.     length++;
  97.  
  98.     if (!(length % CHUNK_SIZE)) [self allocateChunk];
  99.  
  100.     p = points + (length << 1);
  101.    RV1+ = point->x - lastPoint.x;
  102.     *p = point->y - lastPoint.y;
  103.     lastPoint = *point;
  104.  
  105.     bbox[2] = MAX(point->x, bbox[2]);
  106.     bbox[0] = MIN(point->x, bbox[0]);
  107.     bbox[3] = MAX(point->y, bbox[3]);
  108.     bbox[1] = MIN(point->y, bbox[1]);
  109.  
  110.     bounds.origin.x = bbox[0];
  111.     bounds.origin.y = bbox[1];
  112.     bounds.size.width = bbox[2] - bbox[0];
  113.     bounds.size.height = bbox[3] - bbox[1];
  114.  
  115.     return corner;
  116. }
  117.  
  118.  
  119. - (BOOL)create:(NXEvent *)event in:view
  120. /*
  121.  * Before creating, an initial chunk is initialized, and the userPathOps
  122.  * are initialized.  The lastPoint is also remembered as the start point.
  123.  * After the Scribble is created, the initialized flag is set.
  124.  */
  125. {
  126.     NXPoint p;
  127.  
  128.     [self allocateChunk];
  129.     userPathOps[0] = dps_moveto;    
  130.     p = event->location;
  131.     [view convertPoint:&p fromView:nil];
  132.     [view grid:&p];
  133.     points[0] = p.x;
  134.     points[1] = p.y;
  135.     lastPoint = p;
  136.     bbox[0] = bbox[2] = p.x;
  137.     bbox[1] = bbox[3] = p.y;
  138.     bounds.origin = p;
  139.     bounds.size.width = bounds.size.height = 0.0;
  140.  
  141.     if ([super create:event in:view]) {
  142.     gFlags.initialized = YES;
  143.     return YES;
  144.     }
  145.  
  146.     return NO;
  147. }
  148.  
  149.  
  150. - draw
  151. /*
  152.  * The Scribble is drawn simply by scaling appropriately from its
  153.  * initial bounding box and drawing the user path.
  154.  */
  155. {
  156.     NXCoord x, y;
  157.     NXPoint p1, p2;
  158.     int i, count, coords;
  159.     float angle, sx, sy, tx, ty;
  160.  
  161.     if (bounds.size.width < 1.0 || bounds.size.height < 1.0) return self;
  162.  
  163.     if (length && (bbox[2] - bbox[0]) && (bbox[3] - bbox[1])) {
  164.     sx = bounds.size.width / (bbox[2] - bbox[0]);
  165.     sy = bounds.size.height / (bbox[3] - bbox[1]);
  166.     tx = (bounds.origin.x +
  167.           ((points[0]-bbox[0]) / (bbox[2]-bbox[0]) * bounds.size.width)) -
  168.         points[0] * sx;
  169.     ty = (bounds.origin.y +
  170.           ((points[1]-bbox[1]) / (bbox[3]-bbox[1]) * bounds.size.height)) -
  171.         points[1] * sy;
  172.     if (gFlags.arrow && ![self fill] && (sx != 1.0 || sy != 1.0 || tx || ty)) {
  173.         PSgsave();
  174.     }
  175.     if ([self fill]) {
  176.         PSgsave();
  177.         PStranslate(tx, ty);
  178.         PSscale(sx, sy);
  179.         [self setFillColor];
  180.         DPSDoUserPath(points, (length + 1) << 1, dps_float,
  181.               userPathOps, length + 1, bbox,
  182.               gFlags.eofill ? dps_ueofill : dps_ufill);
  183.         PSgrestore();
  184.     }
  185.     if (!gFlags.nooutline) {
  186.         PStranslate(tx, ty);
  187.         PSscale(sx, sy);
  188.         [self setLineColor];
  189.         DPSDoUserPath(points, (length + 1) << 1, dpsRV2at,
  190.               userPathOps, length + 1, bbox, dps_ustroke);
  191.     }
  192.     if (gFlags.arrow && ![self fill]) {
  193.         if (sx != 1.0 || sy != 1.0 || tx || ty) {
  194.         PSgrestore();
  195.         [self setLineColor];
  196.         }
  197.         if (gFlags.arrow != ARROW_AT_END) {
  198.         i = 0;
  199.         p1.x = points[i++];
  200.         p1.y = points[i++];
  201.         p2 = p1;
  202.         p2.x += points[i++];
  203.         p2.y += points[i++];
  204.         count = length - 1;
  205.         while (hypot((p1.x-p2.x)*sx,(p1.y-p2.y)*sy) < 7.0 && count--) {
  206.             p2.x += points[i++];
  207.             p2.y += points[i++];
  208.         }
  209.         angle = atan2((p1.y-p2.y)*sy, (p1.x-p2.x)*sx);
  210.         angle = (angle / 3.1415) * 180.0;
  211.         x = bounds.origin.x + (p1.x - bbox[0]) * sx;
  212.         y = bounds.origin.y + (p1.y - bbox[1]) * sy;
  213.         PSArrow(x, y, angle);
  214.         }
  215.         if (gFlags.arrow != ARROW_AT_START) {
  216.         i = 0;
  217.         coords = (length + 1) << 1;
  218.         p1.x = points[i++];
  219.         p1.y = points[i++];
  220.         while (i < coords) {
  221.             p1.x += points[i++];
  222.             p1.y += points[i++];
  223.         }
  224.         p2 = p1;
  225.         i = coords;
  226.         p2.y -= points[--i];
  227.         p2.x -= points[--i];
  228.         count = length - 1;
  229.         while (hypot((p2.x-p1.x)*sx,(p2.y-p1.y)*sy) < 7.0 && count--) {
  230.             p2.y -= points[--i];
  231.             p2.x -= points[--i];
  232.         }
  233.         angle = atan2((p1.y-p2.y)*sy, (p1.x-p2.x)*sx);
  234.         angle = (angle / 3.1415) * 180.0;
  235.         x = bounds.origin.x + (p1.x - bbox[0]) * sx;
  236.         y = bounds.origin.y + (p1.y - bbox[1]) * sy;
  237.         PSArrow(x, y, angle);
  238.         }
  239.     }
  240.     }
  241.  
  242.     return self;
  243. }
  244.  
  245. - write:(NXTypedStream *)stream
  246. /*
  247.  * The Scribble is written out by writing its length (in segments), its
  248.  * bounding box, and all the points.
  249.  */
  250. {
  251.     int i, numFloats;
  252.  
  253.     [super write:stream];
  254.  
  255.     NXWriteTypes(stream,"iffff",&length,&bbox[0],&bbox[1],&bbox[2],&bbox[3]);
  256.  
  257.     numFloats = (length + 1) << 1;
  258.     for (i = 0; i < numFloats; i++) {
  259.     NXWriteTypes(stream, "f", &points[i]);
  260.     }
  261.  
  262.     return self;
  263. }
  264.  
  265. - read:(NXTypedStream *)stream
  266. {
  267.     int i;
  268.     float *p;
  269.  
  270.     [super read:stream];
  271.  
  272.     NXReadTypes(stream,"iffff",&length,&bbox[0],&bbox[1],&bbox[2],&bbox[3]);
  273.  
  274.     NX_ZONEMALLOC([self zone], points, float, (length + 1) << 1);
  275.     NX_ZONEMALLOC([self zone], userPathOps, char, length + 1);
  276.  
  277.     p = points;
  278.     for (i = 0; i <= length; i++) {
  279.     NXReadTypes(stream, "f", p++);
  280.     NXReadTypes(stream, "f", p++);
  281.     userPathOps[i] = dps_rlineto;
  282.     }
  283.     userPathOps[0] = dps_moveto;
  284.  
  285.     return self;
  286. }
  287.  
  288. @end
  289.  
  290.