|
|
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("Polygon", NULL, "Name of the tool that draws polygons, i.e., the %s of the New %s operation.")
9: */
10:
11: @implementation Polygon
12:
13: + initialize
14: /*
15: * This bumps the class version so that we can compatibly read
16: * old Graphic objects out of an archive.
17: */
18: {
19: [Polygon setVersion:1];
20: return self;
21: }
22:
23: + cursor
24: /*
25: * The cursor inherited from Scribble is a pencil.
26: * That's not very appropriate, so CrossCursor is used instead.
27: */
28: {
29: return CrossCursor;
30: }
31:
32: static void getRectFromBBox(NXRect *r, float x1, float y1, float x2, float y2)
33: /*
34: * Takes two points (x1, y1) and (x2, y2) and updates the r rect to
35: * equal that bounding box.
36: */
37: {
38: r->size.width = x1 - x2;
39: r->size.height = y1 - y2;
40: if (r->size.width < 0.0) {
41: r->origin.x = x2 + r->size.width;
42: r->size.width = 0.0 - r->size.width;
43: } else r->origin.x = x2;
44: if (r->size.height < 0.0) {
45: r->origin.y = y2 + r->size.height;
46: r->size.height = 0.0 - r->size.height;
47: } else r->origin.y = y2;
48: }
49:
50: /*
51: * This class probably is probably not implemented in the optimal way,
52: * but it shows how an existing implementation (i.e. Scribble) can be
53: * used to implement some other object.
54: *
55: * This method creates a polygon. The user must drag out each segment of
56: * the polygon clicking to make a corner, finally ending with a double click.
57: *
58: * Start by getting the starting point of the polygon from the mouse down
59: * event passed in the event parameter (if the ALT key is not down, then we
60: * will close the path even if the user does not explicitly do so).
61: *
62: * Next, we initialize a chunk of space for the points to be stored in
63: * and initialize point[0] and point[1] to be the starting point (since the
64: * first thing in the userpath is a moveto). We also initialize our bounding
65: * box to contain only that point.
66: *
67: * p represents the last point the user moved the mouse to. We initialize it
68: * to start before entering the tracking loop.
69: *
70: * Inside the loop, last represents the last point the user confirmed (by
71: * clicking) as opposed to p, the last point the user moved to. We update
72: * last every time we start the segment tracking loop (the inner,
73: * while (event->type != NX_MOUSEUP) loop).
74: *
75: * In the segment tracking loop, r represents the rectangle which must be
76: * redrawn to get rid of the last time we drew the segment we are currently
77: * tracking. After we [view drawSelf:&r :1] to clear out the last segment,
78: * we recalculate the value of r for the next time around the loop. Finally,
79: * we draw ourselves (i.e. all the other segments besides the one we are
80: * currently tracking) and then draw the segment we are currently tracking.
81: *
82: * After tracking the segment, we check to see if we are done.
83: * We are finished if any of the following are true:
84: * 1. The last segment the user created was smaller than a gridSpacing.
85: * 2. The user clicked on the starting point (thereby closing the path).
86: * 3. The mouse down is outside the view's bounds.
87: * 4. A kit defined or system defined event comes through.
88: *
89: * If we are not done (or we need to close the path), then we store the
90: * new point pair into points (reallocating our points
91: * and userPathOps arrays if we are out of room). We then update our bounding
92: * box to reflect the new point and update our bounds to equal our bounding
93: * box. If we aren't done, we look for the next mouse down to begin the
94: * tracking of another segment.
95: *
96: * After we are finished with all segments, we check to be sure that we have
97: * at least two segments (one segment is a line, not a polygon). If the
98: * path is closed, then we need at least three segments. If we have the
99: * requisite number of segments, then we reallocate our arrays to fit exactly
100: * our number of points and return YES. Otherwise, we free the storage of
101: * those arrays and clean up any drawing we did and return NO.
102: */
103:
104: #define POLYGON_MASK (NX_MOUSEDRAGGEDMASK|NX_MOUSEUPMASK)
105: #define END_POLYGON_MASK (NX_KITDEFINEDMASK|NX_MOUSEDOWNMASK|NX_SYSDEFINEDMASK)
106:
107: - (BOOL)create:(NXEvent *)event in:view
108: {
109: float *pptr;
110: NXRect r, viewBounds;
111: NXPoint start, last, p;
112: Window *window = [view window];
113: BOOL closepath, done = NO, resend = NO;
114: float grid = (float)[view gridSpacing];
115: int windowNum = event->window, arrow = 0;
116:
117: if (![view gridIsEnabled])
118: grid = 1.0;
119:
120: gFlags.initialized = YES;
121: if (gFlags.arrow && gFlags.arrow != ARROW_AT_START) {
122: arrow = gFlags.arrow;
123: gFlags.arrow = (gFlags.arrow == ARROW_AT_END) ? 0 : ARROW_AT_START;
124: }
125:
126: start = event->location;
127: [view convertPoint:&start fromView:nil];
128: [view grid:&start];
129:
130: [view getVisibleRect:&viewBounds];
131:
132: closepath = (event->flags & NX_ALTERNATEMASK) ? NO : YES;
133:
134: length = 0;
135: [self allocateChunk];
136: pptr = points;
137: *pptr++ = bbox[0] = bbox[2] = start.x;
138: *pptr++ = bbox[1] = bbox[3] = start.y;
139: userPathOps[0] = dps_moveto;
140:
141: [view lockFocus];
142:
143: [self setLineColor];
144: PSsetlinewidth(linewidth);
145:
146: p = start;
147: event = [NXApp getNextEvent:POLYGON_MASK];
148: while (!done) {
149: last = p;
150: if (event->type == NX_MOUSEDOWN) {
151: if (event->data.mouse.click > 1) {
152: done = YES;
153: [NXApp getNextEvent:NX_MOUSEUPMASK];
154: } else if (event->window != windowNum) {
155: done = YES;
156: resend = YES;
157: } else {
158: p = event->location;
159: [view convertPoint:&p fromView:nil];
160: done = !NXMouseInRect(&p, &viewBounds, NO);
161: resend = YES;
162: }
163: } else if (event->type == NX_KITDEFINED ||
164: event->type == NX_SYSDEFINED) {
165: done = YES;
166: resend = YES;
167: }
168: if (!done) {
169: while (event->type != NX_MOUSEUP) {
170: p = event->location;
171: [view convertPoint:&p fromView:nil];
172: [view grid:&p];
173: [view drawSelf:&r :1];
174: getRectFromBBox(&r, p.x, p.y, last.x, last.y);
175: [view scrollPointToVisible:&p];
176: NXInsetRect(&r, -2.0, -2.0);
177: [self draw];
178: PSmoveto(last.x, last.y);
179: PSlineto(p.x, p.y);
180: PSstroke();
181: [window flushWindow];
182: event = [NXApp getNextEvent:POLYGON_MASK];
183: }
184: if (fabs(p.x-start.x) <= grid && fabs(p.y-start.y) <= grid) {
185: done = YES;
186: closepath = YES;
187: }
188: }
189: if (!done || (closepath && length > 1)) {
190: if (done) p = start;
191: length++;
192: if (!(length % CHUNK_SIZE)) [self allocateChunk];
193: *pptr++ = p.x - last.x;
194: *pptr++ = p.y - last.y;
195: if (p.x < bbox[0]) bbox[0] = p.x;
196: if (p.x > bbox[2]) bbox[2] = p.x;
197: if (p.y < bbox[1]) bbox[1] = p.y;
198: if (p.y > bbox[3]) bbox[3] = p.y;
199: getRectFromBBox(&bounds, bbox[0], bbox[1], bbox[2], bbox[3]);
200: if (!done) event = [NXApp getNextEvent:END_POLYGON_MASK];
201: }
202: }
203:
204: [view unlockFocus];
205:
206: if (resend) DPSPostEvent(event, 1);
207: if (arrow) gFlags.arrow = arrow;
208:
209: if (length > (closepath ? 2 : 1)) {
210: points = NX_ZONEREALLOC([self zone], points, float, (length+1) << 1);
211: userPathOps = NX_ZONEREALLOC([self zone], userPathOps, char, length+1);
212: return YES;
213: } else {
214: NX_FREE(points); points = NULL;
215: NX_FREE(userPathOps); userPathOps = NULL;
216: [view drawSelf:[self getExtendedBounds:&r] :1];
217: return NO;
218: }
219: }
220:
221: - (Graphic *)colorAcceptorAt:(const NXPoint *)point
222: {
223: if ([self hit:point]) return self;
224: return nil;
225: }
226:
227: @end
228:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.