1 /* This file is part of the GNU plotutils package. Copyright (C) 1995,
2 1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
3
4 The GNU plotutils package is free software. You may redistribute it
5 and/or modify it under the terms of the GNU General Public License as
6 published by the Free Software foundation; either version 2, or (at your
7 option) any later version.
8
9 The GNU plotutils package is distributed in the hope that it will be
10 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with the GNU plotutils package; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
17 Boston, MA 02110-1301, USA. */
18
19 /* This file contains all of libplot's low-level code for constructing and
20 manipulating paths, both simple and compound. It includes functions
21 that construct polygonal and Bezier approximations to a given path.
22 They are used by Plotters whose output format does not support all of
23 libplot's graphics primitives. */
24
25 /* E.g., _fakearc() draws polygonal approximations to circular and elliptic
26 quarter-arcs. Each polygonal approximation will contain
27 2**NUM_ARC_SUBDIVISIONS line segments.
28
29 Similarly, polygonal approximations to quadratic and cubic Beziers will
30 contain no more than 2**MAX_NUM_BEZIER2_SUBDIVISIONS and
31 2**MAX_NUM_BEZIER3_SUBDIVISIONS line segments. However, each bisection
32 algorithm used for drawing a Bezier normally usually its recursion based
33 on a relative flatness criterion (see below). */
34
35 #include "sys-defines.h"
36 #include "extern.h"
37 #include "g_arc.h" /* for chord table */
38
39 /* Number of times a circular arc or quarter-ellipse will be recursively
40 subdivided. Two raised to this power is the number of line segments
41 that the polygonalization will contain. */
42
43 /* NOTE: the maximum allowed value for NUM_ARC_SUBDIVISIONS is
44 TABULATED_ARC_SUBDIVISIONS (i.e., the size of the chordal deviation
45 table for a quarter-circle or quarter-ellipse, defined in g_arc.h). */
46 #define NUM_ARC_SUBDIVISIONS 5
47
48 /* Maximum number of times quadratic and cubic Beziers will be recursively
49 subdivided. For Beziers we use an adaptive algorithm, in which
50 bisection stops when a specified relative flatness has been reached.
51 But these parameters provide a hard cutoff, which overrides the relative
52 flatness end condition. */
53 #define MAX_NUM_BEZIER2_SUBDIVISIONS 6
54 #define MAX_NUM_BEZIER3_SUBDIVISIONS 7
55
56 /* The relative flatness parameters. */
57 #define REL_QUAD_FLATNESS 5e-4
58 #define REL_CUBIC_FLATNESS 5e-4
59
60 #define DATAPOINTS_BUFSIZ PL_MAX_UNFILLED_PATH_LENGTH
61 #define DIST(p0,p1) (sqrt( ((p0).x - (p1).x)*((p0).x - (p1).x) \
62 + ((p0).y - (p1).y)*((p0).y - (p1).y)))
63
64 #define MIDWAY(x0, x1) (0.5 * ((x0) + (x1)))
65
66 /* forward references */
67 static void _prepare_chord_table (double sagitta, double custom_chord_table[TABULATED_ARC_SUBDIVISIONS]);
68 static void _fakearc (plPath *path, plPoint p0, plPoint p1, int arc_type, const double *custom_chord_table, const double m[4]);
69
70 /* ctor for plPath class; constructs an empty plPath, with type set to
71 PATH_SEGMENT_LIST (default type) */
72 plPath *
_new_plPath(void)73 _new_plPath (void)
74 {
75 plPath *path;
76
77 path = (plPath *)_pl_xmalloc (sizeof (plPath));
78
79 path->type = PATH_SEGMENT_LIST;
80 path->segments = (plPathSegment *)NULL;
81 path->segments_len = 0; /* number of slots allocated */
82 path->num_segments = 0; /* number of slots occupied */
83
84 path->primitive = false;
85 path->llx = DBL_MAX;
86 path->lly = DBL_MAX;
87 path->urx = -(DBL_MAX);
88 path->ury = -(DBL_MAX);
89
90 return path;
91 }
92
93 /* dtor for plPath class */
94 void
_delete_plPath(plPath * path)95 _delete_plPath (plPath *path)
96 {
97 if (path == (plPath *)NULL)
98 return;
99
100 if (path->type == PATH_SEGMENT_LIST
101 && path->segments_len > 0) /* number of slots allocated */
102 free (path->segments);
103 free (path);
104 }
105
106 /* reset function for plPath class */
107 void
_reset_plPath(plPath * path)108 _reset_plPath (plPath *path)
109 {
110 if (path == (plPath *)NULL)
111 return;
112
113 if (path->type == PATH_SEGMENT_LIST
114 && path->segments_len > 0) /* number of slots allocated */
115 free (path->segments);
116 path->segments = (plPathSegment *)NULL;
117 path->segments_len = 0;
118 path->type = PATH_SEGMENT_LIST; /* restore to default */
119 path->num_segments = 0;
120
121 path->primitive = false;
122 path->llx = DBL_MAX;
123 path->lly = DBL_MAX;
124 path->urx = -(DBL_MAX);
125 path->ury = -(DBL_MAX);
126 }
127
128 void
_add_moveto(plPath * path,plPoint p)129 _add_moveto (plPath *path, plPoint p)
130 {
131 if (path == (plPath *)NULL)
132 return;
133
134 if (path->type != PATH_SEGMENT_LIST || path->num_segments > 0)
135 return;
136
137 /* empty, so allocate a segment buffer */
138 path->segments = (plPathSegment *)
139 _pl_xmalloc (DATAPOINTS_BUFSIZ * sizeof(plPathSegment));
140 path->segments_len = DATAPOINTS_BUFSIZ;
141
142 path->segments[0].type = S_MOVETO;
143 path->segments[0].p = p;
144 path->num_segments = 1;
145
146 path->llx = p.x;
147 path->lly = p.y;
148 path->urx = p.x;
149 path->ury = p.y;
150 }
151
152 void
_add_line(plPath * path,plPoint p)153 _add_line (plPath *path, plPoint p)
154 {
155 if (path == (plPath *)NULL)
156 return;
157
158 if (path->type != PATH_SEGMENT_LIST || path->num_segments == 0)
159 return;
160
161 if (path->num_segments == 0)
162 /* empty, so allocate a segment buffer */
163 {
164 path->segments = (plPathSegment *)
165 _pl_xmalloc (DATAPOINTS_BUFSIZ * sizeof(plPathSegment));
166 path->segments_len = DATAPOINTS_BUFSIZ;
167 }
168
169 if (path->num_segments == path->segments_len)
170 /* full, so reallocate */
171 {
172 path->segments = (plPathSegment *)
173 _pl_xrealloc (path->segments,
174 2 * path->segments_len * sizeof(plPathSegment));
175 path->segments_len *= 2;
176 }
177
178 path->segments[path->num_segments].type = S_LINE;
179 path->segments[path->num_segments].p = p;
180 path->num_segments++;
181
182 path->llx = DMIN(path->llx, p.x);
183 path->lly = DMIN(path->lly, p.y);
184 path->urx = DMAX(path->urx, p.x);
185 path->ury = DMAX(path->ury, p.y);
186 }
187
188 void
_add_closepath(plPath * path)189 _add_closepath (plPath *path)
190 {
191 if (path == (plPath *)NULL)
192 return;
193
194 if (path->type != PATH_SEGMENT_LIST || path->num_segments == 0)
195 return;
196
197 if (path->num_segments == 0) /* meaningless */
198 return;
199
200 if (path->num_segments == path->segments_len)
201 /* full, so reallocate */
202 {
203 path->segments = (plPathSegment *)
204 _pl_xrealloc (path->segments,
205 2 * path->segments_len * sizeof(plPathSegment));
206 path->segments_len *= 2;
207 }
208
209 path->segments[path->num_segments].type = S_CLOSEPATH;
210 path->segments[path->num_segments].p = path->segments[0].p;
211 path->num_segments++;
212 }
213
214 void
_add_bezier2(plPath * path,plPoint pc,plPoint p)215 _add_bezier2 (plPath *path, plPoint pc, plPoint p)
216 {
217 if (path == (plPath *)NULL)
218 return;
219
220 if (path->type != PATH_SEGMENT_LIST || path->num_segments == 0)
221 return;
222
223 if (path->num_segments == 0)
224 /* empty, so allocate a segment buffer */
225 {
226 path->segments = (plPathSegment *)
227 _pl_xmalloc (DATAPOINTS_BUFSIZ * sizeof(plPathSegment));
228 path->segments_len = DATAPOINTS_BUFSIZ;
229 }
230
231 if (path->num_segments == path->segments_len)
232 /* full, so reallocate */
233 {
234 path->segments = (plPathSegment *)
235 _pl_xrealloc (path->segments,
236 2 * path->segments_len * sizeof(plPathSegment));
237 path->segments_len *= 2;
238 }
239
240 path->segments[path->num_segments].type = S_QUAD;
241 path->segments[path->num_segments].p = p;
242 path->segments[path->num_segments].pc = pc;
243 path->num_segments++;
244 }
245
246 void
_add_bezier3(plPath * path,plPoint pc,plPoint pd,plPoint p)247 _add_bezier3 (plPath *path, plPoint pc, plPoint pd, plPoint p)
248 {
249 if (path == (plPath *)NULL)
250 return;
251
252 if (path->type != PATH_SEGMENT_LIST || path->num_segments == 0)
253 return;
254
255 if (path->num_segments == 0)
256 /* empty, so allocate a segment buffer */
257 {
258 path->segments = (plPathSegment *)
259 _pl_xmalloc (DATAPOINTS_BUFSIZ * sizeof(plPathSegment));
260 path->segments_len = DATAPOINTS_BUFSIZ;
261 }
262
263 if (path->num_segments == path->segments_len)
264 /* full, so reallocate */
265 {
266 path->segments = (plPathSegment *)
267 _pl_xrealloc (path->segments,
268 2 * path->segments_len * sizeof(plPathSegment));
269 path->segments_len *= 2;
270 }
271
272 path->segments[path->num_segments].type = S_CUBIC;
273 path->segments[path->num_segments].p = p;
274 path->segments[path->num_segments].pc = pc;
275 path->segments[path->num_segments].pd = pd;
276 path->num_segments++;
277 }
278
279 void
_add_arc(plPath * path,plPoint pc,plPoint p1)280 _add_arc (plPath *path, plPoint pc, plPoint p1)
281 {
282 if (path == (plPath *)NULL)
283 return;
284
285 if (path->type != PATH_SEGMENT_LIST || path->num_segments == 0)
286 return;
287
288 if (path->num_segments == 0)
289 /* empty, so allocate a segment buffer */
290 {
291 path->segments = (plPathSegment *)
292 _pl_xmalloc (DATAPOINTS_BUFSIZ * sizeof(plPathSegment));
293 path->segments_len = DATAPOINTS_BUFSIZ;
294 }
295
296 if (path->num_segments == path->segments_len)
297 /* full, so reallocate */
298 {
299 path->segments = (plPathSegment *)
300 _pl_xrealloc (path->segments,
301 2 * path->segments_len * sizeof(plPathSegment));
302 path->segments_len *= 2;
303 }
304
305 path->segments[path->num_segments].type = S_ARC;
306 path->segments[path->num_segments].p = p1;
307 path->segments[path->num_segments].pc = pc;
308 path->num_segments++;
309 }
310
311 void
_add_ellarc(plPath * path,plPoint pc,plPoint p1)312 _add_ellarc (plPath *path, plPoint pc, plPoint p1)
313 {
314 if (path == (plPath *)NULL)
315 return;
316
317 if (path->type != PATH_SEGMENT_LIST || path->num_segments == 0)
318 return;
319
320 if (path->num_segments == 0)
321 /* empty, so allocate a segment buffer */
322 {
323 path->segments = (plPathSegment *)
324 _pl_xmalloc (DATAPOINTS_BUFSIZ * sizeof(plPathSegment));
325 path->segments_len = DATAPOINTS_BUFSIZ;
326 }
327
328 if (path->num_segments == path->segments_len)
329 /* full, so reallocate */
330 {
331 path->segments = (plPathSegment *)
332 _pl_xrealloc (path->segments,
333 2 * path->segments_len * sizeof(plPathSegment));
334 path->segments_len *= 2;
335 }
336
337 path->segments[path->num_segments].type = S_ELLARC;
338 path->segments[path->num_segments].p = p1;
339 path->segments[path->num_segments].pc = pc;
340 path->num_segments++;
341 }
342
343 void
_add_box(plPath * path,plPoint p0,plPoint p1,bool clockwise)344 _add_box (plPath *path, plPoint p0, plPoint p1, bool clockwise)
345 {
346 if (path == (plPath *)NULL)
347 return;
348
349 if (path->type != PATH_SEGMENT_LIST || path->num_segments > 0)
350 return;
351
352 path->type = PATH_BOX;
353 path->p0 = p0;
354 path->p1 = p1;
355 path->clockwise = clockwise;
356
357 path->llx = DMIN(path->llx, p0.x);
358 path->lly = DMIN(path->lly, p0.y);
359 path->urx = DMAX(path->urx, p0.x);
360 path->ury = DMAX(path->ury, p0.y);
361
362 path->llx = DMIN(path->llx, p1.x);
363 path->lly = DMIN(path->lly, p1.y);
364 path->urx = DMAX(path->urx, p1.x);
365 path->ury = DMAX(path->ury, p1.y);
366 }
367
368 void
_add_circle(plPath * path,plPoint pc,double radius,bool clockwise)369 _add_circle (plPath *path, plPoint pc, double radius, bool clockwise)
370 {
371 if (path == (plPath *)NULL)
372 return;
373
374 if (path->type != PATH_SEGMENT_LIST || path->num_segments > 0)
375 return;
376
377 path->type = PATH_CIRCLE;
378 path->pc = pc;
379 path->radius = radius;
380 path->clockwise = clockwise;
381 }
382
383 void
_add_ellipse(plPath * path,plPoint pc,double rx,double ry,double angle,bool clockwise)384 _add_ellipse (plPath *path, plPoint pc, double rx, double ry, double angle, bool clockwise)
385 {
386 if (path == (plPath *)NULL)
387 return;
388
389 if (path->type != PATH_SEGMENT_LIST || path->num_segments > 0)
390 return;
391
392 path->type = PATH_ELLIPSE;
393 path->pc = pc;
394 path->rx = rx;
395 path->ry = ry;
396 path->angle = angle;
397 path->clockwise = clockwise;
398 }
399
400 /* Draw a polygonal approximation to the circular arc from p0 to p1, with
401 center pc, by calling _fakearc(), which in turn repeatedly calls
402 _add_line(). It is assumed that p0 and p1 are distinct. It is assumed
403 that pc is on the perpendicular bisector of the line segment joining
404 them, and that the graphics cursor is initially located at p0. */
405 void
_add_arc_as_lines(plPath * path,plPoint pc,plPoint p1)406 _add_arc_as_lines (plPath *path, plPoint pc, plPoint p1)
407 {
408 /* starting point */
409 plPoint p0;
410 /* bisection point of arc, and midpoint of chord */
411 plPoint pb, pm;
412 /* rotation matrix */
413 double m[4];
414 /* other variables */
415 plVector v, v0, v1;
416 double radius, sagitta;
417 double cross, orientation;
418 /* handcrafted relative chordal deviation table, for this arc */
419 double custom_chord_table[TABULATED_ARC_SUBDIVISIONS];
420
421 if (path == (plPath *)NULL)
422 return;
423
424 if (path->type != PATH_SEGMENT_LIST || path->num_segments == 0)
425 return;
426
427 /* determine starting point */
428 p0 = path->segments[path->num_segments - 1].p;
429
430 if (p0.x == p1.x && p0.y == p1.y)
431 /* zero-length arc, draw as zero-length line segment */
432 _add_line (path, p0);
433
434 else
435 /* genuine polygonal approximation */
436 {
437 /* vectors from pc to p0, and pc to p1 */
438 v0.x = p0.x - pc.x;
439 v0.y = p0.y - pc.y;
440 v1.x = p1.x - pc.x;
441 v1.y = p1.y - pc.y;
442
443 /* cross product, zero if points are collinear */
444 cross = v0.x * v1.y - v1.x * v0.y;
445
446 /* Compute orientation. Note libplot convention: if p0, p1, pc are
447 collinear then arc goes counterclockwise from p0 to p1. */
448 orientation = (cross >= 0.0 ? 1.0 : -1.0);
449
450 radius = DIST(pc, p0); /* radius is distance to p0 or p1 */
451
452 pm.x = 0.5 * (p0.x + p1.x); /* midpoint of chord from p0 to p1 */
453 pm.y = 0.5 * (p0.y + p1.y);
454
455 v.x = p1.x - p0.x; /* chord vector from p0 to p1 */
456 v.y = p1.y - p0.y;
457
458 _vscale(&v, radius);
459 pb.x = pc.x + orientation * v.y; /* bisection point of arc */
460 pb.y = pc.y - orientation * v.x;
461
462 sagitta = DIST(pb, pm) / radius;
463
464 /* fill in entries of chordal deviation table for this user-defined
465 sagitta */
466 _prepare_chord_table (sagitta, custom_chord_table);
467
468 /* call _fakearc(), using for `rotation' matrix m[] a clockwise or
469 counterclockwise rotation by 90 degrees, depending on orientation */
470
471 m[0] = 0.0, m[1] = orientation, m[2] = -orientation, m[3] = 0.0;
472 _fakearc (path, p0, p1, USER_DEFINED_ARC, custom_chord_table, m);
473 }
474 }
475
476 /* Draw a polygonal approximation to a quarter-ellipse from p0 to p1, by
477 calling _fakearc(), which in turn repeatedly calls _add_line(). pc is
478 the center of the arc, and p0, p1, pc are assumed not to be collinear.
479 It is assumed that the graphics cursor is located at p0 when this
480 function is called.
481
482 The control triangle for the elliptic arc will have vertices p0, p1, and
483 K = p0 + (p1 - pc) = p1 + (p0 - pc). The arc will pass through p0 and
484 p1, and will be tangent at p0 to the edge from p0 to K, and at p1 to the
485 edge from p1 to K. */
486 void
_add_ellarc_as_lines(plPath * path,plPoint pc,plPoint p1)487 _add_ellarc_as_lines (plPath *path, plPoint pc, plPoint p1)
488 {
489 plPoint p0;
490 plVector v0, v1;
491 double cross;
492 double m[4];
493
494 if (path == (plPath *)NULL)
495 return;
496
497 if (path->type != PATH_SEGMENT_LIST || path->num_segments == 0)
498 return;
499
500 /* determine starting point */
501 p0 = path->segments[path->num_segments - 1].p;
502
503 /* vectors from pc to p0, and pc to p1 */
504 v0.x = p0.x - pc.x;
505 v0.y = p0.y - pc.y;
506 v1.x = p1.x - pc.x;
507 v1.y = p1.y - pc.y;
508
509 /* cross product */
510 cross = v0.x * v1.y - v1.x * v0.y;
511 if (FROUND(cross) == 0.0)
512 /* collinear points, draw line segment from p0 to p1
513 (not quite right, could be bettered) */
514 _add_line (path, p1);
515 else
516 {
517 /* `rotation' matrix (it maps v0 -> -v1 and v1 -> v0) */
518 m[0] = - (v0.x * v0.y + v1.x * v1.y) / cross;
519 m[1] = (v0.x * v0.x + v1.x * v1.x) / cross;
520 m[2] = - (v0.y * v0.y + v1.y * v1.y) / cross;
521 m[3] = (v0.x * v0.y + v1.x * v1.y) / cross;
522
523 /* draw polyline inscribed in the quarter-ellipse */
524 _fakearc (path, p0, p1, QUARTER_ARC, (double *)NULL, m);
525 }
526 }
527
528 /* A function that approximates a circular arc by a cubic Bezier. The
529 approximation used is a standard one. E.g., a quarter circle extending
530 from (1,0) to (0,1), with center (0,0), would be approximated by a cubic
531 Bezier with control points (1,KAPPA) and (KAPPA,1). Here KAPPA =
532 (4/3)[sqrt(2)-1] = 0.552284749825, approximately. The cubic Bezier will
533 touch the quarter-circle along the line x=y.
534
535 For a quarter-circle, the maximum relative error in r as a function of
536 theta is about 2.7e-4. The error in r has the same sign, for all theta. */
537
538 /* According to Berthold K. P. Horn <bkph@ai.mit.edu>, the general formula
539 for KAPPA, for a radius-1 circular arc (not necessary a quarter-circle),
540
541 KAPPA = (4/3)sqrt[(1-cos H)/(1+cos H)]
542 = (4/3)[1-cos H]/[sin H] = (4/3)[sin H]/[1+cosH]
543
544 where H is half the angle subtended by the arc. H=45 degrees for a
545 quarter circle. This is the formula we use. */
546
547 /* Louis Vosloo <support@yandy.com> points out that for a quarter-circle,
548 the value 0.55228... for KAPPA is, for some purposes, sub-optimal. By
549 dropping the requirement that the quarter-circle and the Bezier touch
550 each other along the symmetry line x=y, one can slightly decrease the
551 maximum relative error. He says 0.5541... is the best possible choice.
552 He doesn't have an improved value of KAPPA for a general arc, though. */
553
554 void
_add_arc_as_bezier3(plPath * path,plPoint pc,plPoint p1)555 _add_arc_as_bezier3 (plPath *path, plPoint pc, plPoint p1)
556 {
557 plPoint p0;
558 plVector v0, v1;
559
560 if (path == (plPath *)NULL)
561 return;
562
563 if (path->type != PATH_SEGMENT_LIST || path->num_segments == 0)
564 return;
565
566 /* determine starting point */
567 p0 = path->segments[path->num_segments - 1].p;
568
569 /* vectors to starting, ending points */
570 v0.x = p0.x - pc.x;
571 v0.y = p0.y - pc.y;
572 v1.x = p1.x - pc.x;
573 v1.y = p1.y - pc.y;
574
575 if ((v0.x == 0.0 && v0.y == 0.0) || (v1.x == 0.0 && v1.y == 0.0)
576 || (v0.x == v1.x && v0.y == v1.y))
577 /* degenerate case */
578 _add_line (path, p1);
579 else
580 /* normal case */
581 {
582 double oldangle, newangle, anglerange;
583 double cross;
584 int orientation;
585
586 /* cross product, zero if points are collinear */
587 cross = v0.x * v1.y - v1.x * v0.y;
588
589 /* Compute orientation. Note libplot convention: if p0, p1, pc
590 are collinear then arc goes counterclockwise from p0 to p1. */
591 orientation = (cross >= 0.0 ? 1 : -1);
592
593 /* compute signed subtended angle */
594 oldangle = _xatan2 (v0.y, v0.x);
595 newangle = _xatan2 (v1.y, v1.x);
596 anglerange = newangle - oldangle;
597 if (anglerange > M_PI)
598 anglerange -= (2 * M_PI);
599 if (anglerange <= -(M_PI))
600 anglerange += (2 * M_PI);
601
602 if (FABS(anglerange) > 0.51 * M_PI)
603 /* subtended angle > 0.51 * pi, so split arc in two and recurse,
604 since Bezier approximation isn't very good for angles much
605 greater than 90 degrees */
606 {
607 double radius;
608 plPoint pb;
609 plVector v;
610
611 radius = DIST(pc, p0); /* radius is distance to p0 or p1 */
612
613 v.x = p1.x - p0.x; /* chord vector from p0 to p1 */
614 v.y = p1.y - p0.y;
615
616 _vscale(&v, radius);
617 pb.x = pc.x + orientation * v.y; /* bisection point of arc */
618 pb.y = pc.y - orientation * v.x;
619
620 _add_arc_as_bezier3 (path, pc, pb);
621 _add_arc_as_bezier3 (path, pc, p1);
622 }
623 else
624 /* subtended angle <= 0.51 * pi, so a single Bezier suffices */
625 {
626 double halfangle, sinhalf, coshalf, kappa;
627 plPoint pc_bezier3, pd_bezier3;
628
629 halfangle = 0.5 * FABS(anglerange);
630 sinhalf = sin (halfangle);
631 coshalf = cos (halfangle);
632 /* compute kappa using either of two formulae, depending on
633 numerical stability */
634 if (FABS(sinhalf) < 0.5)
635 kappa = (4.0/3.0) * sinhalf / (1.0 + coshalf);
636 else
637 kappa = (4.0/3.0) * (1.0 - coshalf) / sinhalf;
638
639 pc_bezier3.x = p0.x - kappa * orientation * v0.y;
640 pc_bezier3.y = p0.y + kappa * orientation * v0.x;
641 pd_bezier3.x = p1.x + kappa * orientation * v1.y;
642 pd_bezier3.y = p1.y - kappa * orientation * v1.x;
643 _add_bezier3 (path, pc_bezier3, pd_bezier3, p1);
644 }
645 }
646 }
647
648 #define KAPPA_FOR_QUARTER_CIRCLE 0.552284749825
649
650 void
_add_ellarc_as_bezier3(plPath * path,plPoint pc,plPoint p1)651 _add_ellarc_as_bezier3 (plPath *path, plPoint pc, plPoint p1)
652 {
653 plPoint p0, pc_bezier3, pd_bezier3;
654 plVector v0, v1;
655
656 if (path == (plPath *)NULL)
657 return;
658
659 if (path->type != PATH_SEGMENT_LIST || path->num_segments == 0)
660 return;
661
662 /* determine starting point */
663 p0 = path->segments[path->num_segments - 1].p;
664
665 /* vectors to starting, ending points */
666 v0.x = p0.x - pc.x;
667 v0.y = p0.y - pc.y;
668 v1.x = p1.x - pc.x;
669 v1.y = p1.y - pc.y;
670
671 /* replace by cubic Bezier, with computed control points */
672 pc_bezier3.x = p0.x + KAPPA_FOR_QUARTER_CIRCLE * v1.x;
673 pc_bezier3.y = p0.y + KAPPA_FOR_QUARTER_CIRCLE * v1.y;
674 pd_bezier3.x = p1.x + KAPPA_FOR_QUARTER_CIRCLE * v0.x;
675 pd_bezier3.y = p1.y + KAPPA_FOR_QUARTER_CIRCLE * v0.y;
676 _add_bezier3 (path, pc_bezier3, pd_bezier3, p1);
677 }
678
679 /* Approximate a quadratic Bezier by a polyline: standard deCasteljau
680 bisection algorithm. However, we stop subdividing when an appropriate
681 metric of the quadratic Bezier to be drawn is sufficiently small. If
682 (p0,p1,p2) defines the quadratic Bezier, we require that the length of
683 p0-2*p1+p2 be less than REL_QUAD_FLATNESS times the distance between the
684 endpoints of the original Bezier. */
685
686 void
_add_bezier2_as_lines(plPath * path,plPoint pc,plPoint p)687 _add_bezier2_as_lines (plPath *path, plPoint pc, plPoint p)
688 {
689 plPoint r0[MAX_NUM_BEZIER2_SUBDIVISIONS + 1], r1[MAX_NUM_BEZIER2_SUBDIVISIONS + 1], r2[MAX_NUM_BEZIER2_SUBDIVISIONS + 1];
690 int level[MAX_NUM_BEZIER2_SUBDIVISIONS + 1];
691 int n = 0; /* index of top of stack, < MAX_NUM_BEZIER2_SUBDIVISIONS */
692 int segments_drawn = 0;
693 plPoint p0;
694 double sqdist, max_squared_length;
695
696 if (path == (plPath *)NULL)
697 return;
698
699 if (path->type != PATH_SEGMENT_LIST || path->num_segments == 0)
700 return;
701
702 /* determine starting point */
703 p0 = path->segments[path->num_segments - 1].p;
704
705 /* squared distance between p0 and p */
706 sqdist = (p.x - p0.x) * (p.x - p0.x) + (p.y - p0.y) * (p.y - p0.y);
707 max_squared_length = REL_QUAD_FLATNESS * REL_QUAD_FLATNESS * sqdist;
708
709 r0[0] = p0;
710 r1[0] = pc;
711 r2[0] = p;
712 level[0] = 0;
713 while (n >= 0) /* i.e. while stack is nonempty */
714 {
715 int current_level;
716 plPoint q0, q1, q2;
717
718 current_level = level[n];
719 q0 = r0[n];
720 q1 = r1[n];
721 q2 = r2[n];
722
723 if (current_level >= MAX_NUM_BEZIER2_SUBDIVISIONS)
724 /* to avoid stack overflow, draw as line segment */
725 {
726 _add_line (path, q2);
727 segments_drawn++;
728 n--;
729 }
730 else
731 /* maybe bisect the Bezier */
732 {
733 plPoint qq0, qq1;
734 plPoint qqq0;
735 plVector vec1;
736
737 vec1.x = q0.x - 2 * q1.x + q2.x;
738 vec1.y = q0.y - 2 * q1.y + q2.y;
739
740 if (vec1.x * vec1.x + vec1.y * vec1.y < max_squared_length)
741 /* very flat Bezier, so draw as line segment */
742 {
743 _add_line (path, q2);
744 segments_drawn++;
745 n--;
746 }
747 else
748 /* split Bezier into pair and recurse */
749 /* level[n] >= n is an invariant */
750 {
751 qq0.x = MIDWAY(q0.x, q1.x);
752 qq0.y = MIDWAY(q0.y, q1.y);
753 qq1.x = MIDWAY(q1.x, q2.x);
754 qq1.y = MIDWAY(q1.y, q2.y);
755
756 qqq0.x = MIDWAY(qq0.x, qq1.x);
757 qqq0.y = MIDWAY(qq0.y, qq1.y);
758
759 /* first half, deal with next */
760 r0[n+1] = q0;
761 r1[n+1] = qq0;
762 r2[n+1] = qqq0;
763 level[n+1] = current_level + 1;
764
765 /* second half, deal with later */
766 r0[n] = qqq0;
767 r1[n] = qq1;
768 r2[n] = q2;
769 level[n] = current_level + 1;
770
771 n++;
772 }
773 }
774 }
775 }
776
777 /* Approximate a cubic Bezier by a polyline: standard deCasteljau bisection
778 algorithm. However, we stop subdividing when an appropriate metric of
779 the cubic Bezier to be drawn is sufficiently small. If (p0,p1,p2,p3)
780 defines the cubic Bezier, we require that the lengths of p0-2*p1+p2 and
781 p1-2*p2+p3 be less than REL_CUBIC_FLATNESS times the distance between
782 the endpoints of the original Bezier. */
783
784 void
_add_bezier3_as_lines(plPath * path,plPoint pc,plPoint pd,plPoint p)785 _add_bezier3_as_lines (plPath *path, plPoint pc, plPoint pd, plPoint p)
786 {
787 plPoint r0[MAX_NUM_BEZIER3_SUBDIVISIONS + 1], r1[MAX_NUM_BEZIER3_SUBDIVISIONS + 1], r2[MAX_NUM_BEZIER3_SUBDIVISIONS + 1], r3[MAX_NUM_BEZIER3_SUBDIVISIONS + 1];
788 int level[MAX_NUM_BEZIER3_SUBDIVISIONS + 1];
789 int n = 0; /* index of top of stack, < MAX_NUM_BEZIER3_SUBDIVISIONS */
790 int segments_drawn = 0;
791 plPoint p0;
792 double sqdist, max_squared_length;
793
794 if (path == (plPath *)NULL)
795 return;
796
797 if (path->type != PATH_SEGMENT_LIST || path->num_segments == 0)
798 return;
799
800 /* determine starting point */
801 p0 = path->segments[path->num_segments - 1].p;
802
803 /* squared distance between p0 and p */
804 sqdist = (p.x - p0.x) * (p.x - p0.x) + (p.y - p0.y) * (p.y - p0.y);
805 max_squared_length = REL_CUBIC_FLATNESS * REL_CUBIC_FLATNESS * sqdist;
806
807 r0[0] = p0;
808 r1[0] = pc;
809 r2[0] = pd;
810 r3[0] = p;
811 level[0] = 0;
812
813 while (n >= 0) /* i.e. while stack is nonempty */
814 {
815 int current_level;
816 plPoint q0, q1, q2, q3;
817
818 current_level = level[n];
819 q0 = r0[n];
820 q1 = r1[n];
821 q2 = r2[n];
822 q3 = r3[n];
823
824 if (current_level >= MAX_NUM_BEZIER3_SUBDIVISIONS)
825 /* draw line segment, to avoid stack overflow */
826 {
827 _add_line (path, q3);
828 segments_drawn++;
829 n--;
830 }
831 else
832 /* maybe bisect the Bezier */
833 {
834 plPoint qq0, qq1, qq2;
835 plPoint qqq0, qqq1;
836 plPoint qqqq0;
837 plVector vec1, vec2;
838
839 vec1.x = q0.x - 2 * q1.x + q2.x;
840 vec1.y = q0.y - 2 * q1.y + q2.y;
841 vec2.x = q1.x - 2 * q2.x + q3.x;
842 vec2.y = q1.y - 2 * q2.y + q3.y;
843
844 if (vec1.x * vec1.x + vec1.y * vec1.y < max_squared_length
845 && vec2.x * vec2.x + vec2.y * vec2.y < max_squared_length)
846 /* very flat Bezier, so draw as line segment */
847 {
848 _add_line (path, q3);
849 segments_drawn++;
850 n--;
851 }
852 else
853 /* split Bezier into pair and recurse */
854 /* level[n] >= n is an invariant */
855 {
856 qq0.x = MIDWAY(q0.x, q1.x);
857 qq0.y = MIDWAY(q0.y, q1.y);
858 qq1.x = MIDWAY(q1.x, q2.x);
859 qq1.y = MIDWAY(q1.y, q2.y);
860 qq2.x = MIDWAY(q2.x, q3.x);
861 qq2.y = MIDWAY(q2.y, q3.y);
862
863 qqq0.x = MIDWAY(qq0.x, qq1.x);
864 qqq0.y = MIDWAY(qq0.y, qq1.y);
865 qqq1.x = MIDWAY(qq1.x, qq2.x);
866 qqq1.y = MIDWAY(qq1.y, qq2.y);
867
868 qqqq0.x = MIDWAY(qqq0.x, qqq1.x);
869 qqqq0.y = MIDWAY(qqq0.y, qqq1.y);
870
871 /* first half, deal with next */
872 level[n+1] = current_level + 1;
873 r0[n+1] = q0;
874 r1[n+1] = qq0;
875 r2[n+1] = qqq0;
876 r3[n+1] = qqqq0;
877
878 /* second half, deal with later */
879 level[n] = current_level + 1;
880 r0[n] = qqqq0;
881 r1[n] = qqq1;
882 r2[n] = qq2;
883 r3[n] = q3;
884
885 n++;
886 }
887 }
888 }
889 }
890
891 void
_add_box_as_lines(plPath * path,plPoint p0,plPoint p1,bool clockwise)892 _add_box_as_lines (plPath *path, plPoint p0, plPoint p1, bool clockwise)
893 {
894 bool x_move_is_first;
895 plPoint newpoint;
896
897 if (path == (plPath *)NULL)
898 return;
899
900 if (path->type != PATH_SEGMENT_LIST || path->num_segments > 0)
901 return;
902
903 _add_moveto (path, p0);
904
905 /* if counterclockwise, would first pen motion be in x direction? */
906 x_move_is_first = ((p1.x >= p0.x && p1.y >= p0.y)
907 || (p1.x < p0.x && p1.y < p0.y) ? true : false);
908
909 if (clockwise)
910 /* take complement */
911 x_move_is_first = (x_move_is_first == true ? false : true);
912
913 if (x_move_is_first)
914 {
915 newpoint.x = p1.x;
916 newpoint.y = p0.y;
917 }
918 else
919 {
920 newpoint.x = p0.x;
921 newpoint.y = p1.y;
922 }
923 _add_line (path, newpoint);
924
925 _add_line (path, p1);
926
927 if (x_move_is_first)
928 {
929 newpoint.x = p0.x;
930 newpoint.y = p1.y;
931 }
932 else
933 {
934 newpoint.x = p1.x;
935 newpoint.y = p0.y;
936 }
937 _add_line (path, newpoint);
938
939 _add_line (path, p0);
940
941 path->primitive = true; /* flag as flattened primitive */
942 }
943
944 void
_add_ellipse_as_bezier3s(plPath * path,plPoint pc,double rx,double ry,double angle,bool clockwise)945 _add_ellipse_as_bezier3s (plPath *path, plPoint pc, double rx, double ry, double angle, bool clockwise)
946 {
947 plPoint startpoint, newpoint;
948 double theta, costheta, sintheta;
949 double xc, yc;
950
951 if (path == (plPath *)NULL)
952 return;
953
954 if (path->type != PATH_SEGMENT_LIST || path->num_segments > 0)
955 return;
956
957 /* draw ellipse by drawing four elliptic arcs */
958 theta = (M_PI / 180.0) * angle; /* convert to radians */
959 costheta = cos (theta);
960 sintheta = sin (theta);
961
962 xc = pc.x;
963 yc = pc.y;
964 startpoint.x = xc + rx * costheta;
965 startpoint.y = yc + rx * sintheta;
966 _add_moveto (path, startpoint);
967
968 if (clockwise)
969 {
970 newpoint.x = xc + ry * sintheta;
971 newpoint.y = yc - ry * costheta;
972 }
973 else
974 {
975 newpoint.x = xc - ry * sintheta;
976 newpoint.y = yc + ry * costheta;
977 }
978 _add_ellarc_as_bezier3 (path, pc, newpoint);
979
980 newpoint.x = xc - rx * costheta;
981 newpoint.y = yc - rx * sintheta;
982 _add_ellarc_as_bezier3 (path, pc, newpoint);
983
984 if (clockwise)
985 {
986 newpoint.x = xc - ry * sintheta;
987 newpoint.y = yc + ry * costheta;
988 }
989 else
990 {
991 newpoint.x = xc + ry * sintheta;
992 newpoint.y = yc - ry * costheta;
993 }
994 _add_ellarc_as_bezier3 (path, pc, newpoint);
995
996 _add_ellarc_as_bezier3 (path, pc, startpoint);
997
998 path->primitive = true; /* flag as flattened primitive */
999 }
1000
1001 void
_add_ellipse_as_ellarcs(plPath * path,plPoint pc,double rx,double ry,double angle,bool clockwise)1002 _add_ellipse_as_ellarcs (plPath *path, plPoint pc, double rx, double ry, double angle, bool clockwise)
1003 {
1004 plPoint startpoint, newpoint;
1005 double theta, costheta, sintheta;
1006 double xc, yc;
1007
1008 if (path == (plPath *)NULL)
1009 return;
1010
1011 if (path->type != PATH_SEGMENT_LIST || path->num_segments > 0)
1012 return;
1013
1014 /* draw ellipse by drawing four elliptic arcs */
1015 theta = (M_PI / 180.0) * angle; /* convert to radians */
1016 costheta = cos (theta);
1017 sintheta = sin (theta);
1018
1019 xc = pc.x;
1020 yc = pc.y;
1021 startpoint.x = xc + rx * costheta;
1022 startpoint.y = yc + rx * sintheta;
1023 _add_moveto (path, startpoint);
1024
1025 if (clockwise)
1026 {
1027 newpoint.x = xc + ry * sintheta;
1028 newpoint.y = yc - ry * costheta;
1029 }
1030 else
1031 {
1032 newpoint.x = xc - ry * sintheta;
1033 newpoint.y = yc + ry * costheta;
1034 }
1035 _add_ellarc (path, pc, newpoint);
1036
1037 newpoint.x = xc - rx * costheta;
1038 newpoint.y = yc - rx * sintheta;
1039 _add_ellarc (path, pc, newpoint);
1040
1041 if (clockwise)
1042 {
1043 newpoint.x = xc - ry * sintheta;
1044 newpoint.y = yc + ry * costheta;
1045 }
1046 else
1047 {
1048 newpoint.x = xc + ry * sintheta;
1049 newpoint.y = yc - ry * costheta;
1050 }
1051 _add_ellarc (path, pc, newpoint);
1052
1053 _add_ellarc (path, pc, startpoint);
1054
1055 path->primitive = true; /* flag as flattened primitive */
1056 }
1057
1058 void
_add_ellipse_as_lines(plPath * path,plPoint pc,double rx,double ry,double angle,bool clockwise)1059 _add_ellipse_as_lines (plPath *path, plPoint pc, double rx, double ry, double angle, bool clockwise)
1060 {
1061 plPoint startpoint, newpoint;
1062 double theta, costheta, sintheta;
1063 double xc, yc;
1064
1065 if (path == (plPath *)NULL)
1066 return;
1067
1068 if (path->type != PATH_SEGMENT_LIST || path->num_segments > 0)
1069 return;
1070
1071 /* draw ellipse by drawing four fake elliptic arcs */
1072 theta = (M_PI / 180.0) * angle; /* convert to radians */
1073 costheta = cos (theta);
1074 sintheta = sin (theta);
1075
1076 xc = pc.x;
1077 yc = pc.y;
1078 startpoint.x = xc + rx * costheta;
1079 startpoint.y = yc + rx * sintheta;
1080 _add_moveto (path, startpoint);
1081
1082 if (clockwise)
1083 {
1084 newpoint.x = xc + ry * sintheta;
1085 newpoint.y = yc - ry * costheta;
1086 }
1087 else
1088 {
1089 newpoint.x = xc - ry * sintheta;
1090 newpoint.y = yc + ry * costheta;
1091 }
1092 _add_ellarc_as_lines (path, pc, newpoint);
1093
1094 newpoint.x = xc - rx * costheta;
1095 newpoint.y = yc - rx * sintheta;
1096 _add_ellarc_as_lines (path, pc, newpoint);
1097
1098 if (clockwise)
1099 {
1100 newpoint.x = xc - ry * sintheta;
1101 newpoint.y = yc + ry * costheta;
1102 }
1103 else
1104 {
1105 newpoint.x = xc + ry * sintheta;
1106 newpoint.y = yc - ry * costheta;
1107 }
1108 _add_ellarc_as_lines (path, pc, newpoint);
1109
1110 _add_ellarc_as_lines (path, pc, startpoint);
1111
1112 path->primitive = true; /* flag as flattened primitive */
1113 }
1114
1115 void
_add_circle_as_bezier3s(plPath * path,plPoint pc,double radius,bool clockwise)1116 _add_circle_as_bezier3s (plPath *path, plPoint pc, double radius, bool clockwise)
1117 {
1118 if (path == (plPath *)NULL)
1119 return;
1120
1121 _add_ellipse_as_bezier3s (path, pc, radius, radius, 0.0, clockwise);
1122 path->primitive = true; /* flag as flattened primitive */
1123 }
1124
1125 void
_add_circle_as_ellarcs(plPath * path,plPoint pc,double radius,bool clockwise)1126 _add_circle_as_ellarcs (plPath *path, plPoint pc, double radius, bool clockwise)
1127 {
1128 if (path == (plPath *)NULL)
1129 return;
1130
1131 _add_ellipse_as_ellarcs (path, pc, radius, radius, 0.0, clockwise);
1132 path->primitive = true; /* flag as flattened primitive */
1133 }
1134
1135 void
_add_circle_as_lines(plPath * path,plPoint pc,double radius,bool clockwise)1136 _add_circle_as_lines (plPath *path, plPoint pc, double radius, bool clockwise)
1137 {
1138 if (path == (plPath *)NULL)
1139 return;
1140
1141 _add_ellipse_as_lines (path, pc, radius, radius, 0.0, clockwise);
1142 path->primitive = true; /* flag as flattened primitive */
1143 }
1144
1145 /* The _fakearc() subroutine below, which is called by _add_arc_as_lines()
1146 and _add_ellarc_as_lines(), contains a remote descendent of the
1147 arc-drawing algorithm of Ken Turkowski <turk@apple.com> described in
1148 Graphics Gems V. His algorithm is a recursive circle subdivision
1149 algorithm, which relies on the fact that if s and s' are the (chordal
1150 deviation)/radius associated to (respectively) an arc and a half-arc,
1151 then s' is approximately equal to s/4. The exact formula is s' = 1 -
1152 sqrt (1 - s/2), which applies for all s in the meaningful range, i.e. 0
1153 <= s <= 2.
1154
1155 Ken's algorithm rotates the chord of an arc by 90 degrees and scales it
1156 to have length s'. The resulting vector will be the chordal deviation
1157 vector of the arc, which gives the midpoint of the arc, and hence the
1158 endpoints of each half of the arc. So this drawing technique is
1159 recursive.
1160
1161 The problem with this approach is that scaling a vector to a specified
1162 length requires a square root, so there are two square roots in each
1163 subdivision step. One can attempt to remove one of them by noticing
1164 that the chord half-length h always satisfies h = sqrt(s * (2-s))). So
1165 one can rotate the chord vector by 90 degrees, and multiply its length
1166 by s/2h, i.e., s/2sqrt(s * (2-s)), to get the chordal deviation vector.
1167 This factor still includes a square root though. Also one still needs
1168 to compute a square root in order to proceed from one subdivision step
1169 to the next, i.e. to compute s' from s.
1170
1171 We can get around the square root problem by drawing only circular arcs
1172 with subtended angle of 90 degrees (quarter-circles), or elliptic arcs
1173 that are obtained from such quarter-circles by affine transformations
1174 (so-called quarter-ellipses). To draw the latter, we need only replace
1175 the 90 degree rotation matrix mentioned above by an affine
1176 transformation that maps v0->-v1 and v1->v0, where v0 = p0 - pc and v1 =
1177 p1 - pc are the displacement vectors from the center of the ellipse to
1178 the endpoints of the arc. If we do this, we get an elliptic arc with p0
1179 and p1 as endpoints. The vectors v0 and v1 are said lie along conjugate
1180 diameters of the quarter-ellipse.
1181
1182 So for drawing quarter-ellipses, the only initial value of s we need to
1183 consider is the one for a quarter-circle, which is 1-sqrt(1/2). The
1184 successive values of s/2h that will be encountered, after each
1185 bisection, are pre-computed and stored in a lookup table, found in
1186 g_arc.h.
1187
1188 This approach lets us avoid, completely, any computation of square roots
1189 during the drawing of quarter-circles and quarter-ellipses. The only
1190 square roots we need are precomputed. We don't need any floating point
1191 divisions in the main loop either.
1192
1193 Of course for other angles than 90 degrees, we precompute a chord table
1194 and pass it to _fakearc().
1195
1196 The implementation below does not use recursion (we use a local array,
1197 rather than the call stack, to store the sequence of generated
1198 points). */
1199
1200 static void
_fakearc(plPath * path,plPoint p0,plPoint p1,int arc_type,const double * custom_chord_table,const double m[4])1201 _fakearc (plPath *path, plPoint p0, plPoint p1, int arc_type, const double *custom_chord_table, const double m[4])
1202 {
1203 plPoint p[NUM_ARC_SUBDIVISIONS + 1], q[NUM_ARC_SUBDIVISIONS + 1];
1204 int level[NUM_ARC_SUBDIVISIONS + 1];
1205 int n = 0; /* index of top of stack, < NUM_ARC_SUBDIVISIONS */
1206 int segments_drawn = 0;
1207 const double *our_chord_table;
1208
1209 if (arc_type == USER_DEFINED_ARC)
1210 our_chord_table = custom_chord_table;
1211 else /* custom_chord_table arg ignored */
1212 our_chord_table = _chord_table[arc_type];
1213
1214 p[0] = p0;
1215 q[0] = p1;
1216 level[0] = 0;
1217 while (n >= 0) /* i.e. while stack is nonempty */
1218 {
1219 if (level[n] >= NUM_ARC_SUBDIVISIONS)
1220 { /* draw line segment */
1221 _add_line (path, q[n]);
1222 segments_drawn++;
1223 n--;
1224 }
1225
1226 else /* bisect line segment */
1227 {
1228 plVector v;
1229 plPoint pm, pb;
1230
1231 v.x = q[n].x - p[n].x; /* chord = line segment from p[n] to q[n] */
1232 v.y = q[n].y - p[n].y;
1233
1234 pm.x = p[n].x + 0.5 * v.x; /* midpoint of chord */
1235 pm.y = p[n].y + 0.5 * v.y;
1236
1237 /* Compute bisection point. If m=[0 1 -1 0] this just rotates
1238 the chord clockwise by 90 degrees, and scales it to yield the
1239 chordal deviation vector, which is used as an offset. */
1240
1241 pb.x = pm.x +
1242 our_chord_table[level[n]] * (m[0] * v.x + m[1] * v.y);
1243 pb.y = pm.y +
1244 our_chord_table[level[n]] * (m[2] * v.x + m[3] * v.y);
1245
1246 /* replace line segment by pair; level[n] >= n is an invariant */
1247 p[n+1] = p[n];
1248 q[n+1] = pb; /* first half, deal with next */
1249 level[n+1] = level[n] + 1;
1250
1251 p[n] = pb;
1252 q[n] = q[n]; /* second half, deal with later */
1253 level[n] = level[n] + 1;
1254
1255 n++;
1256 }
1257 }
1258 }
1259
1260 /* prepare_chord_table() computes the list of chordal deviation factors
1261 that _fakearc() needs when it is employed to draw a circular arc of
1262 subtended angle other than the default angles it supports */
1263 static void
_prepare_chord_table(double sagitta,double custom_chord_table[TABULATED_ARC_SUBDIVISIONS])1264 _prepare_chord_table (double sagitta, double custom_chord_table[TABULATED_ARC_SUBDIVISIONS])
1265 {
1266 double half_chord_length;
1267 int i;
1268
1269 half_chord_length = sqrt ( sagitta * (2.0 - sagitta) );
1270 for (i = 0; i < TABULATED_ARC_SUBDIVISIONS; i++)
1271 {
1272 custom_chord_table[i] = 0.5 * sagitta / half_chord_length;
1273 sagitta = 1.0 - sqrt (1.0 - 0.5 * sagitta);
1274 half_chord_length = 0.5 * half_chord_length / (1.0 - sagitta);
1275 }
1276 }
1277
1278 /* Flatten a simple path into a path of segment list type, consisting only
1279 of line segments.
1280
1281 As supplied, the path may be perfectly general: it may be a segment list
1282 (not all segments necessarily being line segments), or be a closed
1283 primitive (box/circle/ellipse). If supplied path already consists only
1284 of line segments (with an initial moveto and possibly a final
1285 closepath), it is returned unchanged; this can be tested for by
1286 comparing pointers for equality. If a new path is returned, it must be
1287 freed with _delete_plPath(). */
1288
1289 plPath *
_flatten_path(const plPath * path)1290 _flatten_path (const plPath *path)
1291 {
1292 plPath *newpath;
1293
1294 if (path == (plPath *)NULL)
1295 return (plPath *)NULL;
1296
1297 switch (path->type)
1298 {
1299 case PATH_SEGMENT_LIST:
1300 {
1301 bool do_flatten = false;
1302 int i;
1303
1304 for (i = 0; i < path->num_segments; i++)
1305 {
1306 if (path->segments[i].type != S_MOVETO
1307 && path->segments[i].type != S_LINE
1308 && path->segments[i].type != S_CLOSEPATH)
1309 {
1310 do_flatten = true;
1311 break;
1312 }
1313 }
1314
1315 if (do_flatten == false)
1316 newpath = (plPath *)path; /* just return original path */
1317 else
1318 {
1319 newpath = _new_plPath ();
1320 for (i = 0; i < path->num_segments; i++)
1321 {
1322 switch ((int)(path->segments[i].type))
1323 {
1324 case (int)S_MOVETO:
1325 _add_moveto (newpath, path->segments[i].p);
1326 break;
1327 case (int)S_LINE:
1328 _add_line (newpath, path->segments[i].p);
1329 break;
1330 case (int)S_CLOSEPATH:
1331 _add_closepath (newpath);
1332 break;
1333
1334 /* now, the types of segment we flatten: */
1335
1336 case (int)S_ARC:
1337 _add_arc_as_lines (newpath,
1338 path->segments[i].pc,
1339 path->segments[i].p);
1340 break;
1341 case (int)S_ELLARC:
1342 _add_ellarc_as_lines (newpath,
1343 path->segments[i].pc,
1344 path->segments[i].p);
1345 break;
1346 case (int)S_QUAD:
1347 _add_bezier2_as_lines (newpath,
1348 path->segments[i].pc,
1349 path->segments[i].p);
1350 break;
1351 case (int)S_CUBIC:
1352 _add_bezier3_as_lines (newpath,
1353 path->segments[i].pc,
1354 path->segments[i].pd,
1355 path->segments[i].p);
1356 break;
1357 default: /* shouldn't happen */
1358 break;
1359 }
1360 }
1361 }
1362 break;
1363 }
1364 case PATH_CIRCLE:
1365 newpath = _new_plPath ();
1366 _add_circle_as_lines (newpath,
1367 path->pc, path->radius, path->clockwise);
1368 break;
1369 case PATH_ELLIPSE:
1370 newpath = _new_plPath ();
1371 _add_ellipse_as_lines (newpath,
1372 path->pc, path->rx, path->ry, path->angle,
1373 path->clockwise);
1374 break;
1375 case PATH_BOX:
1376 newpath = _new_plPath ();
1377 _add_box_as_lines (newpath, path->p0, path->p1, path->clockwise);
1378 break;
1379 default: /* shouldn't happen */
1380 newpath = _new_plPath ();
1381 break;
1382 }
1383 return newpath;
1384 }
1385
1386 /**********************************************************************/
1387
1388 /* The code below exports the _merge_paths() function, which munges an
1389 array of plPath objects and returns a new array. Heuristic
1390 "path-merging" of this sort is performed in g_endpath.c when filling a
1391 compound path (i.e., a multi-plPath path), if the output driver supports
1392 only the filling of single plPaths. That is the case for nearly all of
1393 libplot's output drivers.
1394
1395 `Child' paths are merged into their `parents', so each location in the
1396 returned array where a child was present will contain NULL. Also, any
1397 non-child that had no children will be returned without modification.
1398
1399 You should take this into account when freeing the returned array of
1400 plPaths. Only the elements that are (1) non-NULL, and (2) differ from
1401 the corresponding elements in the originally passed array should have
1402 _delete_plPath() invoked on them. The array as a whole may be
1403 deallocated by calling free().
1404
1405 The _merge_paths() function was inspired by a similar function in
1406 Wolfgang Glunz's pstoedit, which was originally written by Burkhard
1407 Plaum <plaum@ipf.uni-stuttgart.de>. */
1408
1409 /* Note: a well-formed plPath has the form:
1410 { moveto { line | arc }* { closepath }? }
1411
1412 The _merge_paths function was written to merge _closed_ plPath's,
1413 i.e. ones whose endpoint is the same as the startpoint (possibly
1414 implicitly, i.e. closepath is allowed). However, libplot applies it to
1415 open paths too, in which case an `implicit closepath' is added to the
1416 path to close it.
1417
1418 NOTE: The current release of libplot does not yet support `closepath'
1419 segments at a higher level. So we regard a pass-in plPath as `closed'
1420 if its last defining vertex is the same as the first. THIS CONVENTION
1421 WILL GO AWAY. */
1422
1423 /* ad hoc structure for an annotated plPath, in particular one that has
1424 been flattened into line segments and annotated; used only in this file,
1425 for merging purposes */
1426
1427 typedef struct subpath_struct
1428 {
1429 plPathSegment *segments; /* segment array */
1430 int num_segments; /* number of segments */
1431
1432 struct subpath_struct **parents; /* array of pointers to possible parents */
1433 struct subpath_struct *parent; /* pointer to parent path */
1434 struct subpath_struct **children; /* array of pointers to child paths */
1435 int num_children; /* number of children */
1436
1437 int num_outside; /* number of subpaths outside this one */
1438
1439 double llx, lly, urx, ury; /* bounding box of the subpath */
1440 bool inserted; /* subpath has been inserted into result? */
1441 } subpath;
1442
1443 /* forward references */
1444
1445 /* 0. ctors, dtors */
1446 static subpath * new_subpath (void);
1447 static subpath ** new_subpath_array (int n);
1448 static void delete_subpath (subpath *s);
1449 static void delete_subpath_array (subpath **s, int n);
1450
1451 /* 1. functions that act on a subpath, i.e. an `annotated path' */
1452 static bool is_inside_of (const subpath *s, const subpath *other);
1453 static double _cheap_lower_bound_on_distance (const subpath *path1, const subpath *path2);
1454 static void linearize_subpath (subpath *s);
1455 static void read_into_subpath (subpath *s, const plPath *path);
1456
1457 /* 2. miscellaneous */
1458 static void find_parents_in_subpath_list (subpath **annotated_paths, int num_paths);
1459 static void insert_subpath (plPathSegment *parent_segments, const plPathSegment *child_segments, int parent_size, int child_size, int parent_index, int child_index);
1460 static void _compute_closest (const plPathSegment *p1, const plPathSegment *p2, int size1, int size2, double *distance, int *index1, int *index2);
1461
1462 /**********************************************************************/
1463
1464 /* ctor for subpath class */
1465 static subpath *
new_subpath(void)1466 new_subpath (void)
1467 {
1468 subpath *s;
1469
1470 s = (subpath *)_pl_xmalloc (sizeof (subpath));
1471
1472 s->segments = (plPathSegment *)NULL;
1473 s->num_segments = 0;
1474 s->parents = (subpath **)NULL;
1475 s->parent = (subpath *)NULL;
1476 s->children = (subpath **)NULL;
1477 s->num_children = 0;
1478 s->num_outside = 0;
1479 s->llx = DBL_MAX;
1480 s->lly = DBL_MAX;
1481 s->urx = -DBL_MAX;
1482 s->ury = -DBL_MAX;
1483 s->inserted = false;
1484
1485 return s;
1486 }
1487
1488 /* corresponding ctor for a subpath array */
1489 static subpath **
new_subpath_array(int n)1490 new_subpath_array (int n)
1491 {
1492 int i;
1493 subpath **s;
1494
1495 s = (subpath **)_pl_xmalloc (n * sizeof (subpath *));
1496 for (i = 0; i < n; i++)
1497 s[i] = new_subpath ();
1498
1499 return s;
1500 }
1501
1502 /* dtor for subpath class */
1503 static void
delete_subpath(subpath * s)1504 delete_subpath (subpath *s)
1505 {
1506 if (s)
1507 {
1508 if (s->segments)
1509 free (s->segments);
1510 if (s->children)
1511 free (s->children);
1512 if (s->parents)
1513 free (s->parents);
1514
1515 free (s);
1516 }
1517 }
1518
1519 /* corresponding dtor for a subpath array */
1520 static void
delete_subpath_array(subpath ** s,int n)1521 delete_subpath_array (subpath **s, int n)
1522 {
1523 int i;
1524
1525 if (s)
1526 {
1527 for (i = 0; i < n; i++)
1528 delete_subpath (s[i]);
1529 free (s);
1530 }
1531 }
1532
1533 /* replace every segment in a subpath by a lineto (invoked only on a child
1534 subpath, i.e. a subpath with an identified parent) */
1535 static void
linearize_subpath(subpath * s)1536 linearize_subpath (subpath *s)
1537 {
1538 /* replace first segment (moveto) with a lineto */
1539 s->segments[0].type = S_LINE;
1540
1541 /* if final segment is a closepath, also replace with a lineto, back to
1542 point #0 */
1543 if (s->segments[s->num_segments-1].type == S_CLOSEPATH)
1544 {
1545 s->segments[s->num_segments-1].type = S_LINE;
1546 s->segments[s->num_segments-1].p = s->segments[0].p;
1547 }
1548 }
1549
1550 /* Read a sequence of plPathSegments from a plPath into a previously empty
1551 annotated subpath. (This is called only after the plPath has been
1552 flattened, so the segments include no arcs.)
1553
1554 Because the way in which _merge_paths() is currently called in libplot,
1555 we need to handle the possibility that the plPath may not be closed. If
1556 it isn't, we add a closepath.
1557
1558 At present, we allow a final lineto to the start vertex to serve the
1559 same purpose. THIS IS A LIBPLOT CONVENTION THAT WILL GO AWAY. */
1560
1561 static void
read_into_subpath(subpath * s,const plPath * path)1562 read_into_subpath (subpath *s, const plPath *path)
1563 {
1564 bool need_to_close = false;
1565 int i;
1566
1567 /* sanity check */
1568 if (path->type != PATH_SEGMENT_LIST)
1569 return;
1570
1571 /* allocate space for segment array of subpath; add 1 extra slot for
1572 manual closure, if needed */
1573 s->segments = (plPathSegment *)_pl_xmalloc((path->num_segments + 1) * sizeof (plPathSegment));
1574 s->num_segments = path->num_segments;
1575
1576 /* sanity check */
1577 if (path->num_segments == 0)
1578 return;
1579
1580 /* Is this path closed? If not, we'll close manually the annotated path
1581 that we'll construct. WE CURRENTLY TREAT FINAL = INITIAL AS
1582 INDICATING CLOSURE. */
1583 if (path->segments[path->num_segments - 1].type != S_CLOSEPATH
1584 &&
1585 (path->segments[path->num_segments - 1].p.x != path->segments[0].p.x
1586 || path->segments[path->num_segments - 1].p.y != path->segments[0].p.y))
1587 need_to_close = true;
1588
1589 /* copy the segments, updating bounding box to take each juncture point
1590 into account */
1591 for (i = 0; i < path->num_segments; i++)
1592 {
1593 plPathSegment e;
1594
1595 e = path->segments[i];
1596 s->segments[i] = e;
1597
1598 if (e.p.x < s->llx)
1599 s->llx = e.p.x;
1600 if (e.p.y < s->lly)
1601 s->lly = e.p.y;
1602 if (e.p.x > s->urx)
1603 s->urx = e.p.x;
1604 if (e.p.y > s->ury)
1605 s->ury = e.p.y;
1606 }
1607
1608 if (need_to_close)
1609 {
1610 #if 0
1611 s->segments[path->num_segments].type = S_CLOSEPATH;
1612 #else /* currently, use line segment instead of closepath */
1613 s->segments[path->num_segments].type = S_LINE;
1614 #endif
1615 s->segments[path->num_segments].p = path->segments[0].p;
1616 s->num_segments++;
1617 }
1618 }
1619
1620 /* check if a subpath is inside another subpath */
1621 static bool
is_inside_of(const subpath * s,const subpath * other)1622 is_inside_of (const subpath *s, const subpath *other)
1623 {
1624 int inside = 0;
1625 int outside = 0;
1626 int i;
1627
1628 /* if bbox fails to lie inside the other's bbox, false */
1629 if (!((s->llx >= other->llx) && (s->lly >= other->lly) &&
1630 (s->urx <= other->urx) && (s->ury <= other->ury)))
1631 return false;
1632
1633 /* otherwise, check all juncture points */
1634 for (i = 0; i < s->num_segments; i++)
1635 {
1636 bool point_is_inside;
1637
1638 if (s->segments[i].type == S_CLOSEPATH)
1639 /* should have i = num_segments - 1, no associated juncture point */
1640 continue;
1641
1642 /* Check if the vertex s->segments[i].p is inside `other'. Could be
1643 done in a separate function, but we inline it for speed. */
1644 {
1645 /* These two factors should be small positive floating-point
1646 numbers. They should preferably be incommensurate, to minimize
1647 the probability of a degenerate case occurring: two line
1648 segments intersecting at the endpoint of one or the other. */
1649 #define SMALL_X_FACTOR (M_SQRT2 * M_PI)
1650 #define SMALL_Y_FACTOR (M_SQRT2 + M_PI)
1651
1652 /* argument of the now-inlined function (besides `other') */
1653 plPoint p;
1654 /* local variables of the now-inlined function */
1655 int k, crossings;
1656 /* (x1,y1) is effectively the point at infinity */
1657 double x1, y1;
1658 /* (x2,y2) is specified point */
1659 double x2, y2;
1660
1661 /* argument of the now-inlined function (besides `other') */
1662 p = s->segments[i].p;
1663
1664 /* (x1,y1) is effectively the point at infinity */
1665 x1 = (DMAX(p.x, other->urx)
1666 + SMALL_X_FACTOR * (DMAX(p.x, other->urx)
1667 - DMIN(p.x, other->llx)));
1668 y1 = (DMAX(p.y, other->ury)
1669 + SMALL_Y_FACTOR * (DMAX(p.y, other->ury)
1670 - DMIN(p.y, other->lly)));
1671
1672 /* (x2,y2) is specified point */
1673 x2 = p.x;
1674 y2 = p.y;
1675
1676 crossings = 0;
1677 for (k = 0; k < other->num_segments; k++)
1678 {
1679 int j;
1680 double x3, y3, x4, y4, det, det1, det2;
1681
1682 if (other->segments[k].type == S_CLOSEPATH) /* k > 0 */
1683 {
1684 x3 = other->segments[k-1].p.x;
1685 y3 = other->segments[k-1].p.y;
1686 }
1687 else
1688 {
1689 x3 = other->segments[k].p.x;
1690 y3 = other->segments[k].p.y;
1691 }
1692
1693 j = (k == other->num_segments - 1 ? 0 : k + 1);
1694 if (other->segments[j].type == S_CLOSEPATH)
1695 continue;
1696
1697 x4 = other->segments[j].p.x;
1698 y4 = other->segments[j].p.y;
1699
1700 /* (x3,y3)-(x4,y4) is a line segment in the closed path */
1701
1702 /* Check whether the line segments (x1,y1)-(x2,y2) and
1703 (x3-y3)-(x4,y4) cross each other.
1704
1705 System to solve is:
1706
1707 [p1 + (p2 - p1) * t1] - [p3 + (p4 - p3) * t2] = 0
1708
1709 i.e.
1710
1711 (x2 - x1) * t1 - (x4 - x3) * t2 = x3 - x1;
1712 (y2 - y1) * t1 - (y4 - y3) * t2 = y3 - y1;
1713
1714 Solutions are: t1 = det1/det
1715 t2 = det2/det
1716
1717 The line segments cross each other (in their interiors) if
1718 0.0 < t1 < 1.0 and 0.0 < t2 < 1.0 */
1719
1720 det = (x2 - x1) * (-(y4 - y3)) - (-(x4 - x3)) * (y2 - y1);
1721 if (det == 0.0)
1722 /* line segments are parallel; ignore the degenerate case
1723 that they might overlap */
1724 continue;
1725
1726 det1 = (x3 - x1) * (-(y4 - y3)) - (-(x4 - x3)) * (y3 - y1);
1727 det2 = (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1);
1728
1729 if ((det<0.0 && (det1>0.0 || det2>0.0 || det1<det || det2<det))
1730 ||
1731 (det>0.0 && (det1<0.0 || det2<0.0 || det1>det || det2>det)))
1732 /* solution for at least one of t1 and t2 is outside the
1733 interval [0,1], so line segments do not cross */
1734 continue;
1735
1736 /* We ignore the possibility that t1, t2 are both in the interval
1737 [0,1], but
1738 (t1 == 0.0) || (t1 == 1.0) || (t2 == 0.0) || (t2 == 1.0).
1739
1740 t1 == 0.0 should never happen, if p1 is effectively
1741 the point at infinity.
1742
1743 So this degenerate case occurs only if the line segment
1744 (x1,y1)-(x2,y2) goes through either (x3,y3) or (x4,y4), or
1745 the specified point (x2,y2) lies on the line segment
1746 (x3,y3)-(x4,y4) that is part of the path. */
1747
1748 crossings++;
1749 }
1750
1751 /* our determination of whether the point is inside the path;
1752 before we inlined this function, this was the return value */
1753 point_is_inside = (crossings & 1) ? true : false;
1754 }
1755
1756 /* increment inside,outside depending on whether or not the juncture
1757 point was inside the other path */
1758 if (point_is_inside)
1759 inside++;
1760 else
1761 outside++;
1762 }
1763
1764 /* make a democratic decision as to whether the path as a whole is inside
1765 the other path */
1766 return (inside > outside ? true : false);
1767 }
1768
1769 /* Find parent (if any) of each subpath in a list of subpaths. When this
1770 is invoked, each subpath should consist of an initial moveto, at least
1771 one lineto, and a closepath (not currently enforced). */
1772
1773 static void
find_parents_in_subpath_list(subpath ** annotated_paths,int num_paths)1774 find_parents_in_subpath_list (subpath **annotated_paths, int num_paths)
1775 {
1776 int i, j;
1777 subpath *parent;
1778
1779 /* determine for each subpath the subpaths that are nominally outside it */
1780 for (i = 0; i < num_paths; i++)
1781 {
1782 annotated_paths[i]->parents = new_subpath_array (num_paths);
1783 for (j = 0; j < num_paths; j++)
1784 {
1785 if (j != i)
1786 {
1787 if (is_inside_of (annotated_paths[i], annotated_paths[j]))
1788 {
1789 annotated_paths[i]->parents[annotated_paths[i]->num_outside] =
1790 annotated_paths[j];
1791 annotated_paths[i]->num_outside++;
1792 }
1793 }
1794 }
1795 }
1796
1797 /* Now find the real parent subpaths, i.e. the root subpaths. A subpath
1798 is a parent subpath if the number of nominally-outside subpaths is
1799 even, and is a child subpath only if the number is odd. An odd
1800 number, together with a failure to find a suitable potential parent,
1801 will flag a path as an isolate: technically a parent, but without
1802 children. */
1803
1804 for (i = 0; i < num_paths; i++)
1805 {
1806 if ((annotated_paths[i]->num_outside & 1) == 0)
1807 /* an even number of subpaths outside, definitely a parent
1808 (i.e. doesn't have a parent itself, may or may not have children) */
1809 {
1810 /* allocate space for children (if any) */
1811 annotated_paths[i]->children = new_subpath_array (num_paths);
1812 }
1813 }
1814
1815 /* now determine which are children, and link them to their parents */
1816
1817 for (i = 0; i < num_paths; i++)
1818 {
1819 if ((annotated_paths[i]->num_outside & 1) == 0)
1820 /* even number outside, definitely a parent subpath (whether it has
1821 children remains to be determined) */
1822 continue;
1823 else
1824 /* odd number outside, possibly a child, so search linearly through
1825 possible parents until we get a hit; if so, this is a child; if
1826 not, this is an isolate (classed as a parent) */
1827 {
1828 for (j = 0; j < annotated_paths[i]->num_outside; j++)
1829 {
1830 if (annotated_paths[i]->num_outside ==
1831 annotated_paths[i]->parents[j]->num_outside + 1)
1832 /* number outside is one more than the number outside a
1833 potential parent; flag as a child, and add it to the
1834 parent's child list */
1835 {
1836 parent = annotated_paths[i]->parents[j];
1837 annotated_paths[i]->parent = parent; /* give it a parent */
1838 parent->children[parent->num_children] = annotated_paths[i];
1839 parent->num_children++;
1840 break;
1841 }
1842 }
1843 }
1844 }
1845 }
1846
1847 /* Compute closest vertices in two paths. Indices of closest vertices, and
1848 (squared) distance between them, are returned via pointers.
1849
1850 This is invoked in _merge_paths() only on paths that have been
1851 flattened, and have had the initial moveto and the optional final
1852 closepath replaced by line segments. So when this is called, each
1853 segment type is S_LINE. */
1854
1855 static void
_compute_closest(const plPathSegment * p1,const plPathSegment * p2,int size1,int size2,double * distance,int * index1,int * index2)1856 _compute_closest (const plPathSegment *p1, const plPathSegment *p2, int size1, int size2, double *distance, int *index1, int *index2)
1857 {
1858 int best_i = 0, best_j = 0; /* keep compiler happy */
1859 double best_distance = DBL_MAX;
1860 int ii, jj;
1861
1862 for (ii = 0; ii < size1; ii++)
1863 {
1864 plPoint point1;
1865
1866 point1 = p1[ii].p;
1867 for (jj = 0; jj < size2; jj++)
1868 {
1869 double tmp1, tmp2, distance;
1870 plPoint point2;
1871
1872 point2 = p2[jj].p;
1873 tmp1 = point1.x - point2.x;
1874 tmp2 = point1.y - point2.y;
1875 distance = tmp1 * tmp1 + tmp2 * tmp2;
1876 if (distance < best_distance)
1877 {
1878 best_distance = distance;
1879 best_i = ii;
1880 best_j = jj;
1881 }
1882 }
1883 }
1884
1885 /* return the three quantities */
1886 *distance = best_distance;
1887 *index1 = best_i;
1888 *index2 = best_j;
1889 }
1890
1891 /* Compute a cheap lower bound on the (squared) distance between two
1892 subpaths, by looking at their bounding boxes. */
1893
1894 static double
_cheap_lower_bound_on_distance(const subpath * path1,const subpath * path2)1895 _cheap_lower_bound_on_distance (const subpath *path1, const subpath *path2)
1896 {
1897 double xdist = 0.0, ydist = 0.0, dist;
1898
1899 if (path1->urx < path2->llx)
1900 xdist = path2->llx - path1->urx;
1901 else if (path2->urx < path1->llx)
1902 xdist = path1->llx - path2->urx;
1903
1904 if (path1->ury < path2->lly)
1905 ydist = path2->lly - path1->ury;
1906 else if (path2->ury < path1->lly)
1907 ydist = path1->lly - path2->ury;
1908
1909 dist = xdist * xdist + ydist * ydist;
1910
1911 return dist;
1912 }
1913
1914 /* Insert a closed child subpath into a closed parent, by connecting the
1915 two, twice, at a specified vertex of the parent path and a specified
1916 vertex of the child path. This is the key function invoked by
1917 _merge_paths().
1918
1919 Both paths are supplied as segment lists, and all segments are lines;
1920 the final segment of each is a line back to the starting point. I.e.,
1921 for both subpaths, the final vertex duplicates the start vertex. So we
1922 ignore the final vertex of the child (but not that of the parent).
1923 I.e. if the child vertices are numbered 0..child_size-1, we map the case
1924 child_index = child_size-1 to child_index = 0.
1925
1926 The new path has parent_size + child_size + 1 vertices, of which the
1927 last is a repetition of the first. */
1928
1929 /* INDEX MAP:
1930
1931 PARENT -> NEW PATH
1932 i --> i (if 0 <= i < parent_index + 1)
1933 i --> i+child_size+1 (if parent_index + 1 <= i < parent_size)
1934
1935 CHILD -> NEW PATH
1936 i --> i+parent_index+child_size-child_index (0 <= i < child_index+1)
1937 i --> i+parent_index-child_index+1 (child_index+1 <= i < child_size-1)
1938 (0'th element of the child is same as child_size-1 element)
1939
1940 NEW PATH CONTENTS
1941
1942 0..parent_index
1943 0..parent_index of PARENT
1944 parent_index+1
1945 child_index of CHILD (i.e. ->join)
1946 parent_index+2..parent_index+child_size-child_index-1
1947 child_index+1..child_size-2 of CHILD
1948 parent_index+child_size-child_index..parent_index+child_size
1949 0..child_index of CHILD
1950 parent_index+child_size+1
1951 parent_index of PARENT (i.e. ->join)
1952 parent_index+child_size+2..parent_size+child_size
1953 parent_index+1..parent_size-1 of PARENT
1954
1955 */
1956
1957 /* Macros that map from vertices in the child path and the parent path, to
1958 vertices in the merged path. Here the argument `i' is the index in the
1959 original path, and each macro evaluates to the index in the merger. */
1960
1961 /* The first macro should not be applied to i=child_size-1; as noted above,
1962 that vertex is equivalent to i=0, so apply it to i=0 instead. */
1963
1964 #define CHILD_VERTEX_IN_MERGED_PATH(i,parent_index,parent_size,child_index,child_size) ((i) <= (child_index) ? (i) + (parent_index) + (child_size) - (child_index) : (i) + (parent_index) - (child_index) + 1)
1965 #define PARENT_VERTEX_IN_MERGED_PATH(i,parent_index,parent_size,child_index,child_size) ((i) <= (parent_index) ? (i) : (i) + (child_size) + 1)
1966
1967
1968 static void
insert_subpath(plPathSegment * parent,const plPathSegment * child,int parent_size,int child_size,int parent_index,int child_index)1969 insert_subpath (plPathSegment *parent, const plPathSegment *child, int parent_size, int child_size, int parent_index, int child_index)
1970 {
1971 int i;
1972 plPathSegment e1, e2;
1973 int src_index;
1974
1975 /* map case when joining vertex is final vertex of child to case when
1976 it's the 0'th vertex */
1977 if (child_index == child_size - 1)
1978 child_index = 0;
1979
1980 /* move up: add child_size+1 empty slots to parent path */
1981 for (i = parent_size - 1; i >= parent_index + 1; i--)
1982 parent[i + child_size + 1] = parent[i];
1983
1984 /* add a line segment from specified vertex of parent path to specified
1985 vertex of child path */
1986 e1 = child[child_index];
1987 e1.type = S_LINE; /* unnecessary */
1988 parent[parent_index + 1] = e1;
1989
1990 /* copy vertices of child into parent, looping back to start in child if
1991 necessary; note we skip the last (i.e. child_size-1'th) vertex, since
1992 the 0'th vertex is the same */
1993 src_index = child_index;
1994 for (i = 0; i < child_size - 1; i++)
1995 {
1996 src_index++;
1997 if (src_index == child_size - 1)
1998 src_index = 0;
1999 parent[parent_index + 2 + i] = child[src_index];
2000 }
2001
2002 /* add a line segment back from specified vertex of child path to
2003 specified vertex of parent path */
2004 e2 = parent[parent_index];
2005 e2.type = S_LINE;
2006 parent[parent_index + child_size + 1] = e2;
2007 }
2008
2009 /* The key function exported by this module, which is used by libplot for
2010 filling compound paths. */
2011
2012 plPath **
_merge_paths(const plPath ** paths,int num_paths)2013 _merge_paths (const plPath **paths, int num_paths)
2014 {
2015 int i;
2016 subpath **annotated_paths;
2017 plPath **flattened_paths;
2018 plPath **merged_paths;
2019
2020 /* flatten every path to a list of line segments (some paths may come
2021 back unaltered; will be able to compare pointers to check for that) */
2022 flattened_paths = (plPath **)_pl_xmalloc (num_paths * sizeof(plPath *));
2023 for (i = 0; i < num_paths; i++)
2024 {
2025 flattened_paths[i] = _flatten_path (paths[i]);
2026 #ifdef DEBUG
2027 fprintf (stderr, "path %d: %d segments, flattened to %d segments\n",
2028 i, paths[i]->num_segments, flattened_paths[i]->num_segments);
2029 #endif
2030 }
2031
2032 /* Copy each flattened path into a corresponding annotated path
2033 (`subpath'). Manual closure, if necessary (see above) is performed,
2034 i.e. we always add a final closepath to close the path. At this stage
2035 bounding boxes are computed. */
2036 annotated_paths = new_subpath_array (num_paths);
2037 for (i = 0; i < num_paths; i++)
2038 read_into_subpath (annotated_paths[i], flattened_paths[i]);
2039
2040 /* Flattened paths no longer needed, so delete them carefully (some may
2041 be the same as the original paths, due to _flatten_path() having
2042 simply returned its argument) */
2043 for (i = 0; i < num_paths; i++)
2044 if (flattened_paths[i] != paths[i])
2045 _delete_plPath (flattened_paths[i]);
2046
2047 /* determine which subpaths are parents, children */
2048 find_parents_in_subpath_list (annotated_paths, num_paths);
2049
2050 /* in each child, replace each moveto/closepath by a lineto */
2051 for (i = 0; i < num_paths; i++)
2052 if (annotated_paths[i]->parent != (subpath *)NULL)
2053 /* child path */
2054 linearize_subpath (annotated_paths[i]);
2055
2056 /* create array of merged paths: parent paths will have child paths
2057 merged into them, and child paths won't appear */
2058
2059 /* allocate space for new array, to be returned */
2060 merged_paths = (plPath **)_pl_xmalloc (num_paths * sizeof(plPath *));
2061
2062 for (i = 0; i < num_paths; i++)
2063 {
2064 int j, k, num_segments_in_merged_path;
2065 subpath *parent;
2066 plPath *merged_path;
2067 double *parent_to_child_distances;
2068 int *child_best_indices, *parent_best_indices;
2069
2070 if (annotated_paths[i]->parent != (subpath *)NULL)
2071 /* child path; original path will be merged into parent */
2072 {
2073 merged_paths[i] = (plPath *)NULL;
2074 continue;
2075 }
2076
2077 if (annotated_paths[i]->num_children == 0)
2078 /* no parent, but no children either, so no merging done; in output
2079 path array, place original unflattened path */
2080 {
2081 merged_paths[i] = (plPath *)paths[i];
2082 continue;
2083 }
2084
2085 /* this path must be a parent, with one or more children to be merged
2086 into it; so create new empty `merged path' with segments array
2087 that will hold it, and the merged-in children */
2088 parent = annotated_paths[i];
2089 num_segments_in_merged_path = parent->num_segments;
2090 for (j = 0; j < parent->num_children; j++)
2091 num_segments_in_merged_path
2092 += (parent->children[j]->num_segments + 1);
2093
2094 merged_path = _new_plPath ();
2095 merged_path->segments = (plPathSegment *)_pl_xmalloc(num_segments_in_merged_path * sizeof (plPathSegment));
2096 merged_path->num_segments = 0;
2097 merged_path->segments_len = num_segments_in_merged_path;
2098
2099 /* copy parent path into new empty path, i.e. initialize the merged
2100 path */
2101 for (j = 0; j < parent->num_segments; j++)
2102 merged_path->segments[j] = parent->segments[j];
2103 merged_path->num_segments = parent->num_segments;
2104
2105 /* Create temporary storage for `closest vertex pairs' and inter-path
2106 distances. We first compute the shortest distance between each
2107 child path. We also keep track of the shortest distance between
2108 each child and the merged path being constructed, and update it
2109 when any child is added. */
2110
2111 parent_to_child_distances = (double *)_pl_xmalloc(parent->num_children * sizeof (double));
2112 parent_best_indices = (int *)_pl_xmalloc(parent->num_children * sizeof (int));
2113 child_best_indices = (int *)_pl_xmalloc(parent->num_children * sizeof (int));
2114
2115 /* compute closest vertices between merged path (i.e., right now, the
2116 parent) and any child; these arrays will be updated when any child
2117 is inserted into the merged path */
2118 for (j = 0; j < parent->num_children; j++)
2119 _compute_closest (parent->segments,
2120 parent->children[j]->segments,
2121 parent->num_segments,
2122 parent->children[j]->num_segments,
2123 &(parent_to_child_distances[j]),
2124 &(parent_best_indices[j]),
2125 &(child_best_indices[j]));
2126
2127 for (k = 0; k < parent->num_children; k++)
2128 /* insert a child (the closest remaining one!) into the built-up
2129 merged path; and flag the child as having been inserted so that
2130 we don't pay attention to it thereafter */
2131 {
2132 double min_distance;
2133 int closest = 0; /* keep compiler happy */
2134 double *new_parent_to_child_distances;
2135 int *new_child_best_indices, *new_parent_best_indices;
2136
2137 /* allocate storage for arrays that will be used to update the
2138 three abovementioned arrays, with each pass through the loop */
2139 new_parent_to_child_distances = (double *)_pl_xmalloc(parent->num_children * sizeof (double));
2140 new_parent_best_indices = (int *)_pl_xmalloc(parent->num_children * sizeof (int));
2141 new_child_best_indices = (int *)_pl_xmalloc(parent->num_children * sizeof (int));
2142
2143 /* initially, they're the same as the current arrays */
2144 for (j = 0; j < parent->num_children; j++)
2145 {
2146 new_parent_to_child_distances[j] = parent_to_child_distances[j];
2147 new_parent_best_indices[j] = parent_best_indices[j];
2148 new_child_best_indices[j] = child_best_indices[j];
2149 }
2150
2151 /* find closest child to merged path, which has not yet been
2152 inserted */
2153 min_distance = DBL_MAX;
2154 for (j = 0; j < parent->num_children; j++)
2155 {
2156 if (parent->children[j]->inserted) /* ignore this child */
2157 continue;
2158
2159 if (parent_to_child_distances[j] < min_distance)
2160 {
2161 closest = j;
2162 min_distance = parent_to_child_distances[j];
2163 }
2164 }
2165
2166 /* closest remaining child has index `closest'; it will be
2167 inserted into the current merged path */
2168
2169 /* loop over all children, skipping inserted ones and also
2170 skipping `closest', the next child to be inserted */
2171 for (j = 0; j < parent->num_children; j++)
2172 {
2173 double inter_child_distance;
2174 int inter_child_best_index1, inter_child_best_index2;
2175 double lower_bound_on_inter_child_distance;
2176 bool compute_carefully;
2177
2178 if (parent->children[j]->inserted) /* ignore */
2179 continue;
2180
2181 if (j == closest) /* ignore */
2182 continue;
2183
2184 /* compute distance (and closest vertex pairs) between
2185 `closest' and the j'th child; result is only of interest
2186 if the distance is less than parent_to_child_distances[j],
2187 so we first compute a cheap lower bound on the result by
2188 looking at bounding boxes. */
2189
2190 lower_bound_on_inter_child_distance =
2191 _cheap_lower_bound_on_distance (parent->children[j],
2192 parent->children[closest]);
2193 compute_carefully =
2194 (lower_bound_on_inter_child_distance <
2195 parent_to_child_distances[j]) ? true : false;
2196
2197 if (compute_carefully)
2198 /* compute accurate inter-child distance; also which two
2199 vertices yield the minimum distance */
2200 _compute_closest (parent->children[j]->segments,
2201 parent->children[closest]->segments,
2202 parent->children[j]->num_segments,
2203 parent->children[closest]->num_segments,
2204 &inter_child_distance,
2205 &inter_child_best_index1, /* vertex in j */
2206 &inter_child_best_index2); /* in `closest' */
2207
2208 /* fill in j'th element of the new arrays
2209 parent_to_child_distances[], parent_best_indices[] and
2210 child_best_indices[] so as to take the insertion of the
2211 child into account; but we don't update the old arrays
2212 until we do the actual insertion */
2213
2214 if (compute_carefully &&
2215 inter_child_distance < parent_to_child_distances[j])
2216 /* j'th child is nearer to a vertex in `closest', the child
2217 to be inserted, than to any vertex in the current merged
2218 path, so all three arrays are affected */
2219 {
2220 int nearest_index_in_closest_child;
2221
2222 new_parent_to_child_distances[j] = inter_child_distance;
2223 new_child_best_indices[j] = inter_child_best_index1;
2224 nearest_index_in_closest_child = inter_child_best_index2;
2225
2226 /* Compute new value of parent_best_indices[j], taking
2227 into account that `closest' will be inserted into the
2228 merged path, thereby remapping the relevant index in
2229 `closest'. The macro doesn't perform correctly if its
2230 first arg takes the maximum possible value; so
2231 instead, we map that possibility to `0'. See comment
2232 above, before the macro definition. */
2233
2234 if (nearest_index_in_closest_child ==
2235 parent->children[closest]->num_segments - 1)
2236 nearest_index_in_closest_child = 0;
2237 new_parent_best_indices[j] =
2238 CHILD_VERTEX_IN_MERGED_PATH(nearest_index_in_closest_child,
2239 parent_best_indices[closest],
2240 merged_path->num_segments,
2241 child_best_indices[closest],
2242 parent->children[closest]->num_segments);
2243 }
2244 else
2245 /* j'th child is nearer to a vertex in the current merged
2246 path than to any vertex in `closest', the child to be
2247 inserted into the merged path */
2248 {
2249 int nearest_index_in_parent;
2250
2251 nearest_index_in_parent = parent_best_indices[j];
2252
2253 /* compute new value of parent_best_indices[j], taking
2254 into account that `closest' will be inserted into the
2255 merged path, thereby remapping the relevant index in
2256 the merged path */
2257 new_parent_best_indices[j] =
2258 PARENT_VERTEX_IN_MERGED_PATH(nearest_index_in_parent,
2259 parent_best_indices[closest],
2260 merged_path->num_segments,
2261 child_best_indices[closest],
2262 parent->children[closest]->num_segments);
2263 }
2264 }
2265
2266 /* do the actual insertion, by adding a pair of lineto's between
2267 closest vertices; flag child as inserted */
2268 insert_subpath (merged_path->segments,
2269 parent->children[closest]->segments,
2270 merged_path->num_segments,
2271 parent->children[closest]->num_segments,
2272 parent_best_indices[closest],
2273 child_best_indices[closest]);
2274 merged_path->num_segments +=
2275 (parent->children[closest]->num_segments + 1);
2276 parent->children[closest]->inserted = true;
2277
2278 /* update the old arrays to take insertion into account: replace
2279 them by the new ones */
2280 for (j = 0; j < parent->num_children; j++)
2281 {
2282 parent_to_child_distances[j] = new_parent_to_child_distances[j];
2283 parent_best_indices[j] = new_parent_best_indices[j];
2284 child_best_indices[j] = new_child_best_indices[j];
2285 }
2286
2287 free (new_parent_to_child_distances);
2288 free (new_parent_best_indices);
2289 free (new_child_best_indices);
2290 }
2291 /* End of loop over all children of parent subpath; all >=1 children
2292 have now been inserted into the parent, i.e. into the `merged
2293 path' which the parent initialized. However, the merged path's
2294 segments are all lines; so change the first to a moveto. */
2295
2296 merged_path->segments[0].type = S_MOVETO;
2297 merged_paths[i] = merged_path;
2298
2299 /* NOTE: SHOULD ALSO REPLACE LAST LINE SEGMENT BY A CLOSEPATH! */
2300
2301 /* delete temporary storage for `closest vertex pairs' and inter-path
2302 distances */
2303 free (parent_to_child_distances);
2304 free (parent_best_indices);
2305 free (child_best_indices);
2306 }
2307 /* end of loop over parent subpaths */
2308
2309 /* no more annotated paths needed */
2310 delete_subpath_array (annotated_paths, num_paths);
2311
2312 return merged_paths;
2313 }
2314