|
|
1.1 root 1: #ifndef lint
2: static char *rcsid_pathlist_c = "$Header: pathlist.c,v 10.1 86/11/19 10:43:18 jg Exp $";
3: #endif lint
4: /* pathlist.c - Coverter for vertex list
5: *
6: * PathListConverter Convert a list of vertices
7: * into absolute striaght lines
8: * Spline Generates a series of line segments
9: * that make up a smooth curve
10: * Matrix Utility rtn used by Spline to interpolate
11: * points along the curve
12: *
13: * Author:
14: * Scott Bates
15: * Brown University
16: * IRIS, Box 1946
17: * Providence, RI 02912
18: *
19: *
20: * Copyright (c) 1986 Brown University
21: *
22: * Permission to use, copy, modify and distribute this software and its
23: * documentation for any purpose and without fee is hereby granted, provided
24: * that the above copyright notice appear in all copies, and that both
25: * that copyright notice and this permission notice appear in supporting
26: * documentation, and that the name of Brown University not be used in
27: * advertising or publicity pertaining to distribution of the software
28: * without specific, written prior permission. Brown University makes no
29: * representations about the suitability of this software for any purpose.
30: * It is provided "as-is" without express or implied warranty.
31: */
32:
33: #include "private.h"
34: #include "pathlist.h"
35:
36: /*
37: * Convert vertex list
38: */
39:
40: PathListConverter(verts, vertcount, xbase, ybase, newverts, newvertcount, type)
41: register Vertex *verts;
42: int vertcount;
43: short xbase, ybase;
44: Vertex **newverts;
45: int *newvertcount;
46: int type;
47: {
48: register Vertex *ThisVertex = verts;
49: register Vertex *LastVertex;
50: register Vertex *NewVerts;
51: register Segment *CurrentSegment;
52: register i, j;
53: int VertexCount;
54: int VertexIndex = 0;
55: int SegmentIndex = 0;
56: int CurvedSegments = 0;
57: int MaxSegments = INITIAL_SEGMENTS;
58: int TotalVertexCount = vertcount;
59:
60: #ifdef TRACE_X
61: fprintf (stderr, "In PathListConverter\n");
62: fflush (stderr);
63: #endif TRACE_X
64:
65: /*
66: * Perform initial allocation of segment table
67: */
68:
69: CurrentSegment = SegmentTable =
70: (Segment *)malloc(MaxSegments * sizeof(Segment));
71: if(SegmentTable == NULL) {
72: return(NULL);
73: }
74:
75: /*
76: * Prime first segment table entry
77: */
78:
79: ThisVertex->x += xbase;
80: ThisVertex->y += ybase;
81:
82: CurrentSegment->Index = 0;
83: CurrentSegment->Count = 1;
84:
85: switch(ThisVertex->flags & VERTEX_TYPE_MASK) {
86: case(LINE): /* First segment is a line */
87:
88: CurrentSegment->Type = LINE_SEGMENT;
89: break;
90:
91: case(START_CLOSED_CURVE): /* First segment is a closed curve */
92:
93: CurrentSegment->Type = CLOSED_CURVE_SEGMENT;
94: CurvedSegments++;
95: break;
96:
97: default: /* First segment is a line */
98:
99: /*
100: * turn off bogus flags and make this first segment a line
101: */
102:
103: ThisVertex->flags &= ~(START_CLOSED_CURVE | END_CLOSED_CURVE);
104: CurrentSegment->Type = LINE_SEGMENT;
105: }
106:
107: /*
108: * Convert remaining vertices to absolute coordinates and
109: * divide them up into there appropriate segemnts.
110: */
111:
112: do {
113: if(++VertexIndex < vertcount) {
114: /*
115: * Move on to next vertex and save current vertex
116: */
117:
118: LastVertex = ThisVertex++;
119: } else {
120: /*
121: * Conversion has completed. If the last segment was a
122: * curved segment verify it before exiting loop.
123: */
124:
125: if(CurrentSegment->Type == CLOSED_CURVE_SEGMENT &&
126: ((ThisVertex->flags & VERTEX_TYPE_MASK) != END_CLOSED_CURVE ||
127: CurrentSegment->Count < 3)) {
128: return(NULL);
129: } else if(CurrentSegment->Type == OPEN_CURVE_SEGMENT &&
130: CurrentSegment->Count < 3) {
131: return(NULL);
132: }
133: break;
134: }
135:
136: /*
137: * Make Vertex an absolute coordinate
138: */
139:
140: if(ThisVertex->flags & VertexRelative) {
141: ThisVertex->x += LastVertex->x;
142: ThisVertex->y += LastVertex->y;
143: } else {
144: ThisVertex->x += xbase;
145: ThisVertex->y += ybase;
146: }
147:
148: /*
149: * If this is the last vertex turn off any bogus flags
150: * before processing it
151: */
152:
153: if((VertexIndex + 1) == vertcount &&
154: CurrentSegment->Type != CLOSED_CURVE_SEGMENT) {
155: ThisVertex->flags &= ~(END_CLOSED_CURVE);
156: }
157:
158: /*
159: * add this vertex to the current segement or start a new one.
160: */
161:
162: switch(ThisVertex->flags & VERTEX_TYPE_MASK) {
163:
164: case(LINE): /* This vertex is a LINE */
165: switch(LastVertex->flags & VERTEX_TYPE_MASK) {
166:
167: case(LINE): /* Last vertex was a LINE */
168: /*
169: * Add this vertex to the current segment
170: */
171:
172: CurrentSegment->Count++;
173: break;
174:
175: case(CURVE): /* Last vertex was a CURVE */
176: /*
177: * If the current segment type is a closed curve
178: * convert it to an open curve segment.
179: */
180:
181: if(CurrentSegment->Type == CLOSED_CURVE_SEGMENT) {
182: CurrentSegment->Type = OPEN_CURVE_SEGMENT;
183: if(CurrentSegment->Index > 0) {
184: CurrentSegment->Index--;
185: CurrentSegment->Count++;
186: }
187: }
188: if(++CurrentSegment->Count < 3) {
189: return(NULL);
190: }
191:
192: case(END_CLOSED_CURVE): /* Last vertex was a END_CLOSED_CURVE */
193: /*
194: * Start a line segment
195: */
196:
197: StartNewSegment(LINE_SEGMENT, VertexIndex, 1);
198: break;
199:
200: case(START_CLOSED_CURVE): /* Last vertex was start closed curve */
201: /*
202: * Convert the current segment to a line segment
203: */
204:
205: CurrentSegment->Type = LINE_SEGMENT;
206: CurrentSegment->Count++;
207: CurvedSegments--;
208: }
209: break;
210:
211: case(CURVE): /* This vertex was a curve */
212:
213: switch(LastVertex->flags & VERTEX_TYPE_MASK) {
214:
215: case(LINE): /* Last vertex was a line or */
216: case(END_CLOSED_CURVE): /* end closed curve */
217: /*
218: * Start an open curve segment
219: */
220:
221: StartNewSegment(OPEN_CURVE_SEGMENT, VertexIndex - 1, 2);
222: CurvedSegments++;
223: break;
224:
225: case(CURVE): /* Last vertex was a curve or start */
226: case(START_CLOSED_CURVE): /* closed curve */
227: /*
228: * Add this vertex to current segment
229: */
230:
231: CurrentSegment->Count++;
232:
233: }
234: break;
235:
236: case(START_CLOSED_CURVE): /* This vertex is start closed curve */
237:
238: switch(LastVertex->flags & VERTEX_TYPE_MASK) {
239:
240: case(CURVE): /* Last vertex was a curve */
241: /* If the current segment type is a closed curve
242: * convert it to a open curve segment. Then start
243: * a closed curve segment using this vertex.
244: */
245:
246: if(CurrentSegment->Type == CLOSED_CURVE_SEGMENT) {
247: CurrentSegment->Type = OPEN_CURVE_SEGMENT;
248: if(CurrentSegment->Index > 0) {
249: CurrentSegment->Index--;
250: CurrentSegment->Count++;
251: }
252: }
253: if(++CurrentSegment->Count < 3) {
254: return(NULL);
255: }
256:
257: case(LINE): /* Last vertex was a line or */
258: case(END_CLOSED_CURVE): /* end closed curve */
259: /*
260: * Start closed curve segemnt
261: */
262:
263: StartNewSegment(CLOSED_CURVE_SEGMENT, VertexIndex, 1);
264: CurvedSegments++;
265: break;
266:
267: case(START_CLOSED_CURVE): /* Last vertex was start closed curve */
268: /*
269: * Indicate to caller that there was a
270: * path list error.
271: */
272:
273: return(NULL);
274: }
275: break;
276:
277: case(END_CLOSED_CURVE): /* This vertex is end closed curve */
278: /*
279: * Add this vertex to the current segment
280: */
281:
282: ++CurrentSegment->Count;
283:
284: /*
285: * Last vertex was a curve
286: */
287:
288: if((LastVertex->flags & VERTEX_TYPE_MASK) == CURVE) {
289: /*
290: * Vaild vertex count of curved segment
291: */
292:
293: if(CurrentSegment->Count < 3) {
294: return(NULL);
295: }
296:
297: /*
298: * If the current segment is a closed segment
299: * validate that the last vertex of the segment
300: * equals the first.
301: */
302:
303: if(CurrentSegment->Type == CLOSED_CURVE_SEGMENT) {
304: if(verts[CurrentSegment->Index].x != verts[VertexIndex].x ||
305: verts[CurrentSegment->Index].y != verts[VertexIndex].y) {
306: return(NULL);
307: }
308: } else {
309: /*
310: * Start a line segment using this vertex
311: */
312:
313: StartNewSegment(LINE_SEGMENT, VertexIndex, 1);
314: }
315: }
316: break;
317:
318: default:
319: /*
320: * Indicate to caller that there was a
321: * path list error.
322: */
323:
324: return(NULL);
325: }
326: } while(1);
327:
328: /*
329: * If there are curved segments in this path list
330: * then perform the required setup and call the spline
331: * rtn .
332: */
333:
334: SplineUsed = 0;
335: if(CurvedSegments) {
336: Vertex *Vertex_A;
337: Vertex *Vertex_B;
338: Vertex *Vertex_C;
339: Vertex *Vertex_D;
340: int Count;
341:
342: /*
343: * Initial allocating of the spline vertex buffer
344: */
345:
346: SplineVertexIndex = 0;
347: MaxSplineVerts = INITIAL_SPLINE_VERTS;
348: SplineVertexBuffer = (Vertex *)malloc(MaxSplineVerts * sizeof(Vertex));
349: if(SplineVertexBuffer == NULL) {
350: return(NULL);
351: }
352: SplineUsed++;
353:
354: /*
355: * Loop thru all the path list segments looking
356: * for all open and closed curve segments.
357: */
358:
359: for(i = 0; i <= SegmentIndex; i++) {
360:
361: switch((CurrentSegment = &SegmentTable[i])->Type) {
362:
363: case(LINE_SEGMENT):
364: /*
365: * Ignore line segments
366: */
367:
368: continue;
369:
370: case(OPEN_CURVE_SEGMENT):
371: /*
372: * Generate a series of line segments
373: * that represent the open curve defined
374: * by this segment.
375: */
376:
377: Count = 0;
378: Vertex_A = &verts[CurrentSegment->Index +
379: CurrentSegment->Count - 1];
380: Vertex_B = &verts[CurrentSegment->Index];
381: Vertex_C = Vertex_B + 1;
382: Vertex_D = Vertex_C + 1;
383: CurrentSegment->Index = SplineVertexIndex;
384: j = CurrentSegment->Count - 2;
385:
386: while(1) {
387: Count += Spline(Vertex_A, Vertex_B, Vertex_C, Vertex_D);
388: if(--j == 0) {
389: break;
390: }
391: Vertex_A = Vertex_B;
392: Vertex_B = Vertex_C;
393: Vertex_C = Vertex_D++;
394: }
395: break;
396:
397: case(CLOSED_CURVE_SEGMENT):
398: /*
399: * Generate a series of line segments
400: * that represent the closed curve defined
401: * by this segment.
402: */
403:
404: Count = 0;
405: LastVertex = &verts[CurrentSegment->Index + 1];
406: Vertex_A = &verts[CurrentSegment->Index +
407: CurrentSegment->Count - 2];
408: Vertex_B = &verts[CurrentSegment->Index];
409: CurrentSegment->Index = SplineVertexIndex;
410: Vertex_C = Vertex_B + 1;
411: Vertex_D = Vertex_C;
412: j = CurrentSegment->Count - 2;
413:
414: while(j--) {
415: Vertex_D++;
416: Count += Spline(Vertex_A, Vertex_B, Vertex_C, Vertex_D);
417: Vertex_A = Vertex_B;
418: Vertex_B = Vertex_C;
419: Vertex_C = Vertex_D;
420: }
421: Vertex_D = LastVertex;
422: Count += Spline(Vertex_A, Vertex_B, Vertex_C, Vertex_D);
423:
424: }
425:
426: /*
427: * Adjust the current segment count and
428: * increase the total vertex to reflect
429: * the new points generated by the spline rtn.
430: */
431:
432: CurrentSegment->Count = Count;
433: TotalVertexCount += Count;
434:
435: /*
436: * If there are no more curved segments exit early
437: */
438:
439: if(--CurvedSegments == 0) {
440: break;
441: }
442: }
443: }
444:
445: /*
446: * Allocate space for new vertex list
447: */
448:
449: NewVerts = *newverts = (Vertex *)malloc((TotalVertexCount << 1) *
450: sizeof(Vertex));
451: if(NewVerts == NULL) {
452: return(NULL);
453: }
454:
455: /*
456: * Loop thru coordinate list
457: */
458:
459: for(VertexCount = 0, i = 0; i <= SegmentIndex; i++) {
460: /*
461: * If this segment is a line segment get verts from original
462: * vertex list else use the spline vertex list.
463: */
464:
465: if((CurrentSegment = &SegmentTable[i])->Type == LINE_SEGMENT) {
466: ThisVertex = &verts[CurrentSegment->Index];
467: } else {
468: ThisVertex = &SplineVertexBuffer[CurrentSegment->Index];
469: }
470:
471: /*
472: * Get segment vertex count
473: */
474:
475: j = CurrentSegment->Count;
476:
477: /*
478: * Convert path list to fill format
479: */
480:
481: if(type == FILL_PATH_LIST) {
482: do {
483: /*
484: * Something to draw ?
485: */
486:
487: if(ThisVertex->flags & VertexDontDraw) {
488: /*
489: * Indicate start of closed polygon
490: */
491:
492: ThisVertex->flags |= START_OF_CLOSED_POLY;
493:
494: /*
495: * Increment vertex pointers
496: */
497:
498: LastVertex = ThisVertex++;
499:
500: /*
501: * Continue processing of current segment
502: */
503:
504: continue;
505: }
506:
507: /*
508: * If this vertex is not a dup save the
509: * line segment represented by the last
510: * vertex and this one in NewVerts.
511: * If it is a dup ignore this vertex.
512: */
513:
514: if(ThisVertex->x != LastVertex->x ||
515: ThisVertex->y != LastVertex->y) {
516:
517: /*
518: * Save start and end points of
519: * visible line
520: */
521:
522: *NewVerts++ = *LastVertex;
523: *NewVerts++ = *ThisVertex;
524:
525: /*
526: * Increment vertex count
527: */
528:
529: VertexCount += 2;
530:
531: /*
532: * Increment vertex pointers
533: */
534:
535: LastVertex = ThisVertex++;
536: } else {
537: /*
538: * Ignore this vertex
539: */
540:
541: ThisVertex++;
542: }
543: }while(--j);
544: } else {
545: do {
546: /*
547: * Something to draw ?
548: */
549:
550: if(ThisVertex->flags & VertexDontDraw) {
551: /*
552: * Increment vertex pointers
553: */
554:
555: LastVertex = ThisVertex++;
556:
557: /*
558: * Continue processing current segment
559: */
560:
561: continue;
562: }
563:
564: if(ThisVertex->x != LastVertex->x ||
565: ThisVertex->y != LastVertex->y) {
566: /*
567: * Save start and end points of
568: * visible line
569: */
570:
571: *NewVerts++ = *LastVertex;
572: *NewVerts = *ThisVertex;
573:
574: /*
575: * Shorten line by one point if
576: * "VertexDrawLastPoint" flag is off
577: */
578:
579: if(!(ThisVertex->flags & VertexDrawLastPoint)) {
580: int DeltaX, DeltaY;
581: int SignX = 0, SignY = 0;
582:
583: if((DeltaX = ThisVertex->x - LastVertex->x) < 0) {
584: SignX = -1;
585: DeltaX = -DeltaX;
586: }
587:
588: if((DeltaY = ThisVertex->y - LastVertex->y) < 0) {
589: SignY = -1;
590: DeltaY = -DeltaY;
591: }
592:
593: if (DeltaX > DeltaY) {
594: SignX < 0 ? NewVerts->x++ : NewVerts->x--;
595: if ((DeltaX >> 1) <= DeltaY) {
596: SignY < 0 ? NewVerts->y++ : NewVerts->y--;
597: }
598: } else if (DeltaX < DeltaY ) {
599: SignY < 0 ? NewVerts->y++ : NewVerts->y--;
600: if ((DeltaY >> 1) <= DeltaX) {
601: SignX < 0 ? NewVerts->x++ : NewVerts->x--;
602: }
603: } else {
604: if (DeltaX > 0) {
605: SignX < 0 ? NewVerts->x++ : NewVerts->x--;
606: SignY < 0 ? NewVerts->y++ : NewVerts->y--;
607: } else {
608: /*
609: * Line now has a length of zero
610: * so we skip this one. Back up the
611: * buffer pointer and move on to the
612: * next vertex
613: */
614:
615: NewVerts--;
616:
617: /*
618: * Increment vertex pointers
619: */
620:
621: LastVertex = ThisVertex++;
622: continue;
623: }
624: }
625: }
626:
627: /*
628: * Advance buffer pointer
629: */
630:
631: NewVerts++;
632:
633: /*
634: * Increment vertex count
635: */
636:
637: VertexCount += 2;
638:
639: /*
640: * Increment vertex pointers
641: */
642:
643: LastVertex = ThisVertex++;
644: } else {
645: /*
646: * Ignore this vertex
647: */
648:
649: ThisVertex++;
650: }
651: } while(--j);
652: }
653: }
654:
655: /*
656: * Save final vertex count and free any resources used
657: * during path list conversion.
658: */
659:
660: *newvertcount = VertexCount;
661: free((caddr_t)SegmentTable);
662: if(SplineUsed) {
663: free((caddr_t)SplineVertexBuffer);
664: }
665: return(1);
666: }
667:
668: /*
669: * Generate a series of points that will form
670: * a curve between Vertex_B and Vertex_C.
671: */
672:
673: static
674: Spline(Vertex_A, Vertex_B, Vertex_C, Vertex_D)
675: register Vertex *Vertex_A;
676: register Vertex *Vertex_B;
677: register Vertex *Vertex_C;
678: register Vertex *Vertex_D;
679: {
680: register Vertex *Verts = &SplineVertexBuffer[SplineVertexIndex];
681: register i;
682: int nls = 1;
683: int Delta_X, Delta_Y;
684: long Matrix();
685:
686: #ifdef TRACE_X
687: fprintf (stderr, "In Spline\n");
688: fflush (stderr);
689: #endif TRACE_X
690:
691: GrowSplineVertexBuffer(Verts, 2);
692: *Verts++ = *Vertex_B;
693: SplineVertexIndex++;
694: if((Vertex_C->flags & VertexDontDraw) == 0) {
695: /*
696: * Compute how many points to generate
697: * based on the largest delta change in either
698: * the X or Y direction. This number represents
699: * the maximum number of points to be generated
700: * and may be reduced by an increasing amount
701: * as the change (delta) gets larger. This allows
702: * us to generate fewer points and therefore
703: * improve performace and still generate
704: * quality smooth curve.
705: */
706:
707: if((Delta_X = Vertex_C->x - Vertex_B->x) < 0) {
708: Delta_X = -Delta_X;
709: }
710:
711: if((Delta_Y = Vertex_C->y - Vertex_B->y) < 0) {
712: Delta_Y = -Delta_Y;
713: }
714:
715: if(Delta_X > Delta_Y) {
716: if(Delta_X > 64) {
717: nls = Delta_X >> 3;
718: } else if(Delta_X > 32) {
719: nls = Delta_X >> 2;
720: } else if(Delta_X > 16) {
721: nls = Delta_X >> 1;
722: } else {
723: nls = Delta_X;
724: }
725: } else {
726: if(Delta_Y > 64) {
727: nls = Delta_Y >> 3;
728: } else if(Delta_Y > 32) {
729: nls = Delta_Y >> 2;
730: } else if(Delta_Y > 16) {
731: nls = Delta_Y >> 1;
732: } else {
733: nls = Delta_Y;
734: }
735: }
736:
737: /*
738: * Generate the actual points
739: */
740:
741: if(nls) {
742: GrowSplineVertexBuffer(Verts, nls);
743: for(i = 1; i < nls; i++, Verts++) {
744: Verts->x = Matrix((long)Vertex_A->x, (long)Vertex_B->x,
745: (long)Vertex_C->x, (long)Vertex_D->x, nls, i);
746: Verts->y = Matrix((long)Vertex_A->y, (long)Vertex_B->y,
747: (long)Vertex_C->y, (long)Vertex_D->y, nls, i);
748: Verts->flags = Vertex_C->flags & VertexDrawLastPoint;
749: }
750: SplineVertexIndex += nls;
751: } else {
752: nls = 1;
753: SplineVertexIndex++;
754: }
755: } else {
756: SplineVertexIndex++;
757: }
758: *Verts = *Vertex_C;
759:
760: /*
761: * Return the number of points generated
762: */
763:
764: return(nls + 1);
765: }
766:
767: /*
768: * This rtn performs the matrix math require to interpolate a
769: * point on the curve represented by a, b, c, and d. The generated
770: * point will be between points b and c.
771: */
772:
773: static long
774: Matrix(a, b, c, d, nls, i)
775: register long a, b, c, d;
776: register nls;
777: int i;
778: {
779: register long p = SHIFT_LEFT_16(-a + b - c + d);
780:
781: #ifdef TRACE_X
782: fprintf (stderr, "In Matrix\n");
783: fflush (stderr);
784: #endif TRACE_X
785:
786: p = PERCENT_16(p, i, nls) + SHIFT_LEFT_16((a << 1) - (b << 1) + c - d);
787: p = PERCENT_16(p, i, nls) + SHIFT_LEFT_16(-a + c);
788: return(ROUND_16(PERCENT_16(p, i, nls) + SHIFT_LEFT_16(b)));
789: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.