home *** CD-ROM | disk | FTP | other *** search
/ OpenStep 4.2J (Developer) / os42jdev.iso / NextDeveloper / Examples / AppKit / Draw / Scribble.m < prev    next >
Text File  |  1996-04-18  |  8KB  |  300 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.  * NSLocalString("Scribble", NULL, "Name of the tool that draws scribbles, i.e., the %@ of the New %@ operation.")
  9.  */
  10.  
  11. @implementation Scribble : Graphic
  12.  
  13. static NSPoint lastPoint;    /* used in creating only */
  14.  
  15. + (NSCursor *)cursor
  16. /*
  17.  * A Scribble uses a pencil as its cursor.
  18.  */
  19. {
  20.     NSPoint spot;
  21.     static NSCursor *cursor = nil;
  22.  
  23.     if (!cursor) {
  24.     spot.x = 0.0; spot.y = 15.0;
  25.     cursor = [[NSCursor alloc] initWithImage:[NSImage imageNamed:@"Pencil.tiff"] hotSpot:spot];
  26.     }
  27.  
  28.     return cursor ? cursor : [super cursor];
  29. }
  30.  
  31. - (void)dealloc
  32. {
  33.     NSZoneFree([self zone], points);
  34.     NSZoneFree([self zone], userPathOps);
  35.     [super dealloc];
  36. }
  37.  
  38. - (void)allocateChunk
  39. /*
  40.  * The Scribble's storage is allocated in chunks.
  41.  * This allocates another chunk.
  42.  */
  43. {
  44.     int i, newSize;
  45.  
  46.     newSize = length + CHUNK_SIZE;
  47.     if (points) {
  48.     points = NSZoneRealloc((NSZone *)[self zone], points, (newSize << 1) * sizeof(float));
  49.     userPathOps = NSZoneRealloc((NSZone *)[self zone], userPathOps, (newSize) * sizeof(char));
  50.     } else {
  51.     points = NSZoneMalloc((NSZone *)[self zone], (newSize << 1) * sizeof(float));
  52.     userPathOps = NSZoneMalloc((NSZone *)[self zone], (newSize) * sizeof(char));
  53.     }
  54.     for (i = newSize - 1; i >= length; i--) {
  55.     userPathOps[i] = dps_rlineto;
  56.     } 
  57. }
  58.  
  59. - (float)naturalAspectRatio
  60. /*
  61.  * The Scribble's natural aspect ratio is the one it was created with.
  62.  */
  63. {
  64.     return (gFlags.initialized ? ((bbox[2]-bbox[0])/(bbox[3]-bbox[1])) : 0.0);
  65. }
  66.  
  67. - (int)moveCorner:(int)corner to:(NSPoint)point constrain:(BOOL)flag
  68. /*
  69.  * After the Scribble is created (gFlags.initialized == YES), this method
  70.  * just returns super's implementation.  During creation, every time the
  71.  * "corner" is moved, a new line segment is added to the Scribble and
  72.  * the bounding box is expanded if necessary.
  73.  */
  74. {
  75.     float *p;
  76.  
  77.     if (gFlags.initialized) {
  78.     return [super moveCorner:corner to:point constrain:flag];
  79.     }
  80.  
  81.     if (!(point.x - lastPoint.x || point.y - lastPoint.y)) return corner;
  82.  
  83.     length++;
  84.  
  85.     if (!(length % CHUNK_SIZE)) [self allocateChunk];
  86.  
  87.     p = points + (length << 1);
  88.     *p++ = point.x - lastPoint.x;
  89.     *p = point.y - lastPoint.y;
  90.     lastPoint = point;
  91.  
  92.     bbox[2] = MAX(point.x, bbox[2]);
  93.     bbox[0] = MIN(point.x, bbox[0]);
  94.     bbox[3] = MAX(point.y, bbox[3]);
  95.     bbox[1] = MIN(point.y, bbox[1]);
  96.  
  97.     bounds.origin.x = bbox[0];
  98.     bounds.origin.y = bbox[1];
  99.     bounds.size.width = bbox[2] - bbox[0];
  100.     bounds.size.height = bbox[3] - bbox[1];
  101.  
  102.     return corner;
  103. }
  104.  
  105.  
  106. - (BOOL)create:(NSEvent *)event in:view
  107. /*
  108.  * Before creating, an initial chunk is initialized, and the userPathOps
  109.  * are initialized.  The lastPoint is also remembered as the start point.
  110.  * After the Scribble is created, the initialized flag is set.
  111.  */
  112. {
  113.     NSPoint p;
  114.  
  115.     [self allocateChunk];
  116.     userPathOps[0] = dps_moveto;    
  117.     p = [event locationInWindow];
  118.     p = [view convertPoint:p fromView:nil];
  119.     p = [view grid:p];
  120.     points[0] = p.x;
  121.     points[1] = p.y;
  122.     lastPoint = p;
  123.     bbox[0] = bbox[2] = p.x;
  124.     bbox[1] = bbox[3] = p.y;
  125.     bounds.origin = p;
  126.     bounds.size.width = bounds.size.height = 0.0;
  127.  
  128.     if ([super create:event in:view]) {
  129.     gFlags.initialized = YES;
  130.     return YES;
  131.     }
  132.  
  133.     return NO;
  134. }
  135.  
  136.  
  137. - draw
  138. /*
  139.  * The Scribble is drawn simply by scaling appropriately from its
  140.  * initial bounding box and drawing the user path.
  141.  */
  142. {
  143.     float x, y;
  144.     NSPoint p1, p2;
  145.     int i, count, coords;
  146.     float angle, sx, sy, tx, ty;
  147.  
  148.     if (bounds.size.width < 1.0 || bounds.size.height < 1.0) return self;
  149.  
  150.     if (length && (bbox[2] - bbox[0]) && (bbox[3] - bbox[1])) {
  151.     sx = bounds.size.width / (bbox[2] - bbox[0]);
  152.     sy = bounds.size.height / (bbox[3] - bbox[1]);
  153.     tx = (bounds.origin.x +
  154.           ((points[0]-bbox[0]) / (bbox[2]-bbox[0]) * bounds.size.width)) - points[0] * sx;
  155.     ty = (bounds.origin.y +
  156.           ((points[1]-bbox[1]) / (bbox[3]-bbox[1]) * bounds.size.height)) - points[1] * sy;
  157.     if (gFlags.arrow && ![self fill] && (sx != 1.0 || sy != 1.0 || tx || ty)) {
  158.         PSgsave();
  159.     }
  160.     if ([self fill]) {
  161.         PSgsave();
  162.         PStranslate(tx, ty);
  163.         PSscale(sx, sy);
  164.         [self setFillColor];
  165.         PSDoUserPath(points, (length + 1) << 1, dps_float, userPathOps, length + 1, bbox, gFlags.eofill ? dps_ueofill : dps_ufill);
  166.         PSgrestore();
  167.     }
  168.     if (!gFlags.nooutline) {
  169.         PStranslate(tx, ty);
  170.         PSscale(sx, sy);
  171.         [self setLineColor];
  172.         PSDoUserPath(points, (length + 1) << 1, dps_float, userPathOps, length + 1, bbox, dps_ustroke);
  173.     }
  174.     if (gFlags.arrow && ![self fill]) {
  175.         if (sx != 1.0 || sy != 1.0 || tx || ty) {
  176.         PSgrestore();
  177.         [self setLineColor];
  178.         }
  179.         if (gFlags.arrow != ARROW_AT_END) {
  180.         i = 0;
  181.         p1.x = points[i++];
  182.         p1.y = points[i++];
  183.         p2 = p1;
  184.         p2.x += points[i++];
  185.         p2.y += points[i++];
  186.         count = length - 1;
  187.         while (sqrt(((p1.x-p2.x)*sx)*((p1.x-p2.x)*sx) + ((p1.y-p2.y)*sy)*((p1.y-p2.y)*sy)) < 7.0 && count--) {    // no hypot on Windows?
  188.             p2.x += points[i++];
  189.             p2.y += points[i++];
  190.         }
  191.         angle = atan2((p1.y-p2.y)*sy, (p1.x-p2.x)*sx);
  192.         angle = (angle / 3.1415) * 180.0;
  193.         x = bounds.origin.x + (p1.x - bbox[0]) * sx;
  194.         y = bounds.origin.y + (p1.y - bbox[1]) * sy;
  195.         PSArrow(x, y, angle);
  196.         }
  197.         if (gFlags.arrow != ARROW_AT_START) {
  198.         i = 0;
  199.         coords = (length + 1) << 1;
  200.         p1.x = points[i++];
  201.         p1.y = points[i++];
  202.         while (i < coords) {
  203.             p1.x += points[i++];
  204.             p1.y += points[i++];
  205.         }
  206.         p2 = p1;
  207.         i = coords;
  208.         p2.y -= points[--i];
  209.         p2.x -= points[--i];
  210.         count = length - 1;
  211.         while (sqrt(((p2.x-p1.x)*sx)*((p2.x-p1.x)*sx) + ((p2.y-p1.y)*sy)*((p2.y-p1.y)*sy)) < 7.0 && count--) {    // no hypot on Windows?
  212.             p2.y -= points[--i];
  213.             p2.x -= points[--i];
  214.         }
  215.         angle = atan2((p1.y-p2.y)*sy, (p1.x-p2.x)*sx);
  216.         angle = (angle / 3.1415) * 180.0;
  217.         x = bounds.origin.x + (p1.x - bbox[0]) * sx;
  218.         y = bounds.origin.y + (p1.y - bbox[1]) * sy;
  219.         PSArrow(x, y, angle);
  220.         }
  221.     }
  222.     }
  223.  
  224.     return self;
  225. }
  226.  
  227. /* Archiving */
  228.  
  229. #define LENGTH_KEY @"NumberOfPoints"
  230. #define BBOX_KEY @"BoundingBox"
  231. #define DATA_KEY @"Points"
  232.  
  233. - (id)propertyList
  234. {
  235.     int i, numFloats;
  236.     NSMutableArray *floatArray;
  237.     NSMutableDictionary *plist;
  238.     NSRect bboxRect;
  239.  
  240.     plist = [super propertyList];
  241.     bboxRect.origin.x = bbox[0]; bboxRect.origin.y = bbox[1];
  242.     bboxRect.size.width = bbox[2]; bboxRect.size.height = bbox[3];
  243.     [plist setObject:propertyListFromNSRect(bboxRect) forKey:BBOX_KEY];
  244.     [plist setObject:propertyListFromInt(length) forKey:LENGTH_KEY];
  245.     numFloats = (length + 1) << 1;
  246.     floatArray = [NSMutableArray arrayWithCapacity:numFloats];
  247.     for (i = 0; i < numFloats; i++) {
  248.         [floatArray addObject:propertyListFromFloat(points[i])];
  249.     }
  250.     [plist setObject:floatArray forKey:DATA_KEY];
  251.  
  252.     return plist;
  253. }
  254.  
  255. - (NSString *)description
  256. {
  257.     NSMutableDictionary *plist;
  258.     NSRect bboxRect;
  259.  
  260.     plist = [super propertyList];
  261.     bboxRect.origin.x = bbox[0]; bboxRect.origin.y = bbox[1];
  262.     bboxRect.size.width = bbox[2]; bboxRect.size.height = bbox[3];
  263.     [plist setObject:propertyListFromNSRect(bboxRect) forKey:BBOX_KEY];
  264.     [plist setObject:propertyListFromInt(length) forKey:LENGTH_KEY];
  265.     // should we report the points here too?
  266.  
  267.     return [plist description];
  268. }
  269.  
  270. - initFromPropertyList:(id)plist inDirectory:(NSString *)directory
  271. {
  272.     NSEnumerator *enumerator;
  273.     NSArray *floatArray;
  274.     float *p;
  275.     int i;
  276.     NSRect bboxRect;
  277.  
  278.     [super initFromPropertyList:plist inDirectory:directory];
  279.     bboxRect = rectFromPropertyList([plist objectForKey:BBOX_KEY]);
  280.     bbox[0] = bboxRect.origin.x; bbox[1] = bboxRect.origin.y;
  281.     bbox[2] = bboxRect.size.width; bbox[3] = bboxRect.size.height;
  282.     length = [[plist objectForKey:LENGTH_KEY] intValue];
  283.     floatArray = [plist objectForKey:DATA_KEY];
  284.     points = NSZoneMalloc((NSZone *)[self zone], ((length + 1) << 1) * sizeof(float));
  285.     userPathOps = NSZoneMalloc((NSZone *)[self zone], (length + 1) * sizeof(char));
  286.     p = points;
  287.     enumerator = [floatArray objectEnumerator];
  288.     for (i = 0; i <= length; i++) {
  289.         *p++ = [[enumerator nextObject] floatValue];
  290.         *p++ = [[enumerator nextObject] floatValue];
  291.         userPathOps[i] = dps_rlineto;
  292.     }
  293.     userPathOps[0] = dps_moveto;
  294.  
  295.     return self;
  296. }
  297.  
  298. @end
  299.  
  300.