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