home *** CD-ROM | disk | FTP | other *** search
- #import "draw.h"
-
- @implementation Line : Graphic
- /*
- * Drawing a line is simple except that we have to keep track of whether
- * the line goes from the upper left to the lower right of the bounds or
- * from the lower left to the upper right. This can easily be determined
- * every time a corner is moved to a different corner. Therefore, all
- * that is needed is to override moveCorner:to:constrain: to keep track
- * of that. It is an efficiency hack to have the downhill flag kept
- * in our superclass's flags.
- *
- * This line is just a stub to get genstrings to generate
- * a .strings file entry for the name of this type of Graphic.
- * The name is used in the Undo New <Whatever> menu item.
- *
- * NXLocalString("Line", NULL, "Name of the tool that draws lines, i.e., the %s of the New %s operation.")
- */
-
- #define HIT_TOLERANCE 6.0
-
- + initialize
- {
- [Line setVersion:1];
- return self;
- }
-
- - init
- {
- [super init];
- startCorner = LOWER_LEFT;
- return self;
- }
-
- - (BOOL)isValid
- /*
- * A line is validly created if EITHER of the dimensions is big enough.
- */
- {
- return(bounds.size.width >= 5.0 || bounds.size.height >= 5.0);
- }
-
- static int oppositeCorner(int corner)
- {
- switch (corner) {
- case UPPER_RIGHT: return LOWER_LEFT;
- case LOWER_LEFT: return UPPER_RIGHT;
- case UPPER_LEFT: return LOWER_RIGHT;
- case LOWER_RIGHT: return UPPER_LEFT;
- }
-
- return corner;
- }
-
- - (int)moveCorner:(int)corner to:(const NXPoint *)point constrain:(BOOL)flag
- /*
- * Moves the corner to the specified point keeping track of whether the
- * line is going uphill or downhill and where the start corner has moved to.
- */
- {
- int newcorner;
-
- newcorner = [super moveCorner:corner to:point constrain:flag];
-
- if (newcorner != corner) {
- if ((newcorner == UPPER_RIGHT && corner == LOWER_LEFT) ||
- (newcorner == UPPER_LEFT && corner == LOWER_RIGHT) ||
- (newcorner == LOWER_RIGHT && corner == UPPER_LEFT) ||
- (newcorner == LOWER_LEFT && corner == UPPER_RIGHT)) {
- } else {
- gFlags.downhill = !gFlags.downhill;
- }
- if (startCorner == corner) {
- startCorner = newcorner;
- } else {
- startCorner = oppositeCorner(newcorner);
- }
- }
-
- return newcorner;
- }
-
- - constrainCorner:(int)corner toAspectRatio:(float)ratio
- /*
- * Constrains the corner to the nearest 15 degree angle. Ignores ratio.
- */
- {
- NXCoord width, height;
- double angle, distance;
-
- distance = hypot(bounds.size.width, bounds.size.height);
- angle = atan2(bounds.size.height, bounds.size.width);
- angle = (angle / 3.1415) * 180.0;
- angle = floor(angle / 15.0 + 0.5) * 15.0;
- angle = (angle / 180.0) * 3.1415;
- width = floor(cos(angle) * distance + 0.5);
- height = floor(sin(angle) * distance + 0.5);
-
- switch (corner) {
- case LOWER_LEFT:
- bounds.origin.x -= width - bounds.size.width;
- bounds.origin.y -= height - bounds.size.height;
- break;
- case UPPER_LEFT:
- bounds.origin.x -= width - bounds.size.width;
- break;
- case LOWER_RIGHT:
- bounds.origin.y -= height - bounds.size.height;
- break;
- }
-
- bounds.size.width = width;
- bounds.size.height = height;
-
- return self;
- }
-
- - (int)cornerMask
- /*
- * Only put corner knobs at the start and end of the line.
- */
- {
- if (gFlags.downhill) {
- return(UPPER_LEFT_MASK|LOWER_RIGHT_MASK);
- } else {
- return(LOWER_LEFT_MASK|UPPER_RIGHT_MASK);
- }
- }
-
- - draw
- /*
- * Calls drawLine to draw the line, then draws the arrows if any.
- */
- {
- if (bounds.size.width < 1.0 && bounds.size.height < 1.0) return self;
-
- [self setLineColor];
- [self drawLine];
-
- if (gFlags.arrow) {
- if (gFlags.downhill) {
- if (((gFlags.arrow != ARROW_AT_START) &&
- (startCorner == LOWER_RIGHT)) ||
- ((gFlags.arrow != ARROW_AT_END) &&
- (startCorner == UPPER_LEFT))) {
- PSArrow(bounds.origin.x,
- bounds.origin.y + bounds.size.height,
- [self arrowAngle:UPPER_LEFT]);
- }
- if (((gFlags.arrow != ARROW_AT_START) &&
- (startCorner == UPPER_LEFT)) ||
- ((gFlags.arrow != ARROW_AT_END) &&
- (startCorner == LOWER_RIGHT))) {
- PSArrow(bounds.origin.x + bounds.size.width,
- bounds.origin.y,
- [self arrowAngle:LOWER_RIGHT]);
- }
- } else {
- if (((gFlags.arrow != ARROW_AT_START) &&
- (startCorner == LOWER_LEFT)) ||
- ((gFlags.arrow != ARROW_AT_END) &&
- (startCorner == UPPER_RIGHT))) {
- PSArrow(bounds.origin.x + bounds.size.width,
- bounds.origin.y + bounds.size.height,
- [self arrowAngle:UPPER_RIGHT]);
- }
- if (((gFlags.arrow != ARROW_AT_START) &&
- (startCorner == UPPER_RIGHT)) ||
- ((gFlags.arrow != ARROW_AT_END) &&
- (startCorner == LOWER_LEFT))) {
- PSArrow(bounds.origin.x,
- bounds.origin.y,
- [self arrowAngle:LOWER_LEFT]);
- }
- }
- }
-
- return self;
- }
-
- - (BOOL)hit:(const NXPoint *)point
- /*
- * Gets a hit if the point is within HIT_TOLERANCE of the line.
- */
- {
- NXRect r;
- NXPoint p;
- float lineangle, pointangle, distance;
- float tolerance = HIT_TOLERANCE + linewidth;
-
- if (gFlags.locked || !gFlags.active) return NO;
-
- r = bounds;
- if (r.size.width < tolerance) {
- r.size.width += tolerance * 2.0;
- r.origin.x -= tolerance;
- }
- if (r.size.height < tolerance) {
- r.size.height += tolerance * 2.0;
- r.origin.y -= tolerance;
- }
-
- if (!NXMouseInRect(point, &r, NO)) return NO;
-
- p.x = point->x - bounds.origin.x;
- p.y = point->y - bounds.origin.y;
- if (gFlags.downhill) p.y = bounds.size.height - p.y;
- if (p.x && bounds.size.width) {
- lineangle = atan(bounds.size.height/bounds.size.width);
- pointangle = atan(p.y/p.x);
- distance = sqrt(p.x*p.x+p.y*p.y)*sin(fabs(lineangle-pointangle));
- } else {
- distance = fabs(point->x - bounds.origin.x);
- }
-
- return((distance - tolerance) <= linewidth);
- }
-
- /* Methods intended to be subclassed */
-
- - (float)arrowAngle:(int)corner
- /*
- * Returns the angle which the arrow should be drawn at.
- */
- {
- float angle;
- angle = atan2(bounds.size.height, bounds.size.width);
- angle = (angle / 3.1415) * 180.0;
- switch (corner) {
- case UPPER_RIGHT: return angle;
- case LOWER_LEFT: return angle + 180.0;
- case UPPER_LEFT: return 180.0 - angle;
- case LOWER_RIGHT: return - angle;
- }
- return angle;
- }
-
- - drawLine
- /*
- * The actual line drawing is done here so that it can be subclassed.
- */
- {
- if (gFlags.downhill) {
- PSLine(bounds.origin.x, bounds.origin.y + bounds.size.height,
- bounds.size.width, - bounds.size.height);
- } else {
- PSLine(bounds.origin.x, bounds.origin.y,
- bounds.size.width, bounds.size.height);
- }
- return self;
- }
-
- /* Archiving methods */
-
- - write:(NXTypedStream *)stream
- {
- [super write:stream];
- NXWriteType(stream, "i", &startCorner);
- return self;
- }
-
- - read:(NXTypedStream *)stream
- {
- [super read:stream];
- if (NXTypedStreamClassVersion(stream, "Line") > 0) {
- NXReadType(stream, "i", &startCorner);
- } else {
- startCorner = LOWER_LEFT;
- }
- return self;
- }
-
- @end
-