Annotation of Examples/AppKit/Draw/Scribble.m, revision 1.1.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.