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 (VNODE
* dest
, Vector po
, int *new_point
)
187 if (vect_equal (po
, dest
->point
))
189 if (vect_equal (po
, dest
->next
->point
))
194 p
= poly_CreateNode (po
);
199 p
->next
= dest
->next
;
200 p
->cvc_prev
= p
->cvc_next
= NULL
;
201 p
->Flags
.status
= UNKNWN
;
202 return (dest
->next
= dest
->next
->prev
= p
);
205 #define ISECT_BAD_PARAM (-1)
206 #define ISECT_NO_MEMORY (-2)
214 new_descriptor (VNODE
* a
, char poly
, char side
)
216 CVCList
*l
= (CVCList
*) malloc (sizeof (CVCList
));
218 register double ang
, dx
, dy
;
226 l
->next
= l
->prev
= l
;
227 if (side
== 'P') /* previous */
228 vect_sub (v
, a
->prev
->point
, a
->point
);
230 vect_sub (v
, a
->next
->point
, a
->point
);
231 /* Uses slope/(slope+1) in quadrant 1 as a proxy for the angle.
232 * It still has the same monotonic sort result
233 * and is far less expensive to compute than the real angle.
235 if (vect_equal (v
, vect_zero
))
239 if (a
->prev
->cvc_prev
== (CVCList
*) - 1)
240 a
->prev
->cvc_prev
= a
->prev
->cvc_next
= NULL
;
241 poly_ExclVertex (a
->prev
);
242 vect_sub (v
, a
->prev
->point
, a
->point
);
246 if (a
->next
->cvc_prev
== (CVCList
*) - 1)
247 a
->next
->cvc_prev
= a
->next
->cvc_next
= NULL
;
248 poly_ExclVertex (a
->next
);
249 vect_sub (v
, a
->next
->point
, a
->point
);
252 assert (!vect_equal (v
, vect_zero
));
253 dx
= fabs ((double) v
[0]);
254 dy
= fabs ((double) v
[1]);
255 ang
= dy
/ (dy
+ dx
);
256 /* now move to the actual quadrant */
257 if (v
[0] < 0 && v
[1] >= 0)
258 ang
= 2.0 - ang
; /* 2nd quadrant */
259 else if (v
[0] < 0 && v
[1] < 0)
260 ang
+= 2.0; /* 3rd quadrant */
261 else if (v
[0] >= 0 && v
[1] < 0)
262 ang
= 4.0 - ang
; /* 4th quadrant */
264 assert (ang
>= 0.0 && ang
<= 4.0);
266 DEBUGP ("node on %c at (%d,%d) assigned angle %g on side %c\n", poly
,
267 a
->point
[0], a
->point
[1], ang
, side
);
276 argument a is a cross-vertex node.
277 argument poly is the polygon it comes from ('A' or 'B')
278 argument side is the side this descriptor goes on ('P' for previous
280 argument start is the head of the list of cvclists
283 insert_descriptor (VNODE
* a
, char poly
, char side
, CVCList
* start
)
285 CVCList
*l
, *new, *big
, *small
;
287 if (!(new = new_descriptor (a
, poly
, side
)))
289 /* search for the CVCList for this point */
292 start
= new; /* return is also new, so we know where start is */
293 start
->head
= new; /* circular list */
302 if (l
->parent
->point
[0] == a
->point
[0]
303 && l
->parent
->point
[1] == a
->point
[1])
304 { /* this CVCList is at our point */
309 if (l
->head
->parent
->point
[0] == start
->parent
->point
[0]
310 && l
->head
->parent
->point
[1] == start
->parent
->point
[1])
312 /* this seems to be a new point */
313 /* link this cvclist to the list of all cvclists */
314 for (; l
->head
!= new; l
= l
->next
)
324 l
= big
= small
= start
;
327 if (l
->next
->angle
< l
->angle
) /* find start/end of list */
332 else if (new->angle
>= l
->angle
&& new->angle
<= l
->next
->angle
)
334 /* insert new cvc if it lies between existing points */
337 l
->next
= l
->next
->prev
= new;
341 while ((l
= l
->next
) != start
);
342 /* didn't find it between points, it must go on an end */
343 if (big
->angle
<= new->angle
)
346 new->next
= big
->next
;
347 big
->next
= big
->next
->prev
= new;
350 assert (small
->angle
>= new->angle
);
352 new->prev
= small
->prev
;
353 small
->prev
= small
->prev
->next
= new;
359 (C) 1993 Klamer Schutte
360 (C) 1997 Alexey Nikitin, Michael Leonov
362 return 1 if new node in b, 2 if new node in a and 3 if new node in both
366 node_add_point (VNODE
* a
, VNODE
* b
, Vector p
)
370 VNODE
*node_a
, *node_b
;
372 node_a
= node_add (a
, p
, &res
);
374 node_b
= node_add (b
, p
, &res
);
376 if (node_a
== NULL
|| node_b
== NULL
)
377 return ISECT_NO_MEMORY
;
378 node_b
->cvc_prev
= node_b
->cvc_next
= (CVCList
*) - 1;
379 node_a
->cvc_prev
= node_a
->cvc_next
= (CVCList
*) - 1;
381 } /* node_add_point */
388 node_label (VNODE
* pn
)
390 CVCList
*first_l
, *l
;
395 assert (pn
->cvc_next
);
396 this_poly
= pn
->cvc_next
->poly
;
397 /* search counter-clockwise in the cross vertex connectivity (CVC) list
399 * check for shared edges (that could be prev or next in the list since the angles are equal)
400 * and check if this edge (pn -> pn->next) is found between the other poly's entry and exit
402 if (pn
->cvc_next
->angle
== pn
->cvc_next
->prev
->angle
)
403 l
= pn
->cvc_next
->prev
;
405 l
= pn
->cvc_next
->next
;
408 while ((l
->poly
== this_poly
) && (l
!= first_l
->prev
))
410 assert (l
->poly
!= this_poly
);
412 assert (l
&& l
->angle
>= 0 && l
->angle
<= 4.0);
413 if (l
->poly
!= this_poly
)
417 if (l
->parent
->prev
->point
[0] == pn
->next
->point
[0] &&
418 l
->parent
->prev
->point
[1] == pn
->next
->point
[1])
421 pn
->shared
= l
->parent
->prev
;
428 if (l
->angle
== pn
->cvc_next
->angle
)
430 assert (l
->parent
->next
->point
[0] == pn
->next
->point
[0] &&
431 l
->parent
->next
->point
[1] == pn
->next
->point
[1]);
433 pn
->shared
= l
->parent
;
439 if (region
== UNKNWN
)
441 for (l
= l
->next
; l
!= pn
->cvc_next
; l
= l
->next
)
443 if (l
->poly
!= this_poly
)
453 assert (region
!= UNKNWN
);
454 assert (NODE_LABEL (pn
) == UNKNWN
|| NODE_LABEL (pn
) == region
);
455 LABEL_NODE (pn
, region
);
456 if (region
== SHARED
|| region
== SHARED2
)
466 add_descriptors (PLINE
* pl
, char poly
, CVCList
* list
)
468 VNODE
*node
= &pl
->head
;
474 assert (node
->cvc_prev
== (CVCList
*) - 1
475 && node
->cvc_next
== (CVCList
*) - 1);
476 list
= node
->cvc_prev
= insert_descriptor (node
, poly
, 'P', list
);
479 list
= node
->cvc_next
= insert_descriptor (node
, poly
, 'N', list
);
484 while ((node
= node
->next
) != &pl
->head
);
489 cntrbox_adjust (PLINE
* c
, Vector p
)
491 c
->xmin
= min (c
->xmin
, p
[0]);
492 c
->xmax
= max (c
->xmax
, p
[0] + 1);
493 c
->ymin
= min (c
->ymin
, p
[1]);
494 c
->ymax
= max (c
->ymax
, p
[1] + 1);
497 /* some structures for handling segment intersections using the rtrees */
512 jmp_buf env
, sego
, *touch
;
517 * (C) 2006 harry eaton
518 * This replaces the segment in the tree with the two new segments after
519 * a vertex has been added
522 adjust_tree (rtree_t
* tree
, struct seg
*s
)
526 q
= malloc (sizeof (struct seg
));
531 q
->box
.X1
= min (q
->v
->point
[0], q
->v
->next
->point
[0]);
532 q
->box
.X2
= max (q
->v
->point
[0], q
->v
->next
->point
[0]) + 1;
533 q
->box
.Y1
= min (q
->v
->point
[1], q
->v
->next
->point
[1]);
534 q
->box
.Y2
= max (q
->v
->point
[1], q
->v
->next
->point
[1]) + 1;
535 r_insert_entry (tree
, (const BoxType
*) q
, 1);
536 q
= malloc (sizeof (struct seg
));
541 q
->box
.X1
= min (q
->v
->point
[0], q
->v
->next
->point
[0]);
542 q
->box
.X2
= max (q
->v
->point
[0], q
->v
->next
->point
[0]) + 1;
543 q
->box
.Y1
= min (q
->v
->point
[1], q
->v
->next
->point
[1]);
544 q
->box
.Y2
= max (q
->v
->point
[1], q
->v
->next
->point
[1]) + 1;
545 r_insert_entry (tree
, (const BoxType
*) q
, 1);
546 r_delete_entry (tree
, (const BoxType
*) s
);
552 * (C) 2006, harry eaton
553 * This prunes the search for boxes that don't intersect the segment.
556 seg_in_region (const BoxType
* b
, void *cl
)
558 struct info
*i
= (struct info
*) cl
;
560 /* for zero slope the search is aligned on the axis so it is already pruned */
563 y1
= i
->m
* b
->X1
+ i
->b
;
564 y2
= i
->m
* b
->X2
+ i
->b
;
565 if (min (y1
, y2
) >= b
->Y2
)
567 if (max (y1
, y2
) < b
->Y1
)
569 return 1; /* might intersect */
574 * (C) 2006 harry eaton
575 * This routine checks if the segment in the tree intersect the search segment.
576 * If it does, the plines are marked as intersected and the point is marked for
577 * the cvclist. If the point is not already a vertex, a new vertex is inserted
578 * and the search for intersections starts over at the beginning.
579 * That is potentially a significant time penalty, but it does solve the snap rounding
580 * problem. There are efficient algorithms for finding intersections with snap
581 * rounding, but I don't have time to implement them right now.
584 seg_in_seg (const BoxType
* b
, void *cl
)
586 struct info
*i
= (struct info
*) cl
;
587 struct seg
*s
= (struct seg
*) b
;
591 cnt
= vect_inters2 (s
->v
->point
, s
->v
->next
->point
,
592 i
->v
->point
, i
->v
->next
->point
, s1
, s2
);
595 if (i
->touch
) /* if checking touches one find and we're done */
596 longjmp (*i
->touch
, TOUCHES
);
597 i
->s
->p
->Flags
.status
= ISECTED
;
598 s
->p
->Flags
.status
= ISECTED
;
601 res
= node_add_point (i
->v
, s
->v
, cnt
> 1 ? s2
: s1
);
603 return 1; /* error */
604 /* adjust the bounding box and tree if necessary */
607 cntrbox_adjust (i
->s
->p
, cnt
> 1 ? s2
: s1
);
608 if (adjust_tree (i
->s
->p
->tree
, i
->s
))
611 /* if we added a node in the tree we need to change the tree */
614 cntrbox_adjust (s
->p
, cnt
> 1 ? s2
: s1
);
615 if (adjust_tree (i
->tree
, s
))
618 if (res
& 3) /* if a point was inserted start over */
620 #ifdef DEBUG_INTERSECT
621 DEBUGP ("new intersection at (%d, %d)\n", cnt
> 1 ? s2
[0] : s1
[0],
622 cnt
> 1 ? s2
[1] : s1
[1]);
631 make_edge_tree (PLINE
* pb
)
635 rtree_t
*ans
= r_create_tree (NULL
, 0, 0);
639 s
= malloc (sizeof (struct seg
));
640 if (bv
->point
[0] < bv
->next
->point
[0])
642 s
->box
.X1
= bv
->point
[0];
643 s
->box
.X2
= bv
->next
->point
[0] + 1;
647 s
->box
.X2
= bv
->point
[0] + 1;
648 s
->box
.X1
= bv
->next
->point
[0];
650 if (bv
->point
[1] < bv
->next
->point
[1])
652 s
->box
.Y1
= bv
->point
[1];
653 s
->box
.Y2
= bv
->next
->point
[1] + 1;
657 s
->box
.Y2
= bv
->point
[1] + 1;
658 s
->box
.Y1
= bv
->next
->point
[1];
662 r_insert_entry (ans
, (const BoxType
*) s
, 1);
664 while ((bv
= bv
->next
) != &pb
->head
);
669 get_seg (const BoxType
* b
, void *cl
)
671 struct info
*i
= (struct info
*) cl
;
672 struct seg
*s
= (struct seg
*) b
;
676 longjmp (i
->sego
, 1);
683 * (C) 2006, harry eaton
684 * This uses an rtree to find A-B intersections. Whenever a new vertex is
685 * added, the search for intersections is re-started because the rounding
686 * could alter the topology otherwise.
687 * This should use a faster algorithm for snap rounding intersection finding.
688 * The best algorthim is probably found in:
690 * "Improved output-sensitive snap rounding," John Hershberger, Proceedings
691 * of the 22nd annual symposium on Computational geomerty, 2006, pp 357-366.
692 * http://doi.acm.org/10.1145/1137856.1137909
694 * Algorithms described by de Berg, or Goodrich or Halperin, or Hobby would
695 * probably work as well.
700 intersect (jmp_buf * jb
, POLYAREA
* b
, POLYAREA
* a
, int add
)
702 PLINE
*pa
, *pb
; /* pline iterators */
705 VNODE
*av
; /* node iterators */
713 setjmp (info
.env
); /* we loop back here whenever a vertex is inserted */
717 while (pa
) /* Loop over the contours of POLYAREA "a" */
719 int found_overlapping_a_b_contour
= FALSE
;
721 while (pb
) /* Loop over the contours of POLYAREA "b" */
723 /* Are there overlapping bounds? */
724 if (pb
->xmin
<= pa
->xmax
&& pb
->xmax
>= pa
->xmin
&&
725 pb
->ymin
<= pa
->ymax
&& pb
->ymax
>= pa
->ymin
)
727 found_overlapping_a_b_contour
= TRUE
;
733 /* If we didn't find anything intersting, move onto the next "a" contour */
734 if (!found_overlapping_a_b_contour
)
741 /* something intersects so check the edges of the contour */
743 /* Pick which contour has the fewer points, and do the loop
744 * over that. The r_tree makes hit-testing against a contour
745 * faster, so we want to do that on the bigger contour.
747 if (pa
->Count
< pb
->Count
)
758 av
= &looping_over
->head
;
759 do /* Loop over the nodes in the smaller contour */
761 /* check this edge for any insertions */
764 /* compute the slant for region trimming */
765 dx
= av
->next
->point
[0] - av
->point
[0];
770 info
.m
= (av
->next
->point
[1] - av
->point
[1]) / dx
;
771 info
.b
= av
->point
[1] - info
.m
* av
->point
[0];
773 box
.X2
= (box
.X1
= av
->point
[0]) + 1;
774 box
.Y2
= (box
.Y1
= av
->point
[1]) + 1;
776 /* fill in the segment in info corresponding to this node */
777 if (setjmp (info
.sego
) == 0)
779 r_search (looping_over
->tree
, &box
, NULL
, get_seg
, &info
);
783 /* NB: If this actually hits anything, we are teleported back to the beginning */
784 info
.tree
= rtree_over
->tree
;
786 if (UNLIKELY (r_search (info
.tree
, &info
.s
->box
,
787 seg_in_region
, seg_in_seg
, &info
)))
788 return err_no_memory
; /* error */
790 while ((av
= av
->next
) != &looping_over
->head
);
792 /* Continue the with the _same_ "a" contour,
793 * testing it against the next "b" contour.
797 } /* end of setjmp loop */
802 M_POLYAREA_intersect (jmp_buf * e
, POLYAREA
* afst
, POLYAREA
* bfst
, int add
)
804 POLYAREA
*a
= afst
, *b
= bfst
;
805 PLINE
*curcA
, *curcB
;
806 CVCList
*the_list
= NULL
;
808 if (a
== NULL
|| b
== NULL
)
809 error (err_bad_parm
);
814 if (a
->contours
->xmax
>= b
->contours
->xmin
&&
815 a
->contours
->ymax
>= b
->contours
->ymin
&&
816 a
->contours
->xmin
<= b
->contours
->xmax
&&
817 a
->contours
->ymin
<= b
->contours
->ymax
)
819 if (UNLIKELY (intersect (e
, a
, b
, add
)))
820 error (err_no_memory
);
823 while (add
&& (a
= a
->f
) != afst
);
824 for (curcB
= b
->contours
; curcB
!= NULL
; curcB
= curcB
->next
)
825 if (curcB
->Flags
.status
== ISECTED
)
827 the_list
= add_descriptors (curcB
, 'B', the_list
);
828 if (UNLIKELY (the_list
== NULL
))
829 error (err_no_memory
);
832 while (add
&& (b
= b
->f
) != bfst
);
835 for (curcA
= a
->contours
; curcA
!= NULL
; curcA
= curcA
->next
)
836 if (curcA
->Flags
.status
== ISECTED
)
838 the_list
= add_descriptors (curcA
, 'A', the_list
);
839 if (UNLIKELY (the_list
== NULL
))
840 error (err_no_memory
);
843 while (add
&& (a
= a
->f
) != afst
);
844 } /* M_POLYAREA_intersect */
847 cntrbox_inside (PLINE
* c1
, PLINE
* c2
)
849 assert (c1
!= NULL
&& c2
!= NULL
);
850 return ((c1
->xmin
>= c2
->xmin
) &&
851 (c1
->ymin
>= c2
->ymin
) &&
852 (c1
->xmax
<= c2
->xmax
) && (c1
->ymax
<= c2
->ymax
));
855 /*****************************************************************/
856 /* Routines for making labels */
858 /* cntr_in_M_POLYAREA
859 returns poly is inside outfst ? TRUE : FALSE */
861 cntr_in_M_POLYAREA (PLINE
* poly
, POLYAREA
* outfst
, BOOLp test
)
864 POLYAREA
*outer
= outfst
;
867 assert (poly
!= NULL
);
868 assert (outer
!= NULL
);
870 heap
= heap_create ();
873 if (cntrbox_inside (poly
, outer
->contours
))
874 heap_insert (heap
, outer
->contours
->area
, (void *) outer
);
876 /* if checking touching, use only the first polygon */
877 while (!test
&& (outer
= outer
->f
) != outfst
);
878 /* we need only check the smallest poly container
879 * but we must loop in case the box containter is not
880 * the poly container */
883 if (heap_is_empty (heap
))
885 outer
= (POLYAREA
*) heap_remove_smallest (heap
);
886 if (poly_ContourInContour (outer
->contours
, poly
))
888 for (curc
= outer
->contours
->next
; curc
!= NULL
; curc
= curc
->next
)
889 if (poly_ContourInContour (curc
, poly
))
891 /* it's inside a hole in the smallest polygon
892 * no need to check the other polygons */
893 heap_destroy (&heap
);
896 heap_destroy (&heap
);
901 heap_destroy (&heap
);
903 } /* cntr_in_M_POLYAREA */
910 static char u
[] = "UNKNOWN";
911 static char i
[] = "INSIDE";
912 static char o
[] = "OUTSIDE";
913 static char s
[] = "SHARED";
914 static char s2
[] = "SHARED2";
916 switch (NODE_LABEL (v
))
931 #ifdef DEBUG_ALL_LABELS
933 print_labels (PLINE
* a
)
939 DEBUGP ("(%d,%d)->(%d,%d) labeled %s\n", c
->point
[0], c
->point
[1],
940 c
->next
->point
[0], c
->next
->point
[1], theState (c
));
942 while ((c
= c
->next
) != &a
->head
);
950 (C) 1993 Klamer Schutte
951 (C) 1997 Alexey Nikitin, Michael Leonov
955 label_contour (PLINE
* a
)
957 VNODE
*cur
= &a
->head
;
958 VNODE
*first_labelled
= NULL
;
963 if (cur
->cvc_next
) /* examine cross vertex */
965 label
= node_label (cur
);
966 if (first_labelled
== NULL
)
967 first_labelled
= cur
;
971 if (first_labelled
== NULL
)
974 /* This labels nodes which aren't cross-connected */
975 assert (label
== INSIDE
|| label
== OUTSIDE
);
976 LABEL_NODE (cur
, label
);
978 while ((cur
= cur
->next
) != first_labelled
);
979 #ifdef DEBUG_ALL_LABELS
984 } /* label_contour */
987 cntr_label_POLYAREA (PLINE
* poly
, POLYAREA
* ppl
, BOOLp test
)
989 assert (ppl
!= NULL
&& ppl
->contours
!= NULL
);
990 if (poly
->Flags
.status
== ISECTED
)
992 label_contour (poly
); /* should never get here when BOOLp is true */
994 else if (cntr_in_M_POLYAREA (poly
, ppl
, test
))
998 poly
->Flags
.status
= INSIDE
;
1004 poly
->Flags
.status
= OUTSIDE
;
1007 } /* cntr_label_POLYAREA */
1010 M_POLYAREA_label (POLYAREA
* afst
, POLYAREA
* b
, BOOLp touch
)
1018 for (curc
= a
->contours
; curc
!= NULL
; curc
= curc
->next
)
1019 if (cntr_label_POLYAREA (curc
, b
, touch
))
1025 while (!touch
&& (a
= a
->f
) != afst
);
1029 /****************************************************************/
1031 /* routines for temporary storing resulting contours */
1033 InsCntr (jmp_buf * e
, PLINE
* c
, POLYAREA
** dst
)
1039 MemGet (*dst
, POLYAREA
);
1040 (*dst
)->f
= (*dst
)->b
= *dst
;
1045 MemGet (newp
, POLYAREA
);
1047 newp
->b
= (*dst
)->b
;
1048 newp
->f
->b
= newp
->b
->f
= newp
;
1055 PutContour (jmp_buf * e
, PLINE
* cntr
, POLYAREA
** contours
, PLINE
** holes
,
1058 assert (cntr
!= NULL
);
1059 assert (cntr
->Count
> 2);
1061 if (cntr
->Flags
.orient
== PLF_DIR
)
1062 InsCntr (e
, cntr
, contours
);
1063 /* put hole into temporary list */
1066 /* if we know this belongs inside the parent, put it there now */
1069 cntr
->next
= parent
->next
;
1070 parent
->next
= cntr
;
1074 cntr
->next
= *holes
;
1075 *holes
= cntr
; /* let cntr be 1st hole in list */
1081 heap_it (const BoxType
* b
, void *cl
)
1083 heap_t
*heap
= (heap_t
*) cl
;
1084 PLINE
*p
= (PLINE
*) b
;
1086 return 0; /* how did this happen? */
1087 heap_insert (heap
, p
->area
, (void *) p
);
1092 InsertHoles (jmp_buf * e
, POLYAREA
* dest
, PLINE
** src
)
1095 PLINE
*curh
, *container
, *tmp
;
1100 return; /* empty hole list */
1102 error (err_bad_parm
); /* empty contour list */
1104 /* make an rtree of contours */
1105 tree
= r_create_tree (NULL
, 0, 0);
1109 r_insert_entry (tree
, (const BoxType
*) curc
->contours
, 0);
1111 while ((curc
= curc
->f
) != dest
);
1112 /* loop through the holes and put them where they belong */
1113 while ((curh
= *src
) != NULL
)
1118 /* build a heap of all of the polys that the hole is inside its bounding box */
1119 heap
= heap_create ();
1120 r_search (tree
, (BoxType
*) curh
, NULL
, heap_it
, heap
);
1121 if (heap_is_empty (heap
))
1128 poly_DelContour (&curh
);
1129 error (err_bad_parm
);
1131 /* Now search the heap for the container. If there was only one item
1132 * in the heap, assume it is the container without the expense of
1135 tmp
= (PLINE
*) heap_remove_smallest (heap
);
1136 if (heap_is_empty (heap
))
1137 { /* only one possibility it must be the right one */
1138 assert (poly_ContourInContour (tmp
, curh
));
1145 if (poly_ContourInContour (tmp
, curh
))
1150 if (heap_is_empty (heap
))
1152 tmp
= (PLINE
*) heap_remove_smallest (heap
);
1156 heap_destroy (&heap
);
1157 if (container
== NULL
)
1159 /* bad input polygons were given */
1166 poly_DelContour (&curh
);
1167 error (err_bad_parm
);
1171 /* link at front of hole list */
1172 tmp
= container
->next
;
1173 container
->next
= curh
;
1177 r_destroy_tree (&tree
);
1181 /****************************************************************/
1182 /* routines for collecting result */
1190 typedef int (*S_Rule
) (VNODE
*, DIRECTION
*);
1193 typedef int (*J_Rule
) (char, VNODE
*, DIRECTION
*);
1196 UniteS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1199 return (NODE_LABEL (cur
) == OUTSIDE
) || (NODE_LABEL (cur
) == SHARED
);
1203 IsectS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1206 return (NODE_LABEL (cur
) == INSIDE
) || (NODE_LABEL (cur
) == SHARED
);
1210 SubS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1213 return (NODE_LABEL (cur
) == OUTSIDE
) || (NODE_LABEL (cur
) == SHARED2
);
1217 XorS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1219 if (cur
->Flags
.status
== INSIDE
)
1224 if (cur
->Flags
.status
== OUTSIDE
)
1233 IsectJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1235 assert (*cdir
== FORW
);
1236 return (v
->Flags
.status
== INSIDE
|| v
->Flags
.status
== SHARED
);
1240 UniteJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1242 assert (*cdir
== FORW
);
1243 return (v
->Flags
.status
== OUTSIDE
|| v
->Flags
.status
== SHARED
);
1247 XorJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1249 if (v
->Flags
.status
== INSIDE
)
1254 if (v
->Flags
.status
== OUTSIDE
)
1263 SubJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1265 if (p
== 'B' && v
->Flags
.status
== INSIDE
)
1270 if (p
== 'A' && v
->Flags
.status
== OUTSIDE
)
1275 if (v
->Flags
.status
== SHARED2
)
1286 /* return the edge that comes next.
1287 * if the direction is BACKW, then we return the next vertex
1288 * so that prev vertex has the flags for the edge
1290 * returns true if an edge is found, false otherwise
1293 jump (VNODE
** cur
, DIRECTION
* cdir
, J_Rule rule
)
1299 if (!(*cur
)->cvc_prev
) /* not a cross-vertex */
1301 if (*cdir
== FORW
? (*cur
)->Flags
.mark
: (*cur
)->prev
->Flags
.mark
)
1306 DEBUGP ("jump entering node at (%d, %d)\n", (*cur
)->point
[0],
1310 d
= (*cur
)->cvc_prev
->prev
;
1312 d
= (*cur
)->cvc_next
->prev
;
1320 if (!e
->Flags
.mark
&& rule (d
->poly
, e
, &new))
1322 if ((d
->side
== 'N' && new == FORW
) ||
1323 (d
->side
== 'P' && new == BACKW
))
1327 DEBUGP ("jump leaving node at (%d, %d)\n",
1328 e
->next
->point
[0], e
->next
->point
[1]);
1330 DEBUGP ("jump leaving node at (%d, %d)\n",
1331 e
->point
[0], e
->point
[1]);
1339 while ((d
= d
->prev
) != start
);
1344 Gather (VNODE
* start
, PLINE
** result
, J_Rule v_rule
, DIRECTION initdir
)
1346 VNODE
*cur
= start
, *newn
;
1347 DIRECTION dir
= initdir
;
1349 DEBUGP ("gather direction = %d\n", dir
);
1351 assert (*result
== NULL
);
1354 /* see where to go next */
1355 if (!jump (&cur
, &dir
, v_rule
))
1357 /* add edge to polygon */
1360 *result
= poly_NewContour (cur
->point
);
1361 if (*result
== NULL
)
1362 return err_no_memory
;
1366 if ((newn
= poly_CreateNode (cur
->point
)) == NULL
)
1367 return err_no_memory
;
1368 poly_InclVertex ((*result
)->head
.prev
, newn
);
1371 DEBUGP ("gather vertex at (%d, %d)\n", cur
->point
[0], cur
->point
[1]);
1373 /* Now mark the edge as included. */
1374 newn
= (dir
== FORW
? cur
: cur
->prev
);
1375 newn
->Flags
.mark
= 1;
1376 /* for SHARED edge mark both */
1378 newn
->shared
->Flags
.mark
= 1;
1380 /* Advance to the next edge. */
1381 cur
= (dir
== FORW
? cur
->next
: cur
->prev
);
1388 Collect1 (jmp_buf * e
, VNODE
* cur
, DIRECTION dir
, POLYAREA
** contours
,
1389 PLINE
** holes
, J_Rule j_rule
)
1391 PLINE
*p
= NULL
; /* start making contour */
1394 Gather (dir
== FORW
? cur
: cur
->next
, &p
, j_rule
, dir
)) != err_ok
)
1397 poly_DelContour (&p
);
1402 poly_PreContour (p
, TRUE
);
1406 DEBUGP ("adding contour with %d verticies and direction %c\n",
1407 p
->Count
, p
->Flags
.orient
? 'F' : 'B');
1409 PutContour (e
, p
, contours
, holes
, NULL
);
1413 /* some sort of computation error ? */
1415 DEBUGP ("Bad contour! Not enough points!!\n");
1417 poly_DelContour (&p
);
1422 Collect (jmp_buf * e
, PLINE
* a
, POLYAREA
** contours
, PLINE
** holes
,
1423 S_Rule s_rule
, J_Rule j_rule
)
1431 if (s_rule (cur
, &dir
) && cur
->Flags
.mark
== 0)
1432 Collect1 (e
, cur
, dir
, contours
, holes
, j_rule
);
1434 if ((other
->cvc_prev
&& jump (&other
, &dir
, j_rule
)))
1435 Collect1 (e
, other
, dir
, contours
, holes
, j_rule
);
1437 while ((cur
= cur
->next
) != &a
->head
);
1442 cntr_Collect (jmp_buf * e
, PLINE
** A
, POLYAREA
** contours
, PLINE
** holes
,
1443 int action
, PLINE
* parent
)
1447 if ((*A
)->Flags
.status
== ISECTED
)
1452 Collect (e
, *A
, contours
, holes
, UniteS_Rule
, UniteJ_Rule
);
1455 Collect (e
, *A
, contours
, holes
, IsectS_Rule
, IsectJ_Rule
);
1458 Collect (e
, *A
, contours
, holes
, XorS_Rule
, XorJ_Rule
);
1461 Collect (e
, *A
, contours
, holes
, SubS_Rule
, SubJ_Rule
);
1470 if ((*A
)->Flags
.status
== INSIDE
)
1473 /* disappear this contour */
1475 tmprev
->next
= NULL
;
1476 PutContour (e
, tmprev
, contours
, holes
, NULL
);
1481 if ((*A
)->Flags
.status
== INSIDE
)
1484 /* disappear this contour */
1486 tmprev
->next
= NULL
;
1487 poly_InvContour (tmprev
);
1488 PutContour (e
, tmprev
, contours
, holes
, NULL
);
1491 /* break; *//* Fall through (I think this is correct! pcjc2) */
1494 if ((*A
)->Flags
.status
== OUTSIDE
)
1497 /* disappear this contour */
1499 tmprev
->next
= NULL
;
1500 PutContour (e
, tmprev
, contours
, holes
, parent
);
1507 } /* cntr_Collect */
1510 M_B_AREA_Collect (jmp_buf * e
, POLYAREA
* bfst
, POLYAREA
** contours
,
1511 PLINE
** holes
, int action
)
1514 PLINE
**cur
, **next
, *tmp
;
1519 for (cur
= &b
->contours
; *cur
!= NULL
; cur
= next
)
1521 next
= &((*cur
)->next
);
1522 if ((*cur
)->Flags
.status
== ISECTED
)
1525 if ((*cur
)->Flags
.status
== INSIDE
)
1530 poly_InvContour (*cur
);
1536 tmp
->Flags
.status
= UNKNWN
;
1537 PutContour (e
, tmp
, contours
, holes
, NULL
);
1540 break; /* nothing to do - already included */
1542 else if ((*cur
)->Flags
.status
== OUTSIDE
)
1552 tmp
->Flags
.status
= UNKNWN
;
1553 PutContour (e
, tmp
, contours
, holes
, NULL
);
1557 break; /* do nothing, not included */
1561 while ((b
= b
->f
) != bfst
);
1566 M_POLYAREA_Collect (jmp_buf * e
, POLYAREA
* afst
, POLYAREA
** contours
,
1567 PLINE
** holes
, int action
, BOOLp maybe
)
1570 PLINE
**cur
, **next
, *parent
;
1573 while ((a
= a
->f
) != afst
);
1574 /* now the non-intersect parts are collected in temp/holes */
1577 if (maybe
&& a
->contours
->Flags
.status
!= ISECTED
)
1578 parent
= a
->contours
;
1581 for (cur
= &a
->contours
; *cur
!= NULL
; cur
= next
)
1583 next
= &((*cur
)->next
);
1584 /* if we disappear a contour, don't advance twice */
1586 (e
, cur
, contours
, holes
, action
,
1587 *cur
== parent
? NULL
: parent
))
1591 while ((a
= a
->f
) != afst
);
1594 /* determine if two polygons touch or overlap */
1596 Touching (POLYAREA
* a
, POLYAREA
* b
)
1601 if ((code
= setjmp (e
)) == 0)
1604 if (!poly_Valid (a
))
1606 if (!poly_Valid (b
))
1609 M_POLYAREA_intersect (&e
, a
, b
, false);
1611 if (M_POLYAREA_label (a
, b
, TRUE
))
1613 if (M_POLYAREA_label (b
, a
, TRUE
))
1616 else if (code
== TOUCHES
)
1621 /* the main clipping routines */
1623 poly_Boolean (const POLYAREA
* a_org
, const POLYAREA
* b_org
,
1624 POLYAREA
** res
, int action
)
1626 POLYAREA
*a
= NULL
, *b
= NULL
;
1628 if (!poly_M_Copy0 (&a
, a_org
) || !poly_M_Copy0 (&b
, b_org
))
1629 return err_no_memory
;
1631 return poly_Boolean_free (a
, b
, res
, action
);
1632 } /* poly_Boolean */
1634 /* just like poly_Boolean but frees the input polys */
1636 poly_Boolean_free (POLYAREA
* ai
, POLYAREA
* bi
, POLYAREA
** res
, int action
)
1638 POLYAREA
*a
= ai
, *b
= bi
;
1639 PLINE
*p
, *holes
= NULL
;
1676 if ((code
= setjmp (e
)) == 0)
1679 assert (poly_Valid (a
));
1680 assert (poly_Valid (b
));
1683 M_POLYAREA_intersect (&e
, a
, b
, TRUE
);
1685 M_POLYAREA_label (a
, b
, FALSE
);
1686 M_POLYAREA_label (b
, a
, FALSE
);
1688 M_POLYAREA_Collect (&e
, a
, res
, &holes
, action
, b
->f
== b
1689 && !b
->contours
->next
1690 && b
->contours
->Flags
.status
!= ISECTED
);
1692 M_B_AREA_Collect (&e
, b
, res
, &holes
, action
);
1695 InsertHoles (&e
, *res
, &holes
);
1697 /* delete holes if any left */
1698 while ((p
= holes
) != NULL
)
1701 poly_DelContour (&p
);
1709 assert (!*res
|| poly_Valid (*res
));
1711 } /* poly_Boolean_free */
1714 clear_marks (POLYAREA
* p
)
1722 for (c
= n
->contours
; c
; c
= c
->next
)
1729 while ((v
= v
->next
) != &c
->head
);
1732 while ((n
= n
->f
) != p
);
1735 /* compute the intersection and subtraction (divides "a" into two pieces)
1736 * and frees the input polys. This assumes that bi is a single simple polygon.
1739 poly_AndSubtract_free (POLYAREA
* ai
, POLYAREA
* bi
,
1740 POLYAREA
** aandb
, POLYAREA
** aminusb
)
1742 POLYAREA
*a
= ai
, *b
= bi
;
1743 PLINE
*p
, *holes
= NULL
;
1750 if ((code
= setjmp (e
)) == 0)
1754 if (!poly_Valid (a
))
1756 if (!poly_Valid (b
))
1759 M_POLYAREA_intersect (&e
, a
, b
, TRUE
);
1761 M_POLYAREA_label (a
, b
, FALSE
);
1762 M_POLYAREA_label (b
, a
, FALSE
);
1764 M_POLYAREA_Collect (&e
, a
, aandb
, &holes
, PBO_ISECT
, FALSE
);
1765 InsertHoles (&e
, *aandb
, &holes
);
1766 assert (poly_Valid (*aandb
));
1767 /* delete holes if any left */
1768 while ((p
= holes
) != NULL
)
1771 poly_DelContour (&p
);
1776 M_POLYAREA_Collect (&e
, a
, aminusb
, &holes
, PBO_SUB
, FALSE
);
1777 InsertHoles (&e
, *aminusb
, &holes
);
1780 assert (poly_Valid (*aminusb
));
1782 /* delete holes if any left */
1783 while ((p
= holes
) != NULL
)
1786 poly_DelContour (&p
);
1793 poly_Free (aminusb
);
1796 assert (!*aandb
|| poly_Valid (*aandb
));
1797 assert (!*aminusb
|| poly_Valid (*aminusb
));
1799 } /* poly_AndSubtract_free */
1802 cntrbox_pointin (PLINE
* c
, Vector p
)
1804 return (p
[0] >= c
->xmin
&& p
[1] >= c
->ymin
&&
1805 p
[0] <= c
->xmax
&& p
[1] <= c
->ymax
);
1810 node_neighbours (VNODE
* a
, VNODE
* b
)
1812 return (a
== b
) || (a
->next
== b
) || (b
->next
== a
) || (a
->next
== b
->next
);
1816 poly_CreateNode (Vector v
)
1822 res
= (VNODE
*) calloc (1, sizeof (VNODE
));
1825 // bzero (res, sizeof (VNODE) - sizeof(Vector));
1833 poly_IniContour (PLINE
* c
)
1837 /* bzero (c, sizeof(PLINE)); */
1838 c
->head
.next
= c
->head
.prev
= &c
->head
;
1839 c
->xmin
= c
->ymin
= 0x7fffffff;
1840 c
->xmax
= c
->ymax
= 0x80000000;
1841 c
->is_round
= FALSE
;
1848 poly_NewContour (Vector v
)
1852 res
= (PLINE
*) calloc (1, sizeof (PLINE
));
1856 poly_IniContour (res
);
1860 Vcopy (res
->head
.point
, v
);
1861 cntrbox_adjust (res
, v
);
1868 poly_ClrContour (PLINE
* c
)
1873 while ((cur
= c
->head
.next
) != &c
->head
)
1875 poly_ExclVertex (cur
);
1878 poly_IniContour (c
);
1882 poly_DelContour (PLINE
** c
)
1888 for (cur
= (*c
)->head
.prev
; cur
!= &(*c
)->head
; cur
= prev
)
1891 if (cur
->cvc_next
!= NULL
)
1893 free (cur
->cvc_next
);
1894 free (cur
->cvc_prev
);
1898 if ((*c
)->head
.cvc_next
!= NULL
)
1900 free ((*c
)->head
.cvc_next
);
1901 free ((*c
)->head
.cvc_prev
);
1903 /* FIXME -- strict aliasing violation. */
1906 rtree_t
*r
= (*c
)->tree
;
1907 r_destroy_tree (&r
);
1909 free (*c
), *c
= NULL
;
1913 poly_PreContour (PLINE
* C
, BOOLp optimize
)
1923 for (c
= (p
= &C
->head
)->next
; c
!= &C
->head
; c
= (p
= c
)->next
)
1925 /* if the previous node is on the same line with this one, we should remove it */
1926 Vsub2 (p1
, c
->point
, p
->point
);
1927 Vsub2 (p2
, c
->next
->point
, c
->point
);
1928 /* If the product below is zero then
1929 * the points on either side of c
1930 * are on the same line!
1931 * So, remove the point c
1934 if (vect_det2 (p1
, p2
) == 0)
1936 poly_ExclVertex (c
);
1943 C
->xmin
= C
->xmax
= C
->head
.point
[0];
1944 C
->ymin
= C
->ymax
= C
->head
.point
[1];
1946 p
= (c
= &C
->head
)->prev
;
1951 /* calculate area for orientation */
1953 (double) (p
->point
[0] - c
->point
[0]) * (p
->point
[1] +
1955 cntrbox_adjust (C
, c
->point
);
1958 while ((c
= (p
= c
)->next
) != &C
->head
);
1960 C
->area
= ABS (area
);
1962 C
->Flags
.orient
= ((area
< 0) ? PLF_INV
: PLF_DIR
);
1963 C
->tree
= make_edge_tree (C
);
1964 } /* poly_PreContour */
1967 flip_cb (const BoxType
* b
, void *cl
)
1969 struct seg
*s
= (struct seg
*) b
;
1975 poly_InvContour (PLINE
* c
)
1985 cur
->next
= cur
->prev
;
1987 /* fix the segment tree */
1989 while ((cur
= next
) != &c
->head
);
1990 c
->Flags
.orient
^= 1;
1993 r
= r_search (c
->tree
, NULL
, NULL
, flip_cb
, NULL
);
1994 assert (r
== c
->Count
);
1999 poly_ExclVertex (VNODE
* node
)
2001 assert (node
!= NULL
);
2004 free (node
->cvc_next
);
2005 free (node
->cvc_prev
);
2007 node
->prev
->next
= node
->next
;
2008 node
->next
->prev
= node
->prev
;
2012 poly_InclVertex (VNODE
* after
, VNODE
* node
)
2015 assert (after
!= NULL
);
2016 assert (node
!= NULL
);
2019 node
->next
= after
->next
;
2020 after
->next
= after
->next
->prev
= node
;
2021 /* remove points on same line */
2022 if (node
->prev
->prev
== node
)
2023 return; /* we don't have 3 points in the poly yet */
2024 a
= (node
->point
[1] - node
->prev
->prev
->point
[1]);
2025 a
*= (node
->prev
->point
[0] - node
->prev
->prev
->point
[0]);
2026 b
= (node
->point
[0] - node
->prev
->prev
->point
[0]);
2027 b
*= (node
->prev
->point
[1] - node
->prev
->prev
->point
[1]);
2028 if (fabs (a
- b
) < EPSILON
)
2030 VNODE
*t
= node
->prev
;
2031 t
->prev
->next
= node
;
2032 node
->prev
= t
->prev
;
2038 poly_CopyContour (PLINE
** dst
, PLINE
* src
)
2040 VNODE
*cur
, *newnode
;
2042 assert (src
!= NULL
);
2044 *dst
= poly_NewContour (src
->head
.point
);
2048 (*dst
)->Count
= src
->Count
;
2049 (*dst
)->Flags
.orient
= src
->Flags
.orient
;
2050 (*dst
)->xmin
= src
->xmin
, (*dst
)->xmax
= src
->xmax
;
2051 (*dst
)->ymin
= src
->ymin
, (*dst
)->ymax
= src
->ymax
;
2052 (*dst
)->area
= src
->area
;
2054 for (cur
= src
->head
.next
; cur
!= &src
->head
; cur
= cur
->next
)
2056 if ((newnode
= poly_CreateNode (cur
->point
)) == NULL
)
2058 // newnode->Flags = cur->Flags;
2059 poly_InclVertex ((*dst
)->head
.prev
, newnode
);
2061 (*dst
)->tree
= make_edge_tree (*dst
);
2065 /**********************************************************************/
2066 /* polygon routines */
2069 poly_Copy0 (POLYAREA
** dst
, const POLYAREA
* src
)
2073 *dst
= calloc (1, sizeof (POLYAREA
));
2077 return poly_Copy1 (*dst
, src
);
2081 poly_Copy1 (POLYAREA
* dst
, const POLYAREA
* src
)
2083 PLINE
*cur
, **last
= &dst
->contours
;
2086 dst
->f
= dst
->b
= dst
;
2088 for (cur
= src
->contours
; cur
!= NULL
; cur
= cur
->next
)
2090 if (!poly_CopyContour (last
, cur
))
2092 last
= &(*last
)->next
;
2098 poly_M_Incl (POLYAREA
** list
, POLYAREA
* a
)
2101 a
->f
= a
->b
= a
, *list
= a
;
2106 (*list
)->b
= (*list
)->b
->f
= a
;
2111 poly_M_Copy0 (POLYAREA
** dst
, const POLYAREA
* srcfst
)
2113 const POLYAREA
*src
= srcfst
;
2121 if ((di
= poly_Create ()) == NULL
|| !poly_Copy1 (di
, src
))
2123 poly_M_Incl (dst
, di
);
2125 while ((src
= src
->f
) != srcfst
);
2130 poly_InclContour (POLYAREA
* p
, PLINE
* c
)
2134 if ((c
== NULL
) || (p
== NULL
))
2136 if (c
->Flags
.orient
== PLF_DIR
)
2138 if (p
->contours
!= NULL
)
2144 if (p
->contours
== NULL
)
2146 /* link at front of hole list */
2147 tmp
= p
->contours
->next
;
2148 p
->contours
->next
= c
;
2163 crossing (const BoxType
* b
, void *cl
)
2165 struct seg
*s
= (struct seg
*) b
;
2166 struct pip
*p
= (struct pip
*) cl
;
2168 if (s
->v
->point
[1] <= p
->p
[1])
2170 if (s
->v
->next
->point
[1] > p
->p
[1])
2174 Vsub2 (v1
, s
->v
->next
->point
, s
->v
->point
);
2175 Vsub2 (v2
, p
->p
, s
->v
->point
);
2176 cross
= (long long) v1
[0] * v2
[1] - (long long) v2
[0] * v1
[1];
2180 longjmp (p
->env
, 1);
2188 if (s
->v
->next
->point
[1] <= p
->p
[1])
2192 Vsub2 (v1
, s
->v
->next
->point
, s
->v
->point
);
2193 Vsub2 (v2
, p
->p
, s
->v
->point
);
2194 cross
= (long long) v1
[0] * v2
[1] - (long long) v2
[0] * v1
[1];
2198 longjmp (p
->env
, 1);
2208 poly_InsideContour (PLINE
* c
, Vector p
)
2213 if (!cntrbox_pointin (c
, p
))
2216 info
.p
[0] = ray
.X1
= p
[0];
2217 info
.p
[1] = ray
.Y1
= p
[1];
2218 ray
.X2
= 0x7fffffff;
2220 if (setjmp (info
.env
) == 0)
2221 r_search (c
->tree
, &ray
, NULL
, crossing
, &info
);
2226 poly_CheckInside (POLYAREA
* p
, Vector v0
)
2230 if ((p
== NULL
) || (v0
== NULL
) || (p
->contours
== NULL
))
2233 if (poly_InsideContour (cur
, v0
))
2235 for (cur
= cur
->next
; cur
!= NULL
; cur
= cur
->next
)
2236 if (poly_InsideContour (cur
, v0
))
2244 poly_M_CheckInside (POLYAREA
* p
, Vector v0
)
2248 if ((p
== NULL
) || (v0
== NULL
))
2253 if (poly_CheckInside (cur
, v0
))
2256 while ((cur
= cur
->f
) != p
);
2261 dot (Vector A
, Vector B
)
2263 return (double)A
[0] * (double)B
[0] + (double)A
[1] * (double)B
[1];
2266 /* Compute whether point is inside a triangle formed by 3 other pints */
2267 /* Algorithm from http://www.blackpawn.com/texts/pointinpoly/default.html */
2269 point_in_triangle (Vector A
, Vector B
, Vector C
, Vector P
)
2272 double dot00
, dot01
, dot02
, dot11
, dot12
;
2276 /* Compute vectors */
2277 v0
[0] = C
[0] - A
[0]; v0
[1] = C
[1] - A
[1];
2278 v1
[0] = B
[0] - A
[0]; v1
[1] = B
[1] - A
[1];
2279 v2
[0] = P
[0] - A
[0]; v2
[1] = P
[1] - A
[1];
2281 /* Compute dot products */
2282 dot00
= dot (v0
, v0
);
2283 dot01
= dot (v0
, v1
);
2284 dot02
= dot (v0
, v2
);
2285 dot11
= dot (v1
, v1
);
2286 dot12
= dot (v1
, v2
);
2288 /* Compute barycentric coordinates */
2289 invDenom
= 1. / (dot00
* dot11
- dot01
* dot01
);
2290 u
= (dot11
* dot02
- dot01
* dot12
) * invDenom
;
2291 v
= (dot00
* dot12
- dot01
* dot02
) * invDenom
;
2293 /* Check if point is in triangle */
2294 return (u
> 0.0) && (v
> 0.0) && (u
+ v
< 1.0);
2298 /* Returns the dot product of Vector A->B, and a vector
2299 * orthogonal to Vector C->D. The result is not normalisd, so will be
2300 * weighted by the magnitude of the C->D vector.
2303 dot_orthogonal_to_direction (Vector A
, Vector B
, Vector C
, Vector D
)
2306 l1
[0] = B
[0] - A
[0]; l1
[1] = B
[1] - A
[1];
2307 l2
[0] = D
[0] - C
[0]; l2
[1] = D
[1] - C
[1];
2312 return dot (l1
, l3
);
2315 /* Algorithm from http://www.exaflop.org/docs/cgafaq/cga2.html
2317 * "Given a simple polygon, find some point inside it. Here is a method based
2318 * on the proof that there exists an internal diagonal, in [O'Rourke, 13-14].
2319 * The idea is that the midpoint of a diagonal is interior to the polygon.
2321 * 1. Identify a convex vertex v; let its adjacent vertices be a and b.
2322 * 2. For each other vertex q do:
2323 * 2a. If q is inside avb, compute distance to v (orthogonal to ab).
2324 * 2b. Save point q if distance is a new min.
2325 * 3. If no point is inside, return midpoint of ab, or centroid of avb.
2326 * 4. Else if some point inside, qv is internal: return its midpoint."
2328 * [O'Rourke]: Computational Geometry in C (2nd Ed.)
2329 * Joseph O'Rourke, Cambridge University Press 1998,
2330 * ISBN 0-521-64010-5 Pbk, ISBN 0-521-64976-5 Hbk
2333 poly_ComputeInteriorPoint (PLINE
*poly
, Vector v
)
2335 VNODE
*pt1
, *pt2
, *pt3
, *q
;
2336 VNODE
*min_q
= NULL
;
2339 double dir
= (poly
->Flags
.orient
== PLF_DIR
) ? 1. : -1;
2341 /* Find a convex node on the polygon */
2350 dot_product
= dot_orthogonal_to_direction (pt1
->point
, pt2
->point
,
2351 pt3
->point
, pt2
->point
);
2353 if (dot_product
* dir
> 0.)
2356 while ((pt1
= pt1
->next
) != &poly
->head
);
2358 /* Loop over remaining vertices */
2362 /* Is current vertex "q" outside pt1 pt2 pt3 triangle? */
2363 if (!point_in_triangle (pt1
->point
, pt2
->point
, pt3
->point
, q
->point
))
2366 /* NO: compute distance to pt2 (v) orthogonal to pt1 - pt3) */
2367 /* Record minimum */
2368 dist
= dot_orthogonal_to_direction (q
->point
, pt2
->point
, pt1
->point
, pt3
->point
);
2369 if (min_q
== NULL
|| dist
< min_dist
) {
2374 while ((q
= q
->next
) != pt2
);
2376 /* Were any "q" found inside pt1 pt2 pt3? */
2377 if (min_q
== NULL
) {
2378 /* NOT FOUND: Return midpoint of pt1 pt3 */
2379 v
[0] = (pt1
->point
[0] + pt3
->point
[0]) / 2;
2380 v
[1] = (pt1
->point
[1] + pt3
->point
[1]) / 2;
2382 /* FOUND: Return mid point of min_q, pt2 */
2383 v
[0] = (pt2
->point
[0] + min_q
->point
[0]) / 2;
2384 v
[1] = (pt2
->point
[1] + min_q
->point
[1]) / 2;
2389 /* NB: This function assumes the caller _knows_ the contours do not
2390 * intersect. If the contours intersect, the result is undefined.
2391 * It will return the correct result if the two contours share
2392 * common points beteween their contours. (Identical contours
2393 * are treated as being inside each other).
2396 poly_ContourInContour (PLINE
* poly
, PLINE
* inner
)
2399 assert (poly
!= NULL
);
2400 assert (inner
!= NULL
);
2401 if (cntrbox_inside (inner
, poly
))
2403 /* We need to prove the "inner" contour is not outside
2404 * "poly" contour. If it is outside, we can return.
2406 if (!poly_InsideContour (poly
, inner
->head
.point
))
2409 poly_ComputeInteriorPoint (inner
, point
);
2410 return poly_InsideContour (poly
, point
);
2416 poly_Init (POLYAREA
* p
)
2427 if ((res
= malloc (sizeof (POLYAREA
))) != NULL
)
2433 poly_FreeContours (PLINE
** pline
)
2437 while ((pl
= *pline
) != NULL
)
2440 poly_DelContour (&pl
);
2445 poly_Free (POLYAREA
** p
)
2451 for (cur
= (*p
)->f
; cur
!= *p
; cur
= (*p
)->f
)
2453 poly_FreeContours (&cur
->contours
);
2458 poly_FreeContours (&cur
->contours
);
2459 free (*p
), *p
= NULL
;
2463 inside_sector (VNODE
* pn
, Vector p2
)
2465 Vector cdir
, ndir
, pdir
;
2468 assert (pn
!= NULL
);
2469 vect_sub (cdir
, p2
, pn
->point
);
2470 vect_sub (pdir
, pn
->point
, pn
->prev
->point
);
2471 vect_sub (ndir
, pn
->next
->point
, pn
->point
);
2473 p_c
= vect_det2 (pdir
, cdir
) >= 0;
2474 n_c
= vect_det2 (ndir
, cdir
) >= 0;
2475 p_n
= vect_det2 (pdir
, ndir
) >= 0;
2477 if ((p_n
&& p_c
&& n_c
) || ((!p_n
) && (p_c
|| n_c
)))
2481 } /* inside_sector */
2483 /* returns TRUE if bad contour */
2485 poly_ChkContour (PLINE
* a
)
2487 VNODE
*a1
, *a2
, *hit1
, *hit2
;
2498 if (!node_neighbours (a1
, a2
) &&
2499 (icnt
= vect_inters2 (a1
->point
, a1
->next
->point
,
2500 a2
->point
, a2
->next
->point
, i1
, i2
)) > 0)
2505 if (vect_dist2 (i1
, a1
->point
) < EPSILON
)
2507 else if (vect_dist2 (i1
, a1
->next
->point
) < EPSILON
)
2512 if (vect_dist2 (i1
, a2
->point
) < EPSILON
)
2514 else if (vect_dist2 (i1
, a2
->next
->point
) < EPSILON
)
2519 if ((hit1
== NULL
) && (hit2
== NULL
))
2521 /* If the intersection didn't land on an end-point of either
2522 * line, we know the lines cross and we return TRUE.
2526 else if (hit1
== NULL
)
2528 /* An end-point of the second line touched somewhere along the
2529 length of the first line. Check where the second line leads. */
2530 if (inside_sector (hit2
, a1
->point
) !=
2531 inside_sector (hit2
, a1
->next
->point
))
2534 else if (hit2
== NULL
)
2536 /* An end-point of the first line touched somewhere along the
2537 length of the second line. Check where the first line leads. */
2538 if (inside_sector (hit1
, a2
->point
) !=
2539 inside_sector (hit1
, a2
->next
->point
))
2544 /* Both lines intersect at an end-point. Check where they lead. */
2545 if (inside_sector (hit1
, hit2
->prev
->point
) !=
2546 inside_sector (hit1
, hit2
->next
->point
))
2551 while ((a2
= a2
->next
) != &a
->head
);
2553 while ((a1
= a1
->next
) != &a
->head
);
2559 poly_Valid (POLYAREA
* p
)
2563 if ((p
== NULL
) || (p
->contours
== NULL
))
2566 if (p
->contours
->Flags
.orient
== PLF_INV
|| poly_ChkContour (p
->contours
))
2570 DEBUGP ("Invalid Outer PLINE\n");
2571 if (p
->contours
->Flags
.orient
== PLF_INV
)
2572 DEBUGP ("failed orient\n");
2573 if (poly_ChkContour (p
->contours
))
2574 DEBUGP ("failed self-intersection\n");
2575 v
= &p
->contours
->head
;
2579 fprintf (stderr
, "Line [%d %d %d %d 100 100 \"\"]\n",
2580 v
->point
[0], v
->point
[1], n
->point
[0], n
->point
[1]);
2582 while ((v
= v
->next
) != &p
->contours
->head
);
2586 for (c
= p
->contours
->next
; c
!= NULL
; c
= c
->next
)
2588 if (c
->Flags
.orient
== PLF_DIR
||
2589 poly_ChkContour (c
) || !poly_ContourInContour (p
->contours
, c
))
2593 DEBUGP ("Invalid Inner PLINE orient = %d\n", c
->Flags
.orient
);
2594 if (c
->Flags
.orient
== PLF_DIR
)
2595 DEBUGP ("failed orient\n");
2596 if (poly_ChkContour (c
))
2597 DEBUGP ("failed self-intersection\n");
2598 if (!poly_ContourInContour (p
->contours
, c
))
2599 DEBUGP ("failed containment\n");
2604 fprintf (stderr
, "Line [%d %d %d %d 100 100 \"\"]\n",
2605 v
->point
[0], v
->point
[1], n
->point
[0], n
->point
[1]);
2607 while ((v
= v
->next
) != &c
->head
);
2616 Vector vect_zero
= { (long) 0, (long) 0 };
2618 /*********************************************************************/
2619 /* L o n g V e c t o r S t u f f */
2620 /*********************************************************************/
2623 vect_init (Vector v
, double x
, double y
)
2629 #define Vzero(a) ((a)[0] == 0. && (a)[1] == 0.)
2631 #define Vsub(a,b,c) {(a)[0]=(b)[0]-(c)[0];(a)[1]=(b)[1]-(c)[1];}
2634 vect_equal (Vector v1
, Vector v2
)
2636 return (v1
[0] == v2
[0] && v1
[1] == v2
[1]);
2641 vect_sub (Vector res
, Vector v1
, Vector v2
)
2643 Vsub (res
, v1
, v2
)} /* vect_sub */
2646 vect_min (Vector v1
, Vector v2
, Vector v3
)
2648 v1
[0] = (v2
[0] < v3
[0]) ? v2
[0] : v3
[0];
2649 v1
[1] = (v2
[1] < v3
[1]) ? v2
[1] : v3
[1];
2653 vect_max (Vector v1
, Vector v2
, Vector v3
)
2655 v1
[0] = (v2
[0] > v3
[0]) ? v2
[0] : v3
[0];
2656 v1
[1] = (v2
[1] > v3
[1]) ? v2
[1] : v3
[1];
2660 vect_len2 (Vector v
)
2662 return ((double) v
[0] * v
[0] + (double) v
[1] * v
[1]); /* why sqrt? only used for compares */
2666 vect_dist2 (Vector v1
, Vector v2
)
2668 double dx
= v1
[0] - v2
[0];
2669 double dy
= v1
[1] - v2
[1];
2671 return (dx
* dx
+ dy
* dy
); /* why sqrt */
2674 /* value has sign of angle between vectors */
2676 vect_det2 (Vector v1
, Vector v2
)
2678 return (((double) v1
[0] * v2
[1]) - ((double) v2
[0] * v1
[1]));
2682 vect_m_dist (Vector v1
, Vector v2
)
2684 double dx
= v1
[0] - v2
[0];
2685 double dy
= v1
[1] - v2
[1];
2686 double dd
= (dx
* dx
+ dy
* dy
); /* sqrt */
2699 (C) 1993 Klamer Schutte
2700 (C) 1997 Michael Leonov, Alexey Nikitin
2704 vect_inters2 (Vector p1
, Vector p2
, Vector q1
, Vector q2
,
2705 Vector S1
, Vector S2
)
2708 double rpx
, rpy
, rqx
, rqy
;
2710 if (max (p1
[0], p2
[0]) < min (q1
[0], q2
[0]) ||
2711 max (q1
[0], q2
[0]) < min (p1
[0], p2
[0]) ||
2712 max (p1
[1], p2
[1]) < min (q1
[1], q2
[1]) ||
2713 max (q1
[1], q2
[1]) < min (p1
[1], p2
[1]))
2716 rpx
= p2
[0] - p1
[0];
2717 rpy
= p2
[1] - p1
[1];
2718 rqx
= q2
[0] - q1
[0];
2719 rqy
= q2
[1] - q1
[1];
2721 deel
= rpy
* rqx
- rpx
* rqy
; /* -vect_det(rp,rq); */
2723 /* coordinates are 30-bit integers so deel will be exactly zero
2724 * if the lines are parallel
2727 if (deel
== 0) /* parallel */
2729 double dc1
, dc2
, d1
, d2
, h
; /* Check to see whether p1-p2 and q1-q2 are on the same line */
2730 Vector hp1
, hq1
, hp2
, hq2
, q1p1
, q1q2
;
2732 Vsub2 (q1p1
, q1
, p1
);
2733 Vsub2 (q1q2
, q1
, q2
);
2736 /* If this product is not zero then p1-p2 and q1-q2 are not on same line! */
2737 if (vect_det2 (q1p1
, q1q2
) != 0)
2739 dc1
= 0; /* m_len(p1 - p1) */
2741 dc2
= vect_m_dist (p1
, p2
);
2742 d1
= vect_m_dist (p1
, q1
);
2743 d2
= vect_m_dist (p1
, q2
);
2745 /* Sorting the independent points from small to large */
2751 { /* hv and h are used as help-variable. */
2753 h
= dc1
, dc1
= dc2
, dc2
= h
;
2758 h
= d1
, d1
= d2
, d2
= h
;
2761 /* Now the line-pieces are compared */
2793 return (Vequ2 (S1
, S2
) ? 1 : 2);
2796 { /* not parallel */
2798 * We have the lines:
2799 * l1: p1 + s(p2 - p1)
2800 * l2: q1 + t(q2 - q1)
2801 * And we want to know the intersection point.
2803 * p1 + s(p2-p1) = q1 + t(q2-q1)
2804 * which is similar to the two equations:
2805 * p1x + s * rpx = q1x + t * rqx
2806 * p1y + s * rpy = q1y + t * rqy
2807 * Multiplying these by rpy resp. rpx gives:
2808 * rpy * p1x + s * rpx * rpy = rpy * q1x + t * rpy * rqx
2809 * rpx * p1y + s * rpx * rpy = rpx * q1y + t * rpx * rqy
2810 * Subtracting these gives:
2811 * rpy * p1x - rpx * p1y = rpy * q1x - rpx * q1y + t * ( rpy * rqx - rpx * rqy )
2812 * So t can be isolated:
2813 * t = (rpy * ( p1x - q1x ) + rpx * ( - p1y + q1y )) / ( rpy * rqx - rpx * rqy )
2814 * and s can be found similarly
2815 * s = (rqy * (q1x - p1x) + rqx * (p1y - q1y))/( rqy * rpx - rqx * rpy)
2818 if (Vequ2 (q1
, p1
) || Vequ2 (q1
, p2
))
2823 else if (Vequ2 (q2
, p1
) || Vequ2 (q2
, p2
))
2830 s
= (rqy
* (p1
[0] - q1
[0]) + rqx
* (q1
[1] - p1
[1])) / deel
;
2831 if (s
< 0 || s
> 1.)
2833 t
= (rpy
* (p1
[0] - q1
[0]) + rpx
* (q1
[1] - p1
[1])) / deel
;
2834 if (t
< 0 || t
> 1.)
2837 S1
[0] = q1
[0] + ROUND (t
* rqx
);
2838 S1
[1] = q1
[1] + ROUND (t
* rqy
);
2842 } /* vect_inters2 */
2844 /* how about expanding polygons so that edges can be arcs rather than
2845 * lines. Consider using the third coordinate to store the radius of the
2846 * arc. The arc would pass through the vertex points. Positive radius
2847 * would indicate the arc bows left (center on right of P1-P2 path)
2848 * negative radius would put the center on the other side. 0 radius
2849 * would mean the edge is a line instead of arc
2850 * The intersections of the two circles centered at the vertex points
2851 * would determine the two possible arc centers. If P2.x > P1.x then
2852 * the center with smaller Y is selected for positive r. If P2.y > P1.y
2853 * then the center with greate X is selected for positive r.
2855 * the vec_inters2() routine would then need to handle line-line
2856 * line-arc and arc-arc intersections.
2858 * perhaps reverse tracing the arc would require look-ahead to check