4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996,2010 Thomas Nau
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 * Contact addresses for paper mail and Email:
22 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
23 * Thomas.Nau@rz.uni-ulm.de
30 Here's a brief tour of the data and life of a polygon, courtesy of Ben
33 A PCB PolygonType contains an array of points outlining the polygon.
34 This is what is manipulated by the UI and stored in the saved PCB.
36 A PolygonType also contains a POLYAREA called 'Clipped' which is
37 computed dynamically by InitClip every time a board is loaded. The
38 point array is coverted to a POLYAREA by original_poly and then holes
39 are cut in it by clearPoly. After that it is maintained dynamically
40 as parts are added, moved or removed (this is why sometimes bugs can
41 be fixed by just re-loading the board).
43 A POLYAREA consists of a linked list of PLINE structures. The head of
44 that list is POLYAREA.contours. The first contour is an outline of a
45 filled region. All of the subsequent PLINEs are holes cut out of that
46 first contour. POLYAREAs are in a doubly-linked list and each member
47 of the list is an independent (non-overlapping) area with its own
48 outline and holes. The function biggest() finds the largest POLYAREA
49 so that PolygonType.Clipped points to that shape. The rest of the
50 polygon still exists, it's just ignored when turning the polygon into
53 The first POLYAREA in PolygonType.Clipped is what is used for the vast
54 majority of Polygon related tests. The basic logic for an
55 intersection is "is the target shape inside POLYAREA.contours and NOT
56 fully enclosed in any of POLYAREA.contours.next... (the holes)".
58 The polygon dicer (NoHolesPolygonDicer and r_NoHolesPolygonDicer)
59 emits a series of "simple" PLINE shapes. That is, the PLINE isn't
60 linked to any other "holes" oulines). That's the meaning of the first
61 test in r_NoHolesPolygonDicer. It is testing to see if the PLINE
62 contour (the first, making it a solid outline) has a valid next
63 pointer (which would point to one or more holes). The dicer works by
64 recursively chopping the polygon in half through the first hole it
65 sees (which is guaranteed to eliminate at least that one hole). The
66 dicer output is used for HIDs which cannot render things with holes
67 (which would require erasure).
71 /* special polygon editing routines
87 #include "crosshair.h"
94 #include "pcb-printf.h"
103 #ifdef HAVE_LIBDMALLOC
107 #define ROUND(x) ((long)(((x) >= 0 ? (x) + 0.5 : (x) - 0.5)))
109 #define UNSUBTRACT_BLOAT 10
110 #define SUBTRACT_PIN_VIA_BATCH_SIZE 100
111 #define SUBTRACT_LINE_BATCH_SIZE 20
113 static double rotate_circle_seg
[4];
118 double cos_ang
= cos (2.0 * M_PI
/ POLY_CIRC_SEGS_F
);
119 double sin_ang
= sin (2.0 * M_PI
/ POLY_CIRC_SEGS_F
);
121 rotate_circle_seg
[0] = cos_ang
; rotate_circle_seg
[1] = -sin_ang
;
122 rotate_circle_seg
[2] = sin_ang
; rotate_circle_seg
[3] = cos_ang
;
126 polygon_point_idx (PolygonType
*polygon
, PointType
*point
)
128 assert (point
>= polygon
->Points
);
129 assert (point
<= polygon
->Points
+ polygon
->PointN
);
130 return ((char *)point
- (char *)polygon
->Points
) / sizeof (PointType
);
133 /* Find contour number: 0 for outer, 1 for first hole etc.. */
135 polygon_point_contour (PolygonType
*polygon
, Cardinal point
)
138 Cardinal contour
= 0;
140 for (i
= 0; i
< polygon
->HoleIndexN
; i
++)
141 if (point
>= polygon
->HoleIndex
[i
])
147 next_contour_point (PolygonType
*polygon
, Cardinal point
)
150 Cardinal this_contour_start
;
151 Cardinal next_contour_start
;
153 contour
= polygon_point_contour (polygon
, point
);
155 this_contour_start
= (contour
== 0) ? 0 :
156 polygon
->HoleIndex
[contour
- 1];
158 (contour
== polygon
->HoleIndexN
) ? polygon
->PointN
:
159 polygon
->HoleIndex
[contour
];
161 /* Wrap back to the start of the contour we're in if we pass the end */
162 if (++point
== next_contour_start
)
163 point
= this_contour_start
;
169 prev_contour_point (PolygonType
*polygon
, Cardinal point
)
172 Cardinal prev_contour_end
;
173 Cardinal this_contour_end
;
175 contour
= polygon_point_contour (polygon
, point
);
177 prev_contour_end
= (contour
== 0) ? 0 :
178 polygon
->HoleIndex
[contour
- 1];
180 (contour
== polygon
->HoleIndexN
) ? polygon
->PointN
- 1:
181 polygon
->HoleIndex
[contour
] - 1;
183 /* Wrap back to the start of the contour we're in if we pass the end */
184 if (point
== prev_contour_end
)
185 point
= this_contour_end
;
193 add_noholes_polyarea (PLINE
*pline
, void *user_data
)
195 PolygonType
*poly
= (PolygonType
*)user_data
;
197 /* Prepend the pline into the NoHoles linked list */
198 pline
->next
= poly
->NoHoles
;
199 poly
->NoHoles
= pline
;
203 ComputeNoHoles (PolygonType
*poly
)
205 poly_FreeContours (&poly
->NoHoles
);
207 NoHolesPolygonDicer (poly
, NULL
, add_noholes_polyarea
, poly
);
209 printf ("Compute_noholes caught poly->Clipped = NULL\n");
210 poly
->NoHolesValid
= 1;
214 biggest (POLYAREA
* p
)
216 POLYAREA
*n
, *top
= NULL
;
226 if (n
->contours
->area
< PCB
->IsleArea
)
230 poly_DelContour (&n
->contours
);
241 if (n
->contours
->area
> big
)
244 big
= n
->contours
->area
;
247 while ((n
= n
->f
) != p
);
252 tree
= top
->contour_tree
;
253 top
->contours
= p
->contours
;
254 top
->contour_tree
= p
->contour_tree
;
256 p
->contour_tree
= tree
;
264 ContourToPoly (PLINE
* contour
)
267 poly_PreContour (contour
, TRUE
);
268 assert (contour
->Flags
.orient
== PLF_DIR
);
269 if (!(p
= poly_Create ()))
271 poly_InclContour (p
, contour
);
272 assert (poly_Valid (p
));
277 original_poly (PolygonType
* p
)
279 PLINE
*contour
= NULL
;
285 if ((np
= poly_Create ()) == NULL
)
288 /* first make initial polygon contour */
289 for (n
= 0; n
< p
->PointN
; n
++)
291 /* No current contour? Make a new one starting at point */
292 /* (or) Add point to existing contour */
294 v
[0] = p
->Points
[n
].X
;
295 v
[1] = p
->Points
[n
].Y
;
298 if ((contour
= poly_NewContour (v
)) == NULL
)
303 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
306 /* Is current point last in contour? If so process it. */
307 if (n
== p
->PointN
- 1 ||
308 (hole
< p
->HoleIndexN
&& n
== p
->HoleIndex
[hole
] - 1))
310 poly_PreContour (contour
, TRUE
);
312 /* make sure it is a positive contour (outer) or negative (hole) */
313 if (contour
->Flags
.orient
!= (hole
? PLF_INV
: PLF_DIR
))
314 poly_InvContour (contour
);
315 assert (contour
->Flags
.orient
== (hole
? PLF_INV
: PLF_DIR
));
317 poly_InclContour (np
, contour
);
319 assert (poly_Valid (np
));
328 PolygonToPoly (PolygonType
*p
)
330 return original_poly (p
);
334 RectPoly (Coord x1
, Coord x2
, Coord y1
, Coord y2
)
336 PLINE
*contour
= NULL
;
339 /* Return NULL for zero or negatively sized rectangles */
340 if (x2
<= x1
|| y2
<= y1
)
345 if ((contour
= poly_NewContour (v
)) == NULL
)
349 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
352 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
355 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
356 return ContourToPoly (contour
);
360 OctagonPoly (Coord x
, Coord y
, Coord radius
)
362 PLINE
*contour
= NULL
;
365 v
[0] = x
+ ROUND (radius
* 0.5);
366 v
[1] = y
+ ROUND (radius
* TAN_22_5_DEGREE_2
);
367 if ((contour
= poly_NewContour (v
)) == NULL
)
369 v
[0] = x
+ ROUND (radius
* TAN_22_5_DEGREE_2
);
370 v
[1] = y
+ ROUND (radius
* 0.5);
371 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
372 v
[0] = x
- (v
[0] - x
);
373 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
374 v
[0] = x
- ROUND (radius
* 0.5);
375 v
[1] = y
+ ROUND (radius
* TAN_22_5_DEGREE_2
);
376 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
377 v
[1] = y
- (v
[1] - y
);
378 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
379 v
[0] = x
- ROUND (radius
* TAN_22_5_DEGREE_2
);
380 v
[1] = y
- ROUND (radius
* 0.5);
381 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
382 v
[0] = x
- (v
[0] - x
);
383 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
384 v
[0] = x
+ ROUND (radius
* 0.5);
385 v
[1] = y
- ROUND (radius
* TAN_22_5_DEGREE_2
);
386 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
387 return ContourToPoly (contour
);
390 /* add verticies in a fractional-circle starting from v
391 * centered at X, Y and going counter-clockwise
392 * does not include the first point
393 * last argument is 1 for a full circle
394 * 2 for a half circle
395 * or 4 for a quarter circle
398 frac_circle (PLINE
* c
, Coord X
, Coord Y
, Vector v
, int fraction
)
403 poly_InclVertex (c
->head
.prev
, poly_CreateNode (v
));
404 /* move vector to origin */
405 e1
= (v
[0] - X
) * POLY_CIRC_RADIUS_ADJ
;
406 e2
= (v
[1] - Y
) * POLY_CIRC_RADIUS_ADJ
;
408 /* NB: the caller adds the last vertex, hence the -1 */
409 range
= POLY_CIRC_SEGS
/ fraction
- 1;
410 for (i
= 0; i
< range
; i
++)
412 /* rotate the vector */
413 t1
= rotate_circle_seg
[0] * e1
+ rotate_circle_seg
[1] * e2
;
414 e2
= rotate_circle_seg
[2] * e1
+ rotate_circle_seg
[3] * e2
;
416 v
[0] = X
+ ROUND (e1
);
417 v
[1] = Y
+ ROUND (e2
);
418 poly_InclVertex (c
->head
.prev
, poly_CreateNode (v
));
422 /* create a circle approximation from lines */
424 CirclePoly (Coord x
, Coord y
, Coord radius
)
433 if ((contour
= poly_NewContour (v
)) == NULL
)
435 frac_circle (contour
, x
, y
, v
, 1);
436 contour
->is_round
= TRUE
;
439 contour
->radius
= radius
;
440 return ContourToPoly (contour
);
443 /* make a rounded-corner rectangle with radius t beyond x1,x2,y1,y2 rectangle */
445 RoundRect (Coord x1
, Coord x2
, Coord y1
, Coord y2
, Coord t
)
447 PLINE
*contour
= NULL
;
454 if ((contour
= poly_NewContour (v
)) == NULL
)
456 frac_circle (contour
, x1
, y1
, v
, 4);
459 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
460 frac_circle (contour
, x2
, y1
, v
, 4);
463 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
464 frac_circle (contour
, x2
, y2
, v
, 4);
467 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
468 frac_circle (contour
, x1
, y2
, v
, 4);
469 return ContourToPoly (contour
);
474 ArcPolyNoIntersect (ArcType
* a
, Coord thick
)
476 PLINE
*contour
= NULL
;
481 double ang
, da
, rx
, ry
;
489 a
->StartAngle
+= a
->Delta
;
490 a
->Delta
= -a
->Delta
;
492 half
= (thick
+ 1) / 2;
493 ends
= GetArcEnds (a
);
494 /* start with inner radius */
495 rx
= MAX (a
->Width
- half
, 0);
496 ry
= MAX (a
->Height
- half
, 0);
499 segs
= MAX (segs
, a
->Delta
* M_PI
/ 360 *
500 sqrt (sqrt ((double)rx
* rx
+ (double)ry
* ry
) /
501 POLY_ARC_MAX_DEVIATION
/ 2 / thick
));
502 segs
= MAX(segs
, a
->Delta
/ ARC_ANGLE
);
505 da
= (1.0 * a
->Delta
) / segs
;
506 radius_adj
= (M_PI
*da
/360)*(M_PI
*da
/360)/2;
507 v
[0] = a
->X
- rx
* cos (ang
* M180
);
508 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
509 if ((contour
= poly_NewContour (v
)) == NULL
)
511 for (i
= 0; i
< segs
- 1; i
++)
514 v
[0] = a
->X
- rx
* cos (ang
* M180
);
515 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
516 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
518 /* find last point */
519 ang
= a
->StartAngle
+ a
->Delta
;
520 v
[0] = a
->X
- rx
* cos (ang
* M180
) * (1 - radius_adj
);
521 v
[1] = a
->Y
+ ry
* sin (ang
* M180
) * (1 - radius_adj
);
522 /* add the round cap at the end */
523 frac_circle (contour
, ends
->X2
, ends
->Y2
, v
, 2);
524 /* and now do the outer arc (going backwards) */
525 rx
= (a
->Width
+ half
) * (1+radius_adj
);
526 ry
= (a
->Width
+ half
) * (1+radius_adj
);
528 for (i
= 0; i
< segs
; i
++)
530 v
[0] = a
->X
- rx
* cos (ang
* M180
);
531 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
532 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
535 /* now add other round cap */
537 v
[0] = a
->X
- rx
* cos (ang
* M180
) * (1 - radius_adj
);
538 v
[1] = a
->Y
+ ry
* sin (ang
* M180
) * (1 - radius_adj
);
539 frac_circle (contour
, ends
->X1
, ends
->Y1
, v
, 2);
540 /* now we have the whole contour */
541 if (!(np
= ContourToPoly (contour
)))
546 #define MIN_CLEARANCE_BEFORE_BISECT 10.
548 ArcPoly (ArcType
* a
, Coord thick
)
552 POLYAREA
*tmp1
, *tmp2
, *res
;
554 delta
= (a
->Delta
< 0) ? -a
->Delta
: a
->Delta
;
556 /* If the arc segment would self-intersect, we need to construct it as the union of
557 two non-intersecting segments */
559 if (2 * M_PI
* a
->Width
* (1. - (double)delta
/ 360.) - thick
< MIN_CLEARANCE_BEFORE_BISECT
)
561 int half_delta
= a
->Delta
/ 2;
564 seg1
.Delta
= half_delta
;
565 seg2
.Delta
-= half_delta
;
566 seg2
.StartAngle
+= half_delta
;
568 tmp1
= ArcPolyNoIntersect (&seg1
, thick
);
569 tmp2
= ArcPolyNoIntersect (&seg2
, thick
);
570 poly_Boolean_free (tmp1
, tmp2
, &res
, PBO_UNITE
);
574 return ArcPolyNoIntersect (a
, thick
);
578 LinePoly (LineType
* L
, Coord thick
)
580 PLINE
*contour
= NULL
;
584 long half
;LineType _l
=*L
,*l
=&_l
;
588 half
= (thick
+ 1) / 2;
590 sqrt (SQUARE (l
->Point1
.X
- l
->Point2
.X
) +
591 SQUARE (l
->Point1
.Y
- l
->Point2
.Y
));
592 if (!TEST_FLAG (SQUAREFLAG
,l
))
593 if (d
== 0) /* line is a point */
594 return CirclePoly (l
->Point1
.X
, l
->Point1
.Y
, half
);
598 dx
= (l
->Point1
.Y
- l
->Point2
.Y
) * d
;
599 dy
= (l
->Point2
.X
- l
->Point1
.X
) * d
;
606 if (TEST_FLAG (SQUAREFLAG
,l
))/* take into account the ends */
613 v
[0] = l
->Point1
.X
- dx
;
614 v
[1] = l
->Point1
.Y
- dy
;
615 if ((contour
= poly_NewContour (v
)) == NULL
)
617 v
[0] = l
->Point2
.X
- dx
;
618 v
[1] = l
->Point2
.Y
- dy
;
619 if (TEST_FLAG (SQUAREFLAG
,l
))
620 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
622 frac_circle (contour
, l
->Point2
.X
, l
->Point2
.Y
, v
, 2);
623 v
[0] = l
->Point2
.X
+ dx
;
624 v
[1] = l
->Point2
.Y
+ dy
;
625 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
626 v
[0] = l
->Point1
.X
+ dx
;
627 v
[1] = l
->Point1
.Y
+ dy
;
628 if (TEST_FLAG (SQUAREFLAG
,l
))
629 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
631 frac_circle (contour
, l
->Point1
.X
, l
->Point1
.Y
, v
, 2);
632 /* now we have the line contour */
633 if (!(np
= ContourToPoly (contour
)))
638 /* make a rounded-corner rectangle */
640 SquarePadPoly (PadType
* pad
, Coord clear
)
642 PLINE
*contour
= NULL
;
648 PadType _t
=*pad
,*t
=&_t
;
649 PadType _c
=*pad
,*c
=&_c
;
650 int halfthick
= (pad
->Thickness
+ 1) / 2;
651 int halfclear
= (clear
+ 1) / 2;
654 sqrt (SQUARE (pad
->Point1
.X
- pad
->Point2
.X
) +
655 SQUARE (pad
->Point1
.Y
- pad
->Point2
.Y
));
658 double a
= halfthick
/ d
;
659 tx
= (t
->Point1
.Y
- t
->Point2
.Y
) * a
;
660 ty
= (t
->Point2
.X
- t
->Point1
.X
) * a
;
662 cx
= (c
->Point1
.Y
- c
->Point2
.Y
) * a
;
663 cy
= (c
->Point2
.X
- c
->Point1
.X
) * a
;
687 v
[0] = c
->Point1
.X
- tx
;
688 v
[1] = c
->Point1
.Y
- ty
;
689 if ((contour
= poly_NewContour (v
)) == NULL
)
691 frac_circle (contour
, (t
->Point1
.X
- tx
), (t
->Point1
.Y
- ty
), v
, 4);
693 v
[0] = t
->Point2
.X
- cx
;
694 v
[1] = t
->Point2
.Y
- cy
;
695 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
696 frac_circle (contour
, (t
->Point2
.X
- tx
), (t
->Point2
.Y
- ty
), v
, 4);
698 v
[0] = c
->Point2
.X
+ tx
;
699 v
[1] = c
->Point2
.Y
+ ty
;
700 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
701 frac_circle (contour
, (t
->Point2
.X
+ tx
), (t
->Point2
.Y
+ ty
), v
, 4);
703 v
[0] = t
->Point1
.X
+ cx
;
704 v
[1] = t
->Point1
.Y
+ cy
;
705 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
706 frac_circle (contour
, (t
->Point1
.X
+ tx
), (t
->Point1
.Y
+ ty
), v
, 4);
708 /* now we have the line contour */
709 if (!(np
= ContourToPoly (contour
)))
714 /* clear np1 from the polygon */
716 Subtract (POLYAREA
* np1
, PolygonType
* p
, bool fnp
)
718 POLYAREA
*merged
= NULL
, *np
= np1
;
728 assert (poly_Valid (p
->Clipped
));
729 assert (poly_Valid (np
));
731 x
= poly_Boolean_free (p
->Clipped
, np
, &merged
, PBO_SUB
);
734 x
= poly_Boolean (p
->Clipped
, np
, &merged
, PBO_SUB
);
735 poly_Free (&p
->Clipped
);
737 assert (!merged
|| poly_Valid (merged
));
740 fprintf (stderr
, "Error while clipping PBO_SUB: %d\n", x
);
743 if (p
->NoHoles
) printf ("Just leaked in Subtract\n");
747 p
->Clipped
= biggest (merged
);
748 assert (!p
->Clipped
|| poly_Valid (p
->Clipped
));
750 Message ("Polygon cleared out of existence near (%d, %d)\n",
751 (p
->BoundingBox
.X1
+ p
->BoundingBox
.X2
) / 2,
752 (p
->BoundingBox
.Y1
+ p
->BoundingBox
.Y2
) / 2);
756 /* create a polygon of the pin clearance */
758 PinPoly (PinType
* pin
, Coord thick
, Coord clear
)
762 if (TEST_FLAG (SQUAREFLAG
, pin
))
764 size
= (thick
+ 1) / 2;
765 return RoundRect (pin
->X
- size
, pin
->X
+ size
, pin
->Y
- size
,
766 pin
->Y
+ size
, (clear
+ 1) / 2);
770 size
= (thick
+ clear
+ 1) / 2;
771 if (TEST_FLAG (OCTAGONFLAG
, pin
))
773 return OctagonPoly (pin
->X
, pin
->Y
, size
+ size
);
776 return CirclePoly (pin
->X
, pin
->Y
, size
);
780 BoxPolyBloated (BoxType
*box
, Coord bloat
)
782 return RectPoly (box
->X1
- bloat
, box
->X2
+ bloat
,
783 box
->Y1
- bloat
, box
->Y2
+ bloat
);
786 /* remove the pin clearance from the polygon */
788 SubtractPin (DataType
* d
, PinType
* pin
, LayerType
* l
, PolygonType
* p
)
793 if (pin
->Clearance
== 0)
795 i
= GetLayerNumber (d
, l
);
796 if (TEST_THERM (i
, pin
))
798 np
= ThermPoly ((PCBType
*) (d
->pcb
), pin
, i
);
804 np
= PinPoly (pin
, PIN_SIZE (pin
), pin
->Clearance
);
808 return Subtract (np
, p
, TRUE
);
812 SubtractLine (LineType
* line
, PolygonType
* p
)
816 if (!TEST_FLAG (CLEARLINEFLAG
, line
))
818 if (!(np
= LinePoly (line
, line
->Thickness
+ line
->Clearance
)))
820 return Subtract (np
, p
, true);
824 SubtractArc (ArcType
* arc
, PolygonType
* p
)
828 if (!TEST_FLAG (CLEARLINEFLAG
, arc
))
830 if (!(np
= ArcPoly (arc
, arc
->Thickness
+ arc
->Clearance
)))
832 return Subtract (np
, p
, true);
836 SubtractText (TextType
* text
, PolygonType
* p
)
839 const BoxType
*b
= &text
->BoundingBox
;
841 if (!TEST_FLAG (CLEARLINEFLAG
, text
))
843 if (!(np
= RoundRect (b
->X1
+ PCB
->Bloat
, b
->X2
- PCB
->Bloat
,
844 b
->Y1
+ PCB
->Bloat
, b
->Y2
- PCB
->Bloat
, PCB
->Bloat
)))
846 return Subtract (np
, p
, true);
850 SubtractPad (PadType
* pad
, PolygonType
* p
)
854 if (pad
->Clearance
== 0)
856 if (TEST_FLAG (SQUAREFLAG
, pad
))
859 (np
= SquarePadPoly (pad
, pad
->Thickness
+ pad
->Clearance
)))
865 (np
= LinePoly ((LineType
*) pad
, pad
->Thickness
+ pad
->Clearance
)))
868 return Subtract (np
, p
, true);
873 const BoxType
*other
;
876 PolygonType
*polygon
;
878 POLYAREA
*accumulate
;
884 subtract_accumulated (struct cpInfo
*info
, PolygonType
*polygon
)
886 if (info
->accumulate
== NULL
)
888 Subtract (info
->accumulate
, polygon
, true);
889 info
->accumulate
= NULL
;
890 info
->batch_size
= 0;
894 pin_sub_callback (const BoxType
* b
, void *cl
)
896 PinType
*pin
= (PinType
*) b
;
897 struct cpInfo
*info
= (struct cpInfo
*) cl
;
898 PolygonType
*polygon
;
903 /* don't subtract the object that was put back! */
904 if (b
== info
->other
)
906 polygon
= info
->polygon
;
908 if (pin
->Clearance
== 0)
910 i
= GetLayerNumber (info
->data
, info
->layer
);
911 if (TEST_THERM (i
, pin
))
913 np
= ThermPoly ((PCBType
*) (info
->data
->pcb
), pin
, i
);
919 np
= PinPoly (pin
, PIN_SIZE (pin
), pin
->Clearance
);
921 longjmp (info
->env
, 1);
924 poly_Boolean_free (info
->accumulate
, np
, &merged
, PBO_UNITE
);
925 info
->accumulate
= merged
;
929 if (info
->batch_size
== SUBTRACT_PIN_VIA_BATCH_SIZE
)
930 subtract_accumulated (info
, polygon
);
936 arc_sub_callback (const BoxType
* b
, void *cl
)
938 ArcType
*arc
= (ArcType
*) b
;
939 struct cpInfo
*info
= (struct cpInfo
*) cl
;
940 PolygonType
*polygon
;
942 /* don't subtract the object that was put back! */
943 if (b
== info
->other
)
945 if (!TEST_FLAG (CLEARLINEFLAG
, arc
))
947 polygon
= info
->polygon
;
948 if (SubtractArc (arc
, polygon
) < 0)
949 longjmp (info
->env
, 1);
954 pad_sub_callback (const BoxType
* b
, void *cl
)
956 PadType
*pad
= (PadType
*) b
;
957 struct cpInfo
*info
= (struct cpInfo
*) cl
;
958 PolygonType
*polygon
;
960 /* don't subtract the object that was put back! */
961 if (b
== info
->other
)
963 if (pad
->Clearance
== 0)
965 polygon
= info
->polygon
;
966 if (XOR (TEST_FLAG (ONSOLDERFLAG
, pad
), !info
->bottom
))
968 if (SubtractPad (pad
, polygon
) < 0)
969 longjmp (info
->env
, 1);
976 line_sub_callback (const BoxType
* b
, void *cl
)
978 LineType
*line
= (LineType
*) b
;
979 struct cpInfo
*info
= (struct cpInfo
*) cl
;
980 PolygonType
*polygon
;
984 /* don't subtract the object that was put back! */
985 if (b
== info
->other
)
987 if (!TEST_FLAG (CLEARLINEFLAG
, line
))
989 polygon
= info
->polygon
;
991 if (!(np
= LinePoly (line
, line
->Thickness
+ line
->Clearance
)))
992 longjmp (info
->env
, 1);
994 poly_Boolean_free (info
->accumulate
, np
, &merged
, PBO_UNITE
);
995 info
->accumulate
= merged
;
998 if (info
->batch_size
== SUBTRACT_LINE_BATCH_SIZE
)
999 subtract_accumulated (info
, polygon
);
1005 text_sub_callback (const BoxType
* b
, void *cl
)
1007 TextType
*text
= (TextType
*) b
;
1008 struct cpInfo
*info
= (struct cpInfo
*) cl
;
1009 PolygonType
*polygon
;
1011 /* don't subtract the object that was put back! */
1012 if (b
== info
->other
)
1014 if (!TEST_FLAG (CLEARLINEFLAG
, text
))
1016 polygon
= info
->polygon
;
1017 if (SubtractText (text
, polygon
) < 0)
1018 longjmp (info
->env
, 1);
1023 Group (DataType
*Data
, Cardinal layer
)
1026 for (i
= 0; i
< max_group
; i
++)
1027 for (j
= 0; j
< ((PCBType
*) (Data
->pcb
))->LayerGroups
.Number
[i
]; j
++)
1028 if (layer
== ((PCBType
*) (Data
->pcb
))->LayerGroups
.Entries
[i
][j
])
1034 clearPoly (DataType
*Data
, LayerType
*Layer
, PolygonType
* polygon
,
1035 const BoxType
* here
, Coord expand
)
1042 if (!TEST_FLAG (CLEARPOLYFLAG
, polygon
)
1043 || GetLayerNumber (Data
, Layer
) >= max_copper_layer
)
1045 group
= Group (Data
, GetLayerNumber (Data
, Layer
));
1046 info
.bottom
= (group
== Group (Data
, bottom_silk_layer
));
1050 info
.polygon
= polygon
;
1052 region
= clip_box (here
, &polygon
->BoundingBox
);
1054 region
= polygon
->BoundingBox
;
1055 region
= bloat_box (®ion
, expand
);
1057 if (setjmp (info
.env
) == 0)
1060 info
.accumulate
= NULL
;
1061 info
.batch_size
= 0;
1062 if (info
.bottom
|| group
== Group (Data
, top_silk_layer
))
1063 r
+= r_search (Data
->pad_tree
, ®ion
, NULL
, pad_sub_callback
, &info
);
1064 GROUP_LOOP (Data
, group
);
1067 r_search (layer
->line_tree
, ®ion
, NULL
, line_sub_callback
,
1069 subtract_accumulated (&info
, polygon
);
1071 r_search (layer
->arc_tree
, ®ion
, NULL
, arc_sub_callback
, &info
);
1073 r_search (layer
->text_tree
, ®ion
, NULL
, text_sub_callback
, &info
);
1076 r
+= r_search (Data
->via_tree
, ®ion
, NULL
, pin_sub_callback
, &info
);
1077 r
+= r_search (Data
->pin_tree
, ®ion
, NULL
, pin_sub_callback
, &info
);
1078 subtract_accumulated (&info
, polygon
);
1080 polygon
->NoHolesValid
= 0;
1085 Unsubtract (POLYAREA
* np1
, PolygonType
* p
)
1087 POLYAREA
*merged
= NULL
, *np
= np1
;
1088 POLYAREA
*orig_poly
, *clipped_np
;
1091 assert (p
&& p
->Clipped
);
1093 orig_poly
= original_poly (p
);
1095 x
= poly_Boolean_free (np
, orig_poly
, &clipped_np
, PBO_ISECT
);
1098 fprintf (stderr
, "Error while clipping PBO_ISECT: %d\n", x
);
1099 poly_Free (&clipped_np
);
1103 x
= poly_Boolean_free (p
->Clipped
, clipped_np
, &merged
, PBO_UNITE
);
1106 fprintf (stderr
, "Error while clipping PBO_UNITE: %d\n", x
);
1107 poly_Free (&merged
);
1110 p
->Clipped
= biggest (merged
);
1111 assert (!p
->Clipped
|| poly_Valid (p
->Clipped
));
1116 if (p
->NoHoles
) printf ("Just leaked in Unsubtract\n");
1122 UnsubtractPin (PinType
* pin
, LayerType
* l
, PolygonType
* p
)
1126 /* overlap a bit to prevent gaps from rounding errors */
1127 np
= BoxPolyBloated (&pin
->BoundingBox
, UNSUBTRACT_BLOAT
);
1131 if (!Unsubtract (np
, p
))
1133 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) pin
, 2 * UNSUBTRACT_BLOAT
);
1138 UnsubtractArc (ArcType
* arc
, LayerType
* l
, PolygonType
* p
)
1142 if (!TEST_FLAG (CLEARLINEFLAG
, arc
))
1145 /* overlap a bit to prevent gaps from rounding errors */
1146 np
= BoxPolyBloated (&arc
->BoundingBox
, UNSUBTRACT_BLOAT
);
1150 if (!Unsubtract (np
, p
))
1152 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) arc
, 2 * UNSUBTRACT_BLOAT
);
1157 UnsubtractLine (LineType
* line
, LayerType
* l
, PolygonType
* p
)
1161 if (!TEST_FLAG (CLEARLINEFLAG
, line
))
1164 /* overlap a bit to prevent notches from rounding errors */
1165 np
= BoxPolyBloated (&line
->BoundingBox
, UNSUBTRACT_BLOAT
);
1169 if (!Unsubtract (np
, p
))
1171 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) line
, 2 * UNSUBTRACT_BLOAT
);
1176 UnsubtractText (TextType
* text
, LayerType
* l
, PolygonType
* p
)
1180 if (!TEST_FLAG (CLEARLINEFLAG
, text
))
1183 /* overlap a bit to prevent notches from rounding errors */
1184 np
= BoxPolyBloated (&text
->BoundingBox
, UNSUBTRACT_BLOAT
);
1188 if (!Unsubtract (np
, p
))
1190 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) text
, 2 * UNSUBTRACT_BLOAT
);
1195 UnsubtractPad (PadType
* pad
, LayerType
* l
, PolygonType
* p
)
1199 /* overlap a bit to prevent notches from rounding errors */
1200 np
= BoxPolyBloated (&pad
->BoundingBox
, UNSUBTRACT_BLOAT
);
1204 if (!Unsubtract (np
, p
))
1206 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) pad
, 2 * UNSUBTRACT_BLOAT
);
1210 static bool inhibit
= false;
1213 InitClip (DataType
*Data
, LayerType
*layer
, PolygonType
* p
)
1218 poly_Free (&p
->Clipped
);
1219 p
->Clipped
= original_poly (p
);
1220 poly_FreeContours (&p
->NoHoles
);
1223 assert (poly_Valid (p
->Clipped
));
1224 if (TEST_FLAG (CLEARPOLYFLAG
, p
))
1225 clearPoly (Data
, layer
, p
, NULL
, 0);
1227 p
->NoHolesValid
= 0;
1231 /* --------------------------------------------------------------------------
1232 * remove redundant polygon points. Any point that lies on the straight
1233 * line between the points on either side of it is redundant.
1234 * returns true if any points are removed
1237 RemoveExcessPolygonPoints (LayerType
*Layer
, PolygonType
*Polygon
)
1240 Cardinal n
, prev
, next
;
1242 bool changed
= false;
1247 for (n
= 0; n
< Polygon
->PointN
; n
++)
1249 prev
= prev_contour_point (Polygon
, n
);
1250 next
= next_contour_point (Polygon
, n
);
1251 p
= &Polygon
->Points
[n
];
1253 line
.Point1
= Polygon
->Points
[prev
];
1254 line
.Point2
= Polygon
->Points
[next
];
1256 if (IsPointOnLine (p
->X
, p
->Y
, 0.0, &line
))
1258 RemoveObject (POLYGONPOINT_TYPE
, Layer
, Polygon
, p
);
1265 /* ---------------------------------------------------------------------------
1266 * returns the index of the polygon point which is the end
1267 * point of the segment with the lowest distance to the passed
1271 GetLowestDistancePolygonPoint (PolygonType
*Polygon
, Coord X
, Coord Y
)
1273 double mindistance
= (double) MAX_COORD
* MAX_COORD
;
1274 PointType
*ptr1
, *ptr2
;
1275 Cardinal n
, result
= 0;
1277 /* we calculate the distance to each segment and choose the
1278 * shortest distance. If the closest approach between the
1279 * given point and the projected line (i.e. the segment extended)
1280 * is not on the segment, then the distance is the distance
1281 * to the segment end point.
1284 for (n
= 0; n
< Polygon
->PointN
; n
++)
1287 ptr1
= &Polygon
->Points
[prev_contour_point (Polygon
, n
)];
1288 ptr2
= &Polygon
->Points
[n
];
1290 dx
= ptr2
->X
- ptr1
->X
;
1291 dy
= ptr2
->Y
- ptr1
->Y
;
1292 if (dx
!= 0.0 || dy
!= 0.0)
1294 /* projected intersection is at P1 + u(P2 - P1) */
1295 u
= ((X
- ptr1
->X
) * dx
+ (Y
- ptr1
->Y
) * dy
) / (dx
* dx
+ dy
* dy
);
1298 { /* ptr1 is closest point */
1299 u
= SQUARE (X
- ptr1
->X
) + SQUARE (Y
- ptr1
->Y
);
1302 { /* ptr2 is closest point */
1303 u
= SQUARE (X
- ptr2
->X
) + SQUARE (Y
- ptr2
->Y
);
1306 { /* projected intersection is closest point */
1307 u
= SQUARE (X
- ptr1
->X
* (1.0 - u
) - u
* ptr2
->X
) +
1308 SQUARE (Y
- ptr1
->Y
* (1.0 - u
) - u
* ptr2
->Y
);
1310 if (u
< mindistance
)
1320 /* ---------------------------------------------------------------------------
1321 * go back to the previous point of the polygon
1324 GoToPreviousPoint (void)
1326 switch (Crosshair
.AttachedPolygon
.PointN
)
1328 /* do nothing if mode has just been entered */
1332 /* reset number of points and 'LINE_MODE' state */
1334 Crosshair
.AttachedPolygon
.PointN
= 0;
1335 Crosshair
.AttachedLine
.State
= STATE_FIRST
;
1339 /* back-up one point */
1342 PointType
*points
= Crosshair
.AttachedPolygon
.Points
;
1343 Cardinal n
= Crosshair
.AttachedPolygon
.PointN
- 2;
1345 Crosshair
.AttachedPolygon
.PointN
--;
1346 Crosshair
.AttachedLine
.Point1
.X
= points
[n
].X
;
1347 Crosshair
.AttachedLine
.Point1
.Y
= points
[n
].Y
;
1353 /* ---------------------------------------------------------------------------
1354 * close polygon if possible
1359 Cardinal n
= Crosshair
.AttachedPolygon
.PointN
;
1361 /* check number of points */
1364 /* if 45 degree lines are what we want do a quick check
1365 * if closing the polygon makes sense
1367 if (!TEST_FLAG (ALLDIRECTIONFLAG
, PCB
))
1371 dx
= abs (Crosshair
.AttachedPolygon
.Points
[n
- 1].X
-
1372 Crosshair
.AttachedPolygon
.Points
[0].X
);
1373 dy
= abs (Crosshair
.AttachedPolygon
.Points
[n
- 1].Y
-
1374 Crosshair
.AttachedPolygon
.Points
[0].Y
);
1375 if (!(dx
== 0 || dy
== 0 || dx
== dy
))
1379 ("Cannot close polygon because 45 degree lines are requested.\n"));
1383 CopyAttachedPolygonToLayer ();
1387 Message (_("A polygon has to have at least 3 points\n"));
1390 /* ---------------------------------------------------------------------------
1391 * moves the data of the attached (new) polygon to the current layer
1394 CopyAttachedPolygonToLayer (void)
1396 PolygonType
*polygon
;
1399 /* move data to layer and clear attached struct */
1400 polygon
= CreateNewPolygon (CURRENT
, NoFlags ());
1401 saveID
= polygon
->ID
;
1402 *polygon
= Crosshair
.AttachedPolygon
;
1403 polygon
->ID
= saveID
;
1404 SET_FLAG (CLEARPOLYFLAG
, polygon
);
1405 if (TEST_FLAG (NEWFULLPOLYFLAG
, PCB
))
1406 SET_FLAG (FULLPOLYFLAG
, polygon
);
1407 memset (&Crosshair
.AttachedPolygon
, 0, sizeof (PolygonType
));
1408 SetPolygonBoundingBox (polygon
);
1409 if (!CURRENT
->polygon_tree
)
1410 CURRENT
->polygon_tree
= r_create_tree (NULL
, 0, 0);
1411 r_insert_entry (CURRENT
->polygon_tree
, (BoxType
*) polygon
, 0);
1412 InitClip (PCB
->Data
, CURRENT
, polygon
);
1413 DrawPolygon (CURRENT
, polygon
);
1414 SetChangedFlag (true);
1416 /* reset state of attached line */
1417 Crosshair
.AttachedLine
.State
= STATE_FIRST
;
1420 /* add to undo list */
1421 AddObjectToCreateUndoList (POLYGON_TYPE
, CURRENT
, polygon
, polygon
);
1422 IncrementUndoSerialNumber ();
1425 /* find polygon holes in range, then call the callback function for
1426 * each hole. If the callback returns non-zero, stop
1430 PolygonHoles (PolygonType
*polygon
, const BoxType
*range
,
1431 int (*callback
) (PLINE
*contour
, void *user_data
),
1434 POLYAREA
*pa
= polygon
->Clipped
;
1436 /* If this hole is so big the polygon doesn't exist, then it's not
1441 for (pl
= pa
->contours
->next
; pl
; pl
= pl
->next
)
1443 if (range
!= NULL
&&
1444 (pl
->xmin
> range
->X2
|| pl
->xmax
< range
->X1
||
1445 pl
->ymin
> range
->Y2
|| pl
->ymax
< range
->Y1
))
1447 if (callback (pl
, user_data
))
1461 int (*callback
) (DataType
*, LayerType
*, PolygonType
*, int, void *,
1467 subtract_plow (DataType
*Data
, LayerType
*Layer
, PolygonType
*Polygon
,
1468 int type
, void *ptr1
, void *ptr2
, void *userdata
)
1470 if (!Polygon
->Clipped
)
1476 SubtractPin (Data
, (PinType
*) ptr2
, Layer
, Polygon
);
1477 Polygon
->NoHolesValid
= 0;
1480 SubtractLine ((LineType
*) ptr2
, Polygon
);
1481 Polygon
->NoHolesValid
= 0;
1484 SubtractArc ((ArcType
*) ptr2
, Polygon
);
1485 Polygon
->NoHolesValid
= 0;
1488 SubtractPad ((PadType
*) ptr2
, Polygon
);
1489 Polygon
->NoHolesValid
= 0;
1492 SubtractText ((TextType
*) ptr2
, Polygon
);
1493 Polygon
->NoHolesValid
= 0;
1500 add_plow (DataType
*Data
, LayerType
*Layer
, PolygonType
*Polygon
,
1501 int type
, void *ptr1
, void *ptr2
, void *userdata
)
1507 UnsubtractPin ((PinType
*) ptr2
, Layer
, Polygon
);
1510 UnsubtractLine ((LineType
*) ptr2
, Layer
, Polygon
);
1513 UnsubtractArc ((ArcType
*) ptr2
, Layer
, Polygon
);
1516 UnsubtractPad ((PadType
*) ptr2
, Layer
, Polygon
);
1519 UnsubtractText ((TextType
*) ptr2
, Layer
, Polygon
);
1526 plow_callback (const BoxType
* b
, void *cl
)
1528 struct plow_info
*plow
= (struct plow_info
*) cl
;
1529 PolygonType
*polygon
= (PolygonType
*) b
;
1531 if (TEST_FLAG (CLEARPOLYFLAG
, polygon
))
1532 return plow
->callback (plow
->data
, plow
->layer
, polygon
, plow
->type
,
1533 plow
->ptr1
, plow
->ptr2
, plow
->userdata
);
1538 PlowsPolygon (DataType
* Data
, int type
, void *ptr1
, void *ptr2
,
1539 int (*call_back
) (DataType
*data
, LayerType
*lay
,
1540 PolygonType
*poly
, int type
, void *ptr1
,
1541 void *ptr2
, void *userdata
),
1544 BoxType sb
= ((PinType
*) ptr2
)->BoundingBox
;
1546 struct plow_info info
;
1552 info
.callback
= call_back
;
1553 info
.userdata
= userdata
;
1558 if (type
== PIN_TYPE
|| ptr1
== ptr2
|| ptr1
== NULL
)
1560 LAYER_LOOP (Data
, max_copper_layer
);
1564 r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1570 GROUP_LOOP (Data
, GetLayerGroupNumberByNumber (GetLayerNumber (Data
,
1571 ((LayerType
*) ptr1
))));
1575 r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1583 /* the cast works equally well for lines and arcs */
1584 if (!TEST_FLAG (CLEARLINEFLAG
, (LineType
*) ptr2
))
1586 /* silk doesn't plow */
1587 if (GetLayerNumber (Data
, (LayerType
*)ptr1
) >= max_copper_layer
)
1589 GROUP_LOOP (Data
, GetLayerGroupNumberByNumber (GetLayerNumber (Data
,
1590 ((LayerType
*) ptr1
))));
1593 r
+= r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1599 Cardinal group
= GetLayerGroupNumberByNumber (
1600 TEST_FLAG (ONSOLDERFLAG
, (PadType
*) ptr2
) ?
1601 bottom_silk_layer
: top_silk_layer
);
1602 GROUP_LOOP (Data
, group
);
1606 r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
1614 PIN_LOOP ((ElementType
*) ptr1
);
1616 PlowsPolygon (Data
, PIN_TYPE
, ptr1
, pin
, call_back
, userdata
);
1619 PAD_LOOP ((ElementType
*) ptr1
);
1621 PlowsPolygon (Data
, PAD_TYPE
, ptr1
, pad
, call_back
, userdata
);
1631 RestoreToPolygon (DataType
* Data
, int type
, void *ptr1
, void *ptr2
)
1633 if (!Data
->polyClip
)
1636 /* XXX: HACK, this is a kludgy place to put a check for recomputing our outline */
1637 if (type
== LINE_TYPE
|| type
== ARC_TYPE
)
1639 LayerType
*layer
= (LayerType
*) ptr1
;
1641 if (strcmp (layer
->Name
, "outline") == 0 ||
1642 strcmp (layer
->Name
, "route") == 0)
1643 Data
->outline_valid
= false;
1646 if (type
== POLYGON_TYPE
)
1647 InitClip (PCB
->Data
, (LayerType
*) ptr1
, (PolygonType
*) ptr2
);
1649 PlowsPolygon (Data
, type
, ptr1
, ptr2
, add_plow
, NULL
);
1653 ClearFromPolygon (DataType
* Data
, int type
, void *ptr1
, void *ptr2
)
1655 if (!Data
->polyClip
)
1658 /* XXX: HACK, this is a kludgy place to put a check for recomputing our outline */
1659 if (type
== LINE_TYPE
|| type
== ARC_TYPE
)
1661 LayerType
*layer
= (LayerType
*) ptr1
;
1663 if (strcmp (layer
->Name
, "outline") == 0 ||
1664 strcmp (layer
->Name
, "route") == 0)
1665 Data
->outline_valid
= false;
1668 if (type
== POLYGON_TYPE
)
1669 InitClip (PCB
->Data
, (LayerType
*) ptr1
, (PolygonType
*) ptr2
);
1671 PlowsPolygon (Data
, type
, ptr1
, ptr2
, subtract_plow
, NULL
);
1675 isects (POLYAREA
* a
, PolygonType
*p
, bool fr
)
1679 ans
= Touching (a
, p
->Clipped
);
1680 /* argument may be register, so we must copy it */
1689 IsPointInPolygon (Coord X
, Coord Y
, Coord r
, PolygonType
*p
)
1695 if (poly_CheckInside (p
->Clipped
, v
))
1699 if (!(c
= CirclePoly (X
, Y
, r
)))
1701 return isects (c
, p
, true);
1706 IsPointInPolygonIgnoreHoles (Coord X
, Coord Y
, PolygonType
*p
)
1711 return poly_InsideContour (p
->Clipped
->contours
, v
);
1715 IsRectangleInPolygon (Coord X1
, Coord Y1
, Coord X2
, Coord Y2
, PolygonType
*p
)
1719 (s
= RectPoly (min (X1
, X2
), max (X1
, X2
), min (Y1
, Y2
), max (Y1
, Y2
))))
1721 return isects (s
, p
, true);
1724 /* NB: This function will free the passed POLYAREA.
1725 * It must only be passed a single POLYAREA (pa->f == pa->b == pa)
1728 r_NoHolesPolygonDicer (POLYAREA
* pa
,
1729 void (*emit
) (PLINE
*, void *), void *user_data
)
1731 PLINE
*p
= pa
->contours
;
1733 if (!pa
->contours
->next
) /* no holes */
1735 pa
->contours
= NULL
; /* The callback now owns the contour */
1736 /* Don't bother removing it from the POLYAREA's rtree
1737 since we're going to free the POLYAREA below anyway */
1738 emit (p
, user_data
);
1744 POLYAREA
*poly2
, *left
, *right
;
1746 /* make a rectangle of the left region slicing through the middle of the first hole */
1747 poly2
= RectPoly (p
->xmin
, (p
->next
->xmin
+ p
->next
->xmax
) / 2,
1749 poly_AndSubtract_free (pa
, poly2
, &left
, &right
);
1752 POLYAREA
*cur
, *next
;
1757 cur
->f
= cur
->b
= cur
; /* Detach this polygon piece */
1758 r_NoHolesPolygonDicer (cur
, emit
, user_data
);
1759 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1761 while ((cur
= next
) != left
);
1765 POLYAREA
*cur
, *next
;
1770 cur
->f
= cur
->b
= cur
; /* Detach this polygon piece */
1771 r_NoHolesPolygonDicer (cur
, emit
, user_data
);
1772 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1774 while ((cur
= next
) != right
);
1780 NoHolesPolygonDicer (PolygonType
*p
, const BoxType
* clip
,
1781 void (*emit
) (PLINE
*, void *), void *user_data
)
1783 POLYAREA
*main_contour
, *cur
, *next
;
1785 main_contour
= poly_Create ();
1786 /* copy the main poly only */
1787 poly_Copy1 (main_contour
, p
->Clipped
);
1788 /* clip to the bounding box */
1791 POLYAREA
*cbox
= RectPoly (clip
->X1
, clip
->X2
, clip
->Y1
, clip
->Y2
);
1792 poly_Boolean_free (main_contour
, cbox
, &main_contour
, PBO_ISECT
);
1794 if (main_contour
== NULL
)
1797 * NB: Could be more than one piece (because of the clip above)
1803 cur
->f
= cur
->b
= cur
; /* Detach this polygon piece */
1804 r_NoHolesPolygonDicer (cur
, emit
, user_data
);
1805 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1807 while ((cur
= next
) != main_contour
);
1810 /* make a polygon split into multiple parts into multiple polygons */
1812 MorphPolygon (LayerType
*layer
, PolygonType
*poly
)
1814 POLYAREA
*p
, *start
;
1818 if (!poly
->Clipped
|| TEST_FLAG (LOCKFLAG
, poly
))
1820 if (poly
->Clipped
->f
== poly
->Clipped
)
1822 ErasePolygon (poly
);
1823 start
= p
= poly
->Clipped
;
1824 /* This is ugly. The creation of the new polygons can cause
1825 * all of the polygon pointers (including the one we're called
1826 * with to change if there is a realloc in GetPolygonMemory().
1827 * That will invalidate our original "poly" argument, potentially
1828 * inside the loop. We need to steal the Clipped pointer and
1829 * hide it from the Remove call so that it still exists while
1830 * we do this dirty work.
1832 poly
->Clipped
= NULL
;
1833 if (poly
->NoHoles
) printf ("Just leaked in MorpyPolygon\n");
1834 poly
->NoHoles
= NULL
;
1835 flags
= poly
->Flags
;
1836 RemovePolygon (layer
, poly
);
1841 PolygonType
*newone
;
1843 if (p
->contours
->area
> PCB
->IsleArea
)
1845 newone
= CreateNewPolygon (layer
, flags
);
1849 v
= &p
->contours
->head
;
1850 CreateNewPointInPolygon (newone
, v
->point
[0], v
->point
[1]);
1851 for (v
= v
->next
; v
!= &p
->contours
->head
; v
= v
->next
)
1852 CreateNewPointInPolygon (newone
, v
->point
[0], v
->point
[1]);
1853 newone
->BoundingBox
.X1
= p
->contours
->xmin
;
1854 newone
->BoundingBox
.X2
= p
->contours
->xmax
+ 1;
1855 newone
->BoundingBox
.Y1
= p
->contours
->ymin
;
1856 newone
->BoundingBox
.Y2
= p
->contours
->ymax
+ 1;
1857 AddObjectToCreateUndoList (POLYGON_TYPE
, layer
, newone
, newone
);
1858 newone
->Clipped
= p
;
1859 p
= p
->f
; /* go to next pline */
1860 newone
->Clipped
->b
= newone
->Clipped
->f
= newone
->Clipped
; /* unlink from others */
1861 r_insert_entry (layer
->polygon_tree
, (BoxType
*) newone
, 0);
1862 DrawPolygon (layer
, newone
);
1869 poly_DelContour (&t
->contours
);
1875 IncrementUndoSerialNumber ();
1879 void debug_pline (PLINE
*pl
)
1882 pcb_fprintf (stderr
, "\txmin %#mS xmax %#mS ymin %#mS ymax %#mS\n",
1883 pl
->xmin
, pl
->xmax
, pl
->ymin
, pl
->ymax
);
1887 pcb_fprintf(stderr
, "\t\tvnode: %#mD\n", v
->point
[0], v
->point
[1]);
1895 debug_polyarea (POLYAREA
*p
)
1899 fprintf (stderr
, "POLYAREA %p\n", p
);
1900 for (pl
=p
->contours
; pl
; pl
=pl
->next
)
1905 debug_polygon (PolygonType
*p
)
1909 fprintf (stderr
, "POLYGON %p %d pts\n", p
, p
->PointN
);
1910 for (i
=0; i
<p
->PointN
; i
++)
1911 pcb_fprintf(stderr
, "\t%d: %#mD\n", i
, p
->Points
[i
].X
, p
->Points
[i
].Y
);
1914 fprintf (stderr
, "%d holes, starting at indices\n", p
->HoleIndexN
);
1915 for (i
=0; i
<p
->HoleIndexN
; i
++)
1916 fprintf(stderr
, "\t%d: %d\n", i
, p
->HoleIndex
[i
]);
1919 fprintf (stderr
, "it has no holes\n");
1923 debug_polyarea (pa
);
1925 if (pa
== p
->Clipped
)
1930 /* Convert a POLYAREA (and all linked POLYAREA) to
1931 * raw PCB polygons on the given layer.
1934 PolyToPolygonsOnLayer (DataType
*Destination
, LayerType
*Layer
,
1935 POLYAREA
*Input
, FlagType Flags
)
1937 PolygonType
*Polygon
;
1949 Polygon
= CreateNewPolygon (Layer
, Flags
);
1951 pline
= pa
->contours
;
1957 CreateNewHoleInPolygon (Polygon
);
1960 node
= &pline
->head
;
1963 CreateNewPointInPolygon (Polygon
, node
->point
[0],
1966 while ((node
= node
->next
) != &pline
->head
);
1969 while ((pline
= pline
->next
) != NULL
);
1971 InitClip (Destination
, Layer
, Polygon
);
1972 SetPolygonBoundingBox (Polygon
);
1973 if (!Layer
->polygon_tree
)
1974 Layer
->polygon_tree
= r_create_tree (NULL
, 0, 0);
1975 r_insert_entry (Layer
->polygon_tree
, (BoxType
*) Polygon
, 0);
1977 DrawPolygon (Layer
, Polygon
);
1978 /* add to undo list */
1979 AddObjectToCreateUndoList (POLYGON_TYPE
, Layer
, Polygon
, Polygon
);
1981 while ((pa
= pa
->f
) != Input
);
1983 SetChangedFlag (true);
1987 struct clip_outline_info
{
1991 #define ROUTER_THICKNESS MIL_TO_COORD (10)
1992 //#define ROUTER_THICKNESS MIL_TO_COORD (0.1)
1995 arc_outline_callback (const BoxType
* b
, void *cl
)
1997 ArcType
*arc
= (ArcType
*)b
;
1998 struct clip_outline_info
*info
= cl
;
2001 if (!(np
= ArcPoly (arc
, ROUTER_THICKNESS
)))
2004 poly_Boolean_free (info
->poly
, np
, &res
, PBO_SUB
);
2011 line_outline_callback (const BoxType
* b
, void *cl
)
2013 LineType
*line
= (LineType
*)b
;
2014 struct clip_outline_info
*info
= cl
;
2017 if (!(np
= LinePoly (line
, ROUTER_THICKNESS
)))
2020 poly_Boolean_free (info
->poly
, np
, &res
, PBO_SUB
);
2027 delete_piece_cb (gpointer data
, gpointer userdata
)
2029 POLYAREA
*piece
= data
;
2030 POLYAREA
**res
= userdata
;
2032 /* If this item was the start of the list, advance that pointer */
2036 /* But reset it to NULL if it wraps around and hits us again */
2040 piece
->b
->f
= piece
->f
;
2041 piece
->f
->b
= piece
->b
;
2042 piece
->f
= piece
->b
= piece
;
2047 POLYAREA
*board_outline_poly (void)
2051 int found_outline
= 0;
2052 LayerType
*Layer
= NULL
;
2054 struct clip_outline_info info
;
2055 POLYAREA
*whole_world
;
2059 GList
*pieces_to_delete
= NULL
;
2060 bool any_pieces_kept
= false;
2062 #define BLOAT_WORLD MIL_TO_COORD (10)
2064 whole_world
= RectPoly (-BLOAT_WORLD
, BLOAT_WORLD
+ PCB
->MaxWidth
,
2065 -BLOAT_WORLD
, BLOAT_WORLD
+ PCB
->MaxHeight
);
2067 for (i
= 0; i
< max_copper_layer
; i
++)
2069 Layer
= PCB
->Data
->Layer
+ i
;
2071 if (strcmp (Layer
->Name
, "outline") == 0 ||
2072 strcmp (Layer
->Name
, "route") == 0)
2079 if (!found_outline
) {
2080 printf ("Didn't find outline\n");
2084 /* Do stuff to turn the outline layer into a polygon */
2086 /* Ideally, we just want to look at centre-lines, but that is hard!
2088 * Lets add all lines, arcs etc.. together to form a polygon comprising
2089 * the bits we presume a router would remove.
2091 * Then we need to subtract that from some notional "infinite" plane
2092 * polygon, leaving the remaining pieces.
2094 * OR.. we could just look at the holes in the resulting polygon?
2096 * Given these holes, we need to know which are inside and outside.
2107 info
.poly
= whole_world
;
2111 region
.X2
= PCB
->MaxWidth
;
2112 region
.Y2
= PCB
->MaxHeight
;
2114 r_search (Layer
->line_tree
, ®ion
, NULL
, line_outline_callback
, &info
);
2115 r_search (Layer
->arc_tree
, ®ion
, NULL
, arc_outline_callback
, &info
);
2117 clipped
= info
.poly
;
2119 /* Now we just need to work out which pieces of polygon are inside
2120 and outside the board! */
2122 /* If there is no result, or only one piece, return that */
2123 if (clipped
== NULL
|| clipped
->f
== clipped
)
2126 /* WARNING: This next check is O(n^2), where n is the number of clipped
2127 * pieces, hopefully the outline layer isn't too complex!
2131 do { /* LOOP OVER POLYGON PIECES */
2133 if (piece
->contours
== NULL
)
2138 do { /* LOOP OVER POLYGON PIECES */
2141 if (poly_ContourInContour (check
->contours
, piece
->contours
))
2143 } while ((check
= check
->f
) != clipped
);
2145 /* If the piece is inside an odd number of others, keep it */
2146 if ((count
& 1) == 1)
2147 any_pieces_kept
= true;
2149 pieces_to_delete
= g_list_prepend (pieces_to_delete
, piece
);
2151 } while ((piece
= piece
->f
) != clipped
);
2153 /* If we did not find an enclosed area (the board) trimmed from the world polygon,
2154 return the entire subtracted result. This keeps the behaviour similar to what
2155 you see when individual lines on the outline layer don't close to form an
2156 enclosed region. (This fixes being able to cope with such a case where the
2157 outline layer geometry lies outside the world rect polygon above, and divides
2158 that world rect into multiple pieces.
2160 if (any_pieces_kept
)
2161 g_list_foreach (pieces_to_delete
, delete_piece_cb
, &clipped
);
2163 g_list_free (pieces_to_delete
);