This is Line.m in view mode; [Download] [Up]
#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 == LOWER_LEFT)||(newcorner == LOWER_RIGHT)) { gFlags.downhill = 1; } else { gFlags.downhill = 0; } if ((newcorner == UPPER_LEFT)||(newcorner == LOWER_LEFT)) { gFlags.goleft = 1; } else { gFlags.goleft = 0; } 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) { if (gFlags.goleft) { return(LOWER_LEFT_MASK|UPPER_RIGHT_MASK); } else { return(UPPER_LEFT_MASK|LOWER_RIGHT_MASK); } } else { if (gFlags.goleft) { 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. */ { unsigned int code; if (bounds.size.width < 1.0 && bounds.size.height < 1.0) return self; [self setLineColor]; [self drawLine]; if (gFlags.arrow) { code = gFlags.arrow; if (gFlags.downhill) code += 8; if (gFlags.goleft) code += 4; /* upper right */ if ((code == 2)||(code == 3)||(code == 13)||(code == 15)) { PSArrow(bounds.origin.x + bounds.size.width, bounds.origin.y + bounds.size.height, [self arrowAngle:UPPER_RIGHT]); } /* upper left */ if ((code == 6)||(code == 7)||(code == 9)||(code == 11)) { PSArrow(bounds.origin.x, bounds.origin.y + bounds.size.height, [self arrowAngle:UPPER_LEFT]); } /* lower left */ if ((code == 1)||(code == 3)||(code == 14)||(code == 15)) { PSArrow(bounds.origin.x, bounds.origin.y, [self arrowAngle:LOWER_LEFT]); } /* lower right */ if ((code == 5)||(code == 7)||(code == 10)||(code == 11)) { PSArrow(bounds.origin.x + bounds.size.width, bounds.origin.y, [self arrowAngle:LOWER_RIGHT]); } } 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 && !gFlags.goleft) || (!gFlags.downhill && gFlags.goleft)) 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) { if (gFlags.goleft) { PSLine(bounds.origin.x + bounds.size.width, bounds.origin.y + bounds.size.height, - bounds.size.width, - bounds.size.height); } else { PSLine(bounds.origin.x, bounds.origin.y + bounds.size.height, bounds.size.width, - bounds.size.height); } } else { if (gFlags.goleft) { PSLine(bounds.origin.x + bounds.size.width, bounds.origin.y, - 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
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.