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.
17 * <h1><b>Copyright.</b></h1>\n
19 * Copyright (C) 2006 harry eaton
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
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.
55 * \note How about expanding polygons so that edges can be arcs rather
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
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
67 * If P2.y > P1.y then the center with greate X is selected for
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
84 #include "pcb-printf.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
,
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 */
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
151 #undef DEBUG_ALL_LABELS
158 #define DEBUGP(...) pcb_fprintf(stderr, ## __VA_ARGS__)
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,
183 static char *theState (VNODE
* v
);
186 pline_dump (VNODE
* 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
);
204 poly_dump (POLYAREA
* p
)
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
);
224 poly_CreateNodeFull (Vector v
, bool is_round
, Coord cx
, Coord cy
, Coord radius
)
230 res
= g_slice_new0 (VNODE
);
238 res
->is_round
= is_round
;
241 res
->radius
= radius
;
247 poly_CreateNode (Vector v
)
249 return poly_CreateNodeFull (v
, false, 0, 0, 0);
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 */
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
276 node_add_single (VNODE
* dest
, Vector po
)
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
);
288 p
->cvc_prev
= p
->cvc_next
= NULL
;
289 p
->Flags
.status
= UNKNWN
;
293 #define ISECT_BAD_PARAM (-1)
294 #define ISECT_NO_MEMORY (-2)
298 * \brief new_descriptor.
300 * (C) 2006 harry eaton.
303 new_descriptor (VNODE
* a
, PLINE
*pl
, char poly
, char side
)
305 CVCList
*l
= (CVCList
*) malloc (sizeof (CVCList
));
307 register double ang
, dx
, dy
;
313 l
->parent_contour
= pl
;
316 l
->next
= l
->prev
= l
;
318 if (side
== 'P') /* previous */
319 vect_sub (v
, PREV_VERTEX (a
)->point
, a
->point
);
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
))
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?
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 */
357 assert (ang
>= 0.0 && ang
<= 4.0);
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
);
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
)
377 else if (a
->angle
> b
->angle
)
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.
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
)))
401 /* search for the CVCList for this point */
404 start
= newone
; /* return is also new, so we know where start is */
405 start
->head
= newone
; /* circular list */
415 if (vect_equal (l
->parent
->point
, a
->point
)) /* this CVCList is at our point */
418 newone
->head
= l
->head
;
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
)
428 newone
->head
= start
;
436 l
= big
= small
= start
;
439 if (compare_cvc_nodes (l
->next
, l
) < 0)
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 */
449 newone
->next
= l
->next
;
450 l
->next
= l
->next
->prev
= newone
;
454 while ((l
= l
->next
) != start
);
455 /* didn't find it between points, it must go on an end */
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
)
464 newone
->next
= big
->next
;
465 big
->next
= big
->next
->prev
= newone
;
468 assert (small
->angle
>= newone
->angle
);
470 newone
->next
= small
;
471 newone
->prev
= small
->prev
;
472 small
->prev
= small
->prev
->next
= 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
486 * a is considered an edge
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
)
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
518 prev_cvc_from_other_poly (CVCList
*start
)
520 char this_poly
= start
->poly
;
523 while ((l
->poly
== this_poly
|| l
->skip_me
) && l
!= start
->next
)
526 if (l
->poly
== this_poly
|| l
->skip_me
)
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
543 next_cvc_from_other_poly (CVCList
*start
)
545 char this_poly
= start
->poly
;
548 while ((l
->poly
== this_poly
|| l
->skip_me
) && l
!= start
->prev
)
551 if (l
->poly
== this_poly
|| l
->skip_me
)
558 cvc_list_dump (CVCList
*list
)
566 fprintf (stderr
, "CVC list is NULL\n");
572 pcb_fprintf (stderr
, "Dumping CVC list at (%$mn, %$mn)\n", node
->point
[0], node
->point
[1]);
578 pcb_fprintf (stderr
, "%p: angle = %.30e, poly = %c, side = %c, (%mn, %mn)-(%mn, %mn), Vertices: %p-%p Edge: %p\n",
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
));
591 } while ((iter
= iter
->next
) != list
);
593 if ((count
& 1) != 0)
594 g_critical ("Ended up with odd number of entries in CVC list");
600 * (C) 2006 harry eaton.
602 * (C) 2016 Peter Clifton
604 * pn is considered an edge
607 edge_label (VNODE
* pn
, int existing_label
)
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 */
621 l
= EDGE_BACKWARD_VERTEX (pn
)->cvc_next
;
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
);
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
);
640 if (compare_cvc_nodes (l
, test
) == 0)
642 shared_edge_case
= true;
647 test
= next_cvc_from_other_poly (l
);
648 if (compare_cvc_nodes (l
, test
) == 0)
649 shared_edge_case
= true;
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);
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
)
683 * \brief add_descriptors.
685 * (C) 2006 harry eaton.
688 add_descriptors (PLINE
* pl
, char poly
, CVCList
* list
)
690 VNODE
*node
= &pl
->head
; /* node is considered a vertex */
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
);
701 list
= node
->cvc_next
= insert_descriptor (node
, pl
, poly
, 'N', list
);
706 while ((node
= NEXT_VERTEX(node
)) != &pl
->head
);
711 cntrbox_check (PLINE
* c
, Vector p
)
713 return (p
[0] < c
->xmin
||
714 p
[0] + 1 > c
->xmax
||
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 */
738 typedef struct _insert_node_task insert_node_task
;
740 struct _insert_node_task
742 insert_node_task
*next
;
754 jmp_buf *env
, sego
, *touch
;
756 insert_node_task
*node_insert_list
;
758 POLYAREA
*looping_over_poly
;
759 POLYAREA
*rtree_over_poly
;
762 typedef struct contour_info
768 insert_node_task
*node_insert_list
;
769 POLYAREA
*looping_over_poly
;
770 POLYAREA
*rtree_over_poly
;
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.
783 adjust_tree (rtree_t
* tree
, struct seg
*s
)
787 q
= (seg
*)malloc (sizeof (struct seg
));
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
));
802 q
->v
= NEXT_EDGE (s
->v
);
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
);
814 * \brief seg_in_region().
816 * (C) 2006, harry eaton.
818 * This prunes the search for boxes that don't intersect the segment.
821 seg_in_region (const BoxType
* b
, void *cl
)
823 struct info
*i
= (struct info
*) cl
;
825 /* for zero slope the search is aligned on the axis so it is already pruned */
828 y1
= i
->m
* b
->X1
+ i
->b
;
829 y2
= i
->m
* b
->X2
+ i
->b
;
830 if (min (y1
, y2
) >= b
->Y2
)
832 if (max (y1
, y2
) < b
->Y1
)
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
;
852 * \brief seg_in_seg().
854 * (C) 2006 harry eaton.
856 * This routine checks if the segment in the tree intersect the search
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.
869 seg_in_seg (const BoxType
* b
, void *cl
)
871 struct info
*i
= (struct info
*) cl
;
872 struct seg
*s
= (struct seg
*) b
;
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
)
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
);
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
;
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]);
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]);
914 i
->node_insert_list
=
915 prepend_insert_node_task (i
->node_insert_list
, i
->rtree_over_poly
, s
, new_node
);
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);
929 make_edge_tree (PLINE
* pb
)
932 VNODE
*bv
; /* bv is considred an edge */
933 rtree_t
*ans
= r_create_tree (NULL
, 0, 0);
937 s
= (seg
*)malloc (sizeof (struct seg
));
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;
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;
956 s
->box
.Y2
= EDGE_BACKWARD_VERTEX (bv
)->point
[1] + 1;
957 s
->box
.Y1
= EDGE_FORWARD_VERTEX (bv
)->point
[1];
961 r_insert_entry (ans
, (const BoxType
*) s
, 1);
963 while ((bv
= NEXT_EDGE (bv
)) != &pb
->head
);
968 get_seg (const BoxType
* b
, void *cl
)
970 struct info
*i
= (struct info
*) cl
;
971 struct seg
*s
= (struct seg
*) b
;
973 fprintf (stderr
, "get_seg testing against segment %p (seg %p)\n", s
->v
, s
);
977 longjmp (i
->sego
, 1);
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,
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.
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;
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 */
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.
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]};
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)
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)
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)
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)
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
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.
1122 vertex_in_seg_rounded (const BoxType
* b
, void *cl
)
1124 struct info
*i
= (struct info
*) cl
;
1125 struct seg
*s
= (struct seg
*) b
;
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.
1135 if (!line_point_inters (EDGE_BACKWARD_VERTEX (s
->v
)->point
, EDGE_FORWARD_VERTEX (s
->v
)->point
, i
->v
->point
))
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]);
1151 i
->node_insert_list
=
1152 prepend_insert_node_task (i
->node_insert_list
, i
->rtree_over_poly
, s
, new_node
);
1154 return 0; /* Keep looking for intersections with the test vertex */
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
;
1167 PLINE
*looping_over
;
1168 VNODE
*av
; /* node iterators */ /* av is considered an edge */
1172 /* Have vertex_in_seg_rounded return to our desired location if it touches */
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
;
1183 av
= &looping_over
->head
;
1184 do /* Loop over the edges in the a contour */
1186 /* check this vertex for any insertions */
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;
1207 intersect_rounded_impl (jmp_buf * jb
, POLYAREA
* b
, POLYAREA
* a
, int add
)
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" */
1223 c_info
.getout
= NULL
;
1228 retval
= setjmp (out
);
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
;
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
)
1248 /* Process any deferred node insersions */
1249 need_restart
|= process_deferred_intersections (c_info
.node_insert_list
);
1251 return need_restart
;
1255 intersect_rounded (jmp_buf * jb
, POLYAREA
* b
, POLYAREA
* a
, int add
)
1258 while (intersect_rounded_impl (jb
, b
, a
, add
))
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
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,
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.
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
;
1292 PLINE
*looping_over
;
1293 VNODE
*av
; /* node iterators */ /* av is considered an edge */
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
)
1312 info
.rtree_over_poly
= c_info
->rtree_over_poly
;
1313 info
.looping_over_poly
= c_info
->looping_over_poly
;
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 */
1329 /* compute the slant for region trimming */
1330 dx
= EDGE_FORWARD_VERTEX (av
)->point
[0] - EDGE_BACKWARD_VERTEX (av
)->point
[0];
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)
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
)
1353 if (setjmp (restart
))
1356 /* NB: If this actually hits anything, we are teleported back to the beginning */
1357 // info.tree = rtree_over->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;
1375 intersect_impl (jmp_buf * jb
, POLYAREA
* b
, POLYAREA
* a
, int add
)
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
)
1392 c_info
.looping_over_poly
= b
;
1393 c_info
.rtree_over_poly
= a
;
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" */
1407 c_info
.getout
= NULL
;
1412 retval
= setjmp (out
);
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
;
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
)
1432 /* Process any deferred node insersions */
1433 need_restart
|= process_deferred_intersections (c_info
.node_insert_list
);
1435 return need_restart
;
1439 intersect (jmp_buf * jb
, POLYAREA
* b
, POLYAREA
* a
, int add
)
1442 while (intersect_impl (jb
, b
, a
, add
))
1448 intersect_rounded_self (jmp_buf * e
, POLYAREA
*pfst
)
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
);
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
);
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
);
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
);
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 */
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 */
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
))
1568 * \brief cntr_in_M_POLYAREA.
1570 * \return poly is inside outfst ? (which POLYAREA *) : NULL.
1573 cntr_in_M_POLYAREA (PLINE
* poly
, POLYAREA
* outfst
, BOOLp test
)
1575 POLYAREA
*outer
= outfst
;
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
))
1596 outer
= (POLYAREA
*) heap_remove_smallest (heap
);
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 */
1604 case 1: /* Found we are inside this piece, and not any of its holes */
1605 heap_destroy (&heap
);
1607 case 2: /* Found inside a hole in the smallest polygon so far. No need to check the other polygons */
1608 heap_destroy (&heap
);
1611 printf ("Something strange here\n");
1616 heap_destroy (&heap
);
1618 } /* cntr_in_M_POLYAREA */
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
))
1645 #ifdef DEBUG_ALL_LABELS
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
);
1663 * \brief label_contour.
1665 * (C) 2006 harry eaton.
1667 * (C) 1993 Klamer Schutte.
1669 * (C) 1997 Alexey Nikitin, Michael Leonov.
1673 label_contour (PLINE
* a
)
1675 VNODE
*cure
= &a
->head
; /* cur is considered an edge */
1676 VNODE
*first_labelled
= NULL
;
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
;
1689 if (first_labelled
== NULL
)
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
;
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
1721 } /* label_contour */
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
)
1738 inside_container
= cntr_in_M_POLYAREA (pline
, pa
, test
);
1739 if (inside_container
!= NULL
)
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
;
1752 pline
->Flags
.status
= OUTSIDE
;
1755 } /* cntr_label_POLYAREA */
1758 M_POLYAREA_label_separated (PLINE
* afst
, POLYAREA
* b
, BOOLp touch
)
1762 for (curc
= afst
; curc
!= NULL
; curc
= curc
->next
)
1764 if (cntr_label_POLYAREA (curc
, b
, touch
) && touch
)
1771 M_POLYAREA_label (POLYAREA
* afst
, POLYAREA
* b
, BOOLp touch
)
1779 for (curc
= a
->contours
; curc
!= NULL
; curc
= curc
->next
)
1780 if (cntr_label_POLYAREA (curc
, b
, touch
))
1786 while (!touch
&& (a
= a
->f
) != afst
);
1792 * \brief Routines for temporary storing resulting contours.
1795 InsCntr (jmp_buf * e
, PLINE
* c
, POLYAREA
** dst
)
1799 newp
= poly_Create ();
1801 error(err_no_memory
);
1805 newp
->f
= newp
->b
= newp
;
1811 newp
->b
= (*dst
)->b
;
1812 newp
->f
->b
= newp
->b
->f
= newp
;
1817 r_insert_entry (newp
->contour_tree
, (BoxType
*) c
, 0);
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);
1828 if (cntr
->Flags
.orient
== PLF_DIR
)
1831 r_delete_entry (owner
->contour_tree
, (BoxType
*) cntr
);
1832 InsCntr (e
, cntr
, contours
);
1834 /* put hole into temporary list */
1837 /* if we know this belongs inside the parent, put it there now */
1840 cntr
->next
= parent_contour
->next
;
1841 parent_contour
->next
= cntr
;
1842 if (owner
!= parent
)
1845 r_delete_entry (owner
->contour_tree
, (BoxType
*) cntr
);
1846 r_insert_entry (parent
->contour_tree
, (BoxType
*) cntr
, 0);
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 */
1856 r_delete_entry (owner
->contour_tree
, (BoxType
*) cntr
);
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
;
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
;
1892 return 0; /* how did this happen? */
1893 heap_insert (heap
, p
->area
, pa_info
);
1897 struct find_inside_info
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 */
1911 if (check
->Flags
.orient
== PLF_DIR
)
1915 if (poly_ContourInContour (info
->want_inside
, check
))
1917 info
->result
= check
;
1918 longjmp (info
->jb
, 1);
1923 /* Returns a string allocated with g_malloc family of functions */
1925 merge_contour_name (char *old
, const char *new)
1930 return g_strdup (new);
1935 if (strcmp (old
, new) == 0)
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);
1946 InsertHoles (jmp_buf * e
, POLYAREA
* dest
, PLINE
** src
)
1949 PLINE
*curh
, *container
;
1953 int num_polyareas
= 0;
1954 struct polyarea_info
*all_pa_info
, *pa_info
;
1957 return; /* empty hole list */
1959 error (err_bad_parm
); /* empty contour list */
1961 /* Count dest 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);
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);
1985 while ((curc
= curc
->f
) != dest
);
1987 /* loop through the holes and put them where they belong */
1988 while ((curh
= *src
) != 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
))
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
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
;
2020 if (poly_ContourInContour (pa_info
->pa
->contours
, curh
))
2022 container
= pa_info
->pa
->contours
;
2025 if (heap_is_empty (heap
))
2027 pa_info
= (struct polyarea_info
*) heap_remove_smallest (heap
);
2031 heap_destroy (&heap
);
2032 if (container
== NULL
)
2034 /* bad input polygons were given */
2041 poly_DelContour (&curh
);
2042 error (err_bad_parm
);
2046 /* Need to check if this new hole means we need to kick out any old ones for reprocessing */
2049 struct find_inside_info info
;
2052 info
.want_inside
= curh
;
2054 /* Set jump return */
2055 if (setjmp (info
.jb
))
2057 /* Returned here! */
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? */
2071 /* We need to find the contour before it, so we can update its next pointer */
2073 while (prev
->next
!= info
.result
)
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
;
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
);
2102 /* routines for collecting result */
2106 UNINITIALISED
, FORW
, BACKW
2112 * e is considered an edge
2114 typedef int (*J_Rule
) (char p
, VNODE
*e
, DIRECTION
*cdir
);
2116 /* e is considered an edge */
2118 IsectJ_Rule (char p
, VNODE
*e
, DIRECTION
* cdir
)
2121 return (e
->Flags
.status
== INSIDE
|| e
->Flags
.status
== SHARED
);
2124 /* e is considered an edge */
2126 UniteJ_Rule (char p
, VNODE
*e
, DIRECTION
* cdir
)
2129 return (e
->Flags
.status
== OUTSIDE
|| e
->Flags
.status
== SHARED
);
2132 /* e is considered an edge */
2134 XorJ_Rule (char p
, VNODE
*e
, DIRECTION
* cdir
)
2136 if (e
->Flags
.status
== INSIDE
)
2141 if (e
->Flags
.status
== OUTSIDE
)
2149 /* e is considered an edge */
2151 SubJ_Rule (char p
, VNODE
*e
, DIRECTION
* cdir
)
2153 if (p
== 'B' && e
->Flags
.status
== INSIDE
)
2158 if (p
== 'A' && e
->Flags
.status
== OUTSIDE
)
2163 if (e
->Flags
.status
== SHARED2
)
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 */
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 */
2190 if (!(*curv
)->cvc_prev
) /* not a cross-vertex */
2192 if (VERTEX_DIRECTION_EDGE (*curv
, *cdir
)->Flags
.mark
)
2197 DEBUGP ("jump entering node at (%$mn, %$mn)\n", (*curv
)->point
[0], (*curv
)->point
[1]);
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 */
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
);
2212 if (!e
->Flags
.mark
&& j_rule (d
->poly
, e
, &newone
))
2214 if ((d
->side
== 'N' && newone
== FORW
) ||
2215 (d
->side
== 'P' && newone
== BACKW
))
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]);
2223 *contour_name
= merge_contour_name (*contour_name
, d
->parent_contour
->name
);
2230 /* Spin (anti?)clock-wise one edge descriptor (normal case) */
2236 /* Get the edge e, associated with that descriptor */
2237 e
= VERTEX_SIDE_DIR_EDGE (d
->parent
, d
->side
);
2239 if (!e
->Flags
.mark
&& j_rule (d
->poly
, e
, &newone
))
2241 if ((d
->side
== 'N' && newone
== FORW
) ||
2242 (d
->side
== 'P' && newone
== BACKW
))
2245 DEBUGP ("jump leaving node at (%$mn, %$mn)\n",
2246 EDGE_DIRECTION_VERTEX (e
, newone
)->point
[0], EDGE_DIRECTION_VERTEX (e
, newone
)->point
[1]);
2250 *contour_name
= merge_contour_name (*contour_name
, d
->parent_contour
->name
);
2255 while ((d
= d
->prev
) != start
); /* Keep searching around the cvc vertex for a suitable exit edge */
2259 /* start is considered a vertex */
2261 Gather (VNODE
*startv
, PLINE
**result
, J_Rule j_rule
, DIRECTION initdir
, char **contour_name
)
2263 VNODE
*curv
= startv
; /* curv is considered a vertex */
2265 DIRECTION dir
= initdir
;
2267 DEBUGP ("gather direction = %d\n", dir
);
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
;
2280 *result
= poly_NewContour (newn
);
2281 if (*result
== NULL
)
2282 return err_no_memory
;
2286 poly_InclVertex (PREV_VERTEX (&(*result
)->head
), newn
);
2289 DEBUGP ("gather vertex at (%$mn, %$mn), Dir=%i\n", curv
->point
[0], curv
->point
[1], dir
);
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 */
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
))
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
;
2320 /* curv is considered a vertex */
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 */
2327 if ((errc
= Gather (curv
, &p
, j_rule
, dir
, contour_name
)) != err_ok
)
2330 poly_DelContour (&p
);
2335 poly_PreContour (p
, TRUE
);
2339 DEBUGP ("adding contour with %d verticies and direction %c\n",
2340 p
->Count
, p
->Flags
.orient
? 'F' : 'B');
2342 PutContour (e
, p
, contours
, holes
, NULL
, NULL
, NULL
);
2346 /* some sort of computation error ? */
2348 DEBUGP ("Bad contour! Not enough points!!\n");
2350 poly_DelContour (&p
);
2355 Collect (char poly
, jmp_buf * e
, PLINE
* a
, POLYAREA
** contours
, PLINE
** holes
,
2358 VNODE
*cure
; /* cure is considered an edge */
2359 char *contour_name
= merge_contour_name (NULL
, a
->name
);
2360 DIRECTION dir
= UNINITIALISED
;
2363 // cur = (&a->head)->next->next->next->next->next; /* Breaks circ_seg_test9.pcb */
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)
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
);
2385 cntr_Collect (jmp_buf * e
, PLINE
** A
, POLYAREA
** contours
, PLINE
** holes
,
2386 int action
, POLYAREA
* owner
, POLYAREA
* parent
,
2387 PLINE
* parent_contour
)
2391 if ((*A
)->Flags
.status
== ISECTED
)
2396 Collect ('A', e
, *A
, contours
, holes
, UniteJ_Rule
);
2399 Collect ('A', e
, *A
, contours
, holes
, IsectJ_Rule
);
2402 Collect ('A', e
, *A
, contours
, holes
, XorJ_Rule
);
2405 Collect ('A', e
, *A
, contours
, holes
, SubJ_Rule
);
2414 if ((*A
)->Flags
.status
== INSIDE
)
2417 /* disappear this contour (rtree entry removed in PutContour) */
2419 tmprev
->next
= NULL
;
2420 PutContour (e
, tmprev
, contours
, holes
, owner
, NULL
, NULL
);
2425 if ((*A
)->Flags
.status
== INSIDE
)
2428 /* disappear this contour (rtree entry removed in PutContour) */
2430 tmprev
->next
= NULL
;
2431 poly_InvContour (tmprev
);
2432 PutContour (e
, tmprev
, contours
, holes
, owner
, NULL
, NULL
);
2435 /* break; *//* Fall through (I think this is correct! pcjc2) */
2438 if ((*A
)->Flags
.status
== OUTSIDE
)
2441 /* disappear this contour (rtree entry removed in PutContour) */
2443 tmprev
->next
= NULL
;
2444 PutContour (e
, tmprev
, contours
, holes
, owner
, parent
,
2452 } /* cntr_Collect */
2455 M_B_AREA_Collect (jmp_buf * e
, POLYAREA
* bfst
, POLYAREA
** contours
,
2456 PLINE
** holes
, int action
)
2459 PLINE
**cur
, **next
, *tmp
;
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.
2481 Collect ('B', e
, *cur
, contours
, holes
, UniteJ_Rule
);
2484 Collect ('B', e
, *cur
, contours
, holes
, IsectJ_Rule
);
2487 Collect ('B', e
, *cur
, contours
, holes
, XorJ_Rule
);
2490 Collect ('B', e
, *cur
, contours
, holes
, SubJ_Rule
);
2494 else if ((*cur
)->Flags
.status
== INSIDE
)
2499 poly_InvContour (*cur
);
2505 tmp
->Flags
.status
= UNKNWN
;
2506 PutContour (e
, tmp
, contours
, holes
, b
, NULL
, NULL
);
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
);
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
)
2537 tmp
->Flags
.status
= UNKNWN
;
2538 PutContour (e
, tmp
, contours
, holes
, b
, NULL
, NULL
);
2542 break; /* do nothing, not included */
2546 while ((b
= b
->f
) != bfst
);
2551 contour_is_first (POLYAREA
* a
, PLINE
* cur
)
2553 return (a
->contours
== cur
);
2558 contour_is_last (PLINE
* cur
)
2560 return (cur
->next
== NULL
);
2565 remove_polyarea (POLYAREA
** list
, POLYAREA
* piece
)
2567 /* If this item was the start of the list, advance that pointer */
2571 /* But reset it to NULL if it wraps around and hits us again */
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
;
2584 M_POLYAREA_separate_isected (jmp_buf * e
, POLYAREA
** pieces
,
2585 PLINE
** holes
, PLINE
** isected
)
2587 POLYAREA
*a
= *pieces
;
2589 PLINE
*curc
, *next
, *prev
;
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;
2604 finished
= (anext
== *pieces
);
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);
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
));
2628 /* Link into the list of intersected contours */
2629 curc
->next
= *isected
;
2632 else if (hole_contour
)
2634 /* Link into the list of holes */
2635 curc
->next
= *holes
;
2643 if (is_first
&& is_last
)
2645 remove_polyarea (pieces
, a
);
2646 poly_Free (&a
); /* NB: Sets a to NULL */
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. */
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
)
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
2674 POLYAREA
*want_inside
;
2675 POLYAREA
*is_inside
;
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
)
2689 /* Don't look at contours marked as being intersected */
2690 if (check
->Flags
.status
== ISECTED
)
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);
2703 M_POLYAREA_update_primary (jmp_buf * e
, POLYAREA
** pieces
,
2704 PLINE
** holes
, int action
, POLYAREA
* bpa
)
2706 POLYAREA
*a
= *pieces
;
2709 PLINE
*curc
, *next
, *prev
;
2711 /* int inv_inside = 0; */
2713 int del_outside
= 0;
2728 case PBO_XOR
: /* NOT IMPLEMENTED OR USED */
2729 /* inv_inside = 1; */
2734 box
= *((BoxType
*) bpa
->contours
);
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
);
2750 POLYAREA
*inside_container
;
2751 PLINE
*inside_contour
;
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 */
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 */
2791 while (curc
->next
!= NULL
)
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
;
2802 remove_polyarea (pieces
, a
);
2803 poly_Free (&a
); /* NB: Sets a to NULL */
2808 /* Loop whilst we find INSIDE contours to delete */
2811 struct find_inside_m_pa_info info
;
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
;
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
,
2833 /* Nothing found? */
2837 /* We need to find the contour before it, so we can update its next pointer */
2839 while (prev
->next
!= info
.result
)
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
);
2868 /* This path isn't optimised for speed */
2873 int hole_contour
= 0;
2877 finished
= (anext
== *pieces
);
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;
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
)
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
));
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
;
2930 if (is_first
&& is_last
)
2932 remove_polyarea (pieces
, a
);
2933 poly_Free (&a
); /* NB: Sets a to NULL */
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. */
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
)
2952 /* If we deleted all the pieces of the polyarea, *pieces is NULL */
2954 while ((a
= anext
), *pieces
!= NULL
&& !finished
);
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
))
2970 /* XXX: What about rogue contours we re-labelled as INSIDE or OUTSIDE - ARE THEY DEALT WITH OK? */
2974 M_POLYAREA_Collect (jmp_buf * e
, POLYAREA
* afst
, POLYAREA
** contours
,
2975 PLINE
** holes
, int action
, BOOLp maybe
)
2978 POLYAREA
*parent
= NULL
; /* Quiet compiler warning */
2979 PLINE
**cur
, **next
, *parent_contour
;
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
;
2989 parent_contour
= NULL
;
2991 /* Take care of the first contour - so we know if we
2992 * can shortcut reparenting some of its children
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 */
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
,
3017 while ((a
= a
->f
) != afst
);
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
);
3046 add_dummy_descriptors_at_point (Vector point
, char poly
, CVCList
* list
, POLYAREA
*bfst
)
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
);
3067 mark_cvc_list_entry_as_skip (CVCList
*l
)
3076 next_cvc_from_same_poly (CVCList
*start
)
3082 /* Find the next edge from the same polygon */
3083 while (n
->poly
!= start
->poly
)
3091 find_edge_seg (VNODE
*edge
, PLINE
*contour
)
3096 /* fill in the segment in info corresponding to this node */
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;
3106 r_search (contour
->tree
, &box
, NULL
, get_seg
, &info
);
3107 /* Didn't find the edge if we get here */
3113 is_edge_in_contour (VNODE
*edge
, PLINE
*contour
)
3115 VNODE
*e
= &contour
->head
;
3121 while ((e
= NEXT_EDGE(e
)) != &contour
->head
);
3126 find_cvc_at_point (CVCList
*start
, Vector point
)
3135 if (vect_equal (l
->parent
->point
, point
)) /* this CVCList is at our point */
3138 if (vect_equal (l
->head
->parent
->point
, start
->parent
->point
)) /* Check for wrap-around in the list */
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.
3156 PLINE_check_hairline_edges (CVCList
*the_list
, PLINE
*contour
, POLYAREA
*bfst
)
3159 CVCList
*l
, *first_l
, *n
, *nn
;
3161 bool terminate_after_this_iteration
;
3163 /* Scan the PLINE and check for naughty shared edge segments */
3167 if (v
->cvc_prev
== NULL
&&
3168 v
->cvc_next
== NULL
) /* Careful, these can be NULL independantly now! */
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
;
3178 first_l
= v
->cvc_next
;
3181 terminate_after_this_iteration
= false;
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)
3192 /* Not sure why we get this, but apparently we can! */
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??");
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 */
3231 VNODE
*longer
; /* As vertex at our CVC node */
3232 VNODE
*shorter
; /* As vertex at our CVC node */
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
))
3245 shorter
= n
->parent
;
3246 longer_side
= l
->side
;
3247 shorter_side
= n
->side
;
3252 shorter
= l
->parent
;
3253 longer_side
= n
->side
;
3254 shorter_side
= l
->side
;
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
);
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
);
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
;
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
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);
3301 /* SKIP THIS.. NOTHING USES THE CONTOUR TREE AFTER INTERSECTION,
3302 * AND AS THIS IS AN INTERSECTED CONTOUR, THESE CONTOURS ARE
3303 * EVENTUALLY DROPPED.
3306 if (adjust_tree (contour
->tree
, node_seg
))
3307 assert (0); /* XXX: Memory allocation failure */
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
);
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
);
3341 terminate_after_this_iteration
= true;
3344 mark_cvc_list_entry_as_skip (l
);
3347 terminate_after_this_iteration
= true;
3349 mark_cvc_list_entry_as_skip (n
);
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.
3359 if (terminate_after_this_iteration
)
3363 while ((l
= n
) != first_l
);
3365 while ((v
= v
->next
) != &contour
->head
);
3369 M_POLYAREA_check_hairline_edges (CVCList
*the_list
, POLYAREA
*bfst
)
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.
3392 Touching (POLYAREA
* a
, POLYAREA
* b
)
3397 if ((code
= setjmp (e
)) == 0)
3400 if (!poly_Valid (a
))
3402 if (!poly_Valid (b
))
3405 M_POLYAREA_intersect (&e
, a
, b
, false, NULL
);
3407 if (M_POLYAREA_label (a
, b
, TRUE
))
3409 if (M_POLYAREA_label (b
, a
, TRUE
))
3412 else if (code
== TOUCHES
)
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
;
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 */
3435 M_Set_Parentage (POLYAREA
*poly
, POLYPARENTAGE parentage
)
3437 POLYAREA
*piece
= poly
;
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
;
3462 POLYAREA
*a_copy
, *b_copy
;
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
;
3475 /* Move the parentage information over onto the copy */
3478 M_Set_Parentage (a_copy
, a
->parentage
);
3479 M_Set_Parentage (a
, no_parentage
);
3484 M_Set_Parentage (b_copy
, b
->parentage
);
3485 M_Set_Parentage (b
, no_parentage
);
3523 if ((code
= setjmp (e
)) == 0)
3526 assert (poly_Valid (a
));
3527 assert (poly_Valid (b
));
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);
3538 M_POLYAREA_check_hairline_edges (the_list
, a
);
3539 M_POLYAREA_check_hairline_edges (the_list
, b
);
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
);
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
);
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
,
3558 M_B_AREA_Collect (&e
, b
, res
, &holes
, action
);
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
)
3574 poly_DelContour (&p
);
3584 assert (!*res
|| poly_Valid (*res
));
3586 /* Store perantage information */
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
);
3600 } /* poly_Boolean_free */
3603 clear_marks (POLYAREA
* p
)
3611 for (c
= n
->contours
; c
; c
= c
->next
)
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
;
3642 if ((code
= setjmp (e
)) == 0)
3646 if (!poly_Valid (a
))
3648 if (!poly_Valid (b
))
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
)
3663 poly_DelContour (&p
);
3668 M_POLYAREA_Collect (&e
, a
, aminusb
, &holes
, PBO_SUB
, FALSE
);
3669 InsertHoles (&e
, *aminusb
, &holes
);
3672 assert (poly_Valid (*aminusb
));
3674 /* delete holes if any left */
3675 while ((p
= holes
) != NULL
)
3678 poly_DelContour (&p
);
3685 poly_Free (aminusb
);
3688 assert (!*aandb
|| poly_Valid (*aandb
));
3689 assert (!*aminusb
|| poly_Valid (*aminusb
));
3691 } /* poly_AndSubtract_free */
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
);
3702 node_neighbours (VNODE
* a
, VNODE
* b
)
3704 return (a
== b
) || (a
->next
== b
) || (b
->next
== a
) || (a
->next
== b
->next
);
3708 poly_IniContour (PLINE
* c
)
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
;
3724 poly_NewContour (VNODE
*node
)
3728 res
= (PLINE
*) calloc (1, sizeof (PLINE
));
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
);
3746 poly_ClrContour (PLINE
* c
)
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;
3761 poly_IniContour (c
);
3765 poly_DelContour (PLINE
** c
)
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");
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");
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");
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");
3796 free ((*c
)->head
.cvc_prev
);
3798 /*! \todo FIXME -- strict aliasing violation. */
3801 rtree_t
*r
= (*c
)->tree
;
3802 r_destroy_tree (&r
);
3804 free ((*c
)->tristrip_vertices
);
3806 free (*c
), *c
= NULL
;
3810 poly_PreContour (PLINE
* C
, BOOLp 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
);
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?? */
3852 /* calculate area for orientation */
3854 (double) (p
->point
[0] - c
->point
[0]) * (p
->point
[1] +
3856 cntrbox_adjust (C
, c
->point
);
3859 while ((c
= NEXT_VERTEX (p
= c
)) != &C
->head
);
3861 C
->area
= ABS (area
);
3863 C
->Flags
.orient
= ((area
< 0) ? PLF_INV
: PLF_DIR
);
3864 C
->tree
= (rtree_t
*)make_edge_tree (C
);
3865 } /* poly_PreContour */
3868 flip_cb (const BoxType
* b
, void *cl
)
3870 struct seg
*s
= (struct seg
*) b
;
3871 s
->v
= PREV_EDGE (s
->v
);
3876 poly_InvContour (PLINE
* c
)
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
;
3895 // printf ("poly_InvContour\n");
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
;
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;
3931 r_search (c
->tree
, NULL
, NULL
, flip_cb
, NULL
);
3933 assert (r
== c
->Count
);
3939 poly_ExclVertex (VNODE
* node
)
3941 assert (node
!= NULL
);
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
);
3952 poly_InclVertex (VNODE
* after
, VNODE
* node
)
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 */
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
);
3993 poly_CopyContour (PLINE
** dst
, PLINE
* src
)
3995 VNODE
*cur
, *newnode
;
3997 assert (src
!= NULL
);
3999 *dst
= poly_NewContour (poly_CreateNodeFull (src
->head
.point
, src
->head
.is_round
, src
->head
.cx
, src
->head
.cy
, src
->head
.radius
));
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
)
4020 // newnode->Flags = cur->Flags;
4021 poly_InclVertex (PREV_EDGE (&(*dst
)->head
), newnode
);
4023 (*dst
)->tree
= (rtree_t
*)make_edge_tree (*dst
);
4027 /* polygon routines */
4030 poly_Copy1 (POLYAREA
* dst
, const POLYAREA
* src
)
4032 PLINE
*cur
, **last
= &dst
->contours
;
4035 dst
->f
= dst
->b
= dst
;
4037 for (cur
= src
->contours
; cur
!= NULL
; cur
= cur
->next
)
4039 if (!poly_CopyContour (last
, cur
))
4041 r_insert_entry (dst
->contour_tree
, (BoxType
*) * last
, 0);
4042 last
= &(*last
)->next
;
4049 poly_Copy0 (POLYAREA
** dst
, const POLYAREA
* src
)
4055 if ((*dst
= poly_Create ()) == NULL
|| !poly_Copy1 (*dst
, src
))
4062 poly_M_Incl (POLYAREA
** list
, POLYAREA
* a
)
4065 a
->f
= a
->b
= a
, *list
= a
;
4070 (*list
)->b
= (*list
)->b
->f
= a
;
4075 poly_M_Copy0 (POLYAREA
** dst
, const POLYAREA
* srcfst
)
4077 const POLYAREA
*src
= srcfst
;
4086 if ((di
= poly_Create ()) == NULL
|| !poly_Copy1 (di
, src
))
4088 poly_M_Incl (dst
, di
);
4090 while ((src
= src
->f
) != srcfst
);
4095 poly_InclContour (POLYAREA
* p
, PLINE
* c
)
4099 if ((c
== NULL
) || (p
== NULL
))
4101 if (c
->Flags
.orient
== PLF_DIR
)
4103 if (p
->contours
!= NULL
)
4109 if (p
->contours
== NULL
)
4111 /* link at front of hole list */
4112 tmp
= p
->contours
->next
;
4113 p
->contours
->next
= c
;
4116 r_insert_entry (p
->contour_tree
, (BoxType
*) c
, 0);
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])
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];
4146 longjmp (p
->env
, 1);
4154 if (EDGE_FORWARD_VERTEX (s
->v
)->point
[1] <= p
->p
[1])
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];
4164 longjmp (p
->env
, 1);
4174 poly_InsideContour (PLINE
* c
, Vector p
)
4179 if (!cntrbox_pointin (c
, p
))
4182 info
.p
[0] = ray
.X1
= p
[0];
4183 info
.p
[1] = ray
.Y1
= p
[1];
4186 if (setjmp (info
.env
) == 0)
4187 r_search (c
->tree
, &ray
, NULL
, crossing
, &info
);
4192 poly_CheckInside (POLYAREA
* p
, Vector v0
)
4196 if ((p
== NULL
) || (v0
== NULL
) || (p
->contours
== NULL
))
4199 if (poly_InsideContour (cur
, v0
))
4201 for (cur
= cur
->next
; cur
!= NULL
; cur
= cur
->next
)
4202 if (poly_InsideContour (cur
, v0
))
4210 poly_M_CheckInside (POLYAREA
* p
, Vector v0
)
4214 if ((p
== NULL
) || (v0
== NULL
))
4219 if (poly_CheckInside (cur
, v0
))
4222 while ((cur
= cur
->f
) != p
);
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
4236 * Algorithm from http://www.blackpawn.com/texts/pointinpoly/default.html.
4239 point_in_triangle (Vector A
, Vector B
, Vector C
, Vector P
)
4242 double dot00
, dot01
, dot02
, dot11
, dot12
;
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
4276 dot_orthogonal_to_direction (Vector A
, Vector B
, Vector C
, Vector D
)
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];
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
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>
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>
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>
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.
4316 poly_ComputeInteriorPoint (PLINE
*poly
, Vector v
)
4318 VNODE
*pt1
, *pt2
, *pt3
, *q
;
4319 VNODE
*min_q
= NULL
;
4321 double min_dist
= 0.0;
4322 double dir
= (poly
->Flags
.orient
== PLF_DIR
) ? 1. : -1;
4324 /* Find a convex node on the polygon */
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.)
4339 while ((pt1
= NEXT_VERTEX (pt1
)) != &poly
->head
);
4341 /* Loop over remaining vertices */
4345 /* Is current vertex "q" outside pt1 pt2 pt3 triangle? */
4346 if (!point_in_triangle (pt1
->point
, pt2
->point
, pt3
->point
, q
->point
))
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
) {
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;
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;
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
)
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
))
4395 poly_ComputeInteriorPoint (inner
, point
);
4396 return poly_InsideContour (poly
, point
);
4402 poly_Init (POLYAREA
* p
)
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
;
4417 if ((res
= (POLYAREA
*)malloc (sizeof (POLYAREA
))) != NULL
)
4423 poly_FreeContours (PLINE
** pline
)
4427 while ((pl
= *pline
) != NULL
)
4430 poly_DelContour (&pl
);
4435 poly_Free (POLYAREA
** p
)
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
);
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
;
4464 inside_sector (VNODE
* pn
, Vector p2
)
4466 Vector cdir
, ndir
, pdir
;
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
)))
4482 } /* inside_sector */
4487 * \return TRUE if bad contour.
4490 poly_ChkContour (PLINE
* a
)
4492 VNODE
*a1
, *a2
, *hit1
, *hit2
;
4503 if (!node_neighbours (a1
, a2
) &&
4504 (icnt
= vect_inters2 (a1
->point
, a1
->next
->point
,
4505 a2
->point
, a2
->next
->point
, i1
, i2
)) > 0)
4510 if (vect_dist2 (i1
, a1
->point
) < EPSILON
)
4512 else if (vect_dist2 (i1
, a1
->next
->point
) < EPSILON
)
4517 if (vect_dist2 (i1
, a2
->point
) < EPSILON
)
4519 else if (vect_dist2 (i1
, a2
->next
->point
) < EPSILON
)
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.
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
))
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
))
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
))
4556 while ((a2
= a2
->next
) != &a
->head
);
4558 while ((a1
= a1
->next
) != &a
->head
);
4564 poly_Valid (POLYAREA
* p
)
4568 if ((p
== NULL
) || (p
->contours
== NULL
))
4571 if (p
->contours
->Flags
.orient
== PLF_INV
|| poly_ChkContour (p
->contours
))
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
);
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
))
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");
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
);
4621 Vector vect_zero
= { (long) 0, (long) 0 };
4623 /* L o n g V e c t o r S t u f f */
4626 vect_init (Vector v
, double x
, double y
)
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]);
4644 vect_sub (Vector res
, Vector v1
, Vector v2
)
4646 Vsub (res
, v1
, v2
)} /* vect_sub */
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];
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];
4663 vect_len2 (Vector v
)
4665 return ((double) v
[0] * v
[0] + (double) v
[1] * v
[1]); /* why sqrt? only used for compares */
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.
4681 vect_det2 (Vector v1
, Vector v2
)
4683 return (((double) v1
[0] * v2
[1]) - ((double) v2
[0] * v1
[1]));
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 */
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])
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]))
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)
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 */
4763 { /* hv and h are used as help-variable. */
4765 h
= dc1
, dc1
= dc2
, dc2
= h
;
4770 h
= d1
, d1
= d2
, d2
= h
;
4773 /* Now the line-pieces are compared */
4805 return (Vequ2 (S1
, S2
) ? 1 : 2);
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.
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
))
4835 else if (Vequ2 (q2
, p1
) || Vequ2 (q2
, p2
))
4842 s
= (rqy
* (p1
[0] - q1
[0]) + rqx
* (q1
[1] - p1
[1])) / deel
;
4843 if (s
< 0 || s
> 1.)
4845 t
= (rpy
* (p1
[0] - q1
[0]) + rpx
* (q1
[1] - p1
[1])) / deel
;
4846 if (t
< 0 || t
> 1.)
4849 S1
[0] = q1
[0] + ROUND (t
* rqx
);
4850 S1
[1] = q1
[1] + ROUND (t
* rqy
);
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};
4868 cnt
= vect_inters2_fp (p1_fp
, p2_fp
,
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
4890 line_segments_can_merge (VNODE
*s1
, VNODE
*s2
)
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
4910 arc_segments_can_merge (VNODE
*s1
, VNODE
*s2
)
4912 return (s1
->cx
== s2
->cx
&&
4914 s1
->radius
== s2
->radius
);
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!
4928 simplify_contour (PLINE
*contour
)
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)
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;
4959 if (delete_vertex_c
)
4961 // fprintf (stderr, "Merging adjacent arc segments\n");
4967 /* LINE-ARC and ARC-LINE segments cannot merge */
4968 delete_vertex_c
= false;
4972 if (delete_vertex_c
)
4974 assert (c
!= &contour
->head
);
4975 poly_ExclVertex (c
);
4976 g_slice_free (VNODE
, c
);
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.
4997 poly_Simplify (POLYAREA
*poly
)
4999 POLYAREA
*pa
= poly
;
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
);