Annotation of Examples/AppKit/Draw/Line.m, revision 1.1.1.1

1.1       root        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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.