|
|
1.1 root 1: /*
2: * tkTrig.c --
3: *
4: * This file contains a collection of trigonometry utility
5: * routines that are used by Tk and in particular by the
6: * canvas code. It also has miscellaneous geometry functions
7: * used by canvases.
8: *
9: * Copyright 1992 Regents of the University of California.
10: * Permission to use, copy, modify, and distribute this
11: * software and its documentation for any purpose and without
12: * fee is hereby granted, provided that the above copyright
13: * notice appear in all copies. The University of California
14: * makes no representations about the suitability of this
15: * software for any purpose. It is provided "as is" without
16: * express or implied warranty.
17: */
18:
19: #ifndef lint
20: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkTrig.c,v 1.8 92/08/24 09:24:14 ouster Exp $ SPRITE (Berkeley)";
21: #endif
22:
23: #include <stdio.h>
24: #include <math.h>
25: #include "tkconfig.h"
26: #include "tkcanvas.h"
27:
28: #undef MIN
29: #define MIN(a,b) (((a) < (b)) ? (a) : (b))
30: #undef MAX
31: #define MAX(a,b) (((a) > (b)) ? (a) : (b))
32: #define PI 3.14159265358979323846
33:
34: /*
35: *--------------------------------------------------------------
36: *
37: * TkLineToPoint --
38: *
39: * Compute the distance from a point to a finite line segment.
40: *
41: * Results:
42: * The return value is the distance from the line segment
43: * whose end-points are *end1Ptr and *end2Ptr to the point
44: * given by *pointPtr.
45: *
46: * Side effects:
47: * None.
48: *
49: *--------------------------------------------------------------
50: */
51:
52: double
53: TkLineToPoint(end1Ptr, end2Ptr, pointPtr)
54: double end1Ptr[2]; /* Coordinates of first end-point of line. */
55: double end2Ptr[2]; /* Coordinates of second end-point of line. */
56: double pointPtr[2]; /* Points to coords for point. */
57: {
58: double x, y;
59:
60: /*
61: * Compute the point on the line that is closest to the
62: * point. This must be done separately for vertical edges,
63: * horizontal edges, and other edges.
64: */
65:
66: if (end1Ptr[0] == end2Ptr[0]) {
67:
68: /*
69: * Vertical edge.
70: */
71:
72: x = end1Ptr[0];
73: if (end1Ptr[1] >= end2Ptr[1]) {
74: y = MIN(end1Ptr[1], pointPtr[1]);
75: y = MAX(y, end2Ptr[1]);
76: } else {
77: y = MIN(end2Ptr[1], pointPtr[1]);
78: y = MAX(y, end1Ptr[1]);
79: }
80: } else if (end1Ptr[1] == end2Ptr[1]) {
81:
82: /*
83: * Horizontal edge.
84: */
85:
86: y = end1Ptr[1];
87: if (end1Ptr[0] >= end2Ptr[0]) {
88: x = MIN(end1Ptr[0], pointPtr[0]);
89: x = MAX(x, end2Ptr[0]);
90: } else {
91: x = MIN(end2Ptr[0], pointPtr[0]);
92: x = MAX(x, end1Ptr[0]);
93: }
94: } else {
95: double m1, b1, m2, b2;
96:
97: /*
98: * The edge is neither horizontal nor vertical. Convert the
99: * edge to a line equation of the form y = m1*x + b1. Then
100: * compute a line perpendicular to this edge but passing
101: * through the point, also in the form y = m2*x + b2.
102: */
103:
104: m1 = (end2Ptr[1] - end1Ptr[1])/(end2Ptr[0] - end1Ptr[0]);
105: b1 = end1Ptr[1] - m1*end1Ptr[0];
106: m2 = -1.0/m1;
107: b2 = pointPtr[1] - m2*pointPtr[0];
108: x = (b2 - b1)/(m1 - m2);
109: y = m1*x + b1;
110: if (end1Ptr[0] > end2Ptr[0]) {
111: if (x > end1Ptr[0]) {
112: x = end1Ptr[0];
113: y = end1Ptr[1];
114: } else if (x < end2Ptr[0]) {
115: x = end2Ptr[0];
116: y = end2Ptr[1];
117: }
118: } else {
119: if (x > end2Ptr[0]) {
120: x = end2Ptr[0];
121: y = end2Ptr[1];
122: } else if (x < end1Ptr[0]) {
123: x = end1Ptr[0];
124: y = end1Ptr[1];
125: }
126: }
127: }
128:
129: /*
130: * Compute the distance to the closest point.
131: */
132:
133: return hypot(pointPtr[0] - x, pointPtr[1] - y);
134: }
135:
136: /*
137: *--------------------------------------------------------------
138: *
139: * TkLineToArea --
140: *
141: * Determine whether a line lies entirely inside, entirely
142: * outside, or overlapping a given rectangular area.
143: *
144: * Results:
145: * -1 is returned if the line given by end1Ptr and end2Ptr
146: * is entirely outside the rectangle given by rectPtr. 0 is
147: * returned if the polygon overlaps the rectangle, and 1 is
148: * returned if the polygon is entirely inside the rectangle.
149: *
150: * Side effects:
151: * None.
152: *
153: *--------------------------------------------------------------
154: */
155:
156: int
157: TkLineToArea(end1Ptr, end2Ptr, rectPtr)
158: double end1Ptr[2]; /* X and y coordinates for one endpoint
159: * of line. */
160: double end2Ptr[2]; /* X and y coordinates for other endpoint
161: * of line. */
162: double rectPtr[4]; /* Points to coords for rectangle, in the
163: * order x1, y1, x2, y2. X1 must be no
164: * larger than x2, and y1 no larger than y2. */
165: {
166: int inside1, inside2;
167:
168: /*
169: * First check the two points individually to see whether they
170: * are inside the rectangle or not.
171: */
172:
173: inside1 = (end1Ptr[0] >= rectPtr[0]) && (end1Ptr[0] <= rectPtr[2])
174: && (end1Ptr[1] >= rectPtr[1]) && (end1Ptr[1] <= rectPtr[3]);
175: inside2 = (end2Ptr[0] >= rectPtr[0]) && (end2Ptr[0] <= rectPtr[2])
176: && (end2Ptr[1] >= rectPtr[1]) && (end2Ptr[1] <= rectPtr[3]);
177: if (inside1 != inside2) {
178: return 0;
179: }
180: if (inside1 & inside2) {
181: return 1;
182: }
183:
184: /*
185: * Both points are outside the rectangle, but still need to check
186: * for intersections between the line and the rectangle. Horizontal
187: * and vertical lines are particularly easy, so handle them
188: * separately.
189: */
190:
191: if (end1Ptr[0] == end2Ptr[0]) {
192: /*
193: * Vertical line.
194: */
195:
196: if (((end1Ptr[1] >= rectPtr[1]) ^ (end2Ptr[1] >= rectPtr[1]))
197: && (end1Ptr[0] >= rectPtr[0])
198: && (end1Ptr[0] <= rectPtr[2])) {
199: return 0;
200: }
201: } else if (end1Ptr[1] == end2Ptr[1]) {
202: /*
203: * Horizontal line.
204: */
205:
206: if (((end1Ptr[0] >= rectPtr[0]) ^ (end2Ptr[0] >= rectPtr[0]))
207: && (end1Ptr[1] >= rectPtr[1])
208: && (end1Ptr[1] <= rectPtr[3])) {
209: return 0;
210: }
211: } else {
212: double m, x, y, low, high;
213:
214: /*
215: * Diagonal line. Compute slope of line and use
216: * for intersection checks against each of the
217: * sides of the rectangle: left, right, bottom, top.
218: */
219:
220: m = (end2Ptr[1] - end1Ptr[1])/(end2Ptr[0] - end1Ptr[0]);
221: if (end1Ptr[0] < end2Ptr[0]) {
222: low = end1Ptr[0]; high = end2Ptr[0];
223: } else {
224: low = end2Ptr[0]; high = end1Ptr[0];
225: }
226:
227: /*
228: * Left edge.
229: */
230:
231: y = end1Ptr[1] + (rectPtr[0] - end1Ptr[0])*m;
232: if ((rectPtr[0] >= low) && (rectPtr[0] <= high)
233: && (y >= rectPtr[1]) && (y <= rectPtr[3])) {
234: return 0;
235: }
236:
237: /*
238: * Right edge.
239: */
240:
241: y += (rectPtr[2] - rectPtr[0])*m;
242: if ((y >= rectPtr[1]) && (y <= rectPtr[3])
243: && (rectPtr[2] >= low) && (rectPtr[2] <= high)) {
244: return 0;
245: }
246:
247: /*
248: * Bottom edge.
249: */
250:
251: if (end1Ptr[1] < end2Ptr[1]) {
252: low = end1Ptr[1]; high = end2Ptr[1];
253: } else {
254: low = end2Ptr[1]; high = end1Ptr[1];
255: }
256: x = end1Ptr[0] + (rectPtr[1] - end1Ptr[1])/m;
257: if ((x >= rectPtr[0]) && (x <= rectPtr[2])
258: && (rectPtr[1] >= low) && (rectPtr[1] <= high)) {
259: return 0;
260: }
261:
262: /*
263: * Top edge.
264: */
265:
266: x += (rectPtr[3] - rectPtr[1])/m;
267: if ((x >= rectPtr[0]) && (x <= rectPtr[2])
268: && (rectPtr[3] >= low) && (rectPtr[3] <= high)) {
269: return 0;
270: }
271: }
272: return -1;
273: }
274:
275: /*
276: *--------------------------------------------------------------
277: *
278: * TkPolygonToPoint --
279: *
280: * Compute the distance from a point to a polygon.
281: *
282: * Results:
283: * The return value is 0.0 if the point referred to by
284: * pointPtr is within the polygon referred to by polyPtr
285: * and numPoints. Otherwise the return value is the
286: * distance of the point from the polygon.
287: *
288: * Side effects:
289: * None.
290: *
291: *--------------------------------------------------------------
292: */
293:
294: double
295: TkPolygonToPoint(polyPtr, numPoints, pointPtr)
296: double *polyPtr; /* Points to an array coordinates for
297: * closed polygon: x0, y0, x1, y1, ...
298: * The polygon may be self-intersecting. */
299: int numPoints; /* Total number of points at *polyPtr. */
300: double *pointPtr; /* Points to coords for point. */
301: {
302: double bestDist; /* Closest distance between point and
303: * any edge in polygon. */
304: int intersections; /* Number of edges in the polygon that
305: * intersect a ray extending vertically
306: * upwards from the point to infinity. */
307: int count;
308: register double *pPtr;
309:
310: /*
311: * Iterate through all of the edges in the polygon, updating
312: * bestDist and intersections.
313: *
314: * TRICKY POINT: when computing intersections, include left
315: * x-coordinate of line within its range, but not y-coordinate.
316: * Otherwise if the point lies exactly below a vertex we'll
317: * count it as two intersections.
318: */
319:
320: bestDist = 1.0e40;
321: intersections = 0;
322:
323: for (count = numPoints, pPtr = polyPtr; count > 1; count--, pPtr += 2) {
324: double x, y, dist;
325:
326: /*
327: * Compute the point on the current edge closest to the point
328: * and update the intersection count. This must be done
329: * separately for vertical edges, horizontal edges, and
330: * other edges.
331: */
332:
333: if (pPtr[2] == pPtr[0]) {
334:
335: /*
336: * Vertical edge.
337: */
338:
339: x = pPtr[0];
340: if (pPtr[1] >= pPtr[3]) {
341: y = MIN(pPtr[1], pointPtr[1]);
342: y = MAX(y, pPtr[3]);
343: } else {
344: y = MIN(pPtr[3], pointPtr[1]);
345: y = MAX(y, pPtr[1]);
346: }
347: } else if (pPtr[3] == pPtr[1]) {
348:
349: /*
350: * Horizontal edge.
351: */
352:
353: y = pPtr[1];
354: if (pPtr[0] >= pPtr[2]) {
355: x = MIN(pPtr[0], pointPtr[0]);
356: x = MAX(x, pPtr[2]);
357: if ((pointPtr[1] < y) && (pointPtr[0] < pPtr[0])
358: && (pointPtr[0] >= pPtr[2])) {
359: intersections++;
360: }
361: } else {
362: x = MIN(pPtr[2], pointPtr[0]);
363: x = MAX(x, pPtr[0]);
364: if ((pointPtr[1] < y) && (pointPtr[0] < pPtr[2])
365: && (pointPtr[0] >= pPtr[0])) {
366: intersections++;
367: }
368: }
369: } else {
370: double m1, b1, m2, b2;
371: int lower; /* Non-zero means point below line. */
372:
373: /*
374: * The edge is neither horizontal nor vertical. Convert the
375: * edge to a line equation of the form y = m1*x + b1. Then
376: * compute a line perpendicular to this edge but passing
377: * through the point, also in the form y = m2*x + b2.
378: */
379:
380: m1 = (pPtr[3] - pPtr[1])/(pPtr[2] - pPtr[0]);
381: b1 = pPtr[1] - m1*pPtr[0];
382: m2 = -1.0/m1;
383: b2 = pointPtr[1] - m2*pointPtr[0];
384: x = (b2 - b1)/(m1 - m2);
385: y = m1*x + b1;
386: if (pPtr[0] > pPtr[2]) {
387: if (x > pPtr[0]) {
388: x = pPtr[0];
389: y = pPtr[1];
390: } else if (x < pPtr[2]) {
391: x = pPtr[2];
392: y = pPtr[3];
393: }
394: } else {
395: if (x > pPtr[2]) {
396: x = pPtr[2];
397: y = pPtr[3];
398: } else if (x < pPtr[0]) {
399: x = pPtr[0];
400: y = pPtr[1];
401: }
402: }
403: lower = (m1*pointPtr[0] + b1) > pointPtr[1];
404: if (lower && (pointPtr[0] >= MIN(pPtr[0], pPtr[2]))
405: && (pointPtr[0] < MAX(pPtr[0], pPtr[2]))) {
406: intersections++;
407: }
408: }
409:
410: /*
411: * Compute the distance to the closest point, and see if that
412: * is the best distance seen so far.
413: */
414:
415: dist = hypot(pointPtr[0] - x, pointPtr[1] - y);
416: if (dist < bestDist) {
417: bestDist = dist;
418: }
419: }
420:
421: /*
422: * We've processed all of the points. If the number of intersections
423: * is odd, the point is inside the polygon.
424: */
425:
426: if (intersections & 0x1) {
427: return 0.0;
428: }
429: return bestDist;
430: }
431:
432: /*
433: *--------------------------------------------------------------
434: *
435: * TkPolygonToArea --
436: *
437: * Determine whether a polygon lies entirely inside, entirely
438: * outside, or overlapping a given rectangular area.
439: *
440: * Results:
441: * -1 is returned if the polygon given by polyPtr and numPoints
442: * is entirely outside the rectangle given by rectPtr. 0 is
443: * returned if the polygon overlaps the rectangle, and 1 is
444: * returned if the polygon is entirely inside the rectangle.
445: *
446: * Side effects:
447: * None.
448: *
449: *--------------------------------------------------------------
450: */
451:
452: int
453: TkPolygonToArea(polyPtr, numPoints, rectPtr)
454: double *polyPtr; /* Points to an array coordinates for
455: * closed polygon: x0, y0, x1, y1, ...
456: * The polygon may be self-intersecting. */
457: int numPoints; /* Total number of points at *polyPtr. */
458: register double *rectPtr; /* Points to coords for rectangle, in the
459: * order x1, y1, x2, y2. X1 and y1 must
460: * be lower-left corner. */
461: {
462: int state; /* State of all edges seen so far (-1 means
463: * outside, 1 means inside, won't ever be
464: * 0). */
465: int count;
466: register double *pPtr;
467:
468: /*
469: * Iterate over all of the edges of the polygon and test them
470: * against the rectangle. Can quit as soon as the state becomes
471: * "intersecting".
472: */
473:
474: state = TkLineToArea(polyPtr, polyPtr+2, rectPtr);
475: if (state == 0) {
476: return 0;
477: }
478: for (pPtr = polyPtr+2, count = numPoints-1; count >= 2;
479: pPtr += 2, count--) {
480: if (TkLineToArea(pPtr, pPtr+2, rectPtr) != state) {
481: return 0;
482: }
483: }
484:
485: /*
486: * If all of the edges were inside the rectangle we're done.
487: * If all of the edges were outside, then the rectangle could
488: * still intersect the polygon (if it's entirely enclosed).
489: * Call TkPolygonToPoint to figure this out.
490: */
491:
492: if (state == 1) {
493: return 1;
494: }
495: if (TkPolygonToPoint(polyPtr, numPoints, rectPtr) == 0.0) {
496: return 0;
497: }
498: return -1;
499: }
500:
501: /*
502: *--------------------------------------------------------------
503: *
504: * TkOvalToPoint --
505: *
506: * Computes the distance from a given point to a given
507: * oval, in canvas units.
508: *
509: * Results:
510: * The return value is 0 if the point given by *pointPtr is
511: * inside the oval. If the point isn't inside the
512: * oval then the return value is approximately the distance
513: * from the point to the oval. If the oval is filled, then
514: * anywhere in the interior is considered "inside"; if
515: * the oval isn't filled, then "inside" means only the area
516: * occupied by the outline.
517: *
518: * Side effects:
519: * None.
520: *
521: *--------------------------------------------------------------
522: */
523:
524: /* ARGSUSED */
525: double
526: TkOvalToPoint(ovalPtr, width, filled, pointPtr)
527: double ovalPtr[4]; /* Pointer to array of four coordinates
528: * (x1, y1, x2, y2) defining oval's bounding
529: * box. */
530: double width; /* Width of outline for oval. */
531: int filled; /* Non-zero means oval should be treated as
532: * filled; zero means only consider outline. */
533: double pointPtr[2]; /* Coordinates of point. */
534: {
535: double xDelta, yDelta, scaledDistance, distToOutline, distToCenter;
536:
537: /*
538: * Compute the distance between the center of the oval and the
539: * point in question, using a coordinate system where the oval
540: * has been transformed to a circle with unit radius.
541: */
542:
543: xDelta = (pointPtr[0] - (ovalPtr[0] + ovalPtr[2])/2.0);
544: yDelta = (pointPtr[1] - (ovalPtr[1] + ovalPtr[3])/2.0);
545: distToCenter = hypot(xDelta, yDelta);
546: scaledDistance = hypot(xDelta / ((ovalPtr[2] + width - ovalPtr[0])/2.0),
547: yDelta / ((ovalPtr[3] + width - ovalPtr[1])/2.0));
548:
549:
550: /*
551: * If the scaled distance is greater than 1 then it means no
552: * hit. Compute the distance from the point to the edge of
553: * the circle, then scale this distance back to the original
554: * coordinate system.
555: *
556: * Note: this distance isn't completely accurate. It's only
557: * an approximation, and it can overestimate the correct
558: * distance when the oval is eccentric.
559: */
560:
561: if (scaledDistance > 1.0) {
562: return (distToCenter/scaledDistance) * (scaledDistance - 1.0);
563: }
564:
565: /*
566: * Scaled distance less than 1 means the point is inside the
567: * outer edge of the oval. If this is a filled oval, then we
568: * have a hit. Otherwise, do the same computation as above
569: * (scale back to original coordinate system), but also check
570: * to see if the point is within the width of the outline.
571: */
572:
573: if (filled) {
574: return 0.0;
575: }
576: distToOutline = (distToCenter/scaledDistance) * (1.0 - scaledDistance)
577: - width;
578: if (distToOutline < 0.0) {
579: return 0.0;
580: }
581: return distToOutline;
582: }
583:
584: /*
585: *--------------------------------------------------------------
586: *
587: * TkOvalToArea --
588: *
589: * Determine whether an oval lies entirely inside, entirely
590: * outside, or overlapping a given rectangular area.
591: *
592: * Results:
593: * -1 is returned if the oval described by ovalPtr is entirely
594: * outside the rectangle given by rectPtr. 0 is returned if the
595: * oval overlaps the rectangle, and 1 is returned if the oval
596: * is entirely inside the rectangle.
597: *
598: * Side effects:
599: * None.
600: *
601: *--------------------------------------------------------------
602: */
603:
604: int
605: TkOvalToArea(ovalPtr, rectPtr)
606: register double *ovalPtr; /* Points to coordinates definining the
607: * bounding rectangle for the oval: x1, y1,
608: * x2, y2. X1 must be less than x2 and y1
609: * less than y2. */
610: register double *rectPtr; /* Points to coords for rectangle, in the
611: * order x1, y1, x2, y2. X1 and y1 must
612: * be lower-left corner. */
613: {
614: double centerX, centerY, radX, radY, deltaX, deltaY;
615:
616: /*
617: * First, see if oval is entirely inside rectangle or entirely
618: * outside rectangle.
619: */
620:
621: if ((rectPtr[0] <= ovalPtr[0]) && (rectPtr[2] >= ovalPtr[2])
622: && (rectPtr[1] <= ovalPtr[1]) && (rectPtr[3] >= ovalPtr[3])) {
623: return 1;
624: }
625: if ((rectPtr[2] < ovalPtr[0]) || (rectPtr[0] > ovalPtr[2])
626: || (rectPtr[3] < ovalPtr[1]) || (rectPtr[1] > ovalPtr[3])) {
627: return -1;
628: }
629:
630: /*
631: * Next, go through the rectangle side by side. For each side
632: * of the rectangle, find the point on the side that is closest
633: * to the oval's center, and see if that point is inside the
634: * oval. If at least one such point is inside the oval, then
635: * the rectangle intersects the oval.
636: */
637:
638: centerX = (ovalPtr[0] + ovalPtr[2])/2;
639: centerY = (ovalPtr[1] + ovalPtr[3])/2;
640: radX = (ovalPtr[2] - ovalPtr[0])/2;
641: radY = (ovalPtr[3] - ovalPtr[1])/2;
642:
643: deltaY = rectPtr[1] - centerY;
644: if (deltaY < 0.0) {
645: deltaY = centerY - rectPtr[3];
646: if (deltaY < 0.0) {
647: deltaY = 0;
648: }
649: }
650: deltaY /= radY;
651: deltaY *= deltaY;
652:
653: /*
654: * Left side:
655: */
656:
657: deltaX = (rectPtr[0] - centerX)/radX;
658: deltaX *= deltaX;
659: if ((deltaX + deltaY) <= 1.0) {
660: return 0;
661: }
662:
663: /*
664: * Right side:
665: */
666:
667: deltaX = (rectPtr[2] - centerX)/radX;
668: deltaX *= deltaX;
669: if ((deltaX + deltaY) <= 1.0) {
670: return 0;
671: }
672:
673: deltaX = rectPtr[0] - centerX;
674: if (deltaX < 0.0) {
675: deltaX = centerX - rectPtr[2];
676: if (deltaX < 0.0) {
677: deltaX = 0;
678: }
679: }
680: deltaX /= radX;
681: deltaX *= deltaX;
682:
683: /*
684: * Bottom side:
685: */
686:
687: deltaY = (rectPtr[1] - centerY)/radY;
688: deltaY *= deltaY;
689: if ((deltaX + deltaY) < 1.0) {
690: return 0;
691: }
692:
693: /*
694: * Top side:
695: */
696:
697: deltaY = (rectPtr[3] - centerY)/radY;
698: deltaY *= deltaY;
699: if ((deltaX + deltaY) < 1.0) {
700: return 0;
701: }
702:
703: return -1;
704: }
705:
706: /*
707: *--------------------------------------------------------------
708: *
709: * TkIncludePoint --
710: *
711: * Given a point and a generic canvas item header, expand
712: * the item's bounding box if needed to include the point.
713: *
714: * Results:
715: * None.
716: *
717: * Side effects:
718: * The boudn.
719: *
720: *--------------------------------------------------------------
721: */
722:
723: /* ARGSUSED */
724: void
725: TkIncludePoint(canvasPtr, itemPtr, pointPtr)
726: Tk_Canvas *canvasPtr; /* Canvas containing item. */
727: register Tk_Item *itemPtr; /* Item whose bounding box is
728: * being calculated. */
729: double *pointPtr; /* Address of two doubles giving
730: * x and y coordinates of point. */
731: {
732: int tmp;
733:
734: tmp = pointPtr[0] + 0.5;
735: if (tmp < itemPtr->x1) {
736: itemPtr->x1 = tmp;
737: }
738: if (tmp > itemPtr->x2) {
739: itemPtr->x2 = tmp;
740: }
741: tmp = pointPtr[1] + 0.5;
742: if (tmp < itemPtr->y1) {
743: itemPtr->y1 = tmp;
744: }
745: if (tmp > itemPtr->y2) {
746: itemPtr->y2 = tmp;
747: }
748: }
749:
750: /*
751: *--------------------------------------------------------------
752: *
753: * TkBezierScreenPoints --
754: *
755: * Given four control points, create a larger set of XPoints
756: * for a Bezier spline based on the points.
757: *
758: * Results:
759: * The array at *xPointPtr gets filled in with numSteps XPoints
760: * corresponding to the Bezier spline defined by the four
761: * control points. Note: no output point is generated for the
762: * first input point, but an output point *is* generated for
763: * the last input point.
764: *
765: * Side effects:
766: * None.
767: *
768: *--------------------------------------------------------------
769: */
770:
771: void
772: TkBezierScreenPoints(canvasPtr, control, numSteps, xPointPtr)
773: Tk_Canvas *canvasPtr; /* Canvas in which curve is to be
774: * drawn. */
775: double control[]; /* Array of coordinates for four
776: * control points: x0, y0, x1, y1,
777: * ... x3 y3. */
778: int numSteps; /* Number of curve points to
779: * generate. */
780: register XPoint *xPointPtr; /* Where to put new points. */
781: {
782: int i;
783: double u, u2, u3, t, t2, t3;
784:
785: for (i = 1; i <= numSteps; i++, xPointPtr++) {
786: t = ((double) i)/((double) numSteps);
787: t2 = t*t;
788: t3 = t2*t;
789: u = 1.0 - t;
790: u2 = u*u;
791: u3 = u2*u;
792: xPointPtr->x = SCREEN_X(canvasPtr, (control[0]*u3
793: + 3.0 * (control[2]*t*u2 + control[4]*t2*u) + control[6]*t3));
794: xPointPtr->y = SCREEN_Y(canvasPtr, (control[1]*u3
795: + 3.0 * (control[3]*t*u2 + control[5]*t2*u) + control[7]*t3));
796: }
797: }
798:
799: /*
800: *--------------------------------------------------------------
801: *
802: * TkBezierPoints --
803: *
804: * Given four control points, create a larger set of points
805: * for a Bezier spline based on the points.
806: *
807: * Results:
808: * The array at *coordPtr gets filled in with 2*numSteps
809: * coordinates, which correspond to the Bezier spline defined
810: * by the four control points. Note: no output point is
811: * generated for the first input point, but an output point
812: * *is* generated for the last input point.
813: *
814: * Side effects:
815: * None.
816: *
817: *--------------------------------------------------------------
818: */
819:
820: void
821: TkBezierPoints(control, numSteps, coordPtr)
822: double control[]; /* Array of coordinates for four
823: * control points: x0, y0, x1, y1,
824: * ... x3 y3. */
825: int numSteps; /* Number of curve points to
826: * generate. */
827: register double *coordPtr; /* Where to put new points. */
828: {
829: int i;
830: double u, u2, u3, t, t2, t3;
831:
832: for (i = 1; i <= numSteps; i++, coordPtr += 2) {
833: t = ((double) i)/((double) numSteps);
834: t2 = t*t;
835: t3 = t2*t;
836: u = 1.0 - t;
837: u2 = u*u;
838: u3 = u2*u;
839: coordPtr[0] = control[0]*u3
840: + 3.0 * (control[2]*t*u2 + control[4]*t2*u) + control[6]*t3;
841: coordPtr[1] = control[1]*u3
842: + 3.0 * (control[3]*t*u2 + control[5]*t2*u) + control[7]*t3;
843: }
844: }
845:
846: /*
847: *--------------------------------------------------------------
848: *
849: * TkMakeBezierCurve --
850: *
851: * Given a set of points, create a new set of points that
852: * fit Bezier splines to the line segments connecting the
853: * original points. Produces output points in either of two
854: * forms.
855: *
856: * Results:
857: * Either or both of the xPoints or dblPoints arrays are filled
858: * in. The return value is the number of points placed in the
859: * arrays. Note: if the first and last points are the same, then
860: * a closed curve is generated.
861: *
862: * Side effects:
863: * None.
864: *
865: *--------------------------------------------------------------
866: */
867:
868: int
869: TkMakeBezierCurve(canvasPtr, pointPtr, numPoints, numSteps, xPoints, dblPoints)
870: Tk_Canvas *canvasPtr; /* Canvas in which curve is to be
871: * drawn. */
872: double *pointPtr; /* Array of input coordinates: x0,
873: * y0, x1, y1, etc.. */
874: int numPoints; /* Number of points at pointPtr. */
875: int numSteps; /* Number of steps to use for each
876: * spline segments (determines
877: * smoothness of curve). */
878: XPoint xPoints[]; /* Array of XPoints to fill in (e.g.
879: * for display. NULL means don't
880: * fill in any XPoints. */
881: double dblPoints[]; /* Array of points to fill in as
882: * doubles, in the form x0, y0,
883: * x1, y1, .... NULL means don't
884: * fill in anything in this form.
885: * Caller must make sure that this
886: * array has enough space. */
887: {
888: int closed, outputPoints, i;
889: int numCoords = numPoints*2;
890: double control[8];
891:
892: /*
893: * If the curve is a closed one then generate a special spline
894: * that spans the last points and the first ones. Otherwise
895: * just put the first point into the output.
896: */
897:
898: outputPoints = 0;
899: if ((pointPtr[0] == pointPtr[numCoords-2])
900: && (pointPtr[1] == pointPtr[numCoords-1])) {
901: closed = 1;
902: control[0] = 0.5*pointPtr[numCoords-4] + 0.5*pointPtr[0];
903: control[1] = 0.5*pointPtr[numCoords-3] + 0.5*pointPtr[1];
904: control[2] = 0.167*pointPtr[numCoords-4] + 0.833*pointPtr[0];
905: control[3] = 0.167*pointPtr[numCoords-3] + 0.833*pointPtr[1];
906: control[4] = 0.833*pointPtr[0] + 0.167*pointPtr[2];
907: control[5] = 0.833*pointPtr[1] + 0.167*pointPtr[3];
908: control[6] = 0.5*pointPtr[0] + 0.5*pointPtr[2];
909: control[7] = 0.5*pointPtr[1] + 0.5*pointPtr[3];
910: if (xPoints != NULL) {
911: xPoints->x = SCREEN_X(canvasPtr, control[0]);
912: xPoints->y = SCREEN_Y(canvasPtr, control[1]);
913: TkBezierScreenPoints(canvasPtr, control, numSteps, xPoints+1);
914: xPoints += numSteps+1;
915: }
916: if (dblPoints != NULL) {
917: dblPoints[0] = control[0];
918: dblPoints[1] = control[1];
919: TkBezierPoints(control, numSteps, dblPoints+2);
920: dblPoints += 2*(numSteps+1);
921: }
922: outputPoints += numSteps+1;
923: } else {
924: closed = 0;
925: if (xPoints != NULL) {
926: xPoints->x = SCREEN_X(canvasPtr, pointPtr[0]);
927: xPoints->y = SCREEN_Y(canvasPtr, pointPtr[1]);
928: xPoints += 1;
929: }
930: if (dblPoints != NULL) {
931: dblPoints[0] = pointPtr[0];
932: dblPoints[1] = pointPtr[1];
933: dblPoints += 2;
934: }
935: outputPoints += 1;
936: }
937:
938: for (i = 2; i < numPoints; i++, pointPtr += 2) {
939: /*
940: * Set up the first two control points. This is done
941: * differently for the first spline of an open curve
942: * than for other cases.
943: */
944:
945: if ((i == 2) && !closed) {
946: control[0] = pointPtr[0];
947: control[1] = pointPtr[1];
948: control[2] = 0.333*pointPtr[0] + 0.667*pointPtr[2];
949: control[3] = 0.333*pointPtr[1] + 0.667*pointPtr[3];
950: } else {
951: control[0] = 0.5*pointPtr[0] + 0.5*pointPtr[2];
952: control[1] = 0.5*pointPtr[1] + 0.5*pointPtr[3];
953: control[2] = 0.167*pointPtr[0] + 0.833*pointPtr[2];
954: control[3] = 0.167*pointPtr[1] + 0.833*pointPtr[3];
955: }
956:
957: /*
958: * Set up the last two control points. This is done
959: * differently for the last spline of an open curve
960: * than for other cases.
961: */
962:
963: if ((i == (numPoints-1)) && !closed) {
964: control[4] = .667*pointPtr[2] + .333*pointPtr[4];
965: control[5] = .667*pointPtr[3] + .333*pointPtr[5];
966: control[6] = pointPtr[4];
967: control[7] = pointPtr[5];
968: } else {
969: control[4] = .833*pointPtr[2] + .167*pointPtr[4];
970: control[5] = .833*pointPtr[3] + .167*pointPtr[5];
971: control[6] = 0.5*pointPtr[2] + 0.5*pointPtr[4];
972: control[7] = 0.5*pointPtr[3] + 0.5*pointPtr[5];
973: }
974:
975: /*
976: * If the first two points coincide, or if the last
977: * two points coincide, then generate a single
978: * straight-line segment by outputting the last control
979: * point.
980: */
981:
982: if (((pointPtr[0] == pointPtr[2]) && (pointPtr[1] == pointPtr[3]))
983: || ((pointPtr[2] == pointPtr[4])
984: && (pointPtr[3] == pointPtr[5]))) {
985: if (xPoints != NULL) {
986: xPoints[0].x = SCREEN_X(canvasPtr, control[6]);
987: xPoints[0].y = SCREEN_Y(canvasPtr, control[7]);
988: xPoints++;
989: }
990: if (dblPoints != NULL) {
991: dblPoints[0] = control[6];
992: dblPoints[1] = control[7];
993: dblPoints += 2;
994: }
995: outputPoints += 1;
996: continue;
997: }
998:
999: /*
1000: * Generate a Bezier spline using the control points.
1001: */
1002:
1003:
1004: if (xPoints != NULL) {
1005: TkBezierScreenPoints(canvasPtr, control, numSteps, xPoints);
1006: xPoints += numSteps;
1007: }
1008: if (dblPoints != NULL) {
1009: TkBezierPoints(control, numSteps, dblPoints);
1010: dblPoints += 2*numSteps;
1011: }
1012: outputPoints += numSteps;
1013: }
1014: return outputPoints;
1015: }
1016:
1017: /*
1018: *--------------------------------------------------------------
1019: *
1020: * TkGetMiterPoints --
1021: *
1022: * Given three points forming an angle, compute the
1023: * coordinates of the inside and outside points of
1024: * the mitered corner formed by a line of a given
1025: * width at that angle.
1026: *
1027: * Results:
1028: * If the angle formed by the three points is less than
1029: * 11 degrees then 0 is returned and m1 and m2 aren't
1030: * modified. Otherwise 1 is returned and the points at
1031: * m1 and m2 are filled in with the positions of the points
1032: * of the mitered corner.
1033: *
1034: * Side effects:
1035: * None.
1036: *
1037: *--------------------------------------------------------------
1038: */
1039:
1040: int
1041: TkGetMiterPoints(p1, p2, p3, width, m1, m2)
1042: double p1[]; /* Points to x- and y-coordinates of point
1043: * before vertex. */
1044: double p2[]; /* Points to x- and y-coordinates of vertex
1045: * for mitered joint. */
1046: double p3[]; /* Points to x- and y-coordinates of point
1047: * after vertex. */
1048: double width; /* Width of line. */
1049: double m1[]; /* Points to place to put "left" vertex
1050: * point (see as you face from p1 to p2). */
1051: double m2[]; /* Points to place to put "right" vertex
1052: * point. */
1053: {
1054: double theta1; /* Angle of segment p2-p1. */
1055: double theta2; /* Angle of segment p2-p3. */
1056: double theta; /* Angle between line segments (angle
1057: * of joint). */
1058: double theta3; /* Angle that bisects theta1 and
1059: * theta2 and points to m1. */
1060: double dist; /* Distance of miter points from p2. */
1061: double deltaX, deltaY; /* X and y offsets cooresponding to
1062: * dist (fudge factors for bounding
1063: * box). */
1064: static float elevenDegrees = (11.0*2.0*PI)/360.0;
1065:
1066: if (p2[1] == p1[1]) {
1067: theta1 = (p2[0] < p1[0]) ? 0 : PI;
1068: } else if (p2[0] == p1[0]) {
1069: theta1 = (p2[1] < p1[1]) ? PI/2.0 : -PI/2.0;
1070: } else {
1071: theta1 = atan2(p1[1] - p2[1], p1[0] - p2[0]);
1072: }
1073: if (p3[1] == p2[1]) {
1074: theta2 = (p3[0] > p2[0]) ? 0 : PI;
1075: } else if (p3[0] == p2[0]) {
1076: theta2 = (p3[1] > p2[1]) ? PI/2.0 : -PI/2.0;
1077: } else {
1078: theta2 = atan2(p3[1] - p2[1], p3[0] - p2[0]);
1079: }
1080: theta = theta1 - theta2;
1081: if (theta > PI) {
1082: theta -= 2*PI;
1083: } else if (theta < -PI) {
1084: theta += 2*PI;
1085: }
1086: if ((theta < elevenDegrees) && (theta > -elevenDegrees)) {
1087: return 0;
1088: }
1089: dist = 0.5*width/sin(0.5*theta);
1090: if (dist < 0.0) {
1091: dist = -dist;
1092: }
1093:
1094: /*
1095: * Compute theta3 (make sure that it points to the left when
1096: * looking from p1 to p2).
1097: */
1098:
1099: theta3 = (theta1 + theta2)/2.0;
1100: if (sin(theta3 - (theta1 + PI)) < 0.0) {
1101: theta3 += PI;
1102: }
1103: deltaX = dist*cos(theta3);
1104: m1[0] = p2[0] + deltaX;
1105: m2[0] = p2[0] - deltaX;
1106: deltaY = dist*sin(theta3);
1107: m1[1] = p2[1] + deltaY;
1108: m2[1] = p2[1] - deltaY;
1109: return 1;
1110: }
1111:
1112: /*
1113: *--------------------------------------------------------------
1114: *
1115: * TkGetButtPoints --
1116: *
1117: * Given two points forming a line segment, compute the
1118: * coordinates of two endpoints of a rectangle formed by
1119: * bloating the line segment until it is width units wide.
1120: *
1121: * Results:
1122: * There is no return value. M1 and m2 are filled in to
1123: * correspond to m1 and m2 in the diagram below:
1124: *
1125: * ----------------* m1
1126: * |
1127: * p1 *---------------* p2
1128: * |
1129: * ----------------* m2
1130: *
1131: * M1 and m2 will be W units apart, with p2 centered between
1132: * them and m1-m2 perpendicular to p1-p2. However, if
1133: * "project" is true then m1 and m2 will be as follows:
1134: *
1135: * -------------------* m1
1136: * p2 |
1137: * p1 *---------------* |
1138: * |
1139: * -------------------* m2
1140: *
1141: * In this case p2 will be width/2 units from the segment m1-m2.
1142: *
1143: * Side effects:
1144: * None.
1145: *
1146: *--------------------------------------------------------------
1147: */
1148:
1149: void
1150: TkGetButtPoints(p1, p2, width, project, m1, m2)
1151: double p1[]; /* Points to x- and y-coordinates of point
1152: * before vertex. */
1153: double p2[]; /* Points to x- and y-coordinates of vertex
1154: * for mitered joint. */
1155: double width; /* Width of line. */
1156: int project; /* Non-zero means project p2 by an additional
1157: * width/2 before computing m1 and m2. */
1158: double m1[]; /* Points to place to put "left" result
1159: * point, as you face from p1 to p2. */
1160: double m2[]; /* Points to place to put "right" result
1161: * point. */
1162: {
1163: double length; /* Length of p1-p2 segment. */
1164: double deltaX, deltaY; /* Increments in coords. */
1165:
1166: width *= 0.5;
1167: length = hypot(p2[0] - p1[0], p2[1] - p1[1]);
1168: if (length == 0.0) {
1169: m1[0] = m2[0] = p2[0];
1170: m1[1] = m2[1] = p2[1];
1171: } else {
1172: deltaX = -width * (p2[1] - p1[1]) / length;
1173: deltaY = width * (p2[0] - p1[0]) / length;
1174: m1[0] = p2[0] + deltaX;
1175: m2[0] = p2[0] - deltaX;
1176: m1[1] = p2[1] + deltaY;
1177: m2[1] = p2[1] - deltaY;
1178: if (project) {
1179: m1[0] += deltaY;
1180: m2[0] += deltaY;
1181: m1[1] -= deltaX;
1182: m2[1] -= deltaX;
1183: }
1184: }
1185: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.