More stackup changes
[geda-pcb/pcjc2/v2.git] / src / polygon1.c
blob6b7440daadb4381a0ad14d9c08155e4703097068
1 /*!
2 * \file src/polygon1.c
4 * \brief Polygon clipping functions.
6 * harry eaton implemented the algorithm described in "A Closed Set of
7 * Algorithms for Performing Set Operations on Polygonal Regions in the
8 * Plane" which the original code did not do.
10 * I also modified it for integer coordinates and faster computation.
12 * The license for this modified copy was switched to the GPL per term
13 * (3) of the original LGPL license.
15 * <hr>
17 * <h1><b>Copyright.</b></h1>\n
19 * Copyright (C) 2006 harry eaton
21 * based on:
23 * poly_Boolean: a polygon clip library
25 * Copyright (C) 1997 Alexey Nikitin, Michael Leonov
27 * (also the authors of the paper describing the actual algorithm)
29 * leonov@propro.iis.nsk.su
31 * in turn based on:
33 * nclip: a polygon clip library
35 * Copyright (C) 1993 Klamer Schutte
37 * All cases where original (Klamer Schutte) code is present are marked.
39 * This program is free software; you can redistribute it and/or
40 * modify it under the terms of the GNU General Public
41 * License as published by the Free Software Foundation; either
42 * version 2 of the License, or (at your option) any later version.
44 * This program is distributed in the hope that it will be useful,
45 * but WITHOUT ANY WARRANTY; without even the implied warranty of
46 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
47 * General Public License for more details.
49 * You should have received a copy of the GNU General Public
50 * License along with this program; if not, write to the Free
51 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
53 * <hr>
55 * \note How about expanding polygons so that edges can be arcs rather
56 * than lines.\n
57 * Consider using the third coordinate to store the radius of the arc.\n
58 * The arc would pass through the vertex points.\n
59 * Positive radius would indicate the arc bows left (center on right of
60 * P1-P2 path).\n
61 * Negative radius would put the center on the other side.\n
62 * 0 radius would mean the edge is a line instead of arc.\n
63 * The intersections of the two circles centered at the vertex points
64 * would determine the two possible arc centers.\n
65 * If P2.x > P1.x then the center with smaller Y is selected for
66 * positive r.\n
67 * If P2.y > P1.y then the center with greate X is selected for
68 * positive r.\n
69 * The vec_inters2() routine would then need to handle line-line
70 * line-arc and arc-arc intersections.\n
71 * Perhaps reverse tracing the arc would require look-ahead to check
72 * for arcs
75 //#undef NDEBUG
76 #include <assert.h>
77 #include <stdlib.h>
78 #include <stdio.h>
79 #include <setjmp.h>
80 #include <math.h>
81 #include <string.h>
83 #include "global.h"
84 #include "pcb-printf.h"
85 #include "rtree.h"
86 #include "heap.h"
87 #include "pcb-printf.h"
88 #include "polygon.h" // FOR POLY_CIRC_SEGS
90 #define ROUND(a) (long)((a) > 0 ? ((a) + 0.5) : ((a) - 0.5))
92 #define EPSILON (1E-8)
93 #define IsZero(a, b) (fabs((a) - (b)) < EPSILON)
95 /* L o n g V e c t o r S t u f f */
97 #define Vcopy(a,b) {(a)[0]=(b)[0];(a)[1]=(b)[1];}
98 int vect_equal (Vector v1, Vector v2);
99 void vect_init (Vector v, double x, double y);
100 void vect_sub (Vector res, Vector v2, Vector v3);
102 void vect_min (Vector res, Vector v2, Vector v3);
103 void vect_max (Vector res, Vector v2, Vector v3);
105 double vect_dist2 (Vector v1, Vector v2);
106 double vect_det2 (Vector v1, Vector v2);
107 double vect_len2 (Vector v1);
109 int vect_inters2 (Vector A, Vector B, Vector C, Vector D, Vector S1,
110 Vector S2);
111 int vect_inters2_fp (double p1[2], double p2[2],
112 double q1[2], double q2[2],
113 double S1[2], double S2[2]);
115 /* note that a vertex v's Flags.status represents the edge defined by
116 * v to v->next (i.e. the edge is forward of v)
119 /* Some macros which will hopefully aid readability of the code which
120 * traverses edges and vertices..
122 #define VERTEX_FORWARD_EDGE(v) ((v))
123 #define VERTEX_BACKWARD_EDGE(v) ((v)->prev)
124 #define EDGE_FORWARD_VERTEX(e) ((e)->next)
125 #define EDGE_BACKWARD_VERTEX(e) ((e))
126 #define NEXT_VERTEX(v) ((v)->next)
127 #define PREV_VERTEX(v) ((v)->prev)
128 #define NEXT_EDGE(e) ((e)->next)
129 #define PREV_EDGE(e) ((e)->prev)
130 #define VERTEX_SIDE_DIR_EDGE(v,s) (((s) == 'P') ? VERTEX_BACKWARD_EDGE (v) : VERTEX_FORWARD_EDGE (v)) /* Move backwards for 'P' side, forwards for 'N' */
131 #define EDGE_SIDE_DIR_VERTEX(e,s) (((s) == 'P') ? EDGE_BACKWARD_VERTEX (e) : EDGE_FORWARD_VERTEX (e)) /* Move backwards for 'P' side, forwards for 'N' */
132 #define VERTEX_DIRECTION_EDGE(v,d) (((d) == FORW) ? VERTEX_FORWARD_EDGE (v) : VERTEX_BACKWARD_EDGE (v)) /* Move backwards for BACKW, forwards for FORW */
133 #define EDGE_DIRECTION_VERTEX(e,d) (((d) == FORW) ? EDGE_FORWARD_VERTEX (e) : EDGE_BACKWARD_VERTEX (e)) /* Move backwards for BACKW, forwards for FORW */
135 #define ISECTED 3
136 #define UNKNWN 0
137 #define INSIDE 1
138 #define OUTSIDE 2
139 #define SHARED 3
140 #define SHARED2 4
142 #define TOUCHES 99
144 #define EDGE_LABEL(e) ((e)->Flags.status)
145 #define LABEL_EDGE(e,l) ((e)->Flags.status = (l))
147 #define error(code) longjmp(*(e), code)
149 #undef DEBUG_INTERSECT
150 #undef DEBUG_LABEL
151 #undef DEBUG_ALL_LABELS
152 #undef DEBUG_JUMP
153 #undef DEBUG_GATHER
154 #undef DEBUG_ANGLE
155 #undef DEBUG
156 #define DEBUG
157 #ifdef DEBUG
158 #define DEBUGP(...) pcb_fprintf(stderr, ## __VA_ARGS__)
159 #else
160 #define DEBUGP(...)
161 #endif
163 /* 2-Dimentional stuff */
165 #define Vsub2(r,a,b) {(r)[0] = (a)[0] - (b)[0]; (r)[1] = (a)[1] - (b)[1];}
166 #define Vadd2(r,a,b) {(r)[0] = (a)[0] + (b)[0]; (r)[1] = (a)[1] + (b)[1];}
167 #define Vsca2(r,a,s) {(r)[0] = (a)[0] * (s); (r)[1] = (a)[1] * (s);}
168 #define Vcpy2(r,a) {(r)[0] = (a)[0]; (r)[1] = (a)[1];}
169 #define Vequ2(a,b) ((a)[0] == (b)[0] && (a)[1] == (b)[1])
170 #define Vadds(r,a,b,s) {(r)[0] = ((a)[0] + (b)[0]) * (s); (r)[1] = ((a)[1] + (b)[1]) * (s);}
171 #define Vswp2(a,b) { long t; \
172 t = (a)[0], (a)[0] = (b)[0], (b)[0] = t; \
173 t = (a)[1], (a)[1] = (b)[1], (b)[1] = t; \
176 static POLYPARENTAGE no_parentage = {
177 .immaculate_conception = true,
178 .action = PBO_NONE,
179 .a = NULL,
180 .b = NULL
183 static char *theState (VNODE * v);
185 /* static */ void
186 pline_dump (VNODE * v)
188 VNODE *s, *n;
190 s = v;
193 n = NEXT_VERTEX(v);
194 pcb_fprintf (stderr, "Line [%$mn %$mn %$mn %$mn 10 10 \"%s\"] # %s, radius %$mn\n",
195 v->point[0], v->point[1],
196 n->point[0], n->point[1], theState (v),
197 VERTEX_FORWARD_EDGE (v)->is_round ? "Round" : "Line",
198 VERTEX_FORWARD_EDGE (v)->is_round ? v->radius : 0);
200 while ((v = NEXT_VERTEX(v)) != s);
203 /*static */void
204 poly_dump (POLYAREA * p)
206 POLYAREA *f = p;
207 PLINE *pl;
211 pl = p->contours;
214 pline_dump (&pl->head);
215 fprintf (stderr, "NEXT PLINE\n");
217 while ((pl = pl->next) != NULL);
218 fprintf (stderr, "NEXT POLY\n");
220 while ((p = p->f) != f);
223 static VNODE *
224 poly_CreateNodeFull (Vector v, bool is_round, Coord cx, Coord cy, Coord radius)
226 VNODE *res;
227 Coord *c;
229 assert (v);
230 res = g_slice_new0 (VNODE);
231 if (res == NULL)
232 return NULL;
234 c = res->point;
235 *c++ = *v++;
236 *c = *v;
238 res->is_round = is_round;
239 res->cx = cx;
240 res->cy = cy;
241 res->radius = radius;
243 return res;
246 VNODE *
247 poly_CreateNode (Vector v)
249 return poly_CreateNodeFull (v, false, 0, 0, 0);
252 VNODE *
253 poly_CreateNodeArcApproximation (Vector v, Coord cx, Coord cy, Coord radius)
255 return poly_CreateNodeFull (v, true, cx, cy, radius);
258 /* routines for processing intersections */
261 * \brief node_add.
263 * (C) 1993 Klamer Schutte.
265 * (C) 1997 Alexey Nikitin, Michael Leonov.
267 * (C) 2006 harry eaton.
269 * \return a bit field in new_point that indicates where the point was.
270 * 1 means a new node was created and inserted.
271 * 4 means the intersection was not on the dest point.
273 * dest is considered an edge
275 static VNODE *
276 node_add_single (VNODE * dest, Vector po)
278 VNODE *p;
280 /* XXX: MAY NOT BE CORRECT IF WE NEED TO SEPARATE STRAIGHT AND CURVED SEGMENTS */
281 if (vect_equal (po, EDGE_BACKWARD_VERTEX (dest)->point))
282 return EDGE_BACKWARD_VERTEX (dest);
283 if (vect_equal (po, EDGE_FORWARD_VERTEX (dest)->point))
284 return EDGE_FORWARD_VERTEX (dest);
285 p = poly_CreateNode (po);
286 if (p == NULL)
287 return NULL;
288 p->cvc_prev = p->cvc_next = NULL;
289 p->Flags.status = UNKNWN;
290 return p;
291 } /* node_add */
293 #define ISECT_BAD_PARAM (-1)
294 #define ISECT_NO_MEMORY (-2)
298 * \brief new_descriptor.
300 * (C) 2006 harry eaton.
302 static CVCList *
303 new_descriptor (VNODE * a, PLINE *pl, char poly, char side)
305 CVCList *l = (CVCList *) malloc (sizeof (CVCList));
306 Vector v;
307 register double ang, dx, dy;
309 if (!l)
310 return NULL;
311 l->head = NULL;
312 l->parent = a;
313 l->parent_contour = pl;
314 l->poly = poly;
315 l->side = side;
316 l->next = l->prev = l;
317 l->skip_me = false;
318 if (side == 'P') /* previous */
319 vect_sub (v, PREV_VERTEX (a)->point, a->point);
320 else /* next */
321 vect_sub (v, NEXT_VERTEX (a)->point, a->point);
322 /* Uses slope/(slope+1) in quadrant 1 as a proxy for the angle.
323 * It still has the same monotonic sort result
324 * and is far less expensive to compute than the real angle.
326 if (vect_equal (v, vect_zero))
328 if (side == 'P')
330 if (PREV_VERTEX (a)->cvc_prev == (CVCList *) - 1)
331 PREV_VERTEX (a)->cvc_prev = PREV_VERTEX (a)->cvc_next = NULL;
332 poly_ExclVertex (PREV_VERTEX (a));
333 vect_sub (v, PREV_VERTEX (a)->point, a->point);
334 #warning DOES THIS LEAK A VERTEX?
336 else
338 if (NEXT_VERTEX (a)->cvc_prev == (CVCList *) - 1)
339 NEXT_VERTEX (a)->cvc_prev = NEXT_VERTEX (a)->cvc_next = NULL;
340 poly_ExclVertex (NEXT_VERTEX (a));
341 vect_sub (v, NEXT_VERTEX (a)->point, a->point);
342 #warning DOES THIS LEAK A VERTEX?
345 assert (!vect_equal (v, vect_zero));
346 dx = fabs ((double) v[0]);
347 dy = fabs ((double) v[1]);
348 ang = dy / (dy + dx);
349 /* now move to the actual quadrant */
350 if (v[0] < 0 && v[1] >= 0)
351 ang = 2.0 - ang; /* 2nd quadrant */
352 else if (v[0] < 0 && v[1] < 0)
353 ang += 2.0; /* 3rd quadrant */
354 else if (v[0] >= 0 && v[1] < 0)
355 ang = 4.0 - ang; /* 4th quadrant */
356 l->angle = ang;
357 assert (ang >= 0.0 && ang <= 4.0);
358 #ifdef DEBUG_ANGLE
359 DEBUGP ("node on %c at (%$mn, %$mn) assigned angle %g on side %c\n", poly,
360 a->point[0], a->point[1], ang, side);
361 #endif
362 return l;
366 * \brief Compare the edge angles (and curvatures) to determine
367 * the ordering of two edges around a vertex.
369 * Returns <0 (ie -1) for a < b
370 * Returns =0 for a = b
371 * Returns >1 (ie +1) for a > b
373 static int compare_cvc_nodes (CVCList *a, CVCList *b)
375 if (a->angle < b->angle)
376 return -1;
377 else if (a->angle > b->angle)
378 return 1;
379 else
380 return 0;
384 * \brief insert_descriptor.
386 * (C) 2006 harry eaton.
388 * \param a is a cross-vertex node.
389 * \param poly is the polygon it comes from ('A' or 'B').
390 * \param side is the side this descriptor goes on ('P' for
391 * previous 'N' for next).
392 * \param start is the head of the list of cvclists.
394 static CVCList *
395 insert_descriptor (VNODE * a, PLINE *pl, char poly, char side, CVCList * start)
397 CVCList *l, *newone, *big, *small;
399 if (!(newone = new_descriptor (a, pl, poly, side)))
400 return NULL;
401 /* search for the CVCList for this point */
402 if (!start)
404 start = newone; /* return is also new, so we know where start is */
405 start->head = newone; /* circular list */
406 return newone;
408 else
410 l = start;
413 assert (l->head);
415 if (vect_equal (l->parent->point, a->point)) /* this CVCList is at our point */
417 start = l;
418 newone->head = l->head;
419 break;
422 if (vect_equal (l->head->parent->point, start->parent->point)) /* Wrap around in the list */
424 /* this seems to be a new point */
425 /* link this cvclist to the list of all cvclists */
426 for (; l->head != newone; l = l->next)
427 l->head = newone;
428 newone->head = start;
429 return newone;
431 l = l->head;
433 while (1);
435 assert (start);
436 l = big = small = start;
439 if (compare_cvc_nodes (l->next, l) < 0)
441 small = l->next;
442 big = l;
444 else if (compare_cvc_nodes (newone, l) >= 0 &&
445 compare_cvc_nodes (newone, l->next) <= 0)
447 /* insert new cvc if it lies between existing points */
448 newone->prev = l;
449 newone->next = l->next;
450 l->next = l->next->prev = newone;
451 return newone;
454 while ((l = l->next) != start);
455 /* didn't find it between points, it must go on an end */
457 #if 0
458 /* XXX: DUH.. BOTH OF THESE CODE-PATHS BELOW ARE EQUIVELANT.. INSERT AFTER big, or BEFORE small.
459 * The list rolls around, so big->next == small.
461 if (big->angle <= newone->angle)
463 newone->prev = big;
464 newone->next = big->next;
465 big->next = big->next->prev = newone;
466 return newone;
468 assert (small->angle >= newone->angle);
469 #endif
470 newone->next = small;
471 newone->prev = small->prev;
472 small->prev = small->prev->next = newone;
473 return newone;
477 * \brief node_add_point.
479 * (C) 1993 Klamer Schutte.
481 * (C) 1997 Alexey Nikitin, Michael Leonov.
483 * \return 1 if new node in b, 2 if new node in a and 3 if new node in
484 * both.
486 * a is considered an edge
488 static VNODE *
489 node_add_single_point (VNODE * a, Vector p)
491 VNODE *a_backward_vertex, *a_forward_vertex, *new_node;
493 a_backward_vertex = EDGE_BACKWARD_VERTEX (a);
494 a_forward_vertex = EDGE_FORWARD_VERTEX (a);
496 new_node = node_add_single (a, p);
497 assert (new_node != NULL);
499 new_node->cvc_prev = new_node->cvc_next = (CVCList *) - 1;
501 if (new_node == a_backward_vertex || new_node == a_forward_vertex)
502 return NULL;
504 return new_node;
505 } /* node_add_point */
508 /* \brief Find the previous valid CVCList entry belonging to the next polygon
510 * Skips over any edges which have the "skip_me" flag set, as those
511 * will be from hairline edge pairs which are not considered in the
512 * labeling code (note, they can only ever be started from - ie..
513 * start->skip_me can legitimately be true).
515 * \c NULL if no valid edge from the other polygon was found
517 static CVCList *
518 prev_cvc_from_other_poly (CVCList *start)
520 char this_poly = start->poly;
521 CVCList *l = start;
523 while ((l->poly == this_poly || l->skip_me) && l != start->next)
524 l = l->prev;
526 if (l->poly == this_poly || l->skip_me)
527 return NULL;
529 return l;
533 /* \brief Find the next valid CVCList entry belonging to the next polygon
535 * Skips over any edges which have the "skip_me" flag set, as those
536 * will be from hairline edge pairs which are not considered in the
537 * labeling code (note, they can only ever be started from - ie..
538 * start->skip_me can legitimately be true).
540 * \c NULL if no valid edge from the other polygon was found
542 static CVCList *
543 next_cvc_from_other_poly (CVCList *start)
545 char this_poly = start->poly;
546 CVCList *l = start;
548 while ((l->poly == this_poly || l->skip_me) && l != start->prev)
549 l = l->next;
551 if (l->poly == this_poly || l->skip_me)
552 return NULL;
554 return l;
557 static void
558 cvc_list_dump (CVCList *list)
560 VNODE *node;
561 CVCList *iter;
562 int count = 0;
564 if (list == NULL)
566 fprintf (stderr, "CVC list is NULL\n");
567 return;
570 node = list->parent;
572 pcb_fprintf (stderr, "Dumping CVC list at (%$mn, %$mn)\n", node->point[0], node->point[1]);
574 iter = list;
575 do {
576 count ++;
577 #if 1
578 pcb_fprintf (stderr, "%p: angle = %.30e, poly = %c, side = %c, (%mn, %mn)-(%mn, %mn), Vertices: %p-%p Edge: %p\n",
579 iter,
580 iter->angle,
581 iter->poly,
582 iter->side,
583 EDGE_BACKWARD_VERTEX (VERTEX_SIDE_DIR_EDGE (iter->parent, iter->side))->point[0],
584 EDGE_BACKWARD_VERTEX (VERTEX_SIDE_DIR_EDGE (iter->parent, iter->side))->point[1],
585 EDGE_FORWARD_VERTEX (VERTEX_SIDE_DIR_EDGE (iter->parent, iter->side))->point[0],
586 EDGE_FORWARD_VERTEX (VERTEX_SIDE_DIR_EDGE (iter->parent, iter->side))->point[1],
587 EDGE_BACKWARD_VERTEX (VERTEX_SIDE_DIR_EDGE (iter->parent, iter->side)),
588 EDGE_FORWARD_VERTEX (VERTEX_SIDE_DIR_EDGE (iter->parent, iter->side)),
589 VERTEX_SIDE_DIR_EDGE (iter->parent, iter->side));
590 #endif
591 } while ((iter = iter->next) != list);
593 if ((count & 1) != 0)
594 g_critical ("Ended up with odd number of entries in CVC list");
598 * \brief edge_label.
600 * (C) 2006 harry eaton.
602 * (C) 2016 Peter Clifton
604 * pn is considered an edge
606 static unsigned int
607 edge_label (VNODE * pn, int existing_label)
609 CVCList *l, *test;
610 int region;
611 bool shared_edge_case = false;
613 /* search counter-clockwise in the cross vertex connectivity (CVC) list
615 * check for shared edges (that could be prev or next in the list since the angles are equal)
616 * and check if this edge (pn -> pn->next) is found between the other poly's entry and exit
619 /* Start with l pointing to the CVCNode corresponding to this edge leaving its from vertex */
620 assert (pn);
621 l = EDGE_BACKWARD_VERTEX (pn)->cvc_next;
623 assert (l);
625 /* Shared edges can be sorted in either order, so need to check l->prev as well */
626 test = prev_cvc_from_other_poly (l);
628 if (test == NULL)
630 /* Didn't find anything from the other polygon, this is a point where
631 * contours from the same polygon join, e.g. either end of a shared
632 * edge generated during intersection. Treat as if it were not cross-
633 * connected, by labeling with the current label.
635 region = existing_label;
636 LABEL_EDGE (pn, region);
637 return region;
640 if (compare_cvc_nodes (l, test) == 0)
642 shared_edge_case = true;
643 l = test;
645 else
647 test = next_cvc_from_other_poly (l);
648 if (compare_cvc_nodes (l, test) == 0)
649 shared_edge_case = true;
651 l = test;
654 if (shared_edge_case)
656 /* If this fires, we found two geometrically distinct edges which for some reason compare as equal in our cvc_list.
657 * Shared edges should be geometrically identical (but may be in opposite directions).
659 if (!vect_equal (EDGE_SIDE_DIR_VERTEX (VERTEX_SIDE_DIR_EDGE (l->parent, l->side), l->side)->point,
660 EDGE_FORWARD_VERTEX (pn)->point))
661 g_critical ("Expected shared edge, but geometry doesn't match");
663 /* SHARED is the same direction case,
664 * SHARED2 is the opposite direction case.
666 region = (l->side == 'P') ? SHARED2 : SHARED;
667 pn->shared = VERTEX_SIDE_DIR_EDGE (l->parent, l->side);
668 // cvc_list_dump (l);
670 else
672 region = (l->side == 'P') ? INSIDE : OUTSIDE;
675 assert (EDGE_LABEL (pn) == UNKNWN || EDGE_LABEL (pn) == region);
676 LABEL_EDGE (pn, region);
677 if (region == SHARED || region == SHARED2)
678 return UNKNWN;
679 return region;
680 } /* edge_label */
683 * \brief add_descriptors.
685 * (C) 2006 harry eaton.
687 static CVCList *
688 add_descriptors (PLINE * pl, char poly, CVCList * list)
690 VNODE *node = &pl->head; /* node is considered a vertex */
694 if (node->cvc_prev)
696 assert (node->cvc_prev == (CVCList *) - 1
697 && node->cvc_next == (CVCList *) - 1);
698 list = node->cvc_prev = insert_descriptor (node, pl, poly, 'P', list);
699 if (!node->cvc_prev)
700 return NULL;
701 list = node->cvc_next = insert_descriptor (node, pl, poly, 'N', list);
702 if (!node->cvc_next)
703 return NULL;
706 while ((node = NEXT_VERTEX(node)) != &pl->head);
707 return list;
710 static bool
711 cntrbox_check (PLINE * c, Vector p)
713 return (p[0] < c->xmin ||
714 p[0] + 1 > c->xmax ||
715 p[1] < c->ymin ||
716 p[1] + 1 > c->ymax);
719 static inline void
720 cntrbox_adjust (PLINE * c, Vector p)
722 c->xmin = min (c->xmin, p[0]);
723 c->xmax = max (c->xmax, p[0] + 1);
724 c->ymin = min (c->ymin, p[1]);
725 c->ymax = max (c->ymax, p[1] + 1);
728 /* some structures for handling segment intersections using the rtrees */
730 typedef struct seg
732 BoxType box;
733 VNODE *v;
734 PLINE *p;
735 int intersected;
736 } seg;
738 typedef struct _insert_node_task insert_node_task;
740 struct _insert_node_task
742 insert_node_task *next;
743 seg * node_seg;
744 VNODE *new_node;
745 POLYAREA *poly;
748 typedef struct info
750 double m, b;
751 // rtree_t *tree;
752 VNODE *v;
753 struct seg *s;
754 jmp_buf *env, sego, *touch;
755 int need_restart;
756 insert_node_task *node_insert_list;
757 bool debug;
758 POLYAREA *looping_over_poly;
759 POLYAREA *rtree_over_poly;
760 } info;
762 typedef struct contour_info
764 PLINE *pa;
765 jmp_buf restart;
766 jmp_buf *getout;
767 int need_restart;
768 insert_node_task *node_insert_list;
769 POLYAREA *looping_over_poly;
770 POLYAREA *rtree_over_poly;
771 } contour_info;
775 * \brief adjust_tree().
777 * (C) 2006 harry eaton.
779 * This replaces the segment in the tree with the two new segments after
780 * a vertex has been added.
782 static int
783 adjust_tree (rtree_t * tree, struct seg *s)
785 struct seg *q;
787 q = (seg *)malloc (sizeof (struct seg));
788 if (!q)
789 return 1;
790 q->intersected = 0;
791 q->v = s->v;
792 q->p = s->p;
793 q->box.X1 = min (EDGE_BACKWARD_VERTEX (q->v)->point[0], EDGE_FORWARD_VERTEX (q->v)->point[0]);
794 q->box.X2 = max (EDGE_BACKWARD_VERTEX (q->v)->point[0], EDGE_FORWARD_VERTEX (q->v)->point[0]) + 1;
795 q->box.Y1 = min (EDGE_BACKWARD_VERTEX (q->v)->point[1], EDGE_FORWARD_VERTEX (q->v)->point[1]);
796 q->box.Y2 = max (EDGE_BACKWARD_VERTEX (q->v)->point[1], EDGE_FORWARD_VERTEX (q->v)->point[1]) + 1;
797 r_insert_entry (tree, (const BoxType *) q, 1);
798 q = (seg *)malloc (sizeof (struct seg));
799 if (!q)
800 return 1;
801 q->intersected = 0;
802 q->v = NEXT_EDGE (s->v);
803 q->p = s->p;
804 q->box.X1 = min (EDGE_BACKWARD_VERTEX (q->v)->point[0], EDGE_FORWARD_VERTEX (q->v)->point[0]);
805 q->box.X2 = max (EDGE_BACKWARD_VERTEX (q->v)->point[0], EDGE_FORWARD_VERTEX (q->v)->point[0]) + 1;
806 q->box.Y1 = min (EDGE_BACKWARD_VERTEX (q->v)->point[1], EDGE_FORWARD_VERTEX (q->v)->point[1]);
807 q->box.Y2 = max (EDGE_BACKWARD_VERTEX (q->v)->point[1], EDGE_FORWARD_VERTEX (q->v)->point[1]) + 1;
808 r_insert_entry (tree, (const BoxType *) q, 1);
809 r_delete_entry (tree, (const BoxType *) s);
810 return 0;
814 * \brief seg_in_region().
816 * (C) 2006, harry eaton.
818 * This prunes the search for boxes that don't intersect the segment.
820 static int
821 seg_in_region (const BoxType * b, void *cl)
823 struct info *i = (struct info *) cl;
824 double y1, y2;
825 /* for zero slope the search is aligned on the axis so it is already pruned */
826 if (i->m == 0.)
827 return 1;
828 y1 = i->m * b->X1 + i->b;
829 y2 = i->m * b->X2 + i->b;
830 if (min (y1, y2) >= b->Y2)
831 return 0;
832 if (max (y1, y2) < b->Y1)
833 return 0;
834 return 1; /* might intersect */
838 * \brief Prepend a deferred node-insersion task to a list.
840 static insert_node_task *
841 prepend_insert_node_task (insert_node_task *list, POLYAREA *poly, seg *seg, VNODE *new_node)
843 insert_node_task *task = (insert_node_task *)malloc (sizeof (*task));
844 task->node_seg = seg;
845 task->new_node = new_node;
846 task->next = list;
847 task->poly = poly;
848 return task;
852 * \brief seg_in_seg().
854 * (C) 2006 harry eaton.
856 * This routine checks if the segment in the tree intersect the search
857 * segment.
858 * If it does, the plines are marked as intersected and the point is
859 * marked for the cvclist.
860 * If the point is not already a vertex, a new vertex is inserted and
861 * the search for intersections starts over at the beginning.
862 * That is potentially a significant time penalty, but it does solve the
863 * snap rounding problem.
865 * \todo There are efficient algorithms for finding intersections with
866 * snap rounding, but I don't have time to implement them right now.
868 static int
869 seg_in_seg (const BoxType * b, void *cl)
871 struct info *i = (struct info *) cl;
872 struct seg *s = (struct seg *) b;
873 Vector s1, s2;
874 int cnt;
875 VNODE *new_node;
877 /* When new nodes are added at the end of a pass due to an intersection
878 * the segments may be altered. If either segment we're looking at has
879 * already been intersected this pass, skip it until the next pass.
881 if (s->intersected || i->s->intersected)
882 return 0;
884 cnt = vect_inters2 (EDGE_BACKWARD_VERTEX (s->v)->point, EDGE_FORWARD_VERTEX (s->v)->point,
885 EDGE_BACKWARD_VERTEX (i->v)->point, EDGE_FORWARD_VERTEX (i->v)->point, s1, s2);
886 if (!cnt)
887 return 0;
888 if (i->touch) /* if checking touches one find and we're done */
889 longjmp (*i->touch, TOUCHES);
890 i->s->p->Flags.status = ISECTED;
891 s->p->Flags.status = ISECTED;
892 for (; cnt; cnt--)
894 bool done_insert_on_i = false;
895 new_node = node_add_single_point (i->v, cnt > 1 ? s2 : s1);
896 if (new_node != NULL)
898 #ifdef DEBUG_INTERSECT
899 DEBUGP ("new intersection on segment \"i\" at (%$mn, %$mn)\n",
900 cnt > 1 ? s2[0] : s1[0], cnt > 1 ? s2[1] : s1[1]);
901 #endif
902 i->node_insert_list =
903 prepend_insert_node_task (i->node_insert_list, i->looping_over_poly, i->s, new_node);
904 i->s->intersected = 1;
905 done_insert_on_i = true;
907 new_node = node_add_single_point (s->v, cnt > 1 ? s2 : s1);
908 if (new_node != NULL)
910 #ifdef DEBUG_INTERSECT
911 DEBUGP ("new intersection on segment \"s\" at (%$mn, %$mn)\n",
912 cnt > 1 ? s2[0] : s1[0], cnt > 1 ? s2[1] : s1[1]);
913 #endif
914 i->node_insert_list =
915 prepend_insert_node_task (i->node_insert_list, i->rtree_over_poly, s, new_node);
916 s->intersected = 1;
917 return 0; /* Keep looking for intersections with segment "i" */
919 /* Skip any remaining r_search hits against segment i, as any futher
920 * intersections will be rejected until the next pass anyway.
922 if (done_insert_on_i)
923 longjmp (*i->env, 1);
925 return 0;
928 static void *
929 make_edge_tree (PLINE * pb)
931 struct seg *s;
932 VNODE *bv; /* bv is considred an edge */
933 rtree_t *ans = r_create_tree (NULL, 0, 0);
934 bv = &pb->head;
937 s = (seg *)malloc (sizeof (struct seg));
938 s->intersected = 0;
939 if (EDGE_BACKWARD_VERTEX (bv)->point[0] < EDGE_FORWARD_VERTEX (bv)->point[0])
941 s->box.X1 = EDGE_BACKWARD_VERTEX (bv)->point[0];
942 s->box.X2 = EDGE_FORWARD_VERTEX (bv)->point[0] + 1;
944 else
946 s->box.X2 = EDGE_BACKWARD_VERTEX (bv)->point[0] + 1;
947 s->box.X1 = EDGE_FORWARD_VERTEX (bv)->point[0];
949 if (EDGE_BACKWARD_VERTEX (bv)->point[1] < EDGE_FORWARD_VERTEX (bv)->point[1])
951 s->box.Y1 = EDGE_BACKWARD_VERTEX (bv)->point[1];
952 s->box.Y2 = EDGE_FORWARD_VERTEX (bv)->point[1] + 1;
954 else
956 s->box.Y2 = EDGE_BACKWARD_VERTEX (bv)->point[1] + 1;
957 s->box.Y1 = EDGE_FORWARD_VERTEX (bv)->point[1];
959 s->v = bv;
960 s->p = pb;
961 r_insert_entry (ans, (const BoxType *) s, 1);
963 while ((bv = NEXT_EDGE (bv)) != &pb->head);
964 return (void *) ans;
967 static int
968 get_seg (const BoxType * b, void *cl)
970 struct info *i = (struct info *) cl;
971 struct seg *s = (struct seg *) b;
972 if (i->debug)
973 fprintf (stderr, "get_seg testing against segment %p (seg %p)\n", s->v, s);
974 if (i->v == s->v)
976 i->s = s;
977 longjmp (i->sego, 1);
979 return 0;
983 * \brief intersect_rounded() (and helpers).
985 * (C) 2006, harry eaton.
987 * (C) 2016, Peter Clifton
989 * Handling of snap-rounding edges against other vertex end-points
991 * This uses an rtree to find A-B intersections. Whenever a new vertex is
992 * added, the search for intersections is re-started because the rounding
993 * could alter the topology otherwise.
994 * This should use a faster algorithm for snap rounding intersection finding.
995 * The best algorthim is probably found in:
997 * "Improved output-sensitive snap rounding," John Hershberger,
998 * Proceedings of the 22nd annual symposium on Computational geomerty,
999 * 2006, pp 357-366.
1001 * http://doi.acm.org/10.1145/1137856.1137909
1003 * Algorithms described by de Berg, or Goodrich or Halperin, or Hobby
1004 * would probably work as well.
1007 static bool
1008 process_deferred_intersections (/*POLYAREA *b, */insert_node_task *task)
1010 bool any_inserted = false;
1012 while (task != NULL)
1014 insert_node_task *next = task->next;
1016 /* XXX: If a node was inserted due to an intersection, don't assume we're on the a round contour any more */
1017 task->node_seg->v->is_round = false;
1019 /* Do insersion */
1020 PREV_VERTEX (task->new_node) = EDGE_BACKWARD_VERTEX (task->node_seg->v);
1021 NEXT_VERTEX (task->new_node) = EDGE_FORWARD_VERTEX (task->node_seg->v);
1022 PREV_VERTEX (EDGE_FORWARD_VERTEX (task->node_seg->v)) = task->new_node;
1023 EDGE_FORWARD_VERTEX (task->node_seg->v) = task->new_node;
1024 task->node_seg->p->Count++;
1026 if (cntrbox_check (task->node_seg->p, task->new_node->point))
1028 /* First delete the contour from the contour r-tree, as its bounds
1029 * may be adjusted whilst inserting nodes
1031 r_delete_entry (task->poly->contour_tree, (const BoxType *) task->node_seg->p);
1032 cntrbox_adjust (task->node_seg->p, task->new_node->point);
1033 r_insert_entry (task->poly->contour_tree, (const BoxType *) task->node_seg->p, 0);
1036 if (adjust_tree (task->node_seg->p->tree, task->node_seg))
1037 assert (0); /* XXX: Memory allocation failure */
1039 any_inserted = true; /* Any new nodes could intersect */
1041 free (task);
1042 task = next;
1045 return any_inserted;
1049 * \brief line_point_inters
1051 * Based upon vect_inters2
1053 * (C) 1993 Klamer Schutte.
1055 * (C) 1997 Michael Leonov, Alexey Nikitin.
1057 static bool
1058 line_point_inters (Vector p1, Vector p2, Vector point)
1060 double p1_fp[2] = {p1[0], p1[1]};
1061 double p2_fp[2] = {p2[0], p2[1]};
1062 double q1_fp[2];
1063 double q2_fp[2];
1064 double s1_fp[2];
1065 double s2_fp[2];
1067 q1_fp[0] = point[0] - 0.5;
1068 q2_fp[0] = point[0] + 0.5;
1069 q1_fp[1] = point[1] - 0.5;
1070 q2_fp[1] = point[1] - 0.5;
1072 if (vect_inters2_fp (p1_fp, p2_fp, q1_fp, q2_fp, /* out */s1_fp, s2_fp) > 0)
1073 return true;
1075 q1_fp[0] = point[0] + 0.5;
1076 q2_fp[0] = point[0] + 0.5;
1077 q1_fp[1] = point[1] - 0.5;
1078 q2_fp[1] = point[1] + 0.5;
1080 if (vect_inters2_fp (p1_fp, p2_fp, q1_fp, q2_fp, /* out */s1_fp, s2_fp) > 0)
1081 return true;
1083 q1_fp[0] = point[0] - 0.5;
1084 q2_fp[0] = point[0] + 0.5;
1085 q1_fp[1] = point[1] + 0.5;
1086 q2_fp[1] = point[1] + 0.5;
1088 if (vect_inters2_fp (p1_fp, p2_fp, q1_fp, q2_fp, /* out */s1_fp, s2_fp) > 0)
1089 return true;
1091 q1_fp[0] = point[0] - 0.5;
1092 q2_fp[0] = point[0] - 0.5;
1093 q1_fp[1] = point[1] - 0.5;
1094 q2_fp[1] = point[1] + 0.5;
1096 if (vect_inters2_fp (p1_fp, p2_fp, q1_fp, q2_fp, /* out */s1_fp, s2_fp) > 0)
1097 return true;
1099 return false;
1103 * \brief vertex_in_seg_rounded().
1105 * (C) 2006 harry eaton.
1107 * (C) 2016 Peter Clifton
1109 * This routine checks if the segment in the tree intersect the search
1110 * segment.
1111 * If it does, the plines are marked as intersected and the point is
1112 * marked for the cvclist.
1113 * If the point is not already a vertex, a new vertex is inserted and
1114 * the search for intersections starts over at the beginning.
1115 * That is potentially a significant time penalty, but it does solve the
1116 * snap rounding problem.
1118 * \todo There are efficient algorithms for finding intersections with
1119 * snap rounding, but I don't have time to implement them right now.
1121 static int
1122 vertex_in_seg_rounded (const BoxType * b, void *cl)
1124 struct info *i = (struct info *) cl;
1125 struct seg *s = (struct seg *) b;
1126 VNODE *new_node;
1128 /* When new nodes are added at the end of a pass due to an intersection
1129 * the segments may be altered. If either segment we're looking at has
1130 * already been intersected this pass, skip it until the next pass.
1132 if (s->intersected)
1133 return 0;
1135 if (!line_point_inters (EDGE_BACKWARD_VERTEX (s->v)->point, EDGE_FORWARD_VERTEX (s->v)->point, i->v->point))
1136 return 0;
1138 if (i->touch) /* if checking touches one find and we're done */
1139 longjmp (*i->touch, TOUCHES);
1141 // i->s->p->Flags.status = ISECTED; /* XXX */
1142 // s->p->Flags.status = ISECTED;
1144 new_node = node_add_single_point (s->v, i->v->point);
1145 if (new_node != NULL)
1147 #ifdef DEBUG_INTERSECT
1148 DEBUGP ("found new rounded intersection on segment \"s\" at (%$mn, %$mn)\n",
1149 i->v->point[0], i->v->point[1]);
1150 #endif
1151 i->node_insert_list =
1152 prepend_insert_node_task (i->node_insert_list, i->rtree_over_poly, s, new_node);
1153 s->intersected = 1;
1154 return 0; /* Keep looking for intersections with the test vertex */
1156 return 0;
1160 static int
1161 rounded_contour_bounds_touch (const BoxType * b, void *cl)
1163 contour_info *c_info = (contour_info *) cl;
1164 PLINE *pa = c_info->pa;
1165 PLINE *pb = (PLINE *) b;
1166 PLINE *rtree_over;
1167 PLINE *looping_over;
1168 VNODE *av; /* node iterators */ /* av is considered an edge */
1169 struct info info;
1170 BoxType box;
1172 /* Have vertex_in_seg_rounded return to our desired location if it touches */
1173 info.env = NULL;
1174 info.touch = c_info->getout;
1175 info.need_restart = 0;
1176 info.node_insert_list = c_info->node_insert_list;
1177 info.looping_over_poly = c_info->looping_over_poly;
1178 info.rtree_over_poly = c_info->rtree_over_poly;
1180 looping_over = pa;
1181 rtree_over = pb;
1183 av = &looping_over->head;
1184 do /* Loop over the edges in the a contour */
1186 /* check this vertex for any insertions */
1187 info.v = av;
1189 /* NB: We expand the search box to ensure we catch edges which may round to this coordinate */
1190 box.X2 = (box.X1 = av->point[0] - 1) + 3;
1191 box.Y2 = (box.Y1 = av->point[1] - 1) + 3;
1193 /* NB: If this actually hits anything, we are teleported back to the beginning */
1194 if (rtree_over->tree &&
1195 UNLIKELY (r_search (rtree_over->tree, &box, NULL, vertex_in_seg_rounded, &info)))
1196 assert (0); /* XXX: Memory allocation failure */
1198 while ((av = NEXT_VERTEX (av)) != &looping_over->head);
1200 c_info->node_insert_list = info.node_insert_list;
1201 if (info.need_restart)
1202 c_info->need_restart = 1;
1203 return 0;
1206 static int
1207 intersect_rounded_impl (jmp_buf * jb, POLYAREA * b, POLYAREA * a, int add)
1209 PLINE *pa;
1210 contour_info c_info;
1211 int need_restart = 0;
1212 c_info.need_restart = 0;
1213 c_info.node_insert_list = NULL;
1214 c_info.looping_over_poly = a;
1215 c_info.rtree_over_poly = b;
1217 for (pa = a->contours; pa; pa = pa->next) /* Loop over the contours of POLYAREA "a" */
1219 BoxType sb;
1220 jmp_buf out;
1221 int retval;
1223 c_info.getout = NULL;
1224 c_info.pa = pa;
1226 if (!add)
1228 retval = setjmp (out);
1229 if (retval)
1231 /* The intersection test short-circuited back here,
1232 * we need to clean up, then longjmp to jb */
1233 longjmp (*jb, retval);
1235 c_info.getout = &out;
1238 sb.X1 = pa->xmin;
1239 sb.Y1 = pa->ymin;
1240 sb.X2 = pa->xmax + 1;
1241 sb.Y2 = pa->ymax + 1;
1243 r_search (b->contour_tree, &sb, NULL, rounded_contour_bounds_touch, &c_info);
1244 if (c_info.need_restart)
1245 need_restart = 1;
1248 /* Process any deferred node insersions */
1249 need_restart |= process_deferred_intersections (c_info.node_insert_list);
1251 return need_restart;
1254 static int
1255 intersect_rounded (jmp_buf * jb, POLYAREA * b, POLYAREA * a, int add)
1257 int call_count = 1;
1258 while (intersect_rounded_impl (jb, b, a, add))
1259 call_count++;
1260 return 0;
1264 * \brief intersect() (and helpers).
1266 * (C) 2006, harry eaton.
1268 * This uses an rtree to find A-B intersections.
1269 * Whenever a new vertex is added, the search for intersections is
1270 * re-started because the rounding could alter the topology otherwise.
1271 * This should use a faster algorithm for snap rounding intersection
1272 * finding.
1273 * The best algorthim is probably found in:
1275 * "Improved output-sensitive snap rounding," John Hershberger,
1276 * Proceedings of the 22nd annual symposium on Computational geomerty,
1277 * 2006, pp 357-366.
1279 * http://doi.acm.org/10.1145/1137856.1137909
1281 * Algorithms described by de Berg, or Goodrich or Halperin, or Hobby
1282 * would probably work as well.
1285 static int
1286 contour_bounds_touch (const BoxType * b, void *cl)
1288 contour_info *c_info = (contour_info *) cl;
1289 PLINE *pa = c_info->pa;
1290 PLINE *pb = (PLINE *) b;
1291 PLINE *rtree_over;
1292 PLINE *looping_over;
1293 VNODE *av; /* node iterators */ /* av is considered an edge */
1294 struct info info;
1295 BoxType box;
1296 jmp_buf restart;
1298 /* Have seg_in_seg return to our desired location if it touches */
1299 info.env = &restart;
1300 info.touch = c_info->getout;
1301 info.need_restart = 0;
1302 info.node_insert_list = c_info->node_insert_list;
1304 /* Pick which contour has the fewer points, and do the loop
1305 * over that. The r_tree makes hit-testing against a contour
1306 * faster, so we want to do that on the bigger contour.
1308 if (pa->Count < pb->Count)
1310 rtree_over = pb;
1311 looping_over = pa;
1312 info.rtree_over_poly = c_info->rtree_over_poly;
1313 info.looping_over_poly = c_info->looping_over_poly;
1315 else
1317 rtree_over = pa;
1318 looping_over = pb;
1319 info.rtree_over_poly = c_info->looping_over_poly;
1320 info.looping_over_poly = c_info->rtree_over_poly;
1323 av = &looping_over->head;
1324 do /* Loop over the edges in the smaller contour */
1326 /* check this edge for any insertions */
1327 double dx;
1328 info.v = av;
1329 /* compute the slant for region trimming */
1330 dx = EDGE_FORWARD_VERTEX (av)->point[0] - EDGE_BACKWARD_VERTEX (av)->point[0];
1331 if (dx == 0)
1332 info.m = 0;
1333 else
1335 info.m = (EDGE_FORWARD_VERTEX (av)->point[1] - EDGE_BACKWARD_VERTEX (av)->point[1]) / dx;
1336 info.b = EDGE_BACKWARD_VERTEX (av)->point[1] - info.m * EDGE_BACKWARD_VERTEX (av)->point[0];
1338 box.X2 = (box.X1 = EDGE_BACKWARD_VERTEX (av)->point[0]) + 1;
1339 box.Y2 = (box.Y1 = EDGE_BACKWARD_VERTEX (av)->point[1]) + 1;
1341 /* fill in the segment in info corresponding to this node */
1342 if (setjmp (info.sego) == 0)
1344 info.debug = false;
1345 r_search (looping_over->tree, &box, NULL, get_seg, &info);
1346 g_error ("Did not find segment in contour tree!");
1349 /* If we're going to have another pass anyway, skip this */
1350 if (info.s->intersected && info.node_insert_list != NULL)
1351 continue;
1353 if (setjmp (restart))
1354 continue;
1356 /* NB: If this actually hits anything, we are teleported back to the beginning */
1357 // info.tree = rtree_over->tree;
1358 // if (info.tree)
1359 // if (UNLIKELY (r_search (info.tree, &info.s->box,
1360 if (rtree_over->tree)
1361 if (UNLIKELY (r_search (rtree_over->tree, &info.s->box,
1362 seg_in_region, seg_in_seg, &info)))
1363 assert (0); /* XXX: Memory allocation failure */
1365 while ((av = NEXT_EDGE (av)) != &looping_over->head);
1367 c_info->node_insert_list = info.node_insert_list;
1368 if (info.need_restart)
1369 c_info->need_restart = 1;
1370 return 0;
1374 static int
1375 intersect_impl (jmp_buf * jb, POLYAREA * b, POLYAREA * a, int add)
1377 POLYAREA *t;
1378 PLINE *pa;
1379 contour_info c_info;
1380 int need_restart = 0;
1381 c_info.need_restart = 0;
1382 c_info.node_insert_list = NULL;
1384 /* Search the r-tree of the object with most contours
1385 * We loop over the contours of "a". Swap if necessary.
1387 if (a->contour_tree->size > b->contour_tree->size)
1389 t = b;
1390 b = a;
1391 a = t;
1392 c_info.looping_over_poly = b;
1393 c_info.rtree_over_poly = a;
1395 else
1397 c_info.looping_over_poly = a;
1398 c_info.rtree_over_poly = b;
1401 for (pa = a->contours; pa; pa = pa->next) /* Loop over the contours of POLYAREA "a" */
1403 BoxType sb;
1404 jmp_buf out;
1405 int retval;
1407 c_info.getout = NULL;
1408 c_info.pa = pa;
1410 if (!add)
1412 retval = setjmp (out);
1413 if (retval)
1415 /* The intersection test short-circuited back here,
1416 * we need to clean up, then longjmp to jb */
1417 longjmp (*jb, retval);
1419 c_info.getout = &out;
1422 sb.X1 = pa->xmin;
1423 sb.Y1 = pa->ymin;
1424 sb.X2 = pa->xmax + 1;
1425 sb.Y2 = pa->ymax + 1;
1427 r_search (b->contour_tree, &sb, NULL, contour_bounds_touch, &c_info);
1428 if (c_info.need_restart)
1429 need_restart = 1;
1432 /* Process any deferred node insersions */
1433 need_restart |= process_deferred_intersections (c_info.node_insert_list);
1435 return need_restart;
1438 static int
1439 intersect (jmp_buf * jb, POLYAREA * b, POLYAREA * a, int add)
1441 int call_count = 1;
1442 while (intersect_impl (jb, b, a, add))
1443 call_count++;
1444 return 0;
1447 static void
1448 intersect_rounded_self (jmp_buf * e, POLYAREA *pfst)
1450 POLYAREA *p, *p2;
1452 p = pfst;
1455 p2 = p->f;
1456 for (p2 = p->f; p2 != p; p2 = p2->f)
1458 if (p->contours->xmax >= p2->contours->xmin &&
1459 p->contours->ymax >= p2->contours->ymin &&
1460 p->contours->xmin <= p2->contours->xmax &&
1461 p->contours->ymin <= p2->contours->ymax)
1463 intersect_rounded (e, p, p2, true);
1467 while ((p = p->f) != pfst);
1470 static void
1471 M_POLYAREA_intersect (jmp_buf * e, POLYAREA * afst, POLYAREA * bfst, int add, CVCList **list_out)
1473 POLYAREA *a = afst, *b = bfst;
1474 PLINE *curcA, *curcB;
1475 CVCList *the_list = NULL;
1477 if (a == NULL || b == NULL)
1478 error (err_bad_parm);
1480 if (add)
1482 /* Intersect all a outer contours against all other piece outer + inner contours (and vice-versa) */
1483 /* Intersect all b outer contours against all other piece outer + inner contours (and vice-versa) */
1484 intersect_rounded_self (e, a);
1485 intersect_rounded_self (e, b);
1487 #if 0
1492 if (a->contours->xmax >= b->contours->xmin &&
1493 a->contours->ymax >= b->contours->ymin &&
1494 a->contours->xmin <= b->contours->xmax &&
1495 a->contours->ymin <= b->contours->ymax)
1497 intersect_rounded (e, a, b, add);
1498 intersect_rounded (e, b, a, add);
1501 while (add && (a = a->f) != afst);
1503 while (add && (b = b->f) != bfst);
1504 #endif
1511 if (a->contours->xmax >= b->contours->xmin &&
1512 a->contours->ymax >= b->contours->ymin &&
1513 a->contours->xmin <= b->contours->xmax &&
1514 a->contours->ymin <= b->contours->ymax)
1516 if (UNLIKELY (intersect (e, a, b, add)))
1517 error (err_no_memory);
1520 while (add && (a = a->f) != afst);
1521 for (curcB = b->contours; curcB != NULL; curcB = curcB->next)
1522 if (curcB->Flags.status == ISECTED)
1524 the_list = add_descriptors (curcB, 'B', the_list);
1525 if (UNLIKELY (the_list == NULL))
1526 error (err_no_memory);
1529 while (add && (b = b->f) != bfst);
1532 for (curcA = a->contours; curcA != NULL; curcA = curcA->next)
1533 if (curcA->Flags.status == ISECTED)
1535 the_list = add_descriptors (curcA, 'A', the_list);
1536 if (UNLIKELY (the_list == NULL))
1537 error (err_no_memory);
1540 while (add && (a = a->f) != afst);
1541 if (list_out != NULL)
1542 *list_out = the_list;
1543 } /* M_POLYAREA_intersect */
1545 static inline int
1546 cntrbox_inside (PLINE * c1, PLINE * c2)
1548 assert (c1 != NULL && c2 != NULL);
1549 return ((c1->xmin >= c2->xmin) &&
1550 (c1->ymin >= c2->ymin) &&
1551 (c1->xmax <= c2->xmax) && (c1->ymax <= c2->ymax));
1554 /* Routines for making labels */
1556 static int
1557 count_contours_i_am_inside (const BoxType * b, void *cl)
1559 PLINE *me = (PLINE *) cl;
1560 PLINE *check = (PLINE *) b;
1562 if (poly_ContourInContour (check, me))
1563 return 1;
1564 return 0;
1568 * \brief cntr_in_M_POLYAREA.
1570 * \return poly is inside outfst ? (which POLYAREA *) : NULL.
1572 POLYAREA *
1573 cntr_in_M_POLYAREA (PLINE * poly, POLYAREA * outfst, BOOLp test)
1575 POLYAREA *outer = outfst;
1576 heap_t *heap;
1578 assert (poly != NULL);
1579 assert (outer != NULL);
1581 heap = heap_create ();
1584 if (cntrbox_inside (poly, outer->contours))
1585 heap_insert (heap, outer->contours->area, (void *) outer);
1587 /* if checking touching, use only the first polygon */
1588 while (!test && (outer = outer->f) != outfst);
1589 /* we need only check the smallest poly container
1590 * but we must loop in case the box containter is not
1591 * the poly container */
1594 if (heap_is_empty (heap))
1595 break;
1596 outer = (POLYAREA *) heap_remove_smallest (heap);
1598 switch (r_search
1599 (outer->contour_tree, (BoxType *) poly, NULL,
1600 count_contours_i_am_inside, poly))
1602 case 0: /* Didn't find anything in this piece, Keep looking */
1603 break;
1604 case 1: /* Found we are inside this piece, and not any of its holes */
1605 heap_destroy (&heap);
1606 return outer;
1607 case 2: /* Found inside a hole in the smallest polygon so far. No need to check the other polygons */
1608 heap_destroy (&heap);
1609 return NULL;
1610 default:
1611 printf ("Something strange here\n");
1612 break;
1615 while (1);
1616 heap_destroy (&heap);
1617 return NULL;
1618 } /* cntr_in_M_POLYAREA */
1620 static char *
1621 theState (VNODE * e)
1623 static char u[] = "UNKNOWN";
1624 static char i[] = "INSIDE";
1625 static char o[] = "OUTSIDE";
1626 static char s[] = "SHARED";
1627 static char s2[] = "SHARED2";
1629 switch (EDGE_LABEL (e))
1631 case INSIDE:
1632 return i;
1633 case OUTSIDE:
1634 return o;
1635 case SHARED:
1636 return s;
1637 case SHARED2:
1638 return s2;
1639 default:
1640 return u;
1644 #ifdef DEBUG
1645 #ifdef DEBUG_ALL_LABELS
1646 static void
1647 print_labels (PLINE * a)
1649 VNODE *e = &a->head;
1653 DEBUGP ("(%$mn, %$mn)->(%$mn, %$mn) labeled %s\n",
1654 EDGE_BACKWARD_VERTEX (e)->point[0], EDGE_BACKWARD_VERTEX (e)->point[1],
1655 EDGE_FORWARD_VERTEX (e)->point[0], EDGE_FORWARD_VERTEX (e)->point[1], theState (e));
1657 while ((e = NEXT_EDGE (e)) != &a->head);
1659 #endif
1660 #endif
1663 * \brief label_contour.
1665 * (C) 2006 harry eaton.
1667 * (C) 1993 Klamer Schutte.
1669 * (C) 1997 Alexey Nikitin, Michael Leonov.
1672 static BOOLp
1673 label_contour (PLINE * a)
1675 VNODE *cure = &a->head; /* cur is considered an edge */
1676 VNODE *first_labelled = NULL;
1677 int label = UNKNWN;
1681 if (EDGE_BACKWARD_VERTEX (cure)->cvc_next) /* examine cross vertex */
1683 label = edge_label (cure, label);
1684 if (first_labelled == NULL && label != UNKNWN)
1685 first_labelled = cure;
1686 continue;
1689 if (first_labelled == NULL)
1690 continue;
1692 /* This labels nodes which aren't cross-connected */
1693 assert (label == INSIDE || label == OUTSIDE);
1694 LABEL_EDGE (cure, label);
1696 while ((cure = NEXT_EDGE (cure)) != first_labelled && (first_labelled != NULL || cure != &a->head));
1698 if (first_labelled == NULL)
1700 if (EDGE_LABEL (&a->head) == UNKNWN)
1702 g_warning ("Walked entire contour and couldn't find anything we could label - it is either all INSIDE or OUTSIDE");
1703 /* Mark the contour as NOT intersected, so it can be treated separately below. */
1704 /* XXX: Does this work with separated out intersected contours? */
1705 #warning... WE PROBABLY PRE-MARKED SOME EDGES TO AVOID TRAVERSAL.. (DUE TO SHARING WITHIN THE SAME POLYGON.. CAN WE JUST PASS IT ON??
1706 a->Flags.status = UNKNWN;
1708 else
1710 // g_info ("Walked entire contour and couldn't find anything we could label - it is either all SHARED OR SHARED2");
1711 /* Head was marked, so presumably the entire contour is either SHARED or SHARED2 */
1715 #warning The above loop could run forever if we encounter a contour where the only intersection gets nuked due to shared edges
1716 #ifdef DEBUG_ALL_LABELS
1717 print_labels (a);
1718 DEBUGP ("\n\n");
1719 #endif
1720 return FALSE;
1721 } /* label_contour */
1723 static BOOLp
1724 cntr_label_POLYAREA (PLINE * pline, POLYAREA * pa, BOOLp test)
1726 POLYAREA *inside_container;
1728 assert (pa != NULL && pa->contours != NULL);
1729 if (pline->Flags.status == ISECTED)
1731 label_contour (pline); /* should never get here when BOOLp is true */
1734 /* label_contour may decide the contour is NOT intersected, and we must fall through to the tests below */
1735 if (pline->Flags.status == ISECTED)
1736 return false;
1738 inside_container = cntr_in_M_POLYAREA (pline, pa, test);
1739 if (inside_container != NULL)
1741 if (test)
1742 return TRUE;
1743 pline->Flags.status = INSIDE;
1744 if (pline->is_inside != NULL)
1745 printf ("XXX: pline->is_inside was already set!\n");
1746 pline->is_inside = inside_container;
1748 else
1750 if (test)
1751 return false;
1752 pline->Flags.status = OUTSIDE;
1754 return FALSE;
1755 } /* cntr_label_POLYAREA */
1757 static BOOLp
1758 M_POLYAREA_label_separated (PLINE * afst, POLYAREA * b, BOOLp touch)
1760 PLINE *curc = afst;
1762 for (curc = afst; curc != NULL; curc = curc->next)
1764 if (cntr_label_POLYAREA (curc, b, touch) && touch)
1765 return TRUE;
1767 return FALSE;
1770 static BOOLp
1771 M_POLYAREA_label (POLYAREA * afst, POLYAREA * b, BOOLp touch)
1773 POLYAREA *a = afst;
1774 PLINE *curc;
1776 assert (a != NULL);
1779 for (curc = a->contours; curc != NULL; curc = curc->next)
1780 if (cntr_label_POLYAREA (curc, b, touch))
1782 if (touch)
1783 return TRUE;
1786 while (!touch && (a = a->f) != afst);
1787 return FALSE;
1792 * \brief Routines for temporary storing resulting contours.
1794 static void
1795 InsCntr (jmp_buf * e, PLINE * c, POLYAREA ** dst)
1797 POLYAREA *newp;
1799 newp = poly_Create ();
1800 if (newp == NULL)
1801 error(err_no_memory);
1803 if (*dst == NULL)
1805 newp->f = newp->b = newp;
1806 *dst = newp;
1808 else
1810 newp->f = *dst;
1811 newp->b = (*dst)->b;
1812 newp->f->b = newp->b->f = newp;
1815 c->next = NULL;
1816 newp->contours = c;
1817 r_insert_entry (newp->contour_tree, (BoxType *) c, 0);
1818 } /* InsCntr */
1820 static void
1821 PutContour (jmp_buf * e, PLINE * cntr, POLYAREA ** contours, PLINE ** holes,
1822 POLYAREA * owner, POLYAREA * parent, PLINE * parent_contour)
1824 assert (cntr != NULL);
1825 assert (cntr->Count > 2);
1826 cntr->next = NULL;
1828 if (cntr->Flags.orient == PLF_DIR)
1830 if (owner != NULL)
1831 r_delete_entry (owner->contour_tree, (BoxType *) cntr);
1832 InsCntr (e, cntr, contours);
1834 /* put hole into temporary list */
1835 else
1837 /* if we know this belongs inside the parent, put it there now */
1838 if (parent_contour)
1840 cntr->next = parent_contour->next;
1841 parent_contour->next = cntr;
1842 if (owner != parent)
1844 if (owner != NULL)
1845 r_delete_entry (owner->contour_tree, (BoxType *) cntr);
1846 r_insert_entry (parent->contour_tree, (BoxType *) cntr, 0);
1849 else
1851 cntr->next = *holes;
1852 *holes = cntr; /* let cntr be 1st hole in list */
1853 /* We don't insert the holes into an r-tree,
1854 * they just form a linked list */
1855 if (owner != NULL)
1856 r_delete_entry (owner->contour_tree, (BoxType *) cntr);
1859 } /* PutContour */
1861 static inline void
1862 remove_contour (POLYAREA * piece, PLINE * prev_contour, PLINE * contour,
1863 int remove_rtree_entry)
1865 if (piece->contours == contour)
1866 piece->contours = contour->next;
1867 else if (prev_contour != NULL)
1869 assert (prev_contour->next == contour);
1870 prev_contour->next = contour->next;
1873 contour->next = NULL;
1875 if (remove_rtree_entry)
1876 r_delete_entry (piece->contour_tree, (BoxType *) contour);
1879 struct polyarea_info
1881 BoxType BoundingBox;
1882 POLYAREA *pa;
1885 static int
1886 heap_it (const BoxType * b, void *cl)
1888 heap_t *heap = (heap_t *) cl;
1889 struct polyarea_info *pa_info = (struct polyarea_info *) b;
1890 PLINE *p = pa_info->pa->contours;
1891 if (p->Count == 0)
1892 return 0; /* how did this happen? */
1893 heap_insert (heap, p->area, pa_info);
1894 return 1;
1897 struct find_inside_info
1899 jmp_buf jb;
1900 PLINE *want_inside;
1901 PLINE *result;
1904 static int
1905 find_inside (const BoxType * b, void *cl)
1907 struct find_inside_info *info = (struct find_inside_info *) cl;
1908 PLINE *check = (PLINE *) b;
1909 /* Do test on check to see if it inside info->want_inside */
1910 /* If it is: */
1911 if (check->Flags.orient == PLF_DIR)
1913 return 0;
1915 if (poly_ContourInContour (info->want_inside, check))
1917 info->result = check;
1918 longjmp (info->jb, 1);
1920 return 0;
1923 /* Returns a string allocated with g_malloc family of functions */
1924 static char *
1925 merge_contour_name (char *old, const char *new)
1927 char *combined;
1929 if (old == NULL)
1930 return g_strdup (new);
1932 if (new == NULL)
1933 return old;
1935 if (strcmp (old, new) == 0)
1936 return old;
1938 /* XXX: BUG.. AS SOON AS WE GET A NAME CLASH, WE KEEP APPENDING ALL NEW NET-NAMES WE ENCOUNTER.... WE SHOULD ACTUALLY GATHER A LIST OF NAMES USED, THEN COLLATE AT THE END */
1939 combined = g_strdup_printf ("%s_%s", old, new);
1940 g_free (old);
1942 return combined;
1945 static void
1946 InsertHoles (jmp_buf * e, POLYAREA * dest, PLINE ** src)
1948 POLYAREA *curc;
1949 PLINE *curh, *container;
1950 heap_t *heap;
1951 rtree_t *tree;
1952 int i;
1953 int num_polyareas = 0;
1954 struct polyarea_info *all_pa_info, *pa_info;
1956 if (*src == NULL)
1957 return; /* empty hole list */
1958 if (dest == NULL)
1959 error (err_bad_parm); /* empty contour list */
1961 /* Count dest polyareas */
1962 curc = dest;
1965 num_polyareas++;
1967 while ((curc = curc->f) != dest);
1969 /* make a polyarea info table */
1970 /* make an rtree of polyarea info table */
1971 all_pa_info = (struct polyarea_info *) malloc (sizeof (struct polyarea_info) * num_polyareas);
1972 tree = r_create_tree (NULL, 0, 0);
1973 i = 0;
1974 curc = dest;
1977 all_pa_info[i].BoundingBox.X1 = curc->contours->xmin;
1978 all_pa_info[i].BoundingBox.Y1 = curc->contours->ymin;
1979 all_pa_info[i].BoundingBox.X2 = curc->contours->xmax;
1980 all_pa_info[i].BoundingBox.Y2 = curc->contours->ymax;
1981 all_pa_info[i].pa = curc;
1982 r_insert_entry (tree, (const BoxType *) &all_pa_info[i], 0);
1983 i++;
1985 while ((curc = curc->f) != dest);
1987 /* loop through the holes and put them where they belong */
1988 while ((curh = *src) != NULL)
1990 *src = curh->next;
1992 container = NULL;
1993 /* build a heap of all of the polys that the hole is inside its bounding box */
1994 heap = heap_create ();
1995 r_search (tree, (BoxType *) curh, NULL, heap_it, heap);
1996 if (heap_is_empty (heap))
1998 #ifndef NDEBUG
1999 #ifdef DEBUG
2000 poly_dump (dest);
2001 #endif
2002 #endif
2003 poly_DelContour (&curh);
2004 error (err_bad_parm);
2006 /* Now search the heap for the container. If there was only one item
2007 * in the heap, assume it is the container without the expense of
2008 * proving it.
2010 pa_info = (struct polyarea_info *) heap_remove_smallest (heap);
2011 if (heap_is_empty (heap))
2012 { /* only one possibility it must be the right one */
2013 // assert (poly_ContourInContour (pa_info->pa->contours, curh));
2014 container = pa_info->pa->contours;
2016 else
2020 if (poly_ContourInContour (pa_info->pa->contours, curh))
2022 container = pa_info->pa->contours;
2023 break;
2025 if (heap_is_empty (heap))
2026 break;
2027 pa_info = (struct polyarea_info *) heap_remove_smallest (heap);
2029 while (1);
2031 heap_destroy (&heap);
2032 if (container == NULL)
2034 /* bad input polygons were given */
2035 #ifndef NDEBUG
2036 #ifdef DEBUG
2037 poly_dump (dest);
2038 #endif
2039 #endif
2040 curh->next = NULL;
2041 poly_DelContour (&curh);
2042 error (err_bad_parm);
2044 else
2046 /* Need to check if this new hole means we need to kick out any old ones for reprocessing */
2047 while (1)
2049 struct find_inside_info info;
2050 PLINE *prev;
2052 info.want_inside = curh;
2054 /* Set jump return */
2055 if (setjmp (info.jb))
2057 /* Returned here! */
2059 else
2061 info.result = NULL;
2062 /* Rtree search, calling back a routine to longjmp back with data about any hole inside the added one */
2063 /* Be sure not to bother jumping back to report the main contour! */
2064 r_search (pa_info->pa->contour_tree, (BoxType *) curh, NULL,
2065 find_inside, &info);
2067 /* Nothing found? */
2068 break;
2071 /* We need to find the contour before it, so we can update its next pointer */
2072 prev = container;
2073 while (prev->next != info.result)
2075 prev = prev->next;
2078 /* Remove hole from the contour */
2079 remove_contour (pa_info->pa, prev, info.result, TRUE);
2081 /* Add hole as the next on the list to be processed in this very function */
2082 info.result->next = *src;
2083 *src = info.result;
2085 /* End check for kicked out holes */
2087 /* link at front of hole list */
2088 curh->next = container->next;
2089 container->next = curh;
2090 r_insert_entry (pa_info->pa->contour_tree, (BoxType *) curh, 0);
2092 /* Merge hole names into the outer contour name */
2093 container->name = merge_contour_name (container->name, curh->name);
2097 r_destroy_tree (&tree);
2098 free (all_pa_info);
2099 } /* InsertHoles */
2102 /* routines for collecting result */
2104 typedef enum
2106 UNINITIALISED, FORW, BACKW
2107 } DIRECTION;
2110 * \brief Jump Rule.
2112 * e is considered an edge
2114 typedef int (*J_Rule) (char p, VNODE *e, DIRECTION *cdir);
2116 /* e is considered an edge */
2117 static int
2118 IsectJ_Rule (char p, VNODE *e, DIRECTION * cdir)
2120 *cdir = FORW;
2121 return (e->Flags.status == INSIDE || e->Flags.status == SHARED);
2124 /* e is considered an edge */
2125 static int
2126 UniteJ_Rule (char p, VNODE *e, DIRECTION * cdir)
2128 *cdir = FORW;
2129 return (e->Flags.status == OUTSIDE || e->Flags.status == SHARED);
2132 /* e is considered an edge */
2133 static int
2134 XorJ_Rule (char p, VNODE *e, DIRECTION * cdir)
2136 if (e->Flags.status == INSIDE)
2138 *cdir = BACKW;
2139 return TRUE;
2141 if (e->Flags.status == OUTSIDE)
2143 *cdir = FORW;
2144 return TRUE;
2146 return FALSE;
2149 /* e is considered an edge */
2150 static int
2151 SubJ_Rule (char p, VNODE *e, DIRECTION * cdir)
2153 if (p == 'B' && e->Flags.status == INSIDE)
2155 *cdir = BACKW;
2156 return TRUE;
2158 if (p == 'A' && e->Flags.status == OUTSIDE)
2160 *cdir = FORW;
2161 return TRUE;
2163 if (e->Flags.status == SHARED2)
2165 if (p == 'A')
2166 *cdir = FORW;
2167 else
2168 *cdir = BACKW;
2169 return TRUE;
2171 return FALSE;
2175 * \brief Return the edge that comes next.
2177 * If the direction is BACKW, then we return the next vertex so that
2178 * prev vertex has the flags for the edge.
2180 * \return true if an edge is found, false otherwise.
2182 /* *curv is considered a vertex */
2183 static int
2184 jump (VNODE **curv, DIRECTION *cdir, J_Rule j_rule, char **contour_name)
2186 CVCList *d, *start, *incoming;
2187 VNODE *e; /* e is considered an edge */
2188 DIRECTION newone;
2190 if (!(*curv)->cvc_prev) /* not a cross-vertex */
2192 if (VERTEX_DIRECTION_EDGE (*curv, *cdir)->Flags.mark)
2193 return FALSE;
2194 return TRUE;
2196 #ifdef DEBUG_JUMP
2197 DEBUGP ("jump entering node at (%$mn, %$mn)\n", (*curv)->point[0], (*curv)->point[1]);
2198 #endif
2200 /* Pick the descriptor of the edge we came into this vertex with */
2201 incoming = (*cdir == FORW) ? (*curv)->cvc_prev : (*curv)->cvc_next;
2204 /* First check for any shared edge.. might be sorted inconsistently */
2206 d = incoming;
2207 while (compare_cvc_nodes (d, d->next) == 0)
2209 /* Get the edge e, associated with that descriptor */
2210 e = VERTEX_SIDE_DIR_EDGE (d->parent, d->side);
2211 newone = *cdir;
2212 if (!e->Flags.mark && j_rule (d->poly, e, &newone))
2214 if ((d->side == 'N' && newone == FORW) ||
2215 (d->side == 'P' && newone == BACKW))
2217 #ifdef DEBUG_JUMP
2218 DEBUGP ("REVERSE SPIN jump leaving node at (%$mn, %$mn)\n",
2219 EDGE_DIRECTION_VERTEX (e, newone)->point[0], EDGE_DIRECTION_VERTEX (e, newone)->point[1]);
2220 #endif
2221 *curv = d->parent;
2222 *cdir = newone;
2223 *contour_name = merge_contour_name (*contour_name, d->parent_contour->name);
2224 return TRUE;
2227 d = d->next;
2230 /* Spin (anti?)clock-wise one edge descriptor (normal case) */
2231 d = incoming->prev;
2233 start = d;
2236 /* Get the edge e, associated with that descriptor */
2237 e = VERTEX_SIDE_DIR_EDGE (d->parent, d->side);
2238 newone = *cdir;
2239 if (!e->Flags.mark && j_rule (d->poly, e, &newone))
2241 if ((d->side == 'N' && newone == FORW) ||
2242 (d->side == 'P' && newone == BACKW))
2244 #ifdef DEBUG_JUMP
2245 DEBUGP ("jump leaving node at (%$mn, %$mn)\n",
2246 EDGE_DIRECTION_VERTEX (e, newone)->point[0], EDGE_DIRECTION_VERTEX (e, newone)->point[1]);
2247 #endif
2248 *curv = d->parent;
2249 *cdir = newone;
2250 *contour_name = merge_contour_name (*contour_name, d->parent_contour->name);
2251 return TRUE;
2255 while ((d = d->prev) != start); /* Keep searching around the cvc vertex for a suitable exit edge */
2256 return FALSE;
2259 /* start is considered a vertex */
2260 static int
2261 Gather (VNODE *startv, PLINE **result, J_Rule j_rule, DIRECTION initdir, char **contour_name)
2263 VNODE *curv = startv; /* curv is considered a vertex */
2264 VNODE *newn;
2265 DIRECTION dir = initdir;
2266 #ifdef DEBUG_GATHER
2267 DEBUGP ("gather direction = %d\n", dir);
2268 #endif
2269 assert (*result == NULL);
2272 /* add vertex (edge?) to polygon */
2273 if ((newn = poly_CreateNodeFull (curv->point, VERTEX_DIRECTION_EDGE (curv, dir)->is_round,
2274 VERTEX_DIRECTION_EDGE (curv, dir)->cx,
2275 VERTEX_DIRECTION_EDGE (curv, dir)->cy,
2276 VERTEX_DIRECTION_EDGE (curv, dir)->radius)) == NULL)
2277 return err_no_memory;
2278 if (!*result)
2280 *result = poly_NewContour (newn);
2281 if (*result == NULL)
2282 return err_no_memory;
2284 else
2286 poly_InclVertex (PREV_VERTEX (&(*result)->head), newn);
2288 #ifdef DEBUG_GATHER
2289 DEBUGP ("gather vertex at (%$mn, %$mn), Dir=%i\n", curv->point[0], curv->point[1], dir);
2290 #endif
2292 /* Now mark the edge as included. */
2293 newn = VERTEX_DIRECTION_EDGE (curv, dir);
2294 newn->Flags.mark = 1;
2295 /* for SHARED edge mark both */
2296 if (newn->shared)
2297 newn->shared->Flags.mark = 1;
2299 /* Advance to the next vertex (edge?). */
2300 curv = (dir == FORW) ? NEXT_VERTEX (curv) : PREV_VERTEX (curv);
2302 /* see where to go next */
2303 if (!jump (&curv, &dir, j_rule, contour_name))
2304 break;
2306 while (1);
2308 if (*contour_name != NULL)
2310 // fprintf (stderr, "Setting contour name on intersected contour as %s\n", *contour_name);
2311 (*result)->name = strdup (*contour_name);
2314 g_free (*contour_name);
2315 *contour_name = NULL;
2317 return err_ok;
2318 } /* Gather */
2320 /* curv is considered a vertex */
2321 static void
2322 Collect1 (jmp_buf *e, VNODE *curv, DIRECTION dir, POLYAREA **contours,
2323 PLINE **holes, J_Rule j_rule, char **contour_name)
2325 PLINE *p = NULL; /* start making contour */
2326 int errc = err_ok;
2327 if ((errc = Gather (curv, &p, j_rule, dir, contour_name)) != err_ok)
2329 if (p != NULL)
2330 poly_DelContour (&p);
2331 error (errc);
2333 if (!p)
2334 return;
2335 poly_PreContour (p, TRUE);
2336 if (p->Count > 2)
2338 #ifdef DEBUG_GATHER
2339 DEBUGP ("adding contour with %d verticies and direction %c\n",
2340 p->Count, p->Flags.orient ? 'F' : 'B');
2341 #endif
2342 PutContour (e, p, contours, holes, NULL, NULL, NULL);
2344 else
2346 /* some sort of computation error ? */
2347 #ifdef DEBUG_GATHER
2348 DEBUGP ("Bad contour! Not enough points!!\n");
2349 #endif
2350 poly_DelContour (&p);
2354 static void
2355 Collect (char poly, jmp_buf * e, PLINE * a, POLYAREA ** contours, PLINE ** holes,
2356 J_Rule j_rule)
2358 VNODE *cure; /* cure is considered an edge */
2359 char *contour_name = merge_contour_name (NULL, a->name);
2360 DIRECTION dir = UNINITIALISED;
2362 cure = (&a->head);
2363 // cur = (&a->head)->next->next->next->next->next; /* Breaks circ_seg_test9.pcb */
2366 #if 0
2367 // The following may be a nice speedup, but not sure if it is correct.
2368 // In particular, consider the case when we collect a 'B' polygon contour.
2369 // Could some of that countour may already have been collected, and there
2370 // still be a piece we are interested in after? (Can we reach it though??)
2371 if (cure->Flags.mark != 0)
2372 break;
2373 #endif
2375 if (j_rule (poly, cure, &dir) && cure->Flags.mark == 0)
2376 Collect1 (e, (dir == FORW) ? EDGE_BACKWARD_VERTEX (cure) :
2377 EDGE_FORWARD_VERTEX (cure),
2378 dir, contours, holes, j_rule, &contour_name);
2380 while ((cure = NEXT_EDGE (cure)) != &a->head);
2381 } /* Collect */
2384 static int
2385 cntr_Collect (jmp_buf * e, PLINE ** A, POLYAREA ** contours, PLINE ** holes,
2386 int action, POLYAREA * owner, POLYAREA * parent,
2387 PLINE * parent_contour)
2389 PLINE *tmprev;
2391 if ((*A)->Flags.status == ISECTED)
2393 switch (action)
2395 case PBO_UNITE:
2396 Collect ('A', e, *A, contours, holes, UniteJ_Rule);
2397 break;
2398 case PBO_ISECT:
2399 Collect ('A', e, *A, contours, holes, IsectJ_Rule);
2400 break;
2401 case PBO_XOR:
2402 Collect ('A', e, *A, contours, holes, XorJ_Rule);
2403 break;
2404 case PBO_SUB:
2405 Collect ('A', e, *A, contours, holes, SubJ_Rule);
2406 break;
2409 else
2411 switch (action)
2413 case PBO_ISECT:
2414 if ((*A)->Flags.status == INSIDE)
2416 tmprev = *A;
2417 /* disappear this contour (rtree entry removed in PutContour) */
2418 *A = tmprev->next;
2419 tmprev->next = NULL;
2420 PutContour (e, tmprev, contours, holes, owner, NULL, NULL);
2421 return TRUE;
2423 break;
2424 case PBO_XOR:
2425 if ((*A)->Flags.status == INSIDE)
2427 tmprev = *A;
2428 /* disappear this contour (rtree entry removed in PutContour) */
2429 *A = tmprev->next;
2430 tmprev->next = NULL;
2431 poly_InvContour (tmprev);
2432 PutContour (e, tmprev, contours, holes, owner, NULL, NULL);
2433 return TRUE;
2435 /* break; *//* Fall through (I think this is correct! pcjc2) */
2436 case PBO_UNITE:
2437 case PBO_SUB:
2438 if ((*A)->Flags.status == OUTSIDE)
2440 tmprev = *A;
2441 /* disappear this contour (rtree entry removed in PutContour) */
2442 *A = tmprev->next;
2443 tmprev->next = NULL;
2444 PutContour (e, tmprev, contours, holes, owner, parent,
2445 parent_contour);
2446 return TRUE;
2448 break;
2451 return FALSE;
2452 } /* cntr_Collect */
2454 static void
2455 M_B_AREA_Collect (jmp_buf * e, POLYAREA * bfst, POLYAREA ** contours,
2456 PLINE ** holes, int action)
2458 POLYAREA *b = bfst;
2459 PLINE **cur, **next, *tmp;
2461 assert (b != NULL);
2464 for (cur = &b->contours; *cur != NULL; cur = next)
2466 next = &((*cur)->next);
2467 if ((*cur)->Flags.status == ISECTED)
2469 /* Check for missed intersect contours here. These can come from
2470 * cases where contours of A and B touch at a single-vertex, so
2471 * are labeled ISECTED for processing, yet our JUMP rules for a
2472 * particular operation does not deem to jump between A & B
2473 * contours at the shared vertex.
2475 * NB: There Could be grief if the JUMP rule is inconsistent in
2476 * its interpretation from each side of the vertex.
2478 switch (action)
2480 case PBO_UNITE:
2481 Collect ('B', e, *cur, contours, holes, UniteJ_Rule);
2482 break;
2483 case PBO_ISECT:
2484 Collect ('B', e, *cur, contours, holes, IsectJ_Rule);
2485 break;
2486 case PBO_XOR:
2487 Collect ('B', e, *cur, contours, holes, XorJ_Rule);
2488 break;
2489 case PBO_SUB:
2490 Collect ('B', e, *cur, contours, holes, SubJ_Rule);
2491 break;
2494 else if ((*cur)->Flags.status == INSIDE)
2495 switch (action)
2497 case PBO_XOR:
2498 case PBO_SUB:
2499 poly_InvContour (*cur);
2500 case PBO_ISECT:
2501 tmp = *cur;
2502 *cur = tmp->next;
2503 next = cur;
2504 tmp->next = NULL;
2505 tmp->Flags.status = UNKNWN;
2506 PutContour (e, tmp, contours, holes, b, NULL, NULL);
2507 break;
2508 case PBO_UNITE:
2509 if ((*cur)->name != NULL) {
2510 printf ("XXX: Dropping named contour %s during UNITE operation... merging name into containing contour...\n", (*cur)->name);
2511 if ((*cur)->is_inside != NULL)
2513 PLINE *inside_contour = (*cur)->is_inside->contours;
2514 /* XXX: Could this pointer now be stale? A lot can happen to contours in this algorithm! */
2516 printf ("XXX: OLD NAMES: %s, %s\n", inside_contour->name, (*cur)->name);
2517 inside_contour->name = merge_contour_name (inside_contour->name, (*cur)->name);
2518 printf ("XXX: NEW CONTAINER NAME: %s\n", inside_contour->name);
2520 else
2522 printf ("XXX: Seems we didn't record which contaour we were UNITE'd inside?\n");
2525 break; /* nothing to do - already included */
2527 else if ((*cur)->Flags.status == OUTSIDE)
2528 switch (action)
2530 case PBO_XOR:
2531 case PBO_UNITE:
2532 /* include */
2533 tmp = *cur;
2534 *cur = tmp->next;
2535 next = cur;
2536 tmp->next = NULL;
2537 tmp->Flags.status = UNKNWN;
2538 PutContour (e, tmp, contours, holes, b, NULL, NULL);
2539 break;
2540 case PBO_ISECT:
2541 case PBO_SUB:
2542 break; /* do nothing, not included */
2546 while ((b = b->f) != bfst);
2550 static inline int
2551 contour_is_first (POLYAREA * a, PLINE * cur)
2553 return (a->contours == cur);
2557 static inline int
2558 contour_is_last (PLINE * cur)
2560 return (cur->next == NULL);
2564 static inline void
2565 remove_polyarea (POLYAREA ** list, POLYAREA * piece)
2567 /* If this item was the start of the list, advance that pointer */
2568 if (*list == piece)
2569 *list = (*list)->f;
2571 /* But reset it to NULL if it wraps around and hits us again */
2572 if (*list == piece)
2573 *list = NULL;
2575 piece->b->f = piece->f;
2576 piece->f->b = piece->b;
2577 piece->f = piece->b = piece;
2579 /* Reset parentage information */
2580 piece->parentage = no_parentage;
2583 static void
2584 M_POLYAREA_separate_isected (jmp_buf * e, POLYAREA ** pieces,
2585 PLINE ** holes, PLINE ** isected)
2587 POLYAREA *a = *pieces;
2588 POLYAREA *anext;
2589 PLINE *curc, *next, *prev;
2590 int finished;
2592 if (a == NULL)
2593 return;
2595 /* TODO: STASH ENOUGH INFORMATION EARLIER ON, SO WE CAN REMOVE THE INTERSECTED
2596 CONTOURS WITHOUT HAVING TO WALK THE FULL DATA-STRUCTURE LOOKING FOR THEM. */
2600 int hole_contour = 0;
2601 int is_outline = 1;
2603 anext = a->f;
2604 finished = (anext == *pieces);
2606 prev = NULL;
2607 for (curc = a->contours; curc != NULL; curc = next, is_outline = 0)
2609 int is_first = contour_is_first (a, curc);
2610 int is_last = contour_is_last (curc);
2611 int isect_contour = (curc->Flags.status == ISECTED);
2612 // if (isect_contour && curc->name != NULL)
2613 // printf ("A contour with name %s was ISECTED\n", curc->name);
2615 next = curc->next;
2617 if (isect_contour || hole_contour)
2620 /* Reset the intersection flags, since we keep these pieces */
2621 if (curc->Flags.status != ISECTED)
2622 curc->Flags.status = UNKNWN;
2624 remove_contour (a, prev, curc, !(is_first && is_last));
2626 if (isect_contour)
2628 /* Link into the list of intersected contours */
2629 curc->next = *isected;
2630 *isected = curc;
2632 else if (hole_contour)
2634 /* Link into the list of holes */
2635 curc->next = *holes;
2636 *holes = curc;
2638 else
2640 assert (0);
2643 if (is_first && is_last)
2645 remove_polyarea (pieces, a);
2646 poly_Free (&a); /* NB: Sets a to NULL */
2650 else
2652 /* Note the item we just didn't delete as the next
2653 candidate for having its "next" pointer adjusted.
2654 Saves walking the contour list when we delete one. */
2655 prev = curc;
2658 /* If we move or delete an outer contour, we need to move any holes
2659 we wish to keep within that contour to the holes list. */
2660 if (is_outline && isect_contour)
2661 hole_contour = 1;
2665 /* If we deleted all the pieces of the polyarea, *pieces is NULL */
2667 while ((a = anext), *pieces != NULL && !finished);
2671 struct find_inside_m_pa_info
2673 jmp_buf jb;
2674 POLYAREA *want_inside;
2675 POLYAREA *is_inside;
2676 PLINE *result;
2679 static int
2680 find_inside_m_pa (const BoxType * b, void *cl)
2682 struct find_inside_m_pa_info *info = (struct find_inside_m_pa_info *) cl;
2683 PLINE *check = (PLINE *) b;
2684 POLYAREA *is_inside;
2686 /* Don't report for the main contour */
2687 if (check->Flags.orient == PLF_DIR)
2688 return 0;
2689 /* Don't look at contours marked as being intersected */
2690 if (check->Flags.status == ISECTED)
2691 return 0;
2692 info->is_inside = cntr_in_M_POLYAREA (check, info->want_inside, FALSE);
2693 if (info->is_inside != NULL)
2695 info->result = check;
2696 longjmp (info->jb, 1);
2698 return 0;
2702 static void
2703 M_POLYAREA_update_primary (jmp_buf * e, POLYAREA ** pieces,
2704 PLINE ** holes, int action, POLYAREA * bpa)
2706 POLYAREA *a = *pieces;
2707 POLYAREA *b;
2708 POLYAREA *anext;
2709 PLINE *curc, *next, *prev;
2710 BoxType box;
2711 /* int inv_inside = 0; */
2712 int del_inside = 0;
2713 int del_outside = 0;
2714 int finished;
2716 if (a == NULL)
2717 return;
2719 switch (action)
2721 case PBO_ISECT:
2722 del_outside = 1;
2723 break;
2724 case PBO_UNITE:
2725 case PBO_SUB:
2726 del_inside = 1;
2727 break;
2728 case PBO_XOR: /* NOT IMPLEMENTED OR USED */
2729 /* inv_inside = 1; */
2730 assert (0);
2731 break;
2734 box = *((BoxType *) bpa->contours);
2735 b = bpa;
2736 while ((b = b->f) != bpa)
2738 BoxType *b_box = (BoxType *) b->contours;
2739 MAKEMIN (box.X1, b_box->X1);
2740 MAKEMIN (box.Y1, b_box->Y1);
2741 MAKEMAX (box.X2, b_box->X2);
2742 MAKEMAX (box.Y2, b_box->Y2);
2745 if (del_inside)
2750 POLYAREA *inside_container;
2751 PLINE *inside_contour;
2753 anext = a->f;
2754 finished = (anext == *pieces);
2756 /* Test the outer contour first, as we may need to remove all children */
2758 /* We've not yet split intersected contours out, just ignore them */
2759 if (a->contours->Flags.status != ISECTED &&
2760 /* Pre-filter on bounding box */
2761 ((a->contours->xmin >= box.X1) && (a->contours->ymin >= box.Y1)
2762 && (a->contours->xmax <= box.X2)
2763 && (a->contours->ymax <= box.Y2)) &&
2764 /* Then test properly */
2765 (inside_container = cntr_in_M_POLYAREA (a->contours, bpa, FALSE)) != NULL)
2767 inside_contour = inside_container->contours;
2769 /* Delete this contour, all children -> holes queue */
2771 /* Delete the outer contour */
2772 curc = a->contours;
2773 if (curc->name != NULL)
2775 printf ("XXX: Dropping named contour %s during operation (1)... merging name into containing contour...\n", curc->name);
2776 printf ("XXX: OLD NAMES: %s, %s\n", inside_contour->name, curc->name);
2778 inside_contour->name = merge_contour_name (inside_contour->name, curc->name);
2779 if (curc->name != NULL)
2781 printf ("XXX: NEW CONTAINER NAME: %s\n", inside_contour->name);
2783 remove_contour (a, NULL, curc, FALSE); /* Rtree deleted in poly_Free below */
2784 /* a->contours now points to the remaining holes */
2785 poly_DelContour (&curc);
2787 if (a->contours != NULL)
2789 /* Find the end of the list of holes */
2790 curc = a->contours;
2791 while (curc->next != NULL)
2792 curc = curc->next;
2794 if (curc->name != NULL)
2795 printf ("XXX: Placing holes after deleted outer contour into the holes list\n");
2796 /* Take the holes and prepend to the holes queue */
2797 curc->next = *holes;
2798 *holes = a->contours;
2799 a->contours = NULL;
2802 remove_polyarea (pieces, a);
2803 poly_Free (&a); /* NB: Sets a to NULL */
2805 continue;
2808 /* Loop whilst we find INSIDE contours to delete */
2809 while (1)
2811 struct find_inside_m_pa_info info;
2812 PLINE *prev;
2813 PLINE *inside_contour = NULL;
2815 info.want_inside = bpa;
2817 /* Set jump return */
2818 if (setjmp (info.jb))
2820 /* Returned here! */
2821 inside_contour = info.is_inside->contours;
2823 else
2825 info.result = NULL;
2826 /* r-tree search, calling back a routine to longjmp back with
2827 * data about any hole inside the B polygon.
2828 * NB: Does not jump back to report the main contour!
2830 r_search (a->contour_tree, &box, NULL, find_inside_m_pa,
2831 &info);
2833 /* Nothing found? */
2834 break;
2837 /* We need to find the contour before it, so we can update its next pointer */
2838 prev = a->contours;
2839 while (prev->next != info.result)
2841 prev = prev->next;
2844 /* Remove hole from the contour */
2845 if (info.result->name != NULL)
2847 printf ("XXX: Dropping named contour %s during operation (2)... merging name into containing contour...\n", info.result->name);
2848 printf ("XXX: OLD NAMES: %s, %s\n", inside_contour->name, info.result->name);
2850 inside_contour->name = merge_contour_name (inside_contour->name, info.result->name);
2851 if (info.result->name != NULL)
2853 printf ("XXX: NEW CONTAINER NAME: %s\n", inside_contour->name);
2855 remove_contour (a, prev, info.result, TRUE);
2856 poly_DelContour (&info.result);
2858 /* End check for deleted holes */
2860 /* If we deleted all the pieces of the polyarea, *pieces is NULL */
2862 while ((a = anext), *pieces != NULL && !finished);
2864 return;
2866 else
2868 /* This path isn't optimised for speed */
2873 int hole_contour = 0;
2874 int is_outline = 1;
2876 anext = a->f;
2877 finished = (anext == *pieces);
2879 prev = NULL;
2880 for (curc = a->contours; curc != NULL; curc = next, is_outline = 0)
2882 int is_first = contour_is_first (a, curc);
2883 int is_last = contour_is_last (curc);
2884 int del_contour = 0;
2886 next = curc->next;
2888 if (del_outside)
2889 del_contour = curc->Flags.status != ISECTED &&
2890 cntr_in_M_POLYAREA (curc, bpa, FALSE) == NULL;
2892 /* Skip intersected contours */
2893 if (curc->Flags.status == ISECTED)
2895 prev = curc;
2896 continue;
2899 /* Reset the intersection flags, since we keep these pieces */
2900 curc->Flags.status = UNKNWN;
2902 if (del_contour || hole_contour)
2905 remove_contour (a, prev, curc, !(is_first && is_last));
2907 if (del_contour)
2909 /* Delete the contour */
2910 if (curc->name != NULL)
2912 printf ("XXX: Dropping named contour %s during operation (3)... name probably ok to be dropped? del_outside=%s\n",
2913 curc->name, del_outside ? "true" : "false");
2915 poly_DelContour (&curc); /* NB: Sets curc to NULL */
2917 else if (hole_contour)
2919 if (curc->name != NULL)
2920 printf ("XXX: Placing named contour %s into the holes list\n", curc->name);
2921 /* Link into the list of holes */
2922 curc->next = *holes;
2923 *holes = curc;
2925 else
2927 assert (0);
2930 if (is_first && is_last)
2932 remove_polyarea (pieces, a);
2933 poly_Free (&a); /* NB: Sets a to NULL */
2937 else
2939 /* Note the item we just didn't delete as the next
2940 candidate for having its "next" pointer adjusted.
2941 Saves walking the contour list when we delete one. */
2942 prev = curc;
2945 /* If we move or delete an outer contour, we need to move any holes
2946 we wish to keep within that contour to the holes list. */
2947 if (is_outline && del_contour)
2948 hole_contour = 1;
2952 /* If we deleted all the pieces of the polyarea, *pieces is NULL */
2954 while ((a = anext), *pieces != NULL && !finished);
2957 static void
2958 M_POLYAREA_Collect_separated (jmp_buf * e, PLINE * afst, POLYAREA ** contours,
2959 PLINE ** holes, int action, BOOLp maybe)
2961 PLINE **cur, **next;
2963 for (cur = &afst; *cur != NULL; cur = next)
2965 next = &((*cur)->next);
2966 /* if we disappear a contour, don't advance twice */
2967 if (cntr_Collect (e, cur, contours, holes, action, NULL, NULL, NULL))
2968 next = cur;
2970 /* XXX: What about rogue contours we re-labelled as INSIDE or OUTSIDE - ARE THEY DEALT WITH OK? */
2973 static void
2974 M_POLYAREA_Collect (jmp_buf * e, POLYAREA * afst, POLYAREA ** contours,
2975 PLINE ** holes, int action, BOOLp maybe)
2977 POLYAREA *a = afst;
2978 POLYAREA *parent = NULL; /* Quiet compiler warning */
2979 PLINE **cur, **next, *parent_contour;
2981 assert (a != NULL);
2982 while ((a = a->f) != afst);
2983 /* now the non-intersect parts are collected in temp/holes */
2986 if (maybe && a->contours->Flags.status != ISECTED)
2987 parent_contour = a->contours;
2988 else
2989 parent_contour = NULL;
2991 /* Take care of the first contour - so we know if we
2992 * can shortcut reparenting some of its children
2994 cur = &a->contours;
2995 if (*cur != NULL)
2997 next = &((*cur)->next);
2998 /* if we disappear a contour, don't advance twice */
2999 if (cntr_Collect (e, cur, contours, holes, action, a, NULL, NULL))
3001 parent = (*contours)->b; /* InsCntr inserts behind the head */
3002 next = cur;
3004 else
3005 parent = a;
3006 cur = next;
3008 for (; *cur != NULL; cur = next)
3010 next = &((*cur)->next);
3011 /* if we disappear a contour, don't advance twice */
3012 if (cntr_Collect (e, cur, contours, holes, action, a, parent,
3013 parent_contour))
3014 next = cur;
3017 while ((a = a->f) != afst);
3020 static CVCList *
3021 add_dummy_descriptors_at_point_from_pline (Vector point, PLINE * pl, char poly, CVCList * list)
3023 VNODE *node = &pl->head; /* node is considered a vertex */
3027 if (vect_equal (node->point, point))
3029 if (node->cvc_prev == NULL)
3031 list = node->cvc_prev = insert_descriptor (node, pl, poly, 'P', list);
3032 g_return_val_if_fail (node->cvc_prev != NULL, NULL);
3034 if (node->cvc_next == NULL)
3036 list = node->cvc_next = insert_descriptor (node, pl, poly, 'N', list);
3037 g_return_val_if_fail (node->cvc_next != NULL, NULL);
3041 while ((node = NEXT_VERTEX(node)) != &pl->head);
3042 return list;
3045 static void
3046 add_dummy_descriptors_at_point (Vector point, char poly, CVCList * list, POLYAREA *bfst)
3048 POLYAREA *b = bfst;
3049 PLINE *cur;
3051 assert (b != NULL);
3054 for (cur = b->contours; cur != NULL; cur = cur->next)
3056 if (cur->Flags.status == ISECTED)
3058 add_dummy_descriptors_at_point_from_pline (point, cur, poly, list);
3062 while ((b = b->f) != bfst);
3066 static void
3067 mark_cvc_list_entry_as_skip (CVCList *l)
3069 if (l == NULL)
3070 return;
3072 l->skip_me = true;
3075 static CVCList *
3076 next_cvc_from_same_poly (CVCList *start)
3078 CVCList *n;
3080 n = start->next;
3082 /* Find the next edge from the same polygon */
3083 while (n->poly != start->poly)
3084 n = n->next;
3086 return n;
3089 #if 0
3090 static seg *
3091 find_edge_seg (VNODE *edge, PLINE *contour)
3093 struct info info;
3094 BoxType box;
3096 /* fill in the segment in info corresponding to this node */
3097 info.v = edge;
3098 info.s = NULL;
3099 box.X2 = (box.X1 = EDGE_BACKWARD_VERTEX (edge)->point[0]) + 1;
3100 box.Y2 = (box.Y1 = EDGE_BACKWARD_VERTEX (edge)->point[1]) + 1;
3101 // fprintf (stderr, "NEED TO FIND SEGMENT FOR EDGE %p\n", info.v);
3102 if (setjmp (info.sego) == 0)
3104 // info.debug = true;
3105 info.debug = false;
3106 r_search (contour->tree, &box, NULL, get_seg, &info);
3107 /* Didn't find the edge if we get here */
3109 return info.s;
3112 static bool
3113 is_edge_in_contour (VNODE *edge, PLINE *contour)
3115 VNODE *e = &contour->head;
3118 if (e == edge)
3119 return true;
3121 while ((e = NEXT_EDGE(e)) != &contour->head);
3122 return false;
3125 static CVCList *
3126 find_cvc_at_point (CVCList *start, Vector point)
3128 CVCList *l;
3130 l = start;
3133 assert (l->head);
3135 if (vect_equal (l->parent->point, point)) /* this CVCList is at our point */
3136 return l;
3138 if (vect_equal (l->head->parent->point, start->parent->point)) /* Check for wrap-around in the list */
3139 return NULL;
3141 l = l->head;
3143 while (1);
3145 #endif
3147 /* NOTE: If any contour is split into multiple pieces due to hairline edge pairs
3148 * will not necessarily be inserted into the correct location. Hole contours
3149 * should be OK, but if we have a B polygon outline contour that gets split into
3150 * two, rather than creating a new POLYAREA and redistributing holes, we just
3151 * leave one half of the contour as the "outer", and the other as if it were a
3152 * hole contour. This will cause the polygon to be invalid temporarily, but it
3153 * should be dealt with successfully by the remaining steps of the boolean operation.
3155 static void
3156 PLINE_check_hairline_edges (CVCList *the_list, PLINE *contour, POLYAREA *bfst)
3158 VNODE *v;
3159 CVCList *l, *first_l, *n, *nn;
3160 int test_count;
3161 bool terminate_after_this_iteration;
3163 /* Scan the PLINE and check for naughty shared edge segments */
3164 v = &contour->head;
3167 if (v->cvc_prev == NULL &&
3168 v->cvc_next == NULL) /* Careful, these can be NULL independantly now! */
3169 continue;
3171 /* Just one which isn't NULL, doesn't matter where we start
3172 * circling the CVCList, as long as we start from a descriptor
3173 * belonging to this polygon
3175 if (v->cvc_prev != NULL)
3176 first_l = v->cvc_prev;
3177 else
3178 first_l = v->cvc_next;
3180 test_count = 0;
3181 terminate_after_this_iteration = false;
3182 l = first_l;
3185 n = next_cvc_from_same_poly (l);
3187 /* Skip testing if we wrapped around, and only had one pair to test */
3188 if (n == first_l && test_count == 1)
3189 break;
3191 #if 1
3192 /* Not sure why we get this, but apparently we can! */
3193 if (n == l)
3195 g_warning ("Wrapped around and found ourselves in the CVCList.. not quite sure how we managed that\n"
3196 "Did we perhaps delete the start descriptor? Odd number of entries in a CVCList??");
3197 break;
3199 #endif
3201 /* Check for hairline pairs of edges in the CVCList, they may be sorted in incorrect order,
3202 * and would thus mislead as to whether we are inside or outside a given contour. It is a
3203 * bug if such edges are present, so test for it here where we may detect it. We compare
3204 * l->prev and l, as we know both are still in this_poly.. l->next may not be.
3206 if (compare_cvc_nodes (l, n) == 0)
3208 VNODE *l_otherend = EDGE_SIDE_DIR_VERTEX (VERTEX_SIDE_DIR_EDGE (l->parent, l->side), l->side);
3209 VNODE *n_otherend = EDGE_SIDE_DIR_VERTEX (VERTEX_SIDE_DIR_EDGE (n->parent, n->side), n->side);
3210 VNODE *point_v; /* As vertex */
3212 if (vect_equal (l_otherend->point, n_otherend->point))
3214 /* Mark the shared edges from being used in any possibly existing
3215 * cross-connected node at their other ends.
3217 * NB: This doesn't apply to the case below where edge geometry is different, as we insert an
3218 * additional vertex, and can be pretty sure that vertex will not be cross-connected. The
3219 * longer edge may land at a cross-connected point, and we need to leave that descriptor.
3222 /* NOTE: 'P' at this node means 'N' at otherend */
3223 mark_cvc_list_entry_as_skip ((l->side == 'P') ? l_otherend->cvc_next : l_otherend->cvc_prev);
3224 mark_cvc_list_entry_as_skip ((n->side == 'P') ? n_otherend->cvc_next : n_otherend->cvc_prev);
3226 point_v = l_otherend; /* Vertex end where we will ensure descriptors exist */
3229 else
3231 VNODE *longer; /* As vertex at our CVC node */
3232 VNODE *shorter; /* As vertex at our CVC node */
3233 VNODE *new_node;
3234 char longer_side;
3235 char shorter_side;
3236 // seg *node_seg;
3238 /* Pick which edge is longer, to insert into.
3239 * NOTE: Should work for arcs less than 180 degrees span
3241 if (vect_dist2 (l_otherend->point, l->parent->point) >
3242 vect_dist2 (n_otherend->point, n->parent->point))
3244 longer = l->parent;
3245 shorter = n->parent;
3246 longer_side = l->side;
3247 shorter_side = n->side;
3249 else
3251 longer = n->parent;
3252 shorter = l->parent;
3253 longer_side = n->side;
3254 shorter_side = l->side;
3257 #if 0
3258 node_seg = find_edge_seg (VERTEX_SIDE_DIR_EDGE (longer, longer_side), contour);
3259 if (node_seg == NULL)
3260 g_error ("Did not find segment in contour tree!");
3261 g_assert (node_seg != NULL);
3262 #endif
3264 /* Vertex end where we will ensure descriptors exist, after splitting the longer edge there */
3265 point_v = EDGE_SIDE_DIR_VERTEX (VERTEX_SIDE_DIR_EDGE (shorter, shorter_side), shorter_side);
3267 new_node = node_add_single_point (VERTEX_SIDE_DIR_EDGE (longer, longer_side), point_v->point);
3268 g_assert (new_node != NULL);
3269 new_node->cvc_prev = new_node->cvc_next = NULL;
3272 /* Need a copy of the original longer edge pointer, as
3273 * tracking back from longer (one of its vertex ends)
3274 * won't work once we start adjusting
3276 VNODE *edge = VERTEX_SIDE_DIR_EDGE (longer, longer_side);
3278 /* Do insersion */
3279 PREV_VERTEX (new_node) = EDGE_BACKWARD_VERTEX (edge);
3280 NEXT_VERTEX (new_node) = EDGE_FORWARD_VERTEX (edge);
3281 PREV_VERTEX (EDGE_FORWARD_VERTEX (edge)) = new_node;
3282 EDGE_FORWARD_VERTEX (edge) = new_node;
3283 contour->Count++;
3286 // XXX: REALLY HOPE THIS DOESN'T UPDATE ANYTHING BY SNAP-ROUNDING, OR WE MIGHT AFFECT THE INTERSECTION WITH THE OTHER POLYGON?
3287 #warning NEED AN UPDATE FOR ROUND CONTOURS HERE?
3288 if (cntrbox_check (contour, new_node->point)) /* XXX: DOES THIS WORK / MATTER FOR ARC SEGMENT INSERTIONS? */
3290 g_error ("Need contour update, but cannot do it here");
3291 /* First delete the contour from the contour r-tree, as its bounds
3292 * may be adjusted whilst inserting nodes
3294 #if 0
3295 r_delete_entry (b->contour_tree, (const BoxType *) contour);
3296 cntrbox_adjust (contour, new_node->point); /* XXX: DOES THIS WORK / MATTER FOR ARC SEGMENT INSERTIONS? */
3297 r_insert_entry (b->contour_tree, (const BoxType *) contour, 0);
3298 #endif
3301 /* SKIP THIS.. NOTHING USES THE CONTOUR TREE AFTER INTERSECTION,
3302 * AND AS THIS IS AN INTERSECTED CONTOUR, THESE CONTOURS ARE
3303 * EVENTUALLY DROPPED.
3305 #if 0
3306 if (adjust_tree (contour->tree, node_seg))
3307 assert (0); /* XXX: Memory allocation failure */
3308 #endif
3310 /* Of key importance is ensuring we add any other nodes landing at shorter_side, e.g. the next/prev to shorter.
3311 * This is done below where we look for other descriptors which land at point_v. (Which is the shorter vertex
3312 * which lies at our inserted point on longer).
3317 /* Simple approach - just mark the edges as visited, so we don't traverse them!
3318 * Doing it this way ensures that both pieces of the contour are reachable if
3319 * the hairline edge pair splits this PLINE into two pieces. Since we will ensure
3320 * that both ends of the edges land on a cross-connected vertex, we should
3321 * successfully skip over the pre-marked gap in the contour.
3323 VERTEX_SIDE_DIR_EDGE (l->parent, l->side)->Flags.mark = true;
3324 VERTEX_SIDE_DIR_EDGE (n->parent, n->side)->Flags.mark = true;
3326 add_dummy_descriptors_at_point (point_v->point, first_l->poly, l, bfst); /* Picking 'l' for an arbitrary start CVCList */
3328 /* Now mark the vertices from this CVC list, to avoid complicating the edge labeling code. */
3330 /* Find the next eligible edge to start from, since we're about to delete the
3331 * one we would otherwise have used in the next iteration
3333 nn = next_cvc_from_same_poly (n);
3335 if (l == first_l)
3337 /* NOTE: Need to advance twice, as we're removing this, AND the next descriptor */
3338 first_l = next_cvc_from_same_poly (first_l);
3339 first_l = next_cvc_from_same_poly (first_l);
3340 if (l == first_l)
3341 terminate_after_this_iteration = true;
3344 mark_cvc_list_entry_as_skip (l);
3346 if (n == first_l)
3347 terminate_after_this_iteration = true;
3349 mark_cvc_list_entry_as_skip (n);
3351 n = nn;
3352 // XXX: What if we delete the last cross-connected vertex?? Probably the labelling code fails, as it won't know if the
3353 // contours are entirely INSIDE / OUTSIDE eachother.... may require fix-up later on in the process, as the
3354 // conntours are actually no longer interected.
3357 test_count++;
3359 if (terminate_after_this_iteration)
3360 break;
3363 while ((l = n) != first_l);
3365 while ((v = v->next) != &contour->head);
3368 static void
3369 M_POLYAREA_check_hairline_edges (CVCList *the_list, POLYAREA *bfst)
3371 POLYAREA *b = bfst;
3372 PLINE *cur;
3374 assert (b != NULL);
3377 for (cur = b->contours; cur != NULL; cur = cur->next)
3379 if (cur->Flags.status == ISECTED)
3381 PLINE_check_hairline_edges (the_list, cur, bfst);
3385 while ((b = b->f) != bfst);
3389 * \brief Determine if two polygons touch or overlap.
3391 BOOLp
3392 Touching (POLYAREA * a, POLYAREA * b)
3394 jmp_buf e;
3395 int code;
3397 if ((code = setjmp (e)) == 0)
3399 #ifdef DEBUG
3400 if (!poly_Valid (a))
3401 return -1;
3402 if (!poly_Valid (b))
3403 return -1;
3404 #endif
3405 M_POLYAREA_intersect (&e, a, b, false, NULL);
3407 if (M_POLYAREA_label (a, b, TRUE))
3408 return TRUE;
3409 if (M_POLYAREA_label (b, a, TRUE))
3410 return TRUE;
3412 else if (code == TOUCHES)
3413 return TRUE;
3414 return FALSE;
3418 * \brief The main clipping routines.
3421 poly_Boolean (const POLYAREA * a_org, const POLYAREA * b_org,
3422 POLYAREA ** res, int action)
3424 POLYAREA *a = NULL, *b = NULL;
3426 *res = NULL;
3428 if (!poly_M_Copy0 (&a, a_org) || !poly_M_Copy0 (&b, b_org))
3429 return err_no_memory;
3431 return poly_Boolean_free (a, b, res, action);
3432 } /* poly_Boolean */
3434 static void
3435 M_Set_Parentage (POLYAREA *poly, POLYPARENTAGE parentage)
3437 POLYAREA *piece = poly;
3439 if (poly == NULL)
3440 return;
3444 piece->parentage = parentage;
3446 while ((piece = piece->f) != poly);
3451 * \brief Just like poly_Boolean but frees the input polys.
3454 poly_Boolean_free (POLYAREA * ai, POLYAREA * bi, POLYAREA ** res, int action)
3456 POLYAREA *a = ai, *b = bi;
3457 PLINE *a_isected = NULL;
3458 PLINE *p, *holes = NULL;
3459 jmp_buf e;
3460 int code;
3461 CVCList *the_list;
3462 POLYAREA *a_copy, *b_copy;
3464 *res = NULL;
3466 #if 0
3467 /* Make copies for tracking polygon parentage (DEBUG) */
3468 if (!poly_M_Copy0 (&a_copy, a) || !poly_M_Copy0 (&b_copy, b))
3469 return err_no_memory;
3470 #else
3471 a_copy = NULL;
3472 b_copy = NULL;
3473 #endif
3475 /* Move the parentage information over onto the copy */
3476 if (a_copy != NULL)
3478 M_Set_Parentage (a_copy, a->parentage);
3479 M_Set_Parentage (a, no_parentage);
3482 if (b_copy != NULL)
3484 M_Set_Parentage (b_copy, b->parentage);
3485 M_Set_Parentage (b, no_parentage);
3488 if (!a)
3490 switch (action)
3492 case PBO_XOR:
3493 case PBO_UNITE:
3494 *res = bi;
3495 code = err_ok;
3496 goto out;
3497 case PBO_SUB:
3498 case PBO_ISECT:
3499 if (b != NULL)
3500 poly_Free (&b);
3501 code = err_ok;
3502 goto out;
3505 if (!b)
3507 switch (action)
3509 case PBO_SUB:
3510 case PBO_XOR:
3511 case PBO_UNITE:
3512 *res = ai;
3513 code = err_ok;
3514 goto out;
3515 case PBO_ISECT:
3516 if (a != NULL)
3517 poly_Free (&a);
3518 code = err_ok;
3519 goto out;
3523 if ((code = setjmp (e)) == 0)
3525 #ifdef DEBUG
3526 assert (poly_Valid (a));
3527 assert (poly_Valid (b));
3528 #endif
3530 /* intersect needs to make a list of the contours in a and b which are intersected */
3531 M_POLYAREA_intersect (&e, a, b, TRUE, &the_list);
3533 /* XXX - Need to loop the intersection routines until the geometry stabalises???
3535 // M_POLYAREA_intersect (&e, a, b, TRUE, &the_list);
3537 #if 1
3538 M_POLYAREA_check_hairline_edges (the_list, a);
3539 M_POLYAREA_check_hairline_edges (the_list, b);
3540 #endif
3542 #if 0
3543 /* Second pass gives us a chance to catch any bad-geometry hairlines we convert by inserting nodes */
3544 M_POLYAREA_check_hairline_edges (the_list, a);
3545 M_POLYAREA_check_hairline_edges (the_list, b);
3546 #endif
3548 /* We could speed things up a lot here if we only processed the relevant contours */
3549 /* NB: Relevant parts of a are labeled below */
3550 M_POLYAREA_label (b, a, FALSE);
3552 *res = a;
3553 M_POLYAREA_update_primary (&e, res, &holes, action, b);
3554 M_POLYAREA_separate_isected (&e, res, &holes, &a_isected);
3555 M_POLYAREA_label_separated (a_isected, b, FALSE);
3556 M_POLYAREA_Collect_separated (&e, a_isected, res, &holes, action,
3557 FALSE);
3558 M_B_AREA_Collect (&e, b, res, &holes, action);
3559 poly_Free (&b);
3561 /* free a_isected */
3562 while ((p = a_isected) != NULL)
3564 a_isected = p->next;
3565 poly_DelContour (&p);
3568 InsertHoles (&e, *res, &holes);
3570 /* delete holes if any left */
3571 while ((p = holes) != NULL)
3573 holes = p->next;
3574 poly_DelContour (&p);
3577 if (code)
3579 poly_Free (res);
3580 return code;
3583 out:
3584 assert (!*res || poly_Valid (*res));
3586 /* Store perantage information */
3587 if (*res != NULL)
3589 POLYPARENTAGE parentage;
3591 parentage.immaculate_conception = false;
3592 parentage.action = action;
3593 parentage.a = a_copy;
3594 parentage.b = b_copy;
3596 M_Set_Parentage (*res, parentage);
3599 return code;
3600 } /* poly_Boolean_free */
3602 static void
3603 clear_marks (POLYAREA * p)
3605 POLYAREA *n = p;
3606 PLINE *c;
3607 VNODE *v;
3611 for (c = n->contours; c; c = c->next)
3613 v = &c->head;
3616 v->Flags.mark = 0;
3618 while ((v = NEXT_EDGE (v)) != &c->head);
3621 while ((n = n->f) != p);
3625 * \brief Compute the intersection and subtraction (divides "a" into two
3626 * pieces) and frees the input polys.
3628 * This assumes that bi is a single simple polygon.
3631 poly_AndSubtract_free (POLYAREA * ai, POLYAREA * bi,
3632 POLYAREA ** aandb, POLYAREA ** aminusb)
3634 POLYAREA *a = ai, *b = bi;
3635 PLINE *p, *holes = NULL;
3636 jmp_buf e;
3637 int code;
3639 *aandb = NULL;
3640 *aminusb = NULL;
3642 if ((code = setjmp (e)) == 0)
3645 #ifdef DEBUG
3646 if (!poly_Valid (a))
3647 return -1;
3648 if (!poly_Valid (b))
3649 return -1;
3650 #endif
3651 M_POLYAREA_intersect (&e, a, b, TRUE, NULL);
3653 M_POLYAREA_label (a, b, FALSE);
3654 M_POLYAREA_label (b, a, FALSE);
3656 M_POLYAREA_Collect (&e, a, aandb, &holes, PBO_ISECT, FALSE);
3657 InsertHoles (&e, *aandb, &holes);
3658 assert (poly_Valid (*aandb));
3659 /* delete holes if any left */
3660 while ((p = holes) != NULL)
3662 holes = p->next;
3663 poly_DelContour (&p);
3665 holes = NULL;
3666 clear_marks (a);
3667 clear_marks (b);
3668 M_POLYAREA_Collect (&e, a, aminusb, &holes, PBO_SUB, FALSE);
3669 InsertHoles (&e, *aminusb, &holes);
3670 poly_Free (&a);
3671 poly_Free (&b);
3672 assert (poly_Valid (*aminusb));
3674 /* delete holes if any left */
3675 while ((p = holes) != NULL)
3677 holes = p->next;
3678 poly_DelContour (&p);
3682 if (code)
3684 poly_Free (aandb);
3685 poly_Free (aminusb);
3686 return code;
3688 assert (!*aandb || poly_Valid (*aandb));
3689 assert (!*aminusb || poly_Valid (*aminusb));
3690 return code;
3691 } /* poly_AndSubtract_free */
3693 static inline int
3694 cntrbox_pointin (PLINE * c, Vector p)
3696 return (p[0] >= c->xmin && p[1] >= c->ymin &&
3697 p[0] <= c->xmax && p[1] <= c->ymax);
3701 static inline int
3702 node_neighbours (VNODE * a, VNODE * b)
3704 return (a == b) || (a->next == b) || (b->next == a) || (a->next == b->next);
3707 void
3708 poly_IniContour (PLINE * c)
3710 if (c == NULL)
3711 return;
3712 /* bzero (c, sizeof(PLINE)); */
3713 NEXT_EDGE (&c->head) = PREV_EDGE (&c->head) = &c->head;
3714 c->xmin = c->ymin = COORD_MAX;
3715 c->xmax = c->ymax = -COORD_MAX - 1;
3716 c->is_round = FALSE;
3717 c->cx = 0;
3718 c->cy = 0;
3719 c->radius = 0;
3720 c->name = NULL;
3723 PLINE *
3724 poly_NewContour (VNODE *node)
3726 PLINE *res;
3728 res = (PLINE *) calloc (1, sizeof (PLINE));
3729 if (res == NULL)
3730 return NULL;
3732 poly_IniContour (res);
3734 Vcopy (res->head.point, node->point);
3735 res->head.is_round = node->is_round;
3736 res->head.cx = node->cx;
3737 res->head.cy = node->cy;
3738 res->head.radius = node->radius;
3739 cntrbox_adjust (res, res->head.point);
3740 g_slice_free (VNODE, node);
3742 return res;
3745 void
3746 poly_ClrContour (PLINE * c)
3748 VNODE *cur;
3750 assert (c != NULL);
3751 while ((cur = NEXT_EDGE (&c->head)) != &c->head)
3753 poly_ExclVertex (cur);
3754 g_slice_free (VNODE, cur);
3756 free (c->tristrip_vertices);
3757 c->tristrip_vertices = NULL;
3758 c->tristrip_num_vertices = 0;
3759 free (c->name);
3760 c->name = NULL;
3761 poly_IniContour (c);
3764 void
3765 poly_DelContour (PLINE ** c)
3767 VNODE *cur, *prev;
3769 if (*c == NULL)
3770 return;
3771 for (cur = PREV_EDGE (&(*c)->head); cur != &(*c)->head; cur = prev)
3773 prev = PREV_EDGE (cur);
3775 if (cur->cvc_next == (CVCList *) -1)
3776 fprintf (stderr, "WHY DID THIS HAPPEN?? cvc_next == -1 in poly_DelContour() !\n");
3777 else
3778 free (cur->cvc_next);
3780 if (cur->cvc_prev == (CVCList *) - 1)
3781 fprintf (stderr, "WHY DID THIS HAPPEN?? cvc_prev == -1 in poly_DelContour() !\n");
3782 else
3783 free (cur->cvc_prev);
3785 g_slice_free (VNODE, cur);
3788 if ((*c)->head.cvc_next == (CVCList *) -1)
3789 fprintf (stderr, "WHY DID THIS HAPPEN?? cvc_next == -1 in poly_DelContour() !\n");
3790 else
3791 free ((*c)->head.cvc_next);
3793 if ((*c)->head.cvc_prev == (CVCList *) -1)
3794 fprintf (stderr, "WHY DID THIS HAPPEN?? cvc_prev == -1 in poly_DelContour() !\n");
3795 else
3796 free ((*c)->head.cvc_prev);
3798 /*! \todo FIXME -- strict aliasing violation. */
3799 if ((*c)->tree)
3801 rtree_t *r = (*c)->tree;
3802 r_destroy_tree (&r);
3804 free ((*c)->tristrip_vertices);
3805 free ((*c)->name);
3806 free (*c), *c = NULL;
3809 void
3810 poly_PreContour (PLINE * C, BOOLp optimize)
3812 double area = 0;
3813 VNODE *p, *c;
3814 Vector p1, p2;
3816 assert (C != NULL);
3818 if (optimize)
3820 /* XXX: Looks like this loop misses an iteration? IE.. no test between head->prev ------- head ------- head->next
3821 * in any case, if we remove the head vertex, we need to adjust the polygon...
3822 * does poly_ExclVertex cope with that? - NO!
3824 for (c = NEXT_VERTEX ((p = &C->head)); c != &C->head; c = NEXT_VERTEX (p = c))
3826 /* if the previous node is on the same line with this one, we should remove it */
3827 Vsub2 (p1, c->point, p->point);
3828 Vsub2 (p2, NEXT_VERTEX (c)->point, c->point);
3829 /* If the product below is zero then
3830 * the points on either side of c
3831 * are on the same line!
3832 * So, remove the point c
3835 if (vect_det2 (p1, p2) == 0)
3837 poly_ExclVertex (c);
3838 g_slice_free (VNODE, c);
3839 c = p;
3843 C->Count = 0;
3844 C->xmin = C->xmax = C->head.point[0];
3845 C->ymin = C->ymax = C->head.point[1];
3847 p = PREV_VERTEX ((c = &C->head));
3848 // if (c != p) /* WTF?? */
3849 // {
3852 /* calculate area for orientation */
3853 area +=
3854 (double) (p->point[0] - c->point[0]) * (p->point[1] +
3855 c->point[1]);
3856 cntrbox_adjust (C, c->point);
3857 C->Count++;
3859 while ((c = NEXT_VERTEX (p = c)) != &C->head);
3860 // }
3861 C->area = ABS (area);
3862 if (C->Count > 2)
3863 C->Flags.orient = ((area < 0) ? PLF_INV : PLF_DIR);
3864 C->tree = (rtree_t *)make_edge_tree (C);
3865 } /* poly_PreContour */
3867 static int
3868 flip_cb (const BoxType * b, void *cl)
3870 struct seg *s = (struct seg *) b;
3871 s->v = PREV_EDGE (s->v);
3872 return 1;
3875 void
3876 poly_InvContour (PLINE * c)
3878 VNODE *cur, *next;
3879 #ifndef NDEBUG
3880 int r;
3881 #endif
3883 /* Stash the first data which will get over-written in the loop */
3885 bool stash_is_round = c->head.is_round;
3886 Coord stash_cx = c->head.cx;
3887 Coord stash_cy = c->head.cy;
3888 Coord stash_radius = c->head.radius;
3890 bool next_is_round;
3891 Coord next_cx;
3892 Coord next_cy;
3893 Coord next_radius;
3895 // printf ("poly_InvContour\n");
3897 assert (c != NULL);
3898 cur = &c->head;
3901 /* Swap the attachement of round contour information */
3902 next_is_round = cur->next->is_round;
3903 next_cx = cur->next->cx;
3904 next_cy = cur->next->cy;
3905 next_radius = cur->next->radius;
3907 cur->next->is_round = stash_is_round;
3908 cur->next->cx = stash_cx;
3909 cur->next->cy = stash_cy;
3910 cur->next->radius = stash_radius;
3912 stash_is_round = next_is_round;
3913 stash_cx = next_cx;
3914 stash_cy = next_cy;
3915 stash_radius = next_radius;
3917 next = NEXT_EDGE (cur);
3918 NEXT_EDGE(cur) = PREV_EDGE (cur);
3919 PREV_EDGE (cur) = next;
3921 /* fix the segment tree */
3923 while ((cur = next) != &c->head);
3925 c->Flags.orient ^= 1;
3926 if (c->tree)
3928 #ifndef NDEBUG
3930 #endif
3931 r_search (c->tree, NULL, NULL, flip_cb, NULL);
3932 #ifndef NDEBUG
3933 assert (r == c->Count);
3934 #endif
3938 void
3939 poly_ExclVertex (VNODE * node)
3941 assert (node != NULL);
3942 if (node->cvc_next)
3944 free (node->cvc_next);
3945 free (node->cvc_prev);
3947 NEXT_VERTEX (PREV_VERTEX (node)) = NEXT_VERTEX (node);
3948 PREV_VERTEX (NEXT_VERTEX (node)) = PREV_VERTEX (node);
3951 void
3952 poly_InclVertex (VNODE * after, VNODE * node)
3954 double a, b;
3955 assert (after != NULL);
3956 assert (node != NULL);
3958 PREV_VERTEX (node) = after;
3959 NEXT_VERTEX (node) = NEXT_VERTEX (after);
3960 NEXT_VERTEX (after) = PREV_VERTEX (NEXT_VERTEX (after)) = node;
3961 /* remove points on same line */
3962 if (PREV_VERTEX (PREV_VERTEX (node)) == node)
3963 return; /* we don't have 3 points in the poly yet */
3965 /* NB: a-b below is the two-dimensional cross product of the vectors
3966 * node->prev->prev->point -> node->prev->point and
3967 * node->prev->prev->point -> node->point.
3969 * Its magnitude is the area of the parallelogram with those vectors as sides.
3970 * If the vectors are colinear, this is zero.
3972 a = (node->point[1] - PREV_VERTEX (PREV_VERTEX (node))->point[1]);
3973 a *= (PREV_VERTEX (node)->point[0] - PREV_VERTEX (PREV_VERTEX (node))->point[0]);
3974 b = (node->point[0] - PREV_VERTEX (PREV_VERTEX (node))->point[0]);
3975 b *= (PREV_VERTEX (node)->point[1] - PREV_VERTEX (PREV_VERTEX (node))->point[1]);
3977 // printf ("a-b = %f\n", a-b);
3979 /* XXX: HMM - This doesn't seem to be involved when extra points are left in polygon contours after boolean operations */
3980 if (0)
3981 // if (fabs (a - b) < 1000000) //EPSILON &&
3982 // !node->prev->is_round && !node->is_round)
3983 // !node->prev->is_round && !node->is_round)
3985 VNODE *t = PREV_VERTEX (node);
3986 NEXT_VERTEX (PREV_VERTEX (t)) = node;
3987 PREV_VERTEX (node) = PREV_VERTEX (t);
3988 g_slice_free (VNODE, t);
3992 BOOLp
3993 poly_CopyContour (PLINE ** dst, PLINE * src)
3995 VNODE *cur, *newnode;
3997 assert (src != NULL);
3998 *dst = NULL;
3999 *dst = poly_NewContour (poly_CreateNodeFull (src->head.point, src->head.is_round, src->head.cx, src->head.cy, src->head.radius));
4000 if (*dst == NULL)
4001 return FALSE;
4003 (*dst)->Count = src->Count;
4004 (*dst)->Flags.orient = src->Flags.orient;
4005 (*dst)->xmin = src->xmin, (*dst)->xmax = src->xmax;
4006 (*dst)->ymin = src->ymin, (*dst)->ymax = src->ymax;
4007 (*dst)->area = src->area;
4008 (*dst)->is_round = src->is_round;
4009 (*dst)->cx = src->cx;
4010 (*dst)->cy = src->cy;
4011 (*dst)->radius = src->radius;
4013 if (src->name != NULL)
4014 (*dst)->name = strdup (src->name);
4016 for (cur = NEXT_EDGE (&src->head); cur != &src->head; cur = NEXT_VERTEX (cur))
4018 if ((newnode = poly_CreateNodeFull (cur->point, cur->is_round, cur->cx, cur->cy, cur->radius)) == NULL)
4019 return FALSE;
4020 // newnode->Flags = cur->Flags;
4021 poly_InclVertex (PREV_EDGE (&(*dst)->head), newnode);
4023 (*dst)->tree = (rtree_t *)make_edge_tree (*dst);
4024 return TRUE;
4027 /* polygon routines */
4029 static BOOLp
4030 poly_Copy1 (POLYAREA * dst, const POLYAREA * src)
4032 PLINE *cur, **last = &dst->contours;
4034 *last = NULL;
4035 dst->f = dst->b = dst;
4037 for (cur = src->contours; cur != NULL; cur = cur->next)
4039 if (!poly_CopyContour (last, cur))
4040 return FALSE;
4041 r_insert_entry (dst->contour_tree, (BoxType *) * last, 0);
4042 last = &(*last)->next;
4045 return TRUE;
4048 BOOLp
4049 poly_Copy0 (POLYAREA ** dst, const POLYAREA * src)
4051 *dst = NULL;
4052 if (src == NULL)
4053 return TRUE;
4055 if ((*dst = poly_Create ()) == NULL || !poly_Copy1 (*dst, src))
4056 return FALSE;
4058 return TRUE;
4061 void
4062 poly_M_Incl (POLYAREA ** list, POLYAREA * a)
4064 if (*list == NULL)
4065 a->f = a->b = a, *list = a;
4066 else
4068 a->f = *list;
4069 a->b = (*list)->b;
4070 (*list)->b = (*list)->b->f = a;
4074 BOOLp
4075 poly_M_Copy0 (POLYAREA ** dst, const POLYAREA * srcfst)
4077 const POLYAREA *src = srcfst;
4078 POLYAREA *di;
4080 *dst = NULL;
4081 if (src == NULL)
4082 return TRUE;
4086 if ((di = poly_Create ()) == NULL || !poly_Copy1 (di, src))
4087 return FALSE;
4088 poly_M_Incl (dst, di);
4090 while ((src = src->f) != srcfst);
4091 return TRUE;
4094 BOOLp
4095 poly_InclContour (POLYAREA * p, PLINE * c)
4097 PLINE *tmp;
4099 if ((c == NULL) || (p == NULL))
4100 return FALSE;
4101 if (c->Flags.orient == PLF_DIR)
4103 if (p->contours != NULL)
4104 return FALSE;
4105 p->contours = c;
4107 else
4109 if (p->contours == NULL)
4110 return FALSE;
4111 /* link at front of hole list */
4112 tmp = p->contours->next;
4113 p->contours->next = c;
4114 c->next = tmp;
4116 r_insert_entry (p->contour_tree, (BoxType *) c, 0);
4117 return TRUE;
4120 typedef struct pip
4122 int f;
4123 Vector p;
4124 jmp_buf env;
4125 } pip;
4128 static int
4129 crossing (const BoxType * b, void *cl)
4131 struct seg *s = (struct seg *) b;
4132 struct pip *p = (struct pip *) cl;
4134 if (EDGE_BACKWARD_VERTEX (s->v)->point[1] <= p->p[1])
4136 if (EDGE_FORWARD_VERTEX (s->v)->point[1] > p->p[1])
4138 Vector v1, v2;
4139 long long cross;
4140 Vsub2 (v1, EDGE_FORWARD_VERTEX (s->v)->point, EDGE_BACKWARD_VERTEX (s->v)->point);
4141 Vsub2 (v2, p->p, EDGE_BACKWARD_VERTEX (s->v)->point);
4142 cross = (long long) v1[0] * v2[1] - (long long) v2[0] * v1[1];
4143 if (cross == 0)
4145 p->f = 1;
4146 longjmp (p->env, 1);
4148 if (cross > 0)
4149 p->f += 1;
4152 else
4154 if (EDGE_FORWARD_VERTEX (s->v)->point[1] <= p->p[1])
4156 Vector v1, v2;
4157 long long cross;
4158 Vsub2 (v1, EDGE_FORWARD_VERTEX (s->v)->point, EDGE_BACKWARD_VERTEX (s->v)->point);
4159 Vsub2 (v2, p->p, EDGE_BACKWARD_VERTEX (s->v)->point);
4160 cross = (long long) v1[0] * v2[1] - (long long) v2[0] * v1[1];
4161 if (cross == 0)
4163 p->f = 1;
4164 longjmp (p->env, 1);
4166 if (cross < 0)
4167 p->f -= 1;
4170 return 1;
4174 poly_InsideContour (PLINE * c, Vector p)
4176 struct pip info;
4177 BoxType ray;
4179 if (!cntrbox_pointin (c, p))
4180 return FALSE;
4181 info.f = 0;
4182 info.p[0] = ray.X1 = p[0];
4183 info.p[1] = ray.Y1 = p[1];
4184 ray.X2 = COORD_MAX;
4185 ray.Y2 = p[1] + 1;
4186 if (setjmp (info.env) == 0)
4187 r_search (c->tree, &ray, NULL, crossing, &info);
4188 return info.f;
4191 BOOLp
4192 poly_CheckInside (POLYAREA * p, Vector v0)
4194 PLINE *cur;
4196 if ((p == NULL) || (v0 == NULL) || (p->contours == NULL))
4197 return FALSE;
4198 cur = p->contours;
4199 if (poly_InsideContour (cur, v0))
4201 for (cur = cur->next; cur != NULL; cur = cur->next)
4202 if (poly_InsideContour (cur, v0))
4203 return FALSE;
4204 return TRUE;
4206 return FALSE;
4209 BOOLp
4210 poly_M_CheckInside (POLYAREA * p, Vector v0)
4212 POLYAREA *cur;
4214 if ((p == NULL) || (v0 == NULL))
4215 return FALSE;
4216 cur = p;
4219 if (poly_CheckInside (cur, v0))
4220 return TRUE;
4222 while ((cur = cur->f) != p);
4223 return FALSE;
4226 static double
4227 dot (Vector A, Vector B)
4229 return (double)A[0] * (double)B[0] + (double)A[1] * (double)B[1];
4233 * \brief Compute whether point is inside a triangle formed by 3 other
4234 * points.
4236 * Algorithm from http://www.blackpawn.com/texts/pointinpoly/default.html.
4238 static int
4239 point_in_triangle (Vector A, Vector B, Vector C, Vector P)
4241 Vector v0, v1, v2;
4242 double dot00, dot01, dot02, dot11, dot12;
4243 double invDenom;
4244 double u, v;
4246 /* Compute vectors */
4247 v0[0] = C[0] - A[0]; v0[1] = C[1] - A[1];
4248 v1[0] = B[0] - A[0]; v1[1] = B[1] - A[1];
4249 v2[0] = P[0] - A[0]; v2[1] = P[1] - A[1];
4251 /* Compute dot products */
4252 dot00 = dot (v0, v0);
4253 dot01 = dot (v0, v1);
4254 dot02 = dot (v0, v2);
4255 dot11 = dot (v1, v1);
4256 dot12 = dot (v1, v2);
4258 /* Compute barycentric coordinates */
4259 invDenom = 1. / (dot00 * dot11 - dot01 * dot01);
4260 u = (dot11 * dot02 - dot01 * dot12) * invDenom;
4261 v = (dot00 * dot12 - dot01 * dot02) * invDenom;
4263 /* Check if point is in triangle */
4264 return (u > 0.0) && (v > 0.0) && (u + v < 1.0);
4269 * \brief Returns the dot product of Vector A->B, and a vector
4270 * orthogonal to Vector C->D.
4272 * The result is not normalisd, so will be weighted by the magnitude of
4273 * the C->D vector.
4275 static double
4276 dot_orthogonal_to_direction (Vector A, Vector B, Vector C, Vector D)
4278 Vector l1, l2, l3;
4279 l1[0] = B[0] - A[0]; l1[1] = B[1] - A[1];
4280 l2[0] = D[0] - C[0]; l2[1] = D[1] - C[1];
4282 l3[0] = -l2[1];
4283 l3[1] = l2[0];
4285 return dot (l1, l3);
4289 * \brief Algorithm from http://www.exaflop.org/docs/cgafaq/cga2.html.
4291 * "Given a simple polygon, find some point inside it.
4293 * Here is a method based on the proof that there exists an internal
4294 * diagonal, in [O'Rourke, 13-14].
4296 * The idea is that the midpoint of a diagonal is interior to the
4297 * polygon.
4299 * <ol>
4300 * <li>Identify a convex vertex v; let its adjacent vertices be a and b.</li>
4301 * <li>For each other vertex q do:</li>
4302 * <ol type="a">
4303 * <li>If q is inside avb, compute distance to v (orthogonal to ab).</li>
4304 * <li>Save point q if distance is a new min.</li>
4305 * </ol>
4306 * <li>If no point is inside, return midpoint of ab, or centroid of avb.</li>
4307 * <li>Else if some point inside, qv is internal: return its midpoint."</li>
4308 * </ol>
4310 * [O'Rourke]: Computational Geometry in C (2nd Ed.)
4312 * Joseph O'Rourke, Cambridge University Press 1998,
4313 * ISBN 0-521-64010-5 Pbk, ISBN 0-521-64976-5 Hbk.
4315 static void
4316 poly_ComputeInteriorPoint (PLINE *poly, Vector v)
4318 VNODE *pt1, *pt2, *pt3, *q;
4319 VNODE *min_q = NULL;
4320 double dist;
4321 double min_dist = 0.0;
4322 double dir = (poly->Flags.orient == PLF_DIR) ? 1. : -1;
4324 /* Find a convex node on the polygon */
4325 pt1 = &poly->head;
4328 double dot_product;
4330 pt2 = NEXT_VERTEX (pt1);
4331 pt3 = NEXT_VERTEX (pt2);
4333 dot_product = dot_orthogonal_to_direction (pt1->point, pt2->point,
4334 pt3->point, pt2->point);
4336 if (dot_product * dir > 0.)
4337 break;
4339 while ((pt1 = NEXT_VERTEX (pt1)) != &poly->head);
4341 /* Loop over remaining vertices */
4342 q = pt3;
4345 /* Is current vertex "q" outside pt1 pt2 pt3 triangle? */
4346 if (!point_in_triangle (pt1->point, pt2->point, pt3->point, q->point))
4347 continue;
4349 /* NO: compute distance to pt2 (v) orthogonal to pt1 - pt3) */
4350 /* Record minimum */
4351 dist = dot_orthogonal_to_direction (q->point, pt2->point, pt1->point, pt3->point);
4352 if (min_q == NULL || dist < min_dist) {
4353 min_dist = dist;
4354 min_q = q;
4357 while ((q = NEXT_VERTEX (q)) != pt2);
4359 /* Were any "q" found inside pt1 pt2 pt3? */
4360 if (min_q == NULL) {
4361 /* NOT FOUND: Return midpoint of pt1 pt3 */
4362 v[0] = (pt1->point[0] + pt3->point[0]) / 2;
4363 v[1] = (pt1->point[1] + pt3->point[1]) / 2;
4364 } else {
4365 /* FOUND: Return mid point of min_q, pt2 */
4366 v[0] = (pt2->point[0] + min_q->point[0]) / 2;
4367 v[1] = (pt2->point[1] + min_q->point[1]) / 2;
4373 * \brief .
4375 * \note This function assumes the caller _knows_ the contours do not
4376 * intersect. If the contours intersect, the result is undefined.
4377 * It will return the correct result if the two contours share
4378 * common points beteween their contours. (Identical contours
4379 * are treated as being inside each other).
4382 poly_ContourInContour (PLINE * poly, PLINE * inner)
4384 Vector point;
4385 assert (poly != NULL);
4386 assert (inner != NULL);
4387 if (cntrbox_inside (inner, poly))
4389 /* We need to prove the "inner" contour is not outside
4390 * "poly" contour. If it is outside, we can return.
4392 if (!poly_InsideContour (poly, inner->head.point))
4393 return 0;
4395 poly_ComputeInteriorPoint (inner, point);
4396 return poly_InsideContour (poly, point);
4398 return 0;
4401 void
4402 poly_Init (POLYAREA * p)
4404 p->f = p->b = p;
4405 p->contours = NULL;
4406 p->simple_contours = NULL;
4407 p->contour_tree = r_create_tree (NULL, 0, 0);
4408 p->parentage = no_parentage;
4409 p->user_data = NULL;
4412 POLYAREA *
4413 poly_Create (void)
4415 POLYAREA *res;
4417 if ((res = (POLYAREA *)malloc (sizeof (POLYAREA))) != NULL)
4418 poly_Init (res);
4419 return res;
4422 void
4423 poly_FreeContours (PLINE ** pline)
4425 PLINE *pl;
4427 while ((pl = *pline) != NULL)
4429 *pline = pl->next;
4430 poly_DelContour (&pl);
4434 void
4435 poly_Free (POLYAREA ** p)
4437 POLYAREA *cur;
4439 if (*p == NULL)
4440 return;
4441 for (cur = (*p)->f; cur != *p; cur = (*p)->f)
4443 poly_FreeContours (&cur->contours);
4444 poly_FreeContours (&cur->simple_contours);
4445 r_destroy_tree (&cur->contour_tree);
4446 cur->f->b = cur->b;
4447 cur->b->f = cur->f;
4448 free (cur);
4450 poly_FreeContours (&cur->contours);
4451 poly_FreeContours (&cur->simple_contours);
4452 r_destroy_tree (&cur->contour_tree);
4454 /* Free parentage information - assume all linked polygons share this, so only need to do it for the past polygon */
4455 if ((*p)->parentage.a != NULL)
4456 poly_Free (&(*p)->parentage.a);
4457 if ((*p)->parentage.b != NULL)
4458 poly_Free (&(*p)->parentage.b);
4460 free (*p), *p = NULL;
4463 static BOOLp
4464 inside_sector (VNODE * pn, Vector p2)
4466 Vector cdir, ndir, pdir;
4467 int p_c, n_c, p_n;
4469 assert (pn != NULL);
4470 vect_sub (cdir, p2, pn->point);
4471 vect_sub (pdir, pn->point, PREV_VERTEX (pn)->point);
4472 vect_sub (ndir, NEXT_VERTEX (pn)->point, pn->point);
4474 p_c = vect_det2 (pdir, cdir) >= 0;
4475 n_c = vect_det2 (ndir, cdir) >= 0;
4476 p_n = vect_det2 (pdir, ndir) >= 0;
4478 if ((p_n && p_c && n_c) || ((!p_n) && (p_c || n_c)))
4479 return TRUE;
4480 else
4481 return FALSE;
4482 } /* inside_sector */
4485 * \brief .
4487 * \return TRUE if bad contour.
4489 BOOLp
4490 poly_ChkContour (PLINE * a)
4492 VNODE *a1, *a2, *hit1, *hit2;
4493 Vector i1, i2;
4494 int icnt;
4496 assert (a != NULL);
4497 a1 = &a->head;
4500 a2 = a1;
4503 if (!node_neighbours (a1, a2) &&
4504 (icnt = vect_inters2 (a1->point, a1->next->point,
4505 a2->point, a2->next->point, i1, i2)) > 0)
4507 if (icnt > 1)
4508 return TRUE;
4510 if (vect_dist2 (i1, a1->point) < EPSILON)
4511 hit1 = a1;
4512 else if (vect_dist2 (i1, a1->next->point) < EPSILON)
4513 hit1 = a1->next;
4514 else
4515 hit1 = NULL;
4517 if (vect_dist2 (i1, a2->point) < EPSILON)
4518 hit2 = a2;
4519 else if (vect_dist2 (i1, a2->next->point) < EPSILON)
4520 hit2 = a2->next;
4521 else
4522 hit2 = NULL;
4524 if ((hit1 == NULL) && (hit2 == NULL))
4526 /* If the intersection didn't land on an end-point of either
4527 * line, we know the lines cross and we return TRUE.
4529 return TRUE;
4531 else if (hit1 == NULL)
4533 /* An end-point of the second line touched somewhere along the
4534 length of the first line. Check where the second line leads. */
4535 if (inside_sector (hit2, a1->point) !=
4536 inside_sector (hit2, a1->next->point))
4537 return TRUE;
4539 else if (hit2 == NULL)
4541 /* An end-point of the first line touched somewhere along the
4542 length of the second line. Check where the first line leads. */
4543 if (inside_sector (hit1, a2->point) !=
4544 inside_sector (hit1, a2->next->point))
4545 return TRUE;
4547 else
4549 /* Both lines intersect at an end-point. Check where they lead. */
4550 if (inside_sector (hit1, hit2->prev->point) !=
4551 inside_sector (hit1, hit2->next->point))
4552 return TRUE;
4556 while ((a2 = a2->next) != &a->head);
4558 while ((a1 = a1->next) != &a->head);
4559 return FALSE;
4563 BOOLp
4564 poly_Valid (POLYAREA * p)
4566 PLINE *c;
4568 if ((p == NULL) || (p->contours == NULL))
4569 return FALSE;
4571 if (p->contours->Flags.orient == PLF_INV || poly_ChkContour (p->contours))
4573 #ifndef NDEBUG
4574 VNODE *v, *n;
4575 DEBUGP ("Invalid Outer PLINE\n");
4576 if (p->contours->Flags.orient == PLF_INV)
4577 DEBUGP ("failed orient\n");
4578 if (poly_ChkContour (p->contours))
4579 DEBUGP ("failed self-intersection\n");
4580 v = &p->contours->head;
4583 n = NEXT_VERTEX (v);
4584 pcb_fprintf (stderr, "Line [%$mn %$mn %$mn %$mn 100 100 \"\"]\n",
4585 v->point[0], v->point[1], n->point[0], n->point[1]);
4587 while ((v = NEXT_VERTEX (v)) != &p->contours->head);
4588 #endif
4589 return FALSE;
4591 for (c = p->contours->next; c != NULL; c = c->next)
4593 if (c->Flags.orient == PLF_DIR ||
4594 poly_ChkContour (c) || !poly_ContourInContour (p->contours, c))
4596 #ifndef NDEBUG
4597 VNODE *v, *n;
4598 DEBUGP ("Invalid Inner PLINE orient = %d\n", c->Flags.orient);
4599 if (c->Flags.orient == PLF_DIR)
4600 DEBUGP ("failed orient\n");
4601 if (poly_ChkContour (c))
4602 DEBUGP ("failed self-intersection\n");
4603 if (!poly_ContourInContour (p->contours, c))
4604 DEBUGP ("failed containment\n");
4605 v = &c->head;
4608 n = NEXT_VERTEX (v);
4609 pcb_fprintf (stderr, "Line [%$mn %$mn %$mn %$mn 100 100 \"\"]\n",
4610 v->point[0], v->point[1], n->point[0], n->point[1]);
4612 while ((v = NEXT_VERTEX (v)) != &c->head);
4613 #endif
4614 return FALSE;
4617 return TRUE;
4621 Vector vect_zero = { (long) 0, (long) 0 };
4623 /* L o n g V e c t o r S t u f f */
4625 void
4626 vect_init (Vector v, double x, double y)
4628 v[0] = (long) x;
4629 v[1] = (long) y;
4630 } /* vect_init */
4632 #define Vzero(a) ((a)[0] == 0. && (a)[1] == 0.)
4634 #define Vsub(a,b,c) {(a)[0]=(b)[0]-(c)[0];(a)[1]=(b)[1]-(c)[1];}
4637 vect_equal (Vector v1, Vector v2)
4639 return (v1[0] == v2[0] && v1[1] == v2[1]);
4640 } /* vect_equal */
4643 void
4644 vect_sub (Vector res, Vector v1, Vector v2)
4646 Vsub (res, v1, v2)} /* vect_sub */
4648 void
4649 vect_min (Vector v1, Vector v2, Vector v3)
4651 v1[0] = (v2[0] < v3[0]) ? v2[0] : v3[0];
4652 v1[1] = (v2[1] < v3[1]) ? v2[1] : v3[1];
4653 } /* vect_min */
4655 void
4656 vect_max (Vector v1, Vector v2, Vector v3)
4658 v1[0] = (v2[0] > v3[0]) ? v2[0] : v3[0];
4659 v1[1] = (v2[1] > v3[1]) ? v2[1] : v3[1];
4660 } /* vect_max */
4662 double
4663 vect_len2 (Vector v)
4665 return ((double) v[0] * v[0] + (double) v[1] * v[1]); /* why sqrt? only used for compares */
4668 double
4669 vect_dist2 (Vector v1, Vector v2)
4671 double dx = v1[0] - v2[0];
4672 double dy = v1[1] - v2[1];
4674 return (dx * dx + dy * dy); /* why sqrt */
4678 * \brief Value has sign of angle between vectors.
4680 double
4681 vect_det2 (Vector v1, Vector v2)
4683 return (((double) v1[0] * v2[1]) - ((double) v2[0] * v1[1]));
4686 static double
4687 vect_m_dist_fp (double v1[2], double v2[2])
4689 double dx = v1[0] - v2[0];
4690 double dy = v1[1] - v2[1];
4691 double dd = (dx * dx + dy * dy); /* sqrt */
4693 if (dx > 0)
4694 return +dd;
4695 if (dx < 0)
4696 return -dd;
4697 if (dy > 0)
4698 return +dd;
4699 return -dd;
4700 } /* vect_m_dist_fp */
4703 * \brief vect_inters2_fp.
4705 * (C) 1993 Klamer Schutte.
4707 * (C) 1997 Michael Leonov, Alexey Nikitin.
4709 * (C) 2016 Peter Clifton
4712 vect_inters2_fp (double p1[2], double p2[2],
4713 double q1[2], double q2[2],
4714 double S1[2], double S2[2])
4716 double s, t, deel;
4717 double rpx, rpy, rqx, rqy;
4719 if (max (p1[0], p2[0]) < min (q1[0], q2[0]) ||
4720 max (q1[0], q2[0]) < min (p1[0], p2[0]) ||
4721 max (p1[1], p2[1]) < min (q1[1], q2[1]) ||
4722 max (q1[1], q2[1]) < min (p1[1], p2[1]))
4723 return 0;
4725 rpx = p2[0] - p1[0];
4726 rpy = p2[1] - p1[1];
4727 rqx = q2[0] - q1[0];
4728 rqy = q2[1] - q1[1];
4730 deel = rpy * rqx - rpx * rqy; /* -vect_det(rp,rq); */
4732 /* XXX: THIS IS NOT NECESSARILY TRUE ANY MORE...
4733 * deel MAY NEED TO BE COMPARED USING
4734 * SOME EPSILON VALUE
4736 /* coordinates are 30-bit integers so deel will be exactly zero
4737 * if the lines are parallel
4739 if (deel == 0) /* parallel */
4741 double dc1, dc2, d1, d2, h; /* Check to see whether p1-p2 and q1-q2 are on the same line */
4742 Vector hp1, hq1, hp2, hq2, q1p1, q1q2;
4744 Vsub2 (q1p1, q1, p1);
4745 Vsub2 (q1q2, q1, q2);
4748 /* If this product is not zero then p1-p2 and q1-q2 are not on same line! */
4749 if (vect_det2 (q1p1, q1q2) != 0)
4750 return 0;
4751 dc1 = 0; /* m_len(p1 - p1) */
4753 dc2 = vect_m_dist_fp (p1, p2);
4754 d1 = vect_m_dist_fp (p1, q1);
4755 d2 = vect_m_dist_fp (p1, q2);
4757 /* Sorting the independent points from small to large */
4758 Vcpy2 (hp1, p1);
4759 Vcpy2 (hp2, p2);
4760 Vcpy2 (hq1, q1);
4761 Vcpy2 (hq2, q2);
4762 if (dc1 > dc2)
4763 { /* hv and h are used as help-variable. */
4764 Vswp2 (hp1, hp2);
4765 h = dc1, dc1 = dc2, dc2 = h;
4767 if (d1 > d2)
4769 Vswp2 (hq1, hq2);
4770 h = d1, d1 = d2, d2 = h;
4773 /* Now the line-pieces are compared */
4775 if (dc1 < d1)
4777 if (dc2 < d1)
4778 return 0;
4779 if (dc2 < d2)
4781 Vcpy2 (S1, hp2);
4782 Vcpy2 (S2, hq1);
4784 else
4786 Vcpy2 (S1, hq1);
4787 Vcpy2 (S2, hq2);
4790 else
4792 if (dc1 > d2)
4793 return 0;
4794 if (dc2 < d2)
4796 Vcpy2 (S1, hp1);
4797 Vcpy2 (S2, hp2);
4799 else
4801 Vcpy2 (S1, hp1);
4802 Vcpy2 (S2, hq2);
4805 return (Vequ2 (S1, S2) ? 1 : 2);
4807 else
4808 { /* not parallel */
4810 * We have the lines:
4811 * l1: p1 + s(p2 - p1)
4812 * l2: q1 + t(q2 - q1)
4813 * And we want to know the intersection point.
4814 * Calculate t:
4815 * p1 + s(p2-p1) = q1 + t(q2-q1)
4816 * which is similar to the two equations:
4817 * p1x + s * rpx = q1x + t * rqx
4818 * p1y + s * rpy = q1y + t * rqy
4819 * Multiplying these by rpy resp. rpx gives:
4820 * rpy * p1x + s * rpx * rpy = rpy * q1x + t * rpy * rqx
4821 * rpx * p1y + s * rpx * rpy = rpx * q1y + t * rpx * rqy
4822 * Subtracting these gives:
4823 * rpy * p1x - rpx * p1y = rpy * q1x - rpx * q1y + t * ( rpy * rqx - rpx * rqy )
4824 * So t can be isolated:
4825 * t = (rpy * ( p1x - q1x ) + rpx * ( - p1y + q1y )) / ( rpy * rqx - rpx * rqy )
4826 * and s can be found similarly
4827 * s = (rqy * (q1x - p1x) + rqx * (p1y - q1y))/( rqy * rpx - rqx * rpy)
4830 if (Vequ2 (q1, p1) || Vequ2 (q1, p2))
4832 S1[0] = q1[0];
4833 S1[1] = q1[1];
4835 else if (Vequ2 (q2, p1) || Vequ2 (q2, p2))
4837 S1[0] = q2[0];
4838 S1[1] = q2[1];
4840 else
4842 s = (rqy * (p1[0] - q1[0]) + rqx * (q1[1] - p1[1])) / deel;
4843 if (s < 0 || s > 1.)
4844 return 0;
4845 t = (rpy * (p1[0] - q1[0]) + rpx * (q1[1] - p1[1])) / deel;
4846 if (t < 0 || t > 1.)
4847 return 0;
4849 S1[0] = q1[0] + ROUND (t * rqx);
4850 S1[1] = q1[1] + ROUND (t * rqy);
4852 return 1;
4854 } /* vect_inters2 */
4857 vect_inters2 (Vector p1, Vector p2, Vector q1, Vector q2,
4858 Vector S1, Vector S2)
4860 double p1_fp[2] = {p1[0], p1[1]};
4861 double p2_fp[2] = {p2[0], p2[1]};
4862 double q1_fp[2] = {q1[0], q1[1]};
4863 double q2_fp[2] = {q2[0], q2[1]};
4864 double s1_fp[2] = {0.0, 0.0};
4865 double s2_fp[2] = {0.0, 0.0};
4866 int cnt;
4868 cnt = vect_inters2_fp (p1_fp, p2_fp,
4869 q1_fp, q2_fp,
4870 /* out */
4871 s1_fp,
4872 s2_fp);
4874 S1[0] = s1_fp[0];
4875 S1[1] = s1_fp[1];
4876 S2[0] = s2_fp[0];
4877 S2[1] = s2_fp[1];
4879 return cnt;
4882 /*! \brief line_segments_can_merge
4884 * Return whether the two adjacent edge segments in a contour are colienar,
4885 * and can be simpliied.
4887 * s1 and s2 are treated as edges
4889 static bool
4890 line_segments_can_merge (VNODE *s1, VNODE *s2)
4892 Vector p1, p2;
4894 assert (EDGE_FORWARD_VERTEX (s1) == EDGE_BACKWARD_VERTEX (s2));
4895 Vsub2 (p1, EDGE_BACKWARD_VERTEX (s2)->point, EDGE_BACKWARD_VERTEX (s1)->point); /* See assert above for first arg */
4896 Vsub2 (p2, EDGE_FORWARD_VERTEX (s2)->point, EDGE_BACKWARD_VERTEX (s2)->point);
4898 /* If the product below is zero then segments are on the same line */
4899 return (vect_det2 (p1, p2) == 0);
4902 /*! \brief arc_segments_can_merge
4904 * Return whether the two adjacent edge segments in a contour approximate the
4905 * same arc, are co-circular, and can be simpliied.
4907 * s1 and s2 are treated as edges
4909 static bool
4910 arc_segments_can_merge (VNODE *s1, VNODE *s2)
4912 return (s1->cx == s2->cx &&
4913 s1->cy == s2->cy &&
4914 s1->radius == s2->radius);
4918 /*! \brief
4919 * Simplify a single polygon contour by consolidating adjacent
4920 * line segments approximating the same underlying arc curve.
4922 * NB: Operates on a single contour, does not follow links
4924 * XXX: Does not update the segment r-tree, so no further
4925 * boolean operations should be attempted on this polygon!
4927 static void
4928 simplify_contour (PLINE *contour)
4930 VNODE *p, *c;
4931 int count = 0;
4933 /* XXX: Looks like this loop misses an iteration? IE.. no test between head->prev ------- head ------- head->next
4934 * in any case, if we remove the head vertex, we need to adjust the polygon...
4935 * does poly_ExclVertex cope with that? - NO!
4937 for (c = NEXT_VERTEX ((p = &contour->head)); c != &contour->head; c = NEXT_VERTEX (p = c))
4939 bool delete_vertex_c;
4941 if (VERTEX_FORWARD_EDGE (c)->is_round == false)
4942 count = 0;
4944 if (!VERTEX_FORWARD_EDGE (p)->is_round && !VERTEX_FORWARD_EDGE (c)->is_round)
4946 delete_vertex_c = line_segments_can_merge (VERTEX_FORWARD_EDGE (p), VERTEX_FORWARD_EDGE (c));
4947 // if (delete_vertex_c)
4948 // fprintf (stderr, "Merging adjacent line segments\n");
4950 else if (VERTEX_FORWARD_EDGE (p)->is_round && VERTEX_FORWARD_EDGE (c)->is_round)
4952 delete_vertex_c = arc_segments_can_merge (VERTEX_FORWARD_EDGE (p), VERTEX_FORWARD_EDGE (c));
4953 /* XXX: If we merge too many arc segments, they become more than 180 degrees span, and cw/ccw determination fails */
4954 if (count == POLY_CIRC_SEGS / 2 - 2) /* Thought -1 should work, but then appear to get bad polygon cutouts */
4956 delete_vertex_c = false;
4957 count = 0;
4959 if (delete_vertex_c)
4961 // fprintf (stderr, "Merging adjacent arc segments\n");
4962 count++;
4965 else
4967 /* LINE-ARC and ARC-LINE segments cannot merge */
4968 delete_vertex_c = false;
4969 count = 0;
4972 if (delete_vertex_c)
4974 assert (c != &contour->head);
4975 poly_ExclVertex (c);
4976 g_slice_free (VNODE, c);
4977 c = p;
4978 contour->Count --;
4984 /*! \brief
4985 * Create a simplified copy of the passed polygon.
4986 * Adjacent line segments approximating the same underlying curve
4987 * will be joined up.
4989 * XXX: "simplify_contour()" does not update the segment r-tree when
4990 * simplifying , so no further boolean operations should be
4991 * attempted on this polygon!
4993 * In any case, boolean operations rely on a suitably granular
4994 * linear approximationt to curves, which this operation removes.
4996 void
4997 poly_Simplify (POLYAREA *poly)
4999 POLYAREA *pa = poly;
5000 PLINE *curc;
5001 PLINE **last;
5003 if (poly == NULL)
5004 return;
5008 assert (pa->simple_contours == NULL);
5009 last = &pa->simple_contours;
5010 for (curc = pa->contours; curc != NULL; curc = curc->next)
5012 if (!poly_CopyContour (last, curc))
5013 g_assert_not_reached ();
5014 if (!(*last)->is_round) /* Don't worry about simplifying round contours, since those get special cased on output anyway */
5015 simplify_contour (*last);
5016 last = &(*last)->next;
5019 while ((pa = pa->f) != poly);