home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.3 (Developer) / NeXT_Developer-3.3.iso / NextDeveloper / Examples / AppKit / Draw / Line.m < prev    next >
Encoding:
Text File  |  1992-07-20  |  6.7 KB  |  274 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.  * NXLocalString("Line", NULL, "Name of the tool that draws lines, i.e., the %s of the New %s operation.")
  18.  */
  19.  
  20. #define HIT_TOLERANCE 6.0
  21.  
  22. + initialize
  23. {
  24.     [Line setVersion:1];
  25.     return self;
  26. }
  27.  
  28. - init
  29. {
  30.     [super init];
  31.     startCorner = LOWER_LEFT;
  32.     return self;
  33. }
  34.  
  35. - (BOOL)isValid
  36. /*
  37.  * A line is validly created if EITHER of the dimensions is big enough.
  38.  */
  39. {
  40.     return(bounds.size.width >= 5.0 || bounds.size.height >= 5.0);
  41. }
  42.  
  43. static int oppositeCorner(int corner)
  44. {
  45.     switch (corner) {
  46.     case UPPER_RIGHT: return LOWER_LEFT;
  47.     case LOWER_LEFT: return UPPER_RIGHT;
  48.     case UPPER_LEFT: return LOWER_RIGHT;
  49.     case LOWER_RIGHT: return UPPER_LEFT;
  50.     }
  51.  
  52.     return corner;
  53. }
  54.  
  55. - (int)moveCorner:(int)corner to:(const NXPoint *)point constrain:(BOOL)flag
  56. /*
  57.  * Moves the corner to the specified point keeping track of whether the
  58.  * line is going uphill or downhill and where the start corner has moved to.
  59.  */
  60. {
  61.     int newcorner;
  62.  
  63.     newcorner = [super moveCorner:corner to:point constrain:flag];
  64.  
  65.     if (newcorner != corner) {
  66.     if ((newcorner == UPPER_RIGHT && corner == LOWER_LEFT) ||
  67.         (newcorner == UPPER_LEFT && corner == LOWER_RIGHT) ||
  68.         (newcorner == LOWER_RIGHT && corner == UPPER_LEFT) ||
  69.         (newcorner == LOWER_LEFT && corner == UPPER_RIGHT)) {
  70.     } else {
  71.         gFlags.downhill = !gFlags.downhill;
  72.     }
  73.     if (startCorner == corner) {
  74.         startCorner = newcorner;
  75.     } else {
  76.         startCorner = oppositeCorner(newcorner);
  77.     }
  78.     }
  79.  
  80.     return newcorner;
  81. }
  82.  
  83. - constrainCorner:(int)corner toAspectRatio:(float)ratio
  84. /*
  85.  * Constrains the corner to the nearest 15 degree angle.  Ignores ratio.
  86.  */
  87. {
  88.     NXCoord width, height;
  89.     double angle, distance;
  90.  
  91.     distance = hypot(bounds.size.width, bounds.size.height);
  92.     angle = atan2(bounds.size.height, bounds.size.width);
  93.     angle = (angle / 3.1415) * 180.0;
  94.     angle = floor(angle / 15.0 + 0.5) * 15.0;
  95.     angle = (angle / 180.0) * 3.1415;
  96.     width = floor(cos(angle) * distance + 0.5);
  97.     height = floor(sin(angle) * distance + 0.5);
  98.  
  99.     switch (corner) {
  100.     case LOWER_LEFT:
  101.         bounds.origin.x -= width - bounds.size.width;
  102.         bounds.origin.y -= height - bounds.size.height;
  103.         break;
  104.     case UPPER_LEFT:
  105.         bounds.origin.x -= width - bounds.size.width;
  106.         break;
  107.     case LOWER_RIGHT:
  108.         bounds.origin.y -= height - bounds.size.height;
  109.         break;
  110.     }
  111.  
  112.     bounds.size.width = width;
  113.     bounds.size.height = height;
  114.  
  115.     return self;
  116. }
  117.  
  118. - (int)cornerMask
  119. /*
  120.  * Only put corner knobs at the start and end of the line.
  121.  */
  122. {
  123.     if (gFlags.downhill) {
  124.     return(UPPER_LEFT_MASK|LOWER_RIGHT_MASK);
  125.     } else {
  126.     return(LOWER_LEFT_MASK|UPPER_RIGHT_MASK);
  127.     }
  128. }
  129.  
  130. - draw
  131. /*
  132.  * Calls drawLine to draw the line, then draws the arrows if any.
  133.  */
  134. {
  135.     if (bounds.size.width < 1.0 && bounds.size.height < 1.0) return self;
  136.  
  137.     [self setLineColor];
  138.     [self drawLine];
  139.  
  140.     if (gFlags.arrow) {
  141.     if (gFlags.downhill) {
  142.         if (((gFlags.arrow != ARROW_AT_START) &&
  143.              (startCorner == LOWER_RIGHT)) ||
  144.         ((gFlags.arrow != ARROW_AT_END) &&
  145.          (startCorner == UPPER_LEFT))) {
  146.         PSArrow(bounds.origin.x,
  147.             bounds.origin.y + bounds.size.height,
  148.             [self arrowAngle:UPPER_LEFT]);        
  149.         }
  150.         if (((gFlags.arrow != ARROW_AT_START) &&
  151.              (startCorner == UPPER_LEFT)) ||
  152.         ((gFlags.arrow != ARROW_AT_END) &&
  153.          (startCorner == LOWER_RIGHT))) {
  154.         PSArrow(bounds.origin.x + bounds.size.width,
  155.             bounds.origin.y,
  156.             [self arrowAngle:LOWER_RIGHT]);        
  157.         }
  158.     } else {
  159.         if (((gFlags.arrow != ARROW_AT_START) &&
  160.              (startCorner == LOWER_LEFT)) ||
  161.         ((gFlags.arrow != ARROW_AT_END) &&
  162.          (startCorner == UPPER_RIGHT))) {
  163.         PSArrow(bounds.origin.x + bounds.size.width,
  164.             bounds.origin.y + bounds.size.height,
  165.             [self arrowAngle:UPPER_RIGHT]);        
  166.         }
  167.         if (((gFlags.arrow != ARROW_AT_START) &&
  168.              (startCorner == UPPER_RIGHT)) ||
  169.         ((gFlags.arrow != ARROW_AT_END) &&
  170.          (startCorner == LOWER_LEFT))) {
  171.         PSArrow(bounds.origin.x,
  172.             bounds.origin.y,
  173.             [self arrowAngle:LOWER_LEFT]);        
  174.         }
  175.     }
  176.     }
  177.  
  178.     return self;
  179. }
  180.  
  181. - (BOOL)hit:(const NXPoint *)point
  182. /*
  183.  * Gets a hit if the point is within HIT_TOLERANCE of the line.
  184.  */
  185. {
  186.     NXRect r;
  187.     NXPoint p;
  188.     float lineangle, pointangle, distance;
  189.     float tolerance = HIT_TOLERANCE + linewidth;
  190.  
  191.     if (gFlags.locked || !gFlags.active) return NO;
  192.  
  193.     r = bounds;
  194.     if (r.size.width < tolerance) {
  195.     r.size.width += tolerance * 2.0;
  196.     r.origin.x -= tolerance;
  197.     }
  198.     if (r.size.height < tolerance) {
  199.     r.size.height += tolerance * 2.0;
  200.     r.origin.y -= tolerance;
  201.     }
  202.  
  203.     if (!NXMouseInRect(point, &r, NO)) return NO;
  204.  
  205.     p.x = point->x - bounds.origin.x;
  206.     p.y = point->y - bounds.origin.y;
  207.     if (gFlags.downhill) p.y = bounds.size.height - p.y;
  208.     if (p.x && bounds.size.width) {
  209.     lineangle = atan(bounds.size.height/bounds.size.width);
  210.     pointangle = atan(p.y/p.x);
  211.     distance = sqrt(p.x*p.x+p.y*p.y)*sin(fabs(lineangle-pointangle));
  212.     } else {
  213.     distance = fabs(point->x - bounds.origin.x);
  214.     }
  215.  
  216.     return((distance - tolerance) <= linewidth);
  217. }
  218.  
  219. /* Methods intended to be subclassed */
  220.  
  221. - (float)arrowAngle:(int)corner
  222. /*
  223.  * Returns the angle which the arrow should be drawn at.
  224.  */
  225. {
  226.     float angle;
  227.     angle = atan2(bounds.size.height, bounds.size.width);
  228.     angle = (angle / 3.1415) * 180.0;
  229.     switch (corner) {
  230.     case UPPER_RIGHT: return angle;
  231.     case LOWER_LEFT: return angle + 180.0;
  232.     case UPPER_LEFT: return 180.0 - angle;
  233.     case LOWER_RIGHT: return - angle;
  234.     }
  235.     return angle;
  236. }
  237.  
  238. - drawLine
  239. /*
  240.  * The actual line drawing is done here so that it can be subclassed.
  241.  */
  242. {
  243.     if (gFlags.downhill) {
  244.     PSLine(bounds.origin.x, bounds.origin.y + bounds.size.height,
  245.            bounds.size.width, - bounds.size.height);
  246.     } else {
  247.     PSLine(bounds.origin.x, bounds.origin.y,
  248.            bounds.size.width, bounds.size.height);
  249.     }
  250.     return self;
  251. }
  252.  
  253. /* Archiving methods */
  254.  
  255. - write:(NXTypedStream *)stream
  256. {
  257.     [super write:stream];
  258.     NXWriteType(stream, "i", &startCorner);
  259.     return self;
  260. }
  261.  
  262. - read:(NXTypedStream *)stream
  263. {
  264.     [super read:stream];
  265.     if (NXTypedStreamClassVersion(stream, "Line") > 0) {
  266.     NXReadType(stream, "i", &startCorner);
  267.     } else {
  268.     startCorner = LOWER_LEFT;
  269.     }
  270.     return self;
  271. }
  272.  
  273. @end
  274.