1 /*
2 * tkTrig.c --
3 *
4 * This is an abridged copy of generic/tkTrig.c from the source
5 * distribution for tk8.3.0.
6 *
7 * This file contains a collection of trigonometry utility
8 * routines that are used by Tk and in particular by the
9 * canvas code. It also has miscellaneous geometry functions
10 * used by canvases.
11 *
12 * Copyright (c) 1992-1994 The Regents of the University of California.
13 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
14 *
15 * Changelog:
16 * Replaced "Tk" prefix in function names with "Tkgeomap" to prevent collisions
17 * with later Tk functions. The functions have not been otherwise modified.
18 * Gordon Carrie, 7 December 2003.
19 *
20 * See the file "license.terms" for information on usage and redistribution
21 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
22 *
23 * @(#) $Id: tkTrig.c,v 1.1 2004/02/18 20:26:21 tkgeomap Exp $
24 */
25
26 #include <stdio.h>
27 #include <math.h>
28 #include "tkgeomapInt.h"
29
30 #undef MIN
31 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
32 #undef MAX
33 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
34 #ifndef PI
35 # define PI 3.14159265358979323846
36 #endif /* PI */
37
38 /*
39 *--------------------------------------------------------------
40 *
41 * TkgeomapLineToPoint --
42 *
43 * Compute the distance from a point to a finite line segment.
44 *
45 * Results:
46 * The return value is the distance from the line segment
47 * whose end-points are *end1Ptr and *end2Ptr to the point
48 * given by *pointPtr.
49 *
50 * Side effects:
51 * None.
52 *
53 *--------------------------------------------------------------
54 */
55
56 double
TkgeomapLineToPoint(end1Ptr,end2Ptr,pointPtr)57 TkgeomapLineToPoint(end1Ptr, end2Ptr, pointPtr)
58 double end1Ptr[2]; /* Coordinates of first end-point of line. */
59 double end2Ptr[2]; /* Coordinates of second end-point of line. */
60 double pointPtr[2]; /* Points to coords for point. */
61 {
62 double x, y;
63
64 /*
65 * Compute the point on the line that is closest to the
66 * point. This must be done separately for vertical edges,
67 * horizontal edges, and other edges.
68 */
69
70 if (end1Ptr[0] == end2Ptr[0]) {
71
72 /*
73 * Vertical edge.
74 */
75
76 x = end1Ptr[0];
77 if (end1Ptr[1] >= end2Ptr[1]) {
78 y = MIN(end1Ptr[1], pointPtr[1]);
79 y = MAX(y, end2Ptr[1]);
80 } else {
81 y = MIN(end2Ptr[1], pointPtr[1]);
82 y = MAX(y, end1Ptr[1]);
83 }
84 } else if (end1Ptr[1] == end2Ptr[1]) {
85
86 /*
87 * Horizontal edge.
88 */
89
90 y = end1Ptr[1];
91 if (end1Ptr[0] >= end2Ptr[0]) {
92 x = MIN(end1Ptr[0], pointPtr[0]);
93 x = MAX(x, end2Ptr[0]);
94 } else {
95 x = MIN(end2Ptr[0], pointPtr[0]);
96 x = MAX(x, end1Ptr[0]);
97 }
98 } else {
99 double m1, b1, m2, b2;
100
101 /*
102 * The edge is neither horizontal nor vertical. Convert the
103 * edge to a line equation of the form y = m1*x + b1. Then
104 * compute a line perpendicular to this edge but passing
105 * through the point, also in the form y = m2*x + b2.
106 */
107
108 m1 = (end2Ptr[1] - end1Ptr[1])/(end2Ptr[0] - end1Ptr[0]);
109 b1 = end1Ptr[1] - m1*end1Ptr[0];
110 m2 = -1.0/m1;
111 b2 = pointPtr[1] - m2*pointPtr[0];
112 x = (b2 - b1)/(m1 - m2);
113 y = m1*x + b1;
114 if (end1Ptr[0] > end2Ptr[0]) {
115 if (x > end1Ptr[0]) {
116 x = end1Ptr[0];
117 y = end1Ptr[1];
118 } else if (x < end2Ptr[0]) {
119 x = end2Ptr[0];
120 y = end2Ptr[1];
121 }
122 } else {
123 if (x > end2Ptr[0]) {
124 x = end2Ptr[0];
125 y = end2Ptr[1];
126 } else if (x < end1Ptr[0]) {
127 x = end1Ptr[0];
128 y = end1Ptr[1];
129 }
130 }
131 }
132
133 /*
134 * Compute the distance to the closest point.
135 */
136
137 return hypot(pointPtr[0] - x, pointPtr[1] - y);
138 }
139
140 /*
141 *--------------------------------------------------------------
142 *
143 * TkgeomapLineToArea --
144 *
145 * Determine whether a line lies entirely inside, entirely
146 * outside, or overlapping a given rectangular area.
147 *
148 * Results:
149 * -1 is returned if the line given by end1Ptr and end2Ptr
150 * is entirely outside the rectangle given by rectPtr. 0 is
151 * returned if the polygon overlaps the rectangle, and 1 is
152 * returned if the polygon is entirely inside the rectangle.
153 *
154 * Side effects:
155 * None.
156 *
157 *--------------------------------------------------------------
158 */
159
160 int
TkgeomapLineToArea(end1Ptr,end2Ptr,rectPtr)161 TkgeomapLineToArea(end1Ptr, end2Ptr, rectPtr)
162 double end1Ptr[2]; /* X and y coordinates for one endpoint
163 * of line. */
164 double end2Ptr[2]; /* X and y coordinates for other endpoint
165 * of line. */
166 double rectPtr[4]; /* Points to coords for rectangle, in the
167 * order x1, y1, x2, y2. X1 must be no
168 * larger than x2, and y1 no larger than y2. */
169 {
170 int inside1, inside2;
171
172 /*
173 * First check the two points individually to see whether they
174 * are inside the rectangle or not.
175 */
176
177 inside1 = (end1Ptr[0] >= rectPtr[0]) && (end1Ptr[0] <= rectPtr[2])
178 && (end1Ptr[1] >= rectPtr[1]) && (end1Ptr[1] <= rectPtr[3]);
179 inside2 = (end2Ptr[0] >= rectPtr[0]) && (end2Ptr[0] <= rectPtr[2])
180 && (end2Ptr[1] >= rectPtr[1]) && (end2Ptr[1] <= rectPtr[3]);
181 if (inside1 != inside2) {
182 return 0;
183 }
184 if (inside1 & inside2) {
185 return 1;
186 }
187
188 /*
189 * Both points are outside the rectangle, but still need to check
190 * for intersections between the line and the rectangle. Horizontal
191 * and vertical lines are particularly easy, so handle them
192 * separately.
193 */
194
195 if (end1Ptr[0] == end2Ptr[0]) {
196 /*
197 * Vertical line.
198 */
199
200 if (((end1Ptr[1] >= rectPtr[1]) ^ (end2Ptr[1] >= rectPtr[1]))
201 && (end1Ptr[0] >= rectPtr[0])
202 && (end1Ptr[0] <= rectPtr[2])) {
203 return 0;
204 }
205 } else if (end1Ptr[1] == end2Ptr[1]) {
206 /*
207 * Horizontal line.
208 */
209
210 if (((end1Ptr[0] >= rectPtr[0]) ^ (end2Ptr[0] >= rectPtr[0]))
211 && (end1Ptr[1] >= rectPtr[1])
212 && (end1Ptr[1] <= rectPtr[3])) {
213 return 0;
214 }
215 } else {
216 double m, x, y, low, high;
217
218 /*
219 * Diagonal line. Compute slope of line and use
220 * for intersection checks against each of the
221 * sides of the rectangle: left, right, bottom, top.
222 */
223
224 m = (end2Ptr[1] - end1Ptr[1])/(end2Ptr[0] - end1Ptr[0]);
225 if (end1Ptr[0] < end2Ptr[0]) {
226 low = end1Ptr[0]; high = end2Ptr[0];
227 } else {
228 low = end2Ptr[0]; high = end1Ptr[0];
229 }
230
231 /*
232 * Left edge.
233 */
234
235 y = end1Ptr[1] + (rectPtr[0] - end1Ptr[0])*m;
236 if ((rectPtr[0] >= low) && (rectPtr[0] <= high)
237 && (y >= rectPtr[1]) && (y <= rectPtr[3])) {
238 return 0;
239 }
240
241 /*
242 * Right edge.
243 */
244
245 y += (rectPtr[2] - rectPtr[0])*m;
246 if ((y >= rectPtr[1]) && (y <= rectPtr[3])
247 && (rectPtr[2] >= low) && (rectPtr[2] <= high)) {
248 return 0;
249 }
250
251 /*
252 * Bottom edge.
253 */
254
255 if (end1Ptr[1] < end2Ptr[1]) {
256 low = end1Ptr[1]; high = end2Ptr[1];
257 } else {
258 low = end2Ptr[1]; high = end1Ptr[1];
259 }
260 x = end1Ptr[0] + (rectPtr[1] - end1Ptr[1])/m;
261 if ((x >= rectPtr[0]) && (x <= rectPtr[2])
262 && (rectPtr[1] >= low) && (rectPtr[1] <= high)) {
263 return 0;
264 }
265
266 /*
267 * Top edge.
268 */
269
270 x += (rectPtr[3] - rectPtr[1])/m;
271 if ((x >= rectPtr[0]) && (x <= rectPtr[2])
272 && (rectPtr[3] >= low) && (rectPtr[3] <= high)) {
273 return 0;
274 }
275 }
276 return -1;
277 }
278
279 /*
280 *--------------------------------------------------------------
281 *
282 * TkgeomapPolygonToPoint --
283 *
284 * Compute the distance from a point to a polygon.
285 *
286 * Results:
287 * The return value is 0.0 if the point referred to by
288 * pointPtr is within the polygon referred to by polyPtr
289 * and numPoints. Otherwise the return value is the
290 * distance of the point from the polygon.
291 *
292 * Side effects:
293 * None.
294 *
295 *--------------------------------------------------------------
296 */
297
298 double
TkgeomapPolygonToPoint(polyPtr,numPoints,pointPtr)299 TkgeomapPolygonToPoint(polyPtr, numPoints, pointPtr)
300 double *polyPtr; /* Points to an array coordinates for
301 * closed polygon: x0, y0, x1, y1, ...
302 * The polygon may be self-intersecting. */
303 int numPoints; /* Total number of points at *polyPtr. */
304 double *pointPtr; /* Points to coords for point. */
305 {
306 double bestDist; /* Closest distance between point and
307 * any edge in polygon. */
308 int intersections; /* Number of edges in the polygon that
309 * intersect a ray extending vertically
310 * upwards from the point to infinity. */
311 int count;
312 register double *pPtr;
313
314 /*
315 * Iterate through all of the edges in the polygon, updating
316 * bestDist and intersections.
317 *
318 * TRICKY POINT: when computing intersections, include left
319 * x-coordinate of line within its range, but not y-coordinate.
320 * Otherwise if the point lies exactly below a vertex we'll
321 * count it as two intersections.
322 */
323
324 bestDist = 1.0e36;
325 intersections = 0;
326
327 for (count = numPoints, pPtr = polyPtr; count > 1; count--, pPtr += 2) {
328 double x, y, dist;
329
330 /*
331 * Compute the point on the current edge closest to the point
332 * and update the intersection count. This must be done
333 * separately for vertical edges, horizontal edges, and
334 * other edges.
335 */
336
337 if (pPtr[2] == pPtr[0]) {
338
339 /*
340 * Vertical edge.
341 */
342
343 x = pPtr[0];
344 if (pPtr[1] >= pPtr[3]) {
345 y = MIN(pPtr[1], pointPtr[1]);
346 y = MAX(y, pPtr[3]);
347 } else {
348 y = MIN(pPtr[3], pointPtr[1]);
349 y = MAX(y, pPtr[1]);
350 }
351 } else if (pPtr[3] == pPtr[1]) {
352
353 /*
354 * Horizontal edge.
355 */
356
357 y = pPtr[1];
358 if (pPtr[0] >= pPtr[2]) {
359 x = MIN(pPtr[0], pointPtr[0]);
360 x = MAX(x, pPtr[2]);
361 if ((pointPtr[1] < y) && (pointPtr[0] < pPtr[0])
362 && (pointPtr[0] >= pPtr[2])) {
363 intersections++;
364 }
365 } else {
366 x = MIN(pPtr[2], pointPtr[0]);
367 x = MAX(x, pPtr[0]);
368 if ((pointPtr[1] < y) && (pointPtr[0] < pPtr[2])
369 && (pointPtr[0] >= pPtr[0])) {
370 intersections++;
371 }
372 }
373 } else {
374 double m1, b1, m2, b2;
375 int lower; /* Non-zero means point below line. */
376
377 /*
378 * The edge is neither horizontal nor vertical. Convert the
379 * edge to a line equation of the form y = m1*x + b1. Then
380 * compute a line perpendicular to this edge but passing
381 * through the point, also in the form y = m2*x + b2.
382 */
383
384 m1 = (pPtr[3] - pPtr[1])/(pPtr[2] - pPtr[0]);
385 b1 = pPtr[1] - m1*pPtr[0];
386 m2 = -1.0/m1;
387 b2 = pointPtr[1] - m2*pointPtr[0];
388 x = (b2 - b1)/(m1 - m2);
389 y = m1*x + b1;
390 if (pPtr[0] > pPtr[2]) {
391 if (x > pPtr[0]) {
392 x = pPtr[0];
393 y = pPtr[1];
394 } else if (x < pPtr[2]) {
395 x = pPtr[2];
396 y = pPtr[3];
397 }
398 } else {
399 if (x > pPtr[2]) {
400 x = pPtr[2];
401 y = pPtr[3];
402 } else if (x < pPtr[0]) {
403 x = pPtr[0];
404 y = pPtr[1];
405 }
406 }
407 lower = (m1*pointPtr[0] + b1) > pointPtr[1];
408 if (lower && (pointPtr[0] >= MIN(pPtr[0], pPtr[2]))
409 && (pointPtr[0] < MAX(pPtr[0], pPtr[2]))) {
410 intersections++;
411 }
412 }
413
414 /*
415 * Compute the distance to the closest point, and see if that
416 * is the best distance seen so far.
417 */
418
419 dist = hypot(pointPtr[0] - x, pointPtr[1] - y);
420 if (dist < bestDist) {
421 bestDist = dist;
422 }
423 }
424
425 /*
426 * We've processed all of the points. If the number of intersections
427 * is odd, the point is inside the polygon.
428 */
429
430 if (intersections & 0x1) {
431 return 0.0;
432 }
433 return bestDist;
434 }
435
436 /*
437 *--------------------------------------------------------------
438 *
439 * TkgeomapPolygonToArea --
440 *
441 * Determine whether a polygon lies entirely inside, entirely
442 * outside, or overlapping a given rectangular area.
443 *
444 * Results:
445 * -1 is returned if the polygon given by polyPtr and numPoints
446 * is entirely outside the rectangle given by rectPtr. 0 is
447 * returned if the polygon overlaps the rectangle, and 1 is
448 * returned if the polygon is entirely inside the rectangle.
449 *
450 * Side effects:
451 * None.
452 *
453 *--------------------------------------------------------------
454 */
455
456 int
TkgeomapPolygonToArea(polyPtr,numPoints,rectPtr)457 TkgeomapPolygonToArea(polyPtr, numPoints, rectPtr)
458 double *polyPtr; /* Points to an array coordinates for
459 * closed polygon: x0, y0, x1, y1, ...
460 * The polygon may be self-intersecting. */
461 int numPoints; /* Total number of points at *polyPtr. */
462 register double *rectPtr; /* Points to coords for rectangle, in the
463 * order x1, y1, x2, y2. X1 and y1 must
464 * be lower-left corner. */
465 {
466 int state; /* State of all edges seen so far (-1 means
467 * outside, 1 means inside, won't ever be
468 * 0). */
469 int count;
470 register double *pPtr;
471
472 /*
473 * Iterate over all of the edges of the polygon and test them
474 * against the rectangle. Can quit as soon as the state becomes
475 * "intersecting".
476 */
477
478 state = TkgeomapLineToArea(polyPtr, polyPtr+2, rectPtr);
479 if (state == 0) {
480 return 0;
481 }
482 for (pPtr = polyPtr+2, count = numPoints-1; count >= 2;
483 pPtr += 2, count--) {
484 if (TkgeomapLineToArea(pPtr, pPtr+2, rectPtr) != state) {
485 return 0;
486 }
487 }
488
489 /*
490 * If all of the edges were inside the rectangle we're done.
491 * If all of the edges were outside, then the rectangle could
492 * still intersect the polygon (if it's entirely enclosed).
493 * Call TkgeomapPolygonToPoint to figure this out.
494 */
495
496 if (state == 1) {
497 return 1;
498 }
499 if (TkgeomapPolygonToPoint(polyPtr, numPoints, rectPtr) == 0.0) {
500 return 0;
501 }
502 return -1;
503 }
504
505 /*
506 *--------------------------------------------------------------
507 *
508 * TkgeomapBezierScreenPoints --
509 *
510 * Given four control points, create a larger set of XPoints
511 * for a Bezier spline based on the points.
512 *
513 * Results:
514 * The array at *xPointPtr gets filled in with numSteps XPoints
515 * corresponding to the Bezier spline defined by the four
516 * control points. Note: no output point is generated for the
517 * first input point, but an output point *is* generated for
518 * the last input point.
519 *
520 * Side effects:
521 * None.
522 *
523 *--------------------------------------------------------------
524 */
525
526 void
TkgeomapBezierScreenPoints(canvas,control,numSteps,xPointPtr)527 TkgeomapBezierScreenPoints(canvas, control, numSteps, xPointPtr)
528 Tk_Canvas canvas; /* Canvas in which curve is to be
529 * drawn. */
530 double control[]; /* Array of coordinates for four
531 * control points: x0, y0, x1, y1,
532 * ... x3 y3. */
533 int numSteps; /* Number of curve points to
534 * generate. */
535 register XPoint *xPointPtr; /* Where to put new points. */
536 {
537 int i;
538 double u, u2, u3, t, t2, t3;
539
540 for (i = 1; i <= numSteps; i++, xPointPtr++) {
541 t = ((double) i)/((double) numSteps);
542 t2 = t*t;
543 t3 = t2*t;
544 u = 1.0 - t;
545 u2 = u*u;
546 u3 = u2*u;
547 Tk_CanvasDrawableCoords(canvas,
548 (control[0]*u3 + 3.0 * (control[2]*t*u2 + control[4]*t2*u)
549 + control[6]*t3),
550 (control[1]*u3 + 3.0 * (control[3]*t*u2 + control[5]*t2*u)
551 + control[7]*t3),
552 &xPointPtr->x, &xPointPtr->y);
553 }
554 }
555
556 /*
557 *--------------------------------------------------------------
558 *
559 * TkgeomapBezierPoints --
560 *
561 * Given four control points, create a larger set of points
562 * for a Bezier spline based on the points.
563 *
564 * Results:
565 * The array at *coordPtr gets filled in with 2*numSteps
566 * coordinates, which correspond to the Bezier spline defined
567 * by the four control points. Note: no output point is
568 * generated for the first input point, but an output point
569 * *is* generated for the last input point.
570 *
571 * Side effects:
572 * None.
573 *
574 *--------------------------------------------------------------
575 */
576
577 void
TkgeomapBezierPoints(control,numSteps,coordPtr)578 TkgeomapBezierPoints(control, numSteps, coordPtr)
579 double control[]; /* Array of coordinates for four
580 * control points: x0, y0, x1, y1,
581 * ... x3 y3. */
582 int numSteps; /* Number of curve points to
583 * generate. */
584 register double *coordPtr; /* Where to put new points. */
585 {
586 int i;
587 double u, u2, u3, t, t2, t3;
588
589 for (i = 1; i <= numSteps; i++, coordPtr += 2) {
590 t = ((double) i)/((double) numSteps);
591 t2 = t*t;
592 t3 = t2*t;
593 u = 1.0 - t;
594 u2 = u*u;
595 u3 = u2*u;
596 coordPtr[0] = control[0]*u3
597 + 3.0 * (control[2]*t*u2 + control[4]*t2*u) + control[6]*t3;
598 coordPtr[1] = control[1]*u3
599 + 3.0 * (control[3]*t*u2 + control[5]*t2*u) + control[7]*t3;
600 }
601 }
602
603 /*
604 *--------------------------------------------------------------
605 *
606 * TkgeomapMakeBezierCurve --
607 *
608 * Given a set of points, create a new set of points that fit
609 * parabolic splines to the line segments connecting the original
610 * points. Produces output points in either of two forms.
611 *
612 * Note: in spite of this procedure's name, it does *not* generate
613 * Bezier curves. Since only three control points are used for
614 * each curve segment, not four, the curves are actually just
615 * parabolic.
616 *
617 * Results:
618 * Either or both of the xPoints or dblPoints arrays are filled
619 * in. The return value is the number of points placed in the
620 * arrays. Note: if the first and last points are the same, then
621 * a closed curve is generated.
622 *
623 * Side effects:
624 * None.
625 *
626 *--------------------------------------------------------------
627 */
628
629 int
TkgeomapMakeBezierCurve(canvas,pointPtr,numPoints,numSteps,xPoints,dblPoints)630 TkgeomapMakeBezierCurve(canvas, pointPtr, numPoints, numSteps, xPoints,
631 dblPoints)
632 Tk_Canvas canvas; /* Canvas in which curve is to be
633 * drawn. */
634 double *pointPtr; /* Array of input coordinates: x0,
635 * y0, x1, y1, etc.. */
636 int numPoints; /* Number of points at pointPtr. */
637 int numSteps; /* Number of steps to use for each
638 * spline segments (determines
639 * smoothness of curve). */
640 XPoint xPoints[]; /* Array of XPoints to fill in (e.g.
641 * for display. NULL means don't
642 * fill in any XPoints. */
643 double dblPoints[]; /* Array of points to fill in as
644 * doubles, in the form x0, y0,
645 * x1, y1, .... NULL means don't
646 * fill in anything in this form.
647 * Caller must make sure that this
648 * array has enough space. */
649 {
650 int closed, outputPoints, i;
651 int numCoords = numPoints*2;
652 double control[8];
653
654 /*
655 * If the curve is a closed one then generate a special spline
656 * that spans the last points and the first ones. Otherwise
657 * just put the first point into the output.
658 */
659
660 if (!pointPtr) {
661 /* Of pointPtr == NULL, this function returns an upper limit.
662 * of the array size to store the coordinates. This can be
663 * used to allocate storage, before the actual coordinates
664 * are calculated. */
665 return 1 + numPoints * numSteps;
666 }
667
668 outputPoints = 0;
669 if ((pointPtr[0] == pointPtr[numCoords-2])
670 && (pointPtr[1] == pointPtr[numCoords-1])) {
671 closed = 1;
672 control[0] = 0.5*pointPtr[numCoords-4] + 0.5*pointPtr[0];
673 control[1] = 0.5*pointPtr[numCoords-3] + 0.5*pointPtr[1];
674 control[2] = 0.167*pointPtr[numCoords-4] + 0.833*pointPtr[0];
675 control[3] = 0.167*pointPtr[numCoords-3] + 0.833*pointPtr[1];
676 control[4] = 0.833*pointPtr[0] + 0.167*pointPtr[2];
677 control[5] = 0.833*pointPtr[1] + 0.167*pointPtr[3];
678 control[6] = 0.5*pointPtr[0] + 0.5*pointPtr[2];
679 control[7] = 0.5*pointPtr[1] + 0.5*pointPtr[3];
680 if (xPoints != NULL) {
681 Tk_CanvasDrawableCoords(canvas, control[0], control[1],
682 &xPoints->x, &xPoints->y);
683 TkgeomapBezierScreenPoints(canvas, control, numSteps, xPoints+1);
684 xPoints += numSteps+1;
685 }
686 if (dblPoints != NULL) {
687 dblPoints[0] = control[0];
688 dblPoints[1] = control[1];
689 TkgeomapBezierPoints(control, numSteps, dblPoints+2);
690 dblPoints += 2*(numSteps+1);
691 }
692 outputPoints += numSteps+1;
693 } else {
694 closed = 0;
695 if (xPoints != NULL) {
696 Tk_CanvasDrawableCoords(canvas, pointPtr[0], pointPtr[1],
697 &xPoints->x, &xPoints->y);
698 xPoints += 1;
699 }
700 if (dblPoints != NULL) {
701 dblPoints[0] = pointPtr[0];
702 dblPoints[1] = pointPtr[1];
703 dblPoints += 2;
704 }
705 outputPoints += 1;
706 }
707
708 for (i = 2; i < numPoints; i++, pointPtr += 2) {
709 /*
710 * Set up the first two control points. This is done
711 * differently for the first spline of an open curve
712 * than for other cases.
713 */
714
715 if ((i == 2) && !closed) {
716 control[0] = pointPtr[0];
717 control[1] = pointPtr[1];
718 control[2] = 0.333*pointPtr[0] + 0.667*pointPtr[2];
719 control[3] = 0.333*pointPtr[1] + 0.667*pointPtr[3];
720 } else {
721 control[0] = 0.5*pointPtr[0] + 0.5*pointPtr[2];
722 control[1] = 0.5*pointPtr[1] + 0.5*pointPtr[3];
723 control[2] = 0.167*pointPtr[0] + 0.833*pointPtr[2];
724 control[3] = 0.167*pointPtr[1] + 0.833*pointPtr[3];
725 }
726
727 /*
728 * Set up the last two control points. This is done
729 * differently for the last spline of an open curve
730 * than for other cases.
731 */
732
733 if ((i == (numPoints-1)) && !closed) {
734 control[4] = .667*pointPtr[2] + .333*pointPtr[4];
735 control[5] = .667*pointPtr[3] + .333*pointPtr[5];
736 control[6] = pointPtr[4];
737 control[7] = pointPtr[5];
738 } else {
739 control[4] = .833*pointPtr[2] + .167*pointPtr[4];
740 control[5] = .833*pointPtr[3] + .167*pointPtr[5];
741 control[6] = 0.5*pointPtr[2] + 0.5*pointPtr[4];
742 control[7] = 0.5*pointPtr[3] + 0.5*pointPtr[5];
743 }
744
745 /*
746 * If the first two points coincide, or if the last
747 * two points coincide, then generate a single
748 * straight-line segment by outputting the last control
749 * point.
750 */
751
752 if (((pointPtr[0] == pointPtr[2]) && (pointPtr[1] == pointPtr[3]))
753 || ((pointPtr[2] == pointPtr[4])
754 && (pointPtr[3] == pointPtr[5]))) {
755 if (xPoints != NULL) {
756 Tk_CanvasDrawableCoords(canvas, control[6], control[7],
757 &xPoints[0].x, &xPoints[0].y);
758 xPoints++;
759 }
760 if (dblPoints != NULL) {
761 dblPoints[0] = control[6];
762 dblPoints[1] = control[7];
763 dblPoints += 2;
764 }
765 outputPoints += 1;
766 continue;
767 }
768
769 /*
770 * Generate a Bezier spline using the control points.
771 */
772
773
774 if (xPoints != NULL) {
775 TkgeomapBezierScreenPoints(canvas, control, numSteps, xPoints);
776 xPoints += numSteps;
777 }
778 if (dblPoints != NULL) {
779 TkgeomapBezierPoints(control, numSteps, dblPoints);
780 dblPoints += 2*numSteps;
781 }
782 outputPoints += numSteps;
783 }
784 return outputPoints;
785 }
786
787 /*
788 *--------------------------------------------------------------
789 *
790 * TkgeomapMakeBezierPostscript --
791 *
792 * This procedure generates Postscript commands that create
793 * a path corresponding to a given Bezier curve.
794 *
795 * Results:
796 * None. Postscript commands to generate the path are appended
797 * to the interp's result.
798 *
799 * Side effects:
800 * None.
801 *
802 *--------------------------------------------------------------
803 */
804
805 void
TkgeomapMakeBezierPostscript(interp,canvas,pointPtr,numPoints)806 TkgeomapMakeBezierPostscript(interp, canvas, pointPtr, numPoints)
807 Tcl_Interp *interp; /* Interpreter in whose result the
808 * Postscript is to be stored. */
809 Tk_Canvas canvas; /* Canvas widget for which the
810 * Postscript is being generated. */
811 double *pointPtr; /* Array of input coordinates: x0,
812 * y0, x1, y1, etc.. */
813 int numPoints; /* Number of points at pointPtr. */
814 {
815 int closed, i;
816 int numCoords = numPoints*2;
817 double control[8];
818 char buffer[200];
819
820 /*
821 * If the curve is a closed one then generate a special spline
822 * that spans the last points and the first ones. Otherwise
823 * just put the first point into the path.
824 */
825
826 if ((pointPtr[0] == pointPtr[numCoords-2])
827 && (pointPtr[1] == pointPtr[numCoords-1])) {
828 closed = 1;
829 control[0] = 0.5*pointPtr[numCoords-4] + 0.5*pointPtr[0];
830 control[1] = 0.5*pointPtr[numCoords-3] + 0.5*pointPtr[1];
831 control[2] = 0.167*pointPtr[numCoords-4] + 0.833*pointPtr[0];
832 control[3] = 0.167*pointPtr[numCoords-3] + 0.833*pointPtr[1];
833 control[4] = 0.833*pointPtr[0] + 0.167*pointPtr[2];
834 control[5] = 0.833*pointPtr[1] + 0.167*pointPtr[3];
835 control[6] = 0.5*pointPtr[0] + 0.5*pointPtr[2];
836 control[7] = 0.5*pointPtr[1] + 0.5*pointPtr[3];
837 sprintf(buffer, "%.15g %.15g moveto\n%.15g %.15g %.15g %.15g %.15g %.15g curveto\n",
838 control[0], Tk_CanvasPsY(canvas, control[1]),
839 control[2], Tk_CanvasPsY(canvas, control[3]),
840 control[4], Tk_CanvasPsY(canvas, control[5]),
841 control[6], Tk_CanvasPsY(canvas, control[7]));
842 } else {
843 closed = 0;
844 control[6] = pointPtr[0];
845 control[7] = pointPtr[1];
846 sprintf(buffer, "%.15g %.15g moveto\n",
847 control[6], Tk_CanvasPsY(canvas, control[7]));
848 }
849 Tcl_AppendResult(interp, buffer, (char *) NULL);
850
851 /*
852 * Cycle through all the remaining points in the curve, generating
853 * a curve section for each vertex in the linear path.
854 */
855
856 for (i = numPoints-2, pointPtr += 2; i > 0; i--, pointPtr += 2) {
857 control[2] = 0.333*control[6] + 0.667*pointPtr[0];
858 control[3] = 0.333*control[7] + 0.667*pointPtr[1];
859
860 /*
861 * Set up the last two control points. This is done
862 * differently for the last spline of an open curve
863 * than for other cases.
864 */
865
866 if ((i == 1) && !closed) {
867 control[6] = pointPtr[2];
868 control[7] = pointPtr[3];
869 } else {
870 control[6] = 0.5*pointPtr[0] + 0.5*pointPtr[2];
871 control[7] = 0.5*pointPtr[1] + 0.5*pointPtr[3];
872 }
873 control[4] = 0.333*control[6] + 0.667*pointPtr[0];
874 control[5] = 0.333*control[7] + 0.667*pointPtr[1];
875
876 sprintf(buffer, "%.15g %.15g %.15g %.15g %.15g %.15g curveto\n",
877 control[2], Tk_CanvasPsY(canvas, control[3]),
878 control[4], Tk_CanvasPsY(canvas, control[5]),
879 control[6], Tk_CanvasPsY(canvas, control[7]));
880 Tcl_AppendResult(interp, buffer, (char *) NULL);
881 }
882 }
883