6 * PCB, interactive printed circuit board design
7 * Copyright (C) 1994,1995,1996 Thomas Nau
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 * Contact addresses for paper mail and Email:
24 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
25 * Thomas.Nau@rz.uni-ulm.de
32 Here's a brief tour of the data and life of a polygon, courtesy of Ben
35 A PCB PolygonType contains an array of points outlining the polygon.
36 This is what is manipulated by the UI and stored in the saved PCB.
38 A PolygonType also contains a POLYAREA called 'Clipped' which is
39 computed dynamically by InitClip every time a board is loaded. The
40 point array is coverted to a POLYAREA by original_poly and then holes
41 are cut in it by clearPoly. After that it is maintained dynamically
42 as parts are added, moved or removed (this is why sometimes bugs can
43 be fixed by just re-loading the board).
45 A POLYAREA consists of a linked list of PLINE structures. The head of
46 that list is POLYAREA.contours. The first contour is an outline of a
47 filled region. All of the subsequent PLINEs are holes cut out of that
48 first contour. POLYAREAs are in a doubly-linked list and each member
49 of the list is an independent (non-overlapping) area with its own
50 outline and holes. The function biggest() finds the largest POLYAREA
51 so that PolygonType.Clipped points to that shape. The rest of the
52 polygon still exists, it's just ignored when turning the polygon into
55 The first POLYAREA in PolygonType.Clipped is what is used for the vast
56 majority of Polygon related tests. The basic logic for an
57 intersection is "is the target shape inside POLYAREA.contours and NOT
58 fully enclosed in any of POLYAREA.contours.next... (the holes)".
60 The polygon dicer (NoHolesPolygonDicer and r_NoHolesPolygonDicer)
61 emits a series of "simple" PLINE shapes. That is, the PLINE isn't
62 linked to any other "holes" oulines). That's the meaning of the first
63 test in r_NoHolesPolygonDicer. It is testing to see if the PLINE
64 contour (the first, making it a solid outline) has a valid next
65 pointer (which would point to one or more holes). The dicer works by
66 recursively chopping the polygon in half through the first hole it
67 sees (which is guaranteed to eliminate at least that one hole). The
68 dicer output is used for HIDs which cannot render things with holes
69 (which would require erasure).
73 /* special polygon editing routines
88 #include "crosshair.h"
103 #ifdef HAVE_LIBDMALLOC
109 #define ROUND(x) ((long)(((x) >= 0 ? (x) + 0.5 : (x) - 0.5)))
111 #define UNSUBTRACT_BLOAT 10
113 /* ---------------------------------------------------------------------------
118 static double circleVerticies
[] = {
120 0.98768834059513777, 0.15643446504023087,
124 add_noholes_polyarea (PLINE
*pline
, void *user_data
)
126 PolygonType
*poly
= user_data
;
128 /* Prepend the pline into the NoHoles linked list */
129 pline
->next
= poly
->NoHoles
;
130 poly
->NoHoles
= pline
;
134 ComputeNoHoles (PolygonType
*poly
)
136 poly_FreeContours (&poly
->NoHoles
);
138 NoHolesPolygonDicer (poly
, NULL
, add_noholes_polyarea
, poly
);
140 printf ("Compute_noholes caught poly->Clipped = NULL\n");
141 poly
->NoHolesValid
= 1;
145 biggest (POLYAREA
* p
)
147 POLYAREA
*n
, *top
= NULL
;
156 if (n
->contours
->area
< PCB
->IsleArea
)
160 poly_DelContour (&n
->contours
);
171 if (n
->contours
->area
> big
)
174 big
= n
->contours
->area
;
177 while ((n
= n
->f
) != p
);
182 top
->contours
= p
->contours
;
191 ContourToPoly (PLINE
* contour
)
194 poly_PreContour (contour
, TRUE
);
195 assert (contour
->Flags
.orient
== PLF_DIR
);
196 if (!(p
= poly_Create ()))
198 poly_InclContour (p
, contour
);
199 assert (poly_Valid (p
));
204 original_poly (PolygonType
* p
)
206 PLINE
*contour
= NULL
;
210 /* first make initial polygon contour */
211 POLYGONPOINT_LOOP (p
);
217 if ((contour
= poly_NewContour (v
)) == NULL
)
222 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
226 poly_PreContour (contour
, TRUE
);
227 /* make sure it is a positive contour */
228 if ((contour
->Flags
.orient
) != PLF_DIR
)
229 poly_InvContour (contour
);
230 assert ((contour
->Flags
.orient
) == PLF_DIR
);
231 if ((np
= poly_Create ()) == NULL
)
233 poly_InclContour (np
, contour
);
234 assert (poly_Valid (np
));
239 RectPoly (LocationType x1
, LocationType x2
, LocationType y1
, LocationType y2
)
241 PLINE
*contour
= NULL
;
248 if ((contour
= poly_NewContour (v
)) == NULL
)
252 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
255 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
258 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
259 return ContourToPoly (contour
);
263 OctagonPoly (LocationType x
, LocationType y
, BDimension radius
)
265 PLINE
*contour
= NULL
;
268 v
[0] = x
+ ROUND (radius
* 0.5);
269 v
[1] = y
+ ROUND (radius
* TAN_22_5_DEGREE_2
);
270 if ((contour
= poly_NewContour (v
)) == NULL
)
272 v
[0] = x
+ ROUND (radius
* TAN_22_5_DEGREE_2
);
273 v
[1] = y
+ ROUND (radius
* 0.5);
274 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
275 v
[0] = x
- (v
[0] - x
);
276 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
277 v
[0] = x
- ROUND (radius
* 0.5);
278 v
[1] = y
+ ROUND (radius
* TAN_22_5_DEGREE_2
);
279 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
280 v
[1] = y
- (v
[1] - y
);
281 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
282 v
[0] = x
- ROUND (radius
* TAN_22_5_DEGREE_2
);
283 v
[1] = y
- ROUND (radius
* 0.5);
284 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
285 v
[0] = x
- (v
[0] - x
);
286 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
287 v
[0] = x
+ ROUND (radius
* 0.5);
288 v
[1] = y
- ROUND (radius
* TAN_22_5_DEGREE_2
);
289 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
290 return ContourToPoly (contour
);
293 /* add verticies in a fractional-circle starting from v
294 * centered at X, Y and going counter-clockwise
295 * does not include the first point
296 * last argument is 1 for a full circle
297 * 2 for a half circle
298 * or 4 for a quarter circle
301 frac_circle (PLINE
* c
, LocationType X
, LocationType Y
, Vector v
, int range
)
306 poly_InclVertex (c
->head
.prev
, poly_CreateNode (v
));
307 /* move vector to origin */
311 /* NB: the caller adds the last vertex, hence the -1 */
312 range
= CIRC_SEGS
/ range
- 1;
313 for (i
= 0; i
< range
; i
++)
315 /* rotate the vector */
316 t1
= e1
* circleVerticies
[2] - e2
* circleVerticies
[3];
317 e2
= e1
* circleVerticies
[3] + e2
* circleVerticies
[2];
319 v
[0] = X
+ ROUND (e1
);
320 v
[1] = Y
+ ROUND (e2
);
321 poly_InclVertex (c
->head
.prev
, poly_CreateNode (v
));
325 /* create a circle approximation from lines */
327 CirclePoly (LocationType x
, LocationType y
, BDimension radius
)
336 if ((contour
= poly_NewContour (v
)) == NULL
)
338 frac_circle (contour
, x
, y
, v
, 1);
339 contour
->is_round
= TRUE
;
342 contour
->radius
= radius
;
343 return ContourToPoly (contour
);
346 /* make a rounded-corner rectangle with radius t beyond x1,x2,y1,y2 rectangle */
348 RoundRect (LocationType x1
, LocationType x2
, LocationType y1
, LocationType y2
,
351 PLINE
*contour
= NULL
;
358 if ((contour
= poly_NewContour (v
)) == NULL
)
360 frac_circle (contour
, x1
, y1
, v
, 4);
363 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
364 frac_circle (contour
, x2
, y1
, v
, 4);
367 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
368 frac_circle (contour
, x2
, y2
, v
, 4);
371 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
372 frac_circle (contour
, x1
, y2
, v
, 4);
373 return ContourToPoly (contour
);
378 ArcPolyNoIntersect (ArcType
* a
, BDimension thick
)
380 PLINE
*contour
= NULL
;
385 double ang
, da
, rx
, ry
;
392 a
->StartAngle
+= a
->Delta
;
393 a
->Delta
= -a
->Delta
;
395 half
= (thick
+ 1) / 2;
396 ends
= GetArcEnds (a
);
397 /* start with inner radius */
398 rx
= MAX (a
->Width
- half
, 0);
399 ry
= MAX (a
->Height
- half
, 0);
400 segs
= a
->Delta
/ ARC_ANGLE
;
402 da
= (1.0 * a
->Delta
) / segs
;
403 v
[0] = a
->X
- rx
* cos (ang
* M180
);
404 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
405 if ((contour
= poly_NewContour (v
)) == NULL
)
407 for (i
= 0; i
< segs
- 1; i
++)
410 v
[0] = a
->X
- rx
* cos (ang
* M180
);
411 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
412 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
414 /* find last point */
415 ang
= a
->StartAngle
+ a
->Delta
;
416 v
[0] = a
->X
- rx
* cos (ang
* M180
);
417 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
418 /* add the round cap at the end */
419 frac_circle (contour
, ends
->X2
, ends
->Y2
, v
, 2);
420 /* and now do the outer arc (going backwards) */
421 rx
= a
->Width
+ half
;
422 ry
= a
->Width
+ half
;
424 for (i
= 0; i
< segs
; i
++)
426 v
[0] = a
->X
- rx
* cos (ang
* M180
);
427 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
428 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
431 /* now add other round cap */
433 v
[0] = a
->X
- rx
* cos (ang
* M180
);
434 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
435 frac_circle (contour
, ends
->X1
, ends
->Y1
, v
, 2);
436 /* now we have the whole contour */
437 if (!(np
= ContourToPoly (contour
)))
442 #define MIN_CLEARANCE_BEFORE_BISECT 10.
444 ArcPoly (ArcType
* a
, BDimension thick
)
448 POLYAREA
*tmp1
, *tmp2
, *res
;
450 delta
= (a
->Delta
< 0) ? -a
->Delta
: a
->Delta
;
452 /* If the arc segment would self-intersect, we need to construct it as the union of
453 two non-intersecting segments */
455 if (2 * M_PI
* a
->Width
* (1. - (double)delta
/ 360.) - thick
< MIN_CLEARANCE_BEFORE_BISECT
)
457 int half_delta
= a
->Delta
/ 2;
460 seg1
.Delta
= half_delta
;
461 seg2
.Delta
-= half_delta
;
462 seg2
.StartAngle
+= half_delta
;
464 tmp1
= ArcPolyNoIntersect (&seg1
, thick
);
465 tmp2
= ArcPolyNoIntersect (&seg2
, thick
);
466 poly_Boolean_free (tmp1
, tmp2
, &res
, PBO_UNITE
);
470 return ArcPolyNoIntersect (a
, thick
);
474 LinePoly (LineType
* L
, BDimension thick
)
476 PLINE
*contour
= NULL
;
480 long half
;LineType _l
=*L
,*l
=&_l
;
484 half
= (thick
+ 1) / 2;
486 sqrt (SQUARE (l
->Point1
.X
- l
->Point2
.X
) +
487 SQUARE (l
->Point1
.Y
- l
->Point2
.Y
));
488 if (!TEST_FLAG (SQUAREFLAG
,l
))
489 if (d
== 0) /* line is a point */
490 return CirclePoly (l
->Point1
.X
, l
->Point1
.Y
, half
);
494 dx
= (l
->Point1
.Y
- l
->Point2
.Y
) * d
;
495 dy
= (l
->Point2
.X
- l
->Point1
.X
) * d
;
502 if (TEST_FLAG (SQUAREFLAG
,l
))/* take into account the ends */
509 v
[0] = l
->Point1
.X
- dx
;
510 v
[1] = l
->Point1
.Y
- dy
;
511 if ((contour
= poly_NewContour (v
)) == NULL
)
513 v
[0] = l
->Point2
.X
- dx
;
514 v
[1] = l
->Point2
.Y
- dy
;
515 if (TEST_FLAG (SQUAREFLAG
,l
))
516 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
518 frac_circle (contour
, l
->Point2
.X
, l
->Point2
.Y
, v
, 2);
519 v
[0] = l
->Point2
.X
+ dx
;
520 v
[1] = l
->Point2
.Y
+ dy
;
521 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
522 v
[0] = l
->Point1
.X
+ dx
;
523 v
[1] = l
->Point1
.Y
+ dy
;
524 if (TEST_FLAG (SQUAREFLAG
,l
))
525 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
527 frac_circle (contour
, l
->Point1
.X
, l
->Point1
.Y
, v
, 2);
528 /* now we have the line contour */
529 if (!(np
= ContourToPoly (contour
)))
534 /* make a rounded-corner rectangle */
536 SquarePadPoly (PadType
* pad
, BDimension clear
)
538 PLINE
*contour
= NULL
;
544 PadType _t
=*pad
,*t
=&_t
;
545 PadType _c
=*pad
,*c
=&_c
;
546 int halfthick
= (pad
->Thickness
+ 1) / 2;
547 int halfclear
= (clear
+ 1) / 2;
550 sqrt (SQUARE (pad
->Point1
.X
- pad
->Point2
.X
) +
551 SQUARE (pad
->Point1
.Y
- pad
->Point2
.Y
));
554 double a
= halfthick
/ d
;
555 tx
= (t
->Point1
.Y
- t
->Point2
.Y
) * a
;
556 ty
= (t
->Point2
.X
- t
->Point1
.X
) * a
;
558 cx
= (c
->Point1
.Y
- c
->Point2
.Y
) * a
;
559 cy
= (c
->Point2
.X
- c
->Point1
.X
) * a
;
583 v
[0] = c
->Point1
.X
- tx
;
584 v
[1] = c
->Point1
.Y
- ty
;
585 if ((contour
= poly_NewContour (v
)) == NULL
)
587 frac_circle (contour
, (t
->Point1
.X
- tx
), (t
->Point1
.Y
- ty
), v
, 4);
589 v
[0] = t
->Point2
.X
- cx
;
590 v
[1] = t
->Point2
.Y
- cy
;
591 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
592 frac_circle (contour
, (t
->Point2
.X
- tx
), (t
->Point2
.Y
- ty
), v
, 4);
594 v
[0] = c
->Point2
.X
+ tx
;
595 v
[1] = c
->Point2
.Y
+ ty
;
596 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
597 frac_circle (contour
, (t
->Point2
.X
+ tx
), (t
->Point2
.Y
+ ty
), v
, 4);
599 v
[0] = t
->Point1
.X
+ cx
;
600 v
[1] = t
->Point1
.Y
+ cy
;
601 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
602 frac_circle (contour
, (t
->Point1
.X
+ tx
), (t
->Point1
.Y
+ ty
), v
, 4);
604 /* now we have the line contour */
605 if (!(np
= ContourToPoly (contour
)))
610 /* clear np1 from the polygon */
612 Subtract (POLYAREA
* np1
, PolygonType
* p
, Boolean fnp
)
614 POLYAREA
*merged
= NULL
, *np
= np1
;
624 assert (poly_Valid (p
->Clipped
));
625 assert (poly_Valid (np
));
627 x
= poly_Boolean_free (p
->Clipped
, np
, &merged
, PBO_SUB
);
630 x
= poly_Boolean (p
->Clipped
, np
, &merged
, PBO_SUB
);
631 poly_Free (&p
->Clipped
);
633 assert (!merged
|| poly_Valid (merged
));
636 fprintf (stderr
, "Error while clipping PBO_SUB: %d\n", x
);
639 if (p
->NoHoles
) printf ("Just leaked in Subtract\n");
643 p
->Clipped
= biggest (merged
);
644 assert (!p
->Clipped
|| poly_Valid (p
->Clipped
));
646 Message ("Polygon cleared out of existence near (%d, %d)\n",
647 (p
->BoundingBox
.X1
+ p
->BoundingBox
.X2
) / 2,
648 (p
->BoundingBox
.Y1
+ p
->BoundingBox
.Y2
) / 2);
652 /* create a polygon of the pin clearance */
654 PinPoly (PinType
* pin
, BDimension thick
, BDimension clear
)
658 if (TEST_FLAG (SQUAREFLAG
, pin
))
660 size
= (thick
+ 1) / 2;
661 return RoundRect (pin
->X
- size
, pin
->X
+ size
, pin
->Y
- size
,
662 pin
->Y
+ size
, (clear
+ 1) / 2);
666 size
= (thick
+ clear
+ 1) / 2;
667 if (TEST_FLAG (OCTAGONFLAG
, pin
))
669 return OctagonPoly (pin
->X
, pin
->Y
, size
+ size
);
672 return CirclePoly (pin
->X
, pin
->Y
, size
);
676 BoxPolyBloated (BoxType
*box
, BDimension bloat
)
678 return RectPoly (box
->X1
- bloat
, box
->X2
+ bloat
,
679 box
->Y1
- bloat
, box
->Y2
+ bloat
);
682 /* remove the pin clearance from the polygon */
684 SubtractPin (DataType
* d
, PinType
* pin
, LayerType
* l
, PolygonType
* p
)
689 if (pin
->Clearance
== 0)
691 i
= GetLayerNumber (d
, l
);
692 if (TEST_THERM (i
, pin
))
694 np
= ThermPoly ((PCBTypePtr
) (d
->pcb
), pin
, i
);
700 np
= PinPoly (pin
, pin
->Thickness
, pin
->Clearance
);
704 return Subtract (np
, p
, TRUE
);
708 SubtractLine (LineType
* line
, PolygonType
* p
)
712 if (!TEST_FLAG (CLEARLINEFLAG
, line
))
714 if (!(np
= LinePoly (line
, line
->Thickness
+ line
->Clearance
)))
716 return Subtract (np
, p
, True
);
720 SubtractArc (ArcType
* arc
, PolygonType
* p
)
724 if (!TEST_FLAG (CLEARLINEFLAG
, arc
))
726 if (!(np
= ArcPoly (arc
, arc
->Thickness
+ arc
->Clearance
)))
728 return Subtract (np
, p
, True
);
732 SubtractText (TextType
* text
, PolygonType
* p
)
735 const BoxType
*b
= &text
->BoundingBox
;
737 if (!TEST_FLAG (CLEARLINEFLAG
, text
))
739 if (!(np
= RoundRect (b
->X1
+ PCB
->Bloat
, b
->X2
- PCB
->Bloat
,
740 b
->Y1
+ PCB
->Bloat
, b
->Y2
- PCB
->Bloat
, PCB
->Bloat
)))
742 return Subtract (np
, p
, True
);
746 SubtractPad (PadType
* pad
, PolygonType
* p
)
750 if (pad
->Clearance
== 0)
752 if (TEST_FLAG (SQUAREFLAG
, pad
))
755 (np
= SquarePadPoly (pad
, pad
->Thickness
+ pad
->Clearance
)))
761 (np
= LinePoly ((LineType
*) pad
, pad
->Thickness
+ pad
->Clearance
)))
764 return Subtract (np
, p
, True
);
769 const BoxType
*other
;
772 PolygonType
*polygon
;
778 pin_sub_callback (const BoxType
* b
, void *cl
)
780 PinTypePtr pin
= (PinTypePtr
) b
;
781 struct cpInfo
*info
= (struct cpInfo
*) cl
;
782 PolygonTypePtr polygon
;
784 /* don't subtract the object that was put back! */
785 if (b
== info
->other
)
787 polygon
= info
->polygon
;
788 if (SubtractPin (info
->data
, pin
, info
->layer
, polygon
) < 0)
789 longjmp (info
->env
, 1);
794 arc_sub_callback (const BoxType
* b
, void *cl
)
796 ArcTypePtr arc
= (ArcTypePtr
) b
;
797 struct cpInfo
*info
= (struct cpInfo
*) cl
;
798 PolygonTypePtr polygon
;
800 /* don't subtract the object that was put back! */
801 if (b
== info
->other
)
803 if (!TEST_FLAG (CLEARLINEFLAG
, arc
))
805 polygon
= info
->polygon
;
806 if (SubtractArc (arc
, polygon
) < 0)
807 longjmp (info
->env
, 1);
812 pad_sub_callback (const BoxType
* b
, void *cl
)
814 PadTypePtr pad
= (PadTypePtr
) b
;
815 struct cpInfo
*info
= (struct cpInfo
*) cl
;
816 PolygonTypePtr polygon
;
818 /* don't subtract the object that was put back! */
819 if (b
== info
->other
)
821 if (pad
->Clearance
== 0)
823 polygon
= info
->polygon
;
824 if (XOR (TEST_FLAG (ONSOLDERFLAG
, pad
), !info
->solder
))
826 if (SubtractPad (pad
, polygon
) < 0)
827 longjmp (info
->env
, 1);
834 line_sub_callback (const BoxType
* b
, void *cl
)
836 LineTypePtr line
= (LineTypePtr
) b
;
837 struct cpInfo
*info
= (struct cpInfo
*) cl
;
838 PolygonTypePtr polygon
;
840 /* don't subtract the object that was put back! */
841 if (b
== info
->other
)
843 if (!TEST_FLAG (CLEARLINEFLAG
, line
))
845 polygon
= info
->polygon
;
846 if (SubtractLine (line
, polygon
) < 0)
847 longjmp (info
->env
, 1);
852 text_sub_callback (const BoxType
* b
, void *cl
)
854 TextTypePtr text
= (TextTypePtr
) b
;
855 struct cpInfo
*info
= (struct cpInfo
*) cl
;
856 PolygonTypePtr polygon
;
858 /* don't subtract the object that was put back! */
859 if (b
== info
->other
)
861 if (!TEST_FLAG (CLEARLINEFLAG
, text
))
863 polygon
= info
->polygon
;
864 if (SubtractText (text
, polygon
) < 0)
865 longjmp (info
->env
, 1);
870 Group (DataTypePtr Data
, Cardinal layer
)
873 for (i
= 0; i
< max_layer
; i
++)
874 for (j
= 0; j
< ((PCBType
*) (Data
->pcb
))->LayerGroups
.Number
[i
]; j
++)
875 if (layer
== ((PCBType
*) (Data
->pcb
))->LayerGroups
.Entries
[i
][j
])
881 clearPoly (DataTypePtr Data
, LayerTypePtr Layer
, PolygonType
* polygon
,
882 const BoxType
* here
, BDimension expand
)
889 if (!TEST_FLAG (CLEARPOLYFLAG
, polygon
)
890 || GetLayerNumber (Data
, Layer
) >= max_layer
)
892 group
= Group (Data
, GetLayerNumber (Data
, Layer
));
893 info
.solder
= (group
== Group (Data
, max_layer
+ SOLDER_LAYER
));
897 info
.polygon
= polygon
;
899 region
= clip_box (here
, &polygon
->BoundingBox
);
901 region
= polygon
->BoundingBox
;
902 region
= bloat_box (®ion
, expand
);
904 if (setjmp (info
.env
) == 0)
906 r
= r_search (Data
->via_tree
, ®ion
, NULL
, pin_sub_callback
, &info
);
907 r
+= r_search (Data
->pin_tree
, ®ion
, NULL
, pin_sub_callback
, &info
);
908 GROUP_LOOP (Data
, group
);
911 r_search (layer
->line_tree
, ®ion
, NULL
, line_sub_callback
,
914 r_search (layer
->arc_tree
, ®ion
, NULL
, arc_sub_callback
, &info
);
916 r_search (layer
->text_tree
, ®ion
, NULL
, text_sub_callback
, &info
);
919 if (info
.solder
|| group
== Group (Data
, max_layer
+ COMPONENT_LAYER
))
920 r
+= r_search (Data
->pad_tree
, ®ion
, NULL
, pad_sub_callback
, &info
);
922 polygon
->NoHolesValid
= 0;
927 Unsubtract (POLYAREA
* np1
, PolygonType
* p
)
929 POLYAREA
*merged
= NULL
, *np
= np1
;
930 POLYAREA
*orig_poly
, *clipped_np
;
933 assert (p
&& p
->Clipped
);
935 orig_poly
= original_poly (p
);
937 x
= poly_Boolean_free (np
, orig_poly
, &clipped_np
, PBO_ISECT
);
940 fprintf (stderr
, "Error while clipping PBO_ISECT: %d\n", x
);
941 poly_Free (&clipped_np
);
945 x
= poly_Boolean_free (p
->Clipped
, clipped_np
, &merged
, PBO_UNITE
);
948 fprintf (stderr
, "Error while clipping PBO_UNITE: %d\n", x
);
952 p
->Clipped
= biggest (merged
);
953 assert (!p
->Clipped
|| poly_Valid (p
->Clipped
));
958 if (p
->NoHoles
) printf ("Just leaked in Unsubtract\n");
964 UnsubtractPin (PinType
* pin
, LayerType
* l
, PolygonType
* p
)
968 /* overlap a bit to prevent gaps from rounding errors */
969 np
= BoxPolyBloated (&pin
->BoundingBox
, UNSUBTRACT_BLOAT
);
973 if (!Unsubtract (np
, p
))
975 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) pin
, 2 * UNSUBTRACT_BLOAT
);
980 UnsubtractArc (ArcType
* arc
, LayerType
* l
, PolygonType
* p
)
984 if (!TEST_FLAG (CLEARLINEFLAG
, arc
))
987 /* overlap a bit to prevent gaps from rounding errors */
988 np
= BoxPolyBloated (&arc
->BoundingBox
, UNSUBTRACT_BLOAT
);
992 if (!Unsubtract (np
, p
))
994 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) arc
, 2 * UNSUBTRACT_BLOAT
);
999 UnsubtractLine (LineType
* line
, LayerType
* l
, PolygonType
* p
)
1003 if (!TEST_FLAG (CLEARLINEFLAG
, line
))
1006 /* overlap a bit to prevent notches from rounding errors */
1007 np
= BoxPolyBloated (&line
->BoundingBox
, UNSUBTRACT_BLOAT
);
1011 if (!Unsubtract (np
, p
))
1013 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) line
, 2 * UNSUBTRACT_BLOAT
);
1018 UnsubtractText (TextType
* text
, LayerType
* l
, PolygonType
* p
)
1022 if (!TEST_FLAG (CLEARLINEFLAG
, text
))
1025 /* overlap a bit to prevent notches from rounding errors */
1026 np
= BoxPolyBloated (&text
->BoundingBox
, UNSUBTRACT_BLOAT
);
1030 if (!Unsubtract (np
, p
))
1032 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) text
, 2 * UNSUBTRACT_BLOAT
);
1037 UnsubtractPad (PadType
* pad
, LayerType
* l
, PolygonType
* p
)
1041 /* overlap a bit to prevent notches from rounding errors */
1042 np
= BoxPolyBloated (&pad
->BoundingBox
, UNSUBTRACT_BLOAT
);
1046 if (!Unsubtract (np
, p
))
1048 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) pad
, 2 * UNSUBTRACT_BLOAT
);
1052 static Boolean inhibit
= False
;
1055 InitClip (DataTypePtr Data
, LayerTypePtr layer
, PolygonType
* p
)
1060 poly_Free (&p
->Clipped
);
1061 p
->Clipped
= original_poly (p
);
1062 poly_FreeContours (&p
->NoHoles
);
1065 assert (poly_Valid (p
->Clipped
));
1066 if (TEST_FLAG (CLEARPOLYFLAG
, p
))
1067 clearPoly (Data
, layer
, p
, NULL
, 0);
1069 p
->NoHolesValid
= 0;
1073 /* --------------------------------------------------------------------------
1074 * remove redundant polygon points. Any point that lies on the straight
1075 * line between the points on either side of it is redundant.
1076 * returns true if any points are removed
1079 RemoveExcessPolygonPoints (LayerTypePtr Layer
, PolygonTypePtr Polygon
)
1081 PointTypePtr pt1
, pt2
, pt3
;
1084 Boolean changed
= False
;
1088 /* there are always at least three points in a polygon */
1089 pt1
= &Polygon
->Points
[Polygon
->PointN
- 1];
1090 pt2
= &Polygon
->Points
[0];
1091 pt3
= &Polygon
->Points
[1];
1092 for (n
= 0; n
< Polygon
->PointN
; n
++, pt1
++, pt2
++, pt3
++)
1094 /* wrap around polygon */
1096 pt1
= &Polygon
->Points
[0];
1097 if (n
== Polygon
->PointN
- 1)
1098 pt3
= &Polygon
->Points
[0];
1102 if (IsPointOnLine ((float) pt2
->X
, (float) pt2
->Y
, 0.0, &line
))
1104 RemoveObject (POLYGONPOINT_TYPE
, (void *) Layer
, (void *) Polygon
,
1112 /* ---------------------------------------------------------------------------
1113 * returns the index of the polygon point which is the end
1114 * point of the segment with the lowest distance to the passed
1118 GetLowestDistancePolygonPoint (PolygonTypePtr Polygon
, LocationType X
,
1121 double mindistance
= (double) MAX_COORD
* MAX_COORD
;
1122 PointTypePtr ptr1
= &Polygon
->Points
[Polygon
->PointN
- 1],
1123 ptr2
= &Polygon
->Points
[0];
1124 Cardinal n
, result
= 0;
1126 /* we calculate the distance to each segment and choose the
1127 * shortest distance. If the closest approach between the
1128 * given point and the projected line (i.e. the segment extended)
1129 * is not on the segment, then the distance is the distance
1130 * to the segment end point.
1133 for (n
= 0; n
< Polygon
->PointN
; n
++, ptr2
++)
1135 register double u
, dx
, dy
;
1136 dx
= ptr2
->X
- ptr1
->X
;
1137 dy
= ptr2
->Y
- ptr1
->Y
;
1138 if (dx
!= 0.0 || dy
!= 0.0)
1140 /* projected intersection is at P1 + u(P2 - P1) */
1141 u
= ((X
- ptr1
->X
) * dx
+ (Y
- ptr1
->Y
) * dy
) / (dx
* dx
+ dy
* dy
);
1144 { /* ptr1 is closest point */
1145 u
= SQUARE (X
- ptr1
->X
) + SQUARE (Y
- ptr1
->Y
);
1148 { /* ptr2 is closest point */
1149 u
= SQUARE (X
- ptr2
->X
) + SQUARE (Y
- ptr2
->Y
);
1152 { /* projected intersection is closest point */
1153 u
= SQUARE (X
- ptr1
->X
* (1.0 - u
) - u
* ptr2
->X
) +
1154 SQUARE (Y
- ptr1
->Y
* (1.0 - u
) - u
* ptr2
->Y
);
1156 if (u
< mindistance
)
1167 /* ---------------------------------------------------------------------------
1168 * go back to the previous point of the polygon
1171 GoToPreviousPoint (void)
1173 switch (Crosshair
.AttachedPolygon
.PointN
)
1175 /* do nothing if mode has just been entered */
1179 /* reset number of points and 'LINE_MODE' state */
1181 Crosshair
.AttachedPolygon
.PointN
= 0;
1182 Crosshair
.AttachedLine
.State
= STATE_FIRST
;
1186 /* back-up one point */
1189 PointTypePtr points
= Crosshair
.AttachedPolygon
.Points
;
1190 Cardinal n
= Crosshair
.AttachedPolygon
.PointN
- 2;
1192 Crosshair
.AttachedPolygon
.PointN
--;
1193 Crosshair
.AttachedLine
.Point1
.X
= points
[n
].X
;
1194 Crosshair
.AttachedLine
.Point1
.Y
= points
[n
].Y
;
1200 /* ---------------------------------------------------------------------------
1201 * close polygon if possible
1206 Cardinal n
= Crosshair
.AttachedPolygon
.PointN
;
1208 /* check number of points */
1211 /* if 45 degree lines are what we want do a quick check
1212 * if closing the polygon makes sense
1214 if (!TEST_FLAG (ALLDIRECTIONFLAG
, PCB
))
1218 dx
= abs (Crosshair
.AttachedPolygon
.Points
[n
- 1].X
-
1219 Crosshair
.AttachedPolygon
.Points
[0].X
);
1220 dy
= abs (Crosshair
.AttachedPolygon
.Points
[n
- 1].Y
-
1221 Crosshair
.AttachedPolygon
.Points
[0].Y
);
1222 if (!(dx
== 0 || dy
== 0 || dx
== dy
))
1226 ("Cannot close polygon because 45 degree lines are requested.\n"));
1230 CopyAttachedPolygonToLayer ();
1234 Message (_("A polygon has to have at least 3 points\n"));
1237 /* ---------------------------------------------------------------------------
1238 * moves the data of the attached (new) polygon to the current layer
1241 CopyAttachedPolygonToLayer (void)
1243 PolygonTypePtr polygon
;
1246 /* move data to layer and clear attached struct */
1247 polygon
= CreateNewPolygon (CURRENT
, NoFlags ());
1248 saveID
= polygon
->ID
;
1249 *polygon
= Crosshair
.AttachedPolygon
;
1250 polygon
->ID
= saveID
;
1251 SET_FLAG (CLEARPOLYFLAG
, polygon
);
1252 if (TEST_FLAG (NEWFULLPOLYFLAG
, PCB
))
1253 SET_FLAG (FULLPOLYFLAG
, polygon
);
1254 memset (&Crosshair
.AttachedPolygon
, 0, sizeof (PolygonType
));
1255 SetPolygonBoundingBox (polygon
);
1256 if (!CURRENT
->polygon_tree
)
1257 CURRENT
->polygon_tree
= r_create_tree (NULL
, 0, 0);
1258 r_insert_entry (CURRENT
->polygon_tree
, (BoxType
*) polygon
, 0);
1259 InitClip (PCB
->Data
, CURRENT
, polygon
);
1260 DrawPolygon (CURRENT
, polygon
, 0);
1261 SetChangedFlag (True
);
1263 /* reset state of attached line */
1264 Crosshair
.AttachedLine
.State
= STATE_FIRST
;
1267 /* add to undo list */
1268 AddObjectToCreateUndoList (POLYGON_TYPE
, CURRENT
, polygon
, polygon
);
1269 IncrementUndoSerialNumber ();
1272 /* find polygon holes in range, then call the callback function for
1273 * each hole. If the callback returns non-zero, stop
1277 PolygonHoles (PolygonType
*polygon
, const BoxType
*range
,
1278 int (*callback
) (PLINE
*contour
, void *user_data
),
1281 POLYAREA
*pa
= polygon
->Clipped
;
1283 /* If this hole is so big the polygon doesn't exist, then it's not
1288 for (pl
= pa
->contours
->next
; pl
; pl
= pl
->next
)
1290 if (range
!= NULL
&&
1291 (pl
->xmin
> range
->X2
|| pl
->xmax
< range
->X1
||
1292 pl
->ymin
> range
->Y2
|| pl
->ymax
< range
->Y1
))
1294 if (callback (pl
, user_data
))
1308 int (*callback
) (DataTypePtr
, LayerTypePtr
, PolygonTypePtr
, int, void *,
1313 subtract_plow (DataTypePtr Data
, LayerTypePtr Layer
, PolygonTypePtr Polygon
,
1314 int type
, void *ptr1
, void *ptr2
)
1316 if (!Polygon
->Clipped
)
1322 SubtractPin (Data
, (PinTypePtr
) ptr2
, Layer
, Polygon
);
1323 Polygon
->NoHolesValid
= 0;
1326 SubtractLine ((LineTypePtr
) ptr2
, Polygon
);
1327 Polygon
->NoHolesValid
= 0;
1330 SubtractArc ((ArcTypePtr
) ptr2
, Polygon
);
1331 Polygon
->NoHolesValid
= 0;
1334 SubtractPad ((PadTypePtr
) ptr2
, Polygon
);
1335 Polygon
->NoHolesValid
= 0;
1338 SubtractText ((TextTypePtr
) ptr2
, Polygon
);
1339 Polygon
->NoHolesValid
= 0;
1346 add_plow (DataTypePtr Data
, LayerTypePtr Layer
, PolygonTypePtr Polygon
,
1347 int type
, void *ptr1
, void *ptr2
)
1353 UnsubtractPin ((PinTypePtr
) ptr2
, Layer
, Polygon
);
1356 UnsubtractLine ((LineTypePtr
) ptr2
, Layer
, Polygon
);
1359 UnsubtractArc ((ArcTypePtr
) ptr2
, Layer
, Polygon
);
1362 UnsubtractPad ((PadTypePtr
) ptr2
, Layer
, Polygon
);
1365 UnsubtractText ((TextTypePtr
) ptr2
, Layer
, Polygon
);
1372 plow_callback (const BoxType
* b
, void *cl
)
1374 struct plow_info
*plow
= (struct plow_info
*) cl
;
1375 PolygonTypePtr polygon
= (PolygonTypePtr
) b
;
1377 if (TEST_FLAG (CLEARPOLYFLAG
, polygon
))
1378 return plow
->callback (plow
->data
, plow
->layer
, polygon
, plow
->type
,
1379 plow
->ptr1
, plow
->ptr2
);
1384 PlowsPolygon (DataType
* Data
, int type
, void *ptr1
, void *ptr2
,
1385 int (*call_back
) (DataTypePtr data
, LayerTypePtr lay
,
1386 PolygonTypePtr poly
, int type
, void *ptr1
,
1389 BoxType sb
= ((PinTypePtr
) ptr2
)->BoundingBox
;
1391 struct plow_info info
;
1397 info
.callback
= call_back
;
1402 if (type
== PIN_TYPE
|| ptr1
== ptr2
|| ptr1
== NULL
)
1404 LAYER_LOOP (Data
, max_layer
);
1408 r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1414 GROUP_LOOP (Data
, GetLayerGroupNumberByNumber (GetLayerNumber (Data
,
1415 ((LayerTypePtr
) ptr1
))));
1419 r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1427 /* the cast works equally well for lines and arcs */
1428 if (!TEST_FLAG (CLEARLINEFLAG
, (LineTypePtr
) ptr2
))
1430 /* silk doesn't plow */
1431 if (GetLayerNumber (Data
, ptr1
) >= max_layer
)
1433 GROUP_LOOP (Data
, GetLayerGroupNumberByNumber (GetLayerNumber (Data
,
1434 ((LayerTypePtr
) ptr1
))));
1437 r
+= r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1443 Cardinal group
= TEST_FLAG (ONSOLDERFLAG
,
1444 (PadType
*) ptr2
) ? SOLDER_LAYER
:
1446 group
= GetLayerGroupNumberByNumber (max_layer
+ group
);
1447 GROUP_LOOP (Data
, group
);
1451 r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1459 PIN_LOOP ((ElementType
*) ptr1
);
1461 PlowsPolygon (Data
, PIN_TYPE
, ptr1
, pin
, call_back
);
1464 PAD_LOOP ((ElementType
*) ptr1
);
1466 PlowsPolygon (Data
, PAD_TYPE
, ptr1
, pad
, call_back
);
1476 RestoreToPolygon (DataType
* Data
, int type
, void *ptr1
, void *ptr2
)
1478 if (type
== POLYGON_TYPE
)
1479 InitClip (PCB
->Data
, (LayerTypePtr
) ptr1
, (PolygonTypePtr
) ptr2
);
1481 PlowsPolygon (Data
, type
, ptr1
, ptr2
, add_plow
);
1485 ClearFromPolygon (DataType
* Data
, int type
, void *ptr1
, void *ptr2
)
1487 if (type
== POLYGON_TYPE
)
1488 InitClip (PCB
->Data
, (LayerTypePtr
) ptr1
, (PolygonTypePtr
) ptr2
);
1490 PlowsPolygon (Data
, type
, ptr1
, ptr2
, subtract_plow
);
1494 isects (POLYAREA
* a
, PolygonTypePtr p
, Boolean fr
)
1498 ans
= Touching (a
, p
->Clipped
);
1499 /* argument may be register, so we must copy it */
1508 IsPointInPolygon (LocationType X
, LocationType Y
, BDimension r
,
1515 if (poly_CheckInside (p
->Clipped
, v
))
1519 if (!(c
= CirclePoly (X
, Y
, r
)))
1521 return isects (c
, p
, True
);
1526 IsPointInPolygonIgnoreHoles (LocationType X
, LocationType Y
, PolygonTypePtr p
)
1531 return poly_InsideContour (p
->Clipped
->contours
, v
);
1535 IsRectangleInPolygon (LocationType X1
, LocationType Y1
, LocationType X2
,
1536 LocationType Y2
, PolygonTypePtr p
)
1540 (s
= RectPoly (min (X1
, X2
), max (X1
, X2
), min (Y1
, Y2
), max (Y1
, Y2
))))
1542 return isects (s
, p
, True
);
1545 /* NB: This function will free the passed POLYAREA.
1546 * It must only be passed a single POLYAREA (pa->f == pa->b == pa)
1549 r_NoHolesPolygonDicer (POLYAREA
* pa
,
1550 void (*emit
) (PLINE
*, void *), void *user_data
)
1552 PLINE
*p
= pa
->contours
;
1554 if (!pa
->contours
->next
) /* no holes */
1556 pa
->contours
= NULL
; /* The callback now owns the contour */
1557 emit (p
, user_data
);
1563 POLYAREA
*poly2
, *left
, *right
;
1565 /* make a rectangle of the left region slicing through the middle of the first hole */
1566 poly2
= RectPoly (p
->xmin
, (p
->next
->xmin
+ p
->next
->xmax
) / 2,
1568 poly_AndSubtract_free (pa
, poly2
, &left
, &right
);
1571 POLYAREA
*cur
, *next
;
1576 cur
->f
= cur
->b
= cur
; /* Detach this polygon piece */
1577 r_NoHolesPolygonDicer (cur
, emit
, user_data
);
1578 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1580 while ((cur
= next
) != left
);
1584 POLYAREA
*cur
, *next
;
1589 cur
->f
= cur
->b
= cur
; /* Detach this polygon piece */
1590 r_NoHolesPolygonDicer (cur
, emit
, user_data
);
1591 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1593 while ((cur
= next
) != right
);
1599 NoHolesPolygonDicer (PolygonTypePtr p
, const BoxType
* clip
,
1600 void (*emit
) (PLINE
*, void *), void *user_data
)
1602 POLYAREA
*save
, *ans
, *cur
, *next
;
1604 ans
= save
= poly_Create ();
1605 /* copy the main poly only */
1606 poly_Copy1 (save
, p
->Clipped
);
1607 /* clip to the bounding box */
1610 POLYAREA
*cbox
= RectPoly (clip
->X1
, clip
->X2
, clip
->Y1
, clip
->Y2
);
1613 int r
= poly_Boolean_free (save
, cbox
, &ans
, PBO_ISECT
);
1622 * NB: Could be more than one piece (because of the clip above)
1628 cur
->f
= cur
->b
= cur
; /* Detach this polygon piece */
1629 r_NoHolesPolygonDicer (cur
, emit
, user_data
);
1630 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1632 while ((cur
= next
) != save
);
1635 /* make a polygon split into multiple parts into multiple polygons */
1637 MorphPolygon (LayerTypePtr layer
, PolygonTypePtr poly
)
1639 POLYAREA
*p
, *start
;
1640 Boolean many
= False
;
1643 if (!poly
->Clipped
|| TEST_FLAG (LOCKFLAG
, poly
))
1645 if (poly
->Clipped
->f
== poly
->Clipped
)
1647 ErasePolygon (poly
);
1648 start
= p
= poly
->Clipped
;
1649 /* This is ugly. The creation of the new polygons can cause
1650 * all of the polygon pointers (including the one we're called
1651 * with to change if there is a realloc in GetPolygonMemory().
1652 * That will invalidate our original "poly" argument, potentially
1653 * inside the loop. We need to steal the Clipped pointer and
1654 * hide it from the Remove call so that it still exists while
1655 * we do this dirty work.
1657 poly
->Clipped
= NULL
;
1658 if (poly
->NoHoles
) printf ("Just leaked in MorpyPolygon\n");
1659 poly
->NoHoles
= NULL
;
1660 flags
= poly
->Flags
;
1661 RemovePolygon (layer
, poly
);
1668 if (p
->contours
->area
> PCB
->IsleArea
)
1670 new = CreateNewPolygon (layer
, flags
);
1674 v
= &p
->contours
->head
;
1675 CreateNewPointInPolygon (new, v
->point
[0], v
->point
[1]);
1676 for (v
= v
->next
; v
!= &p
->contours
->head
; v
= v
->next
)
1677 CreateNewPointInPolygon (new, v
->point
[0], v
->point
[1]);
1678 new->BoundingBox
.X1
= p
->contours
->xmin
;
1679 new->BoundingBox
.X2
= p
->contours
->xmax
+ 1;
1680 new->BoundingBox
.Y1
= p
->contours
->ymin
;
1681 new->BoundingBox
.Y2
= p
->contours
->ymax
+ 1;
1682 AddObjectToCreateUndoList (POLYGON_TYPE
, layer
, new, new);
1684 p
= p
->f
; /* go to next pline */
1685 new->Clipped
->b
= new->Clipped
->f
= new->Clipped
; /* unlink from others */
1686 r_insert_entry (layer
->polygon_tree
, (BoxType
*) new, 0);
1687 DrawPolygon (layer
, new, 0);
1694 poly_DelContour (&t
->contours
);
1700 IncrementUndoSerialNumber ();
1704 void debug_pline (PLINE
*pl
)
1707 fprintf (stderr
, "\txmin %d xmax %d ymin %d ymax %d\n",
1708 pl
->xmin
, pl
->xmax
, pl
->ymin
, pl
->ymax
);
1712 fprintf(stderr
, "\t\tvnode: %d,%d\n", v
->point
[0], v
->point
[1]);
1720 debug_polyarea (POLYAREA
*p
)
1724 fprintf (stderr
, "POLYAREA %p\n", p
);
1725 for (pl
=p
->contours
; pl
; pl
=pl
->next
)
1730 debug_polygon (PolygonType
*p
)
1734 fprintf (stderr
, "POLYGON %p %d pts\n", p
, p
->PointN
);
1735 for (i
=0; i
<p
->PointN
; i
++)
1736 fprintf(stderr
, "\t%d: %d, %d\n", i
, p
->Points
[i
].X
, p
->Points
[i
].Y
);
1740 debug_polyarea (pa
);
1742 if (pa
== p
->Clipped
)