|
|
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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.