2 polygon clipping functions. harry eaton implemented the algorithm
3 described in "A Closed Set of Algorithms for Performing Set
4 Operations on Polygonal Regions in the Plane" which the original
5 code did not do. I also modified it for integer coordinates
6 and faster computation. The license for this modified copy was
7 switched to the GPL per term (3) of the original LGPL license.
8 Copyright (C) 2006 harry eaton
11 poly_Boolean: a polygon clip library
12 Copyright (C) 1997 Alexey Nikitin, Michael Leonov
13 (also the authors of the paper describing the actual algorithm)
14 leonov@propro.iis.nsk.su
17 nclip: a polygon clip library
18 Copyright (C) 1993 Klamer Schutte
20 This program is free software; you can redistribute it and/or
21 modify it under the terms of the GNU General Public
22 License as published by the Free Software Foundation; either
23 version 2 of the License, or (at your option) any later version.
25 This program is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 General Public License for more details.
30 You should have received a copy of the GNU General Public
31 License along with this program; if not, write to the Free
32 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 (C) 1997 Alexey Nikitin, Michael Leonov
36 (C) 1993 Klamer Schutte
38 all cases where original (Klamer Schutte) code is present
53 #define ROUND(a) (long)((a) > 0 ? ((a) + 0.5) : ((a) - 0.5))
55 #define EPSILON (1E-8)
56 #define IsZero(a, b) (fabs((a) - (b)) < EPSILON)
58 /*********************************************************************/
59 /* L o n g V e c t o r S t u f f */
60 /*********************************************************************/
62 #define Vcopy(a,b) {(a)[0]=(b)[0];(a)[1]=(b)[1];}
63 int vect_equal (Vector v1
, Vector v2
);
64 void vect_init (Vector v
, double x
, double y
);
65 void vect_sub (Vector res
, Vector v2
, Vector v3
);
67 void vect_min (Vector res
, Vector v2
, Vector v3
);
68 void vect_max (Vector res
, Vector v2
, Vector v3
);
70 double vect_dist2 (Vector v1
, Vector v2
);
71 double vect_det2 (Vector v1
, Vector v2
);
72 double vect_len2 (Vector v1
);
74 int vect_inters2 (Vector A
, Vector B
, Vector C
, Vector D
, Vector S1
,
77 /* note that a vertex v's Flags.status represents the edge defined by
78 * v to v->next (i.e. the edge is forward of v)
89 #define NODE_LABEL(n) ((n)->Flags.status)
90 #define LABEL_NODE(n,l) ((n)->Flags.status = (l))
92 #define error(code) longjmp(*(e), code)
94 #define MemGet(ptr, type) \
95 if (UNLIKELY (((ptr) = (type *)malloc(sizeof(type))) == NULL)) \
99 #undef DEBUG_ALL_LABELS
105 #define DEBUGP(...) pcb_fprintf(stderr, ## __VA_ARGS__)
110 /* ///////////////////////////////////////////////////////////////////////////// * /
111 / * 2-Dimentional stuff
112 / * ///////////////////////////////////////////////////////////////////////////// */
114 #define Vsub2(r,a,b) {(r)[0] = (a)[0] - (b)[0]; (r)[1] = (a)[1] - (b)[1];}
115 #define Vadd2(r,a,b) {(r)[0] = (a)[0] + (b)[0]; (r)[1] = (a)[1] + (b)[1];}
116 #define Vsca2(r,a,s) {(r)[0] = (a)[0] * (s); (r)[1] = (a)[1] * (s);}
117 #define Vcpy2(r,a) {(r)[0] = (a)[0]; (r)[1] = (a)[1];}
118 #define Vequ2(a,b) ((a)[0] == (b)[0] && (a)[1] == (b)[1])
119 #define Vadds(r,a,b,s) {(r)[0] = ((a)[0] + (b)[0]) * (s); (r)[1] = ((a)[1] + (b)[1]) * (s);}
120 #define Vswp2(a,b) { long t; \
121 t = (a)[0], (a)[0] = (b)[0], (b)[0] = t; \
122 t = (a)[1], (a)[1] = (b)[1], (b)[1] = t; \
126 static char *theState (VNODE
* v
);
129 pline_dump (VNODE
* v
)
137 pcb_fprintf (stderr
, "Line [%#mS %#mS %#mS %#mS 10 10 \"%s\"]\n",
138 v
->point
[0], v
->point
[1],
139 n
->point
[0], n
->point
[1], theState (v
));
141 while ((v
= v
->next
) != s
);
145 poly_dump (POLYAREA
* p
)
155 pline_dump (&pl
->head
);
156 fprintf (stderr
, "NEXT PLINE\n");
158 while ((pl
= pl
->next
) != NULL
);
159 fprintf (stderr
, "NEXT POLY\n");
161 while ((p
= p
->f
) != f
);
165 /***************************************************************/
166 /* routines for processing intersections */
170 (C) 1993 Klamer Schutte
171 (C) 1997 Alexey Nikitin, Michael Leonov
174 returns a bit field in new_point that indicates where the
176 1 means a new node was created and inserted
177 4 means the intersection was not on the dest point
180 node_add_single (VNODE
* dest
, Vector po
)
184 if (vect_equal (po
, dest
->point
))
186 if (vect_equal (po
, dest
->next
->point
))
188 p
= poly_CreateNode (po
);
191 p
->cvc_prev
= p
->cvc_next
= NULL
;
192 p
->Flags
.status
= UNKNWN
;
196 #define ISECT_BAD_PARAM (-1)
197 #define ISECT_NO_MEMORY (-2)
205 new_descriptor (VNODE
* a
, char poly
, char side
)
207 CVCList
*l
= (CVCList
*) malloc (sizeof (CVCList
));
209 register double ang
, dx
, dy
;
217 l
->next
= l
->prev
= l
;
218 if (side
== 'P') /* previous */
219 vect_sub (v
, a
->prev
->point
, a
->point
);
221 vect_sub (v
, a
->next
->point
, a
->point
);
222 /* Uses slope/(slope+1) in quadrant 1 as a proxy for the angle.
223 * It still has the same monotonic sort result
224 * and is far less expensive to compute than the real angle.
226 if (vect_equal (v
, vect_zero
))
230 if (a
->prev
->cvc_prev
== (CVCList
*) - 1)
231 a
->prev
->cvc_prev
= a
->prev
->cvc_next
= NULL
;
232 poly_ExclVertex (a
->prev
);
233 vect_sub (v
, a
->prev
->point
, a
->point
);
237 if (a
->next
->cvc_prev
== (CVCList
*) - 1)
238 a
->next
->cvc_prev
= a
->next
->cvc_next
= NULL
;
239 poly_ExclVertex (a
->next
);
240 vect_sub (v
, a
->next
->point
, a
->point
);
243 assert (!vect_equal (v
, vect_zero
));
244 dx
= fabs ((double) v
[0]);
245 dy
= fabs ((double) v
[1]);
246 ang
= dy
/ (dy
+ dx
);
247 /* now move to the actual quadrant */
248 if (v
[0] < 0 && v
[1] >= 0)
249 ang
= 2.0 - ang
; /* 2nd quadrant */
250 else if (v
[0] < 0 && v
[1] < 0)
251 ang
+= 2.0; /* 3rd quadrant */
252 else if (v
[0] >= 0 && v
[1] < 0)
253 ang
= 4.0 - ang
; /* 4th quadrant */
255 assert (ang
>= 0.0 && ang
<= 4.0);
257 DEBUGP ("node on %c at %#mD assigned angle %g on side %c\n", poly
,
258 a
->point
[0], a
->point
[1], ang
, side
);
267 argument a is a cross-vertex node.
268 argument poly is the polygon it comes from ('A' or 'B')
269 argument side is the side this descriptor goes on ('P' for previous
271 argument start is the head of the list of cvclists
274 insert_descriptor (VNODE
* a
, char poly
, char side
, CVCList
* start
)
276 CVCList
*l
, *newone
, *big
, *small
;
278 if (!(newone
= new_descriptor (a
, poly
, side
)))
280 /* search for the CVCList for this point */
283 start
= newone
; /* return is also new, so we know where start is */
284 start
->head
= newone
; /* circular list */
293 if (l
->parent
->point
[0] == a
->point
[0]
294 && l
->parent
->point
[1] == a
->point
[1])
295 { /* this CVCList is at our point */
297 newone
->head
= l
->head
;
300 if (l
->head
->parent
->point
[0] == start
->parent
->point
[0]
301 && l
->head
->parent
->point
[1] == start
->parent
->point
[1])
303 /* this seems to be a new point */
304 /* link this cvclist to the list of all cvclists */
305 for (; l
->head
!= newone
; l
= l
->next
)
307 newone
->head
= start
;
315 l
= big
= small
= start
;
318 if (l
->next
->angle
< l
->angle
) /* find start/end of list */
323 else if (newone
->angle
>= l
->angle
&& newone
->angle
<= l
->next
->angle
)
325 /* insert new cvc if it lies between existing points */
327 newone
->next
= l
->next
;
328 l
->next
= l
->next
->prev
= newone
;
332 while ((l
= l
->next
) != start
);
333 /* didn't find it between points, it must go on an end */
334 if (big
->angle
<= newone
->angle
)
337 newone
->next
= big
->next
;
338 big
->next
= big
->next
->prev
= newone
;
341 assert (small
->angle
>= newone
->angle
);
342 newone
->next
= small
;
343 newone
->prev
= small
->prev
;
344 small
->prev
= small
->prev
->next
= newone
;
350 (C) 1993 Klamer Schutte
351 (C) 1997 Alexey Nikitin, Michael Leonov
353 return 1 if new node in b, 2 if new node in a and 3 if new node in both
357 node_add_single_point (VNODE
* a
, Vector p
)
359 VNODE
*next_a
, *new_node
;
363 new_node
= node_add_single (a
, p
);
364 assert (new_node
!= NULL
);
366 new_node
->cvc_prev
= new_node
->cvc_next
= (CVCList
*) - 1;
368 if (new_node
== a
|| new_node
== next_a
)
372 } /* node_add_point */
379 node_label (VNODE
* pn
)
381 CVCList
*first_l
, *l
;
386 assert (pn
->cvc_next
);
387 this_poly
= pn
->cvc_next
->poly
;
388 /* search counter-clockwise in the cross vertex connectivity (CVC) list
390 * check for shared edges (that could be prev or next in the list since the angles are equal)
391 * and check if this edge (pn -> pn->next) is found between the other poly's entry and exit
393 if (pn
->cvc_next
->angle
== pn
->cvc_next
->prev
->angle
)
394 l
= pn
->cvc_next
->prev
;
396 l
= pn
->cvc_next
->next
;
399 while ((l
->poly
== this_poly
) && (l
!= first_l
->prev
))
401 assert (l
->poly
!= this_poly
);
403 assert (l
&& l
->angle
>= 0 && l
->angle
<= 4.0);
404 if (l
->poly
!= this_poly
)
408 if (l
->parent
->prev
->point
[0] == pn
->next
->point
[0] &&
409 l
->parent
->prev
->point
[1] == pn
->next
->point
[1])
412 pn
->shared
= l
->parent
->prev
;
419 if (l
->angle
== pn
->cvc_next
->angle
)
421 assert (l
->parent
->next
->point
[0] == pn
->next
->point
[0] &&
422 l
->parent
->next
->point
[1] == pn
->next
->point
[1]);
424 pn
->shared
= l
->parent
;
430 if (region
== UNKNWN
)
432 for (l
= l
->next
; l
!= pn
->cvc_next
; l
= l
->next
)
434 if (l
->poly
!= this_poly
)
444 assert (region
!= UNKNWN
);
445 assert (NODE_LABEL (pn
) == UNKNWN
|| NODE_LABEL (pn
) == region
);
446 LABEL_NODE (pn
, region
);
447 if (region
== SHARED
|| region
== SHARED2
)
457 add_descriptors (PLINE
* pl
, char poly
, CVCList
* list
)
459 VNODE
*node
= &pl
->head
;
465 assert (node
->cvc_prev
== (CVCList
*) - 1
466 && node
->cvc_next
== (CVCList
*) - 1);
467 list
= node
->cvc_prev
= insert_descriptor (node
, poly
, 'P', list
);
470 list
= node
->cvc_next
= insert_descriptor (node
, poly
, 'N', list
);
475 while ((node
= node
->next
) != &pl
->head
);
480 cntrbox_adjust (PLINE
* c
, Vector p
)
482 c
->xmin
= min (c
->xmin
, p
[0]);
483 c
->xmax
= max (c
->xmax
, p
[0] + 1);
484 c
->ymin
= min (c
->ymin
, p
[1]);
485 c
->ymax
= max (c
->ymax
, p
[1] + 1);
488 /* some structures for handling segment intersections using the rtrees */
498 typedef struct _insert_node_task insert_node_task
;
500 struct _insert_node_task
502 insert_node_task
*next
;
513 jmp_buf *env
, sego
, *touch
;
515 insert_node_task
*node_insert_list
;
518 typedef struct contour_info
524 insert_node_task
*node_insert_list
;
530 * (C) 2006 harry eaton
531 * This replaces the segment in the tree with the two new segments after
532 * a vertex has been added
535 adjust_tree (rtree_t
* tree
, struct seg
*s
)
539 q
= (seg
*)malloc (sizeof (struct seg
));
545 q
->box
.X1
= min (q
->v
->point
[0], q
->v
->next
->point
[0]);
546 q
->box
.X2
= max (q
->v
->point
[0], q
->v
->next
->point
[0]) + 1;
547 q
->box
.Y1
= min (q
->v
->point
[1], q
->v
->next
->point
[1]);
548 q
->box
.Y2
= max (q
->v
->point
[1], q
->v
->next
->point
[1]) + 1;
549 r_insert_entry (tree
, (const BoxType
*) q
, 1);
550 q
= (seg
*)malloc (sizeof (struct seg
));
556 q
->box
.X1
= min (q
->v
->point
[0], q
->v
->next
->point
[0]);
557 q
->box
.X2
= max (q
->v
->point
[0], q
->v
->next
->point
[0]) + 1;
558 q
->box
.Y1
= min (q
->v
->point
[1], q
->v
->next
->point
[1]);
559 q
->box
.Y2
= max (q
->v
->point
[1], q
->v
->next
->point
[1]) + 1;
560 r_insert_entry (tree
, (const BoxType
*) q
, 1);
561 r_delete_entry (tree
, (const BoxType
*) s
);
567 * (C) 2006, harry eaton
568 * This prunes the search for boxes that don't intersect the segment.
571 seg_in_region (const BoxType
* b
, void *cl
)
573 struct info
*i
= (struct info
*) cl
;
575 /* for zero slope the search is aligned on the axis so it is already pruned */
578 y1
= i
->m
* b
->X1
+ i
->b
;
579 y2
= i
->m
* b
->X2
+ i
->b
;
580 if (min (y1
, y2
) >= b
->Y2
)
582 if (max (y1
, y2
) < b
->Y1
)
584 return 1; /* might intersect */
587 /* Prepend a deferred node-insersion task to a list */
588 static insert_node_task
*
589 prepend_insert_node_task (insert_node_task
*list
, seg
*seg
, VNODE
*new_node
)
591 insert_node_task
*task
= (insert_node_task
*)malloc (sizeof (*task
));
592 task
->node_seg
= seg
;
593 task
->new_node
= new_node
;
600 * (C) 2006 harry eaton
601 * This routine checks if the segment in the tree intersect the search segment.
602 * If it does, the plines are marked as intersected and the point is marked for
603 * the cvclist. If the point is not already a vertex, a new vertex is inserted
604 * and the search for intersections starts over at the beginning.
605 * That is potentially a significant time penalty, but it does solve the snap rounding
606 * problem. There are efficient algorithms for finding intersections with snap
607 * rounding, but I don't have time to implement them right now.
610 seg_in_seg (const BoxType
* b
, void *cl
)
612 struct info
*i
= (struct info
*) cl
;
613 struct seg
*s
= (struct seg
*) b
;
618 /* When new nodes are added at the end of a pass due to an intersection
619 * the segments may be altered. If either segment we're looking at has
620 * already been intersected this pass, skip it until the next pass.
622 if (s
->intersected
|| i
->s
->intersected
)
625 cnt
= vect_inters2 (s
->v
->point
, s
->v
->next
->point
,
626 i
->v
->point
, i
->v
->next
->point
, s1
, s2
);
629 if (i
->touch
) /* if checking touches one find and we're done */
630 longjmp (*i
->touch
, TOUCHES
);
631 i
->s
->p
->Flags
.status
= ISECTED
;
632 s
->p
->Flags
.status
= ISECTED
;
635 bool done_insert_on_i
= false;
636 new_node
= node_add_single_point (i
->v
, cnt
> 1 ? s2
: s1
);
637 if (new_node
!= NULL
)
639 #ifdef DEBUG_INTERSECT
640 DEBUGP ("new intersection on segment \"i\" at %#mD\n",
641 cnt
> 1 ? s2
[0] : s1
[0], cnt
> 1 ? s2
[1] : s1
[1]);
643 i
->node_insert_list
=
644 prepend_insert_node_task (i
->node_insert_list
, i
->s
, new_node
);
645 i
->s
->intersected
= 1;
646 done_insert_on_i
= true;
648 new_node
= node_add_single_point (s
->v
, cnt
> 1 ? s2
: s1
);
649 if (new_node
!= NULL
)
651 #ifdef DEBUG_INTERSECT
652 DEBUGP ("new intersection on segment \"s\" at %#mD\n",
653 cnt
> 1 ? s2
[0] : s1
[0], cnt
> 1 ? s2
[1] : s1
[1]);
655 i
->node_insert_list
=
656 prepend_insert_node_task (i
->node_insert_list
, s
, new_node
);
658 return 0; /* Keep looking for intersections with segment "i" */
660 /* Skip any remaining r_search hits against segment i, as any futher
661 * intersections will be rejected until the next pass anyway.
663 if (done_insert_on_i
)
664 longjmp (*i
->env
, 1);
670 make_edge_tree (PLINE
* pb
)
674 rtree_t
*ans
= r_create_tree (NULL
, 0, 0);
678 s
= (seg
*)malloc (sizeof (struct seg
));
680 if (bv
->point
[0] < bv
->next
->point
[0])
682 s
->box
.X1
= bv
->point
[0];
683 s
->box
.X2
= bv
->next
->point
[0] + 1;
687 s
->box
.X2
= bv
->point
[0] + 1;
688 s
->box
.X1
= bv
->next
->point
[0];
690 if (bv
->point
[1] < bv
->next
->point
[1])
692 s
->box
.Y1
= bv
->point
[1];
693 s
->box
.Y2
= bv
->next
->point
[1] + 1;
697 s
->box
.Y2
= bv
->point
[1] + 1;
698 s
->box
.Y1
= bv
->next
->point
[1];
702 r_insert_entry (ans
, (const BoxType
*) s
, 1);
704 while ((bv
= bv
->next
) != &pb
->head
);
709 get_seg (const BoxType
* b
, void *cl
)
711 struct info
*i
= (struct info
*) cl
;
712 struct seg
*s
= (struct seg
*) b
;
716 longjmp (i
->sego
, 1);
722 * intersect() (and helpers)
723 * (C) 2006, harry eaton
724 * This uses an rtree to find A-B intersections. Whenever a new vertex is
725 * added, the search for intersections is re-started because the rounding
726 * could alter the topology otherwise.
727 * This should use a faster algorithm for snap rounding intersection finding.
728 * The best algorthim is probably found in:
730 * "Improved output-sensitive snap rounding," John Hershberger, Proceedings
731 * of the 22nd annual symposium on Computational geomerty, 2006, pp 357-366.
732 * http://doi.acm.org/10.1145/1137856.1137909
734 * Algorithms described by de Berg, or Goodrich or Halperin, or Hobby would
735 * probably work as well.
740 contour_bounds_touch (const BoxType
* b
, void *cl
)
742 contour_info
*c_info
= (contour_info
*) cl
;
743 PLINE
*pa
= c_info
->pa
;
744 PLINE
*pb
= (PLINE
*) b
;
747 VNODE
*av
; /* node iterators */
752 /* Have seg_in_seg return to our desired location if it touches */
754 info
.touch
= c_info
->getout
;
755 info
.need_restart
= 0;
756 info
.node_insert_list
= c_info
->node_insert_list
;
758 /* Pick which contour has the fewer points, and do the loop
759 * over that. The r_tree makes hit-testing against a contour
760 * faster, so we want to do that on the bigger contour.
762 if (pa
->Count
< pb
->Count
)
773 av
= &looping_over
->head
;
774 do /* Loop over the nodes in the smaller contour */
776 /* check this edge for any insertions */
779 /* compute the slant for region trimming */
780 dx
= av
->next
->point
[0] - av
->point
[0];
785 info
.m
= (av
->next
->point
[1] - av
->point
[1]) / dx
;
786 info
.b
= av
->point
[1] - info
.m
* av
->point
[0];
788 box
.X2
= (box
.X1
= av
->point
[0]) + 1;
789 box
.Y2
= (box
.Y1
= av
->point
[1]) + 1;
791 /* fill in the segment in info corresponding to this node */
792 if (setjmp (info
.sego
) == 0)
794 r_search (looping_over
->tree
, &box
, NULL
, get_seg
, &info
);
798 /* If we're going to have another pass anyway, skip this */
799 if (info
.s
->intersected
&& info
.node_insert_list
!= NULL
)
802 if (setjmp (restart
))
805 /* NB: If this actually hits anything, we are teleported back to the beginning */
806 info
.tree
= rtree_over
->tree
;
808 if (UNLIKELY (r_search (info
.tree
, &info
.s
->box
,
809 seg_in_region
, seg_in_seg
, &info
)))
810 assert (0); /* XXX: Memory allocation failure */
812 while ((av
= av
->next
) != &looping_over
->head
);
814 c_info
->node_insert_list
= info
.node_insert_list
;
815 if (info
.need_restart
)
816 c_info
->need_restart
= 1;
821 intersect_impl (jmp_buf * jb
, POLYAREA
* b
, POLYAREA
* a
, int add
)
826 int need_restart
= 0;
827 insert_node_task
*task
;
828 c_info
.need_restart
= 0;
829 c_info
.node_insert_list
= NULL
;
831 /* Search the r-tree of the object with most contours
832 * We loop over the contours of "a". Swap if necessary.
834 if (a
->contour_tree
->size
> b
->contour_tree
->size
)
841 for (pa
= a
->contours
; pa
; pa
= pa
->next
) /* Loop over the contours of POLYAREA "a" */
847 c_info
.getout
= NULL
;
852 retval
= setjmp (out
);
855 /* The intersection test short-circuited back here,
856 * we need to clean up, then longjmp to jb */
857 longjmp (*jb
, retval
);
859 c_info
.getout
= &out
;
864 sb
.X2
= pa
->xmax
+ 1;
865 sb
.Y2
= pa
->ymax
+ 1;
867 r_search (b
->contour_tree
, &sb
, NULL
, contour_bounds_touch
, &c_info
);
868 if (c_info
.need_restart
)
872 /* Process any deferred node insersions */
873 task
= c_info
.node_insert_list
;
876 insert_node_task
*next
= task
->next
;
879 task
->new_node
->prev
= task
->node_seg
->v
;
880 task
->new_node
->next
= task
->node_seg
->v
->next
;
881 task
->node_seg
->v
->next
->prev
= task
->new_node
;
882 task
->node_seg
->v
->next
= task
->new_node
;
883 task
->node_seg
->p
->Count
++;
885 cntrbox_adjust (task
->node_seg
->p
, task
->new_node
->point
);
886 if (adjust_tree (task
->node_seg
->p
->tree
, task
->node_seg
))
887 assert (0); /* XXX: Memory allocation failure */
889 need_restart
= 1; /* Any new nodes could intersect */
899 intersect (jmp_buf * jb
, POLYAREA
* b
, POLYAREA
* a
, int add
)
902 while (intersect_impl (jb
, b
, a
, add
))
908 M_POLYAREA_intersect (jmp_buf * e
, POLYAREA
* afst
, POLYAREA
* bfst
, int add
)
910 POLYAREA
*a
= afst
, *b
= bfst
;
911 PLINE
*curcA
, *curcB
;
912 CVCList
*the_list
= NULL
;
914 if (a
== NULL
|| b
== NULL
)
915 error (err_bad_parm
);
920 if (a
->contours
->xmax
>= b
->contours
->xmin
&&
921 a
->contours
->ymax
>= b
->contours
->ymin
&&
922 a
->contours
->xmin
<= b
->contours
->xmax
&&
923 a
->contours
->ymin
<= b
->contours
->ymax
)
925 if (UNLIKELY (intersect (e
, a
, b
, add
)))
926 error (err_no_memory
);
929 while (add
&& (a
= a
->f
) != afst
);
930 for (curcB
= b
->contours
; curcB
!= NULL
; curcB
= curcB
->next
)
931 if (curcB
->Flags
.status
== ISECTED
)
933 the_list
= add_descriptors (curcB
, 'B', the_list
);
934 if (UNLIKELY (the_list
== NULL
))
935 error (err_no_memory
);
938 while (add
&& (b
= b
->f
) != bfst
);
941 for (curcA
= a
->contours
; curcA
!= NULL
; curcA
= curcA
->next
)
942 if (curcA
->Flags
.status
== ISECTED
)
944 the_list
= add_descriptors (curcA
, 'A', the_list
);
945 if (UNLIKELY (the_list
== NULL
))
946 error (err_no_memory
);
949 while (add
&& (a
= a
->f
) != afst
);
950 } /* M_POLYAREA_intersect */
953 cntrbox_inside (PLINE
* c1
, PLINE
* c2
)
955 assert (c1
!= NULL
&& c2
!= NULL
);
956 return ((c1
->xmin
>= c2
->xmin
) &&
957 (c1
->ymin
>= c2
->ymin
) &&
958 (c1
->xmax
<= c2
->xmax
) && (c1
->ymax
<= c2
->ymax
));
961 /*****************************************************************/
962 /* Routines for making labels */
965 count_contours_i_am_inside (const BoxType
* b
, void *cl
)
967 PLINE
*me
= (PLINE
*) cl
;
968 PLINE
*check
= (PLINE
*) b
;
970 if (poly_ContourInContour (check
, me
))
975 /* cntr_in_M_POLYAREA
976 returns poly is inside outfst ? TRUE : FALSE */
978 cntr_in_M_POLYAREA (PLINE
* poly
, POLYAREA
* outfst
, BOOLp test
)
980 POLYAREA
*outer
= outfst
;
983 assert (poly
!= NULL
);
984 assert (outer
!= NULL
);
986 heap
= heap_create ();
989 if (cntrbox_inside (poly
, outer
->contours
))
990 heap_insert (heap
, outer
->contours
->area
, (void *) outer
);
992 /* if checking touching, use only the first polygon */
993 while (!test
&& (outer
= outer
->f
) != outfst
);
994 /* we need only check the smallest poly container
995 * but we must loop in case the box containter is not
996 * the poly container */
999 if (heap_is_empty (heap
))
1001 outer
= (POLYAREA
*) heap_remove_smallest (heap
);
1004 (outer
->contour_tree
, (BoxType
*) poly
, NULL
,
1005 count_contours_i_am_inside
, poly
))
1007 case 0: /* Didn't find anything in this piece, Keep looking */
1009 case 1: /* Found we are inside this piece, and not any of its holes */
1010 heap_destroy (&heap
);
1012 case 2: /* Found inside a hole in the smallest polygon so far. No need to check the other polygons */
1013 heap_destroy (&heap
);
1016 printf ("Something strange here\n");
1021 heap_destroy (&heap
);
1023 } /* cntr_in_M_POLYAREA */
1028 theState (VNODE
* v
)
1030 static char u
[] = "UNKNOWN";
1031 static char i
[] = "INSIDE";
1032 static char o
[] = "OUTSIDE";
1033 static char s
[] = "SHARED";
1034 static char s2
[] = "SHARED2";
1036 switch (NODE_LABEL (v
))
1051 #ifdef DEBUG_ALL_LABELS
1053 print_labels (PLINE
* a
)
1055 VNODE
*c
= &a
->head
;
1059 DEBUGP ("%#mD->%#mD labeled %s\n", c
->point
[0], c
->point
[1],
1060 c
->next
->point
[0], c
->next
->point
[1], theState (c
));
1062 while ((c
= c
->next
) != &a
->head
);
1069 (C) 2006 harry eaton
1070 (C) 1993 Klamer Schutte
1071 (C) 1997 Alexey Nikitin, Michael Leonov
1075 label_contour (PLINE
* a
)
1077 VNODE
*cur
= &a
->head
;
1078 VNODE
*first_labelled
= NULL
;
1083 if (cur
->cvc_next
) /* examine cross vertex */
1085 label
= node_label (cur
);
1086 if (first_labelled
== NULL
)
1087 first_labelled
= cur
;
1091 if (first_labelled
== NULL
)
1094 /* This labels nodes which aren't cross-connected */
1095 assert (label
== INSIDE
|| label
== OUTSIDE
);
1096 LABEL_NODE (cur
, label
);
1098 while ((cur
= cur
->next
) != first_labelled
);
1099 #ifdef DEBUG_ALL_LABELS
1104 } /* label_contour */
1107 cntr_label_POLYAREA (PLINE
* poly
, POLYAREA
* ppl
, BOOLp test
)
1109 assert (ppl
!= NULL
&& ppl
->contours
!= NULL
);
1110 if (poly
->Flags
.status
== ISECTED
)
1112 label_contour (poly
); /* should never get here when BOOLp is true */
1114 else if (cntr_in_M_POLYAREA (poly
, ppl
, test
))
1118 poly
->Flags
.status
= INSIDE
;
1124 poly
->Flags
.status
= OUTSIDE
;
1127 } /* cntr_label_POLYAREA */
1130 M_POLYAREA_label_separated (PLINE
* afst
, POLYAREA
* b
, BOOLp touch
)
1134 for (curc
= afst
; curc
!= NULL
; curc
= curc
->next
)
1136 if (cntr_label_POLYAREA (curc
, b
, touch
) && touch
)
1143 M_POLYAREA_label (POLYAREA
* afst
, POLYAREA
* b
, BOOLp touch
)
1151 for (curc
= a
->contours
; curc
!= NULL
; curc
= curc
->next
)
1152 if (cntr_label_POLYAREA (curc
, b
, touch
))
1158 while (!touch
&& (a
= a
->f
) != afst
);
1162 /****************************************************************/
1164 /* routines for temporary storing resulting contours */
1166 InsCntr (jmp_buf * e
, PLINE
* c
, POLYAREA
** dst
)
1172 MemGet (*dst
, POLYAREA
);
1173 (*dst
)->f
= (*dst
)->b
= *dst
;
1178 MemGet (newp
, POLYAREA
);
1180 newp
->b
= (*dst
)->b
;
1181 newp
->f
->b
= newp
->b
->f
= newp
;
1184 newp
->contour_tree
= r_create_tree (NULL
, 0, 0);
1185 r_insert_entry (newp
->contour_tree
, (BoxType
*) c
, 0);
1190 PutContour (jmp_buf * e
, PLINE
* cntr
, POLYAREA
** contours
, PLINE
** holes
,
1191 POLYAREA
* owner
, POLYAREA
* parent
, PLINE
* parent_contour
)
1193 assert (cntr
!= NULL
);
1194 assert (cntr
->Count
> 2);
1197 if (cntr
->Flags
.orient
== PLF_DIR
)
1200 r_delete_entry (owner
->contour_tree
, (BoxType
*) cntr
);
1201 InsCntr (e
, cntr
, contours
);
1203 /* put hole into temporary list */
1206 /* if we know this belongs inside the parent, put it there now */
1209 cntr
->next
= parent_contour
->next
;
1210 parent_contour
->next
= cntr
;
1211 if (owner
!= parent
)
1214 r_delete_entry (owner
->contour_tree
, (BoxType
*) cntr
);
1215 r_insert_entry (parent
->contour_tree
, (BoxType
*) cntr
, 0);
1220 cntr
->next
= *holes
;
1221 *holes
= cntr
; /* let cntr be 1st hole in list */
1222 /* We don't insert the holes into an r-tree,
1223 * they just form a linked list */
1225 r_delete_entry (owner
->contour_tree
, (BoxType
*) cntr
);
1231 remove_contour (POLYAREA
* piece
, PLINE
* prev_contour
, PLINE
* contour
,
1232 int remove_rtree_entry
)
1234 if (piece
->contours
== contour
)
1235 piece
->contours
= contour
->next
;
1236 else if (prev_contour
!= NULL
)
1238 assert (prev_contour
->next
== contour
);
1239 prev_contour
->next
= contour
->next
;
1242 contour
->next
= NULL
;
1244 if (remove_rtree_entry
)
1245 r_delete_entry (piece
->contour_tree
, (BoxType
*) contour
);
1248 struct polyarea_info
1250 BoxType BoundingBox
;
1255 heap_it (const BoxType
* b
, void *cl
)
1257 heap_t
*heap
= (heap_t
*) cl
;
1258 struct polyarea_info
*pa_info
= (struct polyarea_info
*) b
;
1259 PLINE
*p
= pa_info
->pa
->contours
;
1261 return 0; /* how did this happen? */
1262 heap_insert (heap
, p
->area
, pa_info
);
1266 struct find_inside_info
1274 find_inside (const BoxType
* b
, void *cl
)
1276 struct find_inside_info
*info
= (struct find_inside_info
*) cl
;
1277 PLINE
*check
= (PLINE
*) b
;
1278 /* Do test on check to see if it inside info->want_inside */
1280 if (check
->Flags
.orient
== PLF_DIR
)
1284 if (poly_ContourInContour (info
->want_inside
, check
))
1286 info
->result
= check
;
1287 longjmp (info
->jb
, 1);
1293 InsertHoles (jmp_buf * e
, POLYAREA
* dest
, PLINE
** src
)
1296 PLINE
*curh
, *container
;
1300 int num_polyareas
= 0;
1301 struct polyarea_info
*all_pa_info
, *pa_info
;
1304 return; /* empty hole list */
1306 error (err_bad_parm
); /* empty contour list */
1308 /* Count dest polyareas */
1314 while ((curc
= curc
->f
) != dest
);
1316 /* make a polyarea info table */
1317 /* make an rtree of polyarea info table */
1318 all_pa_info
= (struct polyarea_info
*) malloc (sizeof (struct polyarea_info
) * num_polyareas
);
1319 tree
= r_create_tree (NULL
, 0, 0);
1324 all_pa_info
[i
].BoundingBox
.X1
= curc
->contours
->xmin
;
1325 all_pa_info
[i
].BoundingBox
.Y1
= curc
->contours
->ymin
;
1326 all_pa_info
[i
].BoundingBox
.X2
= curc
->contours
->xmax
;
1327 all_pa_info
[i
].BoundingBox
.Y2
= curc
->contours
->ymax
;
1328 all_pa_info
[i
].pa
= curc
;
1329 r_insert_entry (tree
, (const BoxType
*) &all_pa_info
[i
], 0);
1332 while ((curc
= curc
->f
) != dest
);
1334 /* loop through the holes and put them where they belong */
1335 while ((curh
= *src
) != NULL
)
1340 /* build a heap of all of the polys that the hole is inside its bounding box */
1341 heap
= heap_create ();
1342 r_search (tree
, (BoxType
*) curh
, NULL
, heap_it
, heap
);
1343 if (heap_is_empty (heap
))
1350 poly_DelContour (&curh
);
1351 error (err_bad_parm
);
1353 /* Now search the heap for the container. If there was only one item
1354 * in the heap, assume it is the container without the expense of
1357 pa_info
= (struct polyarea_info
*) heap_remove_smallest (heap
);
1358 if (heap_is_empty (heap
))
1359 { /* only one possibility it must be the right one */
1360 assert (poly_ContourInContour (pa_info
->pa
->contours
, curh
));
1361 container
= pa_info
->pa
->contours
;
1367 if (poly_ContourInContour (pa_info
->pa
->contours
, curh
))
1369 container
= pa_info
->pa
->contours
;
1372 if (heap_is_empty (heap
))
1374 pa_info
= (struct polyarea_info
*) heap_remove_smallest (heap
);
1378 heap_destroy (&heap
);
1379 if (container
== NULL
)
1381 /* bad input polygons were given */
1388 poly_DelContour (&curh
);
1389 error (err_bad_parm
);
1393 /* Need to check if this new hole means we need to kick out any old ones for reprocessing */
1396 struct find_inside_info info
;
1399 info
.want_inside
= curh
;
1401 /* Set jump return */
1402 if (setjmp (info
.jb
))
1404 /* Returned here! */
1409 /* Rtree search, calling back a routine to longjmp back with data about any hole inside the added one */
1410 /* Be sure not to bother jumping back to report the main contour! */
1411 r_search (pa_info
->pa
->contour_tree
, (BoxType
*) curh
, NULL
,
1412 find_inside
, &info
);
1414 /* Nothing found? */
1418 /* We need to find the contour before it, so we can update its next pointer */
1420 while (prev
->next
!= info
.result
)
1425 /* Remove hole from the contour */
1426 remove_contour (pa_info
->pa
, prev
, info
.result
, TRUE
);
1428 /* Add hole as the next on the list to be processed in this very function */
1429 info
.result
->next
= *src
;
1432 /* End check for kicked out holes */
1434 /* link at front of hole list */
1435 curh
->next
= container
->next
;
1436 container
->next
= curh
;
1437 r_insert_entry (pa_info
->pa
->contour_tree
, (BoxType
*) curh
, 0);
1441 r_destroy_tree (&tree
);
1446 /****************************************************************/
1447 /* routines for collecting result */
1455 typedef int (*S_Rule
) (VNODE
*, DIRECTION
*);
1458 typedef int (*J_Rule
) (char, VNODE
*, DIRECTION
*);
1461 UniteS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1464 return (NODE_LABEL (cur
) == OUTSIDE
) || (NODE_LABEL (cur
) == SHARED
);
1468 IsectS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1471 return (NODE_LABEL (cur
) == INSIDE
) || (NODE_LABEL (cur
) == SHARED
);
1475 SubS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1478 return (NODE_LABEL (cur
) == OUTSIDE
) || (NODE_LABEL (cur
) == SHARED2
);
1482 XorS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1484 if (cur
->Flags
.status
== INSIDE
)
1489 if (cur
->Flags
.status
== OUTSIDE
)
1498 IsectJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1500 assert (*cdir
== FORW
);
1501 return (v
->Flags
.status
== INSIDE
|| v
->Flags
.status
== SHARED
);
1505 UniteJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1507 assert (*cdir
== FORW
);
1508 return (v
->Flags
.status
== OUTSIDE
|| v
->Flags
.status
== SHARED
);
1512 XorJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1514 if (v
->Flags
.status
== INSIDE
)
1519 if (v
->Flags
.status
== OUTSIDE
)
1528 SubJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1530 if (p
== 'B' && v
->Flags
.status
== INSIDE
)
1535 if (p
== 'A' && v
->Flags
.status
== OUTSIDE
)
1540 if (v
->Flags
.status
== SHARED2
)
1551 /* return the edge that comes next.
1552 * if the direction is BACKW, then we return the next vertex
1553 * so that prev vertex has the flags for the edge
1555 * returns true if an edge is found, false otherwise
1558 jump (VNODE
** cur
, DIRECTION
* cdir
, J_Rule rule
)
1564 if (!(*cur
)->cvc_prev
) /* not a cross-vertex */
1566 if (*cdir
== FORW
? (*cur
)->Flags
.mark
: (*cur
)->prev
->Flags
.mark
)
1571 DEBUGP ("jump entering node at %$mD\n", (*cur
)->point
[0], (*cur
)->point
[1]);
1574 d
= (*cur
)->cvc_prev
->prev
;
1576 d
= (*cur
)->cvc_next
->prev
;
1584 if (!e
->Flags
.mark
&& rule (d
->poly
, e
, &newone
))
1586 if ((d
->side
== 'N' && newone
== FORW
) ||
1587 (d
->side
== 'P' && newone
== BACKW
))
1591 DEBUGP ("jump leaving node at %#mD\n",
1592 e
->next
->point
[0], e
->next
->point
[1]);
1594 DEBUGP ("jump leaving node at %#mD\n",
1595 e
->point
[0], e
->point
[1]);
1603 while ((d
= d
->prev
) != start
);
1608 Gather (VNODE
* start
, PLINE
** result
, J_Rule v_rule
, DIRECTION initdir
)
1610 VNODE
*cur
= start
, *newn
;
1611 DIRECTION dir
= initdir
;
1613 DEBUGP ("gather direction = %d\n", dir
);
1615 assert (*result
== NULL
);
1618 /* see where to go next */
1619 if (!jump (&cur
, &dir
, v_rule
))
1621 /* add edge to polygon */
1624 *result
= poly_NewContour (cur
->point
);
1625 if (*result
== NULL
)
1626 return err_no_memory
;
1630 if ((newn
= poly_CreateNode (cur
->point
)) == NULL
)
1631 return err_no_memory
;
1632 poly_InclVertex ((*result
)->head
.prev
, newn
);
1635 DEBUGP ("gather vertex at %#mD\n", cur
->point
[0], cur
->point
[1]);
1637 /* Now mark the edge as included. */
1638 newn
= (dir
== FORW
? cur
: cur
->prev
);
1639 newn
->Flags
.mark
= 1;
1640 /* for SHARED edge mark both */
1642 newn
->shared
->Flags
.mark
= 1;
1644 /* Advance to the next edge. */
1645 cur
= (dir
== FORW
? cur
->next
: cur
->prev
);
1652 Collect1 (jmp_buf * e
, VNODE
* cur
, DIRECTION dir
, POLYAREA
** contours
,
1653 PLINE
** holes
, J_Rule j_rule
)
1655 PLINE
*p
= NULL
; /* start making contour */
1658 Gather (dir
== FORW
? cur
: cur
->next
, &p
, j_rule
, dir
)) != err_ok
)
1661 poly_DelContour (&p
);
1666 poly_PreContour (p
, TRUE
);
1670 DEBUGP ("adding contour with %d verticies and direction %c\n",
1671 p
->Count
, p
->Flags
.orient
? 'F' : 'B');
1673 PutContour (e
, p
, contours
, holes
, NULL
, NULL
, NULL
);
1677 /* some sort of computation error ? */
1679 DEBUGP ("Bad contour! Not enough points!!\n");
1681 poly_DelContour (&p
);
1686 Collect (jmp_buf * e
, PLINE
* a
, POLYAREA
** contours
, PLINE
** holes
,
1687 S_Rule s_rule
, J_Rule j_rule
)
1695 if (s_rule (cur
, &dir
) && cur
->Flags
.mark
== 0)
1696 Collect1 (e
, cur
, dir
, contours
, holes
, j_rule
);
1698 if ((other
->cvc_prev
&& jump (&other
, &dir
, j_rule
)))
1699 Collect1 (e
, other
, dir
, contours
, holes
, j_rule
);
1701 while ((cur
= cur
->next
) != &a
->head
);
1706 cntr_Collect (jmp_buf * e
, PLINE
** A
, POLYAREA
** contours
, PLINE
** holes
,
1707 int action
, POLYAREA
* owner
, POLYAREA
* parent
,
1708 PLINE
* parent_contour
)
1712 if ((*A
)->Flags
.status
== ISECTED
)
1717 Collect (e
, *A
, contours
, holes
, UniteS_Rule
, UniteJ_Rule
);
1720 Collect (e
, *A
, contours
, holes
, IsectS_Rule
, IsectJ_Rule
);
1723 Collect (e
, *A
, contours
, holes
, XorS_Rule
, XorJ_Rule
);
1726 Collect (e
, *A
, contours
, holes
, SubS_Rule
, SubJ_Rule
);
1735 if ((*A
)->Flags
.status
== INSIDE
)
1738 /* disappear this contour (rtree entry removed in PutContour) */
1740 tmprev
->next
= NULL
;
1741 PutContour (e
, tmprev
, contours
, holes
, owner
, NULL
, NULL
);
1746 if ((*A
)->Flags
.status
== INSIDE
)
1749 /* disappear this contour (rtree entry removed in PutContour) */
1751 tmprev
->next
= NULL
;
1752 poly_InvContour (tmprev
);
1753 PutContour (e
, tmprev
, contours
, holes
, owner
, NULL
, NULL
);
1756 /* break; *//* Fall through (I think this is correct! pcjc2) */
1759 if ((*A
)->Flags
.status
== OUTSIDE
)
1762 /* disappear this contour (rtree entry removed in PutContour) */
1764 tmprev
->next
= NULL
;
1765 PutContour (e
, tmprev
, contours
, holes
, owner
, parent
,
1773 } /* cntr_Collect */
1776 M_B_AREA_Collect (jmp_buf * e
, POLYAREA
* bfst
, POLYAREA
** contours
,
1777 PLINE
** holes
, int action
)
1780 PLINE
**cur
, **next
, *tmp
;
1785 for (cur
= &b
->contours
; *cur
!= NULL
; cur
= next
)
1787 next
= &((*cur
)->next
);
1788 if ((*cur
)->Flags
.status
== ISECTED
)
1791 if ((*cur
)->Flags
.status
== INSIDE
)
1796 poly_InvContour (*cur
);
1802 tmp
->Flags
.status
= UNKNWN
;
1803 PutContour (e
, tmp
, contours
, holes
, b
, NULL
, NULL
);
1806 break; /* nothing to do - already included */
1808 else if ((*cur
)->Flags
.status
== OUTSIDE
)
1818 tmp
->Flags
.status
= UNKNWN
;
1819 PutContour (e
, tmp
, contours
, holes
, b
, NULL
, NULL
);
1823 break; /* do nothing, not included */
1827 while ((b
= b
->f
) != bfst
);
1832 contour_is_first (POLYAREA
* a
, PLINE
* cur
)
1834 return (a
->contours
== cur
);
1839 contour_is_last (PLINE
* cur
)
1841 return (cur
->next
== NULL
);
1846 remove_polyarea (POLYAREA
** list
, POLYAREA
* piece
)
1848 /* If this item was the start of the list, advance that pointer */
1852 /* But reset it to NULL if it wraps around and hits us again */
1856 piece
->b
->f
= piece
->f
;
1857 piece
->f
->b
= piece
->b
;
1858 piece
->f
= piece
->b
= piece
;
1862 M_POLYAREA_separate_isected (jmp_buf * e
, POLYAREA
** pieces
,
1863 PLINE
** holes
, PLINE
** isected
)
1865 POLYAREA
*a
= *pieces
;
1867 PLINE
*curc
, *next
, *prev
;
1873 /* TODO: STASH ENOUGH INFORMATION EARLIER ON, SO WE CAN REMOVE THE INTERSECTED
1874 CONTOURS WITHOUT HAVING TO WALK THE FULL DATA-STRUCTURE LOOKING FOR THEM. */
1878 int hole_contour
= 0;
1882 finished
= (anext
== *pieces
);
1885 for (curc
= a
->contours
; curc
!= NULL
; curc
= next
, is_outline
= 0)
1887 int is_first
= contour_is_first (a
, curc
);
1888 int is_last
= contour_is_last (curc
);
1889 int isect_contour
= (curc
->Flags
.status
== ISECTED
);
1893 if (isect_contour
|| hole_contour
)
1896 /* Reset the intersection flags, since we keep these pieces */
1897 if (curc
->Flags
.status
!= ISECTED
)
1898 curc
->Flags
.status
= UNKNWN
;
1900 remove_contour (a
, prev
, curc
, !(is_first
&& is_last
));
1904 /* Link into the list of intersected contours */
1905 curc
->next
= *isected
;
1908 else if (hole_contour
)
1910 /* Link into the list of holes */
1911 curc
->next
= *holes
;
1919 if (is_first
&& is_last
)
1921 remove_polyarea (pieces
, a
);
1922 poly_Free (&a
); /* NB: Sets a to NULL */
1928 /* Note the item we just didn't delete as the next
1929 candidate for having its "next" pointer adjusted.
1930 Saves walking the contour list when we delete one. */
1934 /* If we move or delete an outer contour, we need to move any holes
1935 we wish to keep within that contour to the holes list. */
1936 if (is_outline
&& isect_contour
)
1941 /* If we deleted all the pieces of the polyarea, *pieces is NULL */
1943 while ((a
= anext
), *pieces
!= NULL
&& !finished
);
1947 struct find_inside_m_pa_info
1950 POLYAREA
*want_inside
;
1955 find_inside_m_pa (const BoxType
* b
, void *cl
)
1957 struct find_inside_m_pa_info
*info
= (struct find_inside_m_pa_info
*) cl
;
1958 PLINE
*check
= (PLINE
*) b
;
1959 /* Don't report for the main contour */
1960 if (check
->Flags
.orient
== PLF_DIR
)
1962 /* Don't look at contours marked as being intersected */
1963 if (check
->Flags
.status
== ISECTED
)
1965 if (cntr_in_M_POLYAREA (check
, info
->want_inside
, FALSE
))
1967 info
->result
= check
;
1968 longjmp (info
->jb
, 1);
1975 M_POLYAREA_update_primary (jmp_buf * e
, POLYAREA
** pieces
,
1976 PLINE
** holes
, int action
, POLYAREA
* bpa
)
1978 POLYAREA
*a
= *pieces
;
1981 PLINE
*curc
, *next
, *prev
;
1983 /* int inv_inside = 0; */
1985 int del_outside
= 0;
2000 case PBO_XOR
: /* NOT IMPLEMENTED OR USED */
2001 /* inv_inside = 1; */
2006 box
= *((BoxType
*) bpa
->contours
);
2008 while ((b
= b
->f
) != bpa
)
2010 BoxType
*b_box
= (BoxType
*) b
->contours
;
2011 MAKEMIN (box
.X1
, b_box
->X1
);
2012 MAKEMIN (box
.Y1
, b_box
->Y1
);
2013 MAKEMAX (box
.X2
, b_box
->X2
);
2014 MAKEMAX (box
.Y2
, b_box
->Y2
);
2023 finished
= (anext
== *pieces
);
2025 /* Test the outer contour first, as we may need to remove all children */
2027 /* We've not yet split intersected contours out, just ignore them */
2028 if (a
->contours
->Flags
.status
!= ISECTED
&&
2029 /* Pre-filter on bounding box */
2030 ((a
->contours
->xmin
>= box
.X1
) && (a
->contours
->ymin
>= box
.Y1
)
2031 && (a
->contours
->xmax
<= box
.X2
)
2032 && (a
->contours
->ymax
<= box
.Y2
)) &&
2033 /* Then test properly */
2034 cntr_in_M_POLYAREA (a
->contours
, bpa
, FALSE
))
2037 /* Delete this contour, all children -> holes queue */
2039 /* Delete the outer contour */
2041 remove_contour (a
, NULL
, curc
, FALSE
); /* Rtree deleted in poly_Free below */
2042 /* a->contours now points to the remaining holes */
2043 poly_DelContour (&curc
);
2045 if (a
->contours
!= NULL
)
2047 /* Find the end of the list of holes */
2049 while (curc
->next
!= NULL
)
2052 /* Take the holes and prepend to the holes queue */
2053 curc
->next
= *holes
;
2054 *holes
= a
->contours
;
2058 remove_polyarea (pieces
, a
);
2059 poly_Free (&a
); /* NB: Sets a to NULL */
2064 /* Loop whilst we find INSIDE contours to delete */
2067 struct find_inside_m_pa_info info
;
2070 info
.want_inside
= bpa
;
2072 /* Set jump return */
2073 if (setjmp (info
.jb
))
2075 /* Returned here! */
2080 /* r-tree search, calling back a routine to longjmp back with
2081 * data about any hole inside the B polygon.
2082 * NB: Does not jump back to report the main contour!
2084 r_search (a
->contour_tree
, &box
, NULL
, find_inside_m_pa
,
2087 /* Nothing found? */
2091 /* We need to find the contour before it, so we can update its next pointer */
2093 while (prev
->next
!= info
.result
)
2098 /* Remove hole from the contour */
2099 remove_contour (a
, prev
, info
.result
, TRUE
);
2100 poly_DelContour (&info
.result
);
2102 /* End check for deleted holes */
2104 /* If we deleted all the pieces of the polyarea, *pieces is NULL */
2106 while ((a
= anext
), *pieces
!= NULL
&& !finished
);
2112 /* This path isn't optimised for speed */
2117 int hole_contour
= 0;
2121 finished
= (anext
== *pieces
);
2124 for (curc
= a
->contours
; curc
!= NULL
; curc
= next
, is_outline
= 0)
2126 int is_first
= contour_is_first (a
, curc
);
2127 int is_last
= contour_is_last (curc
);
2128 int del_contour
= 0;
2133 del_contour
= curc
->Flags
.status
!= ISECTED
&&
2134 !cntr_in_M_POLYAREA (curc
, bpa
, FALSE
);
2136 /* Skip intersected contours */
2137 if (curc
->Flags
.status
== ISECTED
)
2143 /* Reset the intersection flags, since we keep these pieces */
2144 curc
->Flags
.status
= UNKNWN
;
2146 if (del_contour
|| hole_contour
)
2149 remove_contour (a
, prev
, curc
, !(is_first
&& is_last
));
2153 /* Delete the contour */
2154 poly_DelContour (&curc
); /* NB: Sets curc to NULL */
2156 else if (hole_contour
)
2158 /* Link into the list of holes */
2159 curc
->next
= *holes
;
2167 if (is_first
&& is_last
)
2169 remove_polyarea (pieces
, a
);
2170 poly_Free (&a
); /* NB: Sets a to NULL */
2176 /* Note the item we just didn't delete as the next
2177 candidate for having its "next" pointer adjusted.
2178 Saves walking the contour list when we delete one. */
2182 /* If we move or delete an outer contour, we need to move any holes
2183 we wish to keep within that contour to the holes list. */
2184 if (is_outline
&& del_contour
)
2189 /* If we deleted all the pieces of the polyarea, *pieces is NULL */
2191 while ((a
= anext
), *pieces
!= NULL
&& !finished
);
2195 M_POLYAREA_Collect_separated (jmp_buf * e
, PLINE
* afst
, POLYAREA
** contours
,
2196 PLINE
** holes
, int action
, BOOLp maybe
)
2198 PLINE
**cur
, **next
;
2200 for (cur
= &afst
; *cur
!= NULL
; cur
= next
)
2202 next
= &((*cur
)->next
);
2203 /* if we disappear a contour, don't advance twice */
2204 if (cntr_Collect (e
, cur
, contours
, holes
, action
, NULL
, NULL
, NULL
))
2210 M_POLYAREA_Collect (jmp_buf * e
, POLYAREA
* afst
, POLYAREA
** contours
,
2211 PLINE
** holes
, int action
, BOOLp maybe
)
2214 POLYAREA
*parent
= NULL
; /* Quiet compiler warning */
2215 PLINE
**cur
, **next
, *parent_contour
;
2218 while ((a
= a
->f
) != afst
);
2219 /* now the non-intersect parts are collected in temp/holes */
2222 if (maybe
&& a
->contours
->Flags
.status
!= ISECTED
)
2223 parent_contour
= a
->contours
;
2225 parent_contour
= NULL
;
2227 /* Take care of the first contour - so we know if we
2228 * can shortcut reparenting some of its children
2233 next
= &((*cur
)->next
);
2234 /* if we disappear a contour, don't advance twice */
2235 if (cntr_Collect (e
, cur
, contours
, holes
, action
, a
, NULL
, NULL
))
2237 parent
= (*contours
)->b
; /* InsCntr inserts behind the head */
2244 for (; *cur
!= NULL
; cur
= next
)
2246 next
= &((*cur
)->next
);
2247 /* if we disappear a contour, don't advance twice */
2248 if (cntr_Collect (e
, cur
, contours
, holes
, action
, a
, parent
,
2253 while ((a
= a
->f
) != afst
);
2256 /* determine if two polygons touch or overlap */
2258 Touching (POLYAREA
* a
, POLYAREA
* b
)
2263 if ((code
= setjmp (e
)) == 0)
2266 if (!poly_Valid (a
))
2268 if (!poly_Valid (b
))
2271 M_POLYAREA_intersect (&e
, a
, b
, false);
2273 if (M_POLYAREA_label (a
, b
, TRUE
))
2275 if (M_POLYAREA_label (b
, a
, TRUE
))
2278 else if (code
== TOUCHES
)
2283 /* the main clipping routines */
2285 poly_Boolean (const POLYAREA
* a_org
, const POLYAREA
* b_org
,
2286 POLYAREA
** res
, int action
)
2288 POLYAREA
*a
= NULL
, *b
= NULL
;
2290 if (!poly_M_Copy0 (&a
, a_org
) || !poly_M_Copy0 (&b
, b_org
))
2291 return err_no_memory
;
2293 return poly_Boolean_free (a
, b
, res
, action
);
2294 } /* poly_Boolean */
2296 /* just like poly_Boolean but frees the input polys */
2298 poly_Boolean_free (POLYAREA
* ai
, POLYAREA
* bi
, POLYAREA
** res
, int action
)
2300 POLYAREA
*a
= ai
, *b
= bi
;
2301 PLINE
*a_isected
= NULL
;
2302 PLINE
*p
, *holes
= NULL
;
2339 if ((code
= setjmp (e
)) == 0)
2342 assert (poly_Valid (a
));
2343 assert (poly_Valid (b
));
2346 /* intersect needs to make a list of the contours in a and b which are intersected */
2347 M_POLYAREA_intersect (&e
, a
, b
, TRUE
);
2349 /* We could speed things up a lot here if we only processed the relevant contours */
2350 /* NB: Relevant parts of a are labeled below */
2351 M_POLYAREA_label (b
, a
, FALSE
);
2354 M_POLYAREA_update_primary (&e
, res
, &holes
, action
, b
);
2355 M_POLYAREA_separate_isected (&e
, res
, &holes
, &a_isected
);
2356 M_POLYAREA_label_separated (a_isected
, b
, FALSE
);
2357 M_POLYAREA_Collect_separated (&e
, a_isected
, res
, &holes
, action
,
2359 M_B_AREA_Collect (&e
, b
, res
, &holes
, action
);
2362 /* free a_isected */
2363 while ((p
= a_isected
) != NULL
)
2365 a_isected
= p
->next
;
2366 poly_DelContour (&p
);
2369 InsertHoles (&e
, *res
, &holes
);
2371 /* delete holes if any left */
2372 while ((p
= holes
) != NULL
)
2375 poly_DelContour (&p
);
2383 assert (!*res
|| poly_Valid (*res
));
2385 } /* poly_Boolean_free */
2388 clear_marks (POLYAREA
* p
)
2396 for (c
= n
->contours
; c
; c
= c
->next
)
2403 while ((v
= v
->next
) != &c
->head
);
2406 while ((n
= n
->f
) != p
);
2409 /* compute the intersection and subtraction (divides "a" into two pieces)
2410 * and frees the input polys. This assumes that bi is a single simple polygon.
2413 poly_AndSubtract_free (POLYAREA
* ai
, POLYAREA
* bi
,
2414 POLYAREA
** aandb
, POLYAREA
** aminusb
)
2416 POLYAREA
*a
= ai
, *b
= bi
;
2417 PLINE
*p
, *holes
= NULL
;
2424 if ((code
= setjmp (e
)) == 0)
2428 if (!poly_Valid (a
))
2430 if (!poly_Valid (b
))
2433 M_POLYAREA_intersect (&e
, a
, b
, TRUE
);
2435 M_POLYAREA_label (a
, b
, FALSE
);
2436 M_POLYAREA_label (b
, a
, FALSE
);
2438 M_POLYAREA_Collect (&e
, a
, aandb
, &holes
, PBO_ISECT
, FALSE
);
2439 InsertHoles (&e
, *aandb
, &holes
);
2440 assert (poly_Valid (*aandb
));
2441 /* delete holes if any left */
2442 while ((p
= holes
) != NULL
)
2445 poly_DelContour (&p
);
2450 M_POLYAREA_Collect (&e
, a
, aminusb
, &holes
, PBO_SUB
, FALSE
);
2451 InsertHoles (&e
, *aminusb
, &holes
);
2454 assert (poly_Valid (*aminusb
));
2456 /* delete holes if any left */
2457 while ((p
= holes
) != NULL
)
2460 poly_DelContour (&p
);
2467 poly_Free (aminusb
);
2470 assert (!*aandb
|| poly_Valid (*aandb
));
2471 assert (!*aminusb
|| poly_Valid (*aminusb
));
2473 } /* poly_AndSubtract_free */
2476 cntrbox_pointin (PLINE
* c
, Vector p
)
2478 return (p
[0] >= c
->xmin
&& p
[1] >= c
->ymin
&&
2479 p
[0] <= c
->xmax
&& p
[1] <= c
->ymax
);
2484 node_neighbours (VNODE
* a
, VNODE
* b
)
2486 return (a
== b
) || (a
->next
== b
) || (b
->next
== a
) || (a
->next
== b
->next
);
2490 poly_CreateNode (Vector v
)
2496 res
= (VNODE
*) calloc (1, sizeof (VNODE
));
2499 // bzero (res, sizeof (VNODE) - sizeof(Vector));
2507 poly_IniContour (PLINE
* c
)
2511 /* bzero (c, sizeof(PLINE)); */
2512 c
->head
.next
= c
->head
.prev
= &c
->head
;
2513 c
->xmin
= c
->ymin
= 0x7fffffff;
2514 c
->xmax
= c
->ymax
= 0x80000000;
2515 c
->is_round
= FALSE
;
2522 poly_NewContour (Vector v
)
2526 res
= (PLINE
*) calloc (1, sizeof (PLINE
));
2530 poly_IniContour (res
);
2534 Vcopy (res
->head
.point
, v
);
2535 cntrbox_adjust (res
, v
);
2542 poly_ClrContour (PLINE
* c
)
2547 while ((cur
= c
->head
.next
) != &c
->head
)
2549 poly_ExclVertex (cur
);
2552 poly_IniContour (c
);
2556 poly_DelContour (PLINE
** c
)
2562 for (cur
= (*c
)->head
.prev
; cur
!= &(*c
)->head
; cur
= prev
)
2565 if (cur
->cvc_next
!= NULL
)
2567 free (cur
->cvc_next
);
2568 free (cur
->cvc_prev
);
2572 if ((*c
)->head
.cvc_next
!= NULL
)
2574 free ((*c
)->head
.cvc_next
);
2575 free ((*c
)->head
.cvc_prev
);
2577 /* FIXME -- strict aliasing violation. */
2580 rtree_t
*r
= (*c
)->tree
;
2581 r_destroy_tree (&r
);
2583 free (*c
), *c
= NULL
;
2587 poly_PreContour (PLINE
* C
, BOOLp optimize
)
2597 for (c
= (p
= &C
->head
)->next
; c
!= &C
->head
; c
= (p
= c
)->next
)
2599 /* if the previous node is on the same line with this one, we should remove it */
2600 Vsub2 (p1
, c
->point
, p
->point
);
2601 Vsub2 (p2
, c
->next
->point
, c
->point
);
2602 /* If the product below is zero then
2603 * the points on either side of c
2604 * are on the same line!
2605 * So, remove the point c
2608 if (vect_det2 (p1
, p2
) == 0)
2610 poly_ExclVertex (c
);
2617 C
->xmin
= C
->xmax
= C
->head
.point
[0];
2618 C
->ymin
= C
->ymax
= C
->head
.point
[1];
2620 p
= (c
= &C
->head
)->prev
;
2625 /* calculate area for orientation */
2627 (double) (p
->point
[0] - c
->point
[0]) * (p
->point
[1] +
2629 cntrbox_adjust (C
, c
->point
);
2632 while ((c
= (p
= c
)->next
) != &C
->head
);
2634 C
->area
= ABS (area
);
2636 C
->Flags
.orient
= ((area
< 0) ? PLF_INV
: PLF_DIR
);
2637 C
->tree
= (rtree_t
*)make_edge_tree (C
);
2638 } /* poly_PreContour */
2641 flip_cb (const BoxType
* b
, void *cl
)
2643 struct seg
*s
= (struct seg
*) b
;
2649 poly_InvContour (PLINE
* c
)
2661 cur
->next
= cur
->prev
;
2663 /* fix the segment tree */
2665 while ((cur
= next
) != &c
->head
);
2666 c
->Flags
.orient
^= 1;
2672 r_search (c
->tree
, NULL
, NULL
, flip_cb
, NULL
);
2674 assert (r
== c
->Count
);
2680 poly_ExclVertex (VNODE
* node
)
2682 assert (node
!= NULL
);
2685 free (node
->cvc_next
);
2686 free (node
->cvc_prev
);
2688 node
->prev
->next
= node
->next
;
2689 node
->next
->prev
= node
->prev
;
2693 poly_InclVertex (VNODE
* after
, VNODE
* node
)
2696 assert (after
!= NULL
);
2697 assert (node
!= NULL
);
2700 node
->next
= after
->next
;
2701 after
->next
= after
->next
->prev
= node
;
2702 /* remove points on same line */
2703 if (node
->prev
->prev
== node
)
2704 return; /* we don't have 3 points in the poly yet */
2705 a
= (node
->point
[1] - node
->prev
->prev
->point
[1]);
2706 a
*= (node
->prev
->point
[0] - node
->prev
->prev
->point
[0]);
2707 b
= (node
->point
[0] - node
->prev
->prev
->point
[0]);
2708 b
*= (node
->prev
->point
[1] - node
->prev
->prev
->point
[1]);
2709 if (fabs (a
- b
) < EPSILON
)
2711 VNODE
*t
= node
->prev
;
2712 t
->prev
->next
= node
;
2713 node
->prev
= t
->prev
;
2719 poly_CopyContour (PLINE
** dst
, PLINE
* src
)
2721 VNODE
*cur
, *newnode
;
2723 assert (src
!= NULL
);
2725 *dst
= poly_NewContour (src
->head
.point
);
2729 (*dst
)->Count
= src
->Count
;
2730 (*dst
)->Flags
.orient
= src
->Flags
.orient
;
2731 (*dst
)->xmin
= src
->xmin
, (*dst
)->xmax
= src
->xmax
;
2732 (*dst
)->ymin
= src
->ymin
, (*dst
)->ymax
= src
->ymax
;
2733 (*dst
)->area
= src
->area
;
2735 for (cur
= src
->head
.next
; cur
!= &src
->head
; cur
= cur
->next
)
2737 if ((newnode
= poly_CreateNode (cur
->point
)) == NULL
)
2739 // newnode->Flags = cur->Flags;
2740 poly_InclVertex ((*dst
)->head
.prev
, newnode
);
2742 (*dst
)->tree
= (rtree_t
*)make_edge_tree (*dst
);
2746 /**********************************************************************/
2747 /* polygon routines */
2750 poly_Copy0 (POLYAREA
** dst
, const POLYAREA
* src
)
2754 *dst
= (POLYAREA
*)calloc (1, sizeof (POLYAREA
));
2757 (*dst
)->contour_tree
= r_create_tree (NULL
, 0, 0);
2759 return poly_Copy1 (*dst
, src
);
2763 poly_Copy1 (POLYAREA
* dst
, const POLYAREA
* src
)
2765 PLINE
*cur
, **last
= &dst
->contours
;
2768 dst
->f
= dst
->b
= dst
;
2770 for (cur
= src
->contours
; cur
!= NULL
; cur
= cur
->next
)
2772 if (!poly_CopyContour (last
, cur
))
2774 r_insert_entry (dst
->contour_tree
, (BoxType
*) * last
, 0);
2775 last
= &(*last
)->next
;
2781 poly_M_Incl (POLYAREA
** list
, POLYAREA
* a
)
2784 a
->f
= a
->b
= a
, *list
= a
;
2789 (*list
)->b
= (*list
)->b
->f
= a
;
2794 poly_M_Copy0 (POLYAREA
** dst
, const POLYAREA
* srcfst
)
2796 const POLYAREA
*src
= srcfst
;
2804 if ((di
= poly_Create ()) == NULL
|| !poly_Copy1 (di
, src
))
2806 poly_M_Incl (dst
, di
);
2808 while ((src
= src
->f
) != srcfst
);
2813 poly_InclContour (POLYAREA
* p
, PLINE
* c
)
2817 if ((c
== NULL
) || (p
== NULL
))
2819 if (c
->Flags
.orient
== PLF_DIR
)
2821 if (p
->contours
!= NULL
)
2827 if (p
->contours
== NULL
)
2829 /* link at front of hole list */
2830 tmp
= p
->contours
->next
;
2831 p
->contours
->next
= c
;
2834 r_insert_entry (p
->contour_tree
, (BoxType
*) c
, 0);
2847 crossing (const BoxType
* b
, void *cl
)
2849 struct seg
*s
= (struct seg
*) b
;
2850 struct pip
*p
= (struct pip
*) cl
;
2852 if (s
->v
->point
[1] <= p
->p
[1])
2854 if (s
->v
->next
->point
[1] > p
->p
[1])
2858 Vsub2 (v1
, s
->v
->next
->point
, s
->v
->point
);
2859 Vsub2 (v2
, p
->p
, s
->v
->point
);
2860 cross
= (long long) v1
[0] * v2
[1] - (long long) v2
[0] * v1
[1];
2864 longjmp (p
->env
, 1);
2872 if (s
->v
->next
->point
[1] <= p
->p
[1])
2876 Vsub2 (v1
, s
->v
->next
->point
, s
->v
->point
);
2877 Vsub2 (v2
, p
->p
, s
->v
->point
);
2878 cross
= (long long) v1
[0] * v2
[1] - (long long) v2
[0] * v1
[1];
2882 longjmp (p
->env
, 1);
2892 poly_InsideContour (PLINE
* c
, Vector p
)
2897 if (!cntrbox_pointin (c
, p
))
2900 info
.p
[0] = ray
.X1
= p
[0];
2901 info
.p
[1] = ray
.Y1
= p
[1];
2902 ray
.X2
= 0x7fffffff;
2904 if (setjmp (info
.env
) == 0)
2905 r_search (c
->tree
, &ray
, NULL
, crossing
, &info
);
2910 poly_CheckInside (POLYAREA
* p
, Vector v0
)
2914 if ((p
== NULL
) || (v0
== NULL
) || (p
->contours
== NULL
))
2917 if (poly_InsideContour (cur
, v0
))
2919 for (cur
= cur
->next
; cur
!= NULL
; cur
= cur
->next
)
2920 if (poly_InsideContour (cur
, v0
))
2928 poly_M_CheckInside (POLYAREA
* p
, Vector v0
)
2932 if ((p
== NULL
) || (v0
== NULL
))
2937 if (poly_CheckInside (cur
, v0
))
2940 while ((cur
= cur
->f
) != p
);
2945 dot (Vector A
, Vector B
)
2947 return (double)A
[0] * (double)B
[0] + (double)A
[1] * (double)B
[1];
2950 /* Compute whether point is inside a triangle formed by 3 other pints */
2951 /* Algorithm from http://www.blackpawn.com/texts/pointinpoly/default.html */
2953 point_in_triangle (Vector A
, Vector B
, Vector C
, Vector P
)
2956 double dot00
, dot01
, dot02
, dot11
, dot12
;
2960 /* Compute vectors */
2961 v0
[0] = C
[0] - A
[0]; v0
[1] = C
[1] - A
[1];
2962 v1
[0] = B
[0] - A
[0]; v1
[1] = B
[1] - A
[1];
2963 v2
[0] = P
[0] - A
[0]; v2
[1] = P
[1] - A
[1];
2965 /* Compute dot products */
2966 dot00
= dot (v0
, v0
);
2967 dot01
= dot (v0
, v1
);
2968 dot02
= dot (v0
, v2
);
2969 dot11
= dot (v1
, v1
);
2970 dot12
= dot (v1
, v2
);
2972 /* Compute barycentric coordinates */
2973 invDenom
= 1. / (dot00
* dot11
- dot01
* dot01
);
2974 u
= (dot11
* dot02
- dot01
* dot12
) * invDenom
;
2975 v
= (dot00
* dot12
- dot01
* dot02
) * invDenom
;
2977 /* Check if point is in triangle */
2978 return (u
> 0.0) && (v
> 0.0) && (u
+ v
< 1.0);
2982 /* Returns the dot product of Vector A->B, and a vector
2983 * orthogonal to Vector C->D. The result is not normalisd, so will be
2984 * weighted by the magnitude of the C->D vector.
2987 dot_orthogonal_to_direction (Vector A
, Vector B
, Vector C
, Vector D
)
2990 l1
[0] = B
[0] - A
[0]; l1
[1] = B
[1] - A
[1];
2991 l2
[0] = D
[0] - C
[0]; l2
[1] = D
[1] - C
[1];
2996 return dot (l1
, l3
);
2999 /* Algorithm from http://www.exaflop.org/docs/cgafaq/cga2.html
3001 * "Given a simple polygon, find some point inside it. Here is a method based
3002 * on the proof that there exists an internal diagonal, in [O'Rourke, 13-14].
3003 * The idea is that the midpoint of a diagonal is interior to the polygon.
3005 * 1. Identify a convex vertex v; let its adjacent vertices be a and b.
3006 * 2. For each other vertex q do:
3007 * 2a. If q is inside avb, compute distance to v (orthogonal to ab).
3008 * 2b. Save point q if distance is a new min.
3009 * 3. If no point is inside, return midpoint of ab, or centroid of avb.
3010 * 4. Else if some point inside, qv is internal: return its midpoint."
3012 * [O'Rourke]: Computational Geometry in C (2nd Ed.)
3013 * Joseph O'Rourke, Cambridge University Press 1998,
3014 * ISBN 0-521-64010-5 Pbk, ISBN 0-521-64976-5 Hbk
3017 poly_ComputeInteriorPoint (PLINE
*poly
, Vector v
)
3019 VNODE
*pt1
, *pt2
, *pt3
, *q
;
3020 VNODE
*min_q
= NULL
;
3022 double min_dist
= 0.0;
3023 double dir
= (poly
->Flags
.orient
== PLF_DIR
) ? 1. : -1;
3025 /* Find a convex node on the polygon */
3034 dot_product
= dot_orthogonal_to_direction (pt1
->point
, pt2
->point
,
3035 pt3
->point
, pt2
->point
);
3037 if (dot_product
* dir
> 0.)
3040 while ((pt1
= pt1
->next
) != &poly
->head
);
3042 /* Loop over remaining vertices */
3046 /* Is current vertex "q" outside pt1 pt2 pt3 triangle? */
3047 if (!point_in_triangle (pt1
->point
, pt2
->point
, pt3
->point
, q
->point
))
3050 /* NO: compute distance to pt2 (v) orthogonal to pt1 - pt3) */
3051 /* Record minimum */
3052 dist
= dot_orthogonal_to_direction (q
->point
, pt2
->point
, pt1
->point
, pt3
->point
);
3053 if (min_q
== NULL
|| dist
< min_dist
) {
3058 while ((q
= q
->next
) != pt2
);
3060 /* Were any "q" found inside pt1 pt2 pt3? */
3061 if (min_q
== NULL
) {
3062 /* NOT FOUND: Return midpoint of pt1 pt3 */
3063 v
[0] = (pt1
->point
[0] + pt3
->point
[0]) / 2;
3064 v
[1] = (pt1
->point
[1] + pt3
->point
[1]) / 2;
3066 /* FOUND: Return mid point of min_q, pt2 */
3067 v
[0] = (pt2
->point
[0] + min_q
->point
[0]) / 2;
3068 v
[1] = (pt2
->point
[1] + min_q
->point
[1]) / 2;
3073 /* NB: This function assumes the caller _knows_ the contours do not
3074 * intersect. If the contours intersect, the result is undefined.
3075 * It will return the correct result if the two contours share
3076 * common points beteween their contours. (Identical contours
3077 * are treated as being inside each other).
3080 poly_ContourInContour (PLINE
* poly
, PLINE
* inner
)
3083 assert (poly
!= NULL
);
3084 assert (inner
!= NULL
);
3085 if (cntrbox_inside (inner
, poly
))
3087 /* We need to prove the "inner" contour is not outside
3088 * "poly" contour. If it is outside, we can return.
3090 if (!poly_InsideContour (poly
, inner
->head
.point
))
3093 poly_ComputeInteriorPoint (inner
, point
);
3094 return poly_InsideContour (poly
, point
);
3100 poly_Init (POLYAREA
* p
)
3104 p
->contour_tree
= r_create_tree (NULL
, 0, 0);
3112 if ((res
= (POLYAREA
*)malloc (sizeof (POLYAREA
))) != NULL
)
3118 poly_FreeContours (PLINE
** pline
)
3122 while ((pl
= *pline
) != NULL
)
3125 poly_DelContour (&pl
);
3130 poly_Free (POLYAREA
** p
)
3136 for (cur
= (*p
)->f
; cur
!= *p
; cur
= (*p
)->f
)
3138 poly_FreeContours (&cur
->contours
);
3139 r_destroy_tree (&cur
->contour_tree
);
3144 poly_FreeContours (&cur
->contours
);
3145 r_destroy_tree (&cur
->contour_tree
);
3146 free (*p
), *p
= NULL
;
3150 inside_sector (VNODE
* pn
, Vector p2
)
3152 Vector cdir
, ndir
, pdir
;
3155 assert (pn
!= NULL
);
3156 vect_sub (cdir
, p2
, pn
->point
);
3157 vect_sub (pdir
, pn
->point
, pn
->prev
->point
);
3158 vect_sub (ndir
, pn
->next
->point
, pn
->point
);
3160 p_c
= vect_det2 (pdir
, cdir
) >= 0;
3161 n_c
= vect_det2 (ndir
, cdir
) >= 0;
3162 p_n
= vect_det2 (pdir
, ndir
) >= 0;
3164 if ((p_n
&& p_c
&& n_c
) || ((!p_n
) && (p_c
|| n_c
)))
3168 } /* inside_sector */
3170 /* returns TRUE if bad contour */
3172 poly_ChkContour (PLINE
* a
)
3174 VNODE
*a1
, *a2
, *hit1
, *hit2
;
3185 if (!node_neighbours (a1
, a2
) &&
3186 (icnt
= vect_inters2 (a1
->point
, a1
->next
->point
,
3187 a2
->point
, a2
->next
->point
, i1
, i2
)) > 0)
3192 if (vect_dist2 (i1
, a1
->point
) < EPSILON
)
3194 else if (vect_dist2 (i1
, a1
->next
->point
) < EPSILON
)
3199 if (vect_dist2 (i1
, a2
->point
) < EPSILON
)
3201 else if (vect_dist2 (i1
, a2
->next
->point
) < EPSILON
)
3206 if ((hit1
== NULL
) && (hit2
== NULL
))
3208 /* If the intersection didn't land on an end-point of either
3209 * line, we know the lines cross and we return TRUE.
3213 else if (hit1
== NULL
)
3215 /* An end-point of the second line touched somewhere along the
3216 length of the first line. Check where the second line leads. */
3217 if (inside_sector (hit2
, a1
->point
) !=
3218 inside_sector (hit2
, a1
->next
->point
))
3221 else if (hit2
== NULL
)
3223 /* An end-point of the first line touched somewhere along the
3224 length of the second line. Check where the first line leads. */
3225 if (inside_sector (hit1
, a2
->point
) !=
3226 inside_sector (hit1
, a2
->next
->point
))
3231 /* Both lines intersect at an end-point. Check where they lead. */
3232 if (inside_sector (hit1
, hit2
->prev
->point
) !=
3233 inside_sector (hit1
, hit2
->next
->point
))
3238 while ((a2
= a2
->next
) != &a
->head
);
3240 while ((a1
= a1
->next
) != &a
->head
);
3246 poly_Valid (POLYAREA
* p
)
3250 if ((p
== NULL
) || (p
->contours
== NULL
))
3253 if (p
->contours
->Flags
.orient
== PLF_INV
|| poly_ChkContour (p
->contours
))
3257 DEBUGP ("Invalid Outer PLINE\n");
3258 if (p
->contours
->Flags
.orient
== PLF_INV
)
3259 DEBUGP ("failed orient\n");
3260 if (poly_ChkContour (p
->contours
))
3261 DEBUGP ("failed self-intersection\n");
3262 v
= &p
->contours
->head
;
3266 pcb_fprintf (stderr
, "Line [%#mS %#mS %#mS %#mS 100 100 \"\"]\n",
3267 v
->point
[0], v
->point
[1], n
->point
[0], n
->point
[1]);
3269 while ((v
= v
->next
) != &p
->contours
->head
);
3273 for (c
= p
->contours
->next
; c
!= NULL
; c
= c
->next
)
3275 if (c
->Flags
.orient
== PLF_DIR
||
3276 poly_ChkContour (c
) || !poly_ContourInContour (p
->contours
, c
))
3280 DEBUGP ("Invalid Inner PLINE orient = %d\n", c
->Flags
.orient
);
3281 if (c
->Flags
.orient
== PLF_DIR
)
3282 DEBUGP ("failed orient\n");
3283 if (poly_ChkContour (c
))
3284 DEBUGP ("failed self-intersection\n");
3285 if (!poly_ContourInContour (p
->contours
, c
))
3286 DEBUGP ("failed containment\n");
3291 pcb_fprintf (stderr
, "Line [%#mS %#mS %#mS %#mS 100 100 \"\"]\n",
3292 v
->point
[0], v
->point
[1], n
->point
[0], n
->point
[1]);
3294 while ((v
= v
->next
) != &c
->head
);
3303 Vector vect_zero
= { (long) 0, (long) 0 };
3305 /*********************************************************************/
3306 /* L o n g V e c t o r S t u f f */
3307 /*********************************************************************/
3310 vect_init (Vector v
, double x
, double y
)
3316 #define Vzero(a) ((a)[0] == 0. && (a)[1] == 0.)
3318 #define Vsub(a,b,c) {(a)[0]=(b)[0]-(c)[0];(a)[1]=(b)[1]-(c)[1];}
3321 vect_equal (Vector v1
, Vector v2
)
3323 return (v1
[0] == v2
[0] && v1
[1] == v2
[1]);
3328 vect_sub (Vector res
, Vector v1
, Vector v2
)
3330 Vsub (res
, v1
, v2
)} /* vect_sub */
3333 vect_min (Vector v1
, Vector v2
, Vector v3
)
3335 v1
[0] = (v2
[0] < v3
[0]) ? v2
[0] : v3
[0];
3336 v1
[1] = (v2
[1] < v3
[1]) ? v2
[1] : v3
[1];
3340 vect_max (Vector v1
, Vector v2
, Vector v3
)
3342 v1
[0] = (v2
[0] > v3
[0]) ? v2
[0] : v3
[0];
3343 v1
[1] = (v2
[1] > v3
[1]) ? v2
[1] : v3
[1];
3347 vect_len2 (Vector v
)
3349 return ((double) v
[0] * v
[0] + (double) v
[1] * v
[1]); /* why sqrt? only used for compares */
3353 vect_dist2 (Vector v1
, Vector v2
)
3355 double dx
= v1
[0] - v2
[0];
3356 double dy
= v1
[1] - v2
[1];
3358 return (dx
* dx
+ dy
* dy
); /* why sqrt */
3361 /* value has sign of angle between vectors */
3363 vect_det2 (Vector v1
, Vector v2
)
3365 return (((double) v1
[0] * v2
[1]) - ((double) v2
[0] * v1
[1]));
3369 vect_m_dist (Vector v1
, Vector v2
)
3371 double dx
= v1
[0] - v2
[0];
3372 double dy
= v1
[1] - v2
[1];
3373 double dd
= (dx
* dx
+ dy
* dy
); /* sqrt */
3386 (C) 1993 Klamer Schutte
3387 (C) 1997 Michael Leonov, Alexey Nikitin
3391 vect_inters2 (Vector p1
, Vector p2
, Vector q1
, Vector q2
,
3392 Vector S1
, Vector S2
)
3395 double rpx
, rpy
, rqx
, rqy
;
3397 if (max (p1
[0], p2
[0]) < min (q1
[0], q2
[0]) ||
3398 max (q1
[0], q2
[0]) < min (p1
[0], p2
[0]) ||
3399 max (p1
[1], p2
[1]) < min (q1
[1], q2
[1]) ||
3400 max (q1
[1], q2
[1]) < min (p1
[1], p2
[1]))
3403 rpx
= p2
[0] - p1
[0];
3404 rpy
= p2
[1] - p1
[1];
3405 rqx
= q2
[0] - q1
[0];
3406 rqy
= q2
[1] - q1
[1];
3408 deel
= rpy
* rqx
- rpx
* rqy
; /* -vect_det(rp,rq); */
3410 /* coordinates are 30-bit integers so deel will be exactly zero
3411 * if the lines are parallel
3414 if (deel
== 0) /* parallel */
3416 double dc1
, dc2
, d1
, d2
, h
; /* Check to see whether p1-p2 and q1-q2 are on the same line */
3417 Vector hp1
, hq1
, hp2
, hq2
, q1p1
, q1q2
;
3419 Vsub2 (q1p1
, q1
, p1
);
3420 Vsub2 (q1q2
, q1
, q2
);
3423 /* If this product is not zero then p1-p2 and q1-q2 are not on same line! */
3424 if (vect_det2 (q1p1
, q1q2
) != 0)
3426 dc1
= 0; /* m_len(p1 - p1) */
3428 dc2
= vect_m_dist (p1
, p2
);
3429 d1
= vect_m_dist (p1
, q1
);
3430 d2
= vect_m_dist (p1
, q2
);
3432 /* Sorting the independent points from small to large */
3438 { /* hv and h are used as help-variable. */
3440 h
= dc1
, dc1
= dc2
, dc2
= h
;
3445 h
= d1
, d1
= d2
, d2
= h
;
3448 /* Now the line-pieces are compared */
3480 return (Vequ2 (S1
, S2
) ? 1 : 2);
3483 { /* not parallel */
3485 * We have the lines:
3486 * l1: p1 + s(p2 - p1)
3487 * l2: q1 + t(q2 - q1)
3488 * And we want to know the intersection point.
3490 * p1 + s(p2-p1) = q1 + t(q2-q1)
3491 * which is similar to the two equations:
3492 * p1x + s * rpx = q1x + t * rqx
3493 * p1y + s * rpy = q1y + t * rqy
3494 * Multiplying these by rpy resp. rpx gives:
3495 * rpy * p1x + s * rpx * rpy = rpy * q1x + t * rpy * rqx
3496 * rpx * p1y + s * rpx * rpy = rpx * q1y + t * rpx * rqy
3497 * Subtracting these gives:
3498 * rpy * p1x - rpx * p1y = rpy * q1x - rpx * q1y + t * ( rpy * rqx - rpx * rqy )
3499 * So t can be isolated:
3500 * t = (rpy * ( p1x - q1x ) + rpx * ( - p1y + q1y )) / ( rpy * rqx - rpx * rqy )
3501 * and s can be found similarly
3502 * s = (rqy * (q1x - p1x) + rqx * (p1y - q1y))/( rqy * rpx - rqx * rpy)
3505 if (Vequ2 (q1
, p1
) || Vequ2 (q1
, p2
))
3510 else if (Vequ2 (q2
, p1
) || Vequ2 (q2
, p2
))
3517 s
= (rqy
* (p1
[0] - q1
[0]) + rqx
* (q1
[1] - p1
[1])) / deel
;
3518 if (s
< 0 || s
> 1.)
3520 t
= (rpy
* (p1
[0] - q1
[0]) + rpx
* (q1
[1] - p1
[1])) / deel
;
3521 if (t
< 0 || t
> 1.)
3524 S1
[0] = q1
[0] + ROUND (t
* rqx
);
3525 S1
[1] = q1
[1] + ROUND (t
* rqy
);
3529 } /* vect_inters2 */
3531 /* how about expanding polygons so that edges can be arcs rather than
3532 * lines. Consider using the third coordinate to store the radius of the
3533 * arc. The arc would pass through the vertex points. Positive radius
3534 * would indicate the arc bows left (center on right of P1-P2 path)
3535 * negative radius would put the center on the other side. 0 radius
3536 * would mean the edge is a line instead of arc
3537 * The intersections of the two circles centered at the vertex points
3538 * would determine the two possible arc centers. If P2.x > P1.x then
3539 * the center with smaller Y is selected for positive r. If P2.y > P1.y
3540 * then the center with greate X is selected for positive r.
3542 * the vec_inters2() routine would then need to handle line-line
3543 * line-arc and arc-arc intersections.
3545 * perhaps reverse tracing the arc would require look-ahead to check