home *** CD-ROM | disk | FTP | other *** search
/ OpenStep 4.2J (Developer) / os42jdev.iso / NextDeveloper / Examples / AppKit / Draw / Line.m < prev    next >
Text File  |  1996-04-18  |  7KB  |  268 lines

  1. #import "draw.h"
  2.  
  3. @implementation Line : Graphic
  4. /*
  5.  * Drawing a line is simple except that we have to keep track of whether
  6.  * the line goes from the upper left to the lower right of the bounds or
  7.  * from the lower left to the upper right.  This can easily be determined
  8.  * every time a corner is moved to a different corner.  Therefore, all
  9.  * that is needed is to override moveCorner:to:constrain: to keep track
  10.  * of that.  It is an efficiency hack to have the downhill flag kept
  11.  * in our superclass's flags.
  12.  *
  13.  * This line is just a stub to get genstrings to generate
  14.  * a .strings file entry for the name of this type of Graphic.
  15.  * The name is used in the Undo New <Whatever> menu item.
  16.  *
  17.  * NSLocalString("Line", NULL, "Name of the tool that draws lines, i.e., the %@ of the New %@ operation.")
  18.  */
  19.  
  20. #define HIT_TOLERANCE 6.0
  21.  
  22. - (id)init
  23. {
  24.     [super init];
  25.     startCorner = LOWER_LEFT;
  26.     return self;
  27. }
  28.  
  29. - (BOOL)isValid
  30. /*
  31.  * A line is validly created if EITHER of the dimensions is big enough.
  32.  */
  33. {
  34.     return(bounds.size.width >= 5.0 || bounds.size.height >= 5.0);
  35. }
  36.  
  37. static int oppositeCorner(int corner)
  38. {
  39.     switch (corner) {
  40.     case UPPER_RIGHT: return LOWER_LEFT;
  41.     case LOWER_LEFT: return UPPER_RIGHT;
  42.     case UPPER_LEFT: return LOWER_RIGHT;
  43.     case LOWER_RIGHT: return UPPER_LEFT;
  44.     }
  45.  
  46.     return corner;
  47. }
  48.  
  49. - (int)moveCorner:(int)corner to:(NSPoint)point constrain:(BOOL)flag
  50. /*
  51.  * Moves the corner to the specified point keeping track of whether the
  52.  * line is going uphill or downhill and where the start corner has moved to.
  53.  */
  54. {
  55.     int newcorner;
  56.  
  57.     newcorner = [super moveCorner:corner to:point constrain:flag];
  58.  
  59.     if (newcorner != corner) {
  60.     if ((newcorner == UPPER_RIGHT && corner == LOWER_LEFT) ||
  61.         (newcorner == UPPER_LEFT && corner == LOWER_RIGHT) ||
  62.         (newcorner == LOWER_RIGHT && corner == UPPER_LEFT) ||
  63.         (newcorner == LOWER_LEFT && corner == UPPER_RIGHT)) {
  64.     } else {
  65.         gFlags.downhill = !gFlags.downhill;
  66.     }
  67.     if (startCorner == corner) {
  68.         startCorner = newcorner;
  69.     } else {
  70.         startCorner = oppositeCorner(newcorner);
  71.     }
  72.     }
  73.  
  74.     return newcorner;
  75. }
  76.  
  77. - (void)constrainCorner:(int)corner toAspectRatio:(float)ratio
  78. /*
  79.  * Constrains the corner to the nearest 15 degree angle.  Ignores ratio.
  80.  */
  81. {
  82.     float width, height;
  83.     double angle, distance;
  84.  
  85.     distance = sqrt(bounds.size.width*bounds.size.width + bounds.size.height*bounds.size.height);    // hypot not available on Windows?
  86.     angle = atan2(bounds.size.height, bounds.size.width);
  87.     angle = (angle / 3.1415) * 180.0;
  88.     angle = floor(angle / 15.0 + 0.5) * 15.0;
  89.     angle = (angle / 180.0) * 3.1415;
  90.     width = floor(cos(angle) * distance + 0.5);
  91.     height = floor(sin(angle) * distance + 0.5);
  92.  
  93.     switch (corner) {
  94.     case LOWER_LEFT:
  95.         bounds.origin.x -= width - bounds.size.width;
  96.         bounds.origin.y -= height - bounds.size.height;
  97.         break;
  98.     case UPPER_LEFT:
  99.         bounds.origin.x -= width - bounds.size.width;
  100.         break;
  101.     case LOWER_RIGHT:
  102.         bounds.origin.y -= height - bounds.size.height;
  103.         break;
  104.     }
  105.  
  106.     bounds.size.width = width;
  107.     bounds.size.height = height; 
  108. }
  109.  
  110. - (int)cornerMask
  111. /*
  112.  * Only put corner knobs at the start and end of the line.
  113.  */
  114. {
  115.     if (gFlags.downhill) {
  116.     return(UPPER_LEFT_MASK|LOWER_RIGHT_MASK);
  117.     } else {
  118.     return(LOWER_LEFT_MASK|UPPER_RIGHT_MASK);
  119.     }
  120. }
  121.  
  122. - draw
  123. /*
  124.  * Calls drawLine to draw the line, then draws the arrows if any.
  125.  */
  126. {
  127.     if (bounds.size.width < 1.0 && bounds.size.height < 1.0) return self;
  128.  
  129.     [self setLineColor];
  130.     [self drawLine];
  131.  
  132.     if (gFlags.arrow) {
  133.     if (gFlags.downhill) {
  134.         if (((gFlags.arrow != ARROW_AT_START) &&
  135.              (startCorner == LOWER_RIGHT)) ||
  136.         ((gFlags.arrow != ARROW_AT_END) &&
  137.          (startCorner == UPPER_LEFT))) {
  138.         PSArrow(bounds.origin.x,
  139.             bounds.origin.y + bounds.size.height,
  140.             [self arrowAngle:UPPER_LEFT]);        
  141.         }
  142.         if (((gFlags.arrow != ARROW_AT_START) &&
  143.              (startCorner == UPPER_LEFT)) ||
  144.         ((gFlags.arrow != ARROW_AT_END) &&
  145.          (startCorner == LOWER_RIGHT))) {
  146.         PSArrow(bounds.origin.x + bounds.size.width,
  147.             bounds.origin.y,
  148.             [self arrowAngle:LOWER_RIGHT]);        
  149.         }
  150.     } else {
  151.         if (((gFlags.arrow != ARROW_AT_START) &&
  152.              (startCorner == LOWER_LEFT)) ||
  153.         ((gFlags.arrow != ARROW_AT_END) &&
  154.          (startCorner == UPPER_RIGHT))) {
  155.         PSArrow(bounds.origin.x + bounds.size.width,
  156.             bounds.origin.y + bounds.size.height,
  157.             [self arrowAngle:UPPER_RIGHT]);        
  158.         }
  159.         if (((gFlags.arrow != ARROW_AT_START) &&
  160.              (startCorner == UPPER_RIGHT)) ||
  161.         ((gFlags.arrow != ARROW_AT_END) &&
  162.          (startCorner == LOWER_LEFT))) {
  163.         PSArrow(bounds.origin.x,
  164.             bounds.origin.y,
  165.             [self arrowAngle:LOWER_LEFT]);        
  166.         }
  167.     }
  168.     }
  169.  
  170.     return self;
  171. }
  172.  
  173. - (BOOL)hit:(NSPoint)point
  174. /*
  175.  * Gets a hit if the point is within HIT_TOLERANCE of the line.
  176.  */
  177. {
  178.     NSRect r;
  179.     NSPoint p;
  180.     float lineangle, pointangle, distance;
  181.     float tolerance = HIT_TOLERANCE + linewidth;
  182.  
  183.     if (gFlags.locked || !gFlags.active) return NO;
  184.  
  185.     r = bounds;
  186.     if (r.size.width < tolerance) {
  187.     r.size.width += tolerance * 2.0;
  188.     r.origin.x -= tolerance;
  189.     }
  190.     if (r.size.height < tolerance) {
  191.     r.size.height += tolerance * 2.0;
  192.     r.origin.y -= tolerance;
  193.     }
  194.  
  195.     if (!NSMouseInRect(point, r, NO)) return NO;
  196.  
  197.     p.x = point.x - bounds.origin.x;
  198.     p.y = point.y - bounds.origin.y;
  199.     if (gFlags.downhill) p.y = bounds.size.height - p.y;
  200.     if (p.x && bounds.size.width) {
  201.     lineangle = atan(bounds.size.height/bounds.size.width);
  202.     pointangle = atan(p.y/p.x);
  203.     distance = sqrt(p.x*p.x+p.y*p.y)*sin(fabs(lineangle-pointangle));
  204.     } else {
  205.     distance = fabs(point.x - bounds.origin.x);
  206.     }
  207.  
  208.     return((distance - tolerance) <= linewidth);
  209. }
  210.  
  211. /* Methods intended to be subclassed */
  212.  
  213. - (float)arrowAngle:(int)corner
  214. /*
  215.  * Returns the angle which the arrow should be drawn at.
  216.  */
  217. {
  218.     float angle;
  219.     angle = atan2(bounds.size.height, bounds.size.width);
  220.     angle = (angle / 3.1415) * 180.0;
  221.     switch (corner) {
  222.     case UPPER_RIGHT: return angle;
  223.     case LOWER_LEFT: return angle + 180.0;
  224.     case UPPER_LEFT: return 180.0 - angle;
  225.     case LOWER_RIGHT: return - angle;
  226.     }
  227.     return angle;
  228. }
  229.  
  230. - (void)drawLine
  231. /*
  232.  * The actual line drawing is done here so that it can be subclassed.
  233.  */
  234. {
  235.     if (gFlags.downhill) {
  236.     PSLine(bounds.origin.x, bounds.origin.y + bounds.size.height,
  237.            bounds.size.width, - bounds.size.height);
  238.     } else {
  239.     PSLine(bounds.origin.x, bounds.origin.y,
  240.            bounds.size.width, bounds.size.height);
  241.     } 
  242. }
  243.  
  244. /* Archiving methods */
  245.  
  246. #define START_CORNER_KEY @"Start Corner"
  247.  
  248. - (id)propertyList
  249. {
  250.     NSMutableDictionary *plist = [super propertyList];
  251.     [plist setObject:propertyListFromInt(startCorner) forKey:START_CORNER_KEY];
  252.     return plist;
  253. }
  254.  
  255. - (NSString *)description
  256. {
  257.     return [(NSObject *)[self propertyList] description];
  258. }
  259.  
  260. - initFromPropertyList:(id)plist inDirectory:(NSString *)directory
  261. {
  262.     [super initFromPropertyList:plist inDirectory:directory];
  263.     startCorner = [[plist objectForKey:START_CORNER_KEY] intValue];
  264.     return self;
  265. }
  266.  
  267. @end
  268.