]>
cvs.zerfleddert.de Git - micropolis/blob - src/tk/tktrig.c
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
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.
20 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkTrig.c,v 1.8 92/08/24 09:24:14 ouster Exp $ SPRITE (Berkeley)";
29 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
31 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
32 #define PI 3.14159265358979323846
35 *--------------------------------------------------------------
39 * Compute the distance from a point to a finite line segment.
42 * The return value is the distance from the line segment
43 * whose end-points are *end1Ptr and *end2Ptr to the point
49 *--------------------------------------------------------------
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. */
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.
66 if (end1Ptr
[0] == end2Ptr
[0]) {
73 if (end1Ptr
[1] >= end2Ptr
[1]) {
74 y
= MIN(end1Ptr
[1], pointPtr
[1]);
75 y
= MAX(y
, end2Ptr
[1]);
77 y
= MIN(end2Ptr
[1], pointPtr
[1]);
78 y
= MAX(y
, end1Ptr
[1]);
80 } else if (end1Ptr
[1] == end2Ptr
[1]) {
87 if (end1Ptr
[0] >= end2Ptr
[0]) {
88 x
= MIN(end1Ptr
[0], pointPtr
[0]);
89 x
= MAX(x
, end2Ptr
[0]);
91 x
= MIN(end2Ptr
[0], pointPtr
[0]);
92 x
= MAX(x
, end1Ptr
[0]);
95 double m1
, b1
, m2
, b2
;
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.
104 m1
= (end2Ptr
[1] - end1Ptr
[1])/(end2Ptr
[0] - end1Ptr
[0]);
105 b1
= end1Ptr
[1] - m1
*end1Ptr
[0];
107 b2
= pointPtr
[1] - m2
*pointPtr
[0];
108 x
= (b2
- b1
)/(m1
- m2
);
110 if (end1Ptr
[0] > end2Ptr
[0]) {
111 if (x
> end1Ptr
[0]) {
114 } else if (x
< end2Ptr
[0]) {
119 if (x
> end2Ptr
[0]) {
122 } else if (x
< end1Ptr
[0]) {
130 * Compute the distance to the closest point.
133 return hypot(pointPtr
[0] - x
, pointPtr
[1] - y
);
137 *--------------------------------------------------------------
141 * Determine whether a line lies entirely inside, entirely
142 * outside, or overlapping a given rectangular area.
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.
153 *--------------------------------------------------------------
157 TkLineToArea(end1Ptr
, end2Ptr
, rectPtr
)
158 double end1Ptr
[2]; /* X and y coordinates for one endpoint
160 double end2Ptr
[2]; /* X and y coordinates for other endpoint
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. */
166 int inside1
, inside2
;
169 * First check the two points individually to see whether they
170 * are inside the rectangle or not.
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
) {
180 if (inside1
& inside2
) {
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
191 if (end1Ptr
[0] == end2Ptr
[0]) {
196 if (((end1Ptr
[1] >= rectPtr
[1]) ^ (end2Ptr
[1] >= rectPtr
[1]))
197 && (end1Ptr
[0] >= rectPtr
[0])
198 && (end1Ptr
[0] <= rectPtr
[2])) {
201 } else if (end1Ptr
[1] == end2Ptr
[1]) {
206 if (((end1Ptr
[0] >= rectPtr
[0]) ^ (end2Ptr
[0] >= rectPtr
[0]))
207 && (end1Ptr
[1] >= rectPtr
[1])
208 && (end1Ptr
[1] <= rectPtr
[3])) {
212 double m
, x
, y
, low
, high
;
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.
220 m
= (end2Ptr
[1] - end1Ptr
[1])/(end2Ptr
[0] - end1Ptr
[0]);
221 if (end1Ptr
[0] < end2Ptr
[0]) {
222 low
= end1Ptr
[0]; high
= end2Ptr
[0];
224 low
= end2Ptr
[0]; high
= end1Ptr
[0];
231 y
= end1Ptr
[1] + (rectPtr
[0] - end1Ptr
[0])*m
;
232 if ((rectPtr
[0] >= low
) && (rectPtr
[0] <= high
)
233 && (y
>= rectPtr
[1]) && (y
<= rectPtr
[3])) {
241 y
+= (rectPtr
[2] - rectPtr
[0])*m
;
242 if ((y
>= rectPtr
[1]) && (y
<= rectPtr
[3])
243 && (rectPtr
[2] >= low
) && (rectPtr
[2] <= high
)) {
251 if (end1Ptr
[1] < end2Ptr
[1]) {
252 low
= end1Ptr
[1]; high
= end2Ptr
[1];
254 low
= end2Ptr
[1]; high
= end1Ptr
[1];
256 x
= end1Ptr
[0] + (rectPtr
[1] - end1Ptr
[1])/m
;
257 if ((x
>= rectPtr
[0]) && (x
<= rectPtr
[2])
258 && (rectPtr
[1] >= low
) && (rectPtr
[1] <= high
)) {
266 x
+= (rectPtr
[3] - rectPtr
[1])/m
;
267 if ((x
>= rectPtr
[0]) && (x
<= rectPtr
[2])
268 && (rectPtr
[3] >= low
) && (rectPtr
[3] <= high
)) {
276 *--------------------------------------------------------------
278 * TkPolygonToPoint --
280 * Compute the distance from a point to a polygon.
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.
291 *--------------------------------------------------------------
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. */
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. */
308 register double *pPtr
;
311 * Iterate through all of the edges in the polygon, updating
312 * bestDist and intersections.
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.
323 for (count
= numPoints
, pPtr
= polyPtr
; count
> 1; count
--, pPtr
+= 2) {
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
333 if (pPtr
[2] == pPtr
[0]) {
340 if (pPtr
[1] >= pPtr
[3]) {
341 y
= MIN(pPtr
[1], pointPtr
[1]);
344 y
= MIN(pPtr
[3], pointPtr
[1]);
347 } else if (pPtr
[3] == pPtr
[1]) {
354 if (pPtr
[0] >= pPtr
[2]) {
355 x
= MIN(pPtr
[0], pointPtr
[0]);
357 if ((pointPtr
[1] < y
) && (pointPtr
[0] < pPtr
[0])
358 && (pointPtr
[0] >= pPtr
[2])) {
362 x
= MIN(pPtr
[2], pointPtr
[0]);
364 if ((pointPtr
[1] < y
) && (pointPtr
[0] < pPtr
[2])
365 && (pointPtr
[0] >= pPtr
[0])) {
370 double m1
, b1
, m2
, b2
;
371 int lower
; /* Non-zero means point below line. */
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.
380 m1
= (pPtr
[3] - pPtr
[1])/(pPtr
[2] - pPtr
[0]);
381 b1
= pPtr
[1] - m1
*pPtr
[0];
383 b2
= pointPtr
[1] - m2
*pointPtr
[0];
384 x
= (b2
- b1
)/(m1
- m2
);
386 if (pPtr
[0] > pPtr
[2]) {
390 } else if (x
< pPtr
[2]) {
398 } else if (x
< pPtr
[0]) {
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]))) {
411 * Compute the distance to the closest point, and see if that
412 * is the best distance seen so far.
415 dist
= hypot(pointPtr
[0] - x
, pointPtr
[1] - y
);
416 if (dist
< bestDist
) {
422 * We've processed all of the points. If the number of intersections
423 * is odd, the point is inside the polygon.
426 if (intersections
& 0x1) {
433 *--------------------------------------------------------------
437 * Determine whether a polygon lies entirely inside, entirely
438 * outside, or overlapping a given rectangular area.
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.
449 *--------------------------------------------------------------
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. */
462 int state
; /* State of all edges seen so far (-1 means
463 * outside, 1 means inside, won't ever be
466 register double *pPtr
;
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
474 state
= TkLineToArea(polyPtr
, polyPtr
+2, rectPtr
);
478 for (pPtr
= polyPtr
+2, count
= numPoints
-1; count
>= 2;
479 pPtr
+= 2, count
--) {
480 if (TkLineToArea(pPtr
, pPtr
+2, rectPtr
) != state
) {
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.
495 if (TkPolygonToPoint(polyPtr
, numPoints
, rectPtr
) == 0.0) {
502 *--------------------------------------------------------------
506 * Computes the distance from a given point to a given
507 * oval, in canvas units.
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.
521 *--------------------------------------------------------------
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
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. */
535 double xDelta
, yDelta
, scaledDistance
, distToOutline
, distToCenter
;
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.
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));
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
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.
561 if (scaledDistance
> 1.0) {
562 return (distToCenter
/scaledDistance
) * (scaledDistance
- 1.0);
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.
576 distToOutline
= (distToCenter
/scaledDistance
) * (1.0 - scaledDistance
)
578 if (distToOutline
< 0.0) {
581 return distToOutline
;
585 *--------------------------------------------------------------
589 * Determine whether an oval lies entirely inside, entirely
590 * outside, or overlapping a given rectangular area.
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.
601 *--------------------------------------------------------------
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
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. */
614 double centerX
, centerY
, radX
, radY
, deltaX
, deltaY
;
617 * First, see if oval is entirely inside rectangle or entirely
621 if ((rectPtr
[0] <= ovalPtr
[0]) && (rectPtr
[2] >= ovalPtr
[2])
622 && (rectPtr
[1] <= ovalPtr
[1]) && (rectPtr
[3] >= ovalPtr
[3])) {
625 if ((rectPtr
[2] < ovalPtr
[0]) || (rectPtr
[0] > ovalPtr
[2])
626 || (rectPtr
[3] < ovalPtr
[1]) || (rectPtr
[1] > ovalPtr
[3])) {
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.
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;
643 deltaY
= rectPtr
[1] - centerY
;
645 deltaY
= centerY
- rectPtr
[3];
657 deltaX
= (rectPtr
[0] - centerX
)/radX
;
659 if ((deltaX
+ deltaY
) <= 1.0) {
667 deltaX
= (rectPtr
[2] - centerX
)/radX
;
669 if ((deltaX
+ deltaY
) <= 1.0) {
673 deltaX
= rectPtr
[0] - centerX
;
675 deltaX
= centerX
- rectPtr
[2];
687 deltaY
= (rectPtr
[1] - centerY
)/radY
;
689 if ((deltaX
+ deltaY
) < 1.0) {
697 deltaY
= (rectPtr
[3] - centerY
)/radY
;
699 if ((deltaX
+ deltaY
) < 1.0) {
707 *--------------------------------------------------------------
711 * Given a point and a generic canvas item header, expand
712 * the item's bounding box if needed to include the point.
720 *--------------------------------------------------------------
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. */
734 tmp
= pointPtr
[0] + 0.5;
735 if (tmp
< itemPtr
->x1
) {
738 if (tmp
> itemPtr
->x2
) {
741 tmp
= pointPtr
[1] + 0.5;
742 if (tmp
< itemPtr
->y1
) {
745 if (tmp
> itemPtr
->y2
) {
751 *--------------------------------------------------------------
753 * TkBezierScreenPoints --
755 * Given four control points, create a larger set of XPoints
756 * for a Bezier spline based on the points.
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.
768 *--------------------------------------------------------------
772 TkBezierScreenPoints(canvasPtr
, control
, numSteps
, xPointPtr
)
773 Tk_Canvas
*canvasPtr
; /* Canvas in which curve is to be
775 double control
[]; /* Array of coordinates for four
776 * control points: x0, y0, x1, y1,
778 int numSteps
; /* Number of curve points to
780 register XPoint
*xPointPtr
; /* Where to put new points. */
783 double u
, u2
, u3
, t
, t2
, t3
;
785 for (i
= 1; i
<= numSteps
; i
++, xPointPtr
++) {
786 t
= ((double) i
)/((double) numSteps
);
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
));
800 *--------------------------------------------------------------
804 * Given four control points, create a larger set of points
805 * for a Bezier spline based on the points.
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.
817 *--------------------------------------------------------------
821 TkBezierPoints(control
, numSteps
, coordPtr
)
822 double control
[]; /* Array of coordinates for four
823 * control points: x0, y0, x1, y1,
825 int numSteps
; /* Number of curve points to
827 register double *coordPtr
; /* Where to put new points. */
830 double u
, u2
, u3
, t
, t2
, t3
;
832 for (i
= 1; i
<= numSteps
; i
++, coordPtr
+= 2) {
833 t
= ((double) i
)/((double) numSteps
);
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
;
847 *--------------------------------------------------------------
849 * TkMakeBezierCurve --
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
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.
865 *--------------------------------------------------------------
869 TkMakeBezierCurve(canvasPtr
, pointPtr
, numPoints
, numSteps
, xPoints
, dblPoints
)
870 Tk_Canvas
*canvasPtr
; /* Canvas in which curve is to be
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. */
888 int closed
, outputPoints
, i
;
889 int numCoords
= numPoints
*2;
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.
899 if ((pointPtr
[0] == pointPtr
[numCoords
-2])
900 && (pointPtr
[1] == pointPtr
[numCoords
-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;
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);
922 outputPoints
+= numSteps
+1;
925 if (xPoints
!= NULL
) {
926 xPoints
->x
= SCREEN_X(canvasPtr
, pointPtr
[0]);
927 xPoints
->y
= SCREEN_Y(canvasPtr
, pointPtr
[1]);
930 if (dblPoints
!= NULL
) {
931 dblPoints
[0] = pointPtr
[0];
932 dblPoints
[1] = pointPtr
[1];
938 for (i
= 2; i
< numPoints
; i
++, pointPtr
+= 2) {
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.
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];
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];
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.
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];
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];
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
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]);
990 if (dblPoints
!= NULL
) {
991 dblPoints
[0] = control
[6];
992 dblPoints
[1] = control
[7];
1000 * Generate a Bezier spline using the control points.
1004 if (xPoints
!= NULL
) {
1005 TkBezierScreenPoints(canvasPtr
, control
, numSteps
, xPoints
);
1006 xPoints
+= numSteps
;
1008 if (dblPoints
!= NULL
) {
1009 TkBezierPoints(control
, numSteps
, dblPoints
);
1010 dblPoints
+= 2*numSteps
;
1012 outputPoints
+= numSteps
;
1014 return outputPoints
;
1018 *--------------------------------------------------------------
1020 * TkGetMiterPoints --
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.
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.
1037 *--------------------------------------------------------------
1041 TkGetMiterPoints(p1
, p2
, p3
, width
, m1
, m2
)
1042 double p1
[]; /* Points to x- and y-coordinates of point
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
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
1054 double theta1
; /* Angle of segment p2-p1. */
1055 double theta2
; /* Angle of segment p2-p3. */
1056 double theta
; /* Angle between line segments (angle
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
1064 static float elevenDegrees
= (11.0*2.0*PI
)/360.0;
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;
1071 theta1
= atan2(p1
[1] - p2
[1], p1
[0] - p2
[0]);
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;
1078 theta2
= atan2(p3
[1] - p2
[1], p3
[0] - p2
[0]);
1080 theta
= theta1
- theta2
;
1083 } else if (theta
< -PI
) {
1086 if ((theta
< elevenDegrees
) && (theta
> -elevenDegrees
)) {
1089 dist
= 0.5*width
/sin(0.5*theta
);
1095 * Compute theta3 (make sure that it points to the left when
1096 * looking from p1 to p2).
1099 theta3
= (theta1
+ theta2
)/2.0;
1100 if (sin(theta3
- (theta1
+ PI
)) < 0.0) {
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
;
1113 *--------------------------------------------------------------
1115 * TkGetButtPoints --
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.
1122 * There is no return value. M1 and m2 are filled in to
1123 * correspond to m1 and m2 in the diagram below:
1125 * ----------------* m1
1127 * p1 *---------------* p2
1129 * ----------------* m2
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:
1135 * -------------------* m1
1137 * p1 *---------------* |
1139 * -------------------* m2
1141 * In this case p2 will be width/2 units from the segment m1-m2.
1146 *--------------------------------------------------------------
1150 TkGetButtPoints(p1
, p2
, width
, project
, m1
, m2
)
1151 double p1
[]; /* Points to x- and y-coordinates of point
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
1163 double length
; /* Length of p1-p2 segment. */
1164 double deltaX
, deltaY
; /* Increments in coords. */
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];
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
;