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 return ContourToPoly (contour
);
342 /* make a rounded-corner rectangle with radius t beyond x1,x2,y1,y2 rectangle */
344 RoundRect (LocationType x1
, LocationType x2
, LocationType y1
, LocationType y2
,
347 PLINE
*contour
= NULL
;
354 if ((contour
= poly_NewContour (v
)) == NULL
)
356 frac_circle (contour
, x1
, y1
, v
, 4);
359 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
360 frac_circle (contour
, x2
, y1
, v
, 4);
363 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
364 frac_circle (contour
, x2
, y2
, v
, 4);
367 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
368 frac_circle (contour
, x1
, y2
, v
, 4);
369 return ContourToPoly (contour
);
374 ArcPolyNoIntersect (ArcType
* a
, BDimension thick
)
376 PLINE
*contour
= NULL
;
381 double ang
, da
, rx
, ry
;
388 a
->StartAngle
+= a
->Delta
;
389 a
->Delta
= -a
->Delta
;
391 half
= (thick
+ 1) / 2;
392 ends
= GetArcEnds (a
);
393 /* start with inner radius */
394 rx
= MAX (a
->Width
- half
, 0);
395 ry
= MAX (a
->Height
- half
, 0);
396 segs
= a
->Delta
/ ARC_ANGLE
;
398 da
= (1.0 * a
->Delta
) / segs
;
399 v
[0] = a
->X
- rx
* cos (ang
* M180
);
400 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
401 if ((contour
= poly_NewContour (v
)) == NULL
)
403 for (i
= 0; i
< segs
- 1; i
++)
406 v
[0] = a
->X
- rx
* cos (ang
* M180
);
407 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
408 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
410 /* find last point */
411 ang
= a
->StartAngle
+ a
->Delta
;
412 v
[0] = a
->X
- rx
* cos (ang
* M180
);
413 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
414 /* add the round cap at the end */
415 frac_circle (contour
, ends
->X2
, ends
->Y2
, v
, 2);
416 /* and now do the outer arc (going backwards) */
417 rx
= a
->Width
+ half
;
418 ry
= a
->Width
+ half
;
420 for (i
= 0; i
< segs
; i
++)
422 v
[0] = a
->X
- rx
* cos (ang
* M180
);
423 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
424 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
427 /* now add other round cap */
429 v
[0] = a
->X
- rx
* cos (ang
* M180
);
430 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
431 frac_circle (contour
, ends
->X1
, ends
->Y1
, v
, 2);
432 /* now we have the whole contour */
433 if (!(np
= ContourToPoly (contour
)))
438 #define MIN_CLEARANCE_BEFORE_BISECT 10.
440 ArcPoly (ArcType
* a
, BDimension thick
)
444 POLYAREA
*tmp1
, *tmp2
, *res
;
446 delta
= (a
->Delta
< 0) ? -a
->Delta
: a
->Delta
;
448 /* If the arc segment would self-intersect, we need to construct it as the union of
449 two non-intersecting segments */
451 if (2 * M_PI
* a
->Width
* (1. - (double)delta
/ 360.) - thick
< MIN_CLEARANCE_BEFORE_BISECT
)
453 int half_delta
= a
->Delta
/ 2;
456 seg1
.Delta
= half_delta
;
457 seg2
.Delta
-= half_delta
;
458 seg2
.StartAngle
+= half_delta
;
460 tmp1
= ArcPolyNoIntersect (&seg1
, thick
);
461 tmp2
= ArcPolyNoIntersect (&seg2
, thick
);
462 poly_Boolean_free (tmp1
, tmp2
, &res
, PBO_UNITE
);
466 return ArcPolyNoIntersect (a
, thick
);
470 LinePoly (LineType
* L
, BDimension thick
)
472 PLINE
*contour
= NULL
;
476 long half
;LineType _l
=*L
,*l
=&_l
;
480 half
= (thick
+ 1) / 2;
482 sqrt (SQUARE (l
->Point1
.X
- l
->Point2
.X
) +
483 SQUARE (l
->Point1
.Y
- l
->Point2
.Y
));
484 if (!TEST_FLAG (SQUAREFLAG
,l
))
485 if (d
== 0) /* line is a point */
486 return CirclePoly (l
->Point1
.X
, l
->Point1
.Y
, half
);
490 dx
= (l
->Point1
.Y
- l
->Point2
.Y
) * d
;
491 dy
= (l
->Point2
.X
- l
->Point1
.X
) * d
;
498 if (TEST_FLAG (SQUAREFLAG
,l
))/* take into account the ends */
505 v
[0] = l
->Point1
.X
- dx
;
506 v
[1] = l
->Point1
.Y
- dy
;
507 if ((contour
= poly_NewContour (v
)) == NULL
)
509 v
[0] = l
->Point2
.X
- dx
;
510 v
[1] = l
->Point2
.Y
- dy
;
511 if (TEST_FLAG (SQUAREFLAG
,l
))
512 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
514 frac_circle (contour
, l
->Point2
.X
, l
->Point2
.Y
, v
, 2);
515 v
[0] = l
->Point2
.X
+ dx
;
516 v
[1] = l
->Point2
.Y
+ dy
;
517 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
518 v
[0] = l
->Point1
.X
+ dx
;
519 v
[1] = l
->Point1
.Y
+ dy
;
520 if (TEST_FLAG (SQUAREFLAG
,l
))
521 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
523 frac_circle (contour
, l
->Point1
.X
, l
->Point1
.Y
, v
, 2);
524 /* now we have the line contour */
525 if (!(np
= ContourToPoly (contour
)))
530 /* make a rounded-corner rectangle */
532 SquarePadPoly (PadType
* pad
, BDimension clear
)
534 PLINE
*contour
= NULL
;
540 PadType _t
=*pad
,*t
=&_t
;
541 PadType _c
=*pad
,*c
=&_c
;
542 int halfthick
= (pad
->Thickness
+ 1) / 2;
543 int halfclear
= (clear
+ 1) / 2;
546 sqrt (SQUARE (pad
->Point1
.X
- pad
->Point2
.X
) +
547 SQUARE (pad
->Point1
.Y
- pad
->Point2
.Y
));
550 double a
= halfthick
/ d
;
551 tx
= (t
->Point1
.Y
- t
->Point2
.Y
) * a
;
552 ty
= (t
->Point2
.X
- t
->Point1
.X
) * a
;
554 cx
= (c
->Point1
.Y
- c
->Point2
.Y
) * a
;
555 cy
= (c
->Point2
.X
- c
->Point1
.X
) * a
;
579 v
[0] = c
->Point1
.X
- tx
;
580 v
[1] = c
->Point1
.Y
- ty
;
581 if ((contour
= poly_NewContour (v
)) == NULL
)
583 frac_circle (contour
, (t
->Point1
.X
- tx
), (t
->Point1
.Y
- ty
), v
, 4);
585 v
[0] = t
->Point2
.X
- cx
;
586 v
[1] = t
->Point2
.Y
- cy
;
587 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
588 frac_circle (contour
, (t
->Point2
.X
- tx
), (t
->Point2
.Y
- ty
), v
, 4);
590 v
[0] = c
->Point2
.X
+ tx
;
591 v
[1] = c
->Point2
.Y
+ ty
;
592 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
593 frac_circle (contour
, (t
->Point2
.X
+ tx
), (t
->Point2
.Y
+ ty
), v
, 4);
595 v
[0] = t
->Point1
.X
+ cx
;
596 v
[1] = t
->Point1
.Y
+ cy
;
597 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
598 frac_circle (contour
, (t
->Point1
.X
+ tx
), (t
->Point1
.Y
+ ty
), v
, 4);
600 /* now we have the line contour */
601 if (!(np
= ContourToPoly (contour
)))
606 /* clear np1 from the polygon */
608 Subtract (POLYAREA
* np1
, PolygonType
* p
, Boolean fnp
)
610 POLYAREA
*merged
= NULL
, *np
= np1
;
620 assert (poly_Valid (p
->Clipped
));
621 assert (poly_Valid (np
));
623 x
= poly_Boolean_free (p
->Clipped
, np
, &merged
, PBO_SUB
);
626 x
= poly_Boolean (p
->Clipped
, np
, &merged
, PBO_SUB
);
627 poly_Free (&p
->Clipped
);
629 assert (!merged
|| poly_Valid (merged
));
632 fprintf (stderr
, "Error while clipping PBO_SUB: %d\n", x
);
635 if (p
->NoHoles
) printf ("Just leaked in Subtract\n");
639 p
->Clipped
= biggest (merged
);
640 assert (!p
->Clipped
|| poly_Valid (p
->Clipped
));
642 Message ("Polygon cleared out of existence near (%d, %d)\n",
643 (p
->BoundingBox
.X1
+ p
->BoundingBox
.X2
) / 2,
644 (p
->BoundingBox
.Y1
+ p
->BoundingBox
.Y2
) / 2);
648 /* create a polygon of the pin clearance */
650 PinPoly (PinType
* pin
, BDimension thick
, BDimension clear
)
654 if (TEST_FLAG (SQUAREFLAG
, pin
))
656 size
= (thick
+ 1) / 2;
657 return RoundRect (pin
->X
- size
, pin
->X
+ size
, pin
->Y
- size
,
658 pin
->Y
+ size
, (clear
+ 1) / 2);
662 size
= (thick
+ clear
+ 1) / 2;
663 if (TEST_FLAG (OCTAGONFLAG
, pin
))
665 return OctagonPoly (pin
->X
, pin
->Y
, size
+ size
);
668 return CirclePoly (pin
->X
, pin
->Y
, size
);
672 BoxPolyBloated (BoxType
*box
, BDimension bloat
)
674 return RectPoly (box
->X1
- bloat
, box
->X2
+ bloat
,
675 box
->Y1
- bloat
, box
->Y2
+ bloat
);
678 /* remove the pin clearance from the polygon */
680 SubtractPin (DataType
* d
, PinType
* pin
, LayerType
* l
, PolygonType
* p
)
685 if (pin
->Clearance
== 0)
687 i
= GetLayerNumber (d
, l
);
688 if (TEST_THERM (i
, pin
))
690 np
= ThermPoly ((PCBTypePtr
) (d
->pcb
), pin
, i
);
696 np
= PinPoly (pin
, pin
->Thickness
, pin
->Clearance
);
700 return Subtract (np
, p
, TRUE
);
704 SubtractLine (LineType
* line
, PolygonType
* p
)
708 if (!TEST_FLAG (CLEARLINEFLAG
, line
))
710 if (!(np
= LinePoly (line
, line
->Thickness
+ line
->Clearance
)))
712 return Subtract (np
, p
, True
);
716 SubtractArc (ArcType
* arc
, PolygonType
* p
)
720 if (!TEST_FLAG (CLEARLINEFLAG
, arc
))
722 if (!(np
= ArcPoly (arc
, arc
->Thickness
+ arc
->Clearance
)))
724 return Subtract (np
, p
, True
);
728 SubtractText (TextType
* text
, PolygonType
* p
)
731 const BoxType
*b
= &text
->BoundingBox
;
733 if (!TEST_FLAG (CLEARLINEFLAG
, text
))
735 if (!(np
= RoundRect (b
->X1
+ PCB
->Bloat
, b
->X2
- PCB
->Bloat
,
736 b
->Y1
+ PCB
->Bloat
, b
->Y2
- PCB
->Bloat
, PCB
->Bloat
)))
738 return Subtract (np
, p
, True
);
742 SubtractPad (PadType
* pad
, PolygonType
* p
)
746 if (pad
->Clearance
== 0)
748 if (TEST_FLAG (SQUAREFLAG
, pad
))
751 (np
= SquarePadPoly (pad
, pad
->Thickness
+ pad
->Clearance
)))
757 (np
= LinePoly ((LineType
*) pad
, pad
->Thickness
+ pad
->Clearance
)))
760 return Subtract (np
, p
, True
);
765 const BoxType
*other
;
768 PolygonType
*polygon
;
774 pin_sub_callback (const BoxType
* b
, void *cl
)
776 PinTypePtr pin
= (PinTypePtr
) b
;
777 struct cpInfo
*info
= (struct cpInfo
*) cl
;
778 PolygonTypePtr polygon
;
780 /* don't subtract the object that was put back! */
781 if (b
== info
->other
)
783 polygon
= info
->polygon
;
784 if (SubtractPin (info
->data
, pin
, info
->layer
, polygon
) < 0)
785 longjmp (info
->env
, 1);
790 arc_sub_callback (const BoxType
* b
, void *cl
)
792 ArcTypePtr arc
= (ArcTypePtr
) b
;
793 struct cpInfo
*info
= (struct cpInfo
*) cl
;
794 PolygonTypePtr polygon
;
796 /* don't subtract the object that was put back! */
797 if (b
== info
->other
)
799 if (!TEST_FLAG (CLEARLINEFLAG
, arc
))
801 polygon
= info
->polygon
;
802 if (SubtractArc (arc
, polygon
) < 0)
803 longjmp (info
->env
, 1);
808 pad_sub_callback (const BoxType
* b
, void *cl
)
810 PadTypePtr pad
= (PadTypePtr
) b
;
811 struct cpInfo
*info
= (struct cpInfo
*) cl
;
812 PolygonTypePtr polygon
;
814 /* don't subtract the object that was put back! */
815 if (b
== info
->other
)
817 if (pad
->Clearance
== 0)
819 polygon
= info
->polygon
;
820 if (XOR (TEST_FLAG (ONSOLDERFLAG
, pad
), !info
->solder
))
822 if (SubtractPad (pad
, polygon
) < 0)
823 longjmp (info
->env
, 1);
830 line_sub_callback (const BoxType
* b
, void *cl
)
832 LineTypePtr line
= (LineTypePtr
) b
;
833 struct cpInfo
*info
= (struct cpInfo
*) cl
;
834 PolygonTypePtr polygon
;
836 /* don't subtract the object that was put back! */
837 if (b
== info
->other
)
839 if (!TEST_FLAG (CLEARLINEFLAG
, line
))
841 polygon
= info
->polygon
;
842 if (SubtractLine (line
, polygon
) < 0)
843 longjmp (info
->env
, 1);
848 text_sub_callback (const BoxType
* b
, void *cl
)
850 TextTypePtr text
= (TextTypePtr
) b
;
851 struct cpInfo
*info
= (struct cpInfo
*) cl
;
852 PolygonTypePtr polygon
;
854 /* don't subtract the object that was put back! */
855 if (b
== info
->other
)
857 if (!TEST_FLAG (CLEARLINEFLAG
, text
))
859 polygon
= info
->polygon
;
860 if (SubtractText (text
, polygon
) < 0)
861 longjmp (info
->env
, 1);
866 Group (DataTypePtr Data
, Cardinal layer
)
869 for (i
= 0; i
< max_layer
; i
++)
870 for (j
= 0; j
< ((PCBType
*) (Data
->pcb
))->LayerGroups
.Number
[i
]; j
++)
871 if (layer
== ((PCBType
*) (Data
->pcb
))->LayerGroups
.Entries
[i
][j
])
877 clearPoly (DataTypePtr Data
, LayerTypePtr Layer
, PolygonType
* polygon
,
878 const BoxType
* here
, BDimension expand
)
885 if (!TEST_FLAG (CLEARPOLYFLAG
, polygon
)
886 || GetLayerNumber (Data
, Layer
) >= max_layer
)
888 group
= Group (Data
, GetLayerNumber (Data
, Layer
));
889 info
.solder
= (group
== Group (Data
, max_layer
+ SOLDER_LAYER
));
893 info
.polygon
= polygon
;
895 region
= clip_box (here
, &polygon
->BoundingBox
);
897 region
= polygon
->BoundingBox
;
898 region
= bloat_box (®ion
, expand
);
900 if (setjmp (info
.env
) == 0)
902 r
= r_search (Data
->via_tree
, ®ion
, NULL
, pin_sub_callback
, &info
);
903 r
+= r_search (Data
->pin_tree
, ®ion
, NULL
, pin_sub_callback
, &info
);
904 GROUP_LOOP (Data
, group
);
907 r_search (layer
->line_tree
, ®ion
, NULL
, line_sub_callback
,
910 r_search (layer
->arc_tree
, ®ion
, NULL
, arc_sub_callback
, &info
);
912 r_search (layer
->text_tree
, ®ion
, NULL
, text_sub_callback
, &info
);
915 if (info
.solder
|| group
== Group (Data
, max_layer
+ COMPONENT_LAYER
))
916 r
+= r_search (Data
->pad_tree
, ®ion
, NULL
, pad_sub_callback
, &info
);
918 polygon
->NoHolesValid
= 0;
923 Unsubtract (POLYAREA
* np1
, PolygonType
* p
)
925 POLYAREA
*merged
= NULL
, *np
= np1
;
926 POLYAREA
*orig_poly
, *clipped_np
;
929 assert (p
&& p
->Clipped
);
931 orig_poly
= original_poly (p
);
933 x
= poly_Boolean_free (np
, orig_poly
, &clipped_np
, PBO_ISECT
);
936 fprintf (stderr
, "Error while clipping PBO_ISECT: %d\n", x
);
937 poly_Free (&clipped_np
);
941 x
= poly_Boolean_free (p
->Clipped
, clipped_np
, &merged
, PBO_UNITE
);
944 fprintf (stderr
, "Error while clipping PBO_UNITE: %d\n", x
);
948 p
->Clipped
= biggest (merged
);
949 assert (!p
->Clipped
|| poly_Valid (p
->Clipped
));
954 if (p
->NoHoles
) printf ("Just leaked in Unsubtract\n");
960 UnsubtractPin (PinType
* pin
, LayerType
* l
, PolygonType
* p
)
964 /* overlap a bit to prevent gaps from rounding errors */
965 np
= BoxPolyBloated (&pin
->BoundingBox
, UNSUBTRACT_BLOAT
);
969 if (!Unsubtract (np
, p
))
971 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) pin
, 2 * UNSUBTRACT_BLOAT
);
976 UnsubtractArc (ArcType
* arc
, LayerType
* l
, PolygonType
* p
)
980 if (!TEST_FLAG (CLEARLINEFLAG
, arc
))
983 /* overlap a bit to prevent gaps from rounding errors */
984 np
= BoxPolyBloated (&arc
->BoundingBox
, UNSUBTRACT_BLOAT
);
988 if (!Unsubtract (np
, p
))
990 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) arc
, 2 * UNSUBTRACT_BLOAT
);
995 UnsubtractLine (LineType
* line
, LayerType
* l
, PolygonType
* p
)
999 if (!TEST_FLAG (CLEARLINEFLAG
, line
))
1002 /* overlap a bit to prevent notches from rounding errors */
1003 np
= BoxPolyBloated (&line
->BoundingBox
, UNSUBTRACT_BLOAT
);
1007 if (!Unsubtract (np
, p
))
1009 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) line
, 2 * UNSUBTRACT_BLOAT
);
1014 UnsubtractText (TextType
* text
, LayerType
* l
, PolygonType
* p
)
1018 if (!TEST_FLAG (CLEARLINEFLAG
, text
))
1021 /* overlap a bit to prevent notches from rounding errors */
1022 np
= BoxPolyBloated (&text
->BoundingBox
, UNSUBTRACT_BLOAT
);
1026 if (!Unsubtract (np
, p
))
1028 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) text
, 2 * UNSUBTRACT_BLOAT
);
1033 UnsubtractPad (PadType
* pad
, LayerType
* l
, PolygonType
* p
)
1037 /* overlap a bit to prevent notches from rounding errors */
1038 np
= BoxPolyBloated (&pad
->BoundingBox
, UNSUBTRACT_BLOAT
);
1042 if (!Unsubtract (np
, p
))
1044 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) pad
, 2 * UNSUBTRACT_BLOAT
);
1048 static Boolean inhibit
= False
;
1051 InitClip (DataTypePtr Data
, LayerTypePtr layer
, PolygonType
* p
)
1056 poly_Free (&p
->Clipped
);
1057 p
->Clipped
= original_poly (p
);
1058 poly_FreeContours (&p
->NoHoles
);
1061 assert (poly_Valid (p
->Clipped
));
1062 if (TEST_FLAG (CLEARPOLYFLAG
, p
))
1063 clearPoly (Data
, layer
, p
, NULL
, 0);
1065 p
->NoHolesValid
= 0;
1069 /* --------------------------------------------------------------------------
1070 * remove redundant polygon points. Any point that lies on the straight
1071 * line between the points on either side of it is redundant.
1072 * returns true if any points are removed
1075 RemoveExcessPolygonPoints (LayerTypePtr Layer
, PolygonTypePtr Polygon
)
1077 PointTypePtr pt1
, pt2
, pt3
;
1080 Boolean changed
= False
;
1084 /* there are always at least three points in a polygon */
1085 pt1
= &Polygon
->Points
[Polygon
->PointN
- 1];
1086 pt2
= &Polygon
->Points
[0];
1087 pt3
= &Polygon
->Points
[1];
1088 for (n
= 0; n
< Polygon
->PointN
; n
++, pt1
++, pt2
++, pt3
++)
1090 /* wrap around polygon */
1092 pt1
= &Polygon
->Points
[0];
1093 if (n
== Polygon
->PointN
- 1)
1094 pt3
= &Polygon
->Points
[0];
1098 if (IsPointOnLine ((float) pt2
->X
, (float) pt2
->Y
, 0.0, &line
))
1100 RemoveObject (POLYGONPOINT_TYPE
, (void *) Layer
, (void *) Polygon
,
1108 /* ---------------------------------------------------------------------------
1109 * returns the index of the polygon point which is the end
1110 * point of the segment with the lowest distance to the passed
1114 GetLowestDistancePolygonPoint (PolygonTypePtr Polygon
, LocationType X
,
1117 double mindistance
= (double) MAX_COORD
* MAX_COORD
;
1118 PointTypePtr ptr1
= &Polygon
->Points
[Polygon
->PointN
- 1],
1119 ptr2
= &Polygon
->Points
[0];
1120 Cardinal n
, result
= 0;
1122 /* we calculate the distance to each segment and choose the
1123 * shortest distance. If the closest approach between the
1124 * given point and the projected line (i.e. the segment extended)
1125 * is not on the segment, then the distance is the distance
1126 * to the segment end point.
1129 for (n
= 0; n
< Polygon
->PointN
; n
++, ptr2
++)
1131 register double u
, dx
, dy
;
1132 dx
= ptr2
->X
- ptr1
->X
;
1133 dy
= ptr2
->Y
- ptr1
->Y
;
1134 if (dx
!= 0.0 || dy
!= 0.0)
1136 /* projected intersection is at P1 + u(P2 - P1) */
1137 u
= ((X
- ptr1
->X
) * dx
+ (Y
- ptr1
->Y
) * dy
) / (dx
* dx
+ dy
* dy
);
1140 { /* ptr1 is closest point */
1141 u
= SQUARE (X
- ptr1
->X
) + SQUARE (Y
- ptr1
->Y
);
1144 { /* ptr2 is closest point */
1145 u
= SQUARE (X
- ptr2
->X
) + SQUARE (Y
- ptr2
->Y
);
1148 { /* projected intersection is closest point */
1149 u
= SQUARE (X
- ptr1
->X
* (1.0 - u
) - u
* ptr2
->X
) +
1150 SQUARE (Y
- ptr1
->Y
* (1.0 - u
) - u
* ptr2
->Y
);
1152 if (u
< mindistance
)
1163 /* ---------------------------------------------------------------------------
1164 * go back to the previous point of the polygon
1167 GoToPreviousPoint (void)
1169 switch (Crosshair
.AttachedPolygon
.PointN
)
1171 /* do nothing if mode has just been entered */
1175 /* reset number of points and 'LINE_MODE' state */
1177 Crosshair
.AttachedPolygon
.PointN
= 0;
1178 Crosshair
.AttachedLine
.State
= STATE_FIRST
;
1182 /* back-up one point */
1185 PointTypePtr points
= Crosshair
.AttachedPolygon
.Points
;
1186 Cardinal n
= Crosshair
.AttachedPolygon
.PointN
- 2;
1188 Crosshair
.AttachedPolygon
.PointN
--;
1189 Crosshair
.AttachedLine
.Point1
.X
= points
[n
].X
;
1190 Crosshair
.AttachedLine
.Point1
.Y
= points
[n
].Y
;
1196 /* ---------------------------------------------------------------------------
1197 * close polygon if possible
1202 Cardinal n
= Crosshair
.AttachedPolygon
.PointN
;
1204 /* check number of points */
1207 /* if 45 degree lines are what we want do a quick check
1208 * if closing the polygon makes sense
1210 if (!TEST_FLAG (ALLDIRECTIONFLAG
, PCB
))
1214 dx
= abs (Crosshair
.AttachedPolygon
.Points
[n
- 1].X
-
1215 Crosshair
.AttachedPolygon
.Points
[0].X
);
1216 dy
= abs (Crosshair
.AttachedPolygon
.Points
[n
- 1].Y
-
1217 Crosshair
.AttachedPolygon
.Points
[0].Y
);
1218 if (!(dx
== 0 || dy
== 0 || dx
== dy
))
1222 ("Cannot close polygon because 45 degree lines are requested.\n"));
1226 CopyAttachedPolygonToLayer ();
1230 Message (_("A polygon has to have at least 3 points\n"));
1233 /* ---------------------------------------------------------------------------
1234 * moves the data of the attached (new) polygon to the current layer
1237 CopyAttachedPolygonToLayer (void)
1239 PolygonTypePtr polygon
;
1242 /* move data to layer and clear attached struct */
1243 polygon
= CreateNewPolygon (CURRENT
, NoFlags ());
1244 saveID
= polygon
->ID
;
1245 *polygon
= Crosshair
.AttachedPolygon
;
1246 polygon
->ID
= saveID
;
1247 SET_FLAG (CLEARPOLYFLAG
, polygon
);
1248 if (TEST_FLAG (NEWFULLPOLYFLAG
, PCB
))
1249 SET_FLAG (FULLPOLYFLAG
, polygon
);
1250 memset (&Crosshair
.AttachedPolygon
, 0, sizeof (PolygonType
));
1251 SetPolygonBoundingBox (polygon
);
1252 if (!CURRENT
->polygon_tree
)
1253 CURRENT
->polygon_tree
= r_create_tree (NULL
, 0, 0);
1254 r_insert_entry (CURRENT
->polygon_tree
, (BoxType
*) polygon
, 0);
1255 InitClip (PCB
->Data
, CURRENT
, polygon
);
1256 DrawPolygon (CURRENT
, polygon
, 0);
1257 SetChangedFlag (True
);
1259 /* reset state of attached line */
1260 Crosshair
.AttachedLine
.State
= STATE_FIRST
;
1263 /* add to undo list */
1264 AddObjectToCreateUndoList (POLYGON_TYPE
, CURRENT
, polygon
, polygon
);
1265 IncrementUndoSerialNumber ();
1268 /* find polygon holes in range, then call the callback function for
1269 * each hole. If the callback returns non-zero, stop
1273 PolygonHoles (PolygonType
*polygon
, const BoxType
*range
,
1274 int (*callback
) (PLINE
*contour
, void *user_data
),
1277 POLYAREA
*pa
= polygon
->Clipped
;
1279 /* If this hole is so big the polygon doesn't exist, then it's not
1284 for (pl
= pa
->contours
->next
; pl
; pl
= pl
->next
)
1286 if (range
!= NULL
&&
1287 (pl
->xmin
> range
->X2
|| pl
->xmax
< range
->X1
||
1288 pl
->ymin
> range
->Y2
|| pl
->ymax
< range
->Y1
))
1290 if (callback (pl
, user_data
))
1304 int (*callback
) (DataTypePtr
, LayerTypePtr
, PolygonTypePtr
, int, void *,
1309 subtract_plow (DataTypePtr Data
, LayerTypePtr Layer
, PolygonTypePtr Polygon
,
1310 int type
, void *ptr1
, void *ptr2
)
1312 if (!Polygon
->Clipped
)
1318 SubtractPin (Data
, (PinTypePtr
) ptr2
, Layer
, Polygon
);
1319 Polygon
->NoHolesValid
= 0;
1322 SubtractLine ((LineTypePtr
) ptr2
, Polygon
);
1323 Polygon
->NoHolesValid
= 0;
1326 SubtractArc ((ArcTypePtr
) ptr2
, Polygon
);
1327 Polygon
->NoHolesValid
= 0;
1330 SubtractPad ((PadTypePtr
) ptr2
, Polygon
);
1331 Polygon
->NoHolesValid
= 0;
1334 SubtractText ((TextTypePtr
) ptr2
, Polygon
);
1335 Polygon
->NoHolesValid
= 0;
1342 add_plow (DataTypePtr Data
, LayerTypePtr Layer
, PolygonTypePtr Polygon
,
1343 int type
, void *ptr1
, void *ptr2
)
1349 UnsubtractPin ((PinTypePtr
) ptr2
, Layer
, Polygon
);
1352 UnsubtractLine ((LineTypePtr
) ptr2
, Layer
, Polygon
);
1355 UnsubtractArc ((ArcTypePtr
) ptr2
, Layer
, Polygon
);
1358 UnsubtractPad ((PadTypePtr
) ptr2
, Layer
, Polygon
);
1361 UnsubtractText ((TextTypePtr
) ptr2
, Layer
, Polygon
);
1368 plow_callback (const BoxType
* b
, void *cl
)
1370 struct plow_info
*plow
= (struct plow_info
*) cl
;
1371 PolygonTypePtr polygon
= (PolygonTypePtr
) b
;
1373 if (TEST_FLAG (CLEARPOLYFLAG
, polygon
))
1374 return plow
->callback (plow
->data
, plow
->layer
, polygon
, plow
->type
,
1375 plow
->ptr1
, plow
->ptr2
);
1380 PlowsPolygon (DataType
* Data
, int type
, void *ptr1
, void *ptr2
,
1381 int (*call_back
) (DataTypePtr data
, LayerTypePtr lay
,
1382 PolygonTypePtr poly
, int type
, void *ptr1
,
1385 BoxType sb
= ((PinTypePtr
) ptr2
)->BoundingBox
;
1387 struct plow_info info
;
1393 info
.callback
= call_back
;
1398 if (type
== PIN_TYPE
|| ptr1
== ptr2
|| ptr1
== NULL
)
1400 LAYER_LOOP (Data
, max_layer
);
1404 r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1410 GROUP_LOOP (Data
, GetLayerGroupNumberByNumber (GetLayerNumber (Data
,
1411 ((LayerTypePtr
) ptr1
))));
1415 r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1423 /* the cast works equally well for lines and arcs */
1424 if (!TEST_FLAG (CLEARLINEFLAG
, (LineTypePtr
) ptr2
))
1426 /* silk doesn't plow */
1427 if (GetLayerNumber (Data
, ptr1
) >= max_layer
)
1429 GROUP_LOOP (Data
, GetLayerGroupNumberByNumber (GetLayerNumber (Data
,
1430 ((LayerTypePtr
) ptr1
))));
1433 r
+= r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1439 Cardinal group
= TEST_FLAG (ONSOLDERFLAG
,
1440 (PadType
*) ptr2
) ? SOLDER_LAYER
:
1442 group
= GetLayerGroupNumberByNumber (max_layer
+ group
);
1443 GROUP_LOOP (Data
, group
);
1447 r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1455 PIN_LOOP ((ElementType
*) ptr1
);
1457 PlowsPolygon (Data
, PIN_TYPE
, ptr1
, pin
, call_back
);
1460 PAD_LOOP ((ElementType
*) ptr1
);
1462 PlowsPolygon (Data
, PAD_TYPE
, ptr1
, pad
, call_back
);
1472 RestoreToPolygon (DataType
* Data
, int type
, void *ptr1
, void *ptr2
)
1474 if (type
== POLYGON_TYPE
)
1475 InitClip (PCB
->Data
, (LayerTypePtr
) ptr1
, (PolygonTypePtr
) ptr2
);
1477 PlowsPolygon (Data
, type
, ptr1
, ptr2
, add_plow
);
1481 ClearFromPolygon (DataType
* Data
, int type
, void *ptr1
, void *ptr2
)
1483 if (type
== POLYGON_TYPE
)
1484 InitClip (PCB
->Data
, (LayerTypePtr
) ptr1
, (PolygonTypePtr
) ptr2
);
1486 PlowsPolygon (Data
, type
, ptr1
, ptr2
, subtract_plow
);
1490 isects (POLYAREA
* a
, PolygonTypePtr p
, Boolean fr
)
1494 ans
= Touching (a
, p
->Clipped
);
1495 /* argument may be register, so we must copy it */
1504 IsPointInPolygon (LocationType X
, LocationType Y
, BDimension r
,
1511 if (poly_CheckInside (p
->Clipped
, v
))
1515 if (!(c
= CirclePoly (X
, Y
, r
)))
1517 return isects (c
, p
, True
);
1522 IsPointInPolygonIgnoreHoles (LocationType X
, LocationType Y
, PolygonTypePtr p
)
1527 return poly_InsideContour (p
->Clipped
->contours
, v
);
1531 IsRectangleInPolygon (LocationType X1
, LocationType Y1
, LocationType X2
,
1532 LocationType Y2
, PolygonTypePtr p
)
1536 (s
= RectPoly (min (X1
, X2
), max (X1
, X2
), min (Y1
, Y2
), max (Y1
, Y2
))))
1538 return isects (s
, p
, True
);
1541 /* NB: This function will free the passed POLYAREA.
1542 * It must only be passed a single POLYAREA (pa->f == pa->b == pa)
1545 r_NoHolesPolygonDicer (POLYAREA
* pa
,
1546 void (*emit
) (PLINE
*, void *), void *user_data
)
1548 PLINE
*p
= pa
->contours
;
1550 if (!pa
->contours
->next
) /* no holes */
1552 pa
->contours
= NULL
; /* The callback now owns the contour */
1553 emit (p
, user_data
);
1559 POLYAREA
*poly2
, *left
, *right
;
1561 /* make a rectangle of the left region slicing through the middle of the first hole */
1562 poly2
= RectPoly (p
->xmin
, (p
->next
->xmin
+ p
->next
->xmax
) / 2,
1564 poly_AndSubtract_free (pa
, poly2
, &left
, &right
);
1567 POLYAREA
*cur
, *next
;
1572 cur
->f
= cur
->b
= cur
; /* Detach this polygon piece */
1573 r_NoHolesPolygonDicer (cur
, emit
, user_data
);
1574 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1576 while ((cur
= next
) != left
);
1580 POLYAREA
*cur
, *next
;
1585 cur
->f
= cur
->b
= cur
; /* Detach this polygon piece */
1586 r_NoHolesPolygonDicer (cur
, emit
, user_data
);
1587 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1589 while ((cur
= next
) != right
);
1595 NoHolesPolygonDicer (PolygonTypePtr p
, const BoxType
* clip
,
1596 void (*emit
) (PLINE
*, void *), void *user_data
)
1598 POLYAREA
*save
, *ans
, *cur
, *next
;
1600 ans
= save
= poly_Create ();
1601 /* copy the main poly only */
1602 poly_Copy1 (save
, p
->Clipped
);
1603 /* clip to the bounding box */
1606 POLYAREA
*cbox
= RectPoly (clip
->X1
, clip
->X2
, clip
->Y1
, clip
->Y2
);
1609 int r
= poly_Boolean_free (save
, cbox
, &ans
, PBO_ISECT
);
1618 * NB: Could be more than one piece (because of the clip above)
1624 cur
->f
= cur
->b
= cur
; /* Detach this polygon piece */
1625 r_NoHolesPolygonDicer (cur
, emit
, user_data
);
1626 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1628 while ((cur
= next
) != save
);
1631 /* make a polygon split into multiple parts into multiple polygons */
1633 MorphPolygon (LayerTypePtr layer
, PolygonTypePtr poly
)
1635 POLYAREA
*p
, *start
;
1636 Boolean many
= False
;
1639 if (!poly
->Clipped
|| TEST_FLAG (LOCKFLAG
, poly
))
1641 if (poly
->Clipped
->f
== poly
->Clipped
)
1643 ErasePolygon (poly
);
1644 start
= p
= poly
->Clipped
;
1645 /* This is ugly. The creation of the new polygons can cause
1646 * all of the polygon pointers (including the one we're called
1647 * with to change if there is a realloc in GetPolygonMemory().
1648 * That will invalidate our original "poly" argument, potentially
1649 * inside the loop. We need to steal the Clipped pointer and
1650 * hide it from the Remove call so that it still exists while
1651 * we do this dirty work.
1653 poly
->Clipped
= NULL
;
1654 if (poly
->NoHoles
) printf ("Just leaked in MorpyPolygon\n");
1655 poly
->NoHoles
= NULL
;
1656 flags
= poly
->Flags
;
1657 RemovePolygon (layer
, poly
);
1664 if (p
->contours
->area
> PCB
->IsleArea
)
1666 new = CreateNewPolygon (layer
, flags
);
1670 v
= &p
->contours
->head
;
1671 CreateNewPointInPolygon (new, v
->point
[0], v
->point
[1]);
1672 for (v
= v
->next
; v
!= &p
->contours
->head
; v
= v
->next
)
1673 CreateNewPointInPolygon (new, v
->point
[0], v
->point
[1]);
1674 new->BoundingBox
.X1
= p
->contours
->xmin
;
1675 new->BoundingBox
.X2
= p
->contours
->xmax
+ 1;
1676 new->BoundingBox
.Y1
= p
->contours
->ymin
;
1677 new->BoundingBox
.Y2
= p
->contours
->ymax
+ 1;
1678 AddObjectToCreateUndoList (POLYGON_TYPE
, layer
, new, new);
1680 p
= p
->f
; /* go to next pline */
1681 new->Clipped
->b
= new->Clipped
->f
= new->Clipped
; /* unlink from others */
1682 r_insert_entry (layer
->polygon_tree
, (BoxType
*) new, 0);
1683 DrawPolygon (layer
, new, 0);
1690 poly_DelContour (&t
->contours
);
1696 IncrementUndoSerialNumber ();
1700 void debug_pline (PLINE
*pl
)
1703 fprintf (stderr
, "\txmin %d xmax %d ymin %d ymax %d\n",
1704 pl
->xmin
, pl
->xmax
, pl
->ymin
, pl
->ymax
);
1708 fprintf(stderr
, "\t\tvnode: %d,%d\n", v
->point
[0], v
->point
[1]);
1716 debug_polyarea (POLYAREA
*p
)
1720 fprintf (stderr
, "POLYAREA %p\n", p
);
1721 for (pl
=p
->contours
; pl
; pl
=pl
->next
)
1726 debug_polygon (PolygonType
*p
)
1730 fprintf (stderr
, "POLYGON %p %d pts\n", p
, p
->PointN
);
1731 for (i
=0; i
<p
->PointN
; i
++)
1732 fprintf(stderr
, "\t%d: %d, %d\n", i
, p
->Points
[i
].X
, p
->Points
[i
].Y
);
1736 debug_polyarea (pa
);
1738 if (pa
== p
->Clipped
)