]>
Commit | Line | Data |
---|---|---|
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | } |