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
);
145 fprintf (stderr
, "NEXT PLINE\n");
149 poly_dump (POLYAREA
* p
)
155 pline_dump (&p
->contours
->head
);
157 while ((p
= p
->f
) != f
);
158 fprintf (stderr
, "NEXT_POLY\n");
162 /***************************************************************/
163 /* routines for processing intersections */
167 (C) 1993 Klamer Schutte
168 (C) 1997 Alexey Nikitin, Michael Leonov
171 returns a bit field in new_point that indicates where the
173 1 means a new node was created and inserted
174 4 means the intersection was not on the dest point
177 node_add (VNODE
* dest
, Vector po
, int *new_point
)
181 if (vect_equal (po
, dest
->point
))
183 if (vect_equal (po
, dest
->next
->point
))
188 p
= poly_CreateNode (po
);
193 p
->next
= dest
->next
;
194 p
->cvc_prev
= p
->cvc_next
= NULL
;
195 p
->Flags
.status
= UNKNWN
;
196 return (dest
->next
= dest
->next
->prev
= p
);
199 #define ISECT_BAD_PARAM (-1)
200 #define ISECT_NO_MEMORY (-2)
208 new_descriptor (VNODE
* a
, char poly
, char side
)
210 CVCList
*l
= (CVCList
*) malloc (sizeof (CVCList
));
212 register double ang
, dx
, dy
;
220 l
->next
= l
->prev
= l
;
221 if (side
== 'P') /* previous */
222 vect_sub (v
, a
->prev
->point
, a
->point
);
224 vect_sub (v
, a
->next
->point
, a
->point
);
225 /* Uses slope/(slope+1) in quadrant 1 as a proxy for the angle.
226 * It still has the same monotonic sort result
227 * and is far less expensive to compute than the real angle.
229 if (vect_equal (v
, vect_zero
))
233 if (a
->prev
->cvc_prev
== (CVCList
*) - 1)
234 a
->prev
->cvc_prev
= a
->prev
->cvc_next
= NULL
;
235 poly_ExclVertex (a
->prev
);
236 vect_sub (v
, a
->prev
->point
, a
->point
);
240 if (a
->next
->cvc_prev
== (CVCList
*) - 1)
241 a
->next
->cvc_prev
= a
->next
->cvc_next
= NULL
;
242 poly_ExclVertex (a
->next
);
243 vect_sub (v
, a
->next
->point
, a
->point
);
246 assert (!vect_equal (v
, vect_zero
));
247 dx
= fabs ((double) v
[0]);
248 dy
= fabs ((double) v
[1]);
249 ang
= dy
/ (dy
+ dx
);
250 /* now move to the actual quadrant */
251 if (v
[0] < 0 && v
[1] >= 0)
252 ang
= 2.0 - ang
; /* 2nd quadrant */
253 else if (v
[0] < 0 && v
[1] < 0)
254 ang
+= 2.0; /* 3rd quadrant */
255 else if (v
[0] >= 0 && v
[1] < 0)
256 ang
= 4.0 - ang
; /* 4th quadrant */
258 assert (ang
>= 0.0 && ang
<= 4.0);
260 DEBUGP ("node on %c at (%d,%d) assigned angle %g on side %c\n", poly
,
261 a
->point
[0], a
->point
[1], ang
, side
);
270 argument a is a cross-vertex node.
271 argument poly is the polygon it comes from ('A' or 'B')
272 argument side is the side this descriptor goes on ('P' for previous
274 argument start is the head of the list of cvclists
277 insert_descriptor (VNODE
* a
, char poly
, char side
, CVCList
* start
)
279 CVCList
*l
, *new, *big
, *small
;
281 if (!(new = new_descriptor (a
, poly
, side
)))
283 /* search for the CVCList for this point */
286 start
= new; /* return is also new, so we know where start is */
287 start
->head
= new; /* circular list */
296 if (l
->parent
->point
[0] == a
->point
[0]
297 && l
->parent
->point
[1] == a
->point
[1])
298 { /* this CVCList is at our point */
303 if (l
->head
->parent
->point
[0] == start
->parent
->point
[0]
304 && l
->head
->parent
->point
[1] == start
->parent
->point
[1])
306 /* this seems to be a new point */
307 /* link this cvclist to the list of all cvclists */
308 for (; l
->head
!= new; l
= l
->next
)
318 l
= big
= small
= start
;
321 if (l
->next
->angle
< l
->angle
) /* find start/end of list */
326 else if (new->angle
>= l
->angle
&& new->angle
<= l
->next
->angle
)
328 /* insert new cvc if it lies between existing points */
331 l
->next
= l
->next
->prev
= new;
335 while ((l
= l
->next
) != start
);
336 /* didn't find it between points, it must go on an end */
337 if (big
->angle
<= new->angle
)
340 new->next
= big
->next
;
341 big
->next
= big
->next
->prev
= new;
344 assert (small
->angle
>= new->angle
);
346 new->prev
= small
->prev
;
347 small
->prev
= small
->prev
->next
= new;
353 (C) 1993 Klamer Schutte
354 (C) 1997 Alexey Nikitin, Michael Leonov
356 return 1 if new node in b, 2 if new node in a and 3 if new node in both
360 node_add_point (VNODE
* a
, VNODE
* b
, Vector p
)
364 VNODE
*node_a
, *node_b
;
366 node_a
= node_add (a
, p
, &res
);
368 node_b
= node_add (b
, p
, &res
);
370 if (node_a
== NULL
|| node_b
== NULL
)
371 return ISECT_NO_MEMORY
;
372 node_b
->cvc_prev
= node_b
->cvc_next
= (CVCList
*) - 1;
373 node_a
->cvc_prev
= node_a
->cvc_next
= (CVCList
*) - 1;
375 } /* node_add_point */
382 node_label (VNODE
* pn
)
389 assert (pn
->cvc_next
);
390 this_poly
= pn
->cvc_next
->poly
;
391 /* search counter-clockwise in the cross vertex connectivity (CVC) list
393 * check for shared edges (that could be prev or next in the list since the angles are equal)
394 * and check if this edge (pn -> pn->next) is found between the other poly's entry and exit
396 if (pn
->cvc_next
->angle
== pn
->cvc_next
->prev
->angle
)
398 l
= pn
->cvc_next
->prev
;
399 assert (l
->poly
!= this_poly
);
402 l
= pn
->cvc_next
->next
;
403 assert (l
&& l
->angle
>= 0 && l
->angle
<= 4.0);
404 if (l
->poly
!= this_poly
)
408 if (l
->parent
->prev
->point
[0] == pn
->next
->point
[0] &&
409 l
->parent
->prev
->point
[1] == pn
->next
->point
[1])
412 pn
->shared
= l
->parent
->prev
;
419 if (l
->angle
== pn
->cvc_next
->angle
)
421 assert (l
->parent
->next
->point
[0] == pn
->next
->point
[0] &&
422 l
->parent
->next
->point
[1] == pn
->next
->point
[1]);
424 pn
->shared
= l
->parent
;
430 if (region
== UNKNWN
)
432 for (l
= l
->next
; l
!= pn
->cvc_next
; l
= l
->next
)
434 if (l
->poly
!= this_poly
)
444 assert (region
!= UNKNWN
);
445 assert (NODE_LABEL (pn
) == UNKNWN
|| NODE_LABEL (pn
) == region
);
446 LABEL_NODE (pn
, region
);
447 if (region
== SHARED
|| region
== SHARED2
)
457 add_descriptors (PLINE
* pl
, char poly
, CVCList
* list
)
459 VNODE
*node
= &pl
->head
;
465 assert (node
->cvc_prev
== (CVCList
*) - 1
466 && node
->cvc_next
== (CVCList
*) - 1);
467 list
= node
->cvc_prev
= insert_descriptor (node
, poly
, 'P', list
);
470 list
= node
->cvc_next
= insert_descriptor (node
, poly
, 'N', list
);
475 while ((node
= node
->next
) != &pl
->head
);
480 cntrbox_adjust (PLINE
* c
, Vector p
)
482 c
->xmin
= min (c
->xmin
, p
[0]);
483 c
->xmax
= max (c
->xmax
, p
[0] + 1);
484 c
->ymin
= min (c
->ymin
, p
[1]);
485 c
->ymax
= max (c
->ymax
, p
[1] + 1);
488 /* some structures for handling segment intersections using the rtrees */
503 jmp_buf env
, sego
, *touch
;
508 * (C) 2006 harry eaton
509 * This replaces the segment in the tree with the two new segments after
510 * a vertex has been added
513 adjust_tree (rtree_t
* tree
, struct seg
*s
)
517 q
= malloc (sizeof (struct seg
));
522 q
->box
.X1
= min (q
->v
->point
[0], q
->v
->next
->point
[0]);
523 q
->box
.X2
= max (q
->v
->point
[0], q
->v
->next
->point
[0]) + 1;
524 q
->box
.Y1
= min (q
->v
->point
[1], q
->v
->next
->point
[1]);
525 q
->box
.Y2
= max (q
->v
->point
[1], q
->v
->next
->point
[1]) + 1;
526 r_insert_entry (tree
, (const BoxType
*) q
, 1);
527 q
= malloc (sizeof (struct seg
));
532 q
->box
.X1
= min (q
->v
->point
[0], q
->v
->next
->point
[0]);
533 q
->box
.X2
= max (q
->v
->point
[0], q
->v
->next
->point
[0]) + 1;
534 q
->box
.Y1
= min (q
->v
->point
[1], q
->v
->next
->point
[1]);
535 q
->box
.Y2
= max (q
->v
->point
[1], q
->v
->next
->point
[1]) + 1;
536 r_insert_entry (tree
, (const BoxType
*) q
, 1);
537 r_delete_entry (tree
, (const BoxType
*) s
);
543 * (C) 2006, harry eaton
544 * This prunes the search for boxes that don't intersect the segment.
547 seg_in_region (const BoxType
* b
, void *cl
)
549 struct info
*i
= (struct info
*) cl
;
551 /* for zero slope the search is aligned on the axis so it is already pruned */
554 y1
= i
->m
* b
->X1
+ i
->b
;
555 y2
= i
->m
* b
->X2
+ i
->b
;
556 if (min (y1
, y2
) >= b
->Y2
)
558 if (max (y1
, y2
) < b
->Y1
)
560 return 1; /* might intersect */
565 * (C) 2006 harry eaton
566 * This routine checks if the segment in the tree intersect the search segment.
567 * If it does, the plines are marked as intersected and the point is marked for
568 * the cvclist. If the point is not already a vertex, a new vertex is inserted
569 * and the search for intersections starts over at the beginning.
570 * That is potentially a significant time penalty, but it does solve the snap rounding
571 * problem. There are efficient algorithms for finding intersections with snap
572 * rounding, but I don't have time to implement them right now.
575 seg_in_seg (const BoxType
* b
, void *cl
)
577 struct info
*i
= (struct info
*) cl
;
578 struct seg
*s
= (struct seg
*) b
;
582 cnt
= vect_inters2 (s
->v
->point
, s
->v
->next
->point
,
583 i
->v
->point
, i
->v
->next
->point
, s1
, s2
);
586 if (i
->touch
) /* if checking touches one find and we're done */
587 longjmp (*i
->touch
, TOUCHES
);
588 i
->s
->p
->Flags
.status
= ISECTED
;
589 s
->p
->Flags
.status
= ISECTED
;
592 res
= node_add_point (i
->v
, s
->v
, cnt
> 1 ? s2
: s1
);
594 return 1; /* error */
595 /* adjust the bounding box and tree if necessary */
598 cntrbox_adjust (i
->s
->p
, cnt
> 1 ? s2
: s1
);
599 if (adjust_tree (i
->s
->p
->tree
, i
->s
))
602 /* if we added a node in the tree we need to change the tree */
605 cntrbox_adjust (s
->p
, cnt
> 1 ? s2
: s1
);
606 if (adjust_tree (i
->tree
, s
))
609 if (res
& 3) /* if a point was inserted start over */
611 #ifdef DEBUG_INTERSECT
612 DEBUGP ("new intersection at (%d, %d)\n", cnt
> 1 ? s2
[0] : s1
[0],
613 cnt
> 1 ? s2
[1] : s1
[1]);
622 make_edge_tree (PLINE
* pb
)
626 rtree_t
*ans
= r_create_tree (NULL
, 0, 0);
630 s
= malloc (sizeof (struct seg
));
631 if (bv
->point
[0] < bv
->next
->point
[0])
633 s
->box
.X1
= bv
->point
[0];
634 s
->box
.X2
= bv
->next
->point
[0] + 1;
638 s
->box
.X2
= bv
->point
[0] + 1;
639 s
->box
.X1
= bv
->next
->point
[0];
641 if (bv
->point
[1] < bv
->next
->point
[1])
643 s
->box
.Y1
= bv
->point
[1];
644 s
->box
.Y2
= bv
->next
->point
[1] + 1;
648 s
->box
.Y2
= bv
->point
[1] + 1;
649 s
->box
.Y1
= bv
->next
->point
[1];
653 r_insert_entry (ans
, (const BoxType
*) s
, 1);
655 while ((bv
= bv
->next
) != &pb
->head
);
660 get_seg (const BoxType
* b
, void *cl
)
662 struct info
*i
= (struct info
*) cl
;
663 struct seg
*s
= (struct seg
*) b
;
667 longjmp (i
->sego
, 1);
674 * (C) 2006, harry eaton
675 * This uses an rtree to find A-B intersections. Whenever a new vertex is
676 * added, the search for intersections is re-started because the rounding
677 * could alter the topology otherwise.
678 * This should use a faster algorithm for snap rounding intersection finding.
679 * The best algorthim is probably found in:
681 * "Improved output-sensitive snap rounding," John Hershberger, Proceedings
682 * of the 22nd annual symposium on Computational geomerty, 2006, pp 357-366.
683 * http://doi.acm.org/10.1145/1137856.1137909
685 * Algorithms described by de Berg, or Goodrich or Halperin, or Hobby would
686 * probably work as well.
691 intersect (jmp_buf * jb
, POLYAREA
* b
, POLYAREA
* a
, int add
)
693 PLINE
*pa
, *pb
; /* pline iterators */
696 VNODE
*av
; /* node iterators */
704 setjmp (info
.env
); /* we loop back here whenever a vertex is inserted */
708 while (pa
) /* Loop over the contours of POLYAREA "a" */
710 int found_overlapping_a_b_contour
= FALSE
;
712 while (pb
) /* Loop over the contours of POLYAREA "b" */
714 /* Are there overlapping bounds? */
715 if (pb
->xmin
<= pa
->xmax
&& pb
->xmax
>= pa
->xmin
&&
716 pb
->ymin
<= pa
->ymax
&& pb
->ymax
>= pa
->ymin
)
718 found_overlapping_a_b_contour
= TRUE
;
724 /* If we didn't find anything intersting, move onto the next "a" contour */
725 if (!found_overlapping_a_b_contour
)
732 /* something intersects so check the edges of the contour */
734 /* Pick which contour has the fewer points, and do the loop
735 * over that. The r_tree makes hit-testing against a contour
736 * faster, so we want to do that on the bigger contour.
738 if (pa
->Count
< pb
->Count
)
749 av
= &looping_over
->head
;
750 do /* Loop over the nodes in the smaller contour */
752 /* check this edge for any insertions */
755 /* compute the slant for region trimming */
756 dx
= av
->next
->point
[0] - av
->point
[0];
761 info
.m
= (av
->next
->point
[1] - av
->point
[1]) / dx
;
762 info
.b
= av
->point
[1] - info
.m
* av
->point
[0];
764 box
.X2
= (box
.X1
= av
->point
[0]) + 1;
765 box
.Y2
= (box
.Y1
= av
->point
[1]) + 1;
767 /* fill in the segment in info corresponding to this node */
768 if (setjmp (info
.sego
) == 0)
770 r_search (looping_over
->tree
, &box
, NULL
, get_seg
, &info
);
774 /* NB: If this actually hits anything, we are teleported back to the beginning */
775 info
.tree
= rtree_over
->tree
;
777 if (UNLIKELY (r_search (info
.tree
, &info
.s
->box
,
778 seg_in_region
, seg_in_seg
, &info
)))
779 return err_no_memory
; /* error */
781 while ((av
= av
->next
) != &looping_over
->head
);
783 /* Continue the with the _same_ "a" contour,
784 * testing it against the next "b" contour.
788 } /* end of setjmp loop */
793 M_POLYAREA_intersect (jmp_buf * e
, POLYAREA
* afst
, POLYAREA
* bfst
, int add
)
795 POLYAREA
*a
= afst
, *b
= bfst
;
796 PLINE
*curcA
, *curcB
;
797 CVCList
*the_list
= NULL
;
799 if (a
== NULL
|| b
== NULL
)
800 error (err_bad_parm
);
805 if (a
->contours
->xmax
>= b
->contours
->xmin
&&
806 a
->contours
->ymax
>= b
->contours
->ymin
&&
807 a
->contours
->xmin
<= b
->contours
->xmax
&&
808 a
->contours
->ymin
<= b
->contours
->ymax
)
810 if (UNLIKELY (intersect (e
, a
, b
, add
)))
811 error (err_no_memory
);
814 while (add
&& (a
= a
->f
) != afst
);
815 for (curcB
= b
->contours
; curcB
!= NULL
; curcB
= curcB
->next
)
816 if (curcB
->Flags
.status
== ISECTED
)
818 the_list
= add_descriptors (curcB
, 'B', the_list
);
819 if (UNLIKELY (the_list
== NULL
))
820 error (err_no_memory
);
823 while (add
&& (b
= b
->f
) != bfst
);
826 for (curcA
= a
->contours
; curcA
!= NULL
; curcA
= curcA
->next
)
827 if (curcA
->Flags
.status
== ISECTED
)
829 the_list
= add_descriptors (curcA
, 'A', the_list
);
830 if (UNLIKELY (the_list
== NULL
))
831 error (err_no_memory
);
834 while (add
&& (a
= a
->f
) != afst
);
835 } /* M_POLYAREA_intersect */
838 cntrbox_inside (PLINE
* c1
, PLINE
* c2
)
840 assert (c1
!= NULL
&& c2
!= NULL
);
841 return ((c1
->xmin
>= c2
->xmin
) &&
842 (c1
->ymin
>= c2
->ymin
) &&
843 (c1
->xmax
<= c2
->xmax
) && (c1
->ymax
<= c2
->ymax
));
846 /*****************************************************************/
847 /* Routines for making labels */
849 /* cntr_in_M_POLYAREA
850 returns poly is inside outfst ? TRUE : FALSE */
852 cntr_in_M_POLYAREA (PLINE
* poly
, POLYAREA
* outfst
, BOOLp test
)
855 POLYAREA
*outer
= outfst
;
858 assert (poly
!= NULL
);
859 assert (outer
!= NULL
);
861 heap
= heap_create ();
864 if (cntrbox_inside (poly
, outer
->contours
))
865 heap_insert (heap
, outer
->contours
->area
, (void *) outer
);
867 /* if checking touching, use only the first polygon */
868 while (!test
&& (outer
= outer
->f
) != outfst
);
869 /* we need only check the smallest poly container
870 * but we must loop in case the box containter is not
871 * the poly container */
874 if (heap_is_empty (heap
))
876 outer
= (POLYAREA
*) heap_remove_smallest (heap
);
877 if (poly_ContourInContour (outer
->contours
, poly
))
879 for (curc
= outer
->contours
->next
; curc
!= NULL
; curc
= curc
->next
)
880 if (poly_ContourInContour (curc
, poly
))
882 /* it's inside a hole in the smallest polygon
883 * no need to check the other polygons */
884 heap_destroy (&heap
);
887 heap_destroy (&heap
);
892 heap_destroy (&heap
);
894 } /* cntr_in_M_POLYAREA */
901 static char u
[] = "UNKNOWN";
902 static char i
[] = "INSIDE";
903 static char o
[] = "OUTSIDE";
904 static char s
[] = "SHARED";
905 static char s2
[] = "SHARED2";
907 switch (NODE_LABEL (v
))
923 print_labels (PLINE
* a
)
929 DEBUGP ("(%d,%d)->(%d,%d) labeled %s\n", c
->point
[0], c
->point
[1],
930 c
->next
->point
[0], c
->next
->point
[1], theState (c
));
932 while ((c
= c
->next
) != &a
->head
);
939 (C) 1993 Klamer Schutte
940 (C) 1997 Alexey Nikitin, Michael Leonov
944 label_contour (PLINE
* a
)
946 VNODE
*cur
= &a
->head
;
947 int did_label
= FALSE
, label
= UNKNWN
;
953 if (NODE_LABEL (cur
) != UNKNWN
)
955 label
= NODE_LABEL (cur
);
958 if (cur
->cvc_next
) /* examine cross vertex */
960 label
= node_label (cur
);
963 else if (label
== INSIDE
|| label
== OUTSIDE
)
965 LABEL_NODE (cur
, label
);
969 while ((cur
= cur
->next
) != &a
->head
|| did_label
);
970 #ifdef DEBUG_ALL_LABELS
975 } /* label_contour */
978 cntr_label_POLYAREA (PLINE
* poly
, POLYAREA
* ppl
, BOOLp test
)
980 assert (ppl
!= NULL
&& ppl
->contours
!= NULL
);
981 if (poly
->Flags
.status
== ISECTED
)
983 label_contour (poly
); /* should never get here when BOOLp is true */
985 else if (cntr_in_M_POLYAREA (poly
, ppl
, test
))
989 poly
->Flags
.status
= INSIDE
;
995 poly
->Flags
.status
= OUTSIDE
;
998 } /* cntr_label_POLYAREA */
1001 M_POLYAREA_label (POLYAREA
* afst
, POLYAREA
* b
, BOOLp touch
)
1009 for (curc
= a
->contours
; curc
!= NULL
; curc
= curc
->next
)
1010 if (cntr_label_POLYAREA (curc
, b
, touch
))
1016 while (!touch
&& (a
= a
->f
) != afst
);
1020 /****************************************************************/
1022 /* routines for temporary storing resulting contours */
1024 InsCntr (jmp_buf * e
, PLINE
* c
, POLYAREA
** dst
)
1030 MemGet (*dst
, POLYAREA
);
1031 (*dst
)->f
= (*dst
)->b
= *dst
;
1036 MemGet (newp
, POLYAREA
);
1038 newp
->b
= (*dst
)->b
;
1039 newp
->f
->b
= newp
->b
->f
= newp
;
1046 PutContour (jmp_buf * e
, PLINE
* cntr
, POLYAREA
** contours
, PLINE
** holes
,
1049 assert (cntr
!= NULL
);
1050 assert (cntr
->Count
> 2);
1052 if (cntr
->Flags
.orient
== PLF_DIR
)
1053 InsCntr (e
, cntr
, contours
);
1054 /* put hole into temporary list */
1057 /* if we know this belongs inside the parent, put it there now */
1060 cntr
->next
= parent
->next
;
1061 parent
->next
= cntr
;
1065 cntr
->next
= *holes
;
1066 *holes
= cntr
; /* let cntr be 1st hole in list */
1072 heap_it (const BoxType
* b
, void *cl
)
1074 heap_t
*heap
= (heap_t
*) cl
;
1075 PLINE
*p
= (PLINE
*) b
;
1077 return 0; /* how did this happen? */
1078 heap_insert (heap
, p
->area
, (void *) p
);
1083 InsertHoles (jmp_buf * e
, POLYAREA
* dest
, PLINE
** src
)
1086 PLINE
*curh
, *container
, *tmp
;
1091 return; /* empty hole list */
1093 error (err_bad_parm
); /* empty contour list */
1095 /* make an rtree of contours */
1096 tree
= r_create_tree (NULL
, 0, 0);
1100 r_insert_entry (tree
, (const BoxType
*) curc
->contours
, 0);
1102 while ((curc
= curc
->f
) != dest
);
1103 /* loop through the holes and put them where they belong */
1104 while ((curh
= *src
) != NULL
)
1109 /* build a heap of all of the polys that the hole is inside its bounding box */
1110 heap
= heap_create ();
1111 r_search (tree
, (BoxType
*) curh
, NULL
, heap_it
, heap
);
1112 if (heap_is_empty (heap
))
1119 poly_DelContour (&curh
);
1120 error (err_bad_parm
);
1122 /* Now search the heap for the container. If there was only one item
1123 * in the heap, assume it is the container without the expense of
1126 tmp
= (PLINE
*) heap_remove_smallest (heap
);
1127 if (heap_is_empty (heap
))
1128 { /* only one possibility it must be the right one */
1129 assert (poly_ContourInContour (tmp
, curh
));
1136 if (poly_ContourInContour (tmp
, curh
))
1141 if (heap_is_empty (heap
))
1143 tmp
= (PLINE
*) heap_remove_smallest (heap
);
1147 heap_destroy (&heap
);
1148 if (container
== NULL
)
1150 /* bad input polygons were given */
1157 poly_DelContour (&curh
);
1158 error (err_bad_parm
);
1162 /* link at front of hole list */
1163 tmp
= container
->next
;
1164 container
->next
= curh
;
1168 r_destroy_tree (&tree
);
1172 /****************************************************************/
1173 /* routines for collecting result */
1181 typedef int (*S_Rule
) (VNODE
*, DIRECTION
*);
1184 typedef int (*J_Rule
) (char, VNODE
*, DIRECTION
*);
1187 UniteS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1190 return (NODE_LABEL (cur
) == OUTSIDE
) || (NODE_LABEL (cur
) == SHARED
);
1194 IsectS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1197 return (NODE_LABEL (cur
) == INSIDE
) || (NODE_LABEL (cur
) == SHARED
);
1201 SubS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1204 return (NODE_LABEL (cur
) == OUTSIDE
) || (NODE_LABEL (cur
) == SHARED2
);
1208 XorS_Rule (VNODE
* cur
, DIRECTION
* initdir
)
1210 if (cur
->Flags
.status
== INSIDE
)
1215 if (cur
->Flags
.status
== OUTSIDE
)
1224 IsectJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1226 assert (*cdir
== FORW
);
1227 return (v
->Flags
.status
== INSIDE
|| v
->Flags
.status
== SHARED
);
1231 UniteJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1233 assert (*cdir
== FORW
);
1234 return (v
->Flags
.status
== OUTSIDE
|| v
->Flags
.status
== SHARED
);
1238 XorJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1240 if (v
->Flags
.status
== INSIDE
)
1245 if (v
->Flags
.status
== OUTSIDE
)
1254 SubJ_Rule (char p
, VNODE
* v
, DIRECTION
* cdir
)
1256 if (p
== 'B' && v
->Flags
.status
== INSIDE
)
1261 if (p
== 'A' && v
->Flags
.status
== OUTSIDE
)
1266 if (v
->Flags
.status
== SHARED2
)
1277 /* return the edge that comes next.
1278 * if the direction is BACKW, then we return the next vertex
1279 * so that prev vertex has the flags for the edge
1281 * returns true if an edge is found, false otherwise
1284 jump (VNODE
** cur
, DIRECTION
* cdir
, J_Rule rule
)
1290 if (!(*cur
)->cvc_prev
) /* not a cross-vertex */
1292 if (*cdir
== FORW
? (*cur
)->Flags
.mark
: (*cur
)->prev
->Flags
.mark
)
1297 DEBUGP ("jump entering node at (%d, %d)\n", (*cur
)->point
[0],
1301 d
= (*cur
)->cvc_prev
->prev
;
1303 d
= (*cur
)->cvc_next
->prev
;
1311 if (!e
->Flags
.mark
&& rule (d
->poly
, e
, &new))
1313 if ((d
->side
== 'N' && new == FORW
) ||
1314 (d
->side
== 'P' && new == BACKW
))
1318 DEBUGP ("jump leaving node at (%d, %d)\n",
1319 e
->next
->point
[0], e
->next
->point
[1]);
1321 DEBUGP ("jump leaving node at (%d, %d)\n",
1322 e
->point
[0], e
->point
[1]);
1330 while ((d
= d
->prev
) != start
);
1335 Gather (VNODE
* start
, PLINE
** result
, J_Rule v_rule
, DIRECTION initdir
)
1337 VNODE
*cur
= start
, *newn
;
1338 DIRECTION dir
= initdir
;
1340 DEBUGP ("gather direction = %d\n", dir
);
1342 assert (*result
== NULL
);
1345 /* see where to go next */
1346 if (!jump (&cur
, &dir
, v_rule
))
1348 /* add edge to polygon */
1351 *result
= poly_NewContour (cur
->point
);
1352 if (*result
== NULL
)
1353 return err_no_memory
;
1357 if ((newn
= poly_CreateNode (cur
->point
)) == NULL
)
1358 return err_no_memory
;
1359 poly_InclVertex ((*result
)->head
.prev
, newn
);
1362 DEBUGP ("gather vertex at (%d, %d)\n", cur
->point
[0], cur
->point
[1]);
1364 /* Now mark the edge as included. */
1365 newn
= (dir
== FORW
? cur
: cur
->prev
);
1366 newn
->Flags
.mark
= 1;
1367 /* for SHARED edge mark both */
1369 newn
->shared
->Flags
.mark
= 1;
1371 /* Advance to the next edge. */
1372 cur
= (dir
== FORW
? cur
->next
: cur
->prev
);
1379 Collect1 (jmp_buf * e
, VNODE
* cur
, DIRECTION dir
, POLYAREA
** contours
,
1380 PLINE
** holes
, J_Rule j_rule
)
1382 PLINE
*p
= NULL
; /* start making contour */
1385 Gather (dir
== FORW
? cur
: cur
->next
, &p
, j_rule
, dir
)) != err_ok
)
1388 poly_DelContour (&p
);
1393 poly_PreContour (p
, TRUE
);
1397 DEBUGP ("adding contour with %d verticies and direction %c\n",
1398 p
->Count
, p
->Flags
.orient
? 'F' : 'B');
1400 PutContour (e
, p
, contours
, holes
, NULL
);
1404 /* some sort of computation error ? */
1406 DEBUGP ("Bad contour! Not enough points!!\n");
1408 poly_DelContour (&p
);
1413 Collect (jmp_buf * e
, PLINE
* a
, POLYAREA
** contours
, PLINE
** holes
,
1414 S_Rule s_rule
, J_Rule j_rule
)
1422 if (s_rule (cur
, &dir
) && cur
->Flags
.mark
== 0)
1423 Collect1 (e
, cur
, dir
, contours
, holes
, j_rule
);
1425 if ((other
->cvc_prev
&& jump (&other
, &dir
, j_rule
)))
1426 Collect1 (e
, other
, dir
, contours
, holes
, j_rule
);
1428 while ((cur
= cur
->next
) != &a
->head
);
1433 cntr_Collect (jmp_buf * e
, PLINE
** A
, POLYAREA
** contours
, PLINE
** holes
,
1434 int action
, PLINE
* parent
)
1438 if ((*A
)->Flags
.status
== ISECTED
)
1443 Collect (e
, *A
, contours
, holes
, UniteS_Rule
, UniteJ_Rule
);
1446 Collect (e
, *A
, contours
, holes
, IsectS_Rule
, IsectJ_Rule
);
1449 Collect (e
, *A
, contours
, holes
, XorS_Rule
, XorJ_Rule
);
1452 Collect (e
, *A
, contours
, holes
, SubS_Rule
, SubJ_Rule
);
1461 if ((*A
)->Flags
.status
== INSIDE
)
1464 /* disappear this contour */
1466 tmprev
->next
= NULL
;
1467 PutContour (e
, tmprev
, contours
, holes
, NULL
);
1472 if ((*A
)->Flags
.status
== INSIDE
)
1475 /* disappear this contour */
1477 tmprev
->next
= NULL
;
1478 poly_InvContour (tmprev
);
1479 PutContour (e
, tmprev
, contours
, holes
, NULL
);
1482 /* break; *//* Fall through (I think this is correct! pcjc2) */
1485 if ((*A
)->Flags
.status
== OUTSIDE
)
1488 /* disappear this contour */
1490 tmprev
->next
= NULL
;
1491 PutContour (e
, tmprev
, contours
, holes
, parent
);
1498 } /* cntr_Collect */
1501 M_B_AREA_Collect (jmp_buf * e
, POLYAREA
* bfst
, POLYAREA
** contours
,
1502 PLINE
** holes
, int action
)
1505 PLINE
**cur
, **next
, *tmp
;
1510 for (cur
= &b
->contours
; *cur
!= NULL
; cur
= next
)
1512 next
= &((*cur
)->next
);
1513 if ((*cur
)->Flags
.status
== ISECTED
)
1516 if ((*cur
)->Flags
.status
== INSIDE
)
1521 poly_InvContour (*cur
);
1527 tmp
->Flags
.status
= UNKNWN
;
1528 PutContour (e
, tmp
, contours
, holes
, NULL
);
1531 break; /* nothing to do - already included */
1533 else if ((*cur
)->Flags
.status
== OUTSIDE
)
1543 tmp
->Flags
.status
= UNKNWN
;
1544 PutContour (e
, tmp
, contours
, holes
, NULL
);
1548 break; /* do nothing, not included */
1552 while ((b
= b
->f
) != bfst
);
1557 M_POLYAREA_Collect (jmp_buf * e
, POLYAREA
* afst
, POLYAREA
** contours
,
1558 PLINE
** holes
, int action
, BOOLp maybe
)
1561 PLINE
**cur
, **next
, *parent
;
1564 while ((a
= a
->f
) != afst
);
1565 /* now the non-intersect parts are collected in temp/holes */
1568 if (maybe
&& a
->contours
->Flags
.status
!= ISECTED
)
1569 parent
= a
->contours
;
1572 for (cur
= &a
->contours
; *cur
!= NULL
; cur
= next
)
1574 next
= &((*cur
)->next
);
1575 /* if we disappear a contour, don't advance twice */
1577 (e
, cur
, contours
, holes
, action
,
1578 *cur
== parent
? NULL
: parent
))
1582 while ((a
= a
->f
) != afst
);
1585 /* determine if two polygons touch or overlap */
1587 Touching (POLYAREA
* a
, POLYAREA
* b
)
1592 if ((code
= setjmp (e
)) == 0)
1595 if (!poly_Valid (a
))
1597 if (!poly_Valid (b
))
1600 M_POLYAREA_intersect (&e
, a
, b
, False
);
1602 if (M_POLYAREA_label (a
, b
, TRUE
))
1604 if (M_POLYAREA_label (b
, a
, TRUE
))
1607 else if (code
== TOUCHES
)
1612 /* the main clipping routines */
1614 poly_Boolean (const POLYAREA
* a_org
, const POLYAREA
* b_org
,
1615 POLYAREA
** res
, int action
)
1617 POLYAREA
*a
= NULL
, *b
= NULL
;
1619 if (!poly_M_Copy0 (&a
, a_org
) || !poly_M_Copy0 (&b
, b_org
))
1620 return err_no_memory
;
1622 return poly_Boolean_free (a
, b
, res
, action
);
1623 } /* poly_Boolean */
1625 /* just like poly_Boolean but frees the input polys */
1627 poly_Boolean_free (POLYAREA
* ai
, POLYAREA
* bi
, POLYAREA
** res
, int action
)
1629 POLYAREA
*a
= ai
, *b
= bi
;
1630 PLINE
*p
, *holes
= NULL
;
1667 if ((code
= setjmp (e
)) == 0)
1670 assert (poly_Valid (a
));
1671 assert (poly_Valid (b
));
1674 M_POLYAREA_intersect (&e
, a
, b
, TRUE
);
1676 M_POLYAREA_label (a
, b
, FALSE
);
1677 M_POLYAREA_label (b
, a
, FALSE
);
1679 M_POLYAREA_Collect (&e
, a
, res
, &holes
, action
, b
->f
== b
1680 && !b
->contours
->next
1681 && b
->contours
->Flags
.status
!= ISECTED
);
1683 M_B_AREA_Collect (&e
, b
, res
, &holes
, action
);
1686 InsertHoles (&e
, *res
, &holes
);
1688 /* delete holes if any left */
1689 while ((p
= holes
) != NULL
)
1692 poly_DelContour (&p
);
1700 assert (!*res
|| poly_Valid (*res
));
1702 } /* poly_Boolean_free */
1705 clear_marks (POLYAREA
* p
)
1713 for (c
= n
->contours
; c
; c
= c
->next
)
1720 while ((v
= v
->next
) != &c
->head
);
1723 while ((n
= n
->f
) != p
);
1726 /* compute the intersection and subtraction (divides "a" into two pieces)
1727 * and frees the input polys. This assumes that bi is a single simple polygon.
1730 poly_AndSubtract_free (POLYAREA
* ai
, POLYAREA
* bi
,
1731 POLYAREA
** aandb
, POLYAREA
** aminusb
)
1733 POLYAREA
*a
= ai
, *b
= bi
;
1734 PLINE
*p
, *holes
= NULL
;
1741 if ((code
= setjmp (e
)) == 0)
1745 if (!poly_Valid (a
))
1747 if (!poly_Valid (b
))
1750 M_POLYAREA_intersect (&e
, a
, b
, TRUE
);
1752 M_POLYAREA_label (a
, b
, FALSE
);
1753 M_POLYAREA_label (b
, a
, FALSE
);
1755 M_POLYAREA_Collect (&e
, a
, aandb
, &holes
, PBO_ISECT
, FALSE
);
1756 InsertHoles (&e
, *aandb
, &holes
);
1757 assert (poly_Valid (*aandb
));
1758 /* delete holes if any left */
1759 while ((p
= holes
) != NULL
)
1762 poly_DelContour (&p
);
1767 M_POLYAREA_Collect (&e
, a
, aminusb
, &holes
, PBO_SUB
, FALSE
);
1768 InsertHoles (&e
, *aminusb
, &holes
);
1771 assert (poly_Valid (*aminusb
));
1773 /* delete holes if any left */
1774 while ((p
= holes
) != NULL
)
1777 poly_DelContour (&p
);
1784 poly_Free (aminusb
);
1787 assert (!*aandb
|| poly_Valid (*aandb
));
1788 assert (!*aminusb
|| poly_Valid (*aminusb
));
1790 } /* poly_AndSubtract_free */
1793 cntrbox_pointin (PLINE
* c
, Vector p
)
1795 return (p
[0] >= c
->xmin
&& p
[1] >= c
->ymin
&&
1796 p
[0] <= c
->xmax
&& p
[1] <= c
->ymax
);
1801 node_neighbours (VNODE
* a
, VNODE
* b
)
1803 return (a
== b
) || (a
->next
== b
) || (b
->next
== a
) || (a
->next
== b
->next
);
1807 poly_CreateNode (Vector v
)
1813 res
= (VNODE
*) calloc (1, sizeof (VNODE
));
1816 // bzero (res, sizeof (VNODE) - sizeof(Vector));
1824 poly_IniContour (PLINE
* c
)
1828 /* bzero (c, sizeof(PLINE)); */
1829 c
->head
.next
= c
->head
.prev
= &c
->head
;
1830 c
->xmin
= c
->ymin
= 0x7fffffff;
1831 c
->xmax
= c
->ymax
= 0x80000000;
1835 poly_NewContour (Vector v
)
1839 res
= (PLINE
*) calloc (1, sizeof (PLINE
));
1843 poly_IniContour (res
);
1847 Vcopy (res
->head
.point
, v
);
1848 cntrbox_adjust (res
, v
);
1855 poly_ClrContour (PLINE
* c
)
1860 while ((cur
= c
->head
.next
) != &c
->head
)
1862 poly_ExclVertex (cur
);
1865 poly_IniContour (c
);
1869 poly_DelContour (PLINE
** c
)
1875 for (cur
= (*c
)->head
.prev
; cur
!= &(*c
)->head
; cur
= prev
)
1878 if (cur
->cvc_next
!= NULL
)
1880 free (cur
->cvc_next
);
1881 free (cur
->cvc_prev
);
1885 if ((*c
)->head
.cvc_next
!= NULL
)
1887 free ((*c
)->head
.cvc_next
);
1888 free ((*c
)->head
.cvc_prev
);
1890 /* FIXME -- strict aliasing violation. */
1893 rtree_t
*r
= (*c
)->tree
;
1894 r_destroy_tree (&r
);
1896 free (*c
), *c
= NULL
;
1900 poly_PreContour (PLINE
* C
, BOOLp optimize
)
1910 for (c
= (p
= &C
->head
)->next
; c
!= &C
->head
; c
= (p
= c
)->next
)
1912 /* if the previous node is on the same line with this one, we should remove it */
1913 Vsub2 (p1
, c
->point
, p
->point
);
1914 Vsub2 (p2
, c
->next
->point
, c
->point
);
1915 /* If the product below is zero then
1916 * the points on either side of c
1917 * are on the same line!
1918 * So, remove the point c
1921 if (vect_det2 (p1
, p2
) == 0)
1923 poly_ExclVertex (c
);
1930 C
->xmin
= C
->xmax
= C
->head
.point
[0];
1931 C
->ymin
= C
->ymax
= C
->head
.point
[1];
1933 p
= (c
= &C
->head
)->prev
;
1938 /* calculate area for orientation */
1940 (double) (p
->point
[0] - c
->point
[0]) * (p
->point
[1] +
1942 cntrbox_adjust (C
, c
->point
);
1945 while ((c
= (p
= c
)->next
) != &C
->head
);
1947 C
->area
= ABS (area
);
1949 C
->Flags
.orient
= ((area
< 0) ? PLF_INV
: PLF_DIR
);
1950 C
->tree
= make_edge_tree (C
);
1951 } /* poly_PreContour */
1954 flip_cb (const BoxType
* b
, void *cl
)
1956 struct seg
*s
= (struct seg
*) b
;
1962 poly_InvContour (PLINE
* c
)
1972 cur
->next
= cur
->prev
;
1974 /* fix the segment tree */
1976 while ((cur
= next
) != &c
->head
);
1977 c
->Flags
.orient
^= 1;
1980 r
= r_search (c
->tree
, NULL
, NULL
, flip_cb
, NULL
);
1981 assert (r
== c
->Count
);
1986 poly_ExclVertex (VNODE
* node
)
1988 assert (node
!= NULL
);
1991 free (node
->cvc_next
);
1992 free (node
->cvc_prev
);
1994 node
->prev
->next
= node
->next
;
1995 node
->next
->prev
= node
->prev
;
1999 poly_InclVertex (VNODE
* after
, VNODE
* node
)
2002 assert (after
!= NULL
);
2003 assert (node
!= NULL
);
2006 node
->next
= after
->next
;
2007 after
->next
= after
->next
->prev
= node
;
2008 /* remove points on same line */
2009 if (node
->prev
->prev
== node
)
2010 return; /* we don't have 3 points in the poly yet */
2011 a
= (node
->point
[1] - node
->prev
->prev
->point
[1]);
2012 a
*= (node
->prev
->point
[0] - node
->prev
->prev
->point
[0]);
2013 b
= (node
->point
[0] - node
->prev
->prev
->point
[0]);
2014 b
*= (node
->prev
->point
[1] - node
->prev
->prev
->point
[1]);
2015 if (fabs (a
- b
) < EPSILON
)
2017 VNODE
*t
= node
->prev
;
2018 t
->prev
->next
= node
;
2019 node
->prev
= t
->prev
;
2025 poly_CopyContour (PLINE
** dst
, PLINE
* src
)
2027 VNODE
*cur
, *newnode
;
2029 assert (src
!= NULL
);
2031 *dst
= poly_NewContour (src
->head
.point
);
2035 (*dst
)->Count
= src
->Count
;
2036 (*dst
)->Flags
.orient
= src
->Flags
.orient
;
2037 (*dst
)->xmin
= src
->xmin
, (*dst
)->xmax
= src
->xmax
;
2038 (*dst
)->ymin
= src
->ymin
, (*dst
)->ymax
= src
->ymax
;
2039 (*dst
)->area
= src
->area
;
2041 for (cur
= src
->head
.next
; cur
!= &src
->head
; cur
= cur
->next
)
2043 if ((newnode
= poly_CreateNode (cur
->point
)) == NULL
)
2045 // newnode->Flags = cur->Flags;
2046 poly_InclVertex ((*dst
)->head
.prev
, newnode
);
2048 (*dst
)->tree
= make_edge_tree (*dst
);
2052 /**********************************************************************/
2053 /* polygon routines */
2056 poly_Copy0 (POLYAREA
** dst
, const POLYAREA
* src
)
2060 *dst
= calloc (1, sizeof (POLYAREA
));
2064 return poly_Copy1 (*dst
, src
);
2068 poly_Copy1 (POLYAREA
* dst
, const POLYAREA
* src
)
2070 PLINE
*cur
, **last
= &dst
->contours
;
2073 dst
->f
= dst
->b
= dst
;
2075 for (cur
= src
->contours
; cur
!= NULL
; cur
= cur
->next
)
2077 if (!poly_CopyContour (last
, cur
))
2079 last
= &(*last
)->next
;
2085 poly_M_Incl (POLYAREA
** list
, POLYAREA
* a
)
2088 a
->f
= a
->b
= a
, *list
= a
;
2093 (*list
)->b
= (*list
)->b
->f
= a
;
2098 poly_M_Copy0 (POLYAREA
** dst
, const POLYAREA
* srcfst
)
2100 const POLYAREA
*src
= srcfst
;
2108 if ((di
= poly_Create ()) == NULL
|| !poly_Copy1 (di
, src
))
2110 poly_M_Incl (dst
, di
);
2112 while ((src
= src
->f
) != srcfst
);
2117 poly_InclContour (POLYAREA
* p
, PLINE
* c
)
2121 if ((c
== NULL
) || (p
== NULL
))
2123 if (c
->Flags
.orient
== PLF_DIR
)
2125 if (p
->contours
!= NULL
)
2131 if (p
->contours
== NULL
)
2133 /* link at front of hole list */
2134 tmp
= p
->contours
->next
;
2135 p
->contours
->next
= c
;
2150 crossing (const BoxType
* b
, void *cl
)
2152 struct seg
*s
= (struct seg
*) b
;
2153 struct pip
*p
= (struct pip
*) cl
;
2155 if (s
->v
->point
[1] <= p
->p
[1])
2157 if (s
->v
->next
->point
[1] > p
->p
[1])
2161 Vsub2 (v1
, s
->v
->next
->point
, s
->v
->point
);
2162 Vsub2 (v2
, p
->p
, s
->v
->point
);
2163 cross
= (long long) v1
[0] * v2
[1] - (long long) v2
[0] * v1
[1];
2167 longjmp (p
->env
, 1);
2175 if (s
->v
->next
->point
[1] <= p
->p
[1])
2179 Vsub2 (v1
, s
->v
->next
->point
, s
->v
->point
);
2180 Vsub2 (v2
, p
->p
, s
->v
->point
);
2181 cross
= (long long) v1
[0] * v2
[1] - (long long) v2
[0] * v1
[1];
2185 longjmp (p
->env
, 1);
2195 poly_InsideContour (PLINE
* c
, Vector p
)
2200 if (!cntrbox_pointin (c
, p
))
2203 info
.p
[0] = ray
.X1
= p
[0];
2204 info
.p
[1] = ray
.Y1
= p
[1];
2205 ray
.X2
= 0x7fffffff;
2207 if (setjmp (info
.env
) == 0)
2208 r_search (c
->tree
, &ray
, NULL
, crossing
, &info
);
2213 poly_CheckInside (POLYAREA
* p
, Vector v0
)
2217 if ((p
== NULL
) || (v0
== NULL
) || (p
->contours
== NULL
))
2220 if (poly_InsideContour (cur
, v0
))
2222 for (cur
= cur
->next
; cur
!= NULL
; cur
= cur
->next
)
2223 if (poly_InsideContour (cur
, v0
))
2231 poly_M_CheckInside (POLYAREA
* p
, Vector v0
)
2235 if ((p
== NULL
) || (v0
== NULL
))
2240 if (poly_CheckInside (cur
, v0
))
2243 while ((cur
= cur
->f
) != p
);
2248 poly_ContourInContour (PLINE
* poly
, PLINE
* inner
)
2250 assert (poly
!= NULL
);
2251 assert (inner
!= NULL
);
2252 if (cntrbox_inside (inner
, poly
))
2253 return poly_InsideContour (poly
, inner
->head
.point
);
2258 poly_Init (POLYAREA
* p
)
2269 if ((res
= malloc (sizeof (POLYAREA
))) != NULL
)
2275 poly_FreeContours (PLINE
** pline
)
2279 while ((pl
= *pline
) != NULL
)
2282 poly_DelContour (&pl
);
2287 poly_Free (POLYAREA
** p
)
2293 for (cur
= (*p
)->f
; cur
!= *p
; cur
= (*p
)->f
)
2295 poly_FreeContours (&cur
->contours
);
2300 poly_FreeContours (&cur
->contours
);
2301 free (*p
), *p
= NULL
;
2305 inside_sector (VNODE
* pn
, Vector p2
)
2307 Vector cdir
, ndir
, pdir
;
2310 assert (pn
!= NULL
);
2311 vect_sub (cdir
, p2
, pn
->point
);
2312 vect_sub (pdir
, pn
->point
, pn
->prev
->point
);
2313 vect_sub (ndir
, pn
->next
->point
, pn
->point
);
2315 p_c
= vect_det2 (pdir
, cdir
) >= 0;
2316 n_c
= vect_det2 (ndir
, cdir
) >= 0;
2317 p_n
= vect_det2 (pdir
, ndir
) >= 0;
2319 if ((p_n
&& p_c
&& n_c
) || ((!p_n
) && (p_c
|| n_c
)))
2323 } /* inside_sector */
2325 /* returns TRUE if bad contour */
2327 poly_ChkContour (PLINE
* a
)
2329 VNODE
*a1
, *a2
, *hit1
, *hit2
;
2340 if (!node_neighbours (a1
, a2
) &&
2341 (icnt
= vect_inters2 (a1
->point
, a1
->next
->point
,
2342 a2
->point
, a2
->next
->point
, i1
, i2
)) > 0)
2347 if (vect_dist2 (i1
, a1
->point
) < EPSILON
)
2349 else if (vect_dist2 (i1
, a1
->next
->point
) < EPSILON
)
2354 if (vect_dist2 (i1
, a2
->point
) < EPSILON
)
2356 else if (vect_dist2 (i1
, a2
->next
->point
) < EPSILON
)
2362 /* now check if they are inside each other */
2363 if (inside_sector (hit1
, hit2
->prev
->point
) ||
2364 inside_sector (hit1
, hit2
->next
->point
) ||
2365 inside_sector (hit2
, hit1
->prev
->point
) ||
2366 inside_sector (hit2
, hit1
->next
->point
))
2371 while ((a2
= a2
->next
) != &a
->head
);
2373 while ((a1
= a1
->next
) != &a
->head
);
2379 poly_Valid (POLYAREA
* p
)
2383 if ((p
== NULL
) || (p
->contours
== NULL
))
2386 if (p
->contours
->Flags
.orient
== PLF_INV
|| poly_ChkContour (p
->contours
))
2390 DEBUGP ("Invalid Outer PLINE\n");
2391 if (p
->contours
->Flags
.orient
== PLF_INV
)
2392 DEBUGP ("failed orient\n");
2393 if (poly_ChkContour (p
->contours
))
2394 DEBUGP ("failed self-intersection\n");
2395 v
= &p
->contours
->head
;
2399 fprintf (stderr
, "Line [%d %d %d %d 100 100 \"\"]\n",
2400 v
->point
[0], v
->point
[1], n
->point
[0], n
->point
[1]);
2402 while ((v
= v
->next
) != &p
->contours
->head
);
2406 for (c
= p
->contours
->next
; c
!= NULL
; c
= c
->next
)
2408 if (c
->Flags
.orient
== PLF_DIR
||
2409 poly_ChkContour (c
) || !poly_ContourInContour (p
->contours
, c
))
2413 DEBUGP ("Invalid Inner PLINE orient = %d\n", c
->Flags
.orient
);
2414 if (c
->Flags
.orient
== PLF_DIR
)
2415 DEBUGP ("failed orient\n");
2416 if (poly_ChkContour (c
))
2417 DEBUGP ("failed self-intersection\n");
2418 if (!poly_ContourInContour (p
->contours
, c
))
2419 DEBUGP ("failed containment\n");
2424 fprintf (stderr
, "Line [%d %d %d %d 100 100 \"\"]\n",
2425 v
->point
[0], v
->point
[1], n
->point
[0], n
->point
[1]);
2427 while ((v
= v
->next
) != &c
->head
);
2436 Vector vect_zero
= { (long) 0, (long) 0 };
2438 /*********************************************************************/
2439 /* L o n g V e c t o r S t u f f */
2440 /*********************************************************************/
2443 vect_init (Vector v
, double x
, double y
)
2449 #define Vzero(a) ((a)[0] == 0. && (a)[1] == 0.)
2451 #define Vsub(a,b,c) {(a)[0]=(b)[0]-(c)[0];(a)[1]=(b)[1]-(c)[1];}
2454 vect_equal (Vector v1
, Vector v2
)
2456 return (v1
[0] == v2
[0] && v1
[1] == v2
[1]);
2461 vect_sub (Vector res
, Vector v1
, Vector v2
)
2463 Vsub (res
, v1
, v2
)} /* vect_sub */
2466 vect_min (Vector v1
, Vector v2
, Vector v3
)
2468 v1
[0] = (v2
[0] < v3
[0]) ? v2
[0] : v3
[0];
2469 v1
[1] = (v2
[1] < v3
[1]) ? v2
[1] : v3
[1];
2473 vect_max (Vector v1
, Vector v2
, Vector v3
)
2475 v1
[0] = (v2
[0] > v3
[0]) ? v2
[0] : v3
[0];
2476 v1
[1] = (v2
[1] > v3
[1]) ? v2
[1] : v3
[1];
2480 vect_len2 (Vector v
)
2482 return ((double) v
[0] * v
[0] + (double) v
[1] * v
[1]); /* why sqrt? only used for compares */
2486 vect_dist2 (Vector v1
, Vector v2
)
2488 double dx
= v1
[0] - v2
[0];
2489 double dy
= v1
[1] - v2
[1];
2491 return (dx
* dx
+ dy
* dy
); /* why sqrt */
2494 /* value has sign of angle between vectors */
2496 vect_det2 (Vector v1
, Vector v2
)
2498 return (((double) v1
[0] * v2
[1]) - ((double) v2
[0] * v1
[1]));
2502 vect_m_dist (Vector v1
, Vector v2
)
2504 double dx
= v1
[0] - v2
[0];
2505 double dy
= v1
[1] - v2
[1];
2506 double dd
= (dx
* dx
+ dy
* dy
); /* sqrt */
2519 (C) 1993 Klamer Schutte
2520 (C) 1997 Michael Leonov, Alexey Nikitin
2524 vect_inters2 (Vector p1
, Vector p2
, Vector q1
, Vector q2
,
2525 Vector S1
, Vector S2
)
2528 double rpx
, rpy
, rqx
, rqy
;
2530 if (max (p1
[0], p2
[0]) < min (q1
[0], q2
[0]) ||
2531 max (q1
[0], q2
[0]) < min (p1
[0], p2
[0]) ||
2532 max (p1
[1], p2
[1]) < min (q1
[1], q2
[1]) ||
2533 max (q1
[1], q2
[1]) < min (p1
[1], p2
[1]))
2536 rpx
= p2
[0] - p1
[0];
2537 rpy
= p2
[1] - p1
[1];
2538 rqx
= q2
[0] - q1
[0];
2539 rqy
= q2
[1] - q1
[1];
2541 deel
= rpy
* rqx
- rpx
* rqy
; /* -vect_det(rp,rq); */
2543 /* coordinates are 30-bit integers so deel will be exactly zero
2544 * if the lines are parallel
2547 if (deel
== 0) /* parallel */
2549 double dc1
, dc2
, d1
, d2
, h
; /* Check to see whether p1-p2 and q1-q2 are on the same line */
2550 Vector hp1
, hq1
, hp2
, hq2
, q1p1
, q1q2
;
2552 Vsub2 (q1p1
, q1
, p1
);
2553 Vsub2 (q1q2
, q1
, q2
);
2556 /* If this product is not zero then p1-p2 and q1-q2 are not on same line! */
2557 if (vect_det2 (q1p1
, q1q2
) != 0)
2559 dc1
= 0; /* m_len(p1 - p1) */
2561 dc2
= vect_m_dist (p1
, p2
);
2562 d1
= vect_m_dist (p1
, q1
);
2563 d2
= vect_m_dist (p1
, q2
);
2565 /* Sorting the independent points from small to large */
2571 { /* hv and h are used as help-variable. */
2573 h
= dc1
, dc1
= dc2
, dc2
= h
;
2578 h
= d1
, d1
= d2
, d2
= h
;
2581 /* Now the line-pieces are compared */
2613 return (Vequ2 (S1
, S2
) ? 1 : 2);
2616 { /* not parallel */
2618 * We have the lines:
2619 * l1: p1 + s(p2 - p1)
2620 * l2: q1 + t(q2 - q1)
2621 * And we want to know the intersection point.
2623 * p1 + s(p2-p1) = q1 + t(q2-q1)
2624 * which is similar to the two equations:
2625 * p1x + s * rpx = q1x + t * rqx
2626 * p1y + s * rpy = q1y + t * rqy
2627 * Multiplying these by rpy resp. rpx gives:
2628 * rpy * p1x + s * rpx * rpy = rpy * q1x + t * rpy * rqx
2629 * rpx * p1y + s * rpx * rpy = rpx * q1y + t * rpx * rqy
2630 * Subtracting these gives:
2631 * rpy * p1x - rpx * p1y = rpy * q1x - rpx * q1y + t * ( rpy * rqx - rpx * rqy )
2632 * So t can be isolated:
2633 * t = (rpy * ( p1x - q1x ) + rpx * ( - p1y + q1y )) / ( rpy * rqx - rpx * rqy )
2634 * and s can be found similarly
2635 * s = (rqy * (q1x - p1x) + rqx * (p1y - q1y))/( rqy * rpx - rqx * rpy)
2638 if (Vequ2 (q1
, p1
) || Vequ2 (q1
, p2
))
2643 else if (Vequ2 (q2
, p1
) || Vequ2 (q2
, p2
))
2650 s
= (rqy
* (p1
[0] - q1
[0]) + rqx
* (q1
[1] - p1
[1])) / deel
;
2651 if (s
< 0 || s
> 1.)
2653 t
= (rpy
* (p1
[0] - q1
[0]) + rpx
* (q1
[1] - p1
[1])) / deel
;
2654 if (t
< 0 || t
> 1.)
2657 S1
[0] = q1
[0] + ROUND (t
* rqx
);
2658 S1
[1] = q1
[1] + ROUND (t
* rqy
);
2662 } /* vect_inters2 */
2664 /* how about expanding polygons so that edges can be arcs rather than
2665 * lines. Consider using the third coordinate to store the radius of the
2666 * arc. The arc would pass through the vertex points. Positive radius
2667 * would indicate the arc bows left (center on right of P1-P2 path)
2668 * negative radius would put the center on the other side. 0 radius
2669 * would mean the edge is a line instead of arc
2670 * The intersections of the two circles centered at the vertex points
2671 * would determine the two possible arc centers. If P2.x > P1.x then
2672 * the center with smaller Y is selected for positive r. If P2.y > P1.y
2673 * then the center with greate X is selected for positive r.
2675 * the vec_inters2() routine would then need to handle line-line
2676 * line-arc and arc-arc intersections.
2678 * perhaps reverse tracing the arc would require look-ahead to check