fix crashes reported by Debian Cylab Mayhem Team
[swftools.git] / lib / art / art_svp_wind.c
blob161a6329adba5b801daf5d1ce4bec5665e919807
1 /* Libart_LGPL - library of basic graphic primitives
2 * Copyright (C) 1998-2000 Raph Levien
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 /* Primitive intersection and winding number operations on sorted
21 vector paths.
23 These routines are internal to libart, used to construct operations
24 like intersection, union, and difference. */
26 #include "config.h"
27 #include "art_svp_wind.h"
29 #include <stdio.h> /* for printf of debugging info */
30 #include <string.h> /* for memcpy */
31 #include <math.h>
32 #include "art_misc.h"
34 #include "art_rect.h"
35 #include "art_svp.h"
37 #define noVERBOSE
39 #define PT_EQ(p1,p2) ((p1).x == (p2).x && (p1).y == (p2).y)
41 #define PT_CLOSE(p1,p2) (fabs ((p1).x - (p2).x) < 1e-6 && fabs ((p1).y - (p2).y) < 1e-6)
43 /* return nonzero and set *p to the intersection point if the lines
44 z0-z1 and z2-z3 intersect each other. */
45 static int
46 intersect_lines (ArtPoint z0, ArtPoint z1, ArtPoint z2, ArtPoint z3,
47 ArtPoint *p)
49 double a01, b01, c01;
50 double a23, b23, c23;
51 double d0, d1, d2, d3;
52 double det;
54 /* if the vectors share an endpoint, they don't intersect */
55 if (PT_EQ (z0, z2) || PT_EQ (z0, z3) || PT_EQ (z1, z2) || PT_EQ (z1, z3))
56 return 0;
58 #if 0
59 if (PT_CLOSE (z0, z2) || PT_CLOSE (z0, z3) || PT_CLOSE (z1, z2) || PT_CLOSE (z1, z3))
60 return 0;
61 #endif
63 /* find line equations ax + by + c = 0 */
64 a01 = z0.y - z1.y;
65 b01 = z1.x - z0.x;
66 c01 = -(z0.x * a01 + z0.y * b01);
67 /* = -((z0.y - z1.y) * z0.x + (z1.x - z0.x) * z0.y)
68 = (z1.x * z0.y - z1.y * z0.x) */
70 d2 = a01 * z2.x + b01 * z2.y + c01;
71 d3 = a01 * z3.x + b01 * z3.y + c01;
72 if ((d2 > 0) == (d3 > 0))
73 return 0;
75 a23 = z2.y - z3.y;
76 b23 = z3.x - z2.x;
77 c23 = -(z2.x * a23 + z2.y * b23);
79 d0 = a23 * z0.x + b23 * z0.y + c23;
80 d1 = a23 * z1.x + b23 * z1.y + c23;
81 if ((d0 > 0) == (d1 > 0))
82 return 0;
84 /* now we definitely know that the lines intersect */
85 /* solve the two linear equations ax + by + c = 0 */
86 det = 1.0 / (a01 * b23 - a23 * b01);
87 p->x = det * (c23 * b01 - c01 * b23);
88 p->y = det * (c01 * a23 - c23 * a01);
90 return 1;
93 #define EPSILON 1e-6
95 static double
96 trap_epsilon (double v)
98 const double epsilon = EPSILON;
100 if (v < epsilon && v > -epsilon) return 0;
101 else return v;
104 /* Determine the order of line segments z0-z1 and z2-z3.
105 Return +1 if z2-z3 lies entirely to the right of z0-z1,
106 -1 if entirely to the left,
107 or 0 if overlap.
109 The case analysis in this function is quite ugly. The fact that it's
110 almost 200 lines long is ridiculous.
112 Ok, so here's the plan to cut it down:
114 First, do a bounding line comparison on the x coordinates. This is pretty
115 much the common case, and should go quickly. It also takes care of the
116 case where both lines are horizontal.
118 Then, do d0 and d1 computation, but only if a23 is nonzero.
120 Finally, do d2 and d3 computation, but only if a01 is nonzero.
122 Fall through to returning 0 (this will happen when both lines are
123 horizontal and they overlap).
125 static int
126 x_order (ArtPoint z0, ArtPoint z1, ArtPoint z2, ArtPoint z3)
128 double a01, b01, c01;
129 double a23, b23, c23;
130 double d0, d1, d2, d3;
132 if (z0.y == z1.y)
134 if (z2.y == z3.y)
136 double x01min, x01max;
137 double x23min, x23max;
139 if (z0.x > z1.x)
141 x01min = z1.x;
142 x01max = z0.x;
144 else
146 x01min = z0.x;
147 x01max = z1.x;
150 if (z2.x > z3.x)
152 x23min = z3.x;
153 x23max = z2.x;
155 else
157 x23min = z2.x;
158 x23max = z3.x;
161 if (x23min >= x01max) return 1;
162 else if (x01min >= x23max) return -1;
163 else return 0;
165 else
167 /* z0-z1 is horizontal, z2-z3 isn't */
168 a23 = z2.y - z3.y;
169 b23 = z3.x - z2.x;
170 c23 = -(z2.x * a23 + z2.y * b23);
172 if (z3.y < z2.y)
174 a23 = -a23;
175 b23 = -b23;
176 c23 = -c23;
179 d0 = trap_epsilon (a23 * z0.x + b23 * z0.y + c23);
180 d1 = trap_epsilon (a23 * z1.x + b23 * z1.y + c23);
182 if (d0 > 0)
184 if (d1 >= 0) return 1;
185 else return 0;
187 else if (d0 == 0)
189 if (d1 > 0) return 1;
190 else if (d1 < 0) return -1;
191 else /*printf ("case 1 degenerate\n")*/;
192 return 0;
194 else /* d0 < 0 */
196 if (d1 <= 0) return -1;
197 else return 0;
201 else if (z2.y == z3.y)
203 /* z2-z3 is horizontal, z0-z1 isn't */
204 a01 = z0.y - z1.y;
205 b01 = z1.x - z0.x;
206 c01 = -(z0.x * a01 + z0.y * b01);
207 /* = -((z0.y - z1.y) * z0.x + (z1.x - z0.x) * z0.y)
208 = (z1.x * z0.y - z1.y * z0.x) */
210 if (z1.y < z0.y)
212 a01 = -a01;
213 b01 = -b01;
214 c01 = -c01;
217 d2 = trap_epsilon (a01 * z2.x + b01 * z2.y + c01);
218 d3 = trap_epsilon (a01 * z3.x + b01 * z3.y + c01);
220 if (d2 > 0)
222 if (d3 >= 0) return -1;
223 else return 0;
225 else if (d2 == 0)
227 if (d3 > 0) return -1;
228 else if (d3 < 0) return 1;
229 else printf ("case 2 degenerate\n");
230 return 0;
232 else /* d2 < 0 */
234 if (d3 <= 0) return 1;
235 else return 0;
239 /* find line equations ax + by + c = 0 */
240 a01 = z0.y - z1.y;
241 b01 = z1.x - z0.x;
242 c01 = -(z0.x * a01 + z0.y * b01);
243 /* = -((z0.y - z1.y) * z0.x + (z1.x - z0.x) * z0.y)
244 = -(z1.x * z0.y - z1.y * z0.x) */
246 if (a01 > 0)
248 a01 = -a01;
249 b01 = -b01;
250 c01 = -c01;
252 /* so now, (a01, b01) points to the left, thus a01 * x + b01 * y + c01
253 is negative if the point lies to the right of the line */
255 d2 = trap_epsilon (a01 * z2.x + b01 * z2.y + c01);
256 d3 = trap_epsilon (a01 * z3.x + b01 * z3.y + c01);
257 if (d2 > 0)
259 if (d3 >= 0) return -1;
261 else if (d2 == 0)
263 if (d3 > 0) return -1;
264 else if (d3 < 0) return 1;
265 else
266 /*fprintf (stderr, "colinear!\n")*/;
268 else /* d2 < 0 */
270 if (d3 <= 0) return 1;
273 a23 = z2.y - z3.y;
274 b23 = z3.x - z2.x;
275 c23 = -(z2.x * a23 + z2.y * b23);
277 if (a23 > 0)
279 a23 = -a23;
280 b23 = -b23;
281 c23 = -c23;
283 d0 = trap_epsilon (a23 * z0.x + b23 * z0.y + c23);
284 d1 = trap_epsilon (a23 * z1.x + b23 * z1.y + c23);
285 if (d0 > 0)
287 if (d1 >= 0) return 1;
289 else if (d0 == 0)
291 if (d1 > 0) return 1;
292 else if (d1 < 0) return -1;
293 else
294 /*fprintf (stderr, "colinear!\n")*/;
296 else /* d0 < 0 */
298 if (d1 <= 0) return -1;
301 return 0;
304 /* similar to x_order, but to determine whether point z0 + epsilon lies to
305 the left of the line z2-z3 or to the right */
306 static int
307 x_order_2 (ArtPoint z0, ArtPoint z1, ArtPoint z2, ArtPoint z3)
309 double a23, b23, c23;
310 double d0, d1;
312 a23 = z2.y - z3.y;
313 b23 = z3.x - z2.x;
314 c23 = -(z2.x * a23 + z2.y * b23);
316 if (a23 > 0)
318 a23 = -a23;
319 b23 = -b23;
320 c23 = -c23;
323 d0 = a23 * z0.x + b23 * z0.y + c23;
325 if (d0 > EPSILON)
326 return -1;
327 else if (d0 < -EPSILON)
328 return 1;
330 d1 = a23 * z1.x + b23 * z1.y + c23;
331 if (d1 > EPSILON)
332 return -1;
333 else if (d1 < -EPSILON)
334 return 1;
336 if (z0.x == z1.x && z1.x == z2.x && z2.x == z3.x)
338 //art_dprint ("x_order_2: colinear and horizontally aligned!\n");
339 return 0;
342 if (z0.x <= z2.x && z1.x <= z2.x && z0.x <= z3.x && z1.x <= z3.x)
343 return -1;
344 if (z0.x >= z2.x && z1.x >= z2.x && z0.x >= z3.x && z1.x >= z3.x)
345 return 1;
347 //fprintf (stderr, "x_order_2: colinear!\n");
348 return 0;
351 #ifdef DEAD_CODE
352 /* Traverse the vector path, keeping it in x-sorted order.
354 This routine doesn't actually do anything - it's just here for
355 explanatory purposes. */
356 void
357 traverse (ArtSVP *vp)
359 int *active_segs;
360 int n_active_segs;
361 int *cursor;
362 int seg_idx;
363 double y;
364 int tmp1, tmp2;
365 int asi;
366 int i, j;
368 active_segs = art_new (int, vp->n_segs);
369 cursor = art_new (int, vp->n_segs);
371 n_active_segs = 0;
372 seg_idx = 0;
373 y = vp->segs[0].points[0].y;
374 while (seg_idx < vp->n_segs || n_active_segs > 0)
376 printf ("y = %g\n", y);
377 /* delete segments ending at y from active list */
378 for (i = 0; i < n_active_segs; i++)
380 asi = active_segs[i];
381 if (vp->segs[asi].n_points - 1 == cursor[asi] &&
382 vp->segs[asi].points[cursor[asi]].y == y)
384 printf ("deleting %d\n", asi);
385 n_active_segs--;
386 for (j = i; j < n_active_segs; j++)
387 active_segs[j] = active_segs[j + 1];
388 i--;
392 /* insert new segments into the active list */
393 while (seg_idx < vp->n_segs && y == vp->segs[seg_idx].points[0].y)
395 cursor[seg_idx] = 0;
396 printf ("inserting %d\n", seg_idx);
397 for (i = 0; i < n_active_segs; i++)
399 asi = active_segs[i];
400 if (x_order (vp->segs[asi].points[cursor[asi]],
401 vp->segs[asi].points[cursor[asi] + 1],
402 vp->segs[seg_idx].points[0],
403 vp->segs[seg_idx].points[1]) == -1)
404 break;
406 tmp1 = seg_idx;
407 for (j = i; j < n_active_segs; j++)
409 tmp2 = active_segs[j];
410 active_segs[j] = tmp1;
411 tmp1 = tmp2;
413 active_segs[n_active_segs] = tmp1;
414 n_active_segs++;
415 seg_idx++;
418 /* all active segs cross the y scanline (considering segs to be
419 closed on top and open on bottom) */
420 for (i = 0; i < n_active_segs; i++)
422 asi = active_segs[i];
423 printf ("%d (%g, %g) - (%g, %g) %s\n", asi,
424 vp->segs[asi].points[cursor[asi]].x,
425 vp->segs[asi].points[cursor[asi]].y,
426 vp->segs[asi].points[cursor[asi] + 1].x,
427 vp->segs[asi].points[cursor[asi] + 1].y,
428 vp->segs[asi].dir ? "v" : "^");
431 /* advance y to the next event */
432 if (n_active_segs == 0)
434 if (seg_idx < vp->n_segs)
435 y = vp->segs[seg_idx].points[0].y;
436 /* else we're done */
438 else
440 asi = active_segs[0];
441 y = vp->segs[asi].points[cursor[asi] + 1].y;
442 for (i = 1; i < n_active_segs; i++)
444 asi = active_segs[i];
445 if (y > vp->segs[asi].points[cursor[asi] + 1].y)
446 y = vp->segs[asi].points[cursor[asi] + 1].y;
448 if (seg_idx < vp->n_segs && y > vp->segs[seg_idx].points[0].y)
449 y = vp->segs[seg_idx].points[0].y;
452 /* advance cursors to reach new y */
453 for (i = 0; i < n_active_segs; i++)
455 asi = active_segs[i];
456 while (cursor[asi] < vp->segs[asi].n_points - 1 &&
457 y >= vp->segs[asi].points[cursor[asi] + 1].y)
458 cursor[asi]++;
460 printf ("\n");
462 art_free (cursor);
463 art_free (active_segs);
465 #endif
467 /* I believe that the loop will always break with i=1.
469 I think I'll want to change this from a simple sorted list to a
470 modified stack. ips[*][0] will get its own data structure, and
471 ips[*] will in general only be allocated if there is an intersection.
472 Finally, the segment can be traced through the initial point
473 (formerly ips[*][0]), backwards through the stack, and finally
474 to cursor + 1.
476 This change should cut down on allocation bandwidth, and also
477 eliminate the iteration through n_ipl below.
480 static void
481 insert_ip (int seg_i, int *n_ips, int *n_ips_max, ArtPoint **ips, ArtPoint ip)
483 int i;
484 ArtPoint tmp1, tmp2;
485 int n_ipl;
486 ArtPoint *ipl;
488 n_ipl = n_ips[seg_i]++;
489 if (n_ipl == n_ips_max[seg_i])
490 art_expand (ips[seg_i], ArtPoint, n_ips_max[seg_i]);
491 ipl = ips[seg_i];
492 for (i = 1; i < n_ipl; i++)
493 if (ipl[i].y > ip.y)
494 break;
495 tmp1 = ip;
496 for (; i <= n_ipl; i++)
498 tmp2 = ipl[i];
499 ipl[i] = tmp1;
500 tmp1 = tmp2;
504 /* test active segment (i - 1) against i for intersection, if
505 so, add intersection point to both ips lists. */
506 static void
507 intersect_neighbors (int i, int *active_segs,
508 int *n_ips, int *n_ips_max, ArtPoint **ips,
509 int *cursor, ArtSVP *vp)
511 ArtPoint z0, z1, z2, z3;
512 int asi01, asi23;
513 ArtPoint ip;
515 asi01 = active_segs[i - 1];
517 z0 = ips[asi01][0];
518 if (n_ips[asi01] == 1)
519 z1 = vp->segs[asi01].points[cursor[asi01] + 1];
520 else
521 z1 = ips[asi01][1];
523 asi23 = active_segs[i];
525 z2 = ips[asi23][0];
526 if (n_ips[asi23] == 1)
527 z3 = vp->segs[asi23].points[cursor[asi23] + 1];
528 else
529 z3 = ips[asi23][1];
531 if (intersect_lines (z0, z1, z2, z3, &ip))
533 #ifdef VERBOSE
534 printf ("new intersection point: (%g, %g)\n", ip.x, ip.y);
535 #endif
536 insert_ip (asi01, n_ips, n_ips_max, ips, ip);
537 insert_ip (asi23, n_ips, n_ips_max, ips, ip);
541 /* Add a new point to a segment in the svp.
543 Here, we also check to make sure that the segments satisfy nocross.
544 However, this is only valuable for debugging, and could possibly be
545 removed.
547 static void
548 svp_add_point (ArtSVP *svp, int *n_points_max,
549 ArtPoint p, int *seg_map, int *active_segs, int n_active_segs,
550 int i)
552 int asi, asi_left, asi_right;
553 int n_points, n_points_left, n_points_right;
554 ArtSVPSeg *seg;
556 asi = seg_map[active_segs[i]];
557 seg = &svp->segs[asi];
558 n_points = seg->n_points;
559 /* find out whether neighboring segments share a point */
560 if (i > 0)
562 asi_left = seg_map[active_segs[i - 1]];
563 n_points_left = svp->segs[asi_left].n_points;
564 if (n_points_left > 1 &&
565 PT_EQ (svp->segs[asi_left].points[n_points_left - 2],
566 svp->segs[asi].points[n_points - 1]))
568 /* ok, new vector shares a top point with segment to the left -
569 now, check that it satisfies ordering invariant */
570 if (x_order (svp->segs[asi_left].points[n_points_left - 2],
571 svp->segs[asi_left].points[n_points_left - 1],
572 svp->segs[asi].points[n_points - 1],
573 p) < 1)
576 #ifdef VERBOSE
577 printf ("svp_add_point: cross on left!\n");
578 #endif
583 if (i + 1 < n_active_segs)
585 asi_right = seg_map[active_segs[i + 1]];
586 n_points_right = svp->segs[asi_right].n_points;
587 if (n_points_right > 1 &&
588 PT_EQ (svp->segs[asi_right].points[n_points_right - 2],
589 svp->segs[asi].points[n_points - 1]))
591 /* ok, new vector shares a top point with segment to the right -
592 now, check that it satisfies ordering invariant */
593 if (x_order (svp->segs[asi_right].points[n_points_right - 2],
594 svp->segs[asi_right].points[n_points_right - 1],
595 svp->segs[asi].points[n_points - 1],
596 p) > -1)
598 #ifdef VERBOSE
599 printf ("svp_add_point: cross on right!\n");
600 #endif
604 if (n_points_max[asi] == n_points)
605 art_expand (seg->points, ArtPoint, n_points_max[asi]);
606 seg->points[n_points] = p;
607 if (p.x < seg->bbox.x0)
608 seg->bbox.x0 = p.x;
609 else if (p.x > seg->bbox.x1)
610 seg->bbox.x1 = p.x;
611 seg->bbox.y1 = p.y;
612 seg->n_points++;
615 #if 0
616 /* find where the segment (currently at i) is supposed to go, and return
617 the target index - if equal to i, then there is no crossing problem.
619 "Where it is supposed to go" is defined as following:
621 Delete element i, re-insert at position target (bumping everything
622 target and greater to the right).
624 static int
625 find_crossing (int i, int *active_segs, int n_active_segs,
626 int *cursor, ArtPoint **ips, int *n_ips, ArtSVP *vp)
628 int asi, asi_left, asi_right;
629 ArtPoint p0, p1;
630 ArtPoint p0l, p1l;
631 ArtPoint p0r, p1r;
632 int target;
634 asi = active_segs[i];
635 p0 = ips[asi][0];
636 if (n_ips[asi] == 1)
637 p1 = vp->segs[asi].points[cursor[asi] + 1];
638 else
639 p1 = ips[asi][1];
641 for (target = i; target > 0; target--)
643 asi_left = active_segs[target - 1];
644 p0l = ips[asi_left][0];
645 if (n_ips[asi_left] == 1)
646 p1l = vp->segs[asi_left].points[cursor[asi_left] + 1];
647 else
648 p1l = ips[asi_left][1];
649 if (!PT_EQ (p0, p0l))
650 break;
652 #ifdef VERBOSE
653 printf ("point matches on left (%g, %g) - (%g, %g) x (%g, %g) - (%g, %g)!\n",
654 p0l.x, p0l.y, p1l.x, p1l.y, p0.x, p0.y, p1.x, p1.y);
655 #endif
656 if (x_order (p0l, p1l, p0, p1) == 1)
657 break;
659 #ifdef VERBOSE
660 printf ("scanning to the left (i=%d, target=%d)\n", i, target);
661 #endif
664 if (target < i) return target;
666 for (; target < n_active_segs - 1; target++)
668 asi_right = active_segs[target + 1];
669 p0r = ips[asi_right][0];
670 if (n_ips[asi_right] == 1)
671 p1r = vp->segs[asi_right].points[cursor[asi_right] + 1];
672 else
673 p1r = ips[asi_right][1];
674 if (!PT_EQ (p0, p0r))
675 break;
677 #ifdef VERBOSE
678 printf ("point matches on left (%g, %g) - (%g, %g) x (%g, %g) - (%g, %g)!\n",
679 p0.x, p0.y, p1.x, p1.y, p0r.x, p0r.y, p1r.x, p1r.y);
680 #endif
681 if (x_order (p0r, p1r, p0, p1) == 1)
682 break;
684 #ifdef VERBOSE
685 printf ("scanning to the right (i=%d, target=%d)\n", i, target);
686 #endif
689 return target;
691 #endif
693 /* This routine handles the case where the segment changes its position
694 in the active segment list. Generally, this will happen when the
695 segment (defined by i and cursor) shares a top point with a neighbor,
696 but breaks the ordering invariant.
698 Essentially, this routine sorts the lines [start..end), all of which
699 share a top point. This is implemented as your basic insertion sort.
701 This routine takes care of intersecting the appropriate neighbors,
702 as well.
704 A first argument of -1 immediately returns, which helps reduce special
705 casing in the main unwind routine.
707 static void
708 fix_crossing (int start, int end, int *active_segs, int n_active_segs,
709 int *cursor, ArtPoint **ips, int *n_ips, int *n_ips_max,
710 ArtSVP *vp, int *seg_map,
711 ArtSVP **p_new_vp, int *pn_segs_max,
712 int **pn_points_max)
714 int i, j;
715 int target;
716 int asi, asj;
717 ArtPoint p0i, p1i;
718 ArtPoint p0j, p1j;
719 int swap = 0;
720 #ifdef VERBOSE
721 int k;
722 #endif
723 ArtPoint *pts;
725 #ifdef VERBOSE
726 printf ("fix_crossing: [%d..%d)", start, end);
727 for (k = 0; k < n_active_segs; k++)
728 printf (" %d", active_segs[k]);
729 printf ("\n");
730 #endif
732 if (start == -1)
733 return;
735 for (i = start + 1; i < end; i++)
738 asi = active_segs[i];
739 if (cursor[asi] < vp->segs[asi].n_points - 1) {
740 p0i = ips[asi][0];
741 if (n_ips[asi] == 1)
742 p1i = vp->segs[asi].points[cursor[asi] + 1];
743 else
744 p1i = ips[asi][1];
746 for (j = i - 1; j >= start; j--)
748 asj = active_segs[j];
749 if (cursor[asj] < vp->segs[asj].n_points - 1)
751 p0j = ips[asj][0];
752 if (n_ips[asj] == 1)
753 p1j = vp->segs[asj].points[cursor[asj] + 1];
754 else
755 p1j = ips[asj][1];
757 /* we _hope_ p0i = p0j */
758 if (x_order_2 (p0j, p1j, p0i, p1i) == -1)
759 break;
763 target = j + 1;
764 /* target is where active_seg[i] _should_ be in active_segs */
766 if (target != i)
768 swap = 1;
770 #ifdef VERBOSE
771 printf ("fix_crossing: at %i should be %i\n", i, target);
772 #endif
774 /* let's close off all relevant segments */
775 for (j = i; j >= target; j--)
777 asi = active_segs[j];
778 /* First conjunct: this isn't the last point in the original
779 segment.
781 Second conjunct: this isn't the first point in the new
782 segment (i.e. already broken).
784 if (cursor[asi] < vp->segs[asi].n_points - 1 &&
785 (*p_new_vp)->segs[seg_map[asi]].n_points != 1)
787 int seg_num;
788 /* so break here */
789 #ifdef VERBOSE
790 printf ("closing off %d\n", j);
791 #endif
793 pts = art_new (ArtPoint, 16);
794 pts[0] = ips[asi][0];
795 seg_num = art_svp_add_segment (p_new_vp, pn_segs_max,
796 pn_points_max,
797 1, vp->segs[asi].dir,
798 pts,
799 NULL);
800 (*pn_points_max)[seg_num] = 16;
801 seg_map[asi] = seg_num;
805 /* now fix the ordering in active_segs */
806 asi = active_segs[i];
807 for (j = i; j > target; j--)
808 active_segs[j] = active_segs[j - 1];
809 active_segs[j] = asi;
813 if (swap && start > 0)
815 int as_start;
817 as_start = active_segs[start];
818 if (cursor[as_start] < vp->segs[as_start].n_points)
820 #ifdef VERBOSE
821 printf ("checking intersection of %d, %d\n", start - 1, start);
822 #endif
823 intersect_neighbors (start, active_segs,
824 n_ips, n_ips_max, ips,
825 cursor, vp);
829 if (swap && end < n_active_segs)
831 int as_end;
833 as_end = active_segs[end - 1];
834 if (cursor[as_end] < vp->segs[as_end].n_points)
836 #ifdef VERBOSE
837 printf ("checking intersection of %d, %d\n", end - 1, end);
838 #endif
839 intersect_neighbors (end, active_segs,
840 n_ips, n_ips_max, ips,
841 cursor, vp);
844 if (swap)
846 #ifdef VERBOSE
847 printf ("fix_crossing return: [%d..%d)", start, end);
848 for (k = 0; k < n_active_segs; k++)
849 printf (" %d", active_segs[k]);
850 printf ("\n");
851 #endif
855 /* Return a new sorted vector that covers the same area as the
856 argument, but which satisfies the nocross invariant.
858 Basically, this routine works by finding the intersection points,
859 and cutting the segments at those points.
861 Status of this routine:
863 Basic correctness: Seems ok.
865 Numerical stability: known problems in the case of points falling
866 on lines, and colinear lines. For actual use, randomly perturbing
867 the vertices is currently recommended.
869 Speed: pretty good, although a more efficient priority queue, as
870 well as bbox culling of potential intersections, are two
871 optimizations that could help.
873 Precision: pretty good, although the numerical stability problems
874 make this routine unsuitable for precise calculations of
875 differences.
879 /* Here is a more detailed description of the algorithm. It follows
880 roughly the structure of traverse (above), but is obviously quite
881 a bit more complex.
883 Here are a few important data structures:
885 A new sorted vector path (new_svp).
887 For each (active) segment in the original, a list of intersection
888 points.
890 Of course, the original being traversed.
892 The following invariants hold (in addition to the invariants
893 of the traverse procedure).
895 The new sorted vector path lies entirely above the y scan line.
897 The new sorted vector path keeps the nocross invariant.
899 For each active segment, the y scan line crosses the line from the
900 first to the second of the intersection points (where the second
901 point is cursor + 1 if there is only one intersection point).
903 The list of intersection points + the (cursor + 1) point is kept
904 in nondecreasing y order.
906 Of the active segments, none of the lines from first to second
907 intersection point cross the 1st ip..2nd ip line of the left or
908 right neighbor. (However, such a line may cross further
909 intersection points of the neighbors, or segments past the
910 immediate neighbors).
912 Of the active segments, all lines from 1st ip..2nd ip are in
913 strictly increasing x_order (this is very similar to the invariant
914 of the traverse procedure, but is explicitly stated here in terms
915 of ips). (this basically says that nocross holds on the active
916 segments)
918 The combination of the new sorted vector path, the path through all
919 the intersection points to cursor + 1, and [cursor + 1, n_points)
920 covers the same area as the argument.
922 Another important data structure is mapping from original segment
923 number to new segment number.
925 The algorithm is perhaps best understood as advancing the cursors
926 while maintaining these invariants. Here's roughly how it's done.
928 When deleting segments from the active list, those segments are added
929 to the new sorted vector path. In addition, the neighbors may intersect
930 each other, so they are intersection tested (see below).
932 When inserting new segments, they are intersection tested against
933 their neighbors. The top point of the segment becomes the first
934 intersection point.
936 Advancing the cursor is just a bit different from the traverse
937 routine, as the cursor may advance through the intersection points
938 as well. Only when there is a single intersection point in the list
939 does the cursor advance in the original segment. In either case,
940 the new vector is intersection tested against both neighbors. It
941 also causes the vector over which the cursor is advancing to be
942 added to the new svp.
944 Two steps need further clarification:
946 Intersection testing: the 1st ip..2nd ip lines of the neighbors
947 are tested to see if they cross (using intersect_lines). If so,
948 then the intersection point is added to the ip list of both
949 segments, maintaining the invariant that the list of intersection
950 points is nondecreasing in y).
952 Adding vector to new svp: if the new vector shares a top x
953 coordinate with another vector, then it is checked to see whether
954 it is in order. If not, then both segments are "broken," and then
955 restarted. Note: in the case when both segments are in the same
956 order, they may simply be swapped without breaking.
958 For the time being, I'm going to put some of these operations into
959 subroutines. If it turns out to be a performance problem, I could
960 try to reorganize the traverse procedure so that each is only
961 called once, and inline them. But if it's not a performance
962 problem, I'll just keep it this way, because it will probably help
963 to make the code clearer, and I believe this code could use all the
964 clarity it can get. */
966 * art_svp_uncross: Resolve self-intersections of an svp.
967 * @vp: The original svp.
969 * Finds all the intersections within @vp, and constructs a new svp
970 * with new points added at these intersections.
972 * This routine needs to be redone from scratch with numerical robustness
973 * in mind. I'm working on it.
975 * Return value: The new svp.
977 ArtSVP *
978 art_svp_uncross (ArtSVP *vp)
980 int *active_segs;
981 int n_active_segs;
982 int *cursor;
983 int seg_idx;
984 double y;
985 int tmp1, tmp2;
986 int asi;
987 int i, j;
988 /* new data structures */
989 /* intersection points; invariant: *ips[i] is only allocated if
990 i is active */
991 int *n_ips, *n_ips_max;
992 ArtPoint **ips;
993 /* new sorted vector path */
994 int n_segs_max, seg_num;
995 ArtSVP *new_vp;
996 int *n_points_max;
997 /* mapping from argument to new segment numbers - again, only valid
998 if active */
999 int *seg_map;
1000 double y_curs;
1001 ArtPoint p_curs;
1002 int first_share;
1003 double share_x;
1004 ArtPoint *pts;
1006 n_segs_max = 16;
1007 new_vp = (ArtSVP *)art_alloc (sizeof(ArtSVP) +
1008 (n_segs_max - 1) * sizeof(ArtSVPSeg));
1009 new_vp->n_segs = 0;
1011 if (vp->n_segs == 0)
1012 return new_vp;
1014 active_segs = art_new (int, vp->n_segs);
1015 cursor = art_new (int, vp->n_segs);
1017 seg_map = art_new (int, vp->n_segs);
1018 n_ips = art_new (int, vp->n_segs);
1019 n_ips_max = art_new (int, vp->n_segs);
1020 ips = art_new (ArtPoint *, vp->n_segs);
1022 n_points_max = art_new (int, n_segs_max);
1024 n_active_segs = 0;
1025 seg_idx = 0;
1026 y = vp->segs[0].points[0].y;
1027 while (seg_idx < vp->n_segs || n_active_segs > 0)
1029 #ifdef VERBOSE
1030 printf ("y = %g\n", y);
1031 #endif
1033 /* maybe move deletions to end of loop (to avoid so much special
1034 casing on the end of a segment)? */
1036 /* delete segments ending at y from active list */
1037 for (i = 0; i < n_active_segs; i++)
1039 asi = active_segs[i];
1040 if (vp->segs[asi].n_points - 1 == cursor[asi] &&
1041 vp->segs[asi].points[cursor[asi]].y == y)
1045 #ifdef VERBOSE
1046 printf ("deleting %d\n", asi);
1047 #endif
1048 art_free (ips[asi]);
1049 n_active_segs--;
1050 for (j = i; j < n_active_segs; j++)
1051 active_segs[j] = active_segs[j + 1];
1052 if (i < n_active_segs)
1053 asi = active_segs[i];
1054 else
1055 break;
1057 while (vp->segs[asi].n_points - 1 == cursor[asi] &&
1058 vp->segs[asi].points[cursor[asi]].y == y);
1060 /* test intersection of neighbors */
1061 if (i > 0 && i < n_active_segs)
1062 intersect_neighbors (i, active_segs,
1063 n_ips, n_ips_max, ips,
1064 cursor, vp);
1066 i--;
1070 /* insert new segments into the active list */
1071 while (seg_idx < vp->n_segs && y == vp->segs[seg_idx].points[0].y)
1073 #ifdef VERBOSE
1074 printf ("inserting %d\n", seg_idx);
1075 #endif
1076 cursor[seg_idx] = 0;
1077 for (i = 0; i < n_active_segs; i++)
1079 asi = active_segs[i];
1080 if (x_order_2 (vp->segs[seg_idx].points[0],
1081 vp->segs[seg_idx].points[1],
1082 vp->segs[asi].points[cursor[asi]],
1083 vp->segs[asi].points[cursor[asi] + 1]) == -1)
1084 break;
1087 /* Create and initialize the intersection points data structure */
1088 n_ips[seg_idx] = 1;
1089 n_ips_max[seg_idx] = 2;
1090 ips[seg_idx] = art_new (ArtPoint, n_ips_max[seg_idx]);
1091 ips[seg_idx][0] = vp->segs[seg_idx].points[0];
1093 /* Start a new segment in the new vector path */
1094 pts = art_new (ArtPoint, 16);
1095 pts[0] = vp->segs[seg_idx].points[0];
1096 seg_num = art_svp_add_segment (&new_vp, &n_segs_max,
1097 &n_points_max,
1098 1, vp->segs[seg_idx].dir,
1099 pts,
1100 NULL);
1101 n_points_max[seg_num] = 16;
1102 seg_map[seg_idx] = seg_num;
1104 tmp1 = seg_idx;
1105 for (j = i; j < n_active_segs; j++)
1107 tmp2 = active_segs[j];
1108 active_segs[j] = tmp1;
1109 tmp1 = tmp2;
1111 active_segs[n_active_segs] = tmp1;
1112 n_active_segs++;
1114 if (i > 0)
1115 intersect_neighbors (i, active_segs,
1116 n_ips, n_ips_max, ips,
1117 cursor, vp);
1119 if (i + 1 < n_active_segs)
1120 intersect_neighbors (i + 1, active_segs,
1121 n_ips, n_ips_max, ips,
1122 cursor, vp);
1124 seg_idx++;
1127 /* all active segs cross the y scanline (considering segs to be
1128 closed on top and open on bottom) */
1129 #ifdef VERBOSE
1130 for (i = 0; i < n_active_segs; i++)
1132 asi = active_segs[i];
1133 printf ("%d ", asi);
1134 for (j = 0; j < n_ips[asi]; j++)
1135 printf ("(%g, %g) - ",
1136 ips[asi][j].x,
1137 ips[asi][j].y);
1138 printf ("(%g, %g) %s\n",
1139 vp->segs[asi].points[cursor[asi] + 1].x,
1140 vp->segs[asi].points[cursor[asi] + 1].y,
1141 vp->segs[asi].dir ? "v" : "^");
1143 #endif
1145 /* advance y to the next event
1146 Note: this is quadratic. We'd probably get decent constant
1147 factor speed improvement by caching the y_curs values. */
1148 if (n_active_segs == 0)
1150 if (seg_idx < vp->n_segs)
1151 y = vp->segs[seg_idx].points[0].y;
1152 /* else we're done */
1154 else
1156 asi = active_segs[0];
1157 if (n_ips[asi] == 1)
1158 y = vp->segs[asi].points[cursor[asi] + 1].y;
1159 else
1160 y = ips[asi][1].y;
1161 for (i = 1; i < n_active_segs; i++)
1163 asi = active_segs[i];
1164 if (n_ips[asi] == 1)
1165 y_curs = vp->segs[asi].points[cursor[asi] + 1].y;
1166 else
1167 y_curs = ips[asi][1].y;
1168 if (y > y_curs)
1169 y = y_curs;
1171 if (seg_idx < vp->n_segs && y > vp->segs[seg_idx].points[0].y)
1172 y = vp->segs[seg_idx].points[0].y;
1175 first_share = -1;
1176 share_x = 0; /* to avoid gcc warning, although share_x is never
1177 used when first_share is -1 */
1178 /* advance cursors to reach new y */
1179 for (i = 0; i < n_active_segs; i++)
1181 asi = active_segs[i];
1182 if (n_ips[asi] == 1)
1183 p_curs = vp->segs[asi].points[cursor[asi] + 1];
1184 else
1185 p_curs = ips[asi][1];
1186 if (p_curs.y == y)
1188 svp_add_point (new_vp, n_points_max,
1189 p_curs, seg_map, active_segs, n_active_segs, i);
1191 n_ips[asi]--;
1192 for (j = 0; j < n_ips[asi]; j++)
1193 ips[asi][j] = ips[asi][j + 1];
1195 if (n_ips[asi] == 0)
1197 ips[asi][0] = p_curs;
1198 n_ips[asi] = 1;
1199 cursor[asi]++;
1202 if (first_share < 0 || p_curs.x != share_x)
1204 /* this is where crossings are detected, and if
1205 found, the active segments switched around. */
1207 fix_crossing (first_share, i,
1208 active_segs, n_active_segs,
1209 cursor, ips, n_ips, n_ips_max, vp, seg_map,
1210 &new_vp,
1211 &n_segs_max, &n_points_max);
1213 first_share = i;
1214 share_x = p_curs.x;
1217 if (cursor[asi] < vp->segs[asi].n_points - 1)
1220 if (i > 0)
1221 intersect_neighbors (i, active_segs,
1222 n_ips, n_ips_max, ips,
1223 cursor, vp);
1225 if (i + 1 < n_active_segs)
1226 intersect_neighbors (i + 1, active_segs,
1227 n_ips, n_ips_max, ips,
1228 cursor, vp);
1231 else
1233 /* not on a cursor point */
1234 fix_crossing (first_share, i,
1235 active_segs, n_active_segs,
1236 cursor, ips, n_ips, n_ips_max, vp, seg_map,
1237 &new_vp,
1238 &n_segs_max, &n_points_max);
1239 first_share = -1;
1243 /* fix crossing on last shared group */
1244 fix_crossing (first_share, i,
1245 active_segs, n_active_segs,
1246 cursor, ips, n_ips, n_ips_max, vp, seg_map,
1247 &new_vp,
1248 &n_segs_max, &n_points_max);
1250 #ifdef VERBOSE
1251 printf ("\n");
1252 #endif
1255 /* not necessary to sort, new segments only get added at y, which
1256 increases monotonically */
1257 #if 0
1258 qsort (&new_vp->segs, new_vp->n_segs, sizeof (svp_seg), svp_seg_compare);
1260 int k;
1261 for (k = 0; k < new_vp->n_segs - 1; k++)
1263 printf ("(%g, %g) - (%g, %g) %s (%g, %g) - (%g, %g)\n",
1264 new_vp->segs[k].points[0].x,
1265 new_vp->segs[k].points[0].y,
1266 new_vp->segs[k].points[1].x,
1267 new_vp->segs[k].points[1].y,
1268 svp_seg_compare (&new_vp->segs[k], &new_vp->segs[k + 1]) > 1 ? ">": "<",
1269 new_vp->segs[k + 1].points[0].x,
1270 new_vp->segs[k + 1].points[0].y,
1271 new_vp->segs[k + 1].points[1].x,
1272 new_vp->segs[k + 1].points[1].y);
1275 #endif
1277 art_free (n_points_max);
1278 art_free (seg_map);
1279 art_free (n_ips_max);
1280 art_free (n_ips);
1281 art_free (ips);
1282 art_free (cursor);
1283 art_free (active_segs);
1285 return new_vp;
1288 #define noVERBOSE
1290 /* Rewind a svp satisfying the nocross invariant.
1292 The winding number of a segment is defined as the winding number of
1293 the points to the left while travelling in the direction of the
1294 segment. Therefore it preincrements and postdecrements as a scan
1295 line is traversed from left to right.
1297 Status of this routine:
1299 Basic correctness: Was ok in gfonted. However, this code does not
1300 yet compute bboxes for the resulting svp segs.
1302 Numerical stability: known problems in the case of horizontal
1303 segments in polygons with any complexity. For actual use, randomly
1304 perturbing the vertices is recommended.
1306 Speed: good.
1308 Precision: good, except that no attempt is made to remove "hair".
1309 Doing random perturbation just makes matters worse.
1313 * art_svp_rewind_uncrossed: Rewind an svp satisfying the nocross invariant.
1314 * @vp: The original svp.
1315 * @rule: The winding rule.
1317 * Creates a new svp with winding number of 0 or 1 everywhere. The @rule
1318 * argument specifies a rule for how winding numbers in the original
1319 * @vp map to the winding numbers in the result.
1321 * With @rule == ART_WIND_RULE_NONZERO, the resulting svp has a
1322 * winding number of 1 where @vp has a nonzero winding number.
1324 * With @rule == ART_WIND_RULE_INTERSECT, the resulting svp has a
1325 * winding number of 1 where @vp has a winding number greater than
1326 * 1. It is useful for computing intersections.
1328 * With @rule == ART_WIND_RULE_ODDEVEN, the resulting svp has a
1329 * winding number of 1 where @vp has an odd winding number. It is
1330 * useful for implementing the even-odd winding rule of the
1331 * PostScript imaging model.
1333 * With @rule == ART_WIND_RULE_POSITIVE, the resulting svp has a
1334 * winding number of 1 where @vp has a positive winding number. It is
1335 * useful for implementing asymmetric difference.
1337 * This routine needs to be redone from scratch with numerical robustness
1338 * in mind. I'm working on it.
1340 * Return value: The new svp.
1342 ArtSVP *
1343 art_svp_rewind_uncrossed (ArtSVP *vp, ArtWindRule rule)
1345 int *active_segs;
1346 int n_active_segs;
1347 int *cursor;
1348 int seg_idx;
1349 double y;
1350 int tmp1, tmp2;
1351 int asi;
1352 int i, j;
1354 ArtSVP *new_vp;
1355 int n_segs_max;
1356 int *winding;
1357 int left_wind;
1358 int wind;
1359 int keep, invert;
1361 #ifdef VERBOSE
1362 print_svp (vp);
1363 #endif
1364 n_segs_max = 16;
1365 new_vp = (ArtSVP *)art_alloc (sizeof(ArtSVP) +
1366 (n_segs_max - 1) * sizeof(ArtSVPSeg));
1367 new_vp->n_segs = 0;
1369 if (vp->n_segs == 0)
1370 return new_vp;
1372 winding = art_new (int, vp->n_segs);
1374 active_segs = art_new (int, vp->n_segs);
1375 cursor = art_new (int, vp->n_segs);
1377 n_active_segs = 0;
1378 seg_idx = 0;
1379 y = vp->segs[0].points[0].y;
1380 while (seg_idx < vp->n_segs || n_active_segs > 0)
1382 #ifdef VERBOSE
1383 printf ("y = %g\n", y);
1384 #endif
1385 /* delete segments ending at y from active list */
1386 for (i = 0; i < n_active_segs; i++)
1388 asi = active_segs[i];
1389 if (vp->segs[asi].n_points - 1 == cursor[asi] &&
1390 vp->segs[asi].points[cursor[asi]].y == y)
1392 #ifdef VERBOSE
1393 printf ("deleting %d\n", asi);
1394 #endif
1395 n_active_segs--;
1396 for (j = i; j < n_active_segs; j++)
1397 active_segs[j] = active_segs[j + 1];
1398 i--;
1402 /* insert new segments into the active list */
1403 while (seg_idx < vp->n_segs && y == vp->segs[seg_idx].points[0].y)
1405 #ifdef VERBOSE
1406 printf ("inserting %d\n", seg_idx);
1407 #endif
1408 cursor[seg_idx] = 0;
1409 for (i = 0; i < n_active_segs; i++)
1411 asi = active_segs[i];
1412 if (x_order_2 (vp->segs[seg_idx].points[0],
1413 vp->segs[seg_idx].points[1],
1414 vp->segs[asi].points[cursor[asi]],
1415 vp->segs[asi].points[cursor[asi] + 1]) == -1)
1416 break;
1419 /* Determine winding number for this segment */
1420 if (i == 0)
1421 left_wind = 0;
1422 else if (vp->segs[active_segs[i - 1]].dir)
1423 left_wind = winding[active_segs[i - 1]];
1424 else
1425 left_wind = winding[active_segs[i - 1]] - 1;
1427 if (vp->segs[seg_idx].dir)
1428 wind = left_wind + 1;
1429 else
1430 wind = left_wind;
1432 winding[seg_idx] = wind;
1434 switch (rule)
1436 case ART_WIND_RULE_NONZERO:
1437 keep = (wind == 1 || wind == 0);
1438 invert = (wind == 0);
1439 break;
1440 case ART_WIND_RULE_INTERSECT:
1441 keep = (wind == 2);
1442 invert = 0;
1443 break;
1444 case ART_WIND_RULE_ODDEVEN:
1445 keep = 1;
1446 invert = !(wind & 1);
1447 break;
1448 case ART_WIND_RULE_POSITIVE:
1449 keep = (wind == 1);
1450 invert = 0;
1451 break;
1452 default:
1453 keep = 0;
1454 invert = 0;
1455 break;
1458 if (keep)
1460 ArtPoint *points, *new_points;
1461 int n_points;
1462 int new_dir;
1464 #ifdef VERBOSE
1465 printf ("keeping segment %d\n", seg_idx);
1466 #endif
1467 n_points = vp->segs[seg_idx].n_points;
1468 points = vp->segs[seg_idx].points;
1469 new_points = art_new (ArtPoint, n_points);
1470 memcpy (new_points, points, n_points * sizeof (ArtPoint));
1471 new_dir = vp->segs[seg_idx].dir ^ invert;
1472 art_svp_add_segment (&new_vp, &n_segs_max,
1473 NULL,
1474 n_points, new_dir, new_points,
1475 &vp->segs[seg_idx].bbox);
1478 tmp1 = seg_idx;
1479 for (j = i; j < n_active_segs; j++)
1481 tmp2 = active_segs[j];
1482 active_segs[j] = tmp1;
1483 tmp1 = tmp2;
1485 active_segs[n_active_segs] = tmp1;
1486 n_active_segs++;
1487 seg_idx++;
1490 #ifdef VERBOSE
1491 /* all active segs cross the y scanline (considering segs to be
1492 closed on top and open on bottom) */
1493 for (i = 0; i < n_active_segs; i++)
1495 asi = active_segs[i];
1496 printf ("%d:%d (%g, %g) - (%g, %g) %s %d\n", asi,
1497 cursor[asi],
1498 vp->segs[asi].points[cursor[asi]].x,
1499 vp->segs[asi].points[cursor[asi]].y,
1500 vp->segs[asi].points[cursor[asi] + 1].x,
1501 vp->segs[asi].points[cursor[asi] + 1].y,
1502 vp->segs[asi].dir ? "v" : "^",
1503 winding[asi]);
1505 #endif
1507 /* advance y to the next event */
1508 if (n_active_segs == 0)
1510 if (seg_idx < vp->n_segs)
1511 y = vp->segs[seg_idx].points[0].y;
1512 /* else we're done */
1514 else
1516 asi = active_segs[0];
1517 y = vp->segs[asi].points[cursor[asi] + 1].y;
1518 for (i = 1; i < n_active_segs; i++)
1520 asi = active_segs[i];
1521 if (y > vp->segs[asi].points[cursor[asi] + 1].y)
1522 y = vp->segs[asi].points[cursor[asi] + 1].y;
1524 if (seg_idx < vp->n_segs && y > vp->segs[seg_idx].points[0].y)
1525 y = vp->segs[seg_idx].points[0].y;
1528 /* advance cursors to reach new y */
1529 for (i = 0; i < n_active_segs; i++)
1531 asi = active_segs[i];
1532 while (cursor[asi] < vp->segs[asi].n_points - 1 &&
1533 y >= vp->segs[asi].points[cursor[asi] + 1].y)
1534 cursor[asi]++;
1536 #ifdef VERBOSE
1537 printf ("\n");
1538 #endif
1540 art_free (cursor);
1541 art_free (active_segs);
1542 art_free (winding);
1544 return new_vp;