Annotation of Examples/AppKit/Draw/Scribble.m, revision 1.1

1.1     ! root        1: #import "draw.h"
        !             2: 
        !             3: /*
        !             4:  * This line is just a stub to get genstrings to generate
        !             5:  * a .strings file entry for the name of this type of Graphic.
        !             6:  * The name is used in the Undo New <Whatever> menu item.
        !             7:  *
        !             8:  * NXLocalString("Scribble", NULL, "Name of the tool that draws scribbles, i.e., the %s of the New %s operation.")
        !             9:  */
        !            10: 
        !            11: @implementation Scribble : Graphic
        !            12: 
        !            13: static NXPoint lastPoint;      /* used in creating only */
        !            14: 
        !            15: + initialize
        !            16: /*
        !            17:  * This bumps the class version so that we can compatibly read
        !            18:  * old Graphic objects out of an archive.
        !            19:  */
        !            20: {
        !            21:     [Scribble setVersion:1];
        !            22:     return self;
        !            23: }
        !            24: 
        !            25: + cursor
        !            26: /*
        !            27:  * A Scribble uses a pencil as its cursor.
        !            28:  */
        !            29: {
        !            30:     NXPoint spot;
        !            31:     static NXCursor *cursor = nil;
        !            32: 
        !            33:     if (!cursor) {
        !            34:        cursor = [NXCursor newFromImage:[NXImage newFromSection:"Pencil.tiff"]];
        !            35:        spot.x = 0.0; spot.y = 15.0;
        !            36:        [cursor setHotSpot:&spot];
        !            37:     }
        !            38: 
        !            39:     return cursor ? cursor : [super cursor];
        !            40: }
        !            41: 
        !            42: - free
        !            43: {
        !            44:     NX_FREE(points);
        !            45:     NX_FREE(userPathOps);
        !            46:     return [super free];
        !            47: }
        !            48: 
        !            49: - allocateChunk
        !            50: /*
        !            51:  * The Scribble's storage is allocated in chunks.
        !            52:  * This allocates another chunk.
        !            53:  */
        !            54: {
        !            55:     int i, newSize;
        !            56: 
        !            57:     newSize = length + CHUNK_SIZE;
        !            58:     if (points) {
        !            59:        NX_ZONEREALLOC([self zone], points, float, newSize << 1);
        !            60:        NX_ZONEREALLOC([self zone], userPathOps, char, newSize);
        !            61:     } else {
        !            62:        NX_ZONEMALLOC([self zone], points, float, newSize << 1);
        !            63:        NX_ZONEMALLOC([self zone], userPathOps, char, newSize);
        !            64:     }
        !            65:     for (i = newSize - 1; i >= length; i--) {
        !            66:        userPathOps[i] = dps_rlineto;
        !            67:     }
        !            68: 
        !            69:     return self;
        !            70: }
        !            71: 
        !            72: - (float)naturalAspectRatio
        !            73: /*
        !            74:  * The Scribble's natural aspect ratio is the one it was created with.
        !            75:  */
        !            76: {
        !            77:     return (gFlags.initialized ? ((bbox[2]-bbox[0])/(bbox[3]-bbox[1])) : 0.0);
        !            78: }
        !            79: 
        !            80: - (int)moveCorner:(int)corner to:(const NXPoint *)point constrain:(BOOL)flag
        !            81: /*
        !            82:  * After the Scribble is created (gFlags.initialized == YES), this method
        !            83:  * just returns super's implementation.  During creation, every time the
        !            84:  * "corner" is moved, a new line segment is added to the Scribble and
        !            85:  * the bounding box is expanded if necessary.
        !            86:  */
        !            87: {
        !            88:     float *p;
        !            89: 
        !            90:     if (gFlags.initialized) {
        !            91:        return [super moveCorner:corner to:point constrain:flag];
        !            92:     }
        !            93: 
        !            94:     if (!(point->x - lastPoint.x || point->y - lastPoint.y)) return corner;
        !            95: 
        !            96:     length++;
        !            97: 
        !            98:     if (!(length % CHUNK_SIZE)) [self allocateChunk];
        !            99: 
        !           100:     p = points + (length << 1);
        !           101:     *p++ = point->x - lastPoint.x;
        !           102:     *p = point->y - lastPoint.y;
        !           103:     lastPoint = *point;
        !           104: 
        !           105:     bbox[2] = MAX(point->x, bbox[2]);
        !           106:     bbox[0] = MIN(point->x, bbox[0]);
        !           107:     bbox[3] = MAX(point->y, bbox[3]);
        !           108:     bbox[1] = MIN(point->y, bbox[1]);
        !           109: 
        !           110:     bounds.origin.x = bbox[0];
        !           111:     bounds.origin.y = bbox[1];
        !           112:     bounds.size.width = bbox[2] - bbox[0];
        !           113:     bounds.size.height = bbox[3] - bbox[1];
        !           114: 
        !           115:     return corner;
        !           116: }
        !           117: 
        !           118: 
        !           119: - (BOOL)create:(NXEvent *)event in:view
        !           120: /*
        !           121:  * Before creating, an initial chunk is initialized, and the userPathOps
        !           122:  * are initialized.  The lastPoint is also remembered as the start point.
        !           123:  * After the Scribble is created, the initialized flag is set.
        !           124:  */
        !           125: {
        !           126:     NXPoint p;
        !           127: 
        !           128:     [self allocateChunk];
        !           129:     userPathOps[0] = dps_moveto;    
        !           130:     p = event->location;
        !           131:     [view convertPoint:&p fromView:nil];
        !           132:     [view grid:&p];
        !           133:     points[0] = p.x;
        !           134:     points[1] = p.y;
        !           135:     lastPoint = p;
        !           136:     bbox[0] = bbox[2] = p.x;
        !           137:     bbox[1] = bbox[3] = p.y;
        !           138:     bounds.origin = p;
        !           139:     bounds.size.width = bounds.size.height = 0.0;
        !           140: 
        !           141:     if ([super create:event in:view]) {
        !           142:        gFlags.initialized = YES;
        !           143:        return YES;
        !           144:     }
        !           145: 
        !           146:     return NO;
        !           147: }
        !           148: 
        !           149: 
        !           150: - draw
        !           151: /*
        !           152:  * The Scribble is drawn simply by scaling appropriately from its
        !           153:  * initial bounding box and drawing the user path.
        !           154:  */
        !           155: {
        !           156:     NXCoord x, y;
        !           157:     NXPoint p1, p2;
        !           158:     int i, count, coords;
        !           159:     float angle, sx, sy, tx, ty;
        !           160: 
        !           161:     if (bounds.size.width < 1.0 || bounds.size.height < 1.0) return self;
        !           162: 
        !           163:     if (length && (bbox[2] - bbox[0]) && (bbox[3] - bbox[1])) {
        !           164:        sx = bounds.size.width / (bbox[2] - bbox[0]);
        !           165:        sy = bounds.size.height / (bbox[3] - bbox[1]);
        !           166:        tx = (bounds.origin.x +
        !           167:              ((points[0]-bbox[0]) / (bbox[2]-bbox[0]) * bounds.size.width)) -
        !           168:                points[0] * sx;
        !           169:        ty = (bounds.origin.y +
        !           170:              ((points[1]-bbox[1]) / (bbox[3]-bbox[1]) * bounds.size.height)) -
        !           171:                points[1] * sy;
        !           172:        if (gFlags.arrow && ![self fill] && (sx != 1.0 || sy != 1.0 || tx || ty)) {
        !           173:            PSgsave();
        !           174:        }
        !           175:        if ([self fill]) {
        !           176:            PSgsave();
        !           177:            PStranslate(tx, ty);
        !           178:            PSscale(sx, sy);
        !           179:            [self setFillColor];
        !           180:            DPSDoUserPath(points, (length + 1) << 1, dps_float,
        !           181:                          userPathOps, length + 1, bbox,
        !           182:                          gFlags.eofill ? dps_ueofill : dps_ufill);
        !           183:            PSgrestore();
        !           184:        }
        !           185:        if (!gFlags.nooutline) {
        !           186:            PStranslate(tx, ty);
        !           187:            PSscale(sx, sy);
        !           188:            [self setLineColor];
        !           189:            DPSDoUserPath(points, (length + 1) << 1, dps_float,
        !           190:                          userPathOps, length + 1, bbox, dps_ustroke);
        !           191:        }
        !           192:        if (gFlags.arrow && ![self fill]) {
        !           193:            if (sx != 1.0 || sy != 1.0 || tx || ty) {
        !           194:                PSgrestore();
        !           195:                [self setLineColor];
        !           196:            }
        !           197:            if (gFlags.arrow != ARROW_AT_END) {
        !           198:                i = 0;
        !           199:                p1.x = points[i++];
        !           200:                p1.y = points[i++];
        !           201:                p2 = p1;
        !           202:                p2.x += points[i++];
        !           203:                p2.y += points[i++];
        !           204:                count = length - 1;
        !           205:                while (hypot((p1.x-p2.x)*sx,(p1.y-p2.y)*sy) < 7.0 && count--) {
        !           206:                    p2.x += points[i++];
        !           207:                    p2.y += points[i++];
        !           208:                }
        !           209:                angle = atan2((p1.y-p2.y)*sy, (p1.x-p2.x)*sx);
        !           210:                angle = (angle / 3.1415) * 180.0;
        !           211:                x = bounds.origin.x + (p1.x - bbox[0]) * sx;
        !           212:                y = bounds.origin.y + (p1.y - bbox[1]) * sy;
        !           213:                PSArrow(x, y, angle);
        !           214:            }
        !           215:            if (gFlags.arrow != ARROW_AT_START) {
        !           216:                i = 0;
        !           217:                coords = (length + 1) << 1;
        !           218:                p1.x = points[i++];
        !           219:                p1.y = points[i++];
        !           220:                while (i < coords) {
        !           221:                    p1.x += points[i++];
        !           222:                    p1.y += points[i++];
        !           223:                }
        !           224:                p2 = p1;
        !           225:                i = coords;
        !           226:                p2.y -= points[--i];
        !           227:                p2.x -= points[--i];
        !           228:                count = length - 1;
        !           229:                while (hypot((p2.x-p1.x)*sx,(p2.y-p1.y)*sy) < 7.0 && count--) {
        !           230:                    p2.y -= points[--i];
        !           231:                    p2.x -= points[--i];
        !           232:                }
        !           233:                angle = atan2((p1.y-p2.y)*sy, (p1.x-p2.x)*sx);
        !           234:                angle = (angle / 3.1415) * 180.0;
        !           235:                x = bounds.origin.x + (p1.x - bbox[0]) * sx;
        !           236:                y = bounds.origin.y + (p1.y - bbox[1]) * sy;
        !           237:                PSArrow(x, y, angle);
        !           238:            }
        !           239:        }
        !           240:     }
        !           241: 
        !           242:     return self;
        !           243: }
        !           244: 
        !           245: - write:(NXTypedStream *)stream
        !           246: /*
        !           247:  * The Scribble is written out by writing its length (in segments), its
        !           248:  * bounding box, and all the points.
        !           249:  */
        !           250: {
        !           251:     int i, numFloats;
        !           252: 
        !           253:     [super write:stream];
        !           254: 
        !           255:     NXWriteTypes(stream,"iffff",&length,&bbox[0],&bbox[1],&bbox[2],&bbox[3]);
        !           256: 
        !           257:     numFloats = (length + 1) << 1;
        !           258:     for (i = 0; i < numFloats; i++) {
        !           259:        NXWriteTypes(stream, "f", &points[i]);
        !           260:     }
        !           261: 
        !           262:     return self;
        !           263: }
        !           264: 
        !           265: - read:(NXTypedStream *)stream
        !           266: {
        !           267:     int i;
        !           268:     float *p;
        !           269: 
        !           270:     [super read:stream];
        !           271: 
        !           272:     NXReadTypes(stream,"iffff",&length,&bbox[0],&bbox[1],&bbox[2],&bbox[3]);
        !           273: 
        !           274:     NX_ZONEMALLOC([self zone], points, float, (length + 1) << 1);
        !           275:     NX_ZONEMALLOC([self zone], userPathOps, char, length + 1);
        !           276: 
        !           277:     p = points;
        !           278:     for (i = 0; i <= length; i++) {
        !           279:        NXReadTypes(stream, "f", p++);
        !           280:        NXReadTypes(stream, "f", p++);
        !           281:        userPathOps[i] = dps_rlineto;
        !           282:     }
        !           283:     userPathOps[0] = dps_moveto;
        !           284: 
        !           285:     return self;
        !           286: }
        !           287: 
        !           288: @end
        !           289: 

unix.superglobalmegacorp.com

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