home *** CD-ROM | disk | FTP | other *** search
- #import "draw.h"
-
- /*
- * 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("Scribble", NULL, "Name of the tool that draws scribbles, i.e., the %s of the New %s operation.")
- */
-
- @implementation Scribble : Graphic
-
- static NXPoint lastPoint; /* used in creating only */
-
- + initialize
- /*
- * This bumps the class version so that we can compatibly read
- * old Graphic objects out of an archive.
- */
- {
- [Scribble setVersion:1];
- return self;
- }
-
- + cursor
- /*
- * A Scribble uses a pencil as its cursor.
- */
- {
- NXPoint spot;
- static NXCursor *cursor = nil;
-
- if (!cursor) {
- cursor = [NXCursor newFromImage:[NXImage newFromSection:"Pencil.tiff"]];
- spot.x = 0.0; spot.y = 15.0;
- [cursor setHotSpot:&spot];
- }
-
- return cursor ? cursor : [super cursor];
- }
-
- - free
- {
- NX_FREE(points);
- NX_FREE(userPathOps);
- return [super free];
- }
-
- - allocateChunk
- /*
- * The Scribble's storage is allocated in chunks.
- * This allocates another chunk.
- */
- {
- int i, newSize;
-
- newSize = length + CHUNK_SIZE;
- if (points) {
- NX_ZONEREALLOC([self zone], points, float, newSize << 1);
- NX_ZONEREALLOC([self zone], userPathOps, char, newSize);
- } else {
- NX_ZONEMALLOC([self zone], points, float, newSize << 1);
- NX_ZONEMALLOC([self zone], userPathOps, char, newSize);
- }
- for (i = newSize - 1; i >= length; i--) {
- userPathOps[i] = dps_rlineto;
- }
-
- return self;
- }
-
- - (float)naturalAspectRatio
- /*
- * The Scribble's natural aspect ratio is the one it was created with.
- */
- {
- return (gFlags.initialized ? ((bbox[2]-bbox[0])/(bbox[3]-bbox[1])) : 0.0);
- }
-
- - (int)moveCorner:(int)corner to:(const NXPoint *)point constrain:(BOOL)flag
- /*
- * After the Scribble is created (gFlags.initialized == YES), this method
- * just returns super's implementation. During creation, every time the
- * "corner" is moved, a new line segment is added to the Scribble and
- * the bounding box is expanded if necessary.
- */
- {
- float *p;
-
- if (gFlags.initialized) {
- return [super moveCorner:corner to:point constrain:flag];
- }
-
- if (!(point->x - lastPoint.x || point->y - lastPoint.y)) return corner;
-
- length++;
-
- if (!(length % CHUNK_SIZE)) [self allocateChunk];
-
- p = points + (length << 1);
- *p++ = point->x - lastPoint.x;
- *p = point->y - lastPoint.y;
- lastPoint = *point;
-
- bbox[2] = MAX(point->x, bbox[2]);
- bbox[0] = MIN(point->x, bbox[0]);
- bbox[3] = MAX(point->y, bbox[3]);
- bbox[1] = MIN(point->y, bbox[1]);
-
- bounds.origin.x = bbox[0];
- bounds.origin.y = bbox[1];
- bounds.size.width = bbox[2] - bbox[0];
- bounds.size.height = bbox[3] - bbox[1];
-
- return corner;
- }
-
-
- - (BOOL)create:(NXEvent *)event in:view
- /*
- * Before creating, an initial chunk is initialized, and the userPathOps
- * are initialized. The lastPoint is also remembered as the start point.
- * After the Scribble is created, the initialized flag is set.
- */
- {
- NXPoint p;
-
- [self allocateChunk];
- userPathOps[0] = dps_moveto;
- p = event->location;
- [view convertPoint:&p fromView:nil];
- [view grid:&p];
- points[0] = p.x;
- points[1] = p.y;
- lastPoint = p;
- bbox[0] = bbox[2] = p.x;
- bbox[1] = bbox[3] = p.y;
- bounds.origin = p;
- bounds.size.width = bounds.size.height = 0.0;
-
- if ([super create:event in:view]) {
- gFlags.initialized = YES;
- return YES;
- }
-
- return NO;
- }
-
-
- - draw
- /*
- * The Scribble is drawn simply by scaling appropriately from its
- * initial bounding box and drawing the user path.
- */
- {
- NXCoord x, y;
- NXPoint p1, p2;
- int i, count, coords;
- float angle, sx, sy, tx, ty;
-
- if (bounds.size.width < 1.0 || bounds.size.height < 1.0) return self;
-
- if (length && (bbox[2] - bbox[0]) && (bbox[3] - bbox[1])) {
- sx = bounds.size.width / (bbox[2] - bbox[0]);
- sy = bounds.size.height / (bbox[3] - bbox[1]);
- tx = (bounds.origin.x +
- ((points[0]-bbox[0]) / (bbox[2]-bbox[0]) * bounds.size.width)) -
- points[0] * sx;
- ty = (bounds.origin.y +
- ((points[1]-bbox[1]) / (bbox[3]-bbox[1]) * bounds.size.height)) -
- points[1] * sy;
- if (gFlags.arrow && ![self fill] && (sx != 1.0 || sy != 1.0 || tx || ty)) {
- PSgsave();
- }
- if ([self fill]) {
- PSgsave();
- PStranslate(tx, ty);
- PSscale(sx, sy);
- [self setFillColor];
- DPSDoUserPath(points, (length + 1) << 1, dps_float,
- userPathOps, length + 1, bbox,
- gFlags.eofill ? dps_ueofill : dps_ufill);
- PSgrestore();
- }
- if (!gFlags.nooutline) {
- PStranslate(tx, ty);
- PSscale(sx, sy);
- [self setLineColor];
- DPSDoUserPath(points, (length + 1) << 1, dps_float,
- userPathOps, length + 1, bbox, dps_ustroke);
- }
- if (gFlags.arrow && ![self fill]) {
- if (sx != 1.0 || sy != 1.0 || tx || ty) {
- PSgrestore();
- [self setLineColor];
- }
- if (gFlags.arrow != ARROW_AT_END) {
- i = 0;
- p1.x = points[i++];
- p1.y = points[i++];
- p2 = p1;
- p2.x += points[i++];
- p2.y += points[i++];
- count = length - 1;
- while (hypot((p1.x-p2.x)*sx,(p1.y-p2.y)*sy) < 7.0 && count--) {
- p2.x += points[i++];
- p2.y += points[i++];
- }
- angle = atan2((p1.y-p2.y)*sy, (p1.x-p2.x)*sx);
- angle = (angle / 3.1415) * 180.0;
- x = bounds.origin.x + (p1.x - bbox[0]) * sx;
- y = bounds.origin.y + (p1.y - bbox[1]) * sy;
- PSArrow(x, y, angle);
- }
- if (gFlags.arrow != ARROW_AT_START) {
- i = 0;
- coords = (length + 1) << 1;
- p1.x = points[i++];
- p1.y = points[i++];
- while (i < coords) {
- p1.x += points[i++];
- p1.y += points[i++];
- }
- p2 = p1;
- i = coords;
- p2.y -= points[--i];
- p2.x -= points[--i];
- count = length - 1;
- while (hypot((p2.x-p1.x)*sx,(p2.y-p1.y)*sy) < 7.0 && count--) {
- p2.y -= points[--i];
- p2.x -= points[--i];
- }
- angle = atan2((p1.y-p2.y)*sy, (p1.x-p2.x)*sx);
- angle = (angle / 3.1415) * 180.0;
- x = bounds.origin.x + (p1.x - bbox[0]) * sx;
- y = bounds.origin.y + (p1.y - bbox[1]) * sy;
- PSArrow(x, y, angle);
- }
- }
- }
-
- return self;
- }
-
- - write:(NXTypedStream *)stream
- /*
- * The Scribble is written out by writing its length (in segments), its
- * bounding box, and all the points.
- */
- {
- int i, numFloats;
-
- [super write:stream];
-
- NXWriteTypes(stream,"iffff",&length,&bbox[0],&bbox[1],&bbox[2],&bbox[3]);
-
- numFloats = (length + 1) << 1;
- for (i = 0; i < numFloats; i++) {
- NXWriteTypes(stream, "f", &points[i]);
- }
-
- return self;
- }
-
- - read:(NXTypedStream *)stream
- {
- int i;
- float *p;
-
- [super read:stream];
-
- NXReadTypes(stream,"iffff",&length,&bbox[0],&bbox[1],&bbox[2],&bbox[3]);
-
- NX_ZONEMALLOC([self zone], points, float, (length + 1) << 1);
- NX_ZONEMALLOC([self zone], userPathOps, char, length + 1);
-
- p = points;
- for (i = 0; i <= length; i++) {
- NXReadTypes(stream, "f", p++);
- NXReadTypes(stream, "f", p++);
- userPathOps[i] = dps_rlineto;
- }
- userPathOps[0] = dps_moveto;
-
- return self;
- }
-
- @end
-
-