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 #define ABS(x) ((x) < 0 ? -(x) : (x))
61 /*********************************************************************/
62 /* L o n g V e c t o r S t u f f */
63 /*********************************************************************/
65 #define Vcopy(a,b) {(a)[0]=(b)[0];(a)[1]=(b)[1];}
66 int vect_equal (Vector v1
, Vector v2
);
67 void vect_init (Vector v
, double x
, double y
);
68 void vect_sub (Vector res
, Vector v2
, Vector v3
);
70 void vect_min (Vector res
, Vector v2
, Vector v3
);
71 void vect_max (Vector res
, Vector v2
, Vector v3
);
73 double vect_dist2 (Vector v1
, Vector v2
);
74 double vect_det2 (Vector v1
, Vector v2
);
75 double vect_len2 (Vector v1
);
77 int vect_inters2 (Vector A
, Vector B
, Vector C
, Vector D
, Vector S1
,
80 /* note that a vertex v's Flags.status represents the edge defined by
81 * v to v->next (i.e. the edge is forward of v)
92 #define NODE_LABEL(n) ((n)->Flags.status)
93 #define LABEL_NODE(n,l) ((n)->Flags.status = (l))
95 #define error(code) longjmp(*(e), code)
97 #define MemGet(ptr, type) \
98 if (UNLIKELY (((ptr) = malloc(sizeof(type))) == NULL)) \
102 #undef DEBUG_ALL_LABELS
108 #define DEBUGP(...) fprintf(stderr, ## __VA_ARGS__)
113 /* ///////////////////////////////////////////////////////////////////////////// * /
114 / * 2-Dimentional stuff
115 / * ///////////////////////////////////////////////////////////////////////////// */
117 #define Vsub2(r,a,b) {(r)[0] = (a)[0] - (b)[0]; (r)[1] = (a)[1] - (b)[1];}
118 #define Vadd2(r,a,b) {(r)[0] = (a)[0] + (b)[0]; (r)[1] = (a)[1] + (b)[1];}
119 #define Vsca2(r,a,s) {(r)[0] = (a)[0] * (s); (r)[1] = (a)[1] * (s);}
120 #define Vcpy2(r,a) {(r)[0] = (a)[0]; (r)[1] = (a)[1];}
121 #define Vequ2(a,b) ((a)[0] == (b)[0] && (a)[1] == (b)[1])
122 #define Vadds(r,a,b,s) {(r)[0] = ((a)[0] + (b)[0]) * (s); (r)[1] = ((a)[1] + (b)[1]) * (s);}
123 #define Vswp2(a,b) { long t; \
124 t = (a)[0], (a)[0] = (b)[0], (b)[0] = t; \
125 t = (a)[1], (a)[1] = (b)[1], (b)[1] = t; \
129 static char *theState (VNODE
* v
);
132 pline_dump (VNODE
* v
)
140 fprintf (stderr
, "Line [%d %d %d %d 10 10 \"%s\"]\n",
141 v
->point
[0], v
->point
[1],
142 n
->point
[0], n
->point
[1], theState (v
));
144 while ((v
= v
->next
) != s
);
148 poly_dump (POLYAREA
* p
)
158 pline_dump (&pl
->head
);
159 fprintf (stderr
, "NEXT PLINE\n");
161 while ((pl
= pl
->next
) != NULL
);
162 fprintf (stderr
, "NEXT POLY\n");
164 while ((p
= p
->f
) != f
);
168 /***************************************************************/
169 /* routines for processing intersections */
173 (C) 1993 Klamer Schutte
174 (C) 1997 Alexey Nikitin, Michael Leonov
177 returns a bit field in new_point that indicates where the
179 1 means a new node was created and inserted
180 4 means the intersection was not on the dest point
183 node_add_single (VNODE
* dest
, Vector po
)
187 if (vect_equal (po
, dest
->point
))
189 if (vect_equal (po
, dest
->next
->point
))
191 p
= poly_CreateNode (po
);
194 p
->cvc_prev
= p
->cvc_next
= NULL
;
195 p
->Flags
.status
= UNKNWN
;
199 #define ISECT_BAD_PARAM (-1)
200 #define ISECT_NO_MEMORY (-2)
208 new_descriptor (VNODE
* a
, char poly
, char side
)
210 CVCList
*l
= (CVCList
*) malloc (sizeof (CVCList
));
212 register double ang
, dx
, dy
;
220 l
->next
= l
->prev
= l
;
221 if (side
== 'P') /* previous */
222 vect_sub (v
, a
->prev
->point
, a
->point
);
224 vect_sub (v
, a
->next
->point
, a
->point
);
225 /* Uses slope/(slope+1) in quadrant 1 as a proxy for the angle.
226 * It still has the same monotonic sort result
227 * and is far less expensive to compute than the real angle.
229 if (vect_equal (v
, vect_zero
))
233 if (a
->prev
->cvc_prev
== (CVCList
*) - 1)
234 a
->prev
->cvc_prev
= a
->prev
->cvc_next
= NULL
;
235 poly_ExclVertex (a
->prev
);
236 vect_sub (v
, a
->prev
->point
, a
->point
);
240 if (a
->next
->cvc_prev
== (CVCList
*) - 1)
241 a
->next
->cvc_prev
= a
->next
->cvc_next
= NULL
;
242 poly_ExclVertex (a
->next
);
243 vect_sub (v
, a
->next
->point
, a
->point
);
246 assert (!vect_equal (v
, vect_zero
));
247 dx
= fabs ((double) v
[0]);
248 dy
= fabs ((double) v
[1]);
249 ang
= dy
/ (dy
+ dx
);
250 /* now move to the actual quadrant */
251 if (v
[0] < 0 && v
[1] >= 0)
252 ang
= 2.0 - ang
; /* 2nd quadrant */
253 else if (v
[0] < 0 && v
[1] < 0)
254 ang
+= 2.0; /* 3rd quadrant */
255 else if (v
[0] >= 0 && v
[1] < 0)
256 ang
= 4.0 - ang
; /* 4th quadrant */
258 assert (ang
>= 0.0 && ang
<= 4.0);
260 DEBUGP ("node on %c at (%d,%d) assigned angle %g on side %c\n", poly
,
261 a
->point
[0], a
->point
[1], ang
, side
);
270 argument a is a cross-vertex node.
271 argument poly is the polygon it comes from ('A' or 'B')
272 argument side is the side this descriptor goes on ('P' for previous
274 argument start is the head of the list of cvclists
277 insert_descriptor (VNODE
* a
, char poly
, char side
, CVCList
* start
)
279 CVCList
*l
, *new, *big
, *small
;
281 if (!(new = new_descriptor (a
, poly
, side
)))
283 /* search for the CVCList for this point */
286 start
= new; /* return is also new, so we know where start is */
287 start
->head
= new; /* circular list */
296 if (l
->parent
->point
[0] == a
->point
[0]
297 && l
->parent
->point
[1] == a
->point
[1])
298 { /* this CVCList is at our point */
303 if (l
->head
->parent
->point
[0] == start
->parent
->point
[0]
304 && l
->head
->parent
->point
[1] == start
->parent
->point
[1])
306 /* this seems to be a new point */
307 /* link this cvclist to the list of all cvclists */
308 for (; l
->head
!= new; l
= l
->next
)
318 l
= big
= small
= start
;
321 if (l
->next
->angle
< l
->angle
) /* find start/end of list */
326 else if (new->angle
>= l
->angle
&& new->angle
<= l
->next
->angle
)
328 /* insert new cvc if it lies between existing points */
331 l
->next
= l
->next
->prev
= new;
335 while ((l
= l
->next
) != start
);
336 /* didn't find it between points, it must go on an end */
337 if (big
->angle
<= new->angle
)
340 new->next
= big
->next
;
341 big
->next
= big
->next
->prev
= new;
344 assert (small
->angle
>= new->angle
);
346 new->prev
= small
->prev
;
347 small
->prev
= small
->prev
->next
= new;
353 (C) 1993 Klamer Schutte
354 (C) 1997 Alexey Nikitin, Michael Leonov
356 return 1 if new node in b, 2 if new node in a and 3 if new node in both
360 node_add_single_point (VNODE
* a
, Vector p
)
362 VNODE
*next_a
, *new_node
;
366 new_node
= node_add_single (a
, p
);
367 assert (new_node
!= NULL
);
369 new_node
->cvc_prev
= new_node
->cvc_next
= (CVCList
*) - 1;
371 if (new_node
== a
|| new_node
== next_a
)
375 } /* node_add_point */
382 node_label (VNODE
* pn
)
384 CVCList
*first_l
, *l
;
389 assert (pn
->cvc_next
);
390 this_poly
= pn
->cvc_next
->poly
;
391 /* search counter-clockwise in the cross vertex connectivity (CVC) list
393 * check for shared edges (that could be prev or next in the list since the angles are equal)
394 * and check if this edge (pn -> pn->next) is found between the other poly's entry and exit
396 if (pn
->cvc_next
->angle
== pn
->cvc_next
->prev
->angle
)
397 l
= pn
->cvc_next
->prev
;
399 l
= pn
->cvc_next
->next
;
402 while ((l
->poly
== this_poly
) && (l
!= first_l
->prev
))
404 assert (l
->poly
!= this_poly
);
406 assert (l
&& l
->angle
>= 0 && l
->angle
<= 4.0);
407 if (l
->poly
!= this_poly
)
411 if (l
->parent
->prev
->point
[0] == pn
->next
->point
[0] &&
412 l
->parent
->prev
->point
[1] == pn
->next
->point
[1])
415 pn
->shared
= l
->parent
->prev
;
422 if (l
->angle
== pn
->cvc_next
->angle
)
424 assert (l
->parent
->next
->point
[0] == pn
->next
->point
[0] &&
425 l
->parent
->next
->point
[1] == pn
->next
->point
[1]);
427 pn
->shared
= l
->parent
;
433 if (region
== UNKNWN
)
435 for (l
= l
->next
; l
!= pn
->cvc_next
; l
= l
->next
)
437 if (l
->poly
!= this_poly
)
447 assert (region
!= UNKNWN
);
448 assert (NODE_LABEL (pn
) == UNKNWN
|| NODE_LABEL (pn
) == region
);
449 LABEL_NODE (pn
, region
);
450 if (region
== SHARED
|| region
== SHARED2
)
460 add_descriptors (PLINE
* pl
, char poly
, CVCList
* list
)
462 VNODE
*node
= &pl
->head
;
468 assert (node
->cvc_prev
== (CVCList
*) - 1
469 && node
->cvc_next
== (CVCList
*) - 1);
470 list
= node
->cvc_prev
= insert_descriptor (node
, poly
, 'P', list
);
473 list
= node
->cvc_next
= insert_descriptor (node
, poly
, 'N', list
);
478 while ((node
= node
->next
) != &pl
->head
);
483 cntrbox_adjust (PLINE
* c
, Vector p
)
485 c
->xmin
= min (c
->xmin
, p
[0]);
486 c
->xmax
= max (c
->xmax
, p
[0] + 1);
487 c
->ymin
= min (c
->ymin
, p
[1]);
488 c
->ymax
= max (c
->ymax
, p
[1] + 1);
491 /* some structures for handling segment intersections using the rtrees */
501 typedef struct _insert_node_task insert_node_task
;
503 struct _insert_node_task
505 insert_node_task
*next
;
516 jmp_buf *env
, sego
, *touch
;
518 insert_node_task
*node_insert_list
;
521 typedef struct contour_info
527 insert_node_task
*node_insert_list
;
533 * (C) 2006 harry eaton
534 * This replaces the segment in the tree with the two new segments after
535 * a vertex has been added
538 adjust_tree (rtree_t
* tree
, struct seg
*s
)
542 q
= malloc (sizeof (struct seg
));
548 q
->box
.X1
= min (q
->v
->point
[0], q
->v
->next
->point
[0]);
549 q
->box
.X2
= max (q
->v
->point
[0], q
->v
->next
->point
[0]) + 1;
550 q
->box
.Y1
= min (q
->v
->point
[1], q
->v
->next
->point
[1]);
551 q
->box
.Y2
= max (q
->v
->point
[1], q
->v
->next
->point
[1]) + 1;
552 r_insert_entry (tree
, (const BoxType
*) q
, 1);
553 q
= malloc (sizeof (struct seg
));
559 q
->box
.X1
= min (q
->v
->point
[0], q
->v
->next
->point
[0]);
560 q
->box
.X2
= max (q
->v
->point
[0], q
->v
->next
->point
[0]) + 1;
561 q
->box
.Y1
= min (q
->v
->point
[1], q
->v
->next
->point
[1]);
562 q
->box
.Y2
= max (q
->v
->point
[1], q
->v
->next
->point
[1]) + 1;
563 r_insert_entry (tree
, (const BoxType
*) q
, 1);
564 r_delete_entry (tree
, (const BoxType
*) s
);
570 * (C) 2006, harry eaton
571 * This prunes the search for boxes that don't intersect the segment.
574 seg_in_region (const BoxType
* b
, void *cl
)
576 struct info
*i
= (struct info
*) cl
;
578 /* for zero slope the search is aligned on the axis so it is already pruned */
581 y1
= i
->m
* b
->X1
+ i
->b
;
582 y2
= i
->m
* b
->X2
+ i
->b
;
583 if (min (y1
, y2
) >= b
->Y2
)
585 if (max (y1
, y2
) < b
->Y1
)
587 return 1; /* might intersect */
590 /* Prepend a deferred node-insersion task to a list */
591 static insert_node_task
*
592 prepend_insert_node_task (insert_node_task
*list
, seg
*seg
, VNODE
*new_node
)
594 insert_node_task
*task
= malloc (sizeof (*task
));
596 task
->new_node
= new_node
;
603 * (C) 2006 harry eaton
604 * This routine checks if the segment in the tree intersect the search segment.
605 * If it does, the plines are marked as intersected and the point is marked for
606 * the cvclist. If the point is not already a vertex, a new vertex is inserted
607 * and the search for intersections starts over at the beginning.
608 * That is potentially a significant time penalty, but it does solve the snap rounding
609 * problem. There are efficient algorithms for finding intersections with snap
610 * rounding, but I don't have time to implement them right now.
613 seg_in_seg (const BoxType
* b
, void *cl
)
615 struct info
*i
= (struct info
*) cl
;
616 struct seg
*s
= (struct seg
*) b
;
621 /* When new nodes are added at the end of a pass due to an intersection
622 * the segments may be altered. If either segment we're looking at has
623 * already been intersected this pass, skip it until the next pass.
625 if (s
->intersected
|| i
->s
->intersected
)
628 cnt
= vect_inters2 (s
->v
->point
, s
->v
->next
->point
,
629 i
->v
->point
, i
->v
->next
->point
, s1
, s2
);
632 if (i
->touch
) /* if checking touches one find and we're done */
633 longjmp (*i
->touch
, TOUCHES
);
634 i
->s
->p
->Flags
.status
= ISECTED
;
635 s
->p
->Flags
.status
= ISECTED
;
638 bool done_insert_on_i
= false;
639 new_node
= node_add_single_point (i
->v
, cnt
> 1 ? s2
: s1
);
640 if (new_node
!= NULL
)
642 #ifdef DEBUG_INTERSECT
643 DEBUGP ("new intersection on segment \"i\" at (%d, %d)\n",
644 cnt
> 1 ? s2
[0] : s1
[0], cnt
> 1 ? s2
[1] : s1
[1]);
646 i
->node_insert_list
=
647 prepend_insert_node_task (i
->node_insert_list
, i
->s
, new_node
);
648 i
->s
->intersected
= 1;
649 done_insert_on_i
= true;
651 new_node
= node_add_single_point (s
->v
, cnt
> 1 ? s2
: s1
);
652 if (new_node
!= NULL
)
654 #ifdef DEBUG_INTERSECT
655 DEBUGP ("new intersection on segment \"s\" at (%d, %d)\n",
656 cnt
> 1 ? s2
[0] : s1
[0], cnt
> 1 ? s2
[1] : s1
[1]);
658 i
->node_insert_list
=
659 prepend_insert_node_task (i
->node_insert_list
, s
, new_node
);
661 return 0; /* Keep looking for intersections with segment "i" */
663 /* Skip any remaining r_search hits against segment i, as any futher
664 * intersections will be rejected until the next pass anyway.
666 if (done_insert_on_i
)
667 longjmp (*i
->env
, 1);
673 make_edge_tree (PLINE
* pb
)
677 rtree_t
*ans
= r_create_tree (NULL
, 0, 0);
681 s
= malloc (sizeof (struct seg
));
683 if (bv
->point
[0] < bv
->next
->point
[0])
685 s
->box
.X1
= bv
->point
[0];
686 s
->box
.X2
= bv
->next
->point
[0] + 1;
690 s
->box
.X2
= bv
->point
[0] + 1;
691 s
->box
.X1
= bv
->next
->point
[0];
693 if (bv
->point
[1] < bv
->next
->point
[1])
695 s
->box
.Y1
= bv
->point
[1];
696 s
->box
.Y2
= bv
->next
->point
[1] + 1;
700 s
->box
.Y2
= bv
->point
[1] + 1;
701 s
->box
.Y1
= bv
->next
->point
[1];
705 r_insert_entry (ans
, (const BoxType
*) s
, 1);
707 while ((bv
= bv
->next
) != &pb
->head
);
712 get_seg (const BoxType
* b
, void *cl
)
714 struct info
*i
= (struct info
*) cl
;
715 struct seg
*s
= (struct seg
*) b
;
719 longjmp (i
->sego
, 1);
725 * intersect() (and helpers)
726 * (C) 2006, harry eaton
727 * This uses an rtree to find A-B intersections. Whenever a new vertex is
728 * added, the search for intersections is re-started because the rounding
729 * could alter the topology otherwise.
730 * This should use a faster algorithm for snap rounding intersection finding.
731 * The best algorthim is probably found in:
733 * "Improved output-sensitive snap rounding," John Hershberger, Proceedings
734 * of the 22nd annual symposium on Computational geomerty, 2006, pp 357-366.
735 * http://doi.acm.org/10.1145/1137856.1137909
737 * Algorithms described by de Berg, or Goodrich or Halperin, or Hobby would
738 * probably work as well.
743 contour_bounds_touch (const BoxType
* b
, void *cl
)
745 contour_info
*c_info
= (contour_info
*) cl
;
746 PLINE
*pa
= c_info
->pa
;
747 PLINE
*pb
= (PLINE
*) b
;
750 VNODE
*av
; /* node iterators */
755 /* Have seg_in_seg return to our desired location if it touches */
757 info
.touch
= c_info
->getout
;
758 info
.need_restart
= 0;
759 info
.node_insert_list
= c_info
->node_insert_list
;
761 /* Pick which contour has the fewer points, and do the loop
762 * over that. The r_tree makes hit-testing against a contour
763 * faster, so we want to do that on the bigger contour.
765 if (pa
->Count
< pb
->Count
)
776 av
= &looping_over
->head
;
777 do /* Loop over the nodes in the smaller contour */
779 /* check this edge for any insertions */
782 /* compute the slant for region trimming */
783 dx
= av
->next
->point
[0] - av
->point
[0];
788 info
.m
= (av
->next
->point
[1] - av
->point
[1]) / dx
;
789 info
.b
= av
->point
[1] - info
.m
* av
->point
[0];
791 box
.X2
= (box
.X1
= av
->point
[0]) + 1;
792 box
.Y2
= (box
.Y1
= av
->point
[1]) + 1;
794 /* fill in the segment in info corresponding to this node */
795 if (setjmp (info
.sego
) == 0)
797 r_search (looping_over
->tree
, &box
, NULL
, get_seg
, &info
);
801 /* If we're going to have another pass anyway, skip this */
802 if (info
.s
->intersected
&& info
.node_insert_list
!= NULL
)
805 if (setjmp (restart
))
808 /* NB: If this actually hits anything, we are teleported back to the beginning */
809 info
.tree
= rtree_over
->tree
;
811 if (UNLIKELY (r_search (info
.tree
, &info
.s
->box
,
812 seg_in_region
, seg_in_seg
, &info
)))
813 assert (0); /* XXX: Memory allocation failure */
815 while ((av
= av
->next
) != &looping_over
->head
);
817 c_info
->node_insert_list
= info
.node_insert_list
;
818 if (info
.need_restart
)
819 c_info
->need_restart
= 1;
824 intersect_impl (jmp_buf * jb
, POLYAREA
* b
, POLYAREA
* a
, int add
)
829 int need_restart
= 0;
830 insert_node_task
*task
;
831 c_info
.need_restart
= 0;
832 c_info
.node_insert_list
= NULL
;
834 /* Search the r-tree of the object with most contours
835 * We loop over the contours of "a". Swap if necessary.
837 if (a
->contour_tree
->size
> b
->contour_tree
->size
)
844 for (pa
= a
->contours
; pa
; pa
= pa
->next
) /* Loop over the contours of POLYAREA "a" */
850 c_info
.getout
= NULL
;
855 retval
= setjmp (out
);
858 /* The intersection test short-circuited back here,
859 * we need to clean up, then longjmp to jb */
860 longjmp (*jb
, retval
);
862 c_info
.getout
= &out
;
867 sb
.X2
= pa
->xmax
+ 1;
868 sb
.Y2
= pa
->ymax
+ 1;
870 r_search (b
->contour_tree
, &sb
, NULL
, contour_bounds_touch
, &c_info
);
871 if (c_info
.need_restart
)
875 /* Process any deferred node insersions */
876 task
= c_info
.node_insert_list
;
879 insert_node_task
*next
= task
->next
;
882 task
->new_node
->prev
= task
->seg
->v
;
883 task
->new_node
->next
= task
->seg
->v
->next
;
884 task
->seg
->v
->next
->prev
= task
->new_node
;
885 task
->seg
->v
->next
= task
->new_node
;
886 task
->seg
->p
->Count
++;
888 cntrbox_adjust (task
->seg
->p
, task
->new_node
->point
);
889 if (adjust_tree (task
->seg
->p
->tree
, task
->seg
))
890 assert (0); /* XXX: Memory allocation failure */
892 need_restart
= 1; /* Any new nodes could intersect */
902 intersect (jmp_buf * jb
, POLYAREA
* b
, POLYAREA
* a
, int add
)
905 while (intersect_impl (jb
, b
, a
, add
))
911 M_POLYAREA_intersect (jmp_buf * e
, POLYAREA
* afst
, POLYAREA
* bfst
, int add
)
913 POLYAREA
*a
= afst
, *b
= bfst
;
914 PLINE
*curcA
, *curcB
;
915 CVCList
*the_list
= NULL
;
917 if (a
== NULL
|| b
== NULL
)
918 error (err_bad_parm
);
923 if (a
->contours
->xmax
>= b
->contours
->xmin
&&
924 a
->contours
->ymax
>= b
->contours
->ymin
&&
925 a
->contours
->xmin
<= b
->contours
->xmax
&&
926 a
->contours
->ymin
<= b
->contours
->ymax
)
928 if (UNLIKELY (intersect (e
, a
, b
, add
)))
929 error (err_no_memory
);
932 while (add
&& (a
= a
->f
) != afst
);
933 for (curcB
= b
->contours
; curcB
!= NULL
; curcB
= curcB
->next
)
934 if (curcB
->Flags
.status
== ISECTED
)
936 the_list
= add_descriptors (curcB
, 'B', the_list
);
937 if (UNLIKELY (the_list
== NULL
))
938 error (err_no_memory
);
941 while (add
&& (b
= b
->f
) != bfst
);
944 for (curcA
= a
->contours
; curcA
!= NULL
; curcA
= curcA
->next
)
945 if (curcA
->Flags
.status
== ISECTED
)
947 the_list
= add_descriptors (curcA
, 'A', the_list
);
948 if (UNLIKELY (the_list
== NULL
))
949 error (err_no_memory
);
952 while (add
&& (a
= a
->f
) != afst
);
953 } /* M_POLYAREA_intersect */
956 cntrbox_inside (PLINE
* c1
, PLINE
* c2
)
958 assert (c1
!= NULL
&& c2
!= NULL
);
959 return ((c1
->xmin
>= c2
->xmin
) &&
960 (c1
->ymin
>= c2
->ymin
) &&
961 (c1
->xmax
<= c2
->xmax
) && (c1
->ymax
<= c2
->ymax
));
964 /*****************************************************************/
965 /* Routines for making labels */
968 count_contours_i_am_inside (const BoxType
* b
, void *cl
)
971 PLINE
*check
= (PLINE
*) b
;
973 if (poly_ContourInContour (check
, me
))
978 /* cntr_in_M_POLYAREA
979 returns poly is inside outfst ? TRUE : FALSE */
981 cntr_in_M_POLYAREA (PLINE
* poly
, POLYAREA
* outfst
, BOOLp test
)
983 POLYAREA
*outer
= outfst
;
986 assert (poly
!= NULL
);
987 assert (outer
!= NULL
);
989 heap
= heap_create ();
992 if (cntrbox_inside (poly
, outer
->contours
))
993 heap_insert (heap
, outer
->contours
->area
, (void *) outer
);
995 /* if checking touching, use only the first polygon */
996 while (!test
&& (outer
= outer
->f
) != outfst
);
997 /* we need only check the smallest poly container
998 * but we must loop in case the box containter is not
999 * the poly container */
1002 if (heap_is_empty (heap
))
1004 outer
= (POLYAREA
*) heap_remove_smallest (heap
);
1007 (outer
->contour_tree
, (BoxType
*) poly
, NULL
,
1008 count_contours_i_am_inside
, poly
))
1010 case 0: /* Didn't find anything in this piece, Keep looking */
1012 case 1: /* Found we are inside this piece, and not any of its holes */
1013 heap_destroy (&heap
);
1015 case 2: /* Found inside a hole in the smallest polygon so far. No need to check the other polygons */
1016 heap_destroy (&heap
);
1019 printf ("Something strange here\n");
1024 heap_destroy (&heap
);
1026 } /* cntr_in_M_POLYAREA */
1031 theState (VNODE
* v
)
1033 static char u
[] = "UNKNOWN";
1034 static char i
[] = "INSIDE";
1035 static char o
[] = "OUTSIDE";
1036 static char s
[] = "SHARED";
1037 static char s2
[] = "SHARED2";
1039 switch (NODE_LABEL (v
))
1054 #ifdef DEBUG_ALL_LABELS
1056 print_labels (PLINE
* a
)
1058 VNODE
*c
= &a
->head
;
1062 DEBUGP ("(%d,%d)->(%d,%d) labeled %s\n", c
->point
[0], c
->point
[1],
1063 c
->next
->point
[0], c
->next
->point
[1], theState (c
));
1065 while ((c
= c
->next
) != &a
->head
);
1072 (C) 2006 harry eaton
1073 (C) 1993 Klamer Schutte
1074 (C) 1997 Alexey Nikitin, Michael Leonov
1078 label_contour (PLINE
* a
)
1080 VNODE
*cur
= &a
->head
;
1081 VNODE
*first_labelled
= NULL
;
1086 if (cur
->cvc_next
) /* examine cross vertex */
1088 label
= node_label (cur
);
1089 if (first_labelled
== NULL
)
1090 first_labelled
= cur
;
1094 if (first_labelled
== NULL
)
1097 /* This labels nodes which aren't cross-connected */
1098 assert (label
== INSIDE
|| label
== OUTSIDE
);
1099 LABEL_NODE (cur
, label
);
1101 while ((cur
= cur
->next
) != first_labelled
);
1102 #ifdef DEBUG_ALL_LABELS
1107 } /* label_contour */
1110 cntr_label_POLYAREA (PLINE
* poly
, POLYAREA
* ppl
, BOOLp test
)
1112 assert (ppl
!= NULL
&& ppl
->contours
!= NULL
);
1113 if (poly
->Flags
.status
== ISECTED
)
1115 label_contour (poly
); /* should never get here when BOOLp is true */
1117 else if (cntr_in_M_POLYAREA (poly
, ppl
, test
))
1121 poly
->Flags
.status
= INSIDE
;
1127 poly
->Flags
.status
= OUTSIDE
;
1130 } /* cntr_label_POLYAREA */
1133 M_POLYAREA_label_separated (PLINE
* afst
, POLYAREA
* b
, BOOLp touch
)
1137 for (curc
= afst
; curc
!= NULL
; curc
= curc
->next
)
1139 if (cntr_label_POLYAREA (curc
, b
, touch
) && touch
)
1146 M_POLYAREA_label (POLYAREA
* afst
, POLYAREA
* b
, BOOLp touch
)
1154 for (curc
= a
->contours
; curc
!= NULL
; curc
= curc
->next
)
1155 if (cntr_label_POLYAREA (curc
, b
, touch
))
1161 while (!touch
&& (a
= a
->f
) != afst
);
1165 /****************************************************************/
1167 /* routines for temporary storing resulting contours */
1169 InsCntr (jmp_buf * e
, PLINE
* c
, POLYAREA
** dst
)
1175 MemGet (*dst
, POLYAREA
);
1176 (*dst
)->f
= (*dst
)->b
= *dst
;
1181 MemGet (newp
, POLYAREA
);
1183 newp
->b
= (*dst
)->b
;
1184 newp
->f
->b
= newp
->b
->f
= newp
;
1187 newp
->contour_tree
= r_create_tree (NULL
, 0, 0);
1188 r_insert_entry (newp
->contour_tree
, (BoxTypePtr
) c
, 0);
1193 PutContour (jmp_buf * e
, PLINE
* cntr
, POLYAREA
** contours
, PLINE
** holes
,
1194 POLYAREA
* owner
, POLYAREA
* parent
, PLINE
* parent_contour
)
1196 assert (cntr
!= NULL
);
1197 assert (cntr
->Count
> 2);
1200 if (cntr
->Flags
.orient
== PLF_DIR
)
1203 r_delete_entry (owner
->contour_tree
, (BoxType
*) cntr
);
1204 InsCntr (e
, cntr
, contours
);
1206 /* put hole into temporary list */
1209 /* if we know this belongs inside the parent, put it there now */
1212 cntr
->next
= parent_contour
->next
;
1213 parent_contour
->next
= cntr
;
1214 if (owner
!= parent
)
1217 r_delete_entry (owner
->contour_tree
, (BoxType
*) cntr
);
1218 r_insert_entry (parent
->contour_tree
, (BoxType
*) cntr
, 0);
1223 cntr
->next
= *holes
;
1224 *holes
= cntr
; /* let cntr be 1st hole in list */
1225 /* We don't insert the holes into an r-tree,
1226 * they just form a linked list */
1228 r_delete_entry (owner
->contour_tree
, (BoxType
*) cntr
);
1234 remove_contour (POLYAREA
* piece
, PLINE
* prev_contour
, PLINE
* contour
,
1235 int remove_rtree_entry
)
1237 if (piece
->contours
== contour
)
1238 piece
->contours
= contour
->next
;
1239 else if (prev_contour
!= NULL
)
1241 assert (prev_contour
->next
== contour
);
1242 prev_contour
->next
= contour
->next
;
1245 contour
->next
= NULL
;
1247 if (remove_rtree_entry
)
1248 r_delete_entry (piece
->contour_tree
, (BoxType
*) contour
);
1251 struct polyarea_info
1253 BoxType BoundingBox
;
1258 heap_it (const BoxType
* b
, void *cl
)
1260 heap_t
*heap
= (heap_t
*) cl
;
1261 struct polyarea_info
*pa_info
= (struct polyarea_info
*) b
;
1262 PLINE
*p
= pa_info
->pa
->contours
;
1264 return 0; /* how did this happen? */
1265 heap_insert (heap
, p
->area
, pa_info
);
1269 struct find_inside_info
1277 find_inside (const BoxType
* b
, void *cl
)
1279 struct find_inside_info
*info
= cl
;
1280 PLINE
*check
= (PLINE
*) b
;
1281 /* Do test on check to see if it inside info->want_inside */
1283 if (check
->Flags
.orient
== PLF_DIR
)
1287 if (poly_ContourInContour (info
->want_inside
, check
))
1289 info
->result
= check
;
1290 longjmp (info
->jb
, 1);
1296 InsertHoles (jmp_buf * e
, POLYAREA
* dest
, PLINE
** src
)
1299 PLINE
*curh
, *container
;
1303 int num_polyareas
= 0;
1304 struct polyarea_info
*all_pa_info
, *pa_info
;
1307 return; /* empty hole list */
1309 error (err_bad_parm
); /* empty contour list */
1311 /* Count dest polyareas */
1317 while ((curc
= curc
->f
) != dest
);
1319 /* make a polyarea info table */
1320 /* make an rtree of polyarea info table */
1321 all_pa_info
= malloc (sizeof (struct polyarea_info
) * num_polyareas
);
1322 tree
= r_create_tree (NULL
, 0, 0);
1327 all_pa_info
[i
].BoundingBox
.X1
= curc
->contours
->xmin
;
1328 all_pa_info
[i
].BoundingBox
.Y1
= curc
->contours
->ymin
;
1329 all_pa_info
[i
].BoundingBox
.X2
= curc
->contours
->xmax
;
1330 all_pa_info
[i
].BoundingBox
.Y2
= curc
->contours
->ymax
;
1331 all_pa_info
[i
].pa
= curc
;
1332 r_insert_entry (tree
, (const BoxType
*) &all_pa_info
[i
], 0);
1335 while ((curc
= curc
->f
) != dest
);
1337 /* loop through the holes and put them where they belong */
1338 while ((curh
= *src
) != NULL
)
1343 /* build a heap of all of the polys that the hole is inside its bounding box */
1344 heap
= heap_create ();
1345 r_search (tree
, (BoxType
*) curh
, NULL
, heap_it
, heap
);
1346 if (heap_is_empty (heap
))
1353 poly_DelContour (&curh
);
1354 error (err_bad_parm
);
1356 /* Now search the heap for the container. If there was only one item
1357 * in the heap, assume it is the container without the expense of
1360 pa_info
= heap_remove_smallest (heap
);
1361 if (heap_is_empty (heap
))
1362 { /* only one possibility it must be the right one */
1363 assert (poly_ContourInContour (pa_info
->pa
->contours
, curh
));
1364 container
= pa_info
->pa
->contours
;
1370 if (poly_ContourInContour (pa_info
->pa
->contours
, curh
))
1372 container
= pa_info
->pa
->contours
;
1375 if (heap_is_empty (heap
))
1377 pa_info
= heap_remove_smallest (heap
);
1381 heap_destroy (&heap
);
1382 if (container
== NULL
)
1384 /* bad input polygons were given */
1391 poly_DelContour (&curh
);
1392 error (err_bad_parm
);
1396 /* Need to check if this new hole means we need to kick out any old ones for reprocessing */
1399 struct find_inside_info info
;
1402 info
.want_inside
= curh
;
1404 /* Set jump return */
1405 if (setjmp (info
.jb
))
1407 /* Returned here! */
1412 /* Rtree search, calling back a routine to longjmp back with data about any hole inside the added one */
1413 /* Be sure not to bother jumping back to report the main contour! */
1414 r_search (pa_info
->pa
->contour_tree
, (BoxType
*) curh
, NULL
,
1415 find_inside
, &info
);
1417 /* Nothing found? */
1421 /* We need to find the contour before it, so we can update its next pointer */
1423 while (prev
->next
!= info
.result
)
1428 /* Remove hole from the contour */
1429 remove_contour (pa_info
->pa
, prev
, info
.result
, TRUE
);
1431 /* Add hole as the next on the list to be processed in this very function */
1432 info
.result
->next
= *src
;
1435 /* End check for kicked out holes */
1437 /* link at front of hole list */
1438 curh
->next
= container
->next
;
1439 container
->next
= curh
;
1440 r_insert_entry (pa_info
->pa
->contour_tree
, (BoxTypePtr
) curh
, 0);
1444 r_destroy_tree (&tree
);
1449 /****************************************************************/
1450 /* routines for collecting result */
1458 typedef int (*S_Rule
) (VNODE
*, DIRECTION
*);
1461 typedef int (*J_Rule
) (char, VNODE
*, DIRECTION
*);
1464 UniteS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1467 return (NODE_LABEL (cur
) == OUTSIDE
) || (NODE_LABEL (cur
) == SHARED
);
1471 IsectS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1474 return (NODE_LABEL (cur
) == INSIDE
) || (NODE_LABEL (cur
) == SHARED
);
1478 SubS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1481 return (NODE_LABEL (cur
) == OUTSIDE
) || (NODE_LABEL (cur
) == SHARED2
);
1485 XorS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1487 if (cur
->Flags
.status
== INSIDE
)
1492 if (cur
->Flags
.status
== OUTSIDE
)
1501 IsectJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1503 assert (*cdir
== FORW
);
1504 return (v
->Flags
.status
== INSIDE
|| v
->Flags
.status
== SHARED
);
1508 UniteJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1510 assert (*cdir
== FORW
);
1511 return (v
->Flags
.status
== OUTSIDE
|| v
->Flags
.status
== SHARED
);
1515 XorJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1517 if (v
->Flags
.status
== INSIDE
)
1522 if (v
->Flags
.status
== OUTSIDE
)
1531 SubJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1533 if (p
== 'B' && v
->Flags
.status
== INSIDE
)
1538 if (p
== 'A' && v
->Flags
.status
== OUTSIDE
)
1543 if (v
->Flags
.status
== SHARED2
)
1554 /* return the edge that comes next.
1555 * if the direction is BACKW, then we return the next vertex
1556 * so that prev vertex has the flags for the edge
1558 * returns true if an edge is found, false otherwise
1561 jump (VNODE
** cur
, DIRECTION
* cdir
, J_Rule rule
)
1567 if (!(*cur
)->cvc_prev
) /* not a cross-vertex */
1569 if (*cdir
== FORW
? (*cur
)->Flags
.mark
: (*cur
)->prev
->Flags
.mark
)
1574 DEBUGP ("jump entering node at (%d, %d)\n", (*cur
)->point
[0],
1578 d
= (*cur
)->cvc_prev
->prev
;
1580 d
= (*cur
)->cvc_next
->prev
;
1588 if (!e
->Flags
.mark
&& rule (d
->poly
, e
, &new))
1590 if ((d
->side
== 'N' && new == FORW
) ||
1591 (d
->side
== 'P' && new == BACKW
))
1595 DEBUGP ("jump leaving node at (%d, %d)\n",
1596 e
->next
->point
[0], e
->next
->point
[1]);
1598 DEBUGP ("jump leaving node at (%d, %d)\n",
1599 e
->point
[0], e
->point
[1]);
1607 while ((d
= d
->prev
) != start
);
1612 Gather (VNODE
* start
, PLINE
** result
, J_Rule v_rule
, DIRECTION initdir
)
1614 VNODE
*cur
= start
, *newn
;
1615 DIRECTION dir
= initdir
;
1617 DEBUGP ("gather direction = %d\n", dir
);
1619 assert (*result
== NULL
);
1622 /* see where to go next */
1623 if (!jump (&cur
, &dir
, v_rule
))
1625 /* add edge to polygon */
1628 *result
= poly_NewContour (cur
->point
);
1629 if (*result
== NULL
)
1630 return err_no_memory
;
1634 if ((newn
= poly_CreateNode (cur
->point
)) == NULL
)
1635 return err_no_memory
;
1636 poly_InclVertex ((*result
)->head
.prev
, newn
);
1639 DEBUGP ("gather vertex at (%d, %d)\n", cur
->point
[0], cur
->point
[1]);
1641 /* Now mark the edge as included. */
1642 newn
= (dir
== FORW
? cur
: cur
->prev
);
1643 newn
->Flags
.mark
= 1;
1644 /* for SHARED edge mark both */
1646 newn
->shared
->Flags
.mark
= 1;
1648 /* Advance to the next edge. */
1649 cur
= (dir
== FORW
? cur
->next
: cur
->prev
);
1656 Collect1 (jmp_buf * e
, VNODE
* cur
, DIRECTION dir
, POLYAREA
** contours
,
1657 PLINE
** holes
, J_Rule j_rule
)
1659 PLINE
*p
= NULL
; /* start making contour */
1662 Gather (dir
== FORW
? cur
: cur
->next
, &p
, j_rule
, dir
)) != err_ok
)
1665 poly_DelContour (&p
);
1670 poly_PreContour (p
, TRUE
);
1674 DEBUGP ("adding contour with %d verticies and direction %c\n",
1675 p
->Count
, p
->Flags
.orient
? 'F' : 'B');
1677 PutContour (e
, p
, contours
, holes
, NULL
, NULL
, NULL
);
1681 /* some sort of computation error ? */
1683 DEBUGP ("Bad contour! Not enough points!!\n");
1685 poly_DelContour (&p
);
1690 Collect (jmp_buf * e
, PLINE
* a
, POLYAREA
** contours
, PLINE
** holes
,
1691 S_Rule s_rule
, J_Rule j_rule
)
1699 if (s_rule (cur
, &dir
) && cur
->Flags
.mark
== 0)
1700 Collect1 (e
, cur
, dir
, contours
, holes
, j_rule
);
1702 if ((other
->cvc_prev
&& jump (&other
, &dir
, j_rule
)))
1703 Collect1 (e
, other
, dir
, contours
, holes
, j_rule
);
1705 while ((cur
= cur
->next
) != &a
->head
);
1710 cntr_Collect (jmp_buf * e
, PLINE
** A
, POLYAREA
** contours
, PLINE
** holes
,
1711 int action
, POLYAREA
* owner
, POLYAREA
* parent
,
1712 PLINE
* parent_contour
)
1716 if ((*A
)->Flags
.status
== ISECTED
)
1721 Collect (e
, *A
, contours
, holes
, UniteS_Rule
, UniteJ_Rule
);
1724 Collect (e
, *A
, contours
, holes
, IsectS_Rule
, IsectJ_Rule
);
1727 Collect (e
, *A
, contours
, holes
, XorS_Rule
, XorJ_Rule
);
1730 Collect (e
, *A
, contours
, holes
, SubS_Rule
, SubJ_Rule
);
1739 if ((*A
)->Flags
.status
== INSIDE
)
1742 /* disappear this contour (rtree entry removed in PutContour) */
1744 tmprev
->next
= NULL
;
1745 PutContour (e
, tmprev
, contours
, holes
, owner
, NULL
, NULL
);
1750 if ((*A
)->Flags
.status
== INSIDE
)
1753 /* disappear this contour (rtree entry removed in PutContour) */
1755 tmprev
->next
= NULL
;
1756 poly_InvContour (tmprev
);
1757 PutContour (e
, tmprev
, contours
, holes
, owner
, NULL
, NULL
);
1760 /* break; *//* Fall through (I think this is correct! pcjc2) */
1763 if ((*A
)->Flags
.status
== OUTSIDE
)
1766 /* disappear this contour (rtree entry removed in PutContour) */
1768 tmprev
->next
= NULL
;
1769 PutContour (e
, tmprev
, contours
, holes
, owner
, parent
,
1777 } /* cntr_Collect */
1780 M_B_AREA_Collect (jmp_buf * e
, POLYAREA
* bfst
, POLYAREA
** contours
,
1781 PLINE
** holes
, int action
)
1784 PLINE
**cur
, **next
, *tmp
;
1789 for (cur
= &b
->contours
; *cur
!= NULL
; cur
= next
)
1791 next
= &((*cur
)->next
);
1792 if ((*cur
)->Flags
.status
== ISECTED
)
1795 if ((*cur
)->Flags
.status
== INSIDE
)
1800 poly_InvContour (*cur
);
1806 tmp
->Flags
.status
= UNKNWN
;
1807 PutContour (e
, tmp
, contours
, holes
, b
, NULL
, NULL
);
1810 break; /* nothing to do - already included */
1812 else if ((*cur
)->Flags
.status
== OUTSIDE
)
1822 tmp
->Flags
.status
= UNKNWN
;
1823 PutContour (e
, tmp
, contours
, holes
, b
, NULL
, NULL
);
1827 break; /* do nothing, not included */
1831 while ((b
= b
->f
) != bfst
);
1836 contour_is_first (POLYAREA
* a
, PLINE
* cur
)
1838 return (a
->contours
== cur
);
1843 contour_is_last (PLINE
* cur
)
1845 return (cur
->next
== NULL
);
1850 remove_polyarea (POLYAREA
** list
, POLYAREA
* piece
)
1852 /* If this item was the start of the list, advance that pointer */
1856 /* But reset it to NULL if it wraps around and hits us again */
1860 piece
->b
->f
= piece
->f
;
1861 piece
->f
->b
= piece
->b
;
1862 piece
->f
= piece
->b
= piece
;
1866 M_POLYAREA_separate_isected (jmp_buf * e
, POLYAREA
** pieces
,
1867 PLINE
** holes
, PLINE
** isected
)
1869 POLYAREA
*a
= *pieces
;
1871 PLINE
*curc
, *next
, *prev
;
1877 /* TODO: STASH ENOUGH INFORMATION EARLIER ON, SO WE CAN REMOVE THE INTERSECTED
1878 CONTOURS WITHOUT HAVING TO WALK THE FULL DATA-STRUCTURE LOOKING FOR THEM. */
1882 int hole_contour
= 0;
1886 finished
= (anext
== *pieces
);
1889 for (curc
= a
->contours
; curc
!= NULL
; curc
= next
, is_outline
= 0)
1891 int is_first
= contour_is_first (a
, curc
);
1892 int is_last
= contour_is_last (curc
);
1893 int isect_contour
= (curc
->Flags
.status
== ISECTED
);
1897 if (isect_contour
|| hole_contour
)
1900 /* Reset the intersection flags, since we keep these pieces */
1901 if (curc
->Flags
.status
!= ISECTED
)
1902 curc
->Flags
.status
= UNKNWN
;
1904 remove_contour (a
, prev
, curc
, !(is_first
&& is_last
));
1908 /* Link into the list of intersected contours */
1909 curc
->next
= *isected
;
1912 else if (hole_contour
)
1914 /* Link into the list of holes */
1915 curc
->next
= *holes
;
1923 if (is_first
&& is_last
)
1925 remove_polyarea (pieces
, a
);
1926 poly_Free (&a
); /* NB: Sets a to NULL */
1932 /* Note the item we just didn't delete as the next
1933 candidate for having its "next" pointer adjusted.
1934 Saves walking the contour list when we delete one. */
1938 /* If we move or delete an outer contour, we need to move any holes
1939 we wish to keep within that contour to the holes list. */
1940 if (is_outline
&& isect_contour
)
1945 /* If we deleted all the pieces of the polyarea, *pieces is NULL */
1947 while ((a
= anext
), *pieces
!= NULL
&& !finished
);
1951 struct find_inside_m_pa_info
1954 POLYAREA
*want_inside
;
1959 find_inside_m_pa (const BoxType
* b
, void *cl
)
1961 struct find_inside_m_pa_info
*info
= cl
;
1962 PLINE
*check
= (PLINE
*) b
;
1963 /* Don't report for the main contour */
1964 if (check
->Flags
.orient
== PLF_DIR
)
1966 /* Don't look at contours marked as being intersected */
1967 if (check
->Flags
.status
== ISECTED
)
1969 if (cntr_in_M_POLYAREA (check
, info
->want_inside
, FALSE
))
1971 info
->result
= check
;
1972 longjmp (info
->jb
, 1);
1979 M_POLYAREA_update_primary (jmp_buf * e
, POLYAREA
** pieces
,
1980 PLINE
** holes
, int action
, POLYAREA
* bpa
)
1982 POLYAREA
*a
= *pieces
;
1985 PLINE
*curc
, *next
, *prev
;
1989 int del_outside
= 0;
2004 case PBO_XOR
: /* NOT IMPLEMENTED OR USED */
2010 box
= *((BoxType
*) bpa
->contours
);
2012 while ((b
= b
->f
) != bpa
)
2014 BoxType
*b_box
= (BoxType
*) b
->contours
;
2015 MAKEMIN (box
.X1
, b_box
->X1
);
2016 MAKEMIN (box
.Y1
, b_box
->Y1
);
2017 MAKEMAX (box
.X2
, b_box
->X2
);
2018 MAKEMAX (box
.Y2
, b_box
->Y2
);
2027 finished
= (anext
== *pieces
);
2029 /* Test the outer contour first, as we may need to remove all children */
2031 /* We've not yet split intersected contours out, just ignore them */
2032 if (a
->contours
->Flags
.status
!= ISECTED
&&
2033 /* Pre-filter on bounding box */
2034 ((a
->contours
->xmin
>= box
.X1
) && (a
->contours
->ymin
>= box
.Y1
)
2035 && (a
->contours
->xmax
<= box
.X2
)
2036 && (a
->contours
->ymax
<= box
.Y2
)) &&
2037 /* Then test properly */
2038 cntr_in_M_POLYAREA (a
->contours
, bpa
, FALSE
))
2041 /* Delete this contour, all children -> holes queue */
2043 /* Delete the outer contour */
2045 remove_contour (a
, NULL
, curc
, FALSE
); /* Rtree deleted in poly_Free below */
2046 /* a->contours now points to the remaining holes */
2047 poly_DelContour (&curc
);
2049 if (a
->contours
!= NULL
)
2051 /* Find the end of the list of holes */
2053 while (curc
->next
!= NULL
)
2056 /* Take the holes and prepend to the holes queue */
2057 curc
->next
= *holes
;
2058 *holes
= a
->contours
;
2062 remove_polyarea (pieces
, a
);
2063 poly_Free (&a
); /* NB: Sets a to NULL */
2068 /* Loop whilst we find INSIDE contours to delete */
2071 struct find_inside_m_pa_info info
;
2074 info
.want_inside
= bpa
;
2076 /* Set jump return */
2077 if (setjmp (info
.jb
))
2079 /* Returned here! */
2084 /* r-tree search, calling back a routine to longjmp back with
2085 * data about any hole inside the B polygon.
2086 * NB: Does not jump back to report the main contour!
2088 r_search (a
->contour_tree
, &box
, NULL
, find_inside_m_pa
,
2091 /* Nothing found? */
2095 /* We need to find the contour before it, so we can update its next pointer */
2097 while (prev
->next
!= info
.result
)
2102 /* Remove hole from the contour */
2103 remove_contour (a
, prev
, info
.result
, TRUE
);
2104 poly_DelContour (&info
.result
);
2106 /* End check for deleted holes */
2108 /* If we deleted all the pieces of the polyarea, *pieces is NULL */
2110 while ((a
= anext
), *pieces
!= NULL
&& !finished
);
2116 /* This path isn't optimised for speed */
2121 int hole_contour
= 0;
2125 finished
= (anext
== *pieces
);
2128 for (curc
= a
->contours
; curc
!= NULL
; curc
= next
, is_outline
= 0)
2130 int is_first
= contour_is_first (a
, curc
);
2131 int is_last
= contour_is_last (curc
);
2132 int del_contour
= 0;
2137 del_contour
= curc
->Flags
.status
!= ISECTED
&&
2138 !cntr_in_M_POLYAREA (curc
, bpa
, FALSE
);
2140 /* Skip intersected contours */
2141 if (curc
->Flags
.status
== ISECTED
)
2147 /* Reset the intersection flags, since we keep these pieces */
2148 curc
->Flags
.status
= UNKNWN
;
2150 if (del_contour
|| hole_contour
)
2153 remove_contour (a
, prev
, curc
, !(is_first
&& is_last
));
2157 /* Delete the contour */
2158 poly_DelContour (&curc
); /* NB: Sets curc to NULL */
2160 else if (hole_contour
)
2162 /* Link into the list of holes */
2163 curc
->next
= *holes
;
2171 if (is_first
&& is_last
)
2173 remove_polyarea (pieces
, a
);
2174 poly_Free (&a
); /* NB: Sets a to NULL */
2180 /* Note the item we just didn't delete as the next
2181 candidate for having its "next" pointer adjusted.
2182 Saves walking the contour list when we delete one. */
2186 /* If we move or delete an outer contour, we need to move any holes
2187 we wish to keep within that contour to the holes list. */
2188 if (is_outline
&& del_contour
)
2193 /* If we deleted all the pieces of the polyarea, *pieces is NULL */
2195 while ((a
= anext
), *pieces
!= NULL
&& !finished
);
2199 M_POLYAREA_Collect_separated (jmp_buf * e
, PLINE
* afst
, POLYAREA
** contours
,
2200 PLINE
** holes
, int action
, BOOLp maybe
)
2202 PLINE
**cur
, **next
;
2204 for (cur
= &afst
; *cur
!= NULL
; cur
= next
)
2206 next
= &((*cur
)->next
);
2207 /* if we disappear a contour, don't advance twice */
2208 if (cntr_Collect (e
, cur
, contours
, holes
, action
, NULL
, NULL
, NULL
))
2214 M_POLYAREA_Collect (jmp_buf * e
, POLYAREA
* afst
, POLYAREA
** contours
,
2215 PLINE
** holes
, int action
, BOOLp maybe
)
2218 POLYAREA
*parent
= NULL
; /* Quiet compiler warning */
2219 PLINE
**cur
, **next
, *parent_contour
;
2222 while ((a
= a
->f
) != afst
);
2223 /* now the non-intersect parts are collected in temp/holes */
2226 if (maybe
&& a
->contours
->Flags
.status
!= ISECTED
)
2227 parent_contour
= a
->contours
;
2229 parent_contour
= NULL
;
2231 /* Take care of the first contour - so we know if we
2232 * can shortcut reparenting some of its children
2237 next
= &((*cur
)->next
);
2238 /* if we disappear a contour, don't advance twice */
2239 if (cntr_Collect (e
, cur
, contours
, holes
, action
, a
, NULL
, NULL
))
2241 parent
= (*contours
)->b
; /* InsCntr inserts behind the head */
2248 for (; *cur
!= NULL
; cur
= next
)
2250 next
= &((*cur
)->next
);
2251 /* if we disappear a contour, don't advance twice */
2252 if (cntr_Collect (e
, cur
, contours
, holes
, action
, a
, parent
,
2257 while ((a
= a
->f
) != afst
);
2260 /* determine if two polygons touch or overlap */
2262 Touching (POLYAREA
* a
, POLYAREA
* b
)
2267 if ((code
= setjmp (e
)) == 0)
2270 if (!poly_Valid (a
))
2272 if (!poly_Valid (b
))
2275 M_POLYAREA_intersect (&e
, a
, b
, false);
2277 if (M_POLYAREA_label (a
, b
, TRUE
))
2279 if (M_POLYAREA_label (b
, a
, TRUE
))
2282 else if (code
== TOUCHES
)
2287 /* the main clipping routines */
2289 poly_Boolean (const POLYAREA
* a_org
, const POLYAREA
* b_org
,
2290 POLYAREA
** res
, int action
)
2292 POLYAREA
*a
= NULL
, *b
= NULL
;
2294 if (!poly_M_Copy0 (&a
, a_org
) || !poly_M_Copy0 (&b
, b_org
))
2295 return err_no_memory
;
2297 return poly_Boolean_free (a
, b
, res
, action
);
2298 } /* poly_Boolean */
2300 /* just like poly_Boolean but frees the input polys */
2302 poly_Boolean_free (POLYAREA
* ai
, POLYAREA
* bi
, POLYAREA
** res
, int action
)
2304 POLYAREA
*a
= ai
, *b
= bi
;
2305 PLINE
*a_isected
= NULL
;
2306 PLINE
*p
, *holes
= NULL
;
2343 if ((code
= setjmp (e
)) == 0)
2346 assert (poly_Valid (a
));
2347 assert (poly_Valid (b
));
2350 /* intersect needs to make a list of the contours in a and b which are intersected */
2351 M_POLYAREA_intersect (&e
, a
, b
, TRUE
);
2353 /* We could speed things up a lot here if we only processed the relevant contours */
2354 /* NB: Relevant parts of a are labeled below */
2355 M_POLYAREA_label (b
, a
, FALSE
);
2358 M_POLYAREA_update_primary (&e
, res
, &holes
, action
, b
);
2359 M_POLYAREA_separate_isected (&e
, res
, &holes
, &a_isected
);
2360 M_POLYAREA_label_separated (a_isected
, b
, FALSE
);
2361 M_POLYAREA_Collect_separated (&e
, a_isected
, res
, &holes
, action
,
2363 M_B_AREA_Collect (&e
, b
, res
, &holes
, action
);
2366 /* free a_isected */
2367 while ((p
= a_isected
) != NULL
)
2369 a_isected
= p
->next
;
2370 poly_DelContour (&p
);
2373 InsertHoles (&e
, *res
, &holes
);
2375 /* delete holes if any left */
2376 while ((p
= holes
) != NULL
)
2379 poly_DelContour (&p
);
2387 assert (!*res
|| poly_Valid (*res
));
2389 } /* poly_Boolean_free */
2392 clear_marks (POLYAREA
* p
)
2400 for (c
= n
->contours
; c
; c
= c
->next
)
2407 while ((v
= v
->next
) != &c
->head
);
2410 while ((n
= n
->f
) != p
);
2413 /* compute the intersection and subtraction (divides "a" into two pieces)
2414 * and frees the input polys. This assumes that bi is a single simple polygon.
2417 poly_AndSubtract_free (POLYAREA
* ai
, POLYAREA
* bi
,
2418 POLYAREA
** aandb
, POLYAREA
** aminusb
)
2420 POLYAREA
*a
= ai
, *b
= bi
;
2421 PLINE
*p
, *holes
= NULL
;
2428 if ((code
= setjmp (e
)) == 0)
2432 if (!poly_Valid (a
))
2434 if (!poly_Valid (b
))
2437 M_POLYAREA_intersect (&e
, a
, b
, TRUE
);
2439 M_POLYAREA_label (a
, b
, FALSE
);
2440 M_POLYAREA_label (b
, a
, FALSE
);
2442 M_POLYAREA_Collect (&e
, a
, aandb
, &holes
, PBO_ISECT
, FALSE
);
2443 InsertHoles (&e
, *aandb
, &holes
);
2444 assert (poly_Valid (*aandb
));
2445 /* delete holes if any left */
2446 while ((p
= holes
) != NULL
)
2449 poly_DelContour (&p
);
2454 M_POLYAREA_Collect (&e
, a
, aminusb
, &holes
, PBO_SUB
, FALSE
);
2455 InsertHoles (&e
, *aminusb
, &holes
);
2458 assert (poly_Valid (*aminusb
));
2460 /* delete holes if any left */
2461 while ((p
= holes
) != NULL
)
2464 poly_DelContour (&p
);
2471 poly_Free (aminusb
);
2474 assert (!*aandb
|| poly_Valid (*aandb
));
2475 assert (!*aminusb
|| poly_Valid (*aminusb
));
2477 } /* poly_AndSubtract_free */
2480 cntrbox_pointin (PLINE
* c
, Vector p
)
2482 return (p
[0] >= c
->xmin
&& p
[1] >= c
->ymin
&&
2483 p
[0] <= c
->xmax
&& p
[1] <= c
->ymax
);
2488 node_neighbours (VNODE
* a
, VNODE
* b
)
2490 return (a
== b
) || (a
->next
== b
) || (b
->next
== a
) || (a
->next
== b
->next
);
2494 poly_CreateNode (Vector v
)
2500 res
= (VNODE
*) calloc (1, sizeof (VNODE
));
2503 // bzero (res, sizeof (VNODE) - sizeof(Vector));
2511 poly_IniContour (PLINE
* c
)
2515 /* bzero (c, sizeof(PLINE)); */
2516 c
->head
.next
= c
->head
.prev
= &c
->head
;
2517 c
->xmin
= c
->ymin
= 0x7fffffff;
2518 c
->xmax
= c
->ymax
= 0x80000000;
2519 c
->is_round
= FALSE
;
2526 poly_NewContour (Vector v
)
2530 res
= (PLINE
*) calloc (1, sizeof (PLINE
));
2534 poly_IniContour (res
);
2538 Vcopy (res
->head
.point
, v
);
2539 cntrbox_adjust (res
, v
);
2546 poly_ClrContour (PLINE
* c
)
2551 while ((cur
= c
->head
.next
) != &c
->head
)
2553 poly_ExclVertex (cur
);
2556 poly_IniContour (c
);
2560 poly_DelContour (PLINE
** c
)
2566 for (cur
= (*c
)->head
.prev
; cur
!= &(*c
)->head
; cur
= prev
)
2569 if (cur
->cvc_next
!= NULL
)
2571 free (cur
->cvc_next
);
2572 free (cur
->cvc_prev
);
2576 if ((*c
)->head
.cvc_next
!= NULL
)
2578 free ((*c
)->head
.cvc_next
);
2579 free ((*c
)->head
.cvc_prev
);
2581 /* FIXME -- strict aliasing violation. */
2584 rtree_t
*r
= (*c
)->tree
;
2585 r_destroy_tree (&r
);
2587 free (*c
), *c
= NULL
;
2591 poly_PreContour (PLINE
* C
, BOOLp optimize
)
2601 for (c
= (p
= &C
->head
)->next
; c
!= &C
->head
; c
= (p
= c
)->next
)
2603 /* if the previous node is on the same line with this one, we should remove it */
2604 Vsub2 (p1
, c
->point
, p
->point
);
2605 Vsub2 (p2
, c
->next
->point
, c
->point
);
2606 /* If the product below is zero then
2607 * the points on either side of c
2608 * are on the same line!
2609 * So, remove the point c
2612 if (vect_det2 (p1
, p2
) == 0)
2614 poly_ExclVertex (c
);
2621 C
->xmin
= C
->xmax
= C
->head
.point
[0];
2622 C
->ymin
= C
->ymax
= C
->head
.point
[1];
2624 p
= (c
= &C
->head
)->prev
;
2629 /* calculate area for orientation */
2631 (double) (p
->point
[0] - c
->point
[0]) * (p
->point
[1] +
2633 cntrbox_adjust (C
, c
->point
);
2636 while ((c
= (p
= c
)->next
) != &C
->head
);
2638 C
->area
= ABS (area
);
2640 C
->Flags
.orient
= ((area
< 0) ? PLF_INV
: PLF_DIR
);
2641 C
->tree
= make_edge_tree (C
);
2642 } /* poly_PreContour */
2645 flip_cb (const BoxType
* b
, void *cl
)
2647 struct seg
*s
= (struct seg
*) b
;
2653 poly_InvContour (PLINE
* c
)
2663 cur
->next
= cur
->prev
;
2665 /* fix the segment tree */
2667 while ((cur
= next
) != &c
->head
);
2668 c
->Flags
.orient
^= 1;
2671 r
= r_search (c
->tree
, NULL
, NULL
, flip_cb
, NULL
);
2672 assert (r
== c
->Count
);
2677 poly_ExclVertex (VNODE
* node
)
2679 assert (node
!= NULL
);
2682 free (node
->cvc_next
);
2683 free (node
->cvc_prev
);
2685 node
->prev
->next
= node
->next
;
2686 node
->next
->prev
= node
->prev
;
2690 poly_InclVertex (VNODE
* after
, VNODE
* node
)
2693 assert (after
!= NULL
);
2694 assert (node
!= NULL
);
2697 node
->next
= after
->next
;
2698 after
->next
= after
->next
->prev
= node
;
2699 /* remove points on same line */
2700 if (node
->prev
->prev
== node
)
2701 return; /* we don't have 3 points in the poly yet */
2702 a
= (node
->point
[1] - node
->prev
->prev
->point
[1]);
2703 a
*= (node
->prev
->point
[0] - node
->prev
->prev
->point
[0]);
2704 b
= (node
->point
[0] - node
->prev
->prev
->point
[0]);
2705 b
*= (node
->prev
->point
[1] - node
->prev
->prev
->point
[1]);
2706 if (fabs (a
- b
) < EPSILON
)
2708 VNODE
*t
= node
->prev
;
2709 t
->prev
->next
= node
;
2710 node
->prev
= t
->prev
;
2716 poly_CopyContour (PLINE
** dst
, PLINE
* src
)
2718 VNODE
*cur
, *newnode
;
2720 assert (src
!= NULL
);
2722 *dst
= poly_NewContour (src
->head
.point
);
2726 (*dst
)->Count
= src
->Count
;
2727 (*dst
)->Flags
.orient
= src
->Flags
.orient
;
2728 (*dst
)->xmin
= src
->xmin
, (*dst
)->xmax
= src
->xmax
;
2729 (*dst
)->ymin
= src
->ymin
, (*dst
)->ymax
= src
->ymax
;
2730 (*dst
)->area
= src
->area
;
2732 for (cur
= src
->head
.next
; cur
!= &src
->head
; cur
= cur
->next
)
2734 if ((newnode
= poly_CreateNode (cur
->point
)) == NULL
)
2736 // newnode->Flags = cur->Flags;
2737 poly_InclVertex ((*dst
)->head
.prev
, newnode
);
2739 (*dst
)->tree
= make_edge_tree (*dst
);
2743 /**********************************************************************/
2744 /* polygon routines */
2747 poly_Copy0 (POLYAREA
** dst
, const POLYAREA
* src
)
2751 *dst
= calloc (1, sizeof (POLYAREA
));
2754 (*dst
)->contour_tree
= r_create_tree (NULL
, 0, 0);
2756 return poly_Copy1 (*dst
, src
);
2760 poly_Copy1 (POLYAREA
* dst
, const POLYAREA
* src
)
2762 PLINE
*cur
, **last
= &dst
->contours
;
2765 dst
->f
= dst
->b
= dst
;
2767 for (cur
= src
->contours
; cur
!= NULL
; cur
= cur
->next
)
2769 if (!poly_CopyContour (last
, cur
))
2771 r_insert_entry (dst
->contour_tree
, (BoxTypePtr
) * last
, 0);
2772 last
= &(*last
)->next
;
2778 poly_M_Incl (POLYAREA
** list
, POLYAREA
* a
)
2781 a
->f
= a
->b
= a
, *list
= a
;
2786 (*list
)->b
= (*list
)->b
->f
= a
;
2791 poly_M_Copy0 (POLYAREA
** dst
, const POLYAREA
* srcfst
)
2793 const POLYAREA
*src
= srcfst
;
2801 if ((di
= poly_Create ()) == NULL
|| !poly_Copy1 (di
, src
))
2803 poly_M_Incl (dst
, di
);
2805 while ((src
= src
->f
) != srcfst
);
2810 poly_InclContour (POLYAREA
* p
, PLINE
* c
)
2814 if ((c
== NULL
) || (p
== NULL
))
2816 if (c
->Flags
.orient
== PLF_DIR
)
2818 if (p
->contours
!= NULL
)
2824 if (p
->contours
== NULL
)
2826 /* link at front of hole list */
2827 tmp
= p
->contours
->next
;
2828 p
->contours
->next
= c
;
2831 r_insert_entry (p
->contour_tree
, (BoxTypePtr
) c
, 0);
2844 crossing (const BoxType
* b
, void *cl
)
2846 struct seg
*s
= (struct seg
*) b
;
2847 struct pip
*p
= (struct pip
*) cl
;
2849 if (s
->v
->point
[1] <= p
->p
[1])
2851 if (s
->v
->next
->point
[1] > p
->p
[1])
2855 Vsub2 (v1
, s
->v
->next
->point
, s
->v
->point
);
2856 Vsub2 (v2
, p
->p
, s
->v
->point
);
2857 cross
= (long long) v1
[0] * v2
[1] - (long long) v2
[0] * v1
[1];
2861 longjmp (p
->env
, 1);
2869 if (s
->v
->next
->point
[1] <= p
->p
[1])
2873 Vsub2 (v1
, s
->v
->next
->point
, s
->v
->point
);
2874 Vsub2 (v2
, p
->p
, s
->v
->point
);
2875 cross
= (long long) v1
[0] * v2
[1] - (long long) v2
[0] * v1
[1];
2879 longjmp (p
->env
, 1);
2889 poly_InsideContour (PLINE
* c
, Vector p
)
2894 if (!cntrbox_pointin (c
, p
))
2897 info
.p
[0] = ray
.X1
= p
[0];
2898 info
.p
[1] = ray
.Y1
= p
[1];
2899 ray
.X2
= 0x7fffffff;
2901 if (setjmp (info
.env
) == 0)
2902 r_search (c
->tree
, &ray
, NULL
, crossing
, &info
);
2907 poly_CheckInside (POLYAREA
* p
, Vector v0
)
2911 if ((p
== NULL
) || (v0
== NULL
) || (p
->contours
== NULL
))
2914 if (poly_InsideContour (cur
, v0
))
2916 for (cur
= cur
->next
; cur
!= NULL
; cur
= cur
->next
)
2917 if (poly_InsideContour (cur
, v0
))
2925 poly_M_CheckInside (POLYAREA
* p
, Vector v0
)
2929 if ((p
== NULL
) || (v0
== NULL
))
2934 if (poly_CheckInside (cur
, v0
))
2937 while ((cur
= cur
->f
) != p
);
2942 dot (Vector A
, Vector B
)
2944 return (double)A
[0] * (double)B
[0] + (double)A
[1] * (double)B
[1];
2947 /* Compute whether point is inside a triangle formed by 3 other pints */
2948 /* Algorithm from http://www.blackpawn.com/texts/pointinpoly/default.html */
2950 point_in_triangle (Vector A
, Vector B
, Vector C
, Vector P
)
2953 double dot00
, dot01
, dot02
, dot11
, dot12
;
2957 /* Compute vectors */
2958 v0
[0] = C
[0] - A
[0]; v0
[1] = C
[1] - A
[1];
2959 v1
[0] = B
[0] - A
[0]; v1
[1] = B
[1] - A
[1];
2960 v2
[0] = P
[0] - A
[0]; v2
[1] = P
[1] - A
[1];
2962 /* Compute dot products */
2963 dot00
= dot (v0
, v0
);
2964 dot01
= dot (v0
, v1
);
2965 dot02
= dot (v0
, v2
);
2966 dot11
= dot (v1
, v1
);
2967 dot12
= dot (v1
, v2
);
2969 /* Compute barycentric coordinates */
2970 invDenom
= 1. / (dot00
* dot11
- dot01
* dot01
);
2971 u
= (dot11
* dot02
- dot01
* dot12
) * invDenom
;
2972 v
= (dot00
* dot12
- dot01
* dot02
) * invDenom
;
2974 /* Check if point is in triangle */
2975 return (u
> 0.0) && (v
> 0.0) && (u
+ v
< 1.0);
2979 /* Returns the dot product of Vector A->B, and a vector
2980 * orthogonal to Vector C->D. The result is not normalisd, so will be
2981 * weighted by the magnitude of the C->D vector.
2984 dot_orthogonal_to_direction (Vector A
, Vector B
, Vector C
, Vector D
)
2987 l1
[0] = B
[0] - A
[0]; l1
[1] = B
[1] - A
[1];
2988 l2
[0] = D
[0] - C
[0]; l2
[1] = D
[1] - C
[1];
2993 return dot (l1
, l3
);
2996 /* Algorithm from http://www.exaflop.org/docs/cgafaq/cga2.html
2998 * "Given a simple polygon, find some point inside it. Here is a method based
2999 * on the proof that there exists an internal diagonal, in [O'Rourke, 13-14].
3000 * The idea is that the midpoint of a diagonal is interior to the polygon.
3002 * 1. Identify a convex vertex v; let its adjacent vertices be a and b.
3003 * 2. For each other vertex q do:
3004 * 2a. If q is inside avb, compute distance to v (orthogonal to ab).
3005 * 2b. Save point q if distance is a new min.
3006 * 3. If no point is inside, return midpoint of ab, or centroid of avb.
3007 * 4. Else if some point inside, qv is internal: return its midpoint."
3009 * [O'Rourke]: Computational Geometry in C (2nd Ed.)
3010 * Joseph O'Rourke, Cambridge University Press 1998,
3011 * ISBN 0-521-64010-5 Pbk, ISBN 0-521-64976-5 Hbk
3014 poly_ComputeInteriorPoint (PLINE
*poly
, Vector v
)
3016 VNODE
*pt1
, *pt2
, *pt3
, *q
;
3017 VNODE
*min_q
= NULL
;
3020 double dir
= (poly
->Flags
.orient
== PLF_DIR
) ? 1. : -1;
3022 /* Find a convex node on the polygon */
3031 dot_product
= dot_orthogonal_to_direction (pt1
->point
, pt2
->point
,
3032 pt3
->point
, pt2
->point
);
3034 if (dot_product
* dir
> 0.)
3037 while ((pt1
= pt1
->next
) != &poly
->head
);
3039 /* Loop over remaining vertices */
3043 /* Is current vertex "q" outside pt1 pt2 pt3 triangle? */
3044 if (!point_in_triangle (pt1
->point
, pt2
->point
, pt3
->point
, q
->point
))
3047 /* NO: compute distance to pt2 (v) orthogonal to pt1 - pt3) */
3048 /* Record minimum */
3049 dist
= dot_orthogonal_to_direction (q
->point
, pt2
->point
, pt1
->point
, pt3
->point
);
3050 if (min_q
== NULL
|| dist
< min_dist
) {
3055 while ((q
= q
->next
) != pt2
);
3057 /* Were any "q" found inside pt1 pt2 pt3? */
3058 if (min_q
== NULL
) {
3059 /* NOT FOUND: Return midpoint of pt1 pt3 */
3060 v
[0] = (pt1
->point
[0] + pt3
->point
[0]) / 2;
3061 v
[1] = (pt1
->point
[1] + pt3
->point
[1]) / 2;
3063 /* FOUND: Return mid point of min_q, pt2 */
3064 v
[0] = (pt2
->point
[0] + min_q
->point
[0]) / 2;
3065 v
[1] = (pt2
->point
[1] + min_q
->point
[1]) / 2;
3070 /* NB: This function assumes the caller _knows_ the contours do not
3071 * intersect. If the contours intersect, the result is undefined.
3072 * It will return the correct result if the two contours share
3073 * common points beteween their contours. (Identical contours
3074 * are treated as being inside each other).
3077 poly_ContourInContour (PLINE
* poly
, PLINE
* inner
)
3080 assert (poly
!= NULL
);
3081 assert (inner
!= NULL
);
3082 if (cntrbox_inside (inner
, poly
))
3084 /* We need to prove the "inner" contour is not outside
3085 * "poly" contour. If it is outside, we can return.
3087 if (!poly_InsideContour (poly
, inner
->head
.point
))
3090 poly_ComputeInteriorPoint (inner
, point
);
3091 return poly_InsideContour (poly
, point
);
3097 poly_Init (POLYAREA
* p
)
3101 p
->contour_tree
= r_create_tree (NULL
, 0, 0);
3109 if ((res
= malloc (sizeof (POLYAREA
))) != NULL
)
3115 poly_FreeContours (PLINE
** pline
)
3119 while ((pl
= *pline
) != NULL
)
3122 poly_DelContour (&pl
);
3127 poly_Free (POLYAREA
** p
)
3133 for (cur
= (*p
)->f
; cur
!= *p
; cur
= (*p
)->f
)
3135 poly_FreeContours (&cur
->contours
);
3136 r_destroy_tree (&cur
->contour_tree
);
3141 poly_FreeContours (&cur
->contours
);
3142 r_destroy_tree (&cur
->contour_tree
);
3143 free (*p
), *p
= NULL
;
3147 inside_sector (VNODE
* pn
, Vector p2
)
3149 Vector cdir
, ndir
, pdir
;
3152 assert (pn
!= NULL
);
3153 vect_sub (cdir
, p2
, pn
->point
);
3154 vect_sub (pdir
, pn
->point
, pn
->prev
->point
);
3155 vect_sub (ndir
, pn
->next
->point
, pn
->point
);
3157 p_c
= vect_det2 (pdir
, cdir
) >= 0;
3158 n_c
= vect_det2 (ndir
, cdir
) >= 0;
3159 p_n
= vect_det2 (pdir
, ndir
) >= 0;
3161 if ((p_n
&& p_c
&& n_c
) || ((!p_n
) && (p_c
|| n_c
)))
3165 } /* inside_sector */
3167 /* returns TRUE if bad contour */
3169 poly_ChkContour (PLINE
* a
)
3171 VNODE
*a1
, *a2
, *hit1
, *hit2
;
3182 if (!node_neighbours (a1
, a2
) &&
3183 (icnt
= vect_inters2 (a1
->point
, a1
->next
->point
,
3184 a2
->point
, a2
->next
->point
, i1
, i2
)) > 0)
3189 if (vect_dist2 (i1
, a1
->point
) < EPSILON
)
3191 else if (vect_dist2 (i1
, a1
->next
->point
) < EPSILON
)
3196 if (vect_dist2 (i1
, a2
->point
) < EPSILON
)
3198 else if (vect_dist2 (i1
, a2
->next
->point
) < EPSILON
)
3203 if ((hit1
== NULL
) && (hit2
== NULL
))
3205 /* If the intersection didn't land on an end-point of either
3206 * line, we know the lines cross and we return TRUE.
3210 else if (hit1
== NULL
)
3212 /* An end-point of the second line touched somewhere along the
3213 length of the first line. Check where the second line leads. */
3214 if (inside_sector (hit2
, a1
->point
) !=
3215 inside_sector (hit2
, a1
->next
->point
))
3218 else if (hit2
== NULL
)
3220 /* An end-point of the first line touched somewhere along the
3221 length of the second line. Check where the first line leads. */
3222 if (inside_sector (hit1
, a2
->point
) !=
3223 inside_sector (hit1
, a2
->next
->point
))
3228 /* Both lines intersect at an end-point. Check where they lead. */
3229 if (inside_sector (hit1
, hit2
->prev
->point
) !=
3230 inside_sector (hit1
, hit2
->next
->point
))
3235 while ((a2
= a2
->next
) != &a
->head
);
3237 while ((a1
= a1
->next
) != &a
->head
);
3243 poly_Valid (POLYAREA
* p
)
3247 if ((p
== NULL
) || (p
->contours
== NULL
))
3250 if (p
->contours
->Flags
.orient
== PLF_INV
|| poly_ChkContour (p
->contours
))
3254 DEBUGP ("Invalid Outer PLINE\n");
3255 if (p
->contours
->Flags
.orient
== PLF_INV
)
3256 DEBUGP ("failed orient\n");
3257 if (poly_ChkContour (p
->contours
))
3258 DEBUGP ("failed self-intersection\n");
3259 v
= &p
->contours
->head
;
3263 fprintf (stderr
, "Line [%d %d %d %d 100 100 \"\"]\n",
3264 v
->point
[0], v
->point
[1], n
->point
[0], n
->point
[1]);
3266 while ((v
= v
->next
) != &p
->contours
->head
);
3270 for (c
= p
->contours
->next
; c
!= NULL
; c
= c
->next
)
3272 if (c
->Flags
.orient
== PLF_DIR
||
3273 poly_ChkContour (c
) || !poly_ContourInContour (p
->contours
, c
))
3277 DEBUGP ("Invalid Inner PLINE orient = %d\n", c
->Flags
.orient
);
3278 if (c
->Flags
.orient
== PLF_DIR
)
3279 DEBUGP ("failed orient\n");
3280 if (poly_ChkContour (c
))
3281 DEBUGP ("failed self-intersection\n");
3282 if (!poly_ContourInContour (p
->contours
, c
))
3283 DEBUGP ("failed containment\n");
3288 fprintf (stderr
, "Line [%d %d %d %d 100 100 \"\"]\n",
3289 v
->point
[0], v
->point
[1], n
->point
[0], n
->point
[1]);
3291 while ((v
= v
->next
) != &c
->head
);
3300 Vector vect_zero
= { (long) 0, (long) 0 };
3302 /*********************************************************************/
3303 /* L o n g V e c t o r S t u f f */
3304 /*********************************************************************/
3307 vect_init (Vector v
, double x
, double y
)
3313 #define Vzero(a) ((a)[0] == 0. && (a)[1] == 0.)
3315 #define Vsub(a,b,c) {(a)[0]=(b)[0]-(c)[0];(a)[1]=(b)[1]-(c)[1];}
3318 vect_equal (Vector v1
, Vector v2
)
3320 return (v1
[0] == v2
[0] && v1
[1] == v2
[1]);
3325 vect_sub (Vector res
, Vector v1
, Vector v2
)
3327 Vsub (res
, v1
, v2
)} /* vect_sub */
3330 vect_min (Vector v1
, Vector v2
, Vector v3
)
3332 v1
[0] = (v2
[0] < v3
[0]) ? v2
[0] : v3
[0];
3333 v1
[1] = (v2
[1] < v3
[1]) ? v2
[1] : v3
[1];
3337 vect_max (Vector v1
, Vector v2
, Vector v3
)
3339 v1
[0] = (v2
[0] > v3
[0]) ? v2
[0] : v3
[0];
3340 v1
[1] = (v2
[1] > v3
[1]) ? v2
[1] : v3
[1];
3344 vect_len2 (Vector v
)
3346 return ((double) v
[0] * v
[0] + (double) v
[1] * v
[1]); /* why sqrt? only used for compares */
3350 vect_dist2 (Vector v1
, Vector v2
)
3352 double dx
= v1
[0] - v2
[0];
3353 double dy
= v1
[1] - v2
[1];
3355 return (dx
* dx
+ dy
* dy
); /* why sqrt */
3358 /* value has sign of angle between vectors */
3360 vect_det2 (Vector v1
, Vector v2
)
3362 return (((double) v1
[0] * v2
[1]) - ((double) v2
[0] * v1
[1]));
3366 vect_m_dist (Vector v1
, Vector v2
)
3368 double dx
= v1
[0] - v2
[0];
3369 double dy
= v1
[1] - v2
[1];
3370 double dd
= (dx
* dx
+ dy
* dy
); /* sqrt */
3383 (C) 1993 Klamer Schutte
3384 (C) 1997 Michael Leonov, Alexey Nikitin
3388 vect_inters2 (Vector p1
, Vector p2
, Vector q1
, Vector q2
,
3389 Vector S1
, Vector S2
)
3392 double rpx
, rpy
, rqx
, rqy
;
3394 if (max (p1
[0], p2
[0]) < min (q1
[0], q2
[0]) ||
3395 max (q1
[0], q2
[0]) < min (p1
[0], p2
[0]) ||
3396 max (p1
[1], p2
[1]) < min (q1
[1], q2
[1]) ||
3397 max (q1
[1], q2
[1]) < min (p1
[1], p2
[1]))
3400 rpx
= p2
[0] - p1
[0];
3401 rpy
= p2
[1] - p1
[1];
3402 rqx
= q2
[0] - q1
[0];
3403 rqy
= q2
[1] - q1
[1];
3405 deel
= rpy
* rqx
- rpx
* rqy
; /* -vect_det(rp,rq); */
3407 /* coordinates are 30-bit integers so deel will be exactly zero
3408 * if the lines are parallel
3411 if (deel
== 0) /* parallel */
3413 double dc1
, dc2
, d1
, d2
, h
; /* Check to see whether p1-p2 and q1-q2 are on the same line */
3414 Vector hp1
, hq1
, hp2
, hq2
, q1p1
, q1q2
;
3416 Vsub2 (q1p1
, q1
, p1
);
3417 Vsub2 (q1q2
, q1
, q2
);
3420 /* If this product is not zero then p1-p2 and q1-q2 are not on same line! */
3421 if (vect_det2 (q1p1
, q1q2
) != 0)
3423 dc1
= 0; /* m_len(p1 - p1) */
3425 dc2
= vect_m_dist (p1
, p2
);
3426 d1
= vect_m_dist (p1
, q1
);
3427 d2
= vect_m_dist (p1
, q2
);
3429 /* Sorting the independent points from small to large */
3435 { /* hv and h are used as help-variable. */
3437 h
= dc1
, dc1
= dc2
, dc2
= h
;
3442 h
= d1
, d1
= d2
, d2
= h
;
3445 /* Now the line-pieces are compared */
3477 return (Vequ2 (S1
, S2
) ? 1 : 2);
3480 { /* not parallel */
3482 * We have the lines:
3483 * l1: p1 + s(p2 - p1)
3484 * l2: q1 + t(q2 - q1)
3485 * And we want to know the intersection point.
3487 * p1 + s(p2-p1) = q1 + t(q2-q1)
3488 * which is similar to the two equations:
3489 * p1x + s * rpx = q1x + t * rqx
3490 * p1y + s * rpy = q1y + t * rqy
3491 * Multiplying these by rpy resp. rpx gives:
3492 * rpy * p1x + s * rpx * rpy = rpy * q1x + t * rpy * rqx
3493 * rpx * p1y + s * rpx * rpy = rpx * q1y + t * rpx * rqy
3494 * Subtracting these gives:
3495 * rpy * p1x - rpx * p1y = rpy * q1x - rpx * q1y + t * ( rpy * rqx - rpx * rqy )
3496 * So t can be isolated:
3497 * t = (rpy * ( p1x - q1x ) + rpx * ( - p1y + q1y )) / ( rpy * rqx - rpx * rqy )
3498 * and s can be found similarly
3499 * s = (rqy * (q1x - p1x) + rqx * (p1y - q1y))/( rqy * rpx - rqx * rpy)
3502 if (Vequ2 (q1
, p1
) || Vequ2 (q1
, p2
))
3507 else if (Vequ2 (q2
, p1
) || Vequ2 (q2
, p2
))
3514 s
= (rqy
* (p1
[0] - q1
[0]) + rqx
* (q1
[1] - p1
[1])) / deel
;
3515 if (s
< 0 || s
> 1.)
3517 t
= (rpy
* (p1
[0] - q1
[0]) + rpx
* (q1
[1] - p1
[1])) / deel
;
3518 if (t
< 0 || t
> 1.)
3521 S1
[0] = q1
[0] + ROUND (t
* rqx
);
3522 S1
[1] = q1
[1] + ROUND (t
* rqy
);
3526 } /* vect_inters2 */
3528 /* how about expanding polygons so that edges can be arcs rather than
3529 * lines. Consider using the third coordinate to store the radius of the
3530 * arc. The arc would pass through the vertex points. Positive radius
3531 * would indicate the arc bows left (center on right of P1-P2 path)
3532 * negative radius would put the center on the other side. 0 radius
3533 * would mean the edge is a line instead of arc
3534 * The intersections of the two circles centered at the vertex points
3535 * would determine the two possible arc centers. If P2.x > P1.x then
3536 * the center with smaller Y is selected for positive r. If P2.y > P1.y
3537 * then the center with greate X is selected for positive r.
3539 * the vec_inters2() routine would then need to handle line-line
3540 * line-arc and arc-arc intersections.
3542 * perhaps reverse tracing the arc would require look-ahead to check