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 PolygonTypes with Clipped pointing to a "simple"
62 shape. That is, there is a single POLYAREA (the dlink pointers point
63 to itself) and the contours list has only one element (the solid
64 outline, with no "holes" oulines). That's the meaning of the first
65 test in r_NoHolesPolygonDicer. It is testing to see if the PLINE
66 contour (the first, making it a solid outline) has a valid next
67 pointer (which would point to one or more holes). The dicer works by
68 recursively chopping the polygon in half through the first hole it
69 sees (which is guaranteed to eliminate at least that one hole). The
70 dicer output is used for HIDs which cannot render things with holes
71 (which would require erasure).
75 /* special polygon editing routines
90 #include "crosshair.h"
105 #ifdef HAVE_LIBDMALLOC
111 #define ROUND(x) ((long)(((x) >= 0 ? (x) + 0.5 : (x) - 0.5)))
113 #define UNSUBTRACT_BLOAT 10
115 /* ---------------------------------------------------------------------------
120 static double circleVerticies
[] = {
122 0.98480775301221, 0.17364817766693,
127 biggest (POLYAREA
* p
)
129 POLYAREA
*n
, *top
= NULL
;
138 if (n
->contours
->area
< PCB
->IsleArea
)
142 poly_DelContour (&n
->contours
);
153 if (n
->contours
->area
> big
)
156 big
= n
->contours
->area
;
159 while ((n
= n
->f
) != p
);
164 top
->contours
= p
->contours
;
173 ContourToPoly (PLINE
* contour
)
176 poly_PreContour (contour
, TRUE
);
177 assert (contour
->Flags
.orient
== PLF_DIR
);
178 if (!(p
= poly_Create ()))
180 poly_InclContour (p
, contour
);
181 assert (poly_Valid (p
));
186 original_poly (PolygonType
* p
)
188 PLINE
*contour
= NULL
;
192 /* first make initial polygon contour */
193 POLYGONPOINT_LOOP (p
);
199 if ((contour
= poly_NewContour (v
)) == NULL
)
204 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
208 poly_PreContour (contour
, TRUE
);
209 /* make sure it is a positive contour */
210 if ((contour
->Flags
.orient
) != PLF_DIR
)
211 poly_InvContour (contour
);
212 assert ((contour
->Flags
.orient
) == PLF_DIR
);
213 if ((np
= poly_Create ()) == NULL
)
215 poly_InclContour (np
, contour
);
216 assert (poly_Valid (np
));
221 ClipOriginal (PolygonType
* poly
)
223 POLYAREA
*p
, *result
;
226 p
= original_poly (poly
);
227 r
= poly_Boolean_free (poly
->Clipped
, p
, &result
, PBO_ISECT
);
230 fprintf (stderr
, "Error while clipping PBO_ISECT: %d\n", r
);
232 poly
->Clipped
= NULL
;
235 poly
->Clipped
= biggest (result
);
236 assert (!poly
->Clipped
|| poly_Valid (poly
->Clipped
));
241 RectPoly (LocationType x1
, LocationType x2
, LocationType y1
, LocationType y2
)
243 PLINE
*contour
= NULL
;
250 if ((contour
= poly_NewContour (v
)) == NULL
)
254 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
257 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
260 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
261 return ContourToPoly (contour
);
265 OctagonPoly (LocationType x
, LocationType y
, BDimension radius
)
267 PLINE
*contour
= NULL
;
270 v
[0] = x
+ ROUND (radius
* 0.5);
271 v
[1] = y
+ ROUND (radius
* TAN_22_5_DEGREE_2
);
272 if ((contour
= poly_NewContour (v
)) == NULL
)
274 v
[0] = x
+ ROUND (radius
* TAN_22_5_DEGREE_2
);
275 v
[1] = y
+ ROUND (radius
* 0.5);
276 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
277 v
[0] = x
- (v
[0] - x
);
278 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
279 v
[0] = x
- ROUND (radius
* 0.5);
280 v
[1] = y
+ ROUND (radius
* TAN_22_5_DEGREE_2
);
281 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
282 v
[1] = y
- (v
[1] - y
);
283 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
284 v
[0] = x
- ROUND (radius
* TAN_22_5_DEGREE_2
);
285 v
[1] = y
- ROUND (radius
* 0.5);
286 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
287 v
[0] = x
- (v
[0] - x
);
288 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
289 v
[0] = x
+ ROUND (radius
* 0.5);
290 v
[1] = y
- ROUND (radius
* TAN_22_5_DEGREE_2
);
291 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
292 return ContourToPoly (contour
);
295 /* add verticies in a fractional-circle starting from v
296 * centered at X, Y and going counter-clockwise
297 * does not include the first point
298 * last argument is 1 for a full circle
299 * 2 for a half circle
300 * or 4 for a quarter circle
303 frac_circle (PLINE
* c
, LocationType X
, LocationType Y
, Vector v
, int range
)
308 poly_InclVertex (c
->head
.prev
, poly_CreateNode (v
));
309 /* move vector to origin */
313 range
= range
== 1 ? CIRC_SEGS
-1 : (CIRC_SEGS
/ range
);
314 for (i
= 0; i
< range
; i
++)
316 /* rotate the vector */
317 t1
= e1
* circleVerticies
[2] - e2
* circleVerticies
[3];
318 e2
= e1
* circleVerticies
[3] + e2
* circleVerticies
[2];
320 v
[0] = X
+ ROUND (e1
);
321 v
[1] = Y
+ ROUND (e2
);
322 poly_InclVertex (c
->head
.prev
, poly_CreateNode (v
));
326 #define COARSE_CIRCLE 0
327 /* create a 35-vertex circle approximation */
329 CirclePoly (LocationType x
, LocationType y
, BDimension radius
)
338 if ((contour
= poly_NewContour (v
)) == NULL
)
340 frac_circle (contour
, x
, y
, v
, 1);
341 return ContourToPoly (contour
);
344 /* make a rounded-corner rectangle with radius t beyond x1,x2,y1,y2 rectangle */
346 RoundRect (LocationType x1
, LocationType x2
, LocationType y1
, LocationType y2
,
349 PLINE
*contour
= NULL
;
356 if ((contour
= poly_NewContour (v
)) == NULL
)
358 frac_circle (contour
, x1
, y1
, v
, 4);
361 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
362 frac_circle (contour
, x2
, y1
, v
, 4);
365 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
366 frac_circle (contour
, x2
, y2
, v
, 4);
369 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
370 frac_circle (contour
, x1
, y2
, v
, 4);
371 return ContourToPoly (contour
);
376 ArcPolyNoIntersect (ArcType
* a
, BDimension thick
)
378 PLINE
*contour
= NULL
;
383 double ang
, da
, rx
, ry
;
390 a
->StartAngle
+= a
->Delta
;
391 a
->Delta
= -a
->Delta
;
393 half
= (thick
+ 1) / 2;
394 ends
= GetArcEnds (a
);
395 /* start with inner radius */
396 rx
= MAX (a
->Width
- half
, 0);
397 ry
= MAX (a
->Height
- half
, 0);
398 segs
= a
->Delta
/ ARC_ANGLE
;
400 da
= (1.0 * a
->Delta
) / segs
;
401 v
[0] = a
->X
- rx
* cos (ang
* M180
);
402 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
403 if ((contour
= poly_NewContour (v
)) == NULL
)
405 for (i
= 0; i
< segs
- 1; i
++)
408 v
[0] = a
->X
- rx
* cos (ang
* M180
);
409 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
410 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
412 /* find last point */
413 ang
= a
->StartAngle
+ a
->Delta
;
414 v
[0] = a
->X
- rx
* cos (ang
* M180
);
415 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
416 /* add the round cap at the end */
417 frac_circle (contour
, ends
->X2
, ends
->Y2
, v
, 2);
418 /* and now do the outer arc (going backwards) */
419 rx
= a
->Width
+ half
;
420 ry
= a
->Width
+ half
;
422 for (i
= 0; i
< segs
; i
++)
424 v
[0] = a
->X
- rx
* cos (ang
* M180
);
425 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
426 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
429 /* now add other round cap */
431 v
[0] = a
->X
- rx
* cos (ang
* M180
);
432 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
433 frac_circle (contour
, ends
->X1
, ends
->Y1
, v
, 2);
434 /* now we have the whole contour */
435 if (!(np
= ContourToPoly (contour
)))
440 #define MIN_CLEARANCE_BEFORE_BISECT 10.
442 ArcPoly (ArcType
* a
, BDimension thick
)
446 POLYAREA
*tmp1
, *tmp2
, *res
;
448 delta
= (a
->Delta
< 0) ? -a
->Delta
: a
->Delta
;
450 /* If the arc segment would self-intersect, we need to construct it as the union of
451 two non-intersecting segments */
453 if (2 * M_PI
* a
->Width
* (1. - (double)delta
/ 360.) - thick
< MIN_CLEARANCE_BEFORE_BISECT
)
455 int half_delta
= a
->Delta
/ 2;
458 seg1
.Delta
= half_delta
;
459 seg2
.Delta
-= half_delta
;
460 seg2
.StartAngle
+= half_delta
;
462 tmp1
= ArcPolyNoIntersect (&seg1
, thick
);
463 tmp2
= ArcPolyNoIntersect (&seg2
, thick
);
464 poly_Boolean_free (tmp1
, tmp2
, &res
, PBO_UNITE
);
468 return ArcPolyNoIntersect (a
, thick
);
472 LinePoly (LineType
* L
, BDimension thick
)
474 PLINE
*contour
= NULL
;
478 long half
;LineType _l
=*L
,*l
=&_l
;
482 half
= (thick
+ 1) / 2;
484 sqrt (SQUARE (l
->Point1
.X
- l
->Point2
.X
) +
485 SQUARE (l
->Point1
.Y
- l
->Point2
.Y
));
486 if (!TEST_FLAG (SQUAREFLAG
,l
))
487 if (d
== 0) /* line is a point */
488 return CirclePoly (l
->Point1
.X
, l
->Point1
.Y
, half
);
492 dx
= (l
->Point1
.Y
- l
->Point2
.Y
) * d
;
493 dy
= (l
->Point2
.X
- l
->Point1
.X
) * d
;
500 if (TEST_FLAG (SQUAREFLAG
,l
))/* take into account the ends */
507 v
[0] = l
->Point1
.X
- dx
;
508 v
[1] = l
->Point1
.Y
- dy
;
509 if ((contour
= poly_NewContour (v
)) == NULL
)
511 v
[0] = l
->Point2
.X
- dx
;
512 v
[1] = l
->Point2
.Y
- dy
;
513 if (TEST_FLAG (SQUAREFLAG
,l
))
514 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
516 frac_circle (contour
, l
->Point2
.X
, l
->Point2
.Y
, v
, 2);
517 v
[0] = l
->Point2
.X
+ dx
;
518 v
[1] = l
->Point2
.Y
+ dy
;
519 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
520 v
[0] = l
->Point1
.X
+ dx
;
521 v
[1] = l
->Point1
.Y
+ dy
;
522 if (TEST_FLAG (SQUAREFLAG
,l
))
523 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
525 frac_circle (contour
, l
->Point1
.X
, l
->Point1
.Y
, v
, 2);
526 /* now we have the line contour */
527 if (!(np
= ContourToPoly (contour
)))
532 /* make a rounded-corner rectangle */
534 SquarePadPoly (PadType
* pad
, BDimension clear
)
536 PLINE
*contour
= NULL
;
542 PadType _t
=*pad
,*t
=&_t
;
543 PadType _c
=*pad
,*c
=&_c
;
544 int halfthick
= (pad
->Thickness
+ 1) / 2;
545 int halfclear
= (clear
+ 1) / 2;
548 sqrt (SQUARE (pad
->Point1
.X
- pad
->Point2
.X
) +
549 SQUARE (pad
->Point1
.Y
- pad
->Point2
.Y
));
552 double a
= halfthick
/ d
;
553 tx
= (t
->Point1
.Y
- t
->Point2
.Y
) * a
;
554 ty
= (t
->Point2
.X
- t
->Point1
.X
) * a
;
556 cx
= (c
->Point1
.Y
- c
->Point2
.Y
) * a
;
557 cy
= (c
->Point2
.X
- c
->Point1
.X
) * a
;
581 v
[0] = c
->Point1
.X
- tx
;
582 v
[1] = c
->Point1
.Y
- ty
;
583 if ((contour
= poly_NewContour (v
)) == NULL
)
585 frac_circle (contour
, (t
->Point1
.X
- tx
), (t
->Point1
.Y
- ty
), v
, 4);
587 v
[0] = t
->Point2
.X
- cx
;
588 v
[1] = t
->Point2
.Y
- cy
;
589 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
590 frac_circle (contour
, (t
->Point2
.X
- tx
), (t
->Point2
.Y
- ty
), v
, 4);
592 v
[0] = c
->Point2
.X
+ tx
;
593 v
[1] = c
->Point2
.Y
+ ty
;
594 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
595 frac_circle (contour
, (t
->Point2
.X
+ tx
), (t
->Point2
.Y
+ ty
), v
, 4);
597 v
[0] = t
->Point1
.X
+ cx
;
598 v
[1] = t
->Point1
.Y
+ cy
;
599 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
600 frac_circle (contour
, (t
->Point1
.X
+ tx
), (t
->Point1
.Y
+ ty
), v
, 4);
602 /* now we have the line contour */
603 if (!(np
= ContourToPoly (contour
)))
608 /* clear np1 from the polygon */
610 Subtract (POLYAREA
* np1
, PolygonType
* p
, Boolean fnp
)
612 POLYAREA
*merged
= NULL
, *np
= np1
;
622 assert (poly_Valid (p
->Clipped
));
623 assert (poly_Valid (np
));
625 x
= poly_Boolean_free (p
->Clipped
, np
, &merged
, PBO_SUB
);
628 x
= poly_Boolean (p
->Clipped
, np
, &merged
, PBO_SUB
);
629 poly_Free (&p
->Clipped
);
631 assert (!merged
|| poly_Valid (merged
));
634 fprintf (stderr
, "Error while clipping PBO_SUB: %d\n", x
);
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 (TEST_FLAG (SQUAREFLAG
, pad
))
749 (np
= SquarePadPoly (pad
, pad
->Thickness
+ pad
->Clearance
)))
755 (np
= LinePoly ((LineType
*) pad
, pad
->Thickness
+ pad
->Clearance
)))
758 return Subtract (np
, p
, True
);
763 const BoxType
*other
;
766 PolygonType
*polygon
;
772 pin_sub_callback (const BoxType
* b
, void *cl
)
774 PinTypePtr pin
= (PinTypePtr
) b
;
775 struct cpInfo
*info
= (struct cpInfo
*) cl
;
776 PolygonTypePtr polygon
;
778 /* don't subtract the object that was put back! */
779 if (b
== info
->other
)
781 polygon
= info
->polygon
;
782 if (SubtractPin (info
->data
, pin
, info
->layer
, polygon
) < 0)
783 longjmp (info
->env
, 1);
788 arc_sub_callback (const BoxType
* b
, void *cl
)
790 ArcTypePtr arc
= (ArcTypePtr
) b
;
791 struct cpInfo
*info
= (struct cpInfo
*) cl
;
792 PolygonTypePtr polygon
;
794 /* don't subtract the object that was put back! */
795 if (b
== info
->other
)
797 if (!TEST_FLAG (CLEARLINEFLAG
, arc
))
799 polygon
= info
->polygon
;
800 if (SubtractArc (arc
, polygon
) < 0)
801 longjmp (info
->env
, 1);
806 pad_sub_callback (const BoxType
* b
, void *cl
)
808 PadTypePtr pad
= (PadTypePtr
) b
;
809 struct cpInfo
*info
= (struct cpInfo
*) cl
;
810 PolygonTypePtr polygon
;
812 /* don't subtract the object that was put back! */
813 if (b
== info
->other
)
815 polygon
= info
->polygon
;
816 if (XOR (TEST_FLAG (ONSOLDERFLAG
, pad
), !info
->solder
))
818 if (SubtractPad (pad
, polygon
) < 0)
819 longjmp (info
->env
, 1);
826 line_sub_callback (const BoxType
* b
, void *cl
)
828 LineTypePtr line
= (LineTypePtr
) b
;
829 struct cpInfo
*info
= (struct cpInfo
*) cl
;
830 PolygonTypePtr polygon
;
832 /* don't subtract the object that was put back! */
833 if (b
== info
->other
)
835 if (!TEST_FLAG (CLEARLINEFLAG
, line
))
837 polygon
= info
->polygon
;
838 if (SubtractLine (line
, polygon
) < 0)
839 longjmp (info
->env
, 1);
844 text_sub_callback (const BoxType
* b
, void *cl
)
846 TextTypePtr text
= (TextTypePtr
) b
;
847 struct cpInfo
*info
= (struct cpInfo
*) cl
;
848 PolygonTypePtr polygon
;
850 /* don't subtract the object that was put back! */
851 if (b
== info
->other
)
853 if (!TEST_FLAG (CLEARLINEFLAG
, text
))
855 polygon
= info
->polygon
;
856 if (SubtractText (text
, polygon
) < 0)
857 longjmp (info
->env
, 1);
862 Group (DataTypePtr Data
, Cardinal layer
)
865 for (i
= 0; i
< max_layer
; i
++)
866 for (j
= 0; j
< ((PCBType
*) (Data
->pcb
))->LayerGroups
.Number
[i
]; j
++)
867 if (layer
== ((PCBType
*) (Data
->pcb
))->LayerGroups
.Entries
[i
][j
])
873 clearPoly (DataTypePtr Data
, LayerTypePtr Layer
, PolygonType
* polygon
,
874 const BoxType
* here
, BDimension expand
)
881 if (!TEST_FLAG (CLEARPOLYFLAG
, polygon
)
882 || GetLayerNumber (Data
, Layer
) >= max_layer
)
884 group
= Group (Data
, GetLayerNumber (Data
, Layer
));
885 info
.solder
= (group
== Group (Data
, max_layer
+ SOLDER_LAYER
));
889 info
.polygon
= polygon
;
891 region
= clip_box (here
, &polygon
->BoundingBox
);
893 region
= polygon
->BoundingBox
;
894 region
= bloat_box (®ion
, expand
);
896 if (setjmp (info
.env
) == 0)
898 r
= r_search (Data
->via_tree
, ®ion
, NULL
, pin_sub_callback
, &info
);
899 r
+= r_search (Data
->pin_tree
, ®ion
, NULL
, pin_sub_callback
, &info
);
900 GROUP_LOOP (Data
, group
);
903 r_search (layer
->line_tree
, ®ion
, NULL
, line_sub_callback
,
906 r_search (layer
->arc_tree
, ®ion
, NULL
, arc_sub_callback
, &info
);
908 r_search (layer
->text_tree
, ®ion
, NULL
, text_sub_callback
, &info
);
911 if (info
.solder
|| group
== Group (Data
, max_layer
+ COMPONENT_LAYER
))
912 r
+= r_search (Data
->pad_tree
, ®ion
, NULL
, pad_sub_callback
, &info
);
918 Unsubtract (POLYAREA
* np1
, PolygonType
* p
)
920 POLYAREA
*merged
= NULL
, *np
= np1
;
923 assert (p
&& p
->Clipped
);
924 x
= poly_Boolean_free (p
->Clipped
, np
, &merged
, PBO_UNITE
);
927 fprintf (stderr
, "Error while clipping PBO_UNITE: %d\n", x
);
932 p
->Clipped
= biggest (merged
);
933 assert (!p
->Clipped
|| poly_Valid (p
->Clipped
));
934 return ClipOriginal (p
);
938 UnsubtractPin (PinType
* pin
, LayerType
* l
, PolygonType
* p
)
942 /* overlap a bit to prevent gaps from rounding errors */
943 np
= BoxPolyBloated (&pin
->BoundingBox
, UNSUBTRACT_BLOAT
);
947 if (!Unsubtract (np
, p
))
949 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) pin
, 2 * UNSUBTRACT_BLOAT
);
954 UnsubtractArc (ArcType
* arc
, LayerType
* l
, PolygonType
* p
)
958 if (!TEST_FLAG (CLEARLINEFLAG
, arc
))
961 /* overlap a bit to prevent gaps from rounding errors */
962 np
= BoxPolyBloated (&arc
->BoundingBox
, UNSUBTRACT_BLOAT
);
966 if (!Unsubtract (np
, p
))
968 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) arc
, 2 * UNSUBTRACT_BLOAT
);
973 UnsubtractLine (LineType
* line
, LayerType
* l
, PolygonType
* p
)
977 if (!TEST_FLAG (CLEARLINEFLAG
, line
))
980 /* overlap a bit to prevent notches from rounding errors */
981 np
= BoxPolyBloated (&line
->BoundingBox
, UNSUBTRACT_BLOAT
);
985 if (!Unsubtract (np
, p
))
987 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) line
, 2 * UNSUBTRACT_BLOAT
);
992 UnsubtractText (TextType
* text
, LayerType
* l
, PolygonType
* p
)
996 if (!TEST_FLAG (CLEARLINEFLAG
, text
))
999 /* overlap a bit to prevent notches from rounding errors */
1000 np
= BoxPolyBloated (&text
->BoundingBox
, UNSUBTRACT_BLOAT
);
1004 if (!Unsubtract (np
, p
))
1006 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) text
, 2 * UNSUBTRACT_BLOAT
);
1011 UnsubtractPad (PadType
* pad
, LayerType
* l
, PolygonType
* p
)
1015 /* overlap a bit to prevent notches from rounding errors */
1016 np
= BoxPolyBloated (&pad
->BoundingBox
, UNSUBTRACT_BLOAT
);
1020 if (!Unsubtract (np
, p
))
1022 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) pad
, 2 * UNSUBTRACT_BLOAT
);
1026 static Boolean inhibit
= False
;
1029 InitClip (DataTypePtr Data
, LayerTypePtr layer
, PolygonType
* p
)
1034 poly_Free (&p
->Clipped
);
1035 p
->Clipped
= original_poly (p
);
1038 assert (poly_Valid (p
->Clipped
));
1039 if (TEST_FLAG (CLEARPOLYFLAG
, p
))
1040 clearPoly (Data
, layer
, p
, NULL
, 0);
1044 /* --------------------------------------------------------------------------
1045 * remove redundant polygon points. Any point that lies on the straight
1046 * line between the points on either side of it is redundant.
1047 * returns true if any points are removed
1050 RemoveExcessPolygonPoints (LayerTypePtr Layer
, PolygonTypePtr Polygon
)
1052 PointTypePtr pt1
, pt2
, pt3
;
1055 Boolean changed
= False
;
1059 /* there are always at least three points in a polygon */
1060 pt1
= &Polygon
->Points
[Polygon
->PointN
- 1];
1061 pt2
= &Polygon
->Points
[0];
1062 pt3
= &Polygon
->Points
[1];
1063 for (n
= 0; n
< Polygon
->PointN
; n
++, pt1
++, pt2
++, pt3
++)
1065 /* wrap around polygon */
1067 pt1
= &Polygon
->Points
[0];
1068 if (n
== Polygon
->PointN
- 1)
1069 pt3
= &Polygon
->Points
[0];
1073 if (IsPointOnLine ((float) pt2
->X
, (float) pt2
->Y
, 0.0, &line
))
1075 RemoveObject (POLYGONPOINT_TYPE
, (void *) Layer
, (void *) Polygon
,
1083 /* ---------------------------------------------------------------------------
1084 * returns the index of the polygon point which is the end
1085 * point of the segment with the lowest distance to the passed
1089 GetLowestDistancePolygonPoint (PolygonTypePtr Polygon
, LocationType X
,
1092 double mindistance
= (double) MAX_COORD
* MAX_COORD
;
1093 PointTypePtr ptr1
= &Polygon
->Points
[Polygon
->PointN
- 1],
1094 ptr2
= &Polygon
->Points
[0];
1095 Cardinal n
, result
= 0;
1097 /* we calculate the distance to each segment and choose the
1098 * shortest distance. If the closest approach between the
1099 * given point and the projected line (i.e. the segment extended)
1100 * is not on the segment, then the distance is the distance
1101 * to the segment end point.
1104 for (n
= 0; n
< Polygon
->PointN
; n
++, ptr2
++)
1106 register double u
, dx
, dy
;
1107 dx
= ptr2
->X
- ptr1
->X
;
1108 dy
= ptr2
->Y
- ptr1
->Y
;
1109 if (dx
!= 0.0 || dy
!= 0.0)
1111 /* projected intersection is at P1 + u(P2 - P1) */
1112 u
= ((X
- ptr1
->X
) * dx
+ (Y
- ptr1
->Y
) * dy
) / (dx
* dx
+ dy
* dy
);
1115 { /* ptr1 is closest point */
1116 u
= SQUARE (X
- ptr1
->X
) + SQUARE (Y
- ptr1
->Y
);
1119 { /* ptr2 is closest point */
1120 u
= SQUARE (X
- ptr2
->X
) + SQUARE (Y
- ptr2
->Y
);
1123 { /* projected intersection is closest point */
1124 u
= SQUARE (X
- ptr1
->X
* (1.0 - u
) - u
* ptr2
->X
) +
1125 SQUARE (Y
- ptr1
->Y
* (1.0 - u
) - u
* ptr2
->Y
);
1127 if (u
< mindistance
)
1138 /* ---------------------------------------------------------------------------
1139 * go back to the previous point of the polygon
1142 GoToPreviousPoint (void)
1144 switch (Crosshair
.AttachedPolygon
.PointN
)
1146 /* do nothing if mode has just been entered */
1150 /* reset number of points and 'LINE_MODE' state */
1152 Crosshair
.AttachedPolygon
.PointN
= 0;
1153 Crosshair
.AttachedLine
.State
= STATE_FIRST
;
1157 /* back-up one point */
1160 PointTypePtr points
= Crosshair
.AttachedPolygon
.Points
;
1161 Cardinal n
= Crosshair
.AttachedPolygon
.PointN
- 2;
1163 Crosshair
.AttachedPolygon
.PointN
--;
1164 Crosshair
.AttachedLine
.Point1
.X
= points
[n
].X
;
1165 Crosshair
.AttachedLine
.Point1
.Y
= points
[n
].Y
;
1171 /* ---------------------------------------------------------------------------
1172 * close polygon if possible
1177 Cardinal n
= Crosshair
.AttachedPolygon
.PointN
;
1179 /* check number of points */
1182 /* if 45 degree lines are what we want do a quick check
1183 * if closing the polygon makes sense
1185 if (!TEST_FLAG (ALLDIRECTIONFLAG
, PCB
))
1189 dx
= abs (Crosshair
.AttachedPolygon
.Points
[n
- 1].X
-
1190 Crosshair
.AttachedPolygon
.Points
[0].X
);
1191 dy
= abs (Crosshair
.AttachedPolygon
.Points
[n
- 1].Y
-
1192 Crosshair
.AttachedPolygon
.Points
[0].Y
);
1193 if (!(dx
== 0 || dy
== 0 || dx
== dy
))
1197 ("Cannot close polygon because 45 degree lines are requested.\n"));
1201 CopyAttachedPolygonToLayer ();
1205 Message (_("A polygon has to have at least 3 points\n"));
1208 /* ---------------------------------------------------------------------------
1209 * moves the data of the attached (new) polygon to the current layer
1212 CopyAttachedPolygonToLayer (void)
1214 PolygonTypePtr polygon
;
1217 /* move data to layer and clear attached struct */
1218 polygon
= CreateNewPolygon (CURRENT
, NoFlags ());
1219 saveID
= polygon
->ID
;
1220 *polygon
= Crosshair
.AttachedPolygon
;
1221 polygon
->ID
= saveID
;
1222 SET_FLAG (CLEARPOLYFLAG
, polygon
);
1223 if (TEST_FLAG (NEWFULLPOLYFLAG
, PCB
))
1224 SET_FLAG (FULLPOLYFLAG
, polygon
);
1225 memset (&Crosshair
.AttachedPolygon
, 0, sizeof (PolygonType
));
1226 SetPolygonBoundingBox (polygon
);
1227 if (!CURRENT
->polygon_tree
)
1228 CURRENT
->polygon_tree
= r_create_tree (NULL
, 0, 0);
1229 r_insert_entry (CURRENT
->polygon_tree
, (BoxType
*) polygon
, 0);
1230 InitClip (PCB
->Data
, CURRENT
, polygon
);
1231 DrawPolygon (CURRENT
, polygon
, 0);
1232 SetChangedFlag (True
);
1234 /* reset state of attached line */
1235 Crosshair
.AttachedLine
.State
= STATE_FIRST
;
1238 /* add to undo list */
1239 AddObjectToCreateUndoList (POLYGON_TYPE
, CURRENT
, polygon
, polygon
);
1240 IncrementUndoSerialNumber ();
1243 /* find polygon holes in range, then call the callback function for
1244 * each hole. If the callback returns non-zero, stop
1248 PolygonHoles (const BoxType
* range
, LayerTypePtr layer
,
1249 PolygonTypePtr polygon
, int (*any_call
) (PLINE
* contour
,
1251 PolygonTypePtr poly
))
1253 POLYAREA
*pa
= polygon
->Clipped
;
1255 /* If this hole is so big the polygon doesn't exist, then it's not
1260 for (pl
= pa
->contours
->next
; pl
; pl
= pl
->next
)
1262 if (pl
->xmin
> range
->X2
|| pl
->xmax
< range
->X1
||
1263 pl
->ymin
> range
->Y2
|| pl
->ymax
< range
->Y1
)
1265 if (any_call (pl
, layer
, polygon
))
1279 int (*callback
) (DataTypePtr
, LayerTypePtr
, PolygonTypePtr
, int, void *,
1284 subtract_plow (DataTypePtr Data
, LayerTypePtr Layer
, PolygonTypePtr Polygon
,
1285 int type
, void *ptr1
, void *ptr2
)
1287 if (!Polygon
->Clipped
)
1293 SubtractPin (Data
, (PinTypePtr
) ptr2
, Layer
, Polygon
);
1296 SubtractLine ((LineTypePtr
) ptr2
, Polygon
);
1299 SubtractArc ((ArcTypePtr
) ptr2
, Polygon
);
1302 SubtractPad ((PadTypePtr
) ptr2
, Polygon
);
1305 SubtractText ((TextTypePtr
) ptr2
, Polygon
);
1312 add_plow (DataTypePtr Data
, LayerTypePtr Layer
, PolygonTypePtr Polygon
,
1313 int type
, void *ptr1
, void *ptr2
)
1319 UnsubtractPin ((PinTypePtr
) ptr2
, Layer
, Polygon
);
1322 UnsubtractLine ((LineTypePtr
) ptr2
, Layer
, Polygon
);
1325 UnsubtractArc ((ArcTypePtr
) ptr2
, Layer
, Polygon
);
1328 UnsubtractPad ((PadTypePtr
) ptr2
, Layer
, Polygon
);
1331 UnsubtractText ((TextTypePtr
) ptr2
, Layer
, Polygon
);
1338 plow_callback (const BoxType
* b
, void *cl
)
1340 struct plow_info
*plow
= (struct plow_info
*) cl
;
1341 PolygonTypePtr polygon
= (PolygonTypePtr
) b
;
1343 if (TEST_FLAG (CLEARPOLYFLAG
, polygon
))
1344 return plow
->callback (plow
->data
, plow
->layer
, polygon
, plow
->type
,
1345 plow
->ptr1
, plow
->ptr2
);
1350 PlowsPolygon (DataType
* Data
, int type
, void *ptr1
, void *ptr2
,
1351 int (*call_back
) (DataTypePtr data
, LayerTypePtr lay
,
1352 PolygonTypePtr poly
, int type
, void *ptr1
,
1355 BoxType sb
= ((PinTypePtr
) ptr2
)->BoundingBox
;
1357 struct plow_info info
;
1363 info
.callback
= call_back
;
1368 if (type
== PIN_TYPE
|| ptr1
== ptr2
|| ptr1
== NULL
)
1370 LAYER_LOOP (Data
, max_layer
);
1374 r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1380 GROUP_LOOP (Data
, GetLayerGroupNumberByNumber (GetLayerNumber (Data
,
1381 ((LayerTypePtr
) ptr1
))));
1385 r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1393 /* the cast works equally well for lines and arcs */
1394 if (!TEST_FLAG (CLEARLINEFLAG
, (LineTypePtr
) ptr2
))
1396 /* silk doesn't plow */
1397 if (GetLayerNumber (Data
, ptr1
) >= max_layer
)
1399 GROUP_LOOP (Data
, GetLayerGroupNumberByNumber (GetLayerNumber (Data
,
1400 ((LayerTypePtr
) ptr1
))));
1403 r
+= r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1409 Cardinal group
= TEST_FLAG (ONSOLDERFLAG
,
1410 (PadType
*) ptr2
) ? SOLDER_LAYER
:
1412 group
= GetLayerGroupNumberByNumber (max_layer
+ group
);
1413 GROUP_LOOP (Data
, group
);
1417 r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1425 PIN_LOOP ((ElementType
*) ptr1
);
1427 PlowsPolygon (Data
, PIN_TYPE
, ptr1
, pin
, call_back
);
1430 PAD_LOOP ((ElementType
*) ptr1
);
1432 PlowsPolygon (Data
, PAD_TYPE
, ptr1
, pad
, call_back
);
1442 RestoreToPolygon (DataType
* Data
, int type
, void *ptr1
, void *ptr2
)
1444 if (type
== POLYGON_TYPE
)
1445 InitClip (PCB
->Data
, (LayerTypePtr
) ptr1
, (PolygonTypePtr
) ptr2
);
1447 PlowsPolygon (Data
, type
, ptr1
, ptr2
, add_plow
);
1451 ClearFromPolygon (DataType
* Data
, int type
, void *ptr1
, void *ptr2
)
1453 if (type
== POLYGON_TYPE
)
1454 InitClip (PCB
->Data
, (LayerTypePtr
) ptr1
, (PolygonTypePtr
) ptr2
);
1456 PlowsPolygon (Data
, type
, ptr1
, ptr2
, subtract_plow
);
1460 isects (POLYAREA
* a
, PolygonTypePtr p
, Boolean fr
)
1464 ans
= Touching (a
, p
->Clipped
);
1465 /* argument may be register, so we must copy it */
1474 IsPointInPolygon (LocationType X
, LocationType Y
, BDimension r
,
1481 if (poly_CheckInside (p
->Clipped
, v
))
1485 if (!(c
= CirclePoly (X
, Y
, r
)))
1487 return isects (c
, p
, True
);
1492 IsPointInPolygonIgnoreHoles (LocationType X
, LocationType Y
, PolygonTypePtr p
)
1497 return poly_InsideContour (p
->Clipped
->contours
, v
);
1501 IsRectangleInPolygon (LocationType X1
, LocationType Y1
, LocationType X2
,
1502 LocationType Y2
, PolygonTypePtr p
)
1506 (s
= RectPoly (min (X1
, X2
), max (X1
, X2
), min (Y1
, Y2
), max (Y1
, Y2
))))
1508 return isects (s
, p
, True
);
1512 r_NoHolesPolygonDicer (PLINE
* p
,
1513 void (*emit
) (PolygonTypePtr
, void *), void *user_data
)
1517 pa
= (POLYAREA
*) malloc (sizeof (*pa
));
1520 if (!p
->next
) /* no holes */
1525 poly
.BoundingBox
.X1
= p
->xmin
;
1526 poly
.BoundingBox
.X2
= p
->xmax
;
1527 poly
.BoundingBox
.Y1
= p
->ymin
;
1528 poly
.BoundingBox
.Y2
= p
->ymax
;
1529 poly
.PointN
= poly
.PointMax
= 4;
1532 pts
[0].X
= pts
[0].X2
= p
->xmin
;
1533 pts
[0].Y
= pts
[0].Y2
= p
->ymin
;
1534 pts
[1].X
= pts
[1].X2
= p
->xmax
;
1535 pts
[1].Y
= pts
[1].Y2
= p
->ymin
;
1536 pts
[2].X
= pts
[2].X2
= p
->xmax
;
1537 pts
[2].Y
= pts
[2].Y2
= p
->ymax
;
1538 pts
[3].X
= pts
[3].X2
= p
->xmin
;
1539 pts
[3].Y
= pts
[3].Y2
= p
->ymax
;
1540 poly
.Flags
= MakeFlags (CLEARPOLYFLAG
);
1541 emit (&poly
, user_data
);
1547 POLYAREA
*poly2
, *left
, *right
;
1549 /* make a rectangle of the left region slicing through the middle of the first hole */
1551 RectPoly (p
->xmin
, (p
->next
->xmin
+ p
->next
->xmax
) / 2, p
->ymin
,
1553 poly_AndSubtract_free (pa
, poly2
, &left
, &right
);
1560 PLINE
*pl
= x
->contours
;
1561 r_NoHolesPolygonDicer (pl
, emit
, user_data
);
1563 /* the pline was already freed by its use int he recursive dicer */
1566 while ((x
= y
) != left
);
1574 PLINE
*pl
= x
->contours
;
1575 r_NoHolesPolygonDicer (pl
, emit
, user_data
);
1579 while ((x
= y
) != right
);
1585 NoHolesPolygonDicer (PolygonTypePtr p
, const BoxType
* clip
,
1586 void (*emit
) (PolygonTypePtr
, void *), void *user_data
)
1588 POLYAREA
*save
, *ans
;
1590 ans
= save
= poly_Create ();
1591 /* copy the main poly only */
1592 poly_Copy1 (save
, p
->Clipped
);
1593 /* clip to the bounding box */
1596 POLYAREA
*cbox
= RectPoly (clip
->X1
, clip
->X2
, clip
->Y1
, clip
->Y2
);
1599 int r
= poly_Boolean_free (save
, cbox
, &ans
, PBO_ISECT
);
1607 /* now dice it up */
1611 r_NoHolesPolygonDicer (save
->contours
, emit
, user_data
);
1612 /* go to next poly (could be one because of clip) */
1615 /* free the previouse POLYAREA. Note the contour was consumed in the dicer */
1618 while (save
!= ans
);
1621 /* make a polygon split into multiple parts into multiple polygons */
1623 MorphPolygon (LayerTypePtr layer
, PolygonTypePtr poly
)
1625 POLYAREA
*p
, *start
;
1626 Boolean many
= False
;
1629 if (!poly
->Clipped
|| TEST_FLAG (LOCKFLAG
, poly
))
1631 if (poly
->Clipped
->f
== poly
->Clipped
)
1633 ErasePolygon (poly
);
1634 start
= p
= poly
->Clipped
;
1635 /* This is ugly. The creation of the new polygons can cause
1636 * all of the polygon pointers (including the one we're called
1637 * with to change if there is a realloc in GetPolygonMemory().
1638 * That will invalidate our original "poly" argument, potentially
1639 * inside the loop. We need to steal the Clipped pointer and
1640 * hide it from the Remove call so that it still exists while
1641 * we do this dirty work.
1643 poly
->Clipped
= NULL
;
1644 flags
= poly
->Flags
;
1645 RemovePolygon (layer
, poly
);
1652 if (p
->contours
->area
> PCB
->IsleArea
)
1654 new = CreateNewPolygon (layer
, flags
);
1658 v
= &p
->contours
->head
;
1659 CreateNewPointInPolygon (new, v
->point
[0], v
->point
[1]);
1660 for (v
= v
->next
; v
!= &p
->contours
->head
; v
= v
->next
)
1661 CreateNewPointInPolygon (new, v
->point
[0], v
->point
[1]);
1662 new->BoundingBox
.X1
= p
->contours
->xmin
;
1663 new->BoundingBox
.X2
= p
->contours
->xmax
+ 1;
1664 new->BoundingBox
.Y1
= p
->contours
->ymin
;
1665 new->BoundingBox
.Y2
= p
->contours
->ymax
+ 1;
1666 AddObjectToCreateUndoList (POLYGON_TYPE
, layer
, new, new);
1668 p
= p
->f
; /* go to next pline */
1669 new->Clipped
->b
= new->Clipped
->f
= new->Clipped
; /* unlink from others */
1670 r_insert_entry (layer
->polygon_tree
, (BoxType
*) new, 0);
1671 DrawPolygon (layer
, new, 0);
1678 poly_DelContour (&t
->contours
);
1684 IncrementUndoSerialNumber ();
1688 void debug_pline (PLINE
*pl
)
1691 fprintf (stderr
, "\txmin %d xmax %d ymin %d ymax %d\n",
1692 pl
->xmin
, pl
->xmax
, pl
->ymin
, pl
->ymax
);
1696 fprintf(stderr
, "\t\tvnode: %d,%d\n", v
->point
[0], v
->point
[1]);
1704 debug_polyarea (POLYAREA
*p
)
1708 fprintf (stderr
, "POLYAREA %p\n", p
);
1709 for (pl
=p
->contours
; pl
; pl
=pl
->next
)
1714 debug_polygon (PolygonType
*p
)
1718 fprintf (stderr
, "POLYGON %p %d pts\n", p
, p
->PointN
);
1719 for (i
=0; i
<p
->PointN
; i
++)
1720 fprintf(stderr
, "\t%d: %d, %d\n", i
, p
->Points
[i
].X
, p
->Points
[i
].Y
);
1724 debug_polyarea (pa
);
1726 if (pa
== p
->Clipped
)