4 * \brief Special polygon editing routines.
6 * Here's a brief tour of the data and life of a polygon, courtesy of
9 * A PCB PolygonType contains an array of points outlining the polygon.
10 * This is what is manipulated by the UI and stored in the saved PCB.
12 * A PolygonType also contains a POLYAREA called 'Clipped' which is
13 * computed dynamically by InitClip every time a board is loaded.
14 * The point array is coverted to a POLYAREA by original_poly and then
15 * holes are cut in it by clearPoly.
16 * After that it is maintained dynamically as parts are added, moved or
17 * removed (this is why sometimes bugs can be fixed by just re-loading
20 * A POLYAREA consists of a linked list of PLINE structures.
21 * The head of that list is POLYAREA.contours.
22 * The first contour is an outline of a filled region.
23 * All of the subsequent PLINEs are holes cut out of that first contour.
24 * POLYAREAs are in a doubly-linked list and each member of the list is
25 * an independent (non-overlapping) area with its own outline and holes.
26 * The function biggest() finds the largest POLYAREA so that
27 * PolygonType.Clipped points to that shape.
28 * The rest of the polygon still exists, it's just ignored when turning
29 * the polygon into copper.
31 * The first POLYAREA in PolygonType.Clipped is what is used for the
32 * vast majority of Polygon related tests.
33 * The basic logic for an intersection is "is the target shape inside
34 * POLYAREA.contours and NOT fully enclosed in any of
35 * POLYAREA.contours.next... (the holes)".
37 * The polygon dicer (NoHolesPolygonDicer and r_NoHolesPolygonDicer)
38 * emits a series of "simple" PLINE shapes.
39 * That is, the PLINE isn't linked to any other "holes" oulines.
40 * That's the meaning of the first test in r_NoHolesPolygonDicer.
41 * It is testing to see if the PLINE contour (the first, making it a
42 * solid outline) has a valid next pointer (which would point to one or
44 * The dicer works by recursively chopping the polygon in half through
45 * the first hole it sees (which is guaranteed to eliminate at least
47 * The dicer output is used for HIDs which cannot render things with
48 * holes (which would require erasure).
52 * <h1><b>Copyright.</b></h1>\n
54 * PCB, interactive printed circuit board design
56 * Copyright (C) 1994,1995,1996,2010 Thomas Nau
58 * This program is free software; you can redistribute it and/or modify
59 * it under the terms of the GNU General Public License as published by
60 * the Free Software Foundation; either version 2 of the License, or
61 * (at your option) any later version.
63 * This program is distributed in the hope that it will be useful,
64 * but WITHOUT ANY WARRANTY; without even the implied warranty of
65 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
66 * GNU General Public License for more details.
68 * You should have received a copy of the GNU General Public License
69 * along with this program; if not, write to the Free Software
70 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
72 * Contact addresses for paper mail and Email:
74 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
76 * Thomas.Nau@rz.uni-ulm.de
92 #include "crosshair.h"
99 #include "pcb-printf.h"
108 #ifdef HAVE_LIBDMALLOC
113 /* For getrlimit, setrlimit */
114 # include <sys/time.h>
115 # include <sys/resource.h>
118 #undef DEBUG_CIRCSEGS
120 #define ROUND(x) ((long)(((x) >= 0 ? (x) + 0.5 : (x) - 0.5)))
122 #define UNSUBTRACT_BLOAT 10
123 #define SUBTRACT_PIN_VIA_BATCH_SIZE 100
124 #define SUBTRACT_LINE_BATCH_SIZE 20
126 static double rotate_circle_seg
[4];
127 static double bw_rotate_circle_seg
[4];
128 static double circle_points
[POLY_CIRC_SEGS
][2];
138 double cos_ang
= cos (2.0 * M_PI
/ POLY_CIRC_SEGS_D
);
139 double sin_ang
= sin (2.0 * M_PI
/ POLY_CIRC_SEGS_D
);
141 rotate_circle_seg
[0] = cos_ang
; rotate_circle_seg
[1] = -sin_ang
;
142 rotate_circle_seg
[2] = sin_ang
; rotate_circle_seg
[3] = cos_ang
;
144 bw_rotate_circle_seg
[0] = cos_ang
; bw_rotate_circle_seg
[1] = sin_ang
;
145 bw_rotate_circle_seg
[2] = -sin_ang
; bw_rotate_circle_seg
[3] = cos_ang
;
147 for (i
= 0; i
< POLY_CIRC_SEGS
; i
++)
149 circle_points
[i
][0] = cos ((double)i
* 2.0 * M_PI
/ POLY_CIRC_SEGS_D
);
150 circle_points
[i
][1] = sin ((double)i
* 2.0 * M_PI
/ POLY_CIRC_SEGS_D
); /* NB: PCB has a funny idea of coordinates! */
156 /* DEBUG - AVOID PCB running the system out of memory! */
157 getrlimit (RLIMIT_AS
, &limit
);
158 limit
.rlim_cur
= MIN (limit
.rlim_cur
, 2000 * 1024 * 1024 /* 2 GiB limit to virtual memory size */);
159 setrlimit (RLIMIT_AS
, &limit
);
164 polygon_point_idx (PolygonType
*polygon
, PointType
*point
)
166 assert (point
>= polygon
->Points
);
167 assert (point
<= polygon
->Points
+ polygon
->PointN
);
168 return ((char *)point
- (char *)polygon
->Points
) / sizeof (PointType
);
172 * \brief Find contour number.
176 * 1 for first hole etc..
179 polygon_point_contour (PolygonType
*polygon
, Cardinal point
)
182 Cardinal contour
= 0;
184 for (i
= 0; i
< polygon
->HoleIndexN
; i
++)
185 if (point
>= polygon
->HoleIndex
[i
])
191 next_contour_point (PolygonType
*polygon
, Cardinal point
)
194 Cardinal this_contour_start
;
195 Cardinal next_contour_start
;
197 contour
= polygon_point_contour (polygon
, point
);
199 this_contour_start
= (contour
== 0) ? 0 :
200 polygon
->HoleIndex
[contour
- 1];
202 (contour
== polygon
->HoleIndexN
) ? polygon
->PointN
:
203 polygon
->HoleIndex
[contour
];
205 /* Wrap back to the start of the contour we're in if we pass the end */
206 if (++point
== next_contour_start
)
207 point
= this_contour_start
;
213 prev_contour_point (PolygonType
*polygon
, Cardinal point
)
216 Cardinal prev_contour_end
;
217 Cardinal this_contour_end
;
219 contour
= polygon_point_contour (polygon
, point
);
221 prev_contour_end
= (contour
== 0) ? 0 :
222 polygon
->HoleIndex
[contour
- 1];
224 (contour
== polygon
->HoleIndexN
) ? polygon
->PointN
- 1:
225 polygon
->HoleIndex
[contour
] - 1;
227 /* Wrap back to the start of the contour we're in if we pass the end */
228 if (point
== prev_contour_end
)
229 point
= this_contour_end
;
237 add_noholes_polyarea (PLINE
*pline
, void *user_data
)
239 PolygonType
*poly
= (PolygonType
*)user_data
;
241 /* Prepend the pline into the NoHoles linked list */
242 pline
->next
= poly
->NoHoles
;
243 poly
->NoHoles
= pline
;
247 ComputeNoHoles (PolygonType
*poly
)
249 poly_FreeContours (&poly
->NoHoles
);
251 NoHolesPolygonDicer (poly
, NULL
, add_noholes_polyarea
, poly
);
253 printf ("Compute_noholes caught poly->Clipped = NULL\n");
254 poly
->NoHolesValid
= 1;
258 biggest (POLYAREA
* p
)
260 POLYAREA
*n
, *top
= NULL
;
270 if (n
->contours
->area
< PCB
->IsleArea
)
274 poly_DelContour (&n
->contours
);
285 if (n
->contours
->area
> big
)
288 big
= n
->contours
->area
;
291 while ((n
= n
->f
) != p
);
296 tree
= top
->contour_tree
;
297 top
->contours
= p
->contours
;
298 top
->contour_tree
= p
->contour_tree
;
300 p
->contour_tree
= tree
;
308 ContourToPoly (PLINE
* contour
)
311 poly_PreContour (contour
, TRUE
);
312 assert (contour
->Flags
.orient
== PLF_DIR
);
313 if (!(p
= poly_Create ()))
315 if (!poly_InclContour (p
, contour
))
317 printf ("ContourToPoly() WARNING: poly_InclContour returned false\n");
320 assert (poly_Valid (p
));
325 degree_circle (PLINE
* c
, Coord X
, Coord Y
/* <- Center */, Coord radius
, Vector v
/* First point */, Angle sweep
)
327 /* We don't re-add a point at v, nor do we add the last point, sweep degrees around from (X,Y)-v */
336 // poly_InclVertex (c->head.prev, poly_CreateNode (v));
338 if (c
->head
.prev
->point
[0] == v
[0] &&
339 c
->head
.prev
->point
[1] == v
[1])
341 /* Re-use any existing vertex point we got lumbered with (if it matches the coordinate we want) */
342 c
->head
.prev
->is_round
= true;
343 c
->head
.prev
->cx
= X
;
344 c
->head
.prev
->cy
= Y
;
345 c
->head
.prev
->radius
= radius
;
348 poly_InclVertex (c
->head
.prev
, poly_CreateNodeArcApproximation (v
, X
, Y
, radius
));
350 /* move vector to origin */
351 e1
= (v
[0] - X
) * POLY_CIRC_RADIUS_ADJ
;
352 e2
= (v
[1] - Y
) * POLY_CIRC_RADIUS_ADJ
;
357 /* NB: the caller added the first vertex, and will add the last vertex, hence the -1 */
358 range
= POLY_CIRC_SEGS
* sweep
/ 360 - 1;
359 for (i
= 0; i
< range
; i
++)
361 /* rotate the vector */
362 t1
= rotate_circle_seg
[0] * e1
+ rotate_circle_seg
[1] * e2
;
363 e2
= rotate_circle_seg
[2] * e1
+ rotate_circle_seg
[3] * e2
;
365 v
[0] = X
+ ROUND (e1
);
366 v
[1] = Y
+ ROUND (e2
);
367 poly_InclVertex (c
->head
.prev
, poly_CreateNodeArcApproximation (v
, X
, Y
, radius
));
372 /* NB: the caller added the first vertex, and will add the last vertex, hence the -1 */
373 range
= POLY_CIRC_SEGS
* -sweep
/ 360 - 1;
374 for (i
= 0; i
< range
; i
++)
376 /* rotate the vector */
377 t1
= bw_rotate_circle_seg
[0] * e1
+ bw_rotate_circle_seg
[1] * e2
;
378 e2
= bw_rotate_circle_seg
[2] * e1
+ bw_rotate_circle_seg
[3] * e2
;
380 v
[0] = X
+ ROUND (e1
);
381 v
[1] = Y
+ ROUND (e2
);
382 poly_InclVertex (c
->head
.prev
, poly_CreateNodeArcApproximation (v
, X
, Y
, radius
));
389 // Compute angle (segment number) we started at, find next pre-computed vector
390 // Increment vector index if too close to starting point
391 start_angle
= atan2 (v
[1] - Y
, v
[0] - X
);
392 if (start_angle
< 0.0)
393 start_angle
+= 2.0 * M_PI
;
394 start_index
= (int)trunc (start_angle
/ (2.0 * M_PI
) * POLY_CIRC_SEGS_D
+ 1.50); /* 1.50 gives a half segment offset on the next index */
396 // Compute angle (segment number) the caller will end at, find previous pre-computed vector index
397 // Decrement vector index if too close to ending point (NB: end_index >= start_index, and is not wrapped).
398 // end_angle = start_angle + 2.0 * M_PI / fraction;
399 end_index
= (int)trunc ((start_angle
/ (2.0 * M_PI
) + sweep
/ 360.0) * POLY_CIRC_SEGS_D
+ 0.5); /* 0.50 gives a half segment offset */
401 for (i
= start_index
; i
< end_index
; i
++)
403 v
[0] = X
+ ROUND (radius
* circle_points
[i
% POLY_CIRC_SEGS
][0]);
404 v
[1] = Y
+ ROUND (radius
* circle_points
[i
% POLY_CIRC_SEGS
][1]);
405 poly_InclVertex (c
->head
.prev
, poly_CreateNodeArcApproximation (v
, X
, Y
, radius
));
411 // Compute angle (segment number) we started at, find next pre-computed vector
412 // Increment vector index if too close to starting point
413 end_angle
= atan2 (v
[1] - Y
, v
[0] - X
) + sweep
/ 180.0 * M_PI
;
414 while (end_angle
< 0.0)
415 end_angle
+= 2.0 * M_PI
;
416 end_index
= (int)trunc (end_angle
/ (2.0 * M_PI
) * POLY_CIRC_SEGS_D
+ 0.50); /* 0.50 gives a half segment offset on the next index */
418 // Compute angle (segment number) the caller will end at, find previous pre-computed vector index
419 // Decrement vector index if too close to ending point (NB: end_index >= start_index, and is not wrapped).
420 // end_angle = start_angle + 2.0 * M_PI / fraction;
421 start_index
= (int)trunc ((end_angle
/ (2.0 * M_PI
) - sweep
/ 360.0) * POLY_CIRC_SEGS_D
- 0.5); /* 0.50 gives a half segment offset */
423 for (i
= start_index
; i
> end_index
; i
--)
425 v
[0] = X
+ ROUND (radius
* circle_points
[i
% POLY_CIRC_SEGS
][0]);
426 v
[1] = Y
+ ROUND (radius
* circle_points
[i
% POLY_CIRC_SEGS
][1]);
427 poly_InclVertex (c
->head
.prev
, poly_CreateNodeArcApproximation (v
, X
, Y
, radius
));
430 /* NB: the caller added the first vertex, and will add the last vertex, hence the -1 */
431 range
= POLY_CIRC_SEGS
* -sweep
/ 360 - 1;
432 for (i
= 0; i
< range
; i
++)
434 /* rotate the vector */
435 t1
= bw_rotate_circle_seg
[0] * e1
+ bw_rotate_circle_seg
[1] * e2
;
436 e2
= bw_rotate_circle_seg
[2] * e1
+ bw_rotate_circle_seg
[3] * e2
;
438 v
[0] = X
+ ROUND (e1
);
439 v
[1] = Y
+ ROUND (e2
);
440 poly_InclVertex (c
->head
.prev
, poly_CreateNodeArcApproximation (v
, X
, Y
, radius
));
447 original_poly (PolygonType
* p
)
449 PLINE
*contour
= NULL
;
453 unsigned int hole
= 0;
455 if ((np
= poly_Create ()) == NULL
)
458 /* first make initial polygon contour */
459 for (n
= 0; n
< p
->PointN
; n
++)
464 /* No current contour? Make a new one starting at point */
465 /* (or) Add point to existing contour */
467 v
[0] = p
->Points
[n
].X
, v
[1] = p
->Points
[n
].Y
;
469 //prev_n = prev_contour_point (p, n);
471 /* XXX: Need to handle the case of a leftover circular contour point */
473 node
= poly_CreateNodeArcApproximation (v
, 0, 0, 0);
475 node
= poly_CreateNode (v
);
479 if ((contour
= poly_NewContour (node
)) == NULL
)
484 poly_InclVertex (contour
->head
.prev
, node
);
487 if (p
->Points
[n
].included_angle
!= 0)
494 if (next_n
== p
->PointN
||
495 (hole
< p
->HoleIndexN
&& next_n
== p
->HoleIndex
[hole
]))
496 next_n
= (hole
== 0) ? 0 : p
->HoleIndex
[hole
- 1];
498 /* XXX: Compute center of arc */
501 calc_arc_from_points_and_included_angle (&p
->Points
[n
], &p
->Points
[next_n
], p
->Points
[n
].included_angle
,
502 &cx
, &cy
, &radius
, NULL
, NULL
);
504 #if 0 /* DEBUG TO SHOW THE CENTER OF THE ARC */
505 v
[0] = cx
, v
[1] = cy
;
506 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
507 v
[0] = p
->Points
[n
].X
, v
[1] = p
->Points
[n
].Y
;
510 degree_circle (contour
, cx
, cy
, radius
, v
, p
->Points
[n
].included_angle
);
512 #if 0 /* DEBUG TO SHOW THE CENTER OF THE ARC */
513 v
[0] = cx
, v
[1] = cy
; /* DEBUG TO SHOW THE CENTER OF THE ARC */
514 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
518 /* Is current point last in contour? If so process it. */
519 if (n
== p
->PointN
- 1 ||
520 (hole
< p
->HoleIndexN
&& n
== p
->HoleIndex
[hole
] - 1))
522 poly_PreContour (contour
, TRUE
);
524 /* make sure it is a positive contour (outer) or negative (hole) */
525 if (contour
->Flags
.orient
!= (hole
? PLF_INV
: PLF_DIR
))
526 poly_InvContour (contour
);
527 assert (contour
->Flags
.orient
== (hole
? PLF_INV
: PLF_DIR
));
529 poly_InclContour (np
, contour
);
531 assert (poly_Valid (np
));
540 PolygonToPoly (PolygonType
*p
)
542 return original_poly (p
);
546 RectPoly (Coord x1
, Coord x2
, Coord y1
, Coord y2
)
548 PLINE
*contour
= NULL
;
551 /* Return NULL for zero or negatively sized rectangles */
552 if (x2
<= x1
|| y2
<= y1
)
557 if ((contour
= poly_NewContour (poly_CreateNode (v
))) == NULL
)
561 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
564 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
567 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
568 return ContourToPoly (contour
);
572 OctagonPoly (Coord x
, Coord y
, Coord radius
)
574 PLINE
*contour
= NULL
;
577 v
[0] = x
+ ROUND (radius
* 0.5);
578 v
[1] = y
+ ROUND (radius
* TAN_22_5_DEGREE_2
);
579 if ((contour
= poly_NewContour (poly_CreateNode (v
))) == NULL
)
581 v
[0] = x
+ ROUND (radius
* TAN_22_5_DEGREE_2
);
582 v
[1] = y
+ ROUND (radius
* 0.5);
583 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
584 v
[0] = x
- (v
[0] - x
);
585 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
586 v
[0] = x
- ROUND (radius
* 0.5);
587 v
[1] = y
+ ROUND (radius
* TAN_22_5_DEGREE_2
);
588 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
589 v
[1] = y
- (v
[1] - y
);
590 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
591 v
[0] = x
- ROUND (radius
* TAN_22_5_DEGREE_2
);
592 v
[1] = y
- ROUND (radius
* 0.5);
593 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
594 v
[0] = x
- (v
[0] - x
);
595 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
596 v
[0] = x
+ ROUND (radius
* 0.5);
597 v
[1] = y
- ROUND (radius
* TAN_22_5_DEGREE_2
);
598 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
599 return ContourToPoly (contour
);
603 * \brief Add vertices in a fractional-circle starting from v
604 * centered at X, Y and going counter-clockwise.
606 * Does not include the first point.
607 * last argument is 1 for a full circle.
608 * 2 for a half circle.
609 * or 4 for a quarter circle.
612 frac_circle2 (PLINE
* c
, Coord X
, Coord Y
, Vector v
, int fraction
)
614 // double e1, e2, t1;
617 double radius
= hypot (v
[0] - X
, v
[1] - Y
);
622 /* XXX: Circle already has the first node added */
624 // poly_InclVertex (c->head.prev, poly_CreateNode (v));
626 if (c
->head
.prev
->point
[0] == v
[0] &&
627 c
->head
.prev
->point
[1] == v
[1])
629 /* Re-use any existing vertex point we got lumbered with (if it matches the coordinate we want) */
630 c
->head
.prev
->is_round
= true;
631 c
->head
.prev
->cx
= X
;
632 c
->head
.prev
->cy
= Y
;
633 c
->head
.prev
->radius
= radius
;
636 poly_InclVertex (c
->head
.prev
, poly_CreateNodeArcApproximation (v
, X
, Y
, radius
));
639 /* move vector to origin */
640 e1
= (v
[0] - X
) * POLY_CIRC_RADIUS_ADJ
;
641 e2
= (v
[1] - Y
) * POLY_CIRC_RADIUS_ADJ
;
643 /* XXX */ /* NB: the caller adds the last vertex, hence the -1 */
644 range
= POLY_CIRC_SEGS
/ fraction
;
645 for (i
= 0; i
< range
- 1; i
++)
647 /* rotate the vector */
648 t1
= rotate_circle_seg
[0] * e1
+ rotate_circle_seg
[1] * e2
;
649 e2
= rotate_circle_seg
[2] * e1
+ rotate_circle_seg
[3] * e2
;
651 v
[0] = X
+ ROUND (e1
);
652 v
[1] = Y
+ ROUND (e2
);
653 poly_InclVertex (c
->head
.prev
, poly_CreateNodeArcApproximation (v
, X
, Y
, radius
));
657 // Compute angle (segment number) we started at, find next pre-computed vector
658 // Increment vector index if too close to starting point
659 start_angle
= atan2 (v
[1] - Y
, v
[0] - X
);
660 if (start_angle
< 0.0)
661 start_angle
+= 2.0 * M_PI
;
662 start_index
= (int)trunc (start_angle
/ (2.0 * M_PI
) * POLY_CIRC_SEGS_D
+ 1.50); /* 1.50 gives a half segment offset on the next index */
664 // Compute angle (segment number) the caller will end at, find previous pre-computed vector index
665 // Decrement vector index if too close to ending point (NB: end_index >= start_index, and is not wrapped).
666 // end_angle = start_angle + 2.0 * M_PI / fraction;
667 end_index
= (int)trunc ((start_angle
/ (2.0 * M_PI
) + 1.0 / (double)fraction
) * POLY_CIRC_SEGS_D
+ 0.5); /* 0.50 gives a half segment offset */
669 for (i
= start_index
; i
< end_index
; i
++)
671 v
[0] = X
+ ROUND (radius
* circle_points
[i
% POLY_CIRC_SEGS
][0]);
672 v
[1] = Y
+ ROUND (radius
* circle_points
[i
% POLY_CIRC_SEGS
][1]);
673 poly_InclVertex (c
->head
.prev
, poly_CreateNodeArcApproximation (v
, X
, Y
, radius
));
678 * \brief Create a circle approximation from lines.
680 * NB: Name can be NULL, but once passed is owned by the contour.
681 * It will be free'd with free() when no longer required, so
682 * must be allocated by the standard library routines such as
683 * malloc, calloc, realloc.
686 CirclePoly (Coord x
, Coord y
, Coord radius
, char *name
)
695 if ((contour
= poly_NewContour (poly_CreateNodeArcApproximation (v
, x
, y
, radius
))) == NULL
)
697 frac_circle2 (contour
, x
, y
, v
, 1);
698 contour
->is_round
= TRUE
;
701 contour
->radius
= radius
;
702 contour
->name
= name
;
703 return ContourToPoly (contour
);
707 * \brief Make a rounded-corner rectangle with radius t beyond
708 * x1,x2,y1,y2 rectangle.
711 RoundRect (Coord x1
, Coord x2
, Coord y1
, Coord y2
, Coord t
)
713 PLINE
*contour
= NULL
;
721 if ((contour
= poly_NewContour (poly_CreateNode (v
))) == NULL
)
725 frac_circle2 (contour
, x1
, y1
, v
, 4);
728 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
731 frac_circle2 (contour
, x2
, y1
, v
, 4);
734 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
737 frac_circle2 (contour
, x2
, y2
, v
, 4);
740 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
743 frac_circle2 (contour
, x1
, y2
, v
, 4);
744 return ContourToPoly (contour
);
749 ArcPolyNoIntersect (ArcType
* a
, Coord thick
, char *name
)
751 PLINE
*contour
= NULL
;
756 double ang
, da
, rx
, ry
;
764 a
->StartAngle
+= a
->Delta
;
765 a
->Delta
= -a
->Delta
;
767 half
= (thick
+ 1) / 2;
768 ends
= GetArcEnds (a
);
769 /* start with inner radius */
770 rx
= MAX (a
->Width
- half
, 0);
771 ry
= MAX (a
->Height
- half
, 0);
774 segs
= MAX (segs
, a
->Delta
* M_PI
/ 360 *
775 sqrt (hypot (rx
, ry
) /
776 POLY_ARC_MAX_DEVIATION
/ 2 / thick
));
777 segs
= MAX(segs
, a
->Delta
/ ARC_ANGLE
);
780 da
= (1.0 * a
->Delta
) / segs
;
782 /* XXX: No need for radius ofsetting bodgery for the exact arc representation */
786 radius_adj
= (M_PI
*da
/360)*(M_PI
*da
/360)/2;
788 v
[0] = a
->X
- rx
* cos (ang
* M180
);
789 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
791 /* XXX: First point is a vertex? */
792 if ((contour
= poly_NewContour (poly_CreateNode (v
))) == NULL
)
795 contour
->name
= name
;
797 degree_circle (contour
, a
->X
, a
->Y
, rx
, v
, -a
->Delta
);
799 for (i
= 0; i
< segs
- 1; i
++)
802 v
[0] = a
->X
- rx
* cos (ang
* M180
);
803 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
804 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
806 /* find last point */
807 ang
= a
->StartAngle
+ a
->Delta
;
808 v
[0] = a
->X
- rx
* cos (ang
* M180
) * (1 - radius_adj
);
809 v
[1] = a
->Y
+ ry
* sin (ang
* M180
) * (1 - radius_adj
);
810 /* add the round cap at the end */
811 frac_circle2 (contour
, ends
->X2
, ends
->Y2
, v
, 2);
812 /* and now do the outer arc (going backwards) */
813 rx
= (a
->Width
+ half
) * (1+radius_adj
);
814 ry
= (a
->Width
+ half
) * (1+radius_adj
);
816 v
[0] = a
->X
- rx
* cos (ang
* M180
);
817 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
819 degree_circle (contour
, a
->X
, a
->Y
, rx
, v
, a
->Delta
);
821 for (i
= 0; i
< segs
; i
++)
823 v
[0] = a
->X
- rx
* cos (ang
* M180
);
824 v
[1] = a
->Y
+ ry
* sin (ang
* M180
);
825 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
828 /* now add other round cap */
830 v
[0] = a
->X
- rx
* cos (ang
* M180
) * (1 - radius_adj
);
831 v
[1] = a
->Y
+ ry
* sin (ang
* M180
) * (1 - radius_adj
);
832 frac_circle2 (contour
, ends
->X1
, ends
->Y1
, v
, 2);
833 /* now we have the whole contour */
834 if (!(np
= ContourToPoly (contour
)))
839 #define MIN_CLEARANCE_BEFORE_BISECT 10.
841 ArcPoly (ArcType
* a
, Coord thick
, char *name
)
845 POLYAREA
*tmp1
, *tmp2
, *res
;
847 delta
= (a
->Delta
< 0) ? -a
->Delta
: a
->Delta
;
849 /* If the arc segment would self-intersect, we need to construct it as the union of
850 two non-intersecting segments */
852 if (2 * M_PI
* a
->Width
* (1. - (double)delta
/ 360.) - thick
< MIN_CLEARANCE_BEFORE_BISECT
)
854 int half_delta
= a
->Delta
/ 2;
857 seg1
.Delta
= half_delta
;
858 seg2
.Delta
-= half_delta
;
859 seg2
.StartAngle
+= half_delta
;
861 tmp1
= ArcPolyNoIntersect (&seg1
, thick
, name
);
862 tmp2
= ArcPolyNoIntersect (&seg2
, thick
, name
);
863 poly_Boolean_free (tmp1
, tmp2
, &res
, PBO_UNITE
);
867 return ArcPolyNoIntersect (a
, thick
, name
);
871 LinePoly (LineType
* L
, Coord thick
, char *name
)
873 PLINE
*contour
= NULL
;
877 long half
;LineType _l
=*L
,*l
=&_l
;
881 half
= (thick
+ 1) / 2;
882 d
= hypot (l
->Point1
.X
- l
->Point2
.X
, l
->Point1
.Y
- l
->Point2
.Y
);
883 if (!TEST_FLAG (SQUAREFLAG
,l
))
884 if (d
== 0) /* line is a point */
885 return CirclePoly (l
->Point1
.X
, l
->Point1
.Y
, half
, name
);
889 dx
= (l
->Point1
.Y
- l
->Point2
.Y
) * d
;
890 dy
= (l
->Point2
.X
- l
->Point1
.X
) * d
;
897 if (TEST_FLAG (SQUAREFLAG
,l
))/* take into account the ends */
905 v
[0] = l
->Point1
.X
- dx
;
906 v
[1] = l
->Point1
.Y
- dy
;
907 if ((contour
= poly_NewContour (poly_CreateNode (v
))) == NULL
)
909 contour
->name
= name
;
911 v
[0] = l
->Point2
.X
- dx
;
912 v
[1] = l
->Point2
.Y
- dy
;
914 if (TEST_FLAG (SQUAREFLAG
,l
))
915 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
917 frac_circle2 (contour
, l
->Point2
.X
, l
->Point2
.Y
, v
, 2);
919 v
[0] = l
->Point2
.X
+ dx
;
920 v
[1] = l
->Point2
.Y
+ dy
;
921 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
923 v
[0] = l
->Point1
.X
+ dx
;
924 v
[1] = l
->Point1
.Y
+ dy
;
926 if (TEST_FLAG (SQUAREFLAG
,l
))
927 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
929 frac_circle2 (contour
, l
->Point1
.X
, l
->Point1
.Y
, v
, 2);
931 // v[0] = l->Point1.X - dx;
932 // v[1] = l->Point1.Y - dy;
934 /* now we have the line contour */
935 if (!(np
= ContourToPoly (contour
)))
942 * \brief Make a rectangle pad (clear is applied square, not rounded
945 SquarePadPoly (PadType
* pad
, Coord clear
)
947 PLINE
*contour
= NULL
;
952 PadType _c
=*pad
,*c
=&_c
;
953 int halfclear
= (clear
+ 1) / 2;
958 d
= hypot (pad
->Point1
.X
- pad
->Point2
.X
,
959 pad
->Point1
.Y
- pad
->Point2
.Y
);
962 double a
= halfclear
/ d
;
963 cx
= (c
->Point1
.Y
- c
->Point2
.Y
) * a
;
964 cy
= (c
->Point2
.X
- c
->Point1
.X
) * a
;
980 v
[0] = c
->Point1
.X
+ cx
;
981 v
[1] = c
->Point1
.Y
+ cy
;
982 if ((contour
= poly_NewContour (poly_CreateNode (v
))) == NULL
)
985 v
[0] = c
->Point1
.X
- cx
;
986 v
[1] = c
->Point1
.Y
- cy
;
987 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
989 v
[0] = c
->Point2
.X
- cx
;
990 v
[1] = c
->Point2
.Y
- cy
;
991 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
993 v
[0] = c
->Point2
.X
+ cx
;
994 v
[1] = c
->Point2
.Y
+ cy
;
995 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
997 /* now we have the line contour */
998 if (!(np
= ContourToPoly (contour
)))
1004 * \brief Make a rounded-corner rectangle.
1007 SquarePadClearPoly (PadType
* pad
, Coord clear
)
1009 PLINE
*contour
= NULL
;
1010 POLYAREA
*np
= NULL
;
1015 PadType _t
=*pad
,*t
=&_t
;
1016 PadType _c
=*pad
,*c
=&_c
;
1017 int halfthick
= (pad
->Thickness
+ 1) / 2;
1018 int halfclear
= (clear
+ 1) / 2;
1020 d
= hypot (pad
->Point1
.X
- pad
->Point2
.X
, pad
->Point1
.Y
- pad
->Point2
.Y
);
1023 double a
= halfthick
/ d
;
1024 tx
= (t
->Point1
.Y
- t
->Point2
.Y
) * a
;
1025 ty
= (t
->Point2
.X
- t
->Point1
.X
) * a
;
1027 cx
= (c
->Point1
.Y
- c
->Point2
.Y
) * a
;
1028 cy
= (c
->Point2
.X
- c
->Point1
.X
) * a
;
1052 v
[0] = c
->Point1
.X
+ tx
;
1053 v
[1] = c
->Point1
.Y
+ ty
;
1054 if ((contour
= poly_NewContour (poly_CreateNode (v
))) == NULL
)
1057 v
[0] = c
->Point1
.X
- tx
;
1058 v
[1] = c
->Point1
.Y
- ty
;
1059 frac_circle2 (contour
, (t
->Point1
.X
- tx
), (t
->Point1
.Y
- ty
), v
, 4);
1061 v
[0] = t
->Point1
.X
- cx
;
1062 v
[1] = t
->Point1
.Y
- cy
;
1063 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
1065 v
[0] = t
->Point2
.X
- cx
;
1066 v
[1] = t
->Point2
.Y
- cy
;
1067 frac_circle2 (contour
, (t
->Point2
.X
- tx
), (t
->Point2
.Y
- ty
), v
, 4);
1069 v
[0] = c
->Point2
.X
- tx
;
1070 v
[1] = c
->Point2
.Y
- ty
;
1071 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
1073 v
[0] = c
->Point2
.X
+ tx
;
1074 v
[1] = c
->Point2
.Y
+ ty
;
1075 frac_circle2 (contour
, (t
->Point2
.X
+ tx
), (t
->Point2
.Y
+ ty
), v
, 4);
1077 v
[0] = t
->Point2
.X
+ cx
;
1078 v
[1] = t
->Point2
.Y
+ cy
;
1079 poly_InclVertex (contour
->head
.prev
, poly_CreateNode (v
));
1081 v
[0] = t
->Point1
.X
+ cx
;
1082 v
[1] = t
->Point1
.Y
+ cy
;
1083 frac_circle2 (contour
, (t
->Point1
.X
+ tx
), (t
->Point1
.Y
+ ty
), v
, 4);
1085 /* now we have the line contour */
1086 if (!(np
= ContourToPoly (contour
)))
1091 /* HACK */ extern void ghid_notify_polygon_changed (PolygonType
*);
1094 * \brief Clear np1 from the polygon.
1097 Subtract (POLYAREA
* np1
, PolygonType
* p
, bool fnp
)
1099 POLYAREA
*merged
= NULL
, *np
= np1
;
1109 assert (poly_Valid (p
->Clipped
));
1110 assert (poly_Valid (np
));
1112 x
= poly_Boolean_free (p
->Clipped
, np
, &merged
, PBO_SUB
);
1115 x
= poly_Boolean (p
->Clipped
, np
, &merged
, PBO_SUB
);
1116 poly_Free (&p
->Clipped
);
1118 assert (!merged
|| poly_Valid (merged
));
1121 fprintf (stderr
, "Error while clipping PBO_SUB: %d\n", x
);
1122 poly_Free (&merged
);
1124 /* HACK */ ghid_notify_polygon_changed (p
);
1125 if (p
->NoHoles
) printf ("Just leaked in Subtract\n");
1129 p
->Clipped
= biggest (merged
);
1130 /* HACK */ ghid_notify_polygon_changed (p
);
1131 assert (!p
->Clipped
|| poly_Valid (p
->Clipped
));
1133 Message ("Polygon cleared out of existence near (%d, %d)\n",
1134 (p
->BoundingBox
.X1
+ p
->BoundingBox
.X2
) / 2,
1135 (p
->BoundingBox
.Y1
+ p
->BoundingBox
.Y2
) / 2);
1140 * \brief Create a polygon of the pin clearance.
1143 PinPoly (PinType
* pin
, Coord thick
)
1145 int size
= (thick
+ 1) / 2;
1147 if (TEST_FLAG (SQUAREFLAG
, pin
))
1149 return RectPoly (pin
->X
- size
, pin
->X
+ size
,
1150 pin
->Y
- size
, pin
->Y
+ size
);
1152 else if (TEST_FLAG (OCTAGONFLAG
, pin
))
1154 return OctagonPoly (pin
->X
, pin
->Y
, thick
);
1158 return CirclePoly (pin
->X
, pin
->Y
, size
, NULL
);
1162 /* create a polygon of the pin clearance */
1164 PinClearPoly (PinType
* pin
, Coord thick
, Coord clear
)
1168 if (TEST_FLAG (SQUAREFLAG
, pin
))
1170 size
= (thick
+ 1) / 2;
1171 return RoundRect (pin
->X
- size
, pin
->X
+ size
, pin
->Y
- size
,
1172 pin
->Y
+ size
, (clear
+ 1) / 2);
1176 size
= (thick
+ clear
+ 1) / 2;
1177 if (TEST_FLAG (OCTAGONFLAG
, pin
))
1179 return OctagonPoly (pin
->X
, pin
->Y
, size
+ size
);
1182 return CirclePoly (pin
->X
, pin
->Y
, size
, NULL
);
1186 BoxPolyBloated (BoxType
*box
, Coord bloat
)
1188 return RectPoly (box
->X1
- bloat
, box
->X2
+ bloat
,
1189 box
->Y1
- bloat
, box
->Y2
+ bloat
);
1193 PadPoly (PadType
*pad
, Coord size
)
1195 if (TEST_FLAG (SQUAREFLAG
, pad
))
1196 return SquarePadPoly (pad
, size
);
1198 return LinePoly ((LineType
*) pad
, size
, NULL
);
1202 PadClearPoly (PadType
*pad
, Coord size
)
1204 if (TEST_FLAG (SQUAREFLAG
, pad
))
1205 return SquarePadClearPoly (pad
, size
);
1207 return LinePoly ((LineType
*) pad
, size
, NULL
);
1211 * \brief Remove the pin clearance from the polygon.
1214 SubtractPin (DataType
* d
, PinType
* pin
, LayerType
* l
, PolygonType
* p
)
1219 if (pin
->Clearance
== 0)
1221 i
= GetLayerNumber (d
, l
);
1222 if (TEST_THERM (i
, pin
))
1224 np
= ThermPoly ((PCBType
*) (d
->pcb
), pin
, i
);
1230 np
= PinClearPoly (pin
, PIN_SIZE (pin
), pin
->Clearance
);
1234 return Subtract (np
, p
, TRUE
);
1238 SubtractLine (LineType
* line
, PolygonType
* p
)
1242 if (!TEST_FLAG (CLEARLINEFLAG
, line
))
1244 if (!(np
= LinePoly (line
, line
->Thickness
+ line
->Clearance
, NULL
)))
1246 return Subtract (np
, p
, true);
1250 SubtractArc (ArcType
* arc
, PolygonType
* p
)
1254 if (!TEST_FLAG (CLEARLINEFLAG
, arc
))
1256 if (!(np
= ArcPoly (arc
, arc
->Thickness
+ arc
->Clearance
, NULL
)))
1258 return Subtract (np
, p
, true);
1262 SubtractText (TextType
* text
, PolygonType
* p
)
1265 const BoxType
*b
= &text
->BoundingBox
;
1267 if (!TEST_FLAG (CLEARLINEFLAG
, text
))
1269 if (!(np
= RoundRect (b
->X1
+ PCB
->Bloat
, b
->X2
- PCB
->Bloat
,
1270 b
->Y1
+ PCB
->Bloat
, b
->Y2
- PCB
->Bloat
, PCB
->Bloat
)))
1272 return Subtract (np
, p
, true);
1276 SubtractPad (PadType
* pad
, PolygonType
* p
)
1278 POLYAREA
*np
= NULL
;
1280 if (pad
->Clearance
== 0)
1282 if (!(np
= PadClearPoly (pad
, pad
->Thickness
+ pad
->Clearance
)))
1284 return Subtract (np
, p
, true);
1289 const BoxType
*other
;
1292 PolygonType
*polygon
;
1294 POLYAREA
*accumulate
;
1300 subtract_accumulated (struct cpInfo
*info
, PolygonType
*polygon
)
1302 if (info
->accumulate
== NULL
)
1304 Subtract (info
->accumulate
, polygon
, true);
1305 info
->accumulate
= NULL
;
1306 info
->batch_size
= 0;
1310 pin_sub_callback (const BoxType
* b
, void *cl
)
1312 PinType
*pin
= (PinType
*) b
;
1313 struct cpInfo
*info
= (struct cpInfo
*) cl
;
1314 PolygonType
*polygon
;
1319 /* don't subtract the object that was put back! */
1320 if (b
== info
->other
)
1322 polygon
= info
->polygon
;
1324 i
= GetLayerNumber (info
->data
, info
->layer
);
1326 if (VIA_IS_BURIED (pin
) && (!VIA_ON_LAYER (pin
, i
)))
1329 if (pin
->Clearance
== 0)
1331 if (TEST_THERM (i
, pin
))
1333 np
= ThermPoly ((PCBType
*) (info
->data
->pcb
), pin
, i
);
1339 np
= PinClearPoly (pin
, PIN_SIZE (pin
), pin
->Clearance
);
1341 longjmp (info
->env
, 1);
1344 poly_Boolean_free (info
->accumulate
, np
, &merged
, PBO_UNITE
);
1345 info
->accumulate
= merged
;
1347 info
->batch_size
++;
1349 if (info
->batch_size
== SUBTRACT_PIN_VIA_BATCH_SIZE
)
1350 subtract_accumulated (info
, polygon
);
1356 arc_sub_callback (const BoxType
* b
, void *cl
)
1358 ArcType
*arc
= (ArcType
*) b
;
1359 struct cpInfo
*info
= (struct cpInfo
*) cl
;
1360 PolygonType
*polygon
;
1362 /* don't subtract the object that was put back! */
1363 if (b
== info
->other
)
1365 if (!TEST_FLAG (CLEARLINEFLAG
, arc
))
1367 polygon
= info
->polygon
;
1368 if (SubtractArc (arc
, polygon
) < 0)
1369 longjmp (info
->env
, 1);
1374 pad_sub_callback (const BoxType
* b
, void *cl
)
1376 PadType
*pad
= (PadType
*) b
;
1377 struct cpInfo
*info
= (struct cpInfo
*) cl
;
1378 PolygonType
*polygon
;
1380 /* don't subtract the object that was put back! */
1381 if (b
== info
->other
)
1383 if (pad
->Clearance
== 0)
1385 polygon
= info
->polygon
;
1386 if (XOR (TEST_FLAG (ONSOLDERFLAG
, pad
), !info
->bottom
))
1388 if (SubtractPad (pad
, polygon
) < 0)
1389 longjmp (info
->env
, 1);
1396 line_sub_callback (const BoxType
* b
, void *cl
)
1398 LineType
*line
= (LineType
*) b
;
1399 struct cpInfo
*info
= (struct cpInfo
*) cl
;
1400 PolygonType
*polygon
;
1404 /* don't subtract the object that was put back! */
1405 if (b
== info
->other
)
1407 if (!TEST_FLAG (CLEARLINEFLAG
, line
))
1409 polygon
= info
->polygon
;
1411 if (!(np
= LinePoly (line
, line
->Thickness
+ line
->Clearance
, NULL
)))
1412 longjmp (info
->env
, 1);
1414 poly_Boolean_free (info
->accumulate
, np
, &merged
, PBO_UNITE
);
1415 info
->accumulate
= merged
;
1416 info
->batch_size
++;
1418 if (info
->batch_size
== SUBTRACT_LINE_BATCH_SIZE
)
1419 subtract_accumulated (info
, polygon
);
1425 text_sub_callback (const BoxType
* b
, void *cl
)
1427 TextType
*text
= (TextType
*) b
;
1428 struct cpInfo
*info
= (struct cpInfo
*) cl
;
1429 PolygonType
*polygon
;
1431 /* don't subtract the object that was put back! */
1432 if (b
== info
->other
)
1434 if (!TEST_FLAG (CLEARLINEFLAG
, text
))
1436 polygon
= info
->polygon
;
1437 if (SubtractText (text
, polygon
) < 0)
1438 longjmp (info
->env
, 1);
1443 Group (DataType
*Data
, Cardinal layer
)
1446 for (i
= 0; i
< max_group
; i
++)
1447 for (j
= 0; j
< ((PCBType
*) (Data
->pcb
))->LayerGroups
.Number
[i
]; j
++)
1448 if (layer
== ((PCBType
*) (Data
->pcb
))->LayerGroups
.Entries
[i
][j
])
1454 clearPoly (DataType
*Data
, LayerType
*Layer
, PolygonType
* polygon
,
1455 const BoxType
* here
, Coord expand
)
1462 if (!TEST_FLAG (CLEARPOLYFLAG
, polygon
)
1463 || GetLayerNumber (Data
, Layer
) >= max_copper_layer
)
1465 group
= Group (Data
, GetLayerNumber (Data
, Layer
));
1466 info
.bottom
= (group
== Group (Data
, bottom_silk_layer
));
1470 info
.polygon
= polygon
;
1472 region
= clip_box (here
, &polygon
->BoundingBox
);
1474 region
= polygon
->BoundingBox
;
1475 region
= bloat_box (®ion
, expand
);
1477 if (setjmp (info
.env
) == 0)
1480 info
.accumulate
= NULL
;
1481 info
.batch_size
= 0;
1482 if (info
.bottom
|| group
== Group (Data
, top_silk_layer
))
1483 r
+= r_search (Data
->pad_tree
, ®ion
, NULL
, pad_sub_callback
, &info
);
1484 GROUP_LOOP (Data
, group
);
1487 r_search (layer
->line_tree
, ®ion
, NULL
, line_sub_callback
,
1489 subtract_accumulated (&info
, polygon
);
1491 r_search (layer
->arc_tree
, ®ion
, NULL
, arc_sub_callback
, &info
);
1493 r_search (layer
->text_tree
, ®ion
, NULL
, text_sub_callback
, &info
);
1496 r
+= r_search (Data
->via_tree
, ®ion
, NULL
, pin_sub_callback
, &info
);
1497 r
+= r_search (Data
->pin_tree
, ®ion
, NULL
, pin_sub_callback
, &info
);
1498 subtract_accumulated (&info
, polygon
);
1500 polygon
->NoHolesValid
= 0;
1505 Unsubtract (POLYAREA
* np1
, PolygonType
* p
)
1507 POLYAREA
*merged
= NULL
, *np
= np1
;
1508 POLYAREA
*orig_poly
, *clipped_np
;
1511 assert (p
&& p
->Clipped
);
1513 orig_poly
= original_poly (p
);
1515 x
= poly_Boolean_free (np
, orig_poly
, &clipped_np
, PBO_ISECT
);
1518 fprintf (stderr
, "Error while clipping PBO_ISECT: %d\n", x
);
1519 poly_Free (&clipped_np
);
1523 x
= poly_Boolean_free (p
->Clipped
, clipped_np
, &merged
, PBO_UNITE
);
1526 fprintf (stderr
, "Error while clipping PBO_UNITE: %d\n", x
);
1527 poly_Free (&merged
);
1530 p
->Clipped
= biggest (merged
);
1531 /* HACK */ ghid_notify_polygon_changed (p
);
1532 assert (!p
->Clipped
|| poly_Valid (p
->Clipped
));
1537 /* HACK */ ghid_notify_polygon_changed (p
);
1538 if (p
->NoHoles
) printf ("Just leaked in Unsubtract\n");
1544 UnsubtractPin (PinType
* pin
, LayerType
* l
, PolygonType
* p
)
1548 /* overlap a bit to prevent gaps from rounding errors */
1549 np
= BoxPolyBloated (&pin
->BoundingBox
, UNSUBTRACT_BLOAT
);
1553 if (!Unsubtract (np
, p
))
1555 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) pin
, 2 * UNSUBTRACT_BLOAT
);
1560 UnsubtractArc (ArcType
* arc
, LayerType
* l
, PolygonType
* p
)
1564 if (!TEST_FLAG (CLEARLINEFLAG
, arc
))
1567 /* overlap a bit to prevent gaps from rounding errors */
1568 np
= BoxPolyBloated (&arc
->BoundingBox
, UNSUBTRACT_BLOAT
);
1572 if (!Unsubtract (np
, p
))
1574 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) arc
, 2 * UNSUBTRACT_BLOAT
);
1579 UnsubtractLine (LineType
* line
, LayerType
* l
, PolygonType
* p
)
1583 if (!TEST_FLAG (CLEARLINEFLAG
, line
))
1586 /* overlap a bit to prevent notches from rounding errors */
1587 np
= BoxPolyBloated (&line
->BoundingBox
, UNSUBTRACT_BLOAT
);
1591 if (!Unsubtract (np
, p
))
1593 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) line
, 2 * UNSUBTRACT_BLOAT
);
1598 UnsubtractText (TextType
* text
, LayerType
* l
, PolygonType
* p
)
1602 if (!TEST_FLAG (CLEARLINEFLAG
, text
))
1605 /* overlap a bit to prevent notches from rounding errors */
1606 np
= BoxPolyBloated (&text
->BoundingBox
, UNSUBTRACT_BLOAT
);
1610 if (!Unsubtract (np
, p
))
1612 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) text
, 2 * UNSUBTRACT_BLOAT
);
1617 UnsubtractPad (PadType
* pad
, LayerType
* l
, PolygonType
* p
)
1621 /* overlap a bit to prevent notches from rounding errors */
1622 np
= BoxPolyBloated (&pad
->BoundingBox
, UNSUBTRACT_BLOAT
);
1626 if (!Unsubtract (np
, p
))
1628 clearPoly (PCB
->Data
, l
, p
, (const BoxType
*) pad
, 2 * UNSUBTRACT_BLOAT
);
1632 static bool inhibit
= false;
1635 InitClip (DataType
*Data
, LayerType
*layer
, PolygonType
* p
)
1640 poly_Free (&p
->Clipped
);
1641 p
->Clipped
= original_poly (p
);
1642 /* HACK */ ghid_notify_polygon_changed (p
);
1643 poly_FreeContours (&p
->NoHoles
);
1646 assert (poly_Valid (p
->Clipped
));
1647 if (TEST_FLAG (CLEARPOLYFLAG
, p
))
1648 clearPoly (Data
, layer
, p
, NULL
, 0);
1650 p
->NoHolesValid
= 0;
1655 * \brief Remove redundant polygon points.
1657 * Any point that lies on the straight line between the points on either
1658 * side of it is redundant.
1660 * \return True if any points are removed.
1663 RemoveExcessPolygonPoints (LayerType
*Layer
, PolygonType
*Polygon
)
1666 Cardinal n
, prev
, next
;
1668 bool changed
= false;
1674 for (n
= 0; n
< Polygon
->PointN
; n
++)
1676 prev
= prev_contour_point (Polygon
, n
);
1677 next
= next_contour_point (Polygon
, n
);
1678 p
= &Polygon
->Points
[n
];
1680 line
.Point1
= Polygon
->Points
[prev
];
1681 line
.Point2
= Polygon
->Points
[next
];
1683 #warning THIS TEST IS NOT SUFFICIENT.. DOESNT ACCOUNT FOR DIFFERENT ARC CENTERS
1684 if (Polygon
->Points
[prev
].included_angle
== Polygon
->Points
[n
].included_angle
&&
1685 IsPointOnLine (p
->X
, p
->Y
, 0.0, &line
))
1687 RemoveObject (POLYGONPOINT_TYPE
, Layer
, Polygon
, p
);
1698 * \return The index of the polygon point which is the end point of the
1699 * segment with the lowest distance to the passed coordinates.
1702 GetLowestDistancePolygonPoint (PolygonType
*Polygon
, Coord X
, Coord Y
)
1704 double mindistance
= (double) MAX_COORD
* MAX_COORD
;
1705 PointType
*ptr1
, *ptr2
;
1706 Cardinal n
, result
= 0;
1708 /* we calculate the distance to each segment and choose the
1709 * shortest distance. If the closest approach between the
1710 * given point and the projected line (i.e. the segment extended)
1711 * is not on the segment, then the distance is the distance
1712 * to the segment end point.
1715 for (n
= 0; n
< Polygon
->PointN
; n
++)
1718 ptr1
= &Polygon
->Points
[prev_contour_point (Polygon
, n
)];
1719 ptr2
= &Polygon
->Points
[n
];
1721 dx
= ptr2
->X
- ptr1
->X
;
1722 dy
= ptr2
->Y
- ptr1
->Y
;
1723 if (dx
!= 0.0 || dy
!= 0.0)
1725 /* projected intersection is at P1 + u(P2 - P1) */
1726 u
= ((X
- ptr1
->X
) * dx
+ (Y
- ptr1
->Y
) * dy
) / (dx
* dx
+ dy
* dy
);
1729 { /* ptr1 is closest point */
1730 u
= SQUARE (X
- ptr1
->X
) + SQUARE (Y
- ptr1
->Y
);
1733 { /* ptr2 is closest point */
1734 u
= SQUARE (X
- ptr2
->X
) + SQUARE (Y
- ptr2
->Y
);
1737 { /* projected intersection is closest point */
1738 u
= SQUARE (X
- ptr1
->X
* (1.0 - u
) - u
* ptr2
->X
) +
1739 SQUARE (Y
- ptr1
->Y
* (1.0 - u
) - u
* ptr2
->Y
);
1741 if (u
< mindistance
)
1752 * \brief Go back to the previous point of the polygon.
1755 GoToPreviousPoint (void)
1757 switch (Crosshair
.AttachedPolygon
.PointN
)
1759 /* do nothing if mode has just been entered */
1763 /* reset number of points and 'LINE_MODE' state */
1765 Crosshair
.AttachedPolygon
.PointN
= 0;
1766 Crosshair
.AttachedLine
.State
= STATE_FIRST
;
1770 /* back-up one point */
1773 PointType
*points
= Crosshair
.AttachedPolygon
.Points
;
1774 Cardinal n
= Crosshair
.AttachedPolygon
.PointN
- 2;
1776 Crosshair
.AttachedPolygon
.PointN
--;
1777 Crosshair
.AttachedLine
.Point1
.X
= points
[n
].X
;
1778 Crosshair
.AttachedLine
.Point1
.Y
= points
[n
].Y
;
1785 * \brief Close polygon if possible.
1790 Cardinal n
= Crosshair
.AttachedPolygon
.PointN
;
1792 /* check number of points */
1795 /* if 45 degree lines are what we want do a quick check
1796 * if closing the polygon makes sense
1798 if (!TEST_FLAG (ALLDIRECTIONFLAG
, PCB
))
1802 dx
= abs (Crosshair
.AttachedPolygon
.Points
[n
- 1].X
-
1803 Crosshair
.AttachedPolygon
.Points
[0].X
);
1804 dy
= abs (Crosshair
.AttachedPolygon
.Points
[n
- 1].Y
-
1805 Crosshair
.AttachedPolygon
.Points
[0].Y
);
1806 if (!(dx
== 0 || dy
== 0 || dx
== dy
))
1810 ("Cannot close polygon because 45 degree lines are requested.\n"));
1814 CopyAttachedPolygonToLayer ();
1818 Message (_("A polygon has to have at least 3 points\n"));
1822 * \brief Moves the data of the attached (new) polygon to the current
1826 CopyAttachedPolygonToLayer (void)
1828 PolygonType
*polygon
;
1831 /* move data to layer and clear attached struct */
1832 polygon
= CreateNewPolygon (CURRENT
, NoFlags ());
1833 saveID
= polygon
->ID
;
1834 *polygon
= Crosshair
.AttachedPolygon
;
1835 polygon
->ID
= saveID
;
1836 SET_FLAG (CLEARPOLYFLAG
, polygon
);
1837 if (TEST_FLAG (NEWFULLPOLYFLAG
, PCB
))
1838 SET_FLAG (FULLPOLYFLAG
, polygon
);
1839 memset (&Crosshair
.AttachedPolygon
, 0, sizeof (PolygonType
));
1840 SetPolygonBoundingBox (polygon
);
1841 if (!CURRENT
->polygon_tree
)
1842 CURRENT
->polygon_tree
= r_create_tree (NULL
, 0, 0);
1843 r_insert_entry (CURRENT
->polygon_tree
, (BoxType
*) polygon
, 0);
1844 InitClip (PCB
->Data
, CURRENT
, polygon
);
1845 DrawPolygon (CURRENT
, polygon
);
1846 SetChangedFlag (true);
1848 /* reset state of attached line */
1849 Crosshair
.AttachedLine
.State
= STATE_FIRST
;
1852 /* add to undo list */
1853 AddObjectToCreateUndoList (POLYGON_TYPE
, CURRENT
, polygon
, polygon
);
1854 IncrementUndoSerialNumber ();
1858 * \brief Find polygon holes in range, then call the callback function
1861 * If the callback returns non-zero, stop the search.
1864 PolygonHoles (PolygonType
*polygon
, const BoxType
*range
,
1865 int (*callback
) (PLINE
*contour
, void *user_data
),
1868 POLYAREA
*pa
= polygon
->Clipped
;
1870 /* If this hole is so big the polygon doesn't exist, then it's not
1875 for (pl
= pa
->contours
->next
; pl
; pl
= pl
->next
)
1877 if (range
!= NULL
&&
1878 (pl
->xmin
> range
->X2
|| pl
->xmax
< range
->X1
||
1879 pl
->ymin
> range
->Y2
|| pl
->ymax
< range
->Y1
))
1881 if (callback (pl
, user_data
))
1895 int (*callback
) (DataType
*, LayerType
*, PolygonType
*, int, void *,
1901 subtract_plow (DataType
*Data
, LayerType
*Layer
, PolygonType
*Polygon
,
1902 int type
, void *ptr1
, void *ptr2
, void *userdata
)
1905 int layer_n
= GetLayerNumber (Data
, Layer
);
1907 if (!Polygon
->Clipped
)
1912 SubtractPin (Data
, (PinType
*) ptr2
, Layer
, Polygon
);
1913 Polygon
->NoHolesValid
= 0;
1916 via
= (PinType
*) ptr2
;
1917 if (!VIA_IS_BURIED (via
) || VIA_ON_LAYER (via
, layer_n
))
1919 SubtractPin (Data
, via
, Layer
, Polygon
);
1920 Polygon
->NoHolesValid
= 0;
1925 SubtractLine ((LineType
*) ptr2
, Polygon
);
1926 Polygon
->NoHolesValid
= 0;
1929 SubtractArc ((ArcType
*) ptr2
, Polygon
);
1930 Polygon
->NoHolesValid
= 0;
1933 SubtractPad ((PadType
*) ptr2
, Polygon
);
1934 Polygon
->NoHolesValid
= 0;
1937 SubtractText ((TextType
*) ptr2
, Polygon
);
1938 Polygon
->NoHolesValid
= 0;
1945 add_plow (DataType
*Data
, LayerType
*Layer
, PolygonType
*Polygon
,
1946 int type
, void *ptr1
, void *ptr2
, void *userdata
)
1949 int layer_n
= GetLayerNumber (Data
, Layer
);
1954 UnsubtractPin ((PinType
*) ptr2
, Layer
, Polygon
);
1957 via
= (PinType
*) ptr2
;
1958 if (!VIA_IS_BURIED (via
) || VIA_ON_LAYER (via
, layer_n
))
1960 UnsubtractPin ((PinType
*) ptr2
, Layer
, Polygon
);
1965 UnsubtractLine ((LineType
*) ptr2
, Layer
, Polygon
);
1968 UnsubtractArc ((ArcType
*) ptr2
, Layer
, Polygon
);
1971 UnsubtractPad ((PadType
*) ptr2
, Layer
, Polygon
);
1974 UnsubtractText ((TextType
*) ptr2
, Layer
, Polygon
);
1981 plow_callback (const BoxType
* b
, void *cl
)
1983 struct plow_info
*plow
= (struct plow_info
*) cl
;
1984 PolygonType
*polygon
= (PolygonType
*) b
;
1986 if (TEST_FLAG (CLEARPOLYFLAG
, polygon
))
1988 return plow
->callback (plow
->data
, plow
->layer
, polygon
, plow
->type
,
1989 plow
->ptr1
, plow
->ptr2
, plow
->userdata
);
1990 // /* HACK */ ghid_notify_polygon_changed (polygon);
1996 PlowsPolygon (DataType
* Data
, int type
, void *ptr1
, void *ptr2
,
1997 int (*call_back
) (DataType
*data
, LayerType
*lay
,
1998 PolygonType
*poly
, int type
, void *ptr1
,
1999 void *ptr2
, void *userdata
),
2002 BoxType sb
= ((PinType
*) ptr2
)->BoundingBox
;
2004 struct plow_info info
;
2010 info
.callback
= call_back
;
2011 info
.userdata
= userdata
;
2016 if (type
== PIN_TYPE
|| ptr1
== ptr2
|| ptr1
== NULL
)
2018 LAYER_LOOP (Data
, max_copper_layer
);
2022 r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
2028 GROUP_LOOP (Data
, GetLayerGroupNumberByNumber (GetLayerNumber (Data
,
2029 ((LayerType
*) ptr1
))));
2033 r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
2041 /* the cast works equally well for lines and arcs */
2042 if (!TEST_FLAG (CLEARLINEFLAG
, (LineType
*) ptr2
))
2044 /* silk doesn't plow */
2045 if (GetLayerNumber (Data
, (LayerType
*)ptr1
) >= max_copper_layer
)
2047 GROUP_LOOP (Data
, GetLayerGroupNumberByNumber (GetLayerNumber (Data
,
2048 ((LayerType
*) ptr1
))));
2051 r
+= r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
2057 Cardinal group
= GetLayerGroupNumberByNumber (
2058 TEST_FLAG (ONSOLDERFLAG
, (PadType
*) ptr2
) ?
2059 bottom_silk_layer
: top_silk_layer
);
2060 GROUP_LOOP (Data
, group
);
2064 r_search (layer
->polygon_tree
, &sb
, NULL
, plow_callback
, &info
);
2072 PIN_LOOP ((ElementType
*) ptr1
);
2074 PlowsPolygon (Data
, PIN_TYPE
, ptr1
, pin
, call_back
, userdata
);
2077 PAD_LOOP ((ElementType
*) ptr1
);
2079 PlowsPolygon (Data
, PAD_TYPE
, ptr1
, pad
, call_back
, userdata
);
2089 RestoreToPolygon (DataType
* Data
, int type
, void *ptr1
, void *ptr2
)
2091 if (!Data
->polyClip
)
2094 /* XXX: HACK, this is a kludgy place to put a check for recomputing our outline */
2095 if (type
== LINE_TYPE
|| type
== ARC_TYPE
)
2097 LayerType
*layer
= (LayerType
*) ptr1
;
2099 if (strcmp (layer
->Name
, "outline") == 0 ||
2100 strcmp (layer
->Name
, "route") == 0)
2101 Data
->outline_valid
= false;
2104 if (type
== POLYGON_TYPE
)
2106 InitClip (PCB
->Data
, (LayerType
*) ptr1
, (PolygonType
*) ptr2
);
2107 // /* HACK */ ghid_notify_polygon_changed (ptr2);
2110 PlowsPolygon (Data
, type
, ptr1
, ptr2
, add_plow
, NULL
);
2114 ClearFromPolygon (DataType
* Data
, int type
, void *ptr1
, void *ptr2
)
2116 if (!Data
->polyClip
)
2119 /* XXX: HACK, this is a kludgy place to put a check for recomputing our outline */
2120 if (type
== LINE_TYPE
|| type
== ARC_TYPE
)
2122 LayerType
*layer
= (LayerType
*) ptr1
;
2124 if (strcmp (layer
->Name
, "outline") == 0 ||
2125 strcmp (layer
->Name
, "route") == 0)
2126 Data
->outline_valid
= false;
2128 else if (type
== PIN_TYPE
|| type
== VIA_TYPE
)
2130 Data
->outline_valid
= false;
2133 if (type
== POLYGON_TYPE
)
2134 InitClip (PCB
->Data
, (LayerType
*) ptr1
, (PolygonType
*) ptr2
);
2136 PlowsPolygon (Data
, type
, ptr1
, ptr2
, subtract_plow
, NULL
);
2140 isects (POLYAREA
* a
, PolygonType
*p
, bool fr
)
2144 ans
= Touching (a
, p
->Clipped
);
2145 /* argument may be register, so we must copy it */
2154 IsPointInPolygon (Coord X
, Coord Y
, Coord r
, PolygonType
*p
)
2160 if (poly_CheckInside (p
->Clipped
, v
))
2164 if (!(c
= CirclePoly (X
, Y
, r
, NULL
)))
2166 return isects (c
, p
, true);
2171 IsPointInPolygonIgnoreHoles (Coord X
, Coord Y
, PolygonType
*p
)
2176 return poly_InsideContour (p
->Clipped
->contours
, v
);
2180 IsRectangleInPolygon (Coord X1
, Coord Y1
, Coord X2
, Coord Y2
, PolygonType
*p
)
2184 (s
= RectPoly (min (X1
, X2
), max (X1
, X2
), min (Y1
, Y2
), max (Y1
, Y2
))))
2186 return isects (s
, p
, true);
2192 * \note This function will free the passed POLYAREA.
2193 * It must only be passed a single POLYAREA (pa->f == pa->b == pa).
2196 r_NoHolesPolygonDicer (POLYAREA
* pa
,
2197 void (*emit
) (PLINE
*, void *), void *user_data
)
2199 PLINE
*p
= pa
->contours
;
2201 if (!pa
->contours
->next
) /* no holes */
2203 pa
->contours
= NULL
; /* The callback now owns the contour */
2204 /* Don't bother removing it from the POLYAREA's rtree
2205 since we're going to free the POLYAREA below anyway */
2206 emit (p
, user_data
);
2212 POLYAREA
*poly2
, *left
, *right
;
2214 /* make a rectangle of the left region slicing through the middle of the first hole */
2215 poly2
= RectPoly (p
->xmin
, (p
->next
->xmin
+ p
->next
->xmax
) / 2,
2217 poly_AndSubtract_free (pa
, poly2
, &left
, &right
);
2220 POLYAREA
*cur
, *next
;
2225 cur
->f
= cur
->b
= cur
; /* Detach this polygon piece */
2226 r_NoHolesPolygonDicer (cur
, emit
, user_data
);
2227 /* NB: The POLYAREA was freed by its use in the recursive dicer */
2229 while ((cur
= next
) != left
);
2233 POLYAREA
*cur
, *next
;
2238 cur
->f
= cur
->b
= cur
; /* Detach this polygon piece */
2239 r_NoHolesPolygonDicer (cur
, emit
, user_data
);
2240 /* NB: The POLYAREA was freed by its use in the recursive dicer */
2242 while ((cur
= next
) != right
);
2248 NoHolesPolygonDicer (PolygonType
*p
, const BoxType
* clip
,
2249 void (*emit
) (PLINE
*, void *), void *user_data
)
2251 POLYAREA
*main_contour
, *cur
, *next
;
2253 /* copy the main poly only */
2254 poly_Copy0 (&main_contour
, p
->Clipped
);
2255 /* clip to the bounding box */
2258 POLYAREA
*cbox
= RectPoly (clip
->X1
, clip
->X2
, clip
->Y1
, clip
->Y2
);
2259 poly_Boolean_free (main_contour
, cbox
, &main_contour
, PBO_ISECT
);
2261 if (main_contour
== NULL
)
2264 * NB: Could be more than one piece (because of the clip above)
2270 cur
->f
= cur
->b
= cur
; /* Detach this polygon piece */
2271 r_NoHolesPolygonDicer (cur
, emit
, user_data
);
2272 /* NB: The POLYAREA was freed by its use in the recursive dicer */
2274 while ((cur
= next
) != main_contour
);
2278 * \brief Make a polygon split into multiple parts into multiple
2282 MorphPolygon (LayerType
*layer
, PolygonType
*poly
)
2284 POLYAREA
*p
, *start
;
2288 if (!poly
->Clipped
|| TEST_FLAG (LOCKFLAG
, poly
))
2290 if (poly
->Clipped
->f
== poly
->Clipped
)
2292 ErasePolygon (poly
);
2293 start
= p
= poly
->Clipped
;
2294 /* This is ugly. The creation of the new polygons can cause
2295 * all of the polygon pointers (including the one we're called
2296 * with to change if there is a realloc in GetPolygonMemory().
2297 * That will invalidate our original "poly" argument, potentially
2298 * inside the loop. We need to steal the Clipped pointer and
2299 * hide it from the Remove call so that it still exists while
2300 * we do this dirty work.
2302 poly
->Clipped
= NULL
;
2303 /* HACK */ ghid_notify_polygon_changed (poly
);
2304 if (poly
->NoHoles
) printf ("Just leaked in MorpyPolygon\n");
2305 poly
->NoHoles
= NULL
;
2306 flags
= poly
->Flags
;
2307 RemovePolygon (layer
, poly
);
2312 PolygonType
*newone
;
2314 if (p
->contours
->area
> PCB
->IsleArea
)
2316 newone
= CreateNewPolygon (layer
, flags
);
2320 v
= &p
->contours
->head
;
2321 CreateNewPointInPolygon (newone
, v
->point
[0], v
->point
[1], 0);
2322 for (v
= v
->next
; v
!= &p
->contours
->head
; v
= v
->next
)
2323 CreateNewPointInPolygon (newone
, v
->point
[0], v
->point
[1], 0);
2324 newone
->BoundingBox
.X1
= p
->contours
->xmin
;
2325 newone
->BoundingBox
.X2
= p
->contours
->xmax
+ 1;
2326 newone
->BoundingBox
.Y1
= p
->contours
->ymin
;
2327 newone
->BoundingBox
.Y2
= p
->contours
->ymax
+ 1;
2328 AddObjectToCreateUndoList (POLYGON_TYPE
, layer
, newone
, newone
);
2329 newone
->Clipped
= p
;
2330 p
= p
->f
; /* go to next pline */
2331 newone
->Clipped
->b
= newone
->Clipped
->f
= newone
->Clipped
; /* unlink from others */
2332 r_insert_entry (layer
->polygon_tree
, (BoxType
*) newone
, 0);
2333 DrawPolygon (layer
, newone
);
2340 poly_DelContour (&t
->contours
);
2346 IncrementUndoSerialNumber ();
2350 void debug_pline (PLINE
*pl
)
2353 pcb_fprintf (stderr
, "\txmin %#mS xmax %#mS ymin %#mS ymax %#mS\n",
2354 pl
->xmin
, pl
->xmax
, pl
->ymin
, pl
->ymax
);
2358 pcb_fprintf(stderr
, "\t\tvnode: %#mD\n", v
->point
[0], v
->point
[1]);
2366 debug_polyarea (POLYAREA
*p
)
2370 fprintf (stderr
, "POLYAREA %p\n", p
);
2371 for (pl
=p
->contours
; pl
; pl
=pl
->next
)
2376 debug_polygon (PolygonType
*p
)
2380 fprintf (stderr
, "POLYGON %p %d pts\n", p
, p
->PointN
);
2381 for (i
=0; i
<p
->PointN
; i
++)
2382 pcb_fprintf(stderr
, "\t%d: %#mD\n", i
, p
->Points
[i
].X
, p
->Points
[i
].Y
);
2385 fprintf (stderr
, "%d holes, starting at indices\n", p
->HoleIndexN
);
2386 for (i
=0; i
<p
->HoleIndexN
; i
++)
2387 fprintf(stderr
, "\t%d: %d\n", i
, p
->HoleIndex
[i
]);
2390 fprintf (stderr
, "it has no holes\n");
2394 debug_polyarea (pa
);
2396 if (pa
== p
->Clipped
)
2402 * \brief Convert a POLYAREA (and all linked POLYAREA) to raw PCB
2403 * polygons on the given layer.
2406 PolyToPolygonsOnLayer (DataType
*Destination
, LayerType
*Layer
,
2407 POLYAREA
*Input
, FlagType Flags
)
2409 PolygonType
*Polygon
;
2421 Polygon
= CreateNewPolygon (Layer
, Flags
);
2423 pline
= pa
->contours
;
2429 CreateNewHoleInPolygon (Polygon
);
2432 node
= &pline
->head
;
2435 CreateNewPointInPolygon (Polygon
, node
->point
[0],
2439 while ((node
= node
->next
) != &pline
->head
);
2442 while ((pline
= pline
->next
) != NULL
);
2444 InitClip (Destination
, Layer
, Polygon
);
2445 SetPolygonBoundingBox (Polygon
);
2446 if (!Layer
->polygon_tree
)
2447 Layer
->polygon_tree
= r_create_tree (NULL
, 0, 0);
2448 r_insert_entry (Layer
->polygon_tree
, (BoxType
*) Polygon
, 0);
2450 DrawPolygon (Layer
, Polygon
);
2451 /* add to undo list */
2452 AddObjectToCreateUndoList (POLYGON_TYPE
, Layer
, Polygon
, Polygon
);
2454 while ((pa
= pa
->f
) != Input
);
2456 SetChangedFlag (true);
2460 struct clip_outline_info
{
2464 #define ROUTER_THICKNESS MIL_TO_COORD (10)
2465 //#define ROUTER_THICKNESS MIL_TO_COORD (0.1)
2468 arc_outline_callback (const BoxType
* b
, void *cl
)
2470 ArcType
*arc
= (ArcType
*)b
;
2471 struct clip_outline_info
*info
= cl
;
2475 feature_name
= NULL
; /* XXX: PCB does not have any concept of naming arcs */
2477 #ifdef DEBUG_CIRCSEGS
2478 if (!(np
= ArcPoly (arc
, arc
->Thickness
, feature_name
)))
2480 if (!(np
= ArcPoly (arc
, ROUTER_THICKNESS
, feature_name
)))
2484 #ifdef DEBUG_CIRCSEGS
2485 poly_Boolean_free (info
->poly
, np
, &res
, PBO_UNITE
);
2487 poly_Boolean_free (info
->poly
, np
, &res
, PBO_SUB
);
2495 line_outline_callback (const BoxType
* b
, void *cl
)
2497 LineType
*line
= (LineType
*)b
;
2498 struct clip_outline_info
*info
= cl
;
2502 feature_name
= STRDUP(line
->Number
);
2504 #ifdef DEBUG_CIRCSEGS
2505 if (!(np
= LinePoly (line
, line
->Thickness
, feature_name
)))
2507 if (!(np
= LinePoly (line
, ROUTER_THICKNESS
, feature_name
)))
2511 #ifdef DEBUG_CIRCSEGS
2512 poly_Boolean_free (info
->poly
, np
, &res
, PBO_UNITE
);
2514 poly_Boolean_free (info
->poly
, np
, &res
, PBO_SUB
);
2522 pv_outline_callback (const BoxType
* b
, void *cl
)
2524 PinType
*pv
= (PinType
*)b
;
2525 struct clip_outline_info
*info
= cl
;
2529 if (pv
->Element
!= NULL
)
2531 char *element_name
= ((ElementType
*)pv
->Element
)->Name
[NAMEONPCB_INDEX
].TextString
;
2532 char *pin_number
= pv
->Number
;
2534 if (element_name
!= NULL
&& pin_number
!= NULL
)
2536 feature_name
= NULL
; // Win32
2539 // feature_name = tmp = malloc (strlen (element_name) + 1 + strlen (pin_number) + 1);
2540 // tmp = stpcpy (tmp, element_name);
2542 // tmp = stpcpy (tmp, pin_number);
2546 feature_name
= NULL
;
2551 feature_name
= STRDUP(pv
->Name
);
2554 #ifdef DEBUG_CIRCSEGS
2555 if (!(np
= CirclePoly (pv
->X
, pv
->Y
, pv
->Thickness
/ 2, feature_name
)))
2557 if (!(np
= CirclePoly (pv
->X
, pv
->Y
, pv
->DrillingHole
/ 2, feature_name
)))
2561 #ifdef DEBUG_CIRCSEGS
2562 poly_Boolean_free (info
->poly
, np
, &res
, PBO_UNITE
);
2564 poly_Boolean_free (info
->poly
, np
, &res
, PBO_SUB
);
2572 polygon_outline_callback (const BoxType
* b
, void *cl
)
2574 PolygonType
*poly
= (PolygonType
*)b
;
2575 struct clip_outline_info
*info
= cl
;
2578 if (!(np
= original_poly (poly
)))
2581 #ifdef DEBUG_CIRCSEGS
2582 poly_Boolean_free (info
->poly
, np
, &res
, PBO_UNITE
);
2584 poly_Boolean_free (info
->poly
, np
, &res
, PBO_SUB
);
2592 delete_piece_cb (gpointer data
, gpointer userdata
)
2594 POLYAREA
*piece
= data
;
2595 POLYAREA
**res
= userdata
;
2597 /* If this item was the start of the list, advance that pointer */
2601 /* But reset it to NULL if it wraps around and hits us again */
2605 piece
->b
->f
= piece
->f
;
2606 piece
->f
->b
= piece
->b
;
2607 piece
->f
= piece
->b
= piece
;
2609 /* Detach the parentage information, so we don't free it.. copies still belong to the M_POLYAREA we are taking this piece from */
2610 piece
->parentage
.immaculate_conception
= true;
2611 piece
->parentage
.action
= PBO_NONE
;
2612 piece
->parentage
.a
= NULL
;
2613 piece
->parentage
.b
= NULL
;
2618 POLYAREA
*board_outline_poly (bool include_holes
)
2622 int found_outline
= 0;
2623 LayerType
*Layer
= NULL
;
2625 struct clip_outline_info info
;
2626 POLYAREA
*whole_world
;
2630 GList
*pieces_to_delete
= NULL
;
2631 bool any_pieces_kept
= false;
2633 #define BLOAT_WORLD MIL_TO_COORD (10)
2635 whole_world
= RectPoly (-BLOAT_WORLD
, BLOAT_WORLD
+ PCB
->MaxWidth
,
2636 -BLOAT_WORLD
, BLOAT_WORLD
+ PCB
->MaxHeight
);
2638 for (i
= 0; i
< max_copper_layer
; i
++)
2640 Layer
= PCB
->Data
->Layer
+ i
;
2642 if (strcmp (Layer
->Name
, "outline") == 0 ||
2643 strcmp (Layer
->Name
, "route") == 0)
2651 printf ("Didn't find outline\n");
2653 /* Do stuff to turn the outline layer into a polygon */
2655 /* Ideally, we just want to look at centre-lines, but that is hard!
2657 * Lets add all lines, arcs etc.. together to form a polygon comprising
2658 * the bits we presume a router would remove.
2660 * Then we need to subtract that from some notional "infinite" plane
2661 * polygon, leaving the remaining pieces.
2663 * OR.. we could just look at the holes in the resulting polygon?
2665 * Given these holes, we need to know which are inside and outside.
2678 region
.X2
= PCB
->MaxWidth
;
2679 region
.Y2
= PCB
->MaxHeight
;
2682 #ifdef DEBUG_CIRCSEGS
2685 info
.poly
= whole_world
;
2690 r_search (Layer
->line_tree
, ®ion
, NULL
, line_outline_callback
, &info
);
2691 r_search (Layer
->arc_tree
, ®ion
, NULL
, arc_outline_callback
, &info
);
2694 #ifndef DEBUG_CIRCSEGS
2698 r_search (PCB
->Data
->pin_tree
, ®ion
, NULL
, pv_outline_callback
, &info
);
2699 r_search (PCB
->Data
->via_tree
, ®ion
, NULL
, pv_outline_callback
, &info
);
2702 clipped
= info
.poly
;
2704 #ifdef DEBUG_CIRCSEGS
2708 /* Now we just need to work out which pieces of polygon are inside
2709 and outside the board! */
2711 /* If there is no result, or only one piece, return that */
2712 if (clipped
== NULL
|| clipped
->f
== clipped
)
2715 /* WARNING: This next check is O(n^2), where n is the number of clipped
2716 * pieces, hopefully the outline layer isn't too complex!
2720 do { /* LOOP OVER POLYGON PIECES */
2722 if (piece
->contours
== NULL
)
2727 do { /* LOOP OVER POLYGON PIECES */
2730 if (poly_ContourInContour (check
->contours
, piece
->contours
))
2732 } while ((check
= check
->f
) != clipped
);
2734 /* If the piece is inside an odd number of others, keep it */
2735 if ((count
& 1) == 1)
2736 any_pieces_kept
= true;
2738 pieces_to_delete
= g_list_prepend (pieces_to_delete
, piece
);
2740 } while ((piece
= piece
->f
) != clipped
);
2742 /* If we did not find an enclosed area (the board) trimmed from the world polygon,
2743 return the entire subtracted result. This keeps the behaviour similar to what
2744 you see when individual lines on the outline layer don't close to form an
2745 enclosed region. (This fixes being able to cope with such a case where the
2746 outline layer geometry lies outside the world rect polygon above, and divides
2747 that world rect into multiple pieces.
2749 if (any_pieces_kept
)
2750 g_list_foreach (pieces_to_delete
, delete_piece_cb
, &clipped
);
2752 g_list_free (pieces_to_delete
);
2755 #ifdef DEBUG_CIRCSEGS
2756 // The actual operation we want is to split the test polygon into multiple pieces
2757 // along the intersection with the polygon contours of any polygon on the outer layer.
2758 // The result would be nested, touching (not normally produced by the PBO code),
2759 // polygon pieces which could then be culled appropriately by the above contour
2760 // classification code to delete the regions outside the board.
2761 info
.poly
= NULL
; //clipped;
2762 r_search (Layer
->polygon_tree
, ®ion
, NULL
, polygon_outline_callback
, &info
);
2763 clipped
= info
.poly
;
2765 if (clipped
== NULL
)
2768 poly_Free (&whole_world
);