More stackup changes
[geda-pcb/pcjc2/v2.git] / src / polygon.c
blob64828b304661228283d0ecf47c2cc241a8d034c9
1 /*!
2 * \file src/polygon.c
4 * \brief Special polygon editing routines.
6 * Here's a brief tour of the data and life of a polygon, courtesy of
7 * Ben Jackson:
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
18 * the board).
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
43 * more holes).
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
46 * that one hole).
47 * The dicer output is used for HIDs which cannot render things with
48 * holes (which would require erasure).
50 * <hr>
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
79 #ifdef HAVE_CONFIG_H
80 #include "config.h"
81 #endif
83 #include <assert.h>
84 #include <math.h>
85 #include <memory.h>
86 #include <setjmp.h>
87 #include <glib.h>
89 #include "global.h"
90 #include "box.h"
91 #include "create.h"
92 #include "crosshair.h"
93 #include "data.h"
94 #include "draw.h"
95 #include "error.h"
96 #include "find.h"
97 #include "misc.h"
98 #include "move.h"
99 #include "pcb-printf.h"
100 #include "polygon.h"
101 #include "remove.h"
102 #include "rtree.h"
103 #include "search.h"
104 #include "set.h"
105 #include "thermal.h"
106 #include "undo.h"
108 #ifdef HAVE_LIBDMALLOC
109 #include <dmalloc.h>
110 #endif
112 #ifndef WIN32
113 /* For getrlimit, setrlimit */
114 # include <sys/time.h>
115 # include <sys/resource.h>
116 #endif
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];
130 void
131 polygon_init (void)
133 int i;
134 #ifndef WIN32
135 struct rlimit limit;
136 #endif
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! */
153 return;
155 #ifndef WIN32
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);
160 #endif
163 Cardinal
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.
174 * 0 for outer,
176 * 1 for first hole etc..
178 Cardinal
179 polygon_point_contour (PolygonType *polygon, Cardinal point)
181 Cardinal i;
182 Cardinal contour = 0;
184 for (i = 0; i < polygon->HoleIndexN; i++)
185 if (point >= polygon->HoleIndex[i])
186 contour = i + 1;
187 return contour;
190 Cardinal
191 next_contour_point (PolygonType *polygon, Cardinal point)
193 Cardinal contour;
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];
201 next_contour_start =
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;
209 return point;
212 Cardinal
213 prev_contour_point (PolygonType *polygon, Cardinal point)
215 Cardinal contour;
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];
223 this_contour_end =
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;
230 else
231 point--;
233 return point;
236 static void
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;
246 void
247 ComputeNoHoles (PolygonType *poly)
249 poly_FreeContours (&poly->NoHoles);
250 if (poly->Clipped)
251 NoHolesPolygonDicer (poly, NULL, add_noholes_polyarea, poly);
252 else
253 printf ("Compute_noholes caught poly->Clipped = NULL\n");
254 poly->NoHolesValid = 1;
257 static POLYAREA *
258 biggest (POLYAREA * p)
260 POLYAREA *n, *top = NULL;
261 PLINE *pl;
262 rtree_t *tree;
263 double big = -1;
264 if (!p)
265 return NULL;
266 n = p;
269 #if 0
270 if (n->contours->area < PCB->IsleArea)
272 n->b->f = n->f;
273 n->f->b = n->b;
274 poly_DelContour (&n->contours);
275 if (n == p)
276 p = n->f;
277 n = n->f;
278 if (!n->contours)
280 free (n);
281 return NULL;
284 #endif
285 if (n->contours->area > big)
287 top = n;
288 big = n->contours->area;
291 while ((n = n->f) != p);
292 assert (top);
293 if (top == p)
294 return p;
295 pl = top->contours;
296 tree = top->contour_tree;
297 top->contours = p->contours;
298 top->contour_tree = p->contour_tree;
299 p->contours = pl;
300 p->contour_tree = tree;
301 assert (pl);
302 assert (p->f);
303 assert (p->b);
304 return p;
307 POLYAREA *
308 ContourToPoly (PLINE * contour)
310 POLYAREA *p;
311 poly_PreContour (contour, TRUE);
312 assert (contour->Flags.orient == PLF_DIR);
313 if (!(p = poly_Create ()))
314 return NULL;
315 if (!poly_InclContour (p, contour))
317 printf ("ContourToPoly() WARNING: poly_InclContour returned false\n");
318 poly_Free (&p);
320 assert (poly_Valid (p));
321 return p;
324 static void
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 */
328 double e1, e2, t1;
329 int i;
330 int range;
331 double start_angle;
332 double end_angle;
333 int start_index;
334 int end_index;
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;
347 else
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;
354 #if 0
355 if (sweep > 0)
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;
364 e1 = t1;
365 v[0] = X + ROUND (e1);
366 v[1] = Y + ROUND (e2);
367 poly_InclVertex (c->head.prev, poly_CreateNodeArcApproximation (v, X, Y, radius));
370 else
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;
379 e1 = t1;
380 v[0] = X + ROUND (e1);
381 v[1] = Y + ROUND (e2);
382 poly_InclVertex (c->head.prev, poly_CreateNodeArcApproximation (v, X, Y, radius));
385 #endif
387 if (sweep > 0)
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));
408 else
410 #if 1
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));
429 #else
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;
437 e1 = t1;
438 v[0] = X + ROUND (e1);
439 v[1] = Y + ROUND (e2);
440 poly_InclVertex (c->head.prev, poly_CreateNodeArcApproximation (v, X, Y, radius));
442 #endif
446 static POLYAREA *
447 original_poly (PolygonType * p)
449 PLINE *contour = NULL;
450 POLYAREA *np = NULL;
451 Cardinal n;
452 Vector v;
453 unsigned int hole = 0;
455 if ((np = poly_Create ()) == NULL)
456 return NULL;
458 /* first make initial polygon contour */
459 for (n = 0; n < p->PointN; n++)
461 VNODE *node;
462 //Cardinal prev_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 */
472 if (0)
473 node = poly_CreateNodeArcApproximation (v, 0, 0, 0);
474 else
475 node = poly_CreateNode (v);
477 if (contour == NULL)
479 if ((contour = poly_NewContour (node)) == NULL)
480 return NULL;
482 else
484 poly_InclVertex (contour->head.prev, node);
487 if (p->Points[n].included_angle != 0)
489 Cardinal next_n;
490 Coord cx, cy;
491 Coord radius;
493 next_n = n + 1;
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;
508 #endif
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));
515 #endif
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);
530 contour = NULL;
531 assert (poly_Valid (np));
533 hole++;
536 return biggest (np);
539 POLYAREA *
540 PolygonToPoly (PolygonType *p)
542 return original_poly (p);
545 POLYAREA *
546 RectPoly (Coord x1, Coord x2, Coord y1, Coord y2)
548 PLINE *contour = NULL;
549 Vector v;
551 /* Return NULL for zero or negatively sized rectangles */
552 if (x2 <= x1 || y2 <= y1)
553 return NULL;
555 v[0] = x1;
556 v[1] = y1;
557 if ((contour = poly_NewContour (poly_CreateNode (v))) == NULL)
558 return NULL;
559 v[0] = x2;
560 v[1] = y1;
561 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
562 v[0] = x2;
563 v[1] = y2;
564 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
565 v[0] = x1;
566 v[1] = y2;
567 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
568 return ContourToPoly (contour);
571 POLYAREA *
572 OctagonPoly (Coord x, Coord y, Coord radius)
574 PLINE *contour = NULL;
575 Vector v;
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)
580 return 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.
611 void
612 frac_circle2 (PLINE * c, Coord X, Coord Y, Vector v, int fraction)
614 // double e1, e2, t1;
615 int i;
616 // int range;
617 double radius = hypot (v[0] - X, v[1] - Y);
618 double start_angle;
619 int start_index;
620 int end_index;
622 /* XXX: Circle already has the first node added */
623 // if (fraction > 1)
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;
635 else
636 poly_InclVertex (c->head.prev, poly_CreateNodeArcApproximation (v, X, Y, radius));
638 #if 0
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;
650 e1 = t1;
651 v[0] = X + ROUND (e1);
652 v[1] = Y + ROUND (e2);
653 poly_InclVertex (c->head.prev, poly_CreateNodeArcApproximation (v, X, Y, radius));
655 #endif
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.
685 POLYAREA *
686 CirclePoly (Coord x, Coord y, Coord radius, char *name)
688 PLINE *contour;
689 Vector v;
691 if (radius <= 0)
692 return NULL;
693 v[0] = x + radius;
694 v[1] = y;
695 if ((contour = poly_NewContour (poly_CreateNodeArcApproximation (v, x, y, radius))) == NULL)
696 return NULL;
697 frac_circle2 (contour, x, y, v, 1);
698 contour->is_round = TRUE;
699 contour->cx = x;
700 contour->cy = y;
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.
710 POLYAREA *
711 RoundRect (Coord x1, Coord x2, Coord y1, Coord y2, Coord t)
713 PLINE *contour = NULL;
714 Vector v;
716 assert (x2 > x1);
717 assert (y2 > y1);
719 v[0] = x1 - t;
720 v[1] = y2;
721 if ((contour = poly_NewContour (poly_CreateNode (v))) == NULL)
722 return NULL;
723 v[0] = x1 - t;
724 v[1] = y1;
725 frac_circle2 (contour, x1, y1, v, 4);
726 v[0] = x1;
727 v[1] = y1 - t;
728 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
729 v[0] = x2;
730 v[1] = y1 - t;
731 frac_circle2 (contour, x2, y1, v, 4);
732 v[0] = x2 + t;
733 v[1] = y1;
734 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
735 v[0] = x2 + t;
736 v[1] = y2;
737 frac_circle2 (contour, x2, y2, v, 4);
738 v[0] = x2;
739 v[1] = y2 + t;
740 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
741 v[0] = x1;
742 v[1] = y2 + t;
743 frac_circle2 (contour, x1, y2, v, 4);
744 return ContourToPoly (contour);
747 #define ARC_ANGLE 5
748 static POLYAREA *
749 ArcPolyNoIntersect (ArcType * a, Coord thick, char *name)
751 PLINE *contour = NULL;
752 POLYAREA *np = NULL;
753 Vector v;
754 BoxType *ends;
755 int i, segs;
756 double ang, da, rx, ry;
757 long half;
758 double radius_adj;
760 if (thick <= 0)
761 return NULL;
762 if (a->Delta < 0)
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);
772 segs = 1;
773 if(thick > 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);
779 ang = a->StartAngle;
780 da = (1.0 * a->Delta) / segs;
782 /* XXX: No need for radius ofsetting bodgery for the exact arc representation */
783 if (rx == ry)
784 radius_adj = 0.;
785 else
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)
793 return 0;
795 contour->name = name;
796 if (rx == ry)
797 degree_circle (contour, a->X, a->Y, rx, v, -a->Delta);
798 else
799 for (i = 0; i < segs - 1; i++)
801 ang += da;
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);
815 da = -da;
816 v[0] = a->X - rx * cos (ang * M180);
817 v[1] = a->Y + ry * sin (ang * M180);
818 if (rx == ry)
819 degree_circle (contour, a->X, a->Y, rx, v, a->Delta);
820 else
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));
826 ang += da;
828 /* now add other round cap */
829 ang = a->StartAngle;
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)))
835 return NULL;
836 return np;
839 #define MIN_CLEARANCE_BEFORE_BISECT 10.
840 POLYAREA *
841 ArcPoly (ArcType * a, Coord thick, char *name)
843 double delta;
844 ArcType seg1, seg2;
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;
856 seg1 = seg2 = *a;
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);
864 return res;
867 return ArcPolyNoIntersect (a, thick, name);
870 POLYAREA *
871 LinePoly (LineType * L, Coord thick, char *name)
873 PLINE *contour = NULL;
874 POLYAREA *np = NULL;
875 Vector v;
876 double d, dx, dy;
877 long half;LineType _l=*L,*l=&_l;
879 if (thick <= 0)
880 return NULL;
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);
886 if (d != 0)
888 d = half / d;
889 dx = (l->Point1.Y - l->Point2.Y) * d;
890 dy = (l->Point2.X - l->Point1.X) * d;
892 else
894 dx = half;
895 dy = 0;
897 if (TEST_FLAG (SQUAREFLAG,l))/* take into account the ends */
899 l->Point1.X -= dy;
900 l->Point1.Y += dx;
901 l->Point2.X += dy;
902 l->Point2.Y -= dx;
905 v[0] = l->Point1.X - dx;
906 v[1] = l->Point1.Y - dy;
907 if ((contour = poly_NewContour (poly_CreateNode (v))) == NULL)
908 return 0;
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));
916 else
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));
928 else
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)))
936 return NULL;
938 return np;
942 * \brief Make a rectangle pad (clear is applied square, not rounded
944 static POLYAREA *
945 SquarePadPoly (PadType * pad, Coord clear)
947 PLINE *contour = NULL;
948 POLYAREA *np = NULL;
949 Vector v;
950 double d;
951 double cx, cy;
952 PadType _c=*pad,*c=&_c;
953 int halfclear = (clear + 1) / 2;
955 if (clear <= 0)
956 return NULL;
958 d = hypot (pad->Point1.X - pad->Point2.X,
959 pad->Point1.Y - pad->Point2.Y);
960 if (d != 0)
962 double a = halfclear / d;
963 cx = (c->Point1.Y - c->Point2.Y) * a;
964 cy = (c->Point2.X - c->Point1.X) * a;
966 c->Point1.X -= cy;
967 c->Point1.Y += cx;
968 c->Point2.X += cy;
969 c->Point2.Y -= cx;
971 else
973 cx = halfclear;
974 cy = 0;
976 c->Point1.Y += cx;
977 c->Point2.Y -= cx;
980 v[0] = c->Point1.X + cx;
981 v[1] = c->Point1.Y + cy;
982 if ((contour = poly_NewContour (poly_CreateNode (v))) == NULL)
983 return 0;
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)))
999 return NULL;
1000 return np;
1004 * \brief Make a rounded-corner rectangle.
1006 static POLYAREA *
1007 SquarePadClearPoly (PadType * pad, Coord clear)
1009 PLINE *contour = NULL;
1010 POLYAREA *np = NULL;
1011 Vector v;
1012 double d;
1013 double tx, ty;
1014 double cx, cy;
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);
1021 if (d != 0)
1023 double a = halfthick / d;
1024 tx = (t->Point1.Y - t->Point2.Y) * a;
1025 ty = (t->Point2.X - t->Point1.X) * a;
1026 a = halfclear / d;
1027 cx = (c->Point1.Y - c->Point2.Y) * a;
1028 cy = (c->Point2.X - c->Point1.X) * a;
1030 t->Point1.X -= ty;
1031 t->Point1.Y += tx;
1032 t->Point2.X += ty;
1033 t->Point2.Y -= tx;
1034 c->Point1.X -= cy;
1035 c->Point1.Y += cx;
1036 c->Point2.X += cy;
1037 c->Point2.Y -= cx;
1039 else
1041 tx = halfthick;
1042 ty = 0;
1043 cx = halfclear;
1044 cy = 0;
1046 t->Point1.Y += tx;
1047 t->Point2.Y -= tx;
1048 c->Point1.Y += cx;
1049 c->Point2.Y -= cx;
1052 v[0] = c->Point1.X + tx;
1053 v[1] = c->Point1.Y + ty;
1054 if ((contour = poly_NewContour (poly_CreateNode (v))) == NULL)
1055 return 0;
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)))
1087 return NULL;
1088 return np;
1091 /* HACK */ extern void ghid_notify_polygon_changed (PolygonType *);
1094 * \brief Clear np1 from the polygon.
1096 static int
1097 Subtract (POLYAREA * np1, PolygonType * p, bool fnp)
1099 POLYAREA *merged = NULL, *np = np1;
1100 int x;
1101 assert (np);
1102 assert (p);
1103 if (!p->Clipped)
1105 if (fnp)
1106 poly_Free (&np);
1107 return 1;
1109 assert (poly_Valid (p->Clipped));
1110 assert (poly_Valid (np));
1111 if (fnp)
1112 x = poly_Boolean_free (p->Clipped, np, &merged, PBO_SUB);
1113 else
1115 x = poly_Boolean (p->Clipped, np, &merged, PBO_SUB);
1116 poly_Free (&p->Clipped);
1118 assert (!merged || poly_Valid (merged));
1119 if (x != err_ok)
1121 fprintf (stderr, "Error while clipping PBO_SUB: %d\n", x);
1122 poly_Free (&merged);
1123 p->Clipped = NULL;
1124 /* HACK */ ghid_notify_polygon_changed (p);
1125 if (p->NoHoles) printf ("Just leaked in Subtract\n");
1126 p->NoHoles = NULL;
1127 return -1;
1129 p->Clipped = biggest (merged);
1130 /* HACK */ ghid_notify_polygon_changed (p);
1131 assert (!p->Clipped || poly_Valid (p->Clipped));
1132 if (!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);
1136 return 1;
1140 * \brief Create a polygon of the pin clearance.
1142 POLYAREA *
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);
1156 else
1158 return CirclePoly (pin->X, pin->Y, size, NULL);
1162 /* create a polygon of the pin clearance */
1163 static POLYAREA *
1164 PinClearPoly (PinType * pin, Coord thick, Coord clear)
1166 int size;
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);
1174 else
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);
1185 POLYAREA *
1186 BoxPolyBloated (BoxType *box, Coord bloat)
1188 return RectPoly (box->X1 - bloat, box->X2 + bloat,
1189 box->Y1 - bloat, box->Y2 + bloat);
1192 POLYAREA *
1193 PadPoly (PadType *pad, Coord size)
1195 if (TEST_FLAG (SQUAREFLAG, pad))
1196 return SquarePadPoly (pad, size);
1197 else
1198 return LinePoly ((LineType *) pad, size, NULL);
1201 static POLYAREA *
1202 PadClearPoly (PadType *pad, Coord size)
1204 if (TEST_FLAG (SQUAREFLAG, pad))
1205 return SquarePadClearPoly (pad, size);
1206 else
1207 return LinePoly ((LineType *) pad, size, NULL);
1211 * \brief Remove the pin clearance from the polygon.
1213 static int
1214 SubtractPin (DataType * d, PinType * pin, LayerType * l, PolygonType * p)
1216 POLYAREA *np;
1217 Cardinal i;
1219 if (pin->Clearance == 0)
1220 return 0;
1221 i = GetLayerNumber (d, l);
1222 if (TEST_THERM (i, pin))
1224 np = ThermPoly ((PCBType *) (d->pcb), pin, i);
1225 if (!np)
1226 return 0;
1228 else
1230 np = PinClearPoly (pin, PIN_SIZE (pin), pin->Clearance);
1231 if (!np)
1232 return -1;
1234 return Subtract (np, p, TRUE);
1237 static int
1238 SubtractLine (LineType * line, PolygonType * p)
1240 POLYAREA *np;
1242 if (!TEST_FLAG (CLEARLINEFLAG, line))
1243 return 0;
1244 if (!(np = LinePoly (line, line->Thickness + line->Clearance, NULL)))
1245 return -1;
1246 return Subtract (np, p, true);
1249 static int
1250 SubtractArc (ArcType * arc, PolygonType * p)
1252 POLYAREA *np;
1254 if (!TEST_FLAG (CLEARLINEFLAG, arc))
1255 return 0;
1256 if (!(np = ArcPoly (arc, arc->Thickness + arc->Clearance, NULL)))
1257 return -1;
1258 return Subtract (np, p, true);
1261 static int
1262 SubtractText (TextType * text, PolygonType * p)
1264 POLYAREA *np;
1265 const BoxType *b = &text->BoundingBox;
1267 if (!TEST_FLAG (CLEARLINEFLAG, text))
1268 return 0;
1269 if (!(np = RoundRect (b->X1 + PCB->Bloat, b->X2 - PCB->Bloat,
1270 b->Y1 + PCB->Bloat, b->Y2 - PCB->Bloat, PCB->Bloat)))
1271 return -1;
1272 return Subtract (np, p, true);
1275 static int
1276 SubtractPad (PadType * pad, PolygonType * p)
1278 POLYAREA *np = NULL;
1280 if (pad->Clearance == 0)
1281 return 0;
1282 if (!(np = PadClearPoly (pad, pad->Thickness + pad->Clearance)))
1283 return -1;
1284 return Subtract (np, p, true);
1287 struct cpInfo
1289 const BoxType *other;
1290 DataType *data;
1291 LayerType *layer;
1292 PolygonType *polygon;
1293 bool bottom;
1294 POLYAREA *accumulate;
1295 int batch_size;
1296 jmp_buf env;
1299 static void
1300 subtract_accumulated (struct cpInfo *info, PolygonType *polygon)
1302 if (info->accumulate == NULL)
1303 return;
1304 Subtract (info->accumulate, polygon, true);
1305 info->accumulate = NULL;
1306 info->batch_size = 0;
1309 static int
1310 pin_sub_callback (const BoxType * b, void *cl)
1312 PinType *pin = (PinType *) b;
1313 struct cpInfo *info = (struct cpInfo *) cl;
1314 PolygonType *polygon;
1315 POLYAREA *np;
1316 POLYAREA *merged;
1317 Cardinal i;
1319 /* don't subtract the object that was put back! */
1320 if (b == info->other)
1321 return 0;
1322 polygon = info->polygon;
1324 i = GetLayerNumber (info->data, info->layer);
1326 if (VIA_IS_BURIED (pin) && (!VIA_ON_LAYER (pin, i)))
1327 return 0;
1329 if (pin->Clearance == 0)
1330 return 0;
1331 if (TEST_THERM (i, pin))
1333 np = ThermPoly ((PCBType *) (info->data->pcb), pin, i);
1334 if (!np)
1335 return 1;
1337 else
1339 np = PinClearPoly (pin, PIN_SIZE (pin), pin->Clearance);
1340 if (!np)
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);
1352 return 1;
1355 static int
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)
1364 return 0;
1365 if (!TEST_FLAG (CLEARLINEFLAG, arc))
1366 return 0;
1367 polygon = info->polygon;
1368 if (SubtractArc (arc, polygon) < 0)
1369 longjmp (info->env, 1);
1370 return 1;
1373 static int
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)
1382 return 0;
1383 if (pad->Clearance == 0)
1384 return 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);
1390 return 1;
1392 return 0;
1395 static int
1396 line_sub_callback (const BoxType * b, void *cl)
1398 LineType *line = (LineType *) b;
1399 struct cpInfo *info = (struct cpInfo *) cl;
1400 PolygonType *polygon;
1401 POLYAREA *np;
1402 POLYAREA *merged;
1404 /* don't subtract the object that was put back! */
1405 if (b == info->other)
1406 return 0;
1407 if (!TEST_FLAG (CLEARLINEFLAG, line))
1408 return 0;
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);
1421 return 1;
1424 static int
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)
1433 return 0;
1434 if (!TEST_FLAG (CLEARLINEFLAG, text))
1435 return 0;
1436 polygon = info->polygon;
1437 if (SubtractText (text, polygon) < 0)
1438 longjmp (info->env, 1);
1439 return 1;
1442 static int
1443 Group (DataType *Data, Cardinal layer)
1445 Cardinal i, j;
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])
1449 return i;
1450 return i;
1453 static int
1454 clearPoly (DataType *Data, LayerType *Layer, PolygonType * polygon,
1455 const BoxType * here, Coord expand)
1457 int r = 0;
1458 BoxType region;
1459 struct cpInfo info;
1460 Cardinal group;
1462 if (!TEST_FLAG (CLEARPOLYFLAG, polygon)
1463 || GetLayerNumber (Data, Layer) >= max_copper_layer)
1464 return 0;
1465 group = Group (Data, GetLayerNumber (Data, Layer));
1466 info.bottom = (group == Group (Data, bottom_silk_layer));
1467 info.data = Data;
1468 info.other = here;
1469 info.layer = Layer;
1470 info.polygon = polygon;
1471 if (here)
1472 region = clip_box (here, &polygon->BoundingBox);
1473 else
1474 region = polygon->BoundingBox;
1475 region = bloat_box (&region, expand);
1477 if (setjmp (info.env) == 0)
1479 r = 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, &region, NULL, pad_sub_callback, &info);
1484 GROUP_LOOP (Data, group);
1486 r +=
1487 r_search (layer->line_tree, &region, NULL, line_sub_callback,
1488 &info);
1489 subtract_accumulated (&info, polygon);
1490 r +=
1491 r_search (layer->arc_tree, &region, NULL, arc_sub_callback, &info);
1492 r +=
1493 r_search (layer->text_tree, &region, NULL, text_sub_callback, &info);
1495 END_LOOP;
1496 r += r_search (Data->via_tree, &region, NULL, pin_sub_callback, &info);
1497 r += r_search (Data->pin_tree, &region, NULL, pin_sub_callback, &info);
1498 subtract_accumulated (&info, polygon);
1500 polygon->NoHolesValid = 0;
1501 return r;
1504 static int
1505 Unsubtract (POLYAREA * np1, PolygonType * p)
1507 POLYAREA *merged = NULL, *np = np1;
1508 POLYAREA *orig_poly, *clipped_np;
1509 int x;
1510 assert (np);
1511 assert (p && p->Clipped);
1513 orig_poly = original_poly (p);
1515 x = poly_Boolean_free (np, orig_poly, &clipped_np, PBO_ISECT);
1516 if (x != err_ok)
1518 fprintf (stderr, "Error while clipping PBO_ISECT: %d\n", x);
1519 poly_Free (&clipped_np);
1520 goto fail;
1523 x = poly_Boolean_free (p->Clipped, clipped_np, &merged, PBO_UNITE);
1524 if (x != err_ok)
1526 fprintf (stderr, "Error while clipping PBO_UNITE: %d\n", x);
1527 poly_Free (&merged);
1528 goto fail;
1530 p->Clipped = biggest (merged);
1531 /* HACK */ ghid_notify_polygon_changed (p);
1532 assert (!p->Clipped || poly_Valid (p->Clipped));
1533 return 1;
1535 fail:
1536 p->Clipped = NULL;
1537 /* HACK */ ghid_notify_polygon_changed (p);
1538 if (p->NoHoles) printf ("Just leaked in Unsubtract\n");
1539 p->NoHoles = NULL;
1540 return 0;
1543 static int
1544 UnsubtractPin (PinType * pin, LayerType * l, PolygonType * p)
1546 POLYAREA *np;
1548 /* overlap a bit to prevent gaps from rounding errors */
1549 np = BoxPolyBloated (&pin->BoundingBox, UNSUBTRACT_BLOAT);
1551 if (!np)
1552 return 0;
1553 if (!Unsubtract (np, p))
1554 return 0;
1555 clearPoly (PCB->Data, l, p, (const BoxType *) pin, 2 * UNSUBTRACT_BLOAT);
1556 return 1;
1559 static int
1560 UnsubtractArc (ArcType * arc, LayerType * l, PolygonType * p)
1562 POLYAREA *np;
1564 if (!TEST_FLAG (CLEARLINEFLAG, arc))
1565 return 0;
1567 /* overlap a bit to prevent gaps from rounding errors */
1568 np = BoxPolyBloated (&arc->BoundingBox, UNSUBTRACT_BLOAT);
1570 if (!np)
1571 return 0;
1572 if (!Unsubtract (np, p))
1573 return 0;
1574 clearPoly (PCB->Data, l, p, (const BoxType *) arc, 2 * UNSUBTRACT_BLOAT);
1575 return 1;
1578 static int
1579 UnsubtractLine (LineType * line, LayerType * l, PolygonType * p)
1581 POLYAREA *np;
1583 if (!TEST_FLAG (CLEARLINEFLAG, line))
1584 return 0;
1586 /* overlap a bit to prevent notches from rounding errors */
1587 np = BoxPolyBloated (&line->BoundingBox, UNSUBTRACT_BLOAT);
1589 if (!np)
1590 return 0;
1591 if (!Unsubtract (np, p))
1592 return 0;
1593 clearPoly (PCB->Data, l, p, (const BoxType *) line, 2 * UNSUBTRACT_BLOAT);
1594 return 1;
1597 static int
1598 UnsubtractText (TextType * text, LayerType * l, PolygonType * p)
1600 POLYAREA *np;
1602 if (!TEST_FLAG (CLEARLINEFLAG, text))
1603 return 0;
1605 /* overlap a bit to prevent notches from rounding errors */
1606 np = BoxPolyBloated (&text->BoundingBox, UNSUBTRACT_BLOAT);
1608 if (!np)
1609 return -1;
1610 if (!Unsubtract (np, p))
1611 return 0;
1612 clearPoly (PCB->Data, l, p, (const BoxType *) text, 2 * UNSUBTRACT_BLOAT);
1613 return 1;
1616 static int
1617 UnsubtractPad (PadType * pad, LayerType * l, PolygonType * p)
1619 POLYAREA *np;
1621 /* overlap a bit to prevent notches from rounding errors */
1622 np = BoxPolyBloated (&pad->BoundingBox, UNSUBTRACT_BLOAT);
1624 if (!np)
1625 return 0;
1626 if (!Unsubtract (np, p))
1627 return 0;
1628 clearPoly (PCB->Data, l, p, (const BoxType *) pad, 2 * UNSUBTRACT_BLOAT);
1629 return 1;
1632 static bool inhibit = false;
1635 InitClip (DataType *Data, LayerType *layer, PolygonType * p)
1637 if (inhibit)
1638 return 0;
1639 if (p->Clipped)
1640 poly_Free (&p->Clipped);
1641 p->Clipped = original_poly (p);
1642 /* HACK */ ghid_notify_polygon_changed (p);
1643 poly_FreeContours (&p->NoHoles);
1644 if (!p->Clipped)
1645 return 0;
1646 assert (poly_Valid (p->Clipped));
1647 if (TEST_FLAG (CLEARPOLYFLAG, p))
1648 clearPoly (Data, layer, p, NULL, 0);
1649 else
1650 p->NoHolesValid = 0;
1651 return 1;
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.
1662 bool
1663 RemoveExcessPolygonPoints (LayerType *Layer, PolygonType *Polygon)
1665 PointType *p;
1666 Cardinal n, prev, next;
1667 LineType line;
1668 bool changed = false;
1670 if (Undoing ())
1671 return (false);
1673 if (0) {
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];
1682 line.Thickness = 0;
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);
1688 changed = true;
1692 return (changed);
1696 * \brief .
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.
1701 Cardinal
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++)
1717 double u, dx, dy;
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);
1728 if (u < 0.0)
1729 { /* ptr1 is closest point */
1730 u = SQUARE (X - ptr1->X) + SQUARE (Y - ptr1->Y);
1732 else if (u > 1.0)
1733 { /* ptr2 is closest point */
1734 u = SQUARE (X - ptr2->X) + SQUARE (Y - ptr2->Y);
1736 else
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)
1743 mindistance = u;
1744 result = n;
1748 return (result);
1752 * \brief Go back to the previous point of the polygon.
1754 void
1755 GoToPreviousPoint (void)
1757 switch (Crosshair.AttachedPolygon.PointN)
1759 /* do nothing if mode has just been entered */
1760 case 0:
1761 break;
1763 /* reset number of points and 'LINE_MODE' state */
1764 case 1:
1765 Crosshair.AttachedPolygon.PointN = 0;
1766 Crosshair.AttachedLine.State = STATE_FIRST;
1767 addedLines = 0;
1768 break;
1770 /* back-up one point */
1771 default:
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;
1779 break;
1785 * \brief Close polygon if possible.
1787 void
1788 ClosePolygon (void)
1790 Cardinal n = Crosshair.AttachedPolygon.PointN;
1792 /* check number of points */
1793 if (n >= 3)
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))
1800 Coord dx, dy;
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))
1808 Message
1810 ("Cannot close polygon because 45 degree lines are requested.\n"));
1811 return;
1814 CopyAttachedPolygonToLayer ();
1815 Draw ();
1817 else
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
1823 * layer.
1825 void
1826 CopyAttachedPolygonToLayer (void)
1828 PolygonType *polygon;
1829 int saveID;
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;
1850 addedLines = 0;
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
1859 * for each hole.
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),
1866 void *user_data)
1868 POLYAREA *pa = polygon->Clipped;
1869 PLINE *pl;
1870 /* If this hole is so big the polygon doesn't exist, then it's not
1871 * really a hole.
1873 if (!pa)
1874 return 0;
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))
1880 continue;
1881 if (callback (pl, user_data))
1883 return 1;
1886 return 0;
1889 struct plow_info
1891 int type;
1892 void *ptr1, *ptr2;
1893 LayerType *layer;
1894 DataType *data;
1895 int (*callback) (DataType *, LayerType *, PolygonType *, int, void *,
1896 void *, void *);
1897 void *userdata;
1900 static int
1901 subtract_plow (DataType *Data, LayerType *Layer, PolygonType *Polygon,
1902 int type, void *ptr1, void *ptr2, void *userdata)
1904 PinType *via;
1905 int layer_n = GetLayerNumber (Data, Layer);
1907 if (!Polygon->Clipped)
1908 return 0;
1909 switch (type)
1911 case PIN_TYPE:
1912 SubtractPin (Data, (PinType *) ptr2, Layer, Polygon);
1913 Polygon->NoHolesValid = 0;
1914 return 1;
1915 case VIA_TYPE:
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;
1921 return 1;
1923 break;
1924 case LINE_TYPE:
1925 SubtractLine ((LineType *) ptr2, Polygon);
1926 Polygon->NoHolesValid = 0;
1927 return 1;
1928 case ARC_TYPE:
1929 SubtractArc ((ArcType *) ptr2, Polygon);
1930 Polygon->NoHolesValid = 0;
1931 return 1;
1932 case PAD_TYPE:
1933 SubtractPad ((PadType *) ptr2, Polygon);
1934 Polygon->NoHolesValid = 0;
1935 return 1;
1936 case TEXT_TYPE:
1937 SubtractText ((TextType *) ptr2, Polygon);
1938 Polygon->NoHolesValid = 0;
1939 return 1;
1941 return 0;
1944 static int
1945 add_plow (DataType *Data, LayerType *Layer, PolygonType *Polygon,
1946 int type, void *ptr1, void *ptr2, void *userdata)
1948 PinType *via;
1949 int layer_n = GetLayerNumber (Data, Layer);
1951 switch (type)
1953 case PIN_TYPE:
1954 UnsubtractPin ((PinType *) ptr2, Layer, Polygon);
1955 return 1;
1956 case VIA_TYPE:
1957 via = (PinType *) ptr2;
1958 if (!VIA_IS_BURIED (via) || VIA_ON_LAYER (via, layer_n))
1960 UnsubtractPin ((PinType *) ptr2, Layer, Polygon);
1961 return 1;
1963 break;
1964 case LINE_TYPE:
1965 UnsubtractLine ((LineType *) ptr2, Layer, Polygon);
1966 return 1;
1967 case ARC_TYPE:
1968 UnsubtractArc ((ArcType *) ptr2, Layer, Polygon);
1969 return 1;
1970 case PAD_TYPE:
1971 UnsubtractPad ((PadType *) ptr2, Layer, Polygon);
1972 return 1;
1973 case TEXT_TYPE:
1974 UnsubtractText ((TextType *) ptr2, Layer, Polygon);
1975 return 1;
1977 return 0;
1980 static int
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);
1992 return 0;
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),
2000 void *userdata)
2002 BoxType sb = ((PinType *) ptr2)->BoundingBox;
2003 int r = 0;
2004 struct plow_info info;
2006 info.type = type;
2007 info.ptr1 = ptr1;
2008 info.ptr2 = ptr2;
2009 info.data = Data;
2010 info.callback = call_back;
2011 info.userdata = userdata;
2012 switch (type)
2014 case PIN_TYPE:
2015 case VIA_TYPE:
2016 if (type == PIN_TYPE || ptr1 == ptr2 || ptr1 == NULL)
2018 LAYER_LOOP (Data, max_copper_layer);
2020 info.layer = layer;
2021 r +=
2022 r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
2024 END_LOOP;
2026 else
2028 GROUP_LOOP (Data, GetLayerGroupNumberByNumber (GetLayerNumber (Data,
2029 ((LayerType *) ptr1))));
2031 info.layer = layer;
2032 r +=
2033 r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
2035 END_LOOP;
2037 break;
2038 case LINE_TYPE:
2039 case ARC_TYPE:
2040 case TEXT_TYPE:
2041 /* the cast works equally well for lines and arcs */
2042 if (!TEST_FLAG (CLEARLINEFLAG, (LineType *) ptr2))
2043 return 0;
2044 /* silk doesn't plow */
2045 if (GetLayerNumber (Data, (LayerType *)ptr1) >= max_copper_layer)
2046 return 0;
2047 GROUP_LOOP (Data, GetLayerGroupNumberByNumber (GetLayerNumber (Data,
2048 ((LayerType *) ptr1))));
2050 info.layer = layer;
2051 r += r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
2053 END_LOOP;
2054 break;
2055 case PAD_TYPE:
2057 Cardinal group = GetLayerGroupNumberByNumber (
2058 TEST_FLAG (ONSOLDERFLAG, (PadType *) ptr2) ?
2059 bottom_silk_layer : top_silk_layer);
2060 GROUP_LOOP (Data, group);
2062 info.layer = layer;
2063 r +=
2064 r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
2066 END_LOOP;
2068 break;
2070 case ELEMENT_TYPE:
2072 PIN_LOOP ((ElementType *) ptr1);
2074 PlowsPolygon (Data, PIN_TYPE, ptr1, pin, call_back, userdata);
2076 END_LOOP;
2077 PAD_LOOP ((ElementType *) ptr1);
2079 PlowsPolygon (Data, PAD_TYPE, ptr1, pad, call_back, userdata);
2081 END_LOOP;
2083 break;
2085 return r;
2088 void
2089 RestoreToPolygon (DataType * Data, int type, void *ptr1, void *ptr2)
2091 if (!Data->polyClip)
2092 return;
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);
2109 else
2110 PlowsPolygon (Data, type, ptr1, ptr2, add_plow, NULL);
2113 void
2114 ClearFromPolygon (DataType * Data, int type, void *ptr1, void *ptr2)
2116 if (!Data->polyClip)
2117 return;
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);
2135 else
2136 PlowsPolygon (Data, type, ptr1, ptr2, subtract_plow, NULL);
2139 bool
2140 isects (POLYAREA * a, PolygonType *p, bool fr)
2142 POLYAREA *x;
2143 bool ans;
2144 ans = Touching (a, p->Clipped);
2145 /* argument may be register, so we must copy it */
2146 x = a;
2147 if (fr)
2148 poly_Free (&x);
2149 return ans;
2153 bool
2154 IsPointInPolygon (Coord X, Coord Y, Coord r, PolygonType *p)
2156 POLYAREA *c;
2157 Vector v;
2158 v[0] = X;
2159 v[1] = Y;
2160 if (poly_CheckInside (p->Clipped, v))
2161 return true;
2162 if (r < 1)
2163 return false;
2164 if (!(c = CirclePoly (X, Y, r, NULL)))
2165 return false;
2166 return isects (c, p, true);
2170 bool
2171 IsPointInPolygonIgnoreHoles (Coord X, Coord Y, PolygonType *p)
2173 Vector v;
2174 v[0] = X;
2175 v[1] = Y;
2176 return poly_InsideContour (p->Clipped->contours, v);
2179 bool
2180 IsRectangleInPolygon (Coord X1, Coord Y1, Coord X2, Coord Y2, PolygonType *p)
2182 POLYAREA *s;
2183 if (!
2184 (s = RectPoly (min (X1, X2), max (X1, X2), min (Y1, Y2), max (Y1, Y2))))
2185 return false;
2186 return isects (s, p, true);
2190 * \brief .
2192 * \note This function will free the passed POLYAREA.
2193 * It must only be passed a single POLYAREA (pa->f == pa->b == pa).
2195 static void
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);
2207 poly_Free (&pa);
2208 return;
2210 else
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,
2216 p->ymin, p->ymax);
2217 poly_AndSubtract_free (pa, poly2, &left, &right);
2218 if (left)
2220 POLYAREA *cur, *next;
2221 cur = left;
2224 next = cur->f;
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);
2231 if (right)
2233 POLYAREA *cur, *next;
2234 cur = right;
2237 next = cur->f;
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);
2247 void
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 */
2256 if (clip)
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)
2262 return;
2263 /* Now dice it up.
2264 * NB: Could be more than one piece (because of the clip above)
2266 cur = main_contour;
2269 next = cur->f;
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
2279 * polygons.
2281 bool
2282 MorphPolygon (LayerType *layer, PolygonType *poly)
2284 POLYAREA *p, *start;
2285 bool many = false;
2286 FlagType flags;
2288 if (!poly->Clipped || TEST_FLAG (LOCKFLAG, poly))
2289 return false;
2290 if (poly->Clipped->f == poly->Clipped)
2291 return false;
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);
2308 inhibit = true;
2311 VNODE *v;
2312 PolygonType *newone;
2314 if (p->contours->area > PCB->IsleArea)
2316 newone = CreateNewPolygon (layer, flags);
2317 if (!newone)
2318 return false;
2319 many = true;
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);
2335 else
2337 POLYAREA *t = p;
2339 p = p->f;
2340 poly_DelContour (&t->contours);
2341 free (t);
2344 while (p != start);
2345 inhibit = false;
2346 IncrementUndoSerialNumber ();
2347 return many;
2350 void debug_pline (PLINE *pl)
2352 VNODE *v;
2353 pcb_fprintf (stderr, "\txmin %#mS xmax %#mS ymin %#mS ymax %#mS\n",
2354 pl->xmin, pl->xmax, pl->ymin, pl->ymax);
2355 v = &pl->head;
2356 while (v)
2358 pcb_fprintf(stderr, "\t\tvnode: %#mD\n", v->point[0], v->point[1]);
2359 v = v->next;
2360 if (v == &pl->head)
2361 break;
2365 void
2366 debug_polyarea (POLYAREA *p)
2368 PLINE *pl;
2370 fprintf (stderr, "POLYAREA %p\n", p);
2371 for (pl=p->contours; pl; pl=pl->next)
2372 debug_pline (pl);
2375 void
2376 debug_polygon (PolygonType *p)
2378 Cardinal i;
2379 POLYAREA *pa;
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);
2383 if (p->HoleIndexN)
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]);
2389 else
2390 fprintf (stderr, "it has no holes\n");
2391 pa = p->Clipped;
2392 while (pa)
2394 debug_polyarea (pa);
2395 pa = pa->f;
2396 if (pa == p->Clipped)
2397 break;
2402 * \brief Convert a POLYAREA (and all linked POLYAREA) to raw PCB
2403 * polygons on the given layer.
2405 void
2406 PolyToPolygonsOnLayer (DataType *Destination, LayerType *Layer,
2407 POLYAREA *Input, FlagType Flags)
2409 PolygonType *Polygon;
2410 POLYAREA *pa;
2411 PLINE *pline;
2412 VNODE *node;
2413 bool outer;
2415 if (Input == NULL)
2416 return;
2418 pa = Input;
2421 Polygon = CreateNewPolygon (Layer, Flags);
2423 pline = pa->contours;
2424 outer = true;
2428 if (!outer)
2429 CreateNewHoleInPolygon (Polygon);
2430 outer = false;
2432 node = &pline->head;
2435 CreateNewPointInPolygon (Polygon, node->point[0],
2436 node->point[1],
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 {
2461 POLYAREA *poly;
2464 #define ROUTER_THICKNESS MIL_TO_COORD (10)
2465 //#define ROUTER_THICKNESS MIL_TO_COORD (0.1)
2467 static int
2468 arc_outline_callback (const BoxType * b, void *cl)
2470 ArcType *arc = (ArcType *)b;
2471 struct clip_outline_info *info = cl;
2472 POLYAREA *np, *res;
2473 char *feature_name;
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)))
2479 #else
2480 if (!(np = ArcPoly (arc, ROUTER_THICKNESS, feature_name)))
2481 #endif
2482 return 0;
2484 #ifdef DEBUG_CIRCSEGS
2485 poly_Boolean_free (info->poly, np, &res, PBO_UNITE);
2486 #else
2487 poly_Boolean_free (info->poly, np, &res, PBO_SUB);
2488 #endif
2489 info->poly = res;
2491 return 1;
2494 static int
2495 line_outline_callback (const BoxType * b, void *cl)
2497 LineType *line = (LineType *)b;
2498 struct clip_outline_info *info = cl;
2499 POLYAREA *np, *res;
2500 char *feature_name;
2502 feature_name = STRDUP(line->Number);
2504 #ifdef DEBUG_CIRCSEGS
2505 if (!(np = LinePoly (line, line->Thickness, feature_name)))
2506 #else
2507 if (!(np = LinePoly (line, ROUTER_THICKNESS, feature_name)))
2508 #endif
2509 return 0;
2511 #ifdef DEBUG_CIRCSEGS
2512 poly_Boolean_free (info->poly, np, &res, PBO_UNITE);
2513 #else
2514 poly_Boolean_free (info->poly, np, &res, PBO_SUB);
2515 #endif
2516 info->poly = res;
2518 return 1;
2521 static int
2522 pv_outline_callback (const BoxType * b, void *cl)
2524 PinType *pv = (PinType *)b;
2525 struct clip_outline_info *info = cl;
2526 POLYAREA *np, *res;
2527 char *feature_name;
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
2537 // char *tmp;
2539 // feature_name = tmp = malloc (strlen (element_name) + 1 + strlen (pin_number) + 1);
2540 // tmp = stpcpy (tmp, element_name);
2541 // *(tmp++) = '-';
2542 // tmp = stpcpy (tmp, pin_number);
2544 else
2546 feature_name = NULL;
2549 else
2551 feature_name = STRDUP(pv->Name);
2554 #ifdef DEBUG_CIRCSEGS
2555 if (!(np = CirclePoly (pv->X, pv->Y, pv->Thickness / 2, feature_name)))
2556 #else
2557 if (!(np = CirclePoly (pv->X, pv->Y, pv->DrillingHole / 2, feature_name)))
2558 #endif
2559 return 0;
2561 #ifdef DEBUG_CIRCSEGS
2562 poly_Boolean_free (info->poly, np, &res, PBO_UNITE);
2563 #else
2564 poly_Boolean_free (info->poly, np, &res, PBO_SUB);
2565 #endif
2566 info->poly = res;
2568 return 1;
2571 static int
2572 polygon_outline_callback (const BoxType * b, void *cl)
2574 PolygonType *poly = (PolygonType *)b;
2575 struct clip_outline_info *info = cl;
2576 POLYAREA *np, *res;
2578 if (!(np = original_poly (poly)))
2579 return 0;
2581 #ifdef DEBUG_CIRCSEGS
2582 poly_Boolean_free (info->poly, np, &res, PBO_UNITE);
2583 #else
2584 poly_Boolean_free (info->poly, np, &res, PBO_SUB);
2585 #endif
2586 info->poly = res;
2588 return 1;
2591 static void
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 */
2598 if (*res == piece)
2599 *res = (*res)->f;
2601 /* But reset it to NULL if it wraps around and hits us again */
2602 if (*res == piece)
2603 *res = NULL;
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;
2615 poly_Free (&piece);
2618 POLYAREA *board_outline_poly (bool include_holes)
2620 int i;
2621 int count;
2622 int found_outline = 0;
2623 LayerType *Layer = NULL;
2624 BoxType region;
2625 struct clip_outline_info info;
2626 POLYAREA *whole_world;
2627 POLYAREA *clipped;
2628 POLYAREA *piece;
2629 POLYAREA *check;
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)
2645 found_outline = 1;
2646 break;
2650 if (!found_outline)
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.
2666 * _____________
2667 * / ___________ \
2668 * || ||
2669 * || //=\\ ||
2670 * || || || ||
2671 * || \\=// ||
2672 * ||___________||
2673 * \_____________/
2676 region.X1 = 0;
2677 region.Y1 = 0;
2678 region.X2 = PCB->MaxWidth;
2679 region.Y2 = PCB->MaxHeight;
2681 #if 1
2682 #ifdef DEBUG_CIRCSEGS
2683 info.poly = NULL;
2684 #else
2685 info.poly = whole_world;
2686 #endif
2688 if (found_outline)
2690 r_search (Layer->line_tree, &region, NULL, line_outline_callback, &info);
2691 r_search (Layer->arc_tree, &region, NULL, arc_outline_callback, &info);
2694 #ifndef DEBUG_CIRCSEGS
2695 if (include_holes)
2696 #endif
2698 r_search (PCB->Data->pin_tree, &region, NULL, pv_outline_callback, &info);
2699 r_search (PCB->Data->via_tree, &region, NULL, pv_outline_callback, &info);
2702 clipped = info.poly;
2704 #ifdef DEBUG_CIRCSEGS
2705 return clipped;
2706 #endif
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)
2713 return 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!
2719 piece = clipped;
2720 do { /* LOOP OVER POLYGON PIECES */
2722 if (piece->contours == NULL)
2723 printf ("WTF?\n");
2725 count = 0;
2726 check = clipped;
2727 do { /* LOOP OVER POLYGON PIECES */
2728 if (check == piece)
2729 continue;
2730 if (poly_ContourInContour (check->contours, piece->contours))
2731 count ++;
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;
2737 else
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);
2753 #endif
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, &region, NULL, polygon_outline_callback, &info);
2763 clipped = info.poly;
2765 if (clipped == NULL)
2766 return whole_world;
2767 else
2768 poly_Free (&whole_world);
2769 #endif
2771 return clipped;