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
, "%d %d 10 10 \"%s\"]\n", v
->point
[0], v
->point
[1],
142 fprintf (stderr
, "Line [%d %d ", v
->point
[0], v
->point
[1]);
144 while ((v
= v
->next
) != s
);
145 fprintf (stderr
, "%d %d 10 10 \"%s\"]\n", v
->point
[0], v
->point
[1],
147 fprintf (stderr
, "NEXT PLINE\n");
151 poly_dump (POLYAREA
* p
)
157 pline_dump (&p
->contours
->head
);
159 while ((p
= p
->f
) != f
);
160 fprintf (stderr
, "NEXT_POLY\n");
164 /***************************************************************/
165 /* routines for processing intersections */
169 (C) 1993 Klamer Schutte
170 (C) 1997 Alexey Nikitin, Michael Leonov
173 returns a bit field in new_point that indicates where the
175 1 means a new node was created and inserted
176 4 means the intersection was not on the dest point
179 node_add (VNODE
* dest
, Vector po
, int *new_point
)
183 if (vect_equal (po
, dest
->point
))
185 if (vect_equal (po
, dest
->next
->point
))
190 p
= poly_CreateNode (po
);
195 p
->next
= dest
->next
;
196 p
->cvc_prev
= p
->cvc_next
= NULL
;
197 p
->Flags
.status
= UNKNWN
;
198 return (dest
->next
= dest
->next
->prev
= p
);
201 #define ISECT_BAD_PARAM (-1)
202 #define ISECT_NO_MEMORY (-2)
210 new_descriptor (VNODE
* a
, char poly
, char side
)
212 CVCList
*l
= (CVCList
*) malloc (sizeof (CVCList
));
214 register double ang
, dx
, dy
;
222 l
->next
= l
->prev
= l
;
223 if (side
== 'P') /* previous */
224 vect_sub (v
, a
->prev
->point
, a
->point
);
226 vect_sub (v
, a
->next
->point
, a
->point
);
227 /* Uses slope/(slope+1) in quadrant 1 as a proxy for the angle.
228 * It still has the same monotonic sort result
229 * and is far less expensive to compute than the real angle.
231 if (vect_equal (v
, vect_zero
))
235 if (a
->prev
->cvc_prev
== (CVCList
*) - 1)
236 a
->prev
->cvc_prev
= a
->prev
->cvc_next
= NULL
;
237 poly_ExclVertex (a
->prev
);
238 vect_sub (v
, a
->prev
->point
, a
->point
);
242 if (a
->next
->cvc_prev
== (CVCList
*) - 1)
243 a
->next
->cvc_prev
= a
->next
->cvc_next
= NULL
;
244 poly_ExclVertex (a
->next
);
245 vect_sub (v
, a
->next
->point
, a
->point
);
248 assert (!vect_equal (v
, vect_zero
));
249 dx
= fabs ((double) v
[0]);
250 dy
= fabs ((double) v
[1]);
251 ang
= dy
/ (dy
+ dx
);
252 /* now move to the actual quadrant */
253 if (v
[0] < 0 && v
[1] >= 0)
254 ang
= 2.0 - ang
; /* 2nd quadrant */
255 else if (v
[0] < 0 && v
[1] < 0)
256 ang
+= 2.0; /* 3rd quadrant */
257 else if (v
[0] >= 0 && v
[1] < 0)
258 ang
= 4.0 - ang
; /* 4th quadrant */
260 assert (ang
>= 0.0 && ang
<= 4.0);
262 DEBUGP ("node on %c at (%ld,%ld) assigned angle %g on side %c\n", poly
,
263 a
->point
[0], a
->point
[1], ang
, side
);
272 argument a is a cross-vertex node.
273 argument poly is the polygon it comes from ('A' or 'B')
274 argument side is the side this descriptor goes on ('P' for previous
276 argument start is the head of the list of cvclists
279 insert_descriptor (VNODE
* a
, char poly
, char side
, CVCList
* start
)
281 CVCList
*l
, *new, *big
, *small
;
283 if (!(new = new_descriptor (a
, poly
, side
)))
285 /* search for the CVCList for this point */
288 start
= new; /* return is also new, so we know where start is */
289 start
->head
= new; /* circular list */
298 if (l
->parent
->point
[0] == a
->point
[0]
299 && l
->parent
->point
[1] == a
->point
[1])
300 { /* this CVCList is at our point */
305 if (l
->head
->parent
->point
[0] == start
->parent
->point
[0]
306 && l
->head
->parent
->point
[1] == start
->parent
->point
[1])
308 /* this seems to be a new point */
309 /* link this cvclist to the list of all cvclists */
310 for (; l
->head
!= new; l
= l
->next
)
320 l
= big
= small
= start
;
323 if (l
->next
->angle
< l
->angle
) /* find start/end of list */
328 else if (new->angle
>= l
->angle
&& new->angle
<= l
->next
->angle
)
330 /* insert new cvc if it lies between existing points */
333 l
->next
= l
->next
->prev
= new;
337 while ((l
= l
->next
) != start
);
338 /* didn't find it between points, it must go on an end */
339 if (big
->angle
<= new->angle
)
342 new->next
= big
->next
;
343 big
->next
= big
->next
->prev
= new;
346 assert (small
->angle
>= new->angle
);
348 new->prev
= small
->prev
;
349 small
->prev
= small
->prev
->next
= new;
355 (C) 1993 Klamer Schutte
356 (C) 1997 Alexey Nikitin, Michael Leonov
358 return 1 if new node in b, 2 if new node in a and 3 if new node in both
362 node_add_point (VNODE
* a
, VNODE
* b
, Vector p
)
366 VNODE
*node_a
, *node_b
;
368 node_a
= node_add (a
, p
, &res
);
370 node_b
= node_add (b
, p
, &res
);
372 if (node_a
== NULL
|| node_b
== NULL
)
373 return ISECT_NO_MEMORY
;
374 node_b
->cvc_prev
= node_b
->cvc_next
= (CVCList
*) - 1;
375 node_a
->cvc_prev
= node_a
->cvc_next
= (CVCList
*) - 1;
377 } /* node_add_point */
384 node_label (VNODE
* pn
)
391 assert (pn
->cvc_prev
);
392 this_poly
= pn
->cvc_prev
->poly
;
393 /* search clockwise in the cross vertex connectivity (CVC) list
395 * check for shared edges, and check if our edges
396 * are ever found between the other poly's entry and exit
399 DEBUGP ("CVCLIST for point (%ld,%ld)\n", pn
->point
[0], pn
->point
[1]);
401 /* first find whether we're starting inside or outside */
402 for (l
= pn
->cvc_prev
->prev
; l
!= pn
->cvc_prev
; l
= l
->prev
)
404 if (l
->poly
!= this_poly
)
415 assert (l
->angle
>= 0 && l
->angle
<= 4.0);
417 DEBUGP (" poly %c side %c angle = %g\n", l
->poly
, l
->side
, l
->angle
);
419 if (l
->poly
!= this_poly
)
424 if (l
->parent
->prev
->point
[0] == pn
->prev
->point
[0] &&
425 l
->parent
->prev
->point
[1] == pn
->prev
->point
[1])
427 LABEL_NODE (pn
->prev
, SHARED
); /* incoming is shared */
428 pn
->prev
->shared
= l
->parent
->prev
;
430 else if (l
->parent
->prev
->point
[0] == pn
->next
->point
[0] &&
431 l
->parent
->prev
->point
[1] == pn
->next
->point
[1])
433 LABEL_NODE (pn
, SHARED2
); /* outgoing is shared2 */
434 pn
->shared
= l
->parent
->prev
;
440 if (l
->parent
->next
->point
[0] == pn
->next
->point
[0] &&
441 l
->parent
->next
->point
[1] == pn
->next
->point
[1])
443 LABEL_NODE (pn
, SHARED
);
444 pn
->shared
= l
->parent
;
446 else if (l
->parent
->next
->point
[0] == pn
->prev
->point
[0] &&
447 l
->parent
->next
->point
[1] == pn
->prev
->point
[1])
449 LABEL_NODE (pn
->prev
, SHARED2
); /* outgoing is shared2 */
450 pn
->prev
->shared
= l
->parent
;
461 if (NODE_LABEL (v
) != SHARED
&& NODE_LABEL (v
) != SHARED2
)
465 if (NODE_LABEL (v
) != UNKNWN
&& NODE_LABEL (v
) != region
)
468 LABEL_NODE (v
, region
);
472 fprintf (stderr
, "poly %c\n", x
->poly
);
473 pline_dump (x
->parent
);
475 while ((x
= x
->next
) != l
);
478 assert (NODE_LABEL (v
) == UNKNWN
|| NODE_LABEL (v
) == region
);
479 LABEL_NODE (v
, region
);
483 while ((l
= l
->prev
) != pn
->cvc_prev
);
487 assert (NODE_LABEL (pn
) != UNKNWN
&& NODE_LABEL (pn
->prev
) != UNKNWN
);
488 if (NODE_LABEL (pn
) == UNKNWN
|| NODE_LABEL (pn
->prev
) == UNKNWN
)
490 if (NODE_LABEL (pn
) == INSIDE
|| NODE_LABEL (pn
) == OUTSIDE
)
491 return NODE_LABEL (pn
);
500 add_descriptors (PLINE
* pl
, char poly
, CVCList
* list
)
502 VNODE
*node
= &pl
->head
;
508 assert (node
->cvc_prev
== (CVCList
*) - 1
509 && node
->cvc_next
== (CVCList
*) - 1);
510 list
= node
->cvc_prev
= insert_descriptor (node
, poly
, 'P', list
);
513 list
= node
->cvc_next
= insert_descriptor (node
, poly
, 'N', list
);
518 while ((node
= node
->next
) != &pl
->head
);
523 cntrbox_adjust (PLINE
* c
, Vector p
)
525 c
->xmin
= min (c
->xmin
, p
[0]);
526 c
->xmax
= max (c
->xmax
, p
[0] + 1);
527 c
->ymin
= min (c
->ymin
, p
[1]);
528 c
->ymax
= max (c
->ymax
, p
[1] + 1);
531 /* some structures for handling segment intersections using the rtrees */
546 jmp_buf env
, sego
, *touch
;
551 * (C) 2006 harry eaton
552 * This replaces the segment in the tree with the two new segments after
553 * a vertex has been added
556 adjust_tree (rtree_t
* tree
, struct seg
*s
)
560 q
= malloc (sizeof (struct seg
));
565 q
->box
.X1
= min (q
->v
->point
[0], q
->v
->next
->point
[0]);
566 q
->box
.X2
= max (q
->v
->point
[0], q
->v
->next
->point
[0]) + 1;
567 q
->box
.Y1
= min (q
->v
->point
[1], q
->v
->next
->point
[1]);
568 q
->box
.Y2
= max (q
->v
->point
[1], q
->v
->next
->point
[1]) + 1;
569 r_insert_entry (tree
, (const BoxType
*) q
, 1);
570 q
= malloc (sizeof (struct seg
));
575 q
->box
.X1
= min (q
->v
->point
[0], q
->v
->next
->point
[0]);
576 q
->box
.X2
= max (q
->v
->point
[0], q
->v
->next
->point
[0]) + 1;
577 q
->box
.Y1
= min (q
->v
->point
[1], q
->v
->next
->point
[1]);
578 q
->box
.Y2
= max (q
->v
->point
[1], q
->v
->next
->point
[1]) + 1;
579 r_insert_entry (tree
, (const BoxType
*) q
, 1);
580 r_delete_entry (tree
, (const BoxType
*) s
);
586 * (C) 2006, harry eaton
587 * This prunes the search for boxes that don't intersect the segment.
590 seg_in_region (const BoxType
* b
, void *cl
)
592 struct info
*i
= (struct info
*) cl
;
594 /* for zero slope the search is aligned on the axis so it is already pruned */
597 y1
= i
->m
* b
->X1
+ i
->b
;
598 y2
= i
->m
* b
->X2
+ i
->b
;
599 if (min (y1
, y2
) >= b
->Y2
)
601 if (max (y1
, y2
) < b
->Y1
)
603 return 1; /* might intersect */
608 * (C) 2006 harry eaton
609 * This routine checks if the segment in the tree intersect the search segment.
610 * If it does, the plines are marked as intersected and the point is marked for
611 * the cvclist. If the point is not already a vertex, a new vertex is inserted
612 * and the search for intersections starts over at the beginning.
613 * That is potentially a significant time penalty, but it does solve the snap rounding
614 * problem. There are efficient algorithms for finding intersections with snap
615 * rounding, but I don't have time to implement them right now.
618 seg_in_seg (const BoxType
* b
, void *cl
)
620 struct info
*i
= (struct info
*) cl
;
621 struct seg
*s
= (struct seg
*) b
;
625 cnt
= vect_inters2 (s
->v
->point
, s
->v
->next
->point
,
626 i
->v
->point
, i
->v
->next
->point
, s1
, s2
);
629 if (i
->touch
) /* if checking touches one find and we're done */
630 longjmp (*i
->touch
, TOUCHES
);
631 i
->s
->p
->Flags
.status
= ISECTED
;
632 s
->p
->Flags
.status
= ISECTED
;
635 res
= node_add_point (i
->v
, s
->v
, cnt
> 1 ? s2
: s1
);
637 return 1; /* error */
638 /* adjust the bounding box and tree if necessary */
641 cntrbox_adjust (i
->s
->p
, cnt
> 1 ? s2
: s1
);
642 if (adjust_tree (i
->s
->p
->tree
, i
->s
))
645 /* if we added a node in the tree we need to change the tree */
648 cntrbox_adjust (s
->p
, cnt
> 1 ? s2
: s1
);
649 if (adjust_tree (i
->tree
, s
))
652 if (res
& 3) /* if a point was inserted start over */
654 #ifdef DEBUG_INTERSECT
655 DEBUGP ("new intersection at (%d, %d)\n", cnt
> 1 ? s2
[0] : s1
[0],
656 cnt
> 1 ? s2
[1] : s1
[1]);
665 make_edge_tree (PLINE
* pb
)
669 rtree_t
*ans
= r_create_tree (NULL
, 0, 0);
673 s
= malloc (sizeof (struct seg
));
674 if (bv
->point
[0] < bv
->next
->point
[0])
676 s
->box
.X1
= bv
->point
[0];
677 s
->box
.X2
= bv
->next
->point
[0] + 1;
681 s
->box
.X2
= bv
->point
[0] + 1;
682 s
->box
.X1
= bv
->next
->point
[0];
684 if (bv
->point
[1] < bv
->next
->point
[1])
686 s
->box
.Y1
= bv
->point
[1];
687 s
->box
.Y2
= bv
->next
->point
[1] + 1;
691 s
->box
.Y2
= bv
->point
[1] + 1;
692 s
->box
.Y1
= bv
->next
->point
[1];
696 r_insert_entry (ans
, (const BoxType
*) s
, 1);
698 while ((bv
= bv
->next
) != &pb
->head
);
703 get_seg (const BoxType
* b
, void *cl
)
705 struct info
*i
= (struct info
*) cl
;
706 struct seg
*s
= (struct seg
*) b
;
710 longjmp (i
->sego
, 1);
717 * (C) 2006, harry eaton
718 * This uses an rtree to find A-B intersections. Whenever a new vertex is
719 * added, the search for intersections is re-started because the rounding
720 * could alter the topology otherwise.
721 * This should use a faster algorithm for snap rounding intersection finding.
722 * The best algorthim is probably found in:
724 * "Improved output-sensitive snap rounding," John Hershberger, Proceedings
725 * of the 22nd annual symposium on Computational geomerty, 2006, pp 357-366.
726 * http://doi.acm.org/10.1145/1137856.1137909
728 * Algorithms described by de Berg, or Goodrich or Halperin, or Hobby would
729 * probably work as well.
734 intersect (jmp_buf * jb
, POLYAREA
* b
, POLYAREA
* a
, int add
)
736 PLINE
*pa
, *pb
; /* pline iterators */
739 VNODE
*av
; /* node iterators */
747 setjmp (info
.env
); /* we loop back here whenever a vertex is inserted */
751 while (pa
) /* Loop over the contours of POLYAREA "a" */
753 int found_overlapping_a_b_contour
= FALSE
;
755 while (pb
) /* Loop over the contours of POLYAREA "b" */
757 /* Are there overlapping bounds? */
758 if (pb
->xmin
<= pa
->xmax
&& pb
->xmax
>= pa
->xmin
&&
759 pb
->ymin
<= pa
->ymax
&& pb
->ymax
>= pa
->ymin
)
761 found_overlapping_a_b_contour
= TRUE
;
767 /* If we didn't find anything intersting, move onto the next "a" contour */
768 if (!found_overlapping_a_b_contour
)
775 /* something intersects so check the edges of the contour */
777 /* Pick which contour has the fewer points, and do the loop
778 * over that. The r_tree makes hit-testing against a contour
779 * faster, so we want to do that on the bigger contour.
781 if (pa
->Count
< pb
->Count
)
792 av
= &looping_over
->head
;
793 do /* Loop over the nodes in the smaller contour */
795 /* check this edge for any insertions */
798 /* compute the slant for region trimming */
799 dx
= av
->next
->point
[0] - av
->point
[0];
804 info
.m
= (av
->next
->point
[1] - av
->point
[1]) / dx
;
805 info
.b
= av
->point
[1] - info
.m
* av
->point
[0];
807 box
.X2
= (box
.X1
= av
->point
[0]) + 1;
808 box
.Y2
= (box
.Y1
= av
->point
[1]) + 1;
810 /* fill in the segment in info corresponding to this node */
811 if (setjmp (info
.sego
) == 0)
813 r_search (looping_over
->tree
, &box
, NULL
, get_seg
, &info
);
817 /* NB: If this actually hits anything, we are teleported back to the beginning */
818 info
.tree
= rtree_over
->tree
;
820 if (UNLIKELY (r_search (info
.tree
, &info
.s
->box
,
821 seg_in_region
, seg_in_seg
, &info
)))
822 return err_no_memory
; /* error */
824 while ((av
= av
->next
) != &looping_over
->head
);
826 /* Continue the with the _same_ "a" contour,
827 * testing it against the next "b" contour.
831 } /* end of setjmp loop */
836 M_POLYAREA_intersect (jmp_buf * e
, POLYAREA
* afst
, POLYAREA
* bfst
, int add
)
838 POLYAREA
*a
= afst
, *b
= bfst
;
839 PLINE
*curcA
, *curcB
;
840 CVCList
*the_list
= NULL
;
842 if (a
== NULL
|| b
== NULL
)
843 error (err_bad_parm
);
848 if (a
->contours
->xmax
>= b
->contours
->xmin
&&
849 a
->contours
->ymax
>= b
->contours
->ymin
&&
850 a
->contours
->xmin
<= b
->contours
->xmax
&&
851 a
->contours
->ymin
<= b
->contours
->ymax
)
853 if (UNLIKELY (intersect (e
, a
, b
, add
)))
854 error (err_no_memory
);
857 while (add
&& (a
= a
->f
) != afst
);
858 for (curcB
= b
->contours
; curcB
!= NULL
; curcB
= curcB
->next
)
859 if (curcB
->Flags
.status
== ISECTED
)
861 the_list
= add_descriptors (curcB
, 'B', the_list
);
862 if (UNLIKELY (the_list
== NULL
))
863 error (err_no_memory
);
866 while (add
&& (b
= b
->f
) != bfst
);
869 for (curcA
= a
->contours
; curcA
!= NULL
; curcA
= curcA
->next
)
870 if (curcA
->Flags
.status
== ISECTED
)
872 the_list
= add_descriptors (curcA
, 'A', the_list
);
873 if (UNLIKELY (the_list
== NULL
))
874 error (err_no_memory
);
877 while (add
&& (a
= a
->f
) != afst
);
878 } /* M_POLYAREA_intersect */
881 cntrbox_inside (PLINE
* c1
, PLINE
* c2
)
883 assert (c1
!= NULL
&& c2
!= NULL
);
884 return ((c1
->xmin
>= c2
->xmin
) &&
885 (c1
->ymin
>= c2
->ymin
) &&
886 (c1
->xmax
<= c2
->xmax
) && (c1
->ymax
<= c2
->ymax
));
889 /*****************************************************************/
890 /* Routines for making labels */
892 /* cntr_in_M_POLYAREA
893 returns poly is inside outfst ? TRUE : FALSE */
895 cntr_in_M_POLYAREA (PLINE
* poly
, POLYAREA
* outfst
, BOOLp test
)
898 POLYAREA
*outer
= outfst
;
901 assert (poly
!= NULL
);
902 assert (outer
!= NULL
);
904 heap
= heap_create ();
907 if (cntrbox_inside (poly
, outer
->contours
))
908 heap_insert (heap
, outer
->contours
->area
, (void *) outer
);
910 /* if checking touching, use only the first polygon */
911 while (!test
&& (outer
= outer
->f
) != outfst
);
912 /* we need only check the smallest poly container
913 * but we must loop in case the box containter is not
914 * the poly container */
917 if (heap_is_empty (heap
))
919 outer
= (POLYAREA
*) heap_remove_smallest (heap
);
920 if (poly_ContourInContour (outer
->contours
, poly
))
922 for (curc
= outer
->contours
->next
; curc
!= NULL
; curc
= curc
->next
)
923 if (poly_ContourInContour (curc
, poly
))
925 /* it's inside a hole in the smallest polygon
926 * no need to check the other polygons */
927 heap_destroy (&heap
);
930 heap_destroy (&heap
);
935 heap_destroy (&heap
);
937 } /* cntr_in_M_POLYAREA */
944 static char u
[] = "UNKNOWN";
945 static char i
[] = "INSIDE";
946 static char o
[] = "OUTSIDE";
947 static char s
[] = "SHARED";
948 static char s2
[] = "SHARED2";
950 switch (NODE_LABEL (v
))
966 print_labels (PLINE
* a
)
972 DEBUGP ("(%ld,%ld)->(%ld,%ld) labeled %s\n", c
->point
[0], c
->point
[1],
973 c
->next
->point
[0], c
->next
->point
[1], theState (c
));
975 while ((c
= c
->next
) != &a
->head
);
982 (C) 1993 Klamer Schutte
983 (C) 1997 Alexey Nikitin, Michael Leonov
987 label_contour (PLINE
* a
)
989 VNODE
*cur
= &a
->head
;
990 int did_label
= FALSE
, label
= UNKNWN
;
996 if (NODE_LABEL (cur
) != UNKNWN
)
998 label
= NODE_LABEL (cur
);
1001 if (cur
->cvc_next
) /* examine cross vertex */
1003 label
= node_label (cur
);
1006 else if (label
== INSIDE
|| label
== OUTSIDE
)
1008 LABEL_NODE (cur
, label
);
1012 while ((cur
= cur
->next
) != &a
->head
|| did_label
);
1013 #ifdef DEBUG_ALL_LABELS
1018 } /* label_contour */
1021 cntr_label_POLYAREA (PLINE
* poly
, POLYAREA
* ppl
, BOOLp test
)
1023 assert (ppl
!= NULL
&& ppl
->contours
!= NULL
);
1024 if (poly
->Flags
.status
== ISECTED
)
1026 label_contour (poly
); /* should never get here when BOOLp is true */
1028 else if (cntr_in_M_POLYAREA (poly
, ppl
, test
))
1032 poly
->Flags
.status
= INSIDE
;
1038 poly
->Flags
.status
= OUTSIDE
;
1041 } /* cntr_label_POLYAREA */
1044 M_POLYAREA_label (POLYAREA
* afst
, POLYAREA
* b
, BOOLp touch
)
1052 for (curc
= a
->contours
; curc
!= NULL
; curc
= curc
->next
)
1053 if (cntr_label_POLYAREA (curc
, b
, touch
))
1059 while (!touch
&& (a
= a
->f
) != afst
);
1063 /****************************************************************/
1065 /* routines for temporary storing resulting contours */
1067 InsCntr (jmp_buf * e
, PLINE
* c
, POLYAREA
** dst
)
1073 MemGet (*dst
, POLYAREA
);
1074 (*dst
)->f
= (*dst
)->b
= *dst
;
1079 MemGet (newp
, POLYAREA
);
1081 newp
->b
= (*dst
)->b
;
1082 newp
->f
->b
= newp
->b
->f
= newp
;
1089 PutContour (jmp_buf * e
, PLINE
* cntr
, POLYAREA
** contours
, PLINE
** holes
,
1092 assert (cntr
!= NULL
);
1093 assert (cntr
->Count
> 2);
1095 if (cntr
->Flags
.orient
== PLF_DIR
)
1096 InsCntr (e
, cntr
, contours
);
1097 /* put hole into temporary list */
1100 /* if we know this belongs inside the parent, put it there now */
1103 cntr
->next
= parent
->next
;
1104 parent
->next
= cntr
;
1108 cntr
->next
= *holes
;
1109 *holes
= cntr
; /* let cntr be 1st hole in list */
1115 heap_it (const BoxType
* b
, void *cl
)
1117 heap_t
*heap
= (heap_t
*) cl
;
1118 PLINE
*p
= (PLINE
*) b
;
1120 return 0; /* how did this happen? */
1121 heap_insert (heap
, p
->area
, (void *) p
);
1126 InsertHoles (jmp_buf * e
, POLYAREA
* dest
, PLINE
** src
)
1129 PLINE
*curh
, *container
, *tmp
;
1134 return; /* empty hole list */
1136 error (err_bad_parm
); /* empty contour list */
1138 /* make an rtree of contours */
1139 tree
= r_create_tree (NULL
, 0, 0);
1143 r_insert_entry (tree
, (const BoxType
*) curc
->contours
, 0);
1145 while ((curc
= curc
->f
) != dest
);
1146 /* loop through the holes and put them where they belong */
1147 while ((curh
= *src
) != NULL
)
1152 /* build a heap of all of the polys that the hole is inside its bounding box */
1153 heap
= heap_create ();
1154 r_search (tree
, (BoxType
*) curh
, NULL
, heap_it
, heap
);
1155 if (heap_is_empty (heap
))
1162 poly_DelContour (&curh
);
1163 error (err_bad_parm
);
1165 /* Now search the heap for the container. If there was only one item
1166 * in the heap, assume it is the container without the expense of
1169 tmp
= (PLINE
*) heap_remove_smallest (heap
);
1170 if (heap_is_empty (heap
))
1171 { /* only one possibility it must be the right one */
1172 assert (poly_ContourInContour (tmp
, curh
));
1179 if (poly_ContourInContour (tmp
, curh
))
1184 if (heap_is_empty (heap
))
1186 tmp
= (PLINE
*) heap_remove_smallest (heap
);
1190 heap_destroy (&heap
);
1191 if (container
== NULL
)
1193 /* bad input polygons were given */
1200 poly_DelContour (&curh
);
1201 error (err_bad_parm
);
1205 /* link at front of hole list */
1206 tmp
= container
->next
;
1207 container
->next
= curh
;
1211 r_destroy_tree (&tree
);
1215 /****************************************************************/
1216 /* routines for collecting result */
1224 typedef int (*S_Rule
) (VNODE
*, DIRECTION
*);
1227 typedef int (*J_Rule
) (char, VNODE
*, DIRECTION
*);
1230 UniteS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1233 return (NODE_LABEL (cur
) == OUTSIDE
) || (NODE_LABEL (cur
) == SHARED
);
1237 IsectS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1240 return (NODE_LABEL (cur
) == INSIDE
) || (NODE_LABEL (cur
) == SHARED
);
1244 SubS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1247 return (NODE_LABEL (cur
) == OUTSIDE
) || (NODE_LABEL (cur
) == SHARED2
);
1251 XorS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1253 if (cur
->Flags
.status
== INSIDE
)
1258 if (cur
->Flags
.status
== OUTSIDE
)
1267 IsectJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1269 assert (*cdir
== FORW
);
1270 return (v
->Flags
.status
== INSIDE
|| v
->Flags
.status
== SHARED
);
1274 UniteJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1276 assert (*cdir
== FORW
);
1277 return (v
->Flags
.status
== OUTSIDE
|| v
->Flags
.status
== SHARED
);
1281 XorJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1283 if (v
->Flags
.status
== INSIDE
)
1288 if (v
->Flags
.status
== OUTSIDE
)
1297 SubJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1299 if (p
== 'B' && v
->Flags
.status
== INSIDE
)
1304 if (p
== 'A' && v
->Flags
.status
== OUTSIDE
)
1309 if (v
->Flags
.status
== SHARED2
)
1320 /* return the edge that comes next.
1321 * if the direction is BACKW, then we return the next vertex
1322 * so that prev vertex has the flags for the edge
1324 * returns true if an edge is found, false otherwise
1327 jump (VNODE
** cur
, DIRECTION
* cdir
, J_Rule rule
)
1333 if (!(*cur
)->cvc_prev
) /* not a cross-vertex */
1335 if (*cdir
== FORW
? (*cur
)->Flags
.mark
: (*cur
)->prev
->Flags
.mark
)
1340 DEBUGP ("jump entering node at (%ld, %ld)\n", (*cur
)->point
[0],
1344 d
= (*cur
)->cvc_prev
->prev
;
1346 d
= (*cur
)->cvc_next
->prev
;
1354 if (!e
->Flags
.mark
&& rule (d
->poly
, e
, &new))
1356 if ((d
->side
== 'N' && new == FORW
) ||
1357 (d
->side
== 'P' && new == BACKW
))
1361 DEBUGP ("jump leaving node at (%ld, %ld)\n",
1362 e
->next
->point
[0], e
->next
->point
[1]);
1364 DEBUGP ("jump leaving node at (%ld, %ld)\n",
1365 e
->point
[0], e
->point
[1]);
1373 while ((d
= d
->prev
) != start
);
1378 Gather (VNODE
* start
, PLINE
** result
, J_Rule v_rule
, DIRECTION initdir
)
1380 VNODE
*cur
= start
, *newn
;
1381 DIRECTION dir
= initdir
;
1383 DEBUGP ("gather direction = %d\n", dir
);
1385 assert (*result
== NULL
);
1388 /* see where to go next */
1389 if (!jump (&cur
, &dir
, v_rule
))
1391 /* add edge to polygon */
1394 *result
= poly_NewContour (cur
->point
);
1395 if (*result
== NULL
)
1396 return err_no_memory
;
1400 if ((newn
= poly_CreateNode (cur
->point
)) == NULL
)
1401 return err_no_memory
;
1402 poly_InclVertex ((*result
)->head
.prev
, newn
);
1405 DEBUGP ("gather vertex at (%ld, %ld)\n", cur
->point
[0], cur
->point
[1]);
1407 /* Now mark the edge as included. */
1408 newn
= (dir
== FORW
? cur
: cur
->prev
);
1409 newn
->Flags
.mark
= 1;
1410 /* for SHARED edge mark both */
1412 newn
->shared
->Flags
.mark
= 1;
1414 /* Advance to the next edge. */
1415 cur
= (dir
== FORW
? cur
->next
: cur
->prev
);
1422 Collect1 (jmp_buf * e
, VNODE
*cur
, DIRECTION dir
, POLYAREA
**contours
, PLINE
** holes
, J_Rule j_rule
)
1424 PLINE
*p
= NULL
; /* start making contour */
1427 Gather (dir
== FORW
? cur
: cur
->next
, &p
, j_rule
,
1431 poly_DelContour (&p
);
1436 poly_PreContour (p
, TRUE
);
1440 DEBUGP ("adding contour with %d verticies and direction %c\n",
1441 p
->Count
, p
->Flags
.orient
? 'F' : 'B');
1443 PutContour (e
, p
, contours
, holes
, NULL
);
1447 /* some sort of computation error ? */
1449 DEBUGP ("Bad contour! Not enough points!!\n");
1451 poly_DelContour (&p
);
1456 Collect (jmp_buf * e
, PLINE
* a
, POLYAREA
** contours
, PLINE
** holes
,
1457 S_Rule s_rule
, J_Rule j_rule
)
1465 if (s_rule (cur
, &dir
) && cur
->Flags
.mark
== 0)
1466 Collect1(e
, cur
, dir
, contours
, holes
, j_rule
);
1468 if ((other
->cvc_prev
&& jump(&other
, &dir
, j_rule
)))
1469 Collect1(e
, other
, dir
, contours
, holes
, j_rule
);
1471 while ((cur
= cur
->next
) != &a
->head
);
1476 cntr_Collect (jmp_buf * e
, PLINE
** A
, POLYAREA
** contours
, PLINE
** holes
,
1477 int action
, PLINE
* parent
)
1481 if ((*A
)->Flags
.status
== ISECTED
)
1486 Collect (e
, *A
, contours
, holes
, UniteS_Rule
, UniteJ_Rule
);
1489 Collect (e
, *A
, contours
, holes
, IsectS_Rule
, IsectJ_Rule
);
1492 Collect (e
, *A
, contours
, holes
, XorS_Rule
, XorJ_Rule
);
1495 Collect (e
, *A
, contours
, holes
, SubS_Rule
, SubJ_Rule
);
1504 if ((*A
)->Flags
.status
== INSIDE
)
1507 /* disappear this contour */
1509 tmprev
->next
= NULL
;
1510 PutContour (e
, tmprev
, contours
, holes
, NULL
);
1515 if ((*A
)->Flags
.status
== INSIDE
)
1518 /* disappear this contour */
1520 tmprev
->next
= NULL
;
1521 poly_InvContour (tmprev
);
1522 PutContour (e
, tmprev
, contours
, holes
, NULL
);
1528 if ((*A
)->Flags
.status
== OUTSIDE
)
1531 /* disappear this contour */
1533 tmprev
->next
= NULL
;
1534 PutContour (e
, tmprev
, contours
, holes
, parent
);
1541 } /* cntr_Collect */
1544 M_B_AREA_Collect (jmp_buf * e
, POLYAREA
* bfst
, POLYAREA
** contours
,
1545 PLINE
** holes
, int action
)
1548 PLINE
**cur
, **next
, *tmp
;
1553 for (cur
= &b
->contours
; *cur
!= NULL
; cur
= next
)
1555 next
= &((*cur
)->next
);
1556 if ((*cur
)->Flags
.status
== ISECTED
)
1559 if ((*cur
)->Flags
.status
== INSIDE
)
1564 poly_InvContour (*cur
);
1570 tmp
->Flags
.status
= UNKNWN
;
1571 PutContour (e
, tmp
, contours
, holes
, NULL
);
1574 break; /* nothing to do - already included */
1576 else if ((*cur
)->Flags
.status
== OUTSIDE
)
1586 tmp
->Flags
.status
= UNKNWN
;
1587 PutContour (e
, tmp
, contours
, holes
, NULL
);
1591 break; /* do nothing, not included */
1595 while ((b
= b
->f
) != bfst
);
1600 M_POLYAREA_Collect (jmp_buf * e
, POLYAREA
* afst
, POLYAREA
** contours
,
1601 PLINE
** holes
, int action
, BOOLp maybe
)
1604 PLINE
**cur
, **next
, *parent
;
1607 while ((a
= a
->f
) != afst
);
1608 /* now the non-intersect parts are collected in temp/holes */
1611 if (maybe
&& a
->contours
->Flags
.status
!= ISECTED
)
1612 parent
= a
->contours
;
1615 for (cur
= &a
->contours
; *cur
!= NULL
; cur
= next
)
1617 next
= &((*cur
)->next
);
1618 /* if we disappear a contour, don't advance twice */
1620 (e
, cur
, contours
, holes
, action
,
1621 *cur
== parent
? NULL
: parent
))
1625 while ((a
= a
->f
) != afst
);
1628 /* determine if two polygons touch or overlap */
1630 Touching (POLYAREA
* a
, POLYAREA
* b
)
1635 if ((code
= setjmp (e
)) == 0)
1638 if (!poly_Valid (a
))
1640 if (!poly_Valid (b
))
1643 M_POLYAREA_intersect (&e
, a
, b
, False
);
1645 if (M_POLYAREA_label (a
, b
, TRUE
))
1647 if (M_POLYAREA_label (b
, a
, TRUE
))
1650 else if (code
== TOUCHES
)
1655 /* the main clipping routines */
1657 poly_Boolean (const POLYAREA
* a_org
, const POLYAREA
* b_org
,
1658 POLYAREA
** res
, int action
)
1660 POLYAREA
*a
= NULL
, *b
= NULL
;
1662 if (!poly_M_Copy0 (&a
, a_org
) || !poly_M_Copy0 (&b
, b_org
))
1663 return err_no_memory
;
1665 return poly_Boolean_free (a
, b
, res
, action
);
1666 } /* poly_Boolean */
1668 /* just like poly_Boolean but frees the input polys */
1670 poly_Boolean_free (POLYAREA
* ai
, POLYAREA
* bi
, POLYAREA
** res
, int action
)
1672 POLYAREA
*a
= ai
, *b
= bi
;
1673 PLINE
*p
, *holes
= NULL
;
1710 if ((code
= setjmp (e
)) == 0)
1713 assert (poly_Valid (a
));
1714 assert (poly_Valid (b
));
1717 M_POLYAREA_intersect (&e
, a
, b
, TRUE
);
1719 M_POLYAREA_label (a
, b
, FALSE
);
1720 M_POLYAREA_label (b
, a
, FALSE
);
1722 M_POLYAREA_Collect (&e
, a
, res
, &holes
, action
, b
->f
== b
1723 && !b
->contours
->next
1724 && b
->contours
->Flags
.status
!= ISECTED
);
1726 M_B_AREA_Collect (&e
, b
, res
, &holes
, action
);
1729 InsertHoles (&e
, *res
, &holes
);
1731 /* delete holes if any left */
1732 while ((p
= holes
) != NULL
)
1735 poly_DelContour (&p
);
1743 assert (!*res
|| poly_Valid (*res
));
1745 } /* poly_Boolean_free */
1748 clear_marks (POLYAREA
* p
)
1756 for (c
= n
->contours
; c
; c
= c
->next
)
1763 while ((v
= v
->next
) != &c
->head
);
1766 while ((n
= n
->f
) != p
);
1769 /* compute the intersection and subtraction (divides "a" into two pieces)
1770 * and frees the input polys. This assumes that bi is a single simple polygon.
1773 poly_AndSubtract_free (POLYAREA
* ai
, POLYAREA
* bi
,
1774 POLYAREA
** aandb
, POLYAREA
** aminusb
)
1776 POLYAREA
*a
= ai
, *b
= bi
;
1777 PLINE
*p
, *holes
= NULL
;
1784 if ((code
= setjmp (e
)) == 0)
1788 if (!poly_Valid (a
))
1790 if (!poly_Valid (b
))
1793 M_POLYAREA_intersect (&e
, a
, b
, TRUE
);
1795 M_POLYAREA_label (a
, b
, FALSE
);
1796 M_POLYAREA_label (b
, a
, FALSE
);
1798 M_POLYAREA_Collect (&e
, a
, aandb
, &holes
, PBO_ISECT
, FALSE
);
1799 InsertHoles (&e
, *aandb
, &holes
);
1800 assert (poly_Valid (*aandb
));
1801 /* delete holes if any left */
1802 while ((p
= holes
) != NULL
)
1805 poly_DelContour (&p
);
1810 M_POLYAREA_Collect (&e
, a
, aminusb
, &holes
, PBO_SUB
, FALSE
);
1811 InsertHoles (&e
, *aminusb
, &holes
);
1814 assert (poly_Valid (*aminusb
));
1816 /* delete holes if any left */
1817 while ((p
= holes
) != NULL
)
1820 poly_DelContour (&p
);
1827 poly_Free (aminusb
);
1830 assert (!*aandb
|| poly_Valid (*aandb
));
1831 assert (!*aminusb
|| poly_Valid (*aminusb
));
1833 } /* poly_AndSubtract_free */
1836 cntrbox_pointin (PLINE
* c
, Vector p
)
1838 return (p
[0] >= c
->xmin
&& p
[1] >= c
->ymin
&&
1839 p
[0] <= c
->xmax
&& p
[1] <= c
->ymax
);
1844 node_neighbours (VNODE
* a
, VNODE
* b
)
1846 return (a
== b
) || (a
->next
== b
) || (b
->next
== a
) || (a
->next
== b
->next
);
1850 poly_CreateNode (Vector v
)
1856 res
= (VNODE
*) calloc (1, sizeof (VNODE
));
1859 // bzero (res, sizeof (VNODE) - sizeof(Vector));
1867 poly_IniContour (PLINE
* c
)
1871 /* bzero (c, sizeof(PLINE)); */
1872 c
->head
.next
= c
->head
.prev
= &c
->head
;
1873 c
->xmin
= c
->ymin
= 0x7fffffff;
1874 c
->xmax
= c
->ymax
= 0x80000000;
1878 poly_NewContour (Vector v
)
1882 res
= (PLINE
*) calloc (1, sizeof (PLINE
));
1886 poly_IniContour (res
);
1890 Vcopy (res
->head
.point
, v
);
1891 cntrbox_adjust (res
, v
);
1898 poly_ClrContour (PLINE
* c
)
1903 while ((cur
= c
->head
.next
) != &c
->head
)
1905 poly_ExclVertex (cur
);
1908 poly_IniContour (c
);
1912 poly_DelContour (PLINE
** c
)
1918 for (cur
= (*c
)->head
.prev
; cur
!= &(*c
)->head
; cur
= prev
)
1921 if (cur
->cvc_next
!= NULL
)
1923 free (cur
->cvc_next
);
1924 free (cur
->cvc_prev
);
1928 if ((*c
)->head
.cvc_next
!= NULL
)
1930 free ((*c
)->head
.cvc_next
);
1931 free ((*c
)->head
.cvc_prev
);
1933 /* FIXME -- strict aliasing violation. */
1936 rtree_t
*r
= (*c
)->tree
;
1937 r_destroy_tree (&r
);
1939 free (*c
), *c
= NULL
;
1943 poly_PreContour (PLINE
* C
, BOOLp optimize
)
1953 for (c
= (p
= &C
->head
)->next
; c
!= &C
->head
; c
= (p
= c
)->next
)
1955 /* if the previous node is on the same line with this one, we should remove it */
1956 Vsub2 (p1
, c
->point
, p
->point
);
1957 Vsub2 (p2
, c
->next
->point
, c
->point
);
1958 /* If the product below is zero then
1959 * the points on either side of c
1960 * are on the same line!
1961 * So, remove the point c
1964 if (vect_det2 (p1
, p2
) == 0)
1966 poly_ExclVertex (c
);
1973 C
->xmin
= C
->xmax
= C
->head
.point
[0];
1974 C
->ymin
= C
->ymax
= C
->head
.point
[1];
1976 p
= (c
= &C
->head
)->prev
;
1981 /* calculate area for orientation */
1983 (double) (p
->point
[0] - c
->point
[0]) * (p
->point
[1] +
1985 cntrbox_adjust (C
, c
->point
);
1988 while ((c
= (p
= c
)->next
) != &C
->head
);
1990 C
->area
= ABS (area
);
1992 C
->Flags
.orient
= ((area
< 0) ? PLF_INV
: PLF_DIR
);
1993 C
->tree
= make_edge_tree (C
);
1994 } /* poly_PreContour */
1997 flip_cb (const BoxType
* b
, void *cl
)
1999 struct seg
*s
= (struct seg
*) b
;
2005 poly_InvContour (PLINE
* c
)
2015 cur
->next
= cur
->prev
;
2017 /* fix the segment tree */
2019 while ((cur
= next
) != &c
->head
);
2020 c
->Flags
.orient
^= 1;
2023 r
= r_search (c
->tree
, NULL
, NULL
, flip_cb
, NULL
);
2024 assert (r
== c
->Count
);
2029 poly_ExclVertex (VNODE
* node
)
2031 assert (node
!= NULL
);
2034 free (node
->cvc_next
);
2035 free (node
->cvc_prev
);
2037 node
->prev
->next
= node
->next
;
2038 node
->next
->prev
= node
->prev
;
2042 poly_InclVertex (VNODE
* after
, VNODE
* node
)
2045 assert (after
!= NULL
);
2046 assert (node
!= NULL
);
2049 node
->next
= after
->next
;
2050 after
->next
= after
->next
->prev
= node
;
2051 /* remove points on same line */
2052 if (node
->prev
->prev
== node
)
2053 return; /* we don't have 3 points in the poly yet */
2054 a
= (node
->point
[1] - node
->prev
->prev
->point
[1]);
2055 a
*= (node
->prev
->point
[0] - node
->prev
->prev
->point
[0]);
2056 b
= (node
->point
[0] - node
->prev
->prev
->point
[0]);
2057 b
*= (node
->prev
->point
[1] - node
->prev
->prev
->point
[1]);
2058 if (fabs (a
- b
) < EPSILON
)
2060 VNODE
*t
= node
->prev
;
2061 t
->prev
->next
= node
;
2062 node
->prev
= t
->prev
;
2068 poly_CopyContour (PLINE
** dst
, PLINE
* src
)
2070 VNODE
*cur
, *newnode
;
2072 assert (src
!= NULL
);
2074 *dst
= poly_NewContour (src
->head
.point
);
2078 (*dst
)->Count
= src
->Count
;
2079 (*dst
)->Flags
.orient
= src
->Flags
.orient
;
2080 (*dst
)->xmin
= src
->xmin
, (*dst
)->xmax
= src
->xmax
;
2081 (*dst
)->ymin
= src
->ymin
, (*dst
)->ymax
= src
->ymax
;
2082 (*dst
)->area
= src
->area
;
2084 for (cur
= src
->head
.next
; cur
!= &src
->head
; cur
= cur
->next
)
2086 if ((newnode
= poly_CreateNode (cur
->point
)) == NULL
)
2088 // newnode->Flags = cur->Flags;
2089 poly_InclVertex ((*dst
)->head
.prev
, newnode
);
2091 (*dst
)->tree
= make_edge_tree (*dst
);
2095 /**********************************************************************/
2096 /* polygon routines */
2099 poly_Copy0 (POLYAREA
** dst
, const POLYAREA
* src
)
2103 *dst
= calloc (1, sizeof (POLYAREA
));
2107 return poly_Copy1 (*dst
, src
);
2111 poly_Copy1 (POLYAREA
* dst
, const POLYAREA
* src
)
2113 PLINE
*cur
, **last
= &dst
->contours
;
2116 dst
->f
= dst
->b
= dst
;
2118 for (cur
= src
->contours
; cur
!= NULL
; cur
= cur
->next
)
2120 if (!poly_CopyContour (last
, cur
))
2122 last
= &(*last
)->next
;
2128 poly_M_Incl (POLYAREA
** list
, POLYAREA
* a
)
2131 a
->f
= a
->b
= a
, *list
= a
;
2136 (*list
)->b
= (*list
)->b
->f
= a
;
2141 poly_M_Copy0 (POLYAREA
** dst
, const POLYAREA
* srcfst
)
2143 const POLYAREA
*src
= srcfst
;
2151 if ((di
= poly_Create ()) == NULL
|| !poly_Copy1 (di
, src
))
2153 poly_M_Incl (dst
, di
);
2155 while ((src
= src
->f
) != srcfst
);
2160 poly_InclContour (POLYAREA
* p
, PLINE
* c
)
2164 if ((c
== NULL
) || (p
== NULL
))
2166 if (c
->Flags
.orient
== PLF_DIR
)
2168 if (p
->contours
!= NULL
)
2174 if (p
->contours
== NULL
)
2176 /* link at front of hole list */
2177 tmp
= p
->contours
->next
;
2178 p
->contours
->next
= c
;
2193 crossing (const BoxType
* b
, void *cl
)
2195 struct seg
*s
= (struct seg
*) b
;
2196 struct pip
*p
= (struct pip
*) cl
;
2198 if (s
->v
->point
[1] <= p
->p
[1])
2200 if (s
->v
->next
->point
[1] > p
->p
[1])
2204 Vsub2 (v1
, s
->v
->next
->point
, s
->v
->point
);
2205 Vsub2 (v2
, p
->p
, s
->v
->point
);
2206 cross
= (long long) v1
[0] * v2
[1] - (long long) v2
[0] * v1
[1];
2210 longjmp (p
->env
, 1);
2218 if (s
->v
->next
->point
[1] <= p
->p
[1])
2222 Vsub2 (v1
, s
->v
->next
->point
, s
->v
->point
);
2223 Vsub2 (v2
, p
->p
, s
->v
->point
);
2224 cross
= (long long) v1
[0] * v2
[1] - (long long) v2
[0] * v1
[1];
2228 longjmp (p
->env
, 1);
2238 poly_InsideContour (PLINE
* c
, Vector p
)
2243 if (!cntrbox_pointin (c
, p
))
2246 info
.p
[0] = ray
.X1
= p
[0];
2247 info
.p
[1] = ray
.Y1
= p
[1];
2248 ray
.X2
= 0x7fffffff;
2250 if (setjmp (info
.env
) == 0)
2251 r_search (c
->tree
, &ray
, NULL
, crossing
, &info
);
2256 poly_CheckInside (POLYAREA
* p
, Vector v0
)
2260 if ((p
== NULL
) || (v0
== NULL
) || (p
->contours
== NULL
))
2263 if (poly_InsideContour (cur
, v0
))
2265 for (cur
= cur
->next
; cur
!= NULL
; cur
= cur
->next
)
2266 if (poly_InsideContour (cur
, v0
))
2274 poly_M_CheckInside (POLYAREA
* p
, Vector v0
)
2278 if ((p
== NULL
) || (v0
== NULL
))
2283 if (poly_CheckInside (cur
, v0
))
2286 while ((cur
= cur
->f
) != p
);
2291 poly_ContourInContour (PLINE
* poly
, PLINE
* inner
)
2293 assert (poly
!= NULL
);
2294 assert (inner
!= NULL
);
2295 if (cntrbox_inside (inner
, poly
))
2296 return poly_InsideContour (poly
, inner
->head
.point
);
2301 poly_Init (POLYAREA
* p
)
2312 if ((res
= malloc (sizeof (POLYAREA
))) != NULL
)
2318 poly_FreeContours (PLINE
**pline
)
2322 while ((pl
= *pline
) != NULL
)
2325 poly_DelContour (&pl
);
2330 poly_Free (POLYAREA
** p
)
2336 for (cur
= (*p
)->f
; cur
!= *p
; cur
= (*p
)->f
)
2338 poly_FreeContours (&cur
->contours
);
2343 poly_FreeContours (&cur
->contours
);
2344 free (*p
), *p
= NULL
;
2348 inside_sector (VNODE
* pn
, Vector p2
)
2350 Vector cdir
, ndir
, pdir
;
2353 assert (pn
!= NULL
);
2354 vect_sub (cdir
, p2
, pn
->point
);
2355 vect_sub (pdir
, pn
->point
, pn
->prev
->point
);
2356 vect_sub (ndir
, pn
->next
->point
, pn
->point
);
2358 p_c
= vect_det2 (pdir
, cdir
) >= 0;
2359 n_c
= vect_det2 (ndir
, cdir
) >= 0;
2360 p_n
= vect_det2 (pdir
, ndir
) >= 0;
2362 if ((p_n
&& p_c
&& n_c
) || ((!p_n
) && (p_c
|| n_c
)))
2366 } /* inside_sector */
2368 /* returns TRUE if bad contour */
2370 poly_ChkContour (PLINE
* a
)
2372 VNODE
*a1
, *a2
, *hit1
, *hit2
;
2383 if (!node_neighbours (a1
, a2
) &&
2384 (icnt
= vect_inters2 (a1
->point
, a1
->next
->point
,
2385 a2
->point
, a2
->next
->point
, i1
, i2
)) > 0)
2390 if (vect_dist2 (i1
, a1
->point
) < EPSILON
)
2392 else if (vect_dist2 (i1
, a1
->next
->point
) < EPSILON
)
2397 if (vect_dist2 (i1
, a2
->point
) < EPSILON
)
2399 else if (vect_dist2 (i1
, a2
->next
->point
) < EPSILON
)
2405 /* now check if they are inside each other */
2406 if (inside_sector (hit1
, hit2
->prev
->point
) ||
2407 inside_sector (hit1
, hit2
->next
->point
) ||
2408 inside_sector (hit2
, hit1
->prev
->point
) ||
2409 inside_sector (hit2
, hit1
->next
->point
))
2414 while ((a2
= a2
->next
) != &a
->head
);
2416 while ((a1
= a1
->next
) != &a
->head
);
2422 poly_Valid (POLYAREA
* p
)
2426 if ((p
== NULL
) || (p
->contours
== NULL
))
2429 if (p
->contours
->Flags
.orient
== PLF_INV
|| poly_ChkContour (p
->contours
))
2433 DEBUGP ("Invalid Outer PLINE\n");
2434 if (p
->contours
->Flags
.orient
== PLF_INV
)
2435 DEBUGP ("failed orient\n");
2436 if (poly_ChkContour (p
->contours
))
2437 DEBUGP ("failed self-intersection\n");
2438 v
= &p
->contours
->head
;
2441 fprintf (stderr
, "%d %d 100 100 \"\"]\n", v
->point
[0], v
->point
[1]);
2442 fprintf (stderr
, "Line [%d %d ", v
->point
[0], v
->point
[1]);
2444 while ((v
= v
->next
) != &p
->contours
->head
);
2448 for (c
= p
->contours
->next
; c
!= NULL
; c
= c
->next
)
2450 if (c
->Flags
.orient
== PLF_DIR
||
2451 poly_ChkContour (c
) || !poly_ContourInContour (p
->contours
, c
))
2455 DEBUGP ("Invalid Inner PLINE orient = %d\n", c
->Flags
.orient
);
2456 if (c
->Flags
.orient
== PLF_DIR
)
2457 DEBUGP ("failed orient\n");
2458 if (poly_ChkContour (c
))
2459 DEBUGP ("failed self-intersection\n");
2460 if (!poly_ContourInContour (p
->contours
, c
))
2461 DEBUGP ("failed containment\n");
2465 fprintf (stderr
, "%d %d 100 100 \"\"]\n", v
->point
[0],
2467 fprintf (stderr
, "Line [%d %d ", v
->point
[0], v
->point
[1]);
2469 while ((v
= v
->next
) != &c
->head
);
2478 Vector vect_zero
= { (long) 0, (long) 0 };
2480 /*********************************************************************/
2481 /* L o n g V e c t o r S t u f f */
2482 /*********************************************************************/
2485 vect_init (Vector v
, double x
, double y
)
2491 #define Vzero(a) ((a)[0] == 0. && (a)[1] == 0.)
2493 #define Vsub(a,b,c) {(a)[0]=(b)[0]-(c)[0];(a)[1]=(b)[1]-(c)[1];}
2496 vect_equal (Vector v1
, Vector v2
)
2498 return (v1
[0] == v2
[0] && v1
[1] == v2
[1]);
2503 vect_sub (Vector res
, Vector v1
, Vector v2
)
2505 Vsub (res
, v1
, v2
)} /* vect_sub */
2508 vect_min (Vector v1
, Vector v2
, Vector v3
)
2510 v1
[0] = (v2
[0] < v3
[0]) ? v2
[0] : v3
[0];
2511 v1
[1] = (v2
[1] < v3
[1]) ? v2
[1] : v3
[1];
2515 vect_max (Vector v1
, Vector v2
, Vector v3
)
2517 v1
[0] = (v2
[0] > v3
[0]) ? v2
[0] : v3
[0];
2518 v1
[1] = (v2
[1] > v3
[1]) ? v2
[1] : v3
[1];
2522 vect_len2 (Vector v
)
2524 return ((double) v
[0] * v
[0] + (double) v
[1] * v
[1]); /* why sqrt? only used for compares */
2528 vect_dist2 (Vector v1
, Vector v2
)
2530 double dx
= v1
[0] - v2
[0];
2531 double dy
= v1
[1] - v2
[1];
2533 return (dx
* dx
+ dy
* dy
); /* why sqrt */
2536 /* value has sign of angle between vectors */
2538 vect_det2 (Vector v1
, Vector v2
)
2540 return (((double) v1
[0] * v2
[1]) - ((double) v2
[0] * v1
[1]));
2544 vect_m_dist (Vector v1
, Vector v2
)
2546 double dx
= v1
[0] - v2
[0];
2547 double dy
= v1
[1] - v2
[1];
2548 double dd
= (dx
* dx
+ dy
* dy
); /* sqrt */
2561 (C) 1993 Klamer Schutte
2562 (C) 1997 Michael Leonov, Alexey Nikitin
2566 vect_inters2 (Vector p1
, Vector p2
, Vector q1
, Vector q2
,
2567 Vector S1
, Vector S2
)
2570 double rpx
, rpy
, rqx
, rqy
;
2572 if (max (p1
[0], p2
[0]) < min (q1
[0], q2
[0]) ||
2573 max (q1
[0], q2
[0]) < min (p1
[0], p2
[0]) ||
2574 max (p1
[1], p2
[1]) < min (q1
[1], q2
[1]) ||
2575 max (q1
[1], q2
[1]) < min (p1
[1], p2
[1]))
2578 rpx
= p2
[0] - p1
[0];
2579 rpy
= p2
[1] - p1
[1];
2580 rqx
= q2
[0] - q1
[0];
2581 rqy
= q2
[1] - q1
[1];
2583 deel
= rpy
* rqx
- rpx
* rqy
; /* -vect_det(rp,rq); */
2585 /* coordinates are 30-bit integers so deel will be exactly zero
2586 * if the lines are parallel
2589 if (deel
== 0) /* parallel */
2591 double dc1
, dc2
, d1
, d2
, h
; /* Check to see whether p1-p2 and q1-q2 are on the same line */
2592 Vector hp1
, hq1
, hp2
, hq2
, q1p1
, q1q2
;
2594 Vsub2 (q1p1
, q1
, p1
);
2595 Vsub2 (q1q2
, q1
, q2
);
2598 /* If this product is not zero then p1-p2 and q1-q2 are not on same line! */
2599 if (vect_det2 (q1p1
, q1q2
) != 0)
2601 dc1
= 0; /* m_len(p1 - p1) */
2603 dc2
= vect_m_dist (p1
, p2
);
2604 d1
= vect_m_dist (p1
, q1
);
2605 d2
= vect_m_dist (p1
, q2
);
2607 /* Sorting the independent points from small to large */
2613 { /* hv and h are used as help-variable. */
2615 h
= dc1
, dc1
= dc2
, dc2
= h
;
2620 h
= d1
, d1
= d2
, d2
= h
;
2623 /* Now the line-pieces are compared */
2655 return (Vequ2 (S1
, S2
) ? 1 : 2);
2658 { /* not parallel */
2660 * We have the lines:
2661 * l1: p1 + s(p2 - p1)
2662 * l2: q1 + t(q2 - q1)
2663 * And we want to know the intersection point.
2665 * p1 + s(p2-p1) = q1 + t(q2-q1)
2666 * which is similar to the two equations:
2667 * p1x + s * rpx = q1x + t * rqx
2668 * p1y + s * rpy = q1y + t * rqy
2669 * Multiplying these by rpy resp. rpx gives:
2670 * rpy * p1x + s * rpx * rpy = rpy * q1x + t * rpy * rqx
2671 * rpx * p1y + s * rpx * rpy = rpx * q1y + t * rpx * rqy
2672 * Subtracting these gives:
2673 * rpy * p1x - rpx * p1y = rpy * q1x - rpx * q1y + t * ( rpy * rqx - rpx * rqy )
2674 * So t can be isolated:
2675 * t = (rpy * ( p1x - q1x ) + rpx * ( - p1y + q1y )) / ( rpy * rqx - rpx * rqy )
2676 * and s can be found similarly
2677 * s = (rqy * (q1x - p1x) + rqx * (p1y - q1y))/( rqy * rpx - rqx * rpy)
2680 if (Vequ2 (q1
, p1
) || Vequ2 (q1
, p2
))
2685 else if (Vequ2 (q2
, p1
) || Vequ2 (q2
, p2
))
2692 s
= (rqy
* (p1
[0] - q1
[0]) + rqx
* (q1
[1] - p1
[1])) / deel
;
2693 if (s
< 0 || s
> 1.)
2695 t
= (rpy
* (p1
[0] - q1
[0]) + rpx
* (q1
[1] - p1
[1])) / deel
;
2696 if (t
< 0 || t
> 1.)
2699 S1
[0] = q1
[0] + ROUND (t
* rqx
);
2700 S1
[1] = q1
[1] + ROUND (t
* rqy
);
2704 } /* vect_inters2 */
2706 /* how about expanding polygons so that edges can be arcs rather than
2707 * lines. Consider using the third coordinate to store the radius of the
2708 * arc. The arc would pass through the vertex points. Positive radius
2709 * would indicate the arc bows left (center on right of P1-P2 path)
2710 * negative radius would put the center on the other side. 0 radius
2711 * would mean the edge is a line instead of arc
2712 * The intersections of the two circles centered at the vertex points
2713 * would determine the two possible arc centers. If P2.x > P1.x then
2714 * the center with smaller Y is selected for positive r. If P2.y > P1.y
2715 * then the center with greate X is selected for positive r.
2717 * the vec_inters2() routine would then need to handle line-line
2718 * line-arc and arc-arc intersections.
2720 * perhaps reverse tracing the arc would require look-ahead to check