Add cache for board-outline
[geda-pcb/pcjc2.git] / src / polygon.c
blob205a1a3f44f314a9216b01f4154123ac9dcc857e
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996,2010 Thomas Nau
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 * Contact addresses for paper mail and Email:
22 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
23 * Thomas.Nau@rz.uni-ulm.de
30 Here's a brief tour of the data and life of a polygon, courtesy of Ben
31 Jackson:
33 A PCB PolygonType contains an array of points outlining the polygon.
34 This is what is manipulated by the UI and stored in the saved PCB.
36 A PolygonType also contains a POLYAREA called 'Clipped' which is
37 computed dynamically by InitClip every time a board is loaded. The
38 point array is coverted to a POLYAREA by original_poly and then holes
39 are cut in it by clearPoly. After that it is maintained dynamically
40 as parts are added, moved or removed (this is why sometimes bugs can
41 be fixed by just re-loading the board).
43 A POLYAREA consists of a linked list of PLINE structures. The head of
44 that list is POLYAREA.contours. The first contour is an outline of a
45 filled region. All of the subsequent PLINEs are holes cut out of that
46 first contour. POLYAREAs are in a doubly-linked list and each member
47 of the list is an independent (non-overlapping) area with its own
48 outline and holes. The function biggest() finds the largest POLYAREA
49 so that PolygonType.Clipped points to that shape. The rest of the
50 polygon still exists, it's just ignored when turning the polygon into
51 copper.
53 The first POLYAREA in PolygonType.Clipped is what is used for the vast
54 majority of Polygon related tests. The basic logic for an
55 intersection is "is the target shape inside POLYAREA.contours and NOT
56 fully enclosed in any of POLYAREA.contours.next... (the holes)".
58 The polygon dicer (NoHolesPolygonDicer and r_NoHolesPolygonDicer)
59 emits a series of "simple" PLINE shapes. That is, the PLINE isn't
60 linked to any other "holes" oulines). That's the meaning of the first
61 test in r_NoHolesPolygonDicer. It is testing to see if the PLINE
62 contour (the first, making it a solid outline) has a valid next
63 pointer (which would point to one or more holes). The dicer works by
64 recursively chopping the polygon in half through the first hole it
65 sees (which is guaranteed to eliminate at least that one hole). The
66 dicer output is used for HIDs which cannot render things with holes
67 (which would require erasure).
71 /* special polygon editing routines
74 #ifdef HAVE_CONFIG_H
75 #include "config.h"
76 #endif
78 #include <assert.h>
79 #include <math.h>
80 #include <memory.h>
81 #include <setjmp.h>
82 #include <glib.h>
84 #include "global.h"
85 #include "box.h"
86 #include "create.h"
87 #include "crosshair.h"
88 #include "data.h"
89 #include "draw.h"
90 #include "error.h"
91 #include "find.h"
92 #include "misc.h"
93 #include "move.h"
94 #include "pcb-printf.h"
95 #include "polygon.h"
96 #include "remove.h"
97 #include "rtree.h"
98 #include "search.h"
99 #include "set.h"
100 #include "thermal.h"
101 #include "undo.h"
103 #ifdef HAVE_LIBDMALLOC
104 #include <dmalloc.h>
105 #endif
107 #define ROUND(x) ((long)(((x) >= 0 ? (x) + 0.5 : (x) - 0.5)))
109 #define UNSUBTRACT_BLOAT 10
110 #define SUBTRACT_PIN_VIA_BATCH_SIZE 100
111 #define SUBTRACT_LINE_BATCH_SIZE 20
113 static double rotate_circle_seg[4];
115 void
116 polygon_init (void)
118 double cos_ang = cos (2.0 * M_PI / POLY_CIRC_SEGS_F);
119 double sin_ang = sin (2.0 * M_PI / POLY_CIRC_SEGS_F);
121 rotate_circle_seg[0] = cos_ang; rotate_circle_seg[1] = -sin_ang;
122 rotate_circle_seg[2] = sin_ang; rotate_circle_seg[3] = cos_ang;
125 Cardinal
126 polygon_point_idx (PolygonType *polygon, PointType *point)
128 assert (point >= polygon->Points);
129 assert (point <= polygon->Points + polygon->PointN);
130 return ((char *)point - (char *)polygon->Points) / sizeof (PointType);
133 /* Find contour number: 0 for outer, 1 for first hole etc.. */
134 Cardinal
135 polygon_point_contour (PolygonType *polygon, Cardinal point)
137 Cardinal i;
138 Cardinal contour = 0;
140 for (i = 0; i < polygon->HoleIndexN; i++)
141 if (point >= polygon->HoleIndex[i])
142 contour = i + 1;
143 return contour;
146 Cardinal
147 next_contour_point (PolygonType *polygon, Cardinal point)
149 Cardinal contour;
150 Cardinal this_contour_start;
151 Cardinal next_contour_start;
153 contour = polygon_point_contour (polygon, point);
155 this_contour_start = (contour == 0) ? 0 :
156 polygon->HoleIndex[contour - 1];
157 next_contour_start =
158 (contour == polygon->HoleIndexN) ? polygon->PointN :
159 polygon->HoleIndex[contour];
161 /* Wrap back to the start of the contour we're in if we pass the end */
162 if (++point == next_contour_start)
163 point = this_contour_start;
165 return point;
168 Cardinal
169 prev_contour_point (PolygonType *polygon, Cardinal point)
171 Cardinal contour;
172 Cardinal prev_contour_end;
173 Cardinal this_contour_end;
175 contour = polygon_point_contour (polygon, point);
177 prev_contour_end = (contour == 0) ? 0 :
178 polygon->HoleIndex[contour - 1];
179 this_contour_end =
180 (contour == polygon->HoleIndexN) ? polygon->PointN - 1:
181 polygon->HoleIndex[contour] - 1;
183 /* Wrap back to the start of the contour we're in if we pass the end */
184 if (point == prev_contour_end)
185 point = this_contour_end;
186 else
187 point--;
189 return point;
192 static void
193 add_noholes_polyarea (PLINE *pline, void *user_data)
195 PolygonType *poly = (PolygonType *)user_data;
197 /* Prepend the pline into the NoHoles linked list */
198 pline->next = poly->NoHoles;
199 poly->NoHoles = pline;
202 void
203 ComputeNoHoles (PolygonType *poly)
205 poly_FreeContours (&poly->NoHoles);
206 if (poly->Clipped)
207 NoHolesPolygonDicer (poly, NULL, add_noholes_polyarea, poly);
208 else
209 printf ("Compute_noholes caught poly->Clipped = NULL\n");
210 poly->NoHolesValid = 1;
213 static POLYAREA *
214 biggest (POLYAREA * p)
216 POLYAREA *n, *top = NULL;
217 PLINE *pl;
218 rtree_t *tree;
219 double big = -1;
220 if (!p)
221 return NULL;
222 n = p;
225 #if 0
226 if (n->contours->area < PCB->IsleArea)
228 n->b->f = n->f;
229 n->f->b = n->b;
230 poly_DelContour (&n->contours);
231 if (n == p)
232 p = n->f;
233 n = n->f;
234 if (!n->contours)
236 free (n);
237 return NULL;
240 #endif
241 if (n->contours->area > big)
243 top = n;
244 big = n->contours->area;
247 while ((n = n->f) != p);
248 assert (top);
249 if (top == p)
250 return p;
251 pl = top->contours;
252 tree = top->contour_tree;
253 top->contours = p->contours;
254 top->contour_tree = p->contour_tree;
255 p->contours = pl;
256 p->contour_tree = tree;
257 assert (pl);
258 assert (p->f);
259 assert (p->b);
260 return p;
263 POLYAREA *
264 ContourToPoly (PLINE * contour)
266 POLYAREA *p;
267 poly_PreContour (contour, TRUE);
268 assert (contour->Flags.orient == PLF_DIR);
269 if (!(p = poly_Create ()))
270 return NULL;
271 poly_InclContour (p, contour);
272 assert (poly_Valid (p));
273 return p;
276 static POLYAREA *
277 original_poly (PolygonType * p)
279 PLINE *contour = NULL;
280 POLYAREA *np = NULL;
281 Cardinal n;
282 Vector v;
283 int hole = 0;
285 if ((np = poly_Create ()) == NULL)
286 return NULL;
288 /* first make initial polygon contour */
289 for (n = 0; n < p->PointN; n++)
291 /* No current contour? Make a new one starting at point */
292 /* (or) Add point to existing contour */
294 v[0] = p->Points[n].X;
295 v[1] = p->Points[n].Y;
296 if (contour == NULL)
298 if ((contour = poly_NewContour (v)) == NULL)
299 return NULL;
301 else
303 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
306 /* Is current point last in contour? If so process it. */
307 if (n == p->PointN - 1 ||
308 (hole < p->HoleIndexN && n == p->HoleIndex[hole] - 1))
310 poly_PreContour (contour, TRUE);
312 /* make sure it is a positive contour (outer) or negative (hole) */
313 if (contour->Flags.orient != (hole ? PLF_INV : PLF_DIR))
314 poly_InvContour (contour);
315 assert (contour->Flags.orient == (hole ? PLF_INV : PLF_DIR));
317 poly_InclContour (np, contour);
318 contour = NULL;
319 assert (poly_Valid (np));
321 hole++;
324 return biggest (np);
327 POLYAREA *
328 PolygonToPoly (PolygonType *p)
330 return original_poly (p);
333 POLYAREA *
334 RectPoly (Coord x1, Coord x2, Coord y1, Coord y2)
336 PLINE *contour = NULL;
337 Vector v;
339 /* Return NULL for zero or negatively sized rectangles */
340 if (x2 <= x1 || y2 <= y1)
341 return NULL;
343 v[0] = x1;
344 v[1] = y1;
345 if ((contour = poly_NewContour (v)) == NULL)
346 return NULL;
347 v[0] = x2;
348 v[1] = y1;
349 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
350 v[0] = x2;
351 v[1] = y2;
352 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
353 v[0] = x1;
354 v[1] = y2;
355 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
356 return ContourToPoly (contour);
359 POLYAREA *
360 OctagonPoly (Coord x, Coord y, Coord radius)
362 PLINE *contour = NULL;
363 Vector v;
365 v[0] = x + ROUND (radius * 0.5);
366 v[1] = y + ROUND (radius * TAN_22_5_DEGREE_2);
367 if ((contour = poly_NewContour (v)) == NULL)
368 return NULL;
369 v[0] = x + ROUND (radius * TAN_22_5_DEGREE_2);
370 v[1] = y + ROUND (radius * 0.5);
371 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
372 v[0] = x - (v[0] - x);
373 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
374 v[0] = x - ROUND (radius * 0.5);
375 v[1] = y + ROUND (radius * TAN_22_5_DEGREE_2);
376 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
377 v[1] = y - (v[1] - y);
378 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
379 v[0] = x - ROUND (radius * TAN_22_5_DEGREE_2);
380 v[1] = y - ROUND (radius * 0.5);
381 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
382 v[0] = x - (v[0] - x);
383 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
384 v[0] = x + ROUND (radius * 0.5);
385 v[1] = y - ROUND (radius * TAN_22_5_DEGREE_2);
386 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
387 return ContourToPoly (contour);
390 /* add verticies in a fractional-circle starting from v
391 * centered at X, Y and going counter-clockwise
392 * does not include the first point
393 * last argument is 1 for a full circle
394 * 2 for a half circle
395 * or 4 for a quarter circle
397 void
398 frac_circle (PLINE * c, Coord X, Coord Y, Vector v, int fraction)
400 double e1, e2, t1;
401 int i, range;
403 poly_InclVertex (c->head.prev, poly_CreateNode (v));
404 /* move vector to origin */
405 e1 = (v[0] - X) * POLY_CIRC_RADIUS_ADJ;
406 e2 = (v[1] - Y) * POLY_CIRC_RADIUS_ADJ;
408 /* NB: the caller adds the last vertex, hence the -1 */
409 range = POLY_CIRC_SEGS / fraction - 1;
410 for (i = 0; i < range; i++)
412 /* rotate the vector */
413 t1 = rotate_circle_seg[0] * e1 + rotate_circle_seg[1] * e2;
414 e2 = rotate_circle_seg[2] * e1 + rotate_circle_seg[3] * e2;
415 e1 = t1;
416 v[0] = X + ROUND (e1);
417 v[1] = Y + ROUND (e2);
418 poly_InclVertex (c->head.prev, poly_CreateNode (v));
422 /* create a circle approximation from lines */
423 POLYAREA *
424 CirclePoly (Coord x, Coord y, Coord radius)
426 PLINE *contour;
427 Vector v;
429 if (radius <= 0)
430 return NULL;
431 v[0] = x + radius;
432 v[1] = y;
433 if ((contour = poly_NewContour (v)) == NULL)
434 return NULL;
435 frac_circle (contour, x, y, v, 1);
436 contour->is_round = TRUE;
437 contour->cx = x;
438 contour->cy = y;
439 contour->radius = radius;
440 return ContourToPoly (contour);
443 /* make a rounded-corner rectangle with radius t beyond x1,x2,y1,y2 rectangle */
444 POLYAREA *
445 RoundRect (Coord x1, Coord x2, Coord y1, Coord y2, Coord t)
447 PLINE *contour = NULL;
448 Vector v;
450 assert (x2 > x1);
451 assert (y2 > y1);
452 v[0] = x1 - t;
453 v[1] = y1;
454 if ((contour = poly_NewContour (v)) == NULL)
455 return NULL;
456 frac_circle (contour, x1, y1, v, 4);
457 v[0] = x2;
458 v[1] = y1 - t;
459 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
460 frac_circle (contour, x2, y1, v, 4);
461 v[0] = x2 + t;
462 v[1] = y2;
463 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
464 frac_circle (contour, x2, y2, v, 4);
465 v[0] = x1;
466 v[1] = y2 + t;
467 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
468 frac_circle (contour, x1, y2, v, 4);
469 return ContourToPoly (contour);
472 #define ARC_ANGLE 5
473 static POLYAREA *
474 ArcPolyNoIntersect (ArcType * a, Coord thick)
476 PLINE *contour = NULL;
477 POLYAREA *np = NULL;
478 Vector v;
479 BoxType *ends;
480 int i, segs;
481 double ang, da, rx, ry;
482 long half;
483 double radius_adj;
485 if (thick <= 0)
486 return NULL;
487 if (a->Delta < 0)
489 a->StartAngle += a->Delta;
490 a->Delta = -a->Delta;
492 half = (thick + 1) / 2;
493 ends = GetArcEnds (a);
494 /* start with inner radius */
495 rx = MAX (a->Width - half, 0);
496 ry = MAX (a->Height - half, 0);
497 segs = 1;
498 if(thick > 0)
499 segs = MAX (segs, a->Delta * M_PI / 360 *
500 sqrt (sqrt ((double)rx * rx + (double)ry * ry) /
501 POLY_ARC_MAX_DEVIATION / 2 / thick));
502 segs = MAX(segs, a->Delta / ARC_ANGLE);
504 ang = a->StartAngle;
505 da = (1.0 * a->Delta) / segs;
506 radius_adj = (M_PI*da/360)*(M_PI*da/360)/2;
507 v[0] = a->X - rx * cos (ang * M180);
508 v[1] = a->Y + ry * sin (ang * M180);
509 if ((contour = poly_NewContour (v)) == NULL)
510 return 0;
511 for (i = 0; i < segs - 1; i++)
513 ang += da;
514 v[0] = a->X - rx * cos (ang * M180);
515 v[1] = a->Y + ry * sin (ang * M180);
516 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
518 /* find last point */
519 ang = a->StartAngle + a->Delta;
520 v[0] = a->X - rx * cos (ang * M180) * (1 - radius_adj);
521 v[1] = a->Y + ry * sin (ang * M180) * (1 - radius_adj);
522 /* add the round cap at the end */
523 frac_circle (contour, ends->X2, ends->Y2, v, 2);
524 /* and now do the outer arc (going backwards) */
525 rx = (a->Width + half) * (1+radius_adj);
526 ry = (a->Width + half) * (1+radius_adj);
527 da = -da;
528 for (i = 0; i < segs; i++)
530 v[0] = a->X - rx * cos (ang * M180);
531 v[1] = a->Y + ry * sin (ang * M180);
532 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
533 ang += da;
535 /* now add other round cap */
536 ang = a->StartAngle;
537 v[0] = a->X - rx * cos (ang * M180) * (1 - radius_adj);
538 v[1] = a->Y + ry * sin (ang * M180) * (1 - radius_adj);
539 frac_circle (contour, ends->X1, ends->Y1, v, 2);
540 /* now we have the whole contour */
541 if (!(np = ContourToPoly (contour)))
542 return NULL;
543 return np;
546 #define MIN_CLEARANCE_BEFORE_BISECT 10.
547 POLYAREA *
548 ArcPoly (ArcType * a, Coord thick)
550 double delta;
551 ArcType seg1, seg2;
552 POLYAREA *tmp1, *tmp2, *res;
554 delta = (a->Delta < 0) ? -a->Delta : a->Delta;
556 /* If the arc segment would self-intersect, we need to construct it as the union of
557 two non-intersecting segments */
559 if (2 * M_PI * a->Width * (1. - (double)delta / 360.) - thick < MIN_CLEARANCE_BEFORE_BISECT)
561 int half_delta = a->Delta / 2;
563 seg1 = seg2 = *a;
564 seg1.Delta = half_delta;
565 seg2.Delta -= half_delta;
566 seg2.StartAngle += half_delta;
568 tmp1 = ArcPolyNoIntersect (&seg1, thick);
569 tmp2 = ArcPolyNoIntersect (&seg2, thick);
570 poly_Boolean_free (tmp1, tmp2, &res, PBO_UNITE);
571 return res;
574 return ArcPolyNoIntersect (a, thick);
577 POLYAREA *
578 LinePoly (LineType * L, Coord thick)
580 PLINE *contour = NULL;
581 POLYAREA *np = NULL;
582 Vector v;
583 double d, dx, dy;
584 long half;LineType _l=*L,*l=&_l;
586 if (thick <= 0)
587 return NULL;
588 half = (thick + 1) / 2;
590 sqrt (SQUARE (l->Point1.X - l->Point2.X) +
591 SQUARE (l->Point1.Y - l->Point2.Y));
592 if (!TEST_FLAG (SQUAREFLAG,l))
593 if (d == 0) /* line is a point */
594 return CirclePoly (l->Point1.X, l->Point1.Y, half);
595 if (d != 0)
597 d = half / d;
598 dx = (l->Point1.Y - l->Point2.Y) * d;
599 dy = (l->Point2.X - l->Point1.X) * d;
601 else
603 dx = half;
604 dy = 0;
606 if (TEST_FLAG (SQUAREFLAG,l))/* take into account the ends */
608 l->Point1.X -= dy;
609 l->Point1.Y += dx;
610 l->Point2.X += dy;
611 l->Point2.Y -= dx;
613 v[0] = l->Point1.X - dx;
614 v[1] = l->Point1.Y - dy;
615 if ((contour = poly_NewContour (v)) == NULL)
616 return 0;
617 v[0] = l->Point2.X - dx;
618 v[1] = l->Point2.Y - dy;
619 if (TEST_FLAG (SQUAREFLAG,l))
620 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
621 else
622 frac_circle (contour, l->Point2.X, l->Point2.Y, v, 2);
623 v[0] = l->Point2.X + dx;
624 v[1] = l->Point2.Y + dy;
625 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
626 v[0] = l->Point1.X + dx;
627 v[1] = l->Point1.Y + dy;
628 if (TEST_FLAG (SQUAREFLAG,l))
629 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
630 else
631 frac_circle (contour, l->Point1.X, l->Point1.Y, v, 2);
632 /* now we have the line contour */
633 if (!(np = ContourToPoly (contour)))
634 return NULL;
635 return np;
638 /* make a rounded-corner rectangle */
639 POLYAREA *
640 SquarePadPoly (PadType * pad, Coord clear)
642 PLINE *contour = NULL;
643 POLYAREA *np = NULL;
644 Vector v;
645 double d;
646 double tx, ty;
647 double cx, cy;
648 PadType _t=*pad,*t=&_t;
649 PadType _c=*pad,*c=&_c;
650 int halfthick = (pad->Thickness + 1) / 2;
651 int halfclear = (clear + 1) / 2;
654 sqrt (SQUARE (pad->Point1.X - pad->Point2.X) +
655 SQUARE (pad->Point1.Y - pad->Point2.Y));
656 if (d != 0)
658 double a = halfthick / d;
659 tx = (t->Point1.Y - t->Point2.Y) * a;
660 ty = (t->Point2.X - t->Point1.X) * a;
661 a = halfclear / d;
662 cx = (c->Point1.Y - c->Point2.Y) * a;
663 cy = (c->Point2.X - c->Point1.X) * a;
665 t->Point1.X -= ty;
666 t->Point1.Y += tx;
667 t->Point2.X += ty;
668 t->Point2.Y -= tx;
669 c->Point1.X -= cy;
670 c->Point1.Y += cx;
671 c->Point2.X += cy;
672 c->Point2.Y -= cx;
674 else
676 tx = halfthick;
677 ty = 0;
678 cx = halfclear;
679 cy = 0;
681 t->Point1.Y += tx;
682 t->Point2.Y -= tx;
683 c->Point1.Y += cx;
684 c->Point2.Y -= cx;
687 v[0] = c->Point1.X - tx;
688 v[1] = c->Point1.Y - ty;
689 if ((contour = poly_NewContour (v)) == NULL)
690 return 0;
691 frac_circle (contour, (t->Point1.X - tx), (t->Point1.Y - ty), v, 4);
693 v[0] = t->Point2.X - cx;
694 v[1] = t->Point2.Y - cy;
695 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
696 frac_circle (contour, (t->Point2.X - tx), (t->Point2.Y - ty), v, 4);
698 v[0] = c->Point2.X + tx;
699 v[1] = c->Point2.Y + ty;
700 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
701 frac_circle (contour, (t->Point2.X + tx), (t->Point2.Y + ty), v, 4);
703 v[0] = t->Point1.X + cx;
704 v[1] = t->Point1.Y + cy;
705 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
706 frac_circle (contour, (t->Point1.X + tx), (t->Point1.Y + ty), v, 4);
708 /* now we have the line contour */
709 if (!(np = ContourToPoly (contour)))
710 return NULL;
711 return np;
714 /* clear np1 from the polygon */
715 static int
716 Subtract (POLYAREA * np1, PolygonType * p, bool fnp)
718 POLYAREA *merged = NULL, *np = np1;
719 int x;
720 assert (np);
721 assert (p);
722 if (!p->Clipped)
724 if (fnp)
725 poly_Free (&np);
726 return 1;
728 assert (poly_Valid (p->Clipped));
729 assert (poly_Valid (np));
730 if (fnp)
731 x = poly_Boolean_free (p->Clipped, np, &merged, PBO_SUB);
732 else
734 x = poly_Boolean (p->Clipped, np, &merged, PBO_SUB);
735 poly_Free (&p->Clipped);
737 assert (!merged || poly_Valid (merged));
738 if (x != err_ok)
740 fprintf (stderr, "Error while clipping PBO_SUB: %d\n", x);
741 poly_Free (&merged);
742 p->Clipped = NULL;
743 if (p->NoHoles) printf ("Just leaked in Subtract\n");
744 p->NoHoles = NULL;
745 return -1;
747 p->Clipped = biggest (merged);
748 assert (!p->Clipped || poly_Valid (p->Clipped));
749 if (!p->Clipped)
750 Message ("Polygon cleared out of existence near (%d, %d)\n",
751 (p->BoundingBox.X1 + p->BoundingBox.X2) / 2,
752 (p->BoundingBox.Y1 + p->BoundingBox.Y2) / 2);
753 return 1;
756 /* create a polygon of the pin clearance */
757 POLYAREA *
758 PinPoly (PinType * pin, Coord thick, Coord clear)
760 int size;
762 if (TEST_FLAG (SQUAREFLAG, pin))
764 size = (thick + 1) / 2;
765 return RoundRect (pin->X - size, pin->X + size, pin->Y - size,
766 pin->Y + size, (clear + 1) / 2);
768 else
770 size = (thick + clear + 1) / 2;
771 if (TEST_FLAG (OCTAGONFLAG, pin))
773 return OctagonPoly (pin->X, pin->Y, size + size);
776 return CirclePoly (pin->X, pin->Y, size);
779 POLYAREA *
780 BoxPolyBloated (BoxType *box, Coord bloat)
782 return RectPoly (box->X1 - bloat, box->X2 + bloat,
783 box->Y1 - bloat, box->Y2 + bloat);
786 /* remove the pin clearance from the polygon */
787 static int
788 SubtractPin (DataType * d, PinType * pin, LayerType * l, PolygonType * p)
790 POLYAREA *np;
791 Cardinal i;
793 if (pin->Clearance == 0)
794 return 0;
795 i = GetLayerNumber (d, l);
796 if (TEST_THERM (i, pin))
798 np = ThermPoly ((PCBType *) (d->pcb), pin, i);
799 if (!np)
800 return 0;
802 else
804 np = PinPoly (pin, PIN_SIZE (pin), pin->Clearance);
805 if (!np)
806 return -1;
808 return Subtract (np, p, TRUE);
811 static int
812 SubtractLine (LineType * line, PolygonType * p)
814 POLYAREA *np;
816 if (!TEST_FLAG (CLEARLINEFLAG, line))
817 return 0;
818 if (!(np = LinePoly (line, line->Thickness + line->Clearance)))
819 return -1;
820 return Subtract (np, p, true);
823 static int
824 SubtractArc (ArcType * arc, PolygonType * p)
826 POLYAREA *np;
828 if (!TEST_FLAG (CLEARLINEFLAG, arc))
829 return 0;
830 if (!(np = ArcPoly (arc, arc->Thickness + arc->Clearance)))
831 return -1;
832 return Subtract (np, p, true);
835 static int
836 SubtractText (TextType * text, PolygonType * p)
838 POLYAREA *np;
839 const BoxType *b = &text->BoundingBox;
841 if (!TEST_FLAG (CLEARLINEFLAG, text))
842 return 0;
843 if (!(np = RoundRect (b->X1 + PCB->Bloat, b->X2 - PCB->Bloat,
844 b->Y1 + PCB->Bloat, b->Y2 - PCB->Bloat, PCB->Bloat)))
845 return -1;
846 return Subtract (np, p, true);
849 static int
850 SubtractPad (PadType * pad, PolygonType * p)
852 POLYAREA *np = NULL;
854 if (pad->Clearance == 0)
855 return 0;
856 if (TEST_FLAG (SQUAREFLAG, pad))
858 if (!
859 (np = SquarePadPoly (pad, pad->Thickness + pad->Clearance)))
860 return -1;
862 else
864 if (!
865 (np = LinePoly ((LineType *) pad, pad->Thickness + pad->Clearance)))
866 return -1;
868 return Subtract (np, p, true);
871 struct cpInfo
873 const BoxType *other;
874 DataType *data;
875 LayerType *layer;
876 PolygonType *polygon;
877 bool bottom;
878 POLYAREA *accumulate;
879 int batch_size;
880 jmp_buf env;
883 static void
884 subtract_accumulated (struct cpInfo *info, PolygonType *polygon)
886 if (info->accumulate == NULL)
887 return;
888 Subtract (info->accumulate, polygon, true);
889 info->accumulate = NULL;
890 info->batch_size = 0;
893 static int
894 pin_sub_callback (const BoxType * b, void *cl)
896 PinType *pin = (PinType *) b;
897 struct cpInfo *info = (struct cpInfo *) cl;
898 PolygonType *polygon;
899 POLYAREA *np;
900 POLYAREA *merged;
901 Cardinal i;
903 /* don't subtract the object that was put back! */
904 if (b == info->other)
905 return 0;
906 polygon = info->polygon;
908 if (pin->Clearance == 0)
909 return 0;
910 i = GetLayerNumber (info->data, info->layer);
911 if (TEST_THERM (i, pin))
913 np = ThermPoly ((PCBType *) (info->data->pcb), pin, i);
914 if (!np)
915 return 1;
917 else
919 np = PinPoly (pin, PIN_SIZE (pin), pin->Clearance);
920 if (!np)
921 longjmp (info->env, 1);
924 poly_Boolean_free (info->accumulate, np, &merged, PBO_UNITE);
925 info->accumulate = merged;
927 info->batch_size ++;
929 if (info->batch_size == SUBTRACT_PIN_VIA_BATCH_SIZE)
930 subtract_accumulated (info, polygon);
932 return 1;
935 static int
936 arc_sub_callback (const BoxType * b, void *cl)
938 ArcType *arc = (ArcType *) b;
939 struct cpInfo *info = (struct cpInfo *) cl;
940 PolygonType *polygon;
942 /* don't subtract the object that was put back! */
943 if (b == info->other)
944 return 0;
945 if (!TEST_FLAG (CLEARLINEFLAG, arc))
946 return 0;
947 polygon = info->polygon;
948 if (SubtractArc (arc, polygon) < 0)
949 longjmp (info->env, 1);
950 return 1;
953 static int
954 pad_sub_callback (const BoxType * b, void *cl)
956 PadType *pad = (PadType *) b;
957 struct cpInfo *info = (struct cpInfo *) cl;
958 PolygonType *polygon;
960 /* don't subtract the object that was put back! */
961 if (b == info->other)
962 return 0;
963 if (pad->Clearance == 0)
964 return 0;
965 polygon = info->polygon;
966 if (XOR (TEST_FLAG (ONSOLDERFLAG, pad), !info->bottom))
968 if (SubtractPad (pad, polygon) < 0)
969 longjmp (info->env, 1);
970 return 1;
972 return 0;
975 static int
976 line_sub_callback (const BoxType * b, void *cl)
978 LineType *line = (LineType *) b;
979 struct cpInfo *info = (struct cpInfo *) cl;
980 PolygonType *polygon;
981 POLYAREA *np;
982 POLYAREA *merged;
984 /* don't subtract the object that was put back! */
985 if (b == info->other)
986 return 0;
987 if (!TEST_FLAG (CLEARLINEFLAG, line))
988 return 0;
989 polygon = info->polygon;
991 if (!(np = LinePoly (line, line->Thickness + line->Clearance)))
992 longjmp (info->env, 1);
994 poly_Boolean_free (info->accumulate, np, &merged, PBO_UNITE);
995 info->accumulate = merged;
996 info->batch_size ++;
998 if (info->batch_size == SUBTRACT_LINE_BATCH_SIZE)
999 subtract_accumulated (info, polygon);
1001 return 1;
1004 static int
1005 text_sub_callback (const BoxType * b, void *cl)
1007 TextType *text = (TextType *) b;
1008 struct cpInfo *info = (struct cpInfo *) cl;
1009 PolygonType *polygon;
1011 /* don't subtract the object that was put back! */
1012 if (b == info->other)
1013 return 0;
1014 if (!TEST_FLAG (CLEARLINEFLAG, text))
1015 return 0;
1016 polygon = info->polygon;
1017 if (SubtractText (text, polygon) < 0)
1018 longjmp (info->env, 1);
1019 return 1;
1022 static int
1023 Group (DataType *Data, Cardinal layer)
1025 Cardinal i, j;
1026 for (i = 0; i < max_group; i++)
1027 for (j = 0; j < ((PCBType *) (Data->pcb))->LayerGroups.Number[i]; j++)
1028 if (layer == ((PCBType *) (Data->pcb))->LayerGroups.Entries[i][j])
1029 return i;
1030 return i;
1033 static int
1034 clearPoly (DataType *Data, LayerType *Layer, PolygonType * polygon,
1035 const BoxType * here, Coord expand)
1037 int r = 0;
1038 BoxType region;
1039 struct cpInfo info;
1040 Cardinal group;
1042 if (!TEST_FLAG (CLEARPOLYFLAG, polygon)
1043 || GetLayerNumber (Data, Layer) >= max_copper_layer)
1044 return 0;
1045 group = Group (Data, GetLayerNumber (Data, Layer));
1046 info.bottom = (group == Group (Data, bottom_silk_layer));
1047 info.data = Data;
1048 info.other = here;
1049 info.layer = Layer;
1050 info.polygon = polygon;
1051 if (here)
1052 region = clip_box (here, &polygon->BoundingBox);
1053 else
1054 region = polygon->BoundingBox;
1055 region = bloat_box (&region, expand);
1057 if (setjmp (info.env) == 0)
1059 r = 0;
1060 info.accumulate = NULL;
1061 info.batch_size = 0;
1062 if (info.bottom || group == Group (Data, top_silk_layer))
1063 r += r_search (Data->pad_tree, &region, NULL, pad_sub_callback, &info);
1064 GROUP_LOOP (Data, group);
1066 r +=
1067 r_search (layer->line_tree, &region, NULL, line_sub_callback,
1068 &info);
1069 subtract_accumulated (&info, polygon);
1070 r +=
1071 r_search (layer->arc_tree, &region, NULL, arc_sub_callback, &info);
1072 r +=
1073 r_search (layer->text_tree, &region, NULL, text_sub_callback, &info);
1075 END_LOOP;
1076 r += r_search (Data->via_tree, &region, NULL, pin_sub_callback, &info);
1077 r += r_search (Data->pin_tree, &region, NULL, pin_sub_callback, &info);
1078 subtract_accumulated (&info, polygon);
1080 polygon->NoHolesValid = 0;
1081 return r;
1084 static int
1085 Unsubtract (POLYAREA * np1, PolygonType * p)
1087 POLYAREA *merged = NULL, *np = np1;
1088 POLYAREA *orig_poly, *clipped_np;
1089 int x;
1090 assert (np);
1091 assert (p && p->Clipped);
1093 orig_poly = original_poly (p);
1095 x = poly_Boolean_free (np, orig_poly, &clipped_np, PBO_ISECT);
1096 if (x != err_ok)
1098 fprintf (stderr, "Error while clipping PBO_ISECT: %d\n", x);
1099 poly_Free (&clipped_np);
1100 goto fail;
1103 x = poly_Boolean_free (p->Clipped, clipped_np, &merged, PBO_UNITE);
1104 if (x != err_ok)
1106 fprintf (stderr, "Error while clipping PBO_UNITE: %d\n", x);
1107 poly_Free (&merged);
1108 goto fail;
1110 p->Clipped = biggest (merged);
1111 assert (!p->Clipped || poly_Valid (p->Clipped));
1112 return 1;
1114 fail:
1115 p->Clipped = NULL;
1116 if (p->NoHoles) printf ("Just leaked in Unsubtract\n");
1117 p->NoHoles = NULL;
1118 return 0;
1121 static int
1122 UnsubtractPin (PinType * pin, LayerType * l, PolygonType * p)
1124 POLYAREA *np;
1126 /* overlap a bit to prevent gaps from rounding errors */
1127 np = BoxPolyBloated (&pin->BoundingBox, UNSUBTRACT_BLOAT);
1129 if (!np)
1130 return 0;
1131 if (!Unsubtract (np, p))
1132 return 0;
1133 clearPoly (PCB->Data, l, p, (const BoxType *) pin, 2 * UNSUBTRACT_BLOAT);
1134 return 1;
1137 static int
1138 UnsubtractArc (ArcType * arc, LayerType * l, PolygonType * p)
1140 POLYAREA *np;
1142 if (!TEST_FLAG (CLEARLINEFLAG, arc))
1143 return 0;
1145 /* overlap a bit to prevent gaps from rounding errors */
1146 np = BoxPolyBloated (&arc->BoundingBox, UNSUBTRACT_BLOAT);
1148 if (!np)
1149 return 0;
1150 if (!Unsubtract (np, p))
1151 return 0;
1152 clearPoly (PCB->Data, l, p, (const BoxType *) arc, 2 * UNSUBTRACT_BLOAT);
1153 return 1;
1156 static int
1157 UnsubtractLine (LineType * line, LayerType * l, PolygonType * p)
1159 POLYAREA *np;
1161 if (!TEST_FLAG (CLEARLINEFLAG, line))
1162 return 0;
1164 /* overlap a bit to prevent notches from rounding errors */
1165 np = BoxPolyBloated (&line->BoundingBox, UNSUBTRACT_BLOAT);
1167 if (!np)
1168 return 0;
1169 if (!Unsubtract (np, p))
1170 return 0;
1171 clearPoly (PCB->Data, l, p, (const BoxType *) line, 2 * UNSUBTRACT_BLOAT);
1172 return 1;
1175 static int
1176 UnsubtractText (TextType * text, LayerType * l, PolygonType * p)
1178 POLYAREA *np;
1180 if (!TEST_FLAG (CLEARLINEFLAG, text))
1181 return 0;
1183 /* overlap a bit to prevent notches from rounding errors */
1184 np = BoxPolyBloated (&text->BoundingBox, UNSUBTRACT_BLOAT);
1186 if (!np)
1187 return -1;
1188 if (!Unsubtract (np, p))
1189 return 0;
1190 clearPoly (PCB->Data, l, p, (const BoxType *) text, 2 * UNSUBTRACT_BLOAT);
1191 return 1;
1194 static int
1195 UnsubtractPad (PadType * pad, LayerType * l, PolygonType * p)
1197 POLYAREA *np;
1199 /* overlap a bit to prevent notches from rounding errors */
1200 np = BoxPolyBloated (&pad->BoundingBox, UNSUBTRACT_BLOAT);
1202 if (!np)
1203 return 0;
1204 if (!Unsubtract (np, p))
1205 return 0;
1206 clearPoly (PCB->Data, l, p, (const BoxType *) pad, 2 * UNSUBTRACT_BLOAT);
1207 return 1;
1210 static bool inhibit = false;
1213 InitClip (DataType *Data, LayerType *layer, PolygonType * p)
1215 if (inhibit)
1216 return 0;
1217 if (p->Clipped)
1218 poly_Free (&p->Clipped);
1219 p->Clipped = original_poly (p);
1220 poly_FreeContours (&p->NoHoles);
1221 if (!p->Clipped)
1222 return 0;
1223 assert (poly_Valid (p->Clipped));
1224 if (TEST_FLAG (CLEARPOLYFLAG, p))
1225 clearPoly (Data, layer, p, NULL, 0);
1226 else
1227 p->NoHolesValid = 0;
1228 return 1;
1231 /* --------------------------------------------------------------------------
1232 * remove redundant polygon points. Any point that lies on the straight
1233 * line between the points on either side of it is redundant.
1234 * returns true if any points are removed
1236 bool
1237 RemoveExcessPolygonPoints (LayerType *Layer, PolygonType *Polygon)
1239 PointType *p;
1240 Cardinal n, prev, next;
1241 LineType line;
1242 bool changed = false;
1244 if (Undoing ())
1245 return (false);
1247 for (n = 0; n < Polygon->PointN; n++)
1249 prev = prev_contour_point (Polygon, n);
1250 next = next_contour_point (Polygon, n);
1251 p = &Polygon->Points[n];
1253 line.Point1 = Polygon->Points[prev];
1254 line.Point2 = Polygon->Points[next];
1255 line.Thickness = 0;
1256 if (IsPointOnLine (p->X, p->Y, 0.0, &line))
1258 RemoveObject (POLYGONPOINT_TYPE, Layer, Polygon, p);
1259 changed = true;
1262 return (changed);
1265 /* ---------------------------------------------------------------------------
1266 * returns the index of the polygon point which is the end
1267 * point of the segment with the lowest distance to the passed
1268 * coordinates
1270 Cardinal
1271 GetLowestDistancePolygonPoint (PolygonType *Polygon, Coord X, Coord Y)
1273 double mindistance = (double) MAX_COORD * MAX_COORD;
1274 PointType *ptr1, *ptr2;
1275 Cardinal n, result = 0;
1277 /* we calculate the distance to each segment and choose the
1278 * shortest distance. If the closest approach between the
1279 * given point and the projected line (i.e. the segment extended)
1280 * is not on the segment, then the distance is the distance
1281 * to the segment end point.
1284 for (n = 0; n < Polygon->PointN; n++)
1286 double u, dx, dy;
1287 ptr1 = &Polygon->Points[prev_contour_point (Polygon, n)];
1288 ptr2 = &Polygon->Points[n];
1290 dx = ptr2->X - ptr1->X;
1291 dy = ptr2->Y - ptr1->Y;
1292 if (dx != 0.0 || dy != 0.0)
1294 /* projected intersection is at P1 + u(P2 - P1) */
1295 u = ((X - ptr1->X) * dx + (Y - ptr1->Y) * dy) / (dx * dx + dy * dy);
1297 if (u < 0.0)
1298 { /* ptr1 is closest point */
1299 u = SQUARE (X - ptr1->X) + SQUARE (Y - ptr1->Y);
1301 else if (u > 1.0)
1302 { /* ptr2 is closest point */
1303 u = SQUARE (X - ptr2->X) + SQUARE (Y - ptr2->Y);
1305 else
1306 { /* projected intersection is closest point */
1307 u = SQUARE (X - ptr1->X * (1.0 - u) - u * ptr2->X) +
1308 SQUARE (Y - ptr1->Y * (1.0 - u) - u * ptr2->Y);
1310 if (u < mindistance)
1312 mindistance = u;
1313 result = n;
1317 return (result);
1320 /* ---------------------------------------------------------------------------
1321 * go back to the previous point of the polygon
1323 void
1324 GoToPreviousPoint (void)
1326 switch (Crosshair.AttachedPolygon.PointN)
1328 /* do nothing if mode has just been entered */
1329 case 0:
1330 break;
1332 /* reset number of points and 'LINE_MODE' state */
1333 case 1:
1334 Crosshair.AttachedPolygon.PointN = 0;
1335 Crosshair.AttachedLine.State = STATE_FIRST;
1336 addedLines = 0;
1337 break;
1339 /* back-up one point */
1340 default:
1342 PointType *points = Crosshair.AttachedPolygon.Points;
1343 Cardinal n = Crosshair.AttachedPolygon.PointN - 2;
1345 Crosshair.AttachedPolygon.PointN--;
1346 Crosshair.AttachedLine.Point1.X = points[n].X;
1347 Crosshair.AttachedLine.Point1.Y = points[n].Y;
1348 break;
1353 /* ---------------------------------------------------------------------------
1354 * close polygon if possible
1356 void
1357 ClosePolygon (void)
1359 Cardinal n = Crosshair.AttachedPolygon.PointN;
1361 /* check number of points */
1362 if (n >= 3)
1364 /* if 45 degree lines are what we want do a quick check
1365 * if closing the polygon makes sense
1367 if (!TEST_FLAG (ALLDIRECTIONFLAG, PCB))
1369 Coord dx, dy;
1371 dx = abs (Crosshair.AttachedPolygon.Points[n - 1].X -
1372 Crosshair.AttachedPolygon.Points[0].X);
1373 dy = abs (Crosshair.AttachedPolygon.Points[n - 1].Y -
1374 Crosshair.AttachedPolygon.Points[0].Y);
1375 if (!(dx == 0 || dy == 0 || dx == dy))
1377 Message
1379 ("Cannot close polygon because 45 degree lines are requested.\n"));
1380 return;
1383 CopyAttachedPolygonToLayer ();
1384 Draw ();
1386 else
1387 Message (_("A polygon has to have at least 3 points\n"));
1390 /* ---------------------------------------------------------------------------
1391 * moves the data of the attached (new) polygon to the current layer
1393 void
1394 CopyAttachedPolygonToLayer (void)
1396 PolygonType *polygon;
1397 int saveID;
1399 /* move data to layer and clear attached struct */
1400 polygon = CreateNewPolygon (CURRENT, NoFlags ());
1401 saveID = polygon->ID;
1402 *polygon = Crosshair.AttachedPolygon;
1403 polygon->ID = saveID;
1404 SET_FLAG (CLEARPOLYFLAG, polygon);
1405 if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1406 SET_FLAG (FULLPOLYFLAG, polygon);
1407 memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
1408 SetPolygonBoundingBox (polygon);
1409 if (!CURRENT->polygon_tree)
1410 CURRENT->polygon_tree = r_create_tree (NULL, 0, 0);
1411 r_insert_entry (CURRENT->polygon_tree, (BoxType *) polygon, 0);
1412 InitClip (PCB->Data, CURRENT, polygon);
1413 DrawPolygon (CURRENT, polygon);
1414 SetChangedFlag (true);
1416 /* reset state of attached line */
1417 Crosshair.AttachedLine.State = STATE_FIRST;
1418 addedLines = 0;
1420 /* add to undo list */
1421 AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT, polygon, polygon);
1422 IncrementUndoSerialNumber ();
1425 /* find polygon holes in range, then call the callback function for
1426 * each hole. If the callback returns non-zero, stop
1427 * the search.
1430 PolygonHoles (PolygonType *polygon, const BoxType *range,
1431 int (*callback) (PLINE *contour, void *user_data),
1432 void *user_data)
1434 POLYAREA *pa = polygon->Clipped;
1435 PLINE *pl;
1436 /* If this hole is so big the polygon doesn't exist, then it's not
1437 * really a hole.
1439 if (!pa)
1440 return 0;
1441 for (pl = pa->contours->next; pl; pl = pl->next)
1443 if (range != NULL &&
1444 (pl->xmin > range->X2 || pl->xmax < range->X1 ||
1445 pl->ymin > range->Y2 || pl->ymax < range->Y1))
1446 continue;
1447 if (callback (pl, user_data))
1449 return 1;
1452 return 0;
1455 struct plow_info
1457 int type;
1458 void *ptr1, *ptr2;
1459 LayerType *layer;
1460 DataType *data;
1461 int (*callback) (DataType *, LayerType *, PolygonType *, int, void *,
1462 void *, void *);
1463 void *userdata;
1466 static int
1467 subtract_plow (DataType *Data, LayerType *Layer, PolygonType *Polygon,
1468 int type, void *ptr1, void *ptr2, void *userdata)
1470 if (!Polygon->Clipped)
1471 return 0;
1472 switch (type)
1474 case PIN_TYPE:
1475 case VIA_TYPE:
1476 SubtractPin (Data, (PinType *) ptr2, Layer, Polygon);
1477 Polygon->NoHolesValid = 0;
1478 return 1;
1479 case LINE_TYPE:
1480 SubtractLine ((LineType *) ptr2, Polygon);
1481 Polygon->NoHolesValid = 0;
1482 return 1;
1483 case ARC_TYPE:
1484 SubtractArc ((ArcType *) ptr2, Polygon);
1485 Polygon->NoHolesValid = 0;
1486 return 1;
1487 case PAD_TYPE:
1488 SubtractPad ((PadType *) ptr2, Polygon);
1489 Polygon->NoHolesValid = 0;
1490 return 1;
1491 case TEXT_TYPE:
1492 SubtractText ((TextType *) ptr2, Polygon);
1493 Polygon->NoHolesValid = 0;
1494 return 1;
1496 return 0;
1499 static int
1500 add_plow (DataType *Data, LayerType *Layer, PolygonType *Polygon,
1501 int type, void *ptr1, void *ptr2, void *userdata)
1503 switch (type)
1505 case PIN_TYPE:
1506 case VIA_TYPE:
1507 UnsubtractPin ((PinType *) ptr2, Layer, Polygon);
1508 return 1;
1509 case LINE_TYPE:
1510 UnsubtractLine ((LineType *) ptr2, Layer, Polygon);
1511 return 1;
1512 case ARC_TYPE:
1513 UnsubtractArc ((ArcType *) ptr2, Layer, Polygon);
1514 return 1;
1515 case PAD_TYPE:
1516 UnsubtractPad ((PadType *) ptr2, Layer, Polygon);
1517 return 1;
1518 case TEXT_TYPE:
1519 UnsubtractText ((TextType *) ptr2, Layer, Polygon);
1520 return 1;
1522 return 0;
1525 static int
1526 plow_callback (const BoxType * b, void *cl)
1528 struct plow_info *plow = (struct plow_info *) cl;
1529 PolygonType *polygon = (PolygonType *) b;
1531 if (TEST_FLAG (CLEARPOLYFLAG, polygon))
1532 return plow->callback (plow->data, plow->layer, polygon, plow->type,
1533 plow->ptr1, plow->ptr2, plow->userdata);
1534 return 0;
1538 PlowsPolygon (DataType * Data, int type, void *ptr1, void *ptr2,
1539 int (*call_back) (DataType *data, LayerType *lay,
1540 PolygonType *poly, int type, void *ptr1,
1541 void *ptr2, void *userdata),
1542 void *userdata)
1544 BoxType sb = ((PinType *) ptr2)->BoundingBox;
1545 int r = 0;
1546 struct plow_info info;
1548 info.type = type;
1549 info.ptr1 = ptr1;
1550 info.ptr2 = ptr2;
1551 info.data = Data;
1552 info.callback = call_back;
1553 info.userdata = userdata;
1554 switch (type)
1556 case PIN_TYPE:
1557 case VIA_TYPE:
1558 if (type == PIN_TYPE || ptr1 == ptr2 || ptr1 == NULL)
1560 LAYER_LOOP (Data, max_copper_layer);
1562 info.layer = layer;
1563 r +=
1564 r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
1566 END_LOOP;
1568 else
1570 GROUP_LOOP (Data, GetLayerGroupNumberByNumber (GetLayerNumber (Data,
1571 ((LayerType *) ptr1))));
1573 info.layer = layer;
1574 r +=
1575 r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
1577 END_LOOP;
1579 break;
1580 case LINE_TYPE:
1581 case ARC_TYPE:
1582 case TEXT_TYPE:
1583 /* the cast works equally well for lines and arcs */
1584 if (!TEST_FLAG (CLEARLINEFLAG, (LineType *) ptr2))
1585 return 0;
1586 /* silk doesn't plow */
1587 if (GetLayerNumber (Data, (LayerType *)ptr1) >= max_copper_layer)
1588 return 0;
1589 GROUP_LOOP (Data, GetLayerGroupNumberByNumber (GetLayerNumber (Data,
1590 ((LayerType *) ptr1))));
1592 info.layer = layer;
1593 r += r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
1595 END_LOOP;
1596 break;
1597 case PAD_TYPE:
1599 Cardinal group = GetLayerGroupNumberByNumber (
1600 TEST_FLAG (ONSOLDERFLAG, (PadType *) ptr2) ?
1601 bottom_silk_layer : top_silk_layer);
1602 GROUP_LOOP (Data, group);
1604 info.layer = layer;
1605 r +=
1606 r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
1608 END_LOOP;
1610 break;
1612 case ELEMENT_TYPE:
1614 PIN_LOOP ((ElementType *) ptr1);
1616 PlowsPolygon (Data, PIN_TYPE, ptr1, pin, call_back, userdata);
1618 END_LOOP;
1619 PAD_LOOP ((ElementType *) ptr1);
1621 PlowsPolygon (Data, PAD_TYPE, ptr1, pad, call_back, userdata);
1623 END_LOOP;
1625 break;
1627 return r;
1630 void
1631 RestoreToPolygon (DataType * Data, int type, void *ptr1, void *ptr2)
1633 if (!Data->polyClip)
1634 return;
1636 /* XXX: HACK, this is a kludgy place to put a check for recomputing our outline */
1637 if (type == LINE_TYPE || type == ARC_TYPE)
1639 LayerType *layer = (LayerType *) ptr1;
1641 if (strcmp (layer->Name, "outline") == 0 ||
1642 strcmp (layer->Name, "route") == 0)
1643 Data->outline_valid = false;
1646 if (type == POLYGON_TYPE)
1647 InitClip (PCB->Data, (LayerType *) ptr1, (PolygonType *) ptr2);
1648 else
1649 PlowsPolygon (Data, type, ptr1, ptr2, add_plow, NULL);
1652 void
1653 ClearFromPolygon (DataType * Data, int type, void *ptr1, void *ptr2)
1655 if (!Data->polyClip)
1656 return;
1658 /* XXX: HACK, this is a kludgy place to put a check for recomputing our outline */
1659 if (type == LINE_TYPE || type == ARC_TYPE)
1661 LayerType *layer = (LayerType *) ptr1;
1663 if (strcmp (layer->Name, "outline") == 0 ||
1664 strcmp (layer->Name, "route") == 0)
1665 Data->outline_valid = false;
1668 if (type == POLYGON_TYPE)
1669 InitClip (PCB->Data, (LayerType *) ptr1, (PolygonType *) ptr2);
1670 else
1671 PlowsPolygon (Data, type, ptr1, ptr2, subtract_plow, NULL);
1674 bool
1675 isects (POLYAREA * a, PolygonType *p, bool fr)
1677 POLYAREA *x;
1678 bool ans;
1679 ans = Touching (a, p->Clipped);
1680 /* argument may be register, so we must copy it */
1681 x = a;
1682 if (fr)
1683 poly_Free (&x);
1684 return ans;
1688 bool
1689 IsPointInPolygon (Coord X, Coord Y, Coord r, PolygonType *p)
1691 POLYAREA *c;
1692 Vector v;
1693 v[0] = X;
1694 v[1] = Y;
1695 if (poly_CheckInside (p->Clipped, v))
1696 return true;
1697 if (r < 1)
1698 return false;
1699 if (!(c = CirclePoly (X, Y, r)))
1700 return false;
1701 return isects (c, p, true);
1705 bool
1706 IsPointInPolygonIgnoreHoles (Coord X, Coord Y, PolygonType *p)
1708 Vector v;
1709 v[0] = X;
1710 v[1] = Y;
1711 return poly_InsideContour (p->Clipped->contours, v);
1714 bool
1715 IsRectangleInPolygon (Coord X1, Coord Y1, Coord X2, Coord Y2, PolygonType *p)
1717 POLYAREA *s;
1718 if (!
1719 (s = RectPoly (min (X1, X2), max (X1, X2), min (Y1, Y2), max (Y1, Y2))))
1720 return false;
1721 return isects (s, p, true);
1724 /* NB: This function will free the passed POLYAREA.
1725 * It must only be passed a single POLYAREA (pa->f == pa->b == pa)
1727 static void
1728 r_NoHolesPolygonDicer (POLYAREA * pa,
1729 void (*emit) (PLINE *, void *), void *user_data)
1731 PLINE *p = pa->contours;
1733 if (!pa->contours->next) /* no holes */
1735 pa->contours = NULL; /* The callback now owns the contour */
1736 /* Don't bother removing it from the POLYAREA's rtree
1737 since we're going to free the POLYAREA below anyway */
1738 emit (p, user_data);
1739 poly_Free (&pa);
1740 return;
1742 else
1744 POLYAREA *poly2, *left, *right;
1746 /* make a rectangle of the left region slicing through the middle of the first hole */
1747 poly2 = RectPoly (p->xmin, (p->next->xmin + p->next->xmax) / 2,
1748 p->ymin, p->ymax);
1749 poly_AndSubtract_free (pa, poly2, &left, &right);
1750 if (left)
1752 POLYAREA *cur, *next;
1753 cur = left;
1756 next = cur->f;
1757 cur->f = cur->b = cur; /* Detach this polygon piece */
1758 r_NoHolesPolygonDicer (cur, emit, user_data);
1759 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1761 while ((cur = next) != left);
1763 if (right)
1765 POLYAREA *cur, *next;
1766 cur = right;
1769 next = cur->f;
1770 cur->f = cur->b = cur; /* Detach this polygon piece */
1771 r_NoHolesPolygonDicer (cur, emit, user_data);
1772 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1774 while ((cur = next) != right);
1779 void
1780 NoHolesPolygonDicer (PolygonType *p, const BoxType * clip,
1781 void (*emit) (PLINE *, void *), void *user_data)
1783 POLYAREA *main_contour, *cur, *next;
1785 main_contour = poly_Create ();
1786 /* copy the main poly only */
1787 poly_Copy1 (main_contour, p->Clipped);
1788 /* clip to the bounding box */
1789 if (clip)
1791 POLYAREA *cbox = RectPoly (clip->X1, clip->X2, clip->Y1, clip->Y2);
1792 poly_Boolean_free (main_contour, cbox, &main_contour, PBO_ISECT);
1794 if (main_contour == NULL)
1795 return;
1796 /* Now dice it up.
1797 * NB: Could be more than one piece (because of the clip above)
1799 cur = main_contour;
1802 next = cur->f;
1803 cur->f = cur->b = cur; /* Detach this polygon piece */
1804 r_NoHolesPolygonDicer (cur, emit, user_data);
1805 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1807 while ((cur = next) != main_contour);
1810 /* make a polygon split into multiple parts into multiple polygons */
1811 bool
1812 MorphPolygon (LayerType *layer, PolygonType *poly)
1814 POLYAREA *p, *start;
1815 bool many = false;
1816 FlagType flags;
1818 if (!poly->Clipped || TEST_FLAG (LOCKFLAG, poly))
1819 return false;
1820 if (poly->Clipped->f == poly->Clipped)
1821 return false;
1822 ErasePolygon (poly);
1823 start = p = poly->Clipped;
1824 /* This is ugly. The creation of the new polygons can cause
1825 * all of the polygon pointers (including the one we're called
1826 * with to change if there is a realloc in GetPolygonMemory().
1827 * That will invalidate our original "poly" argument, potentially
1828 * inside the loop. We need to steal the Clipped pointer and
1829 * hide it from the Remove call so that it still exists while
1830 * we do this dirty work.
1832 poly->Clipped = NULL;
1833 if (poly->NoHoles) printf ("Just leaked in MorpyPolygon\n");
1834 poly->NoHoles = NULL;
1835 flags = poly->Flags;
1836 RemovePolygon (layer, poly);
1837 inhibit = true;
1840 VNODE *v;
1841 PolygonType *newone;
1843 if (p->contours->area > PCB->IsleArea)
1845 newone = CreateNewPolygon (layer, flags);
1846 if (!newone)
1847 return false;
1848 many = true;
1849 v = &p->contours->head;
1850 CreateNewPointInPolygon (newone, v->point[0], v->point[1]);
1851 for (v = v->next; v != &p->contours->head; v = v->next)
1852 CreateNewPointInPolygon (newone, v->point[0], v->point[1]);
1853 newone->BoundingBox.X1 = p->contours->xmin;
1854 newone->BoundingBox.X2 = p->contours->xmax + 1;
1855 newone->BoundingBox.Y1 = p->contours->ymin;
1856 newone->BoundingBox.Y2 = p->contours->ymax + 1;
1857 AddObjectToCreateUndoList (POLYGON_TYPE, layer, newone, newone);
1858 newone->Clipped = p;
1859 p = p->f; /* go to next pline */
1860 newone->Clipped->b = newone->Clipped->f = newone->Clipped; /* unlink from others */
1861 r_insert_entry (layer->polygon_tree, (BoxType *) newone, 0);
1862 DrawPolygon (layer, newone);
1864 else
1866 POLYAREA *t = p;
1868 p = p->f;
1869 poly_DelContour (&t->contours);
1870 free (t);
1873 while (p != start);
1874 inhibit = false;
1875 IncrementUndoSerialNumber ();
1876 return many;
1879 void debug_pline (PLINE *pl)
1881 VNODE *v;
1882 pcb_fprintf (stderr, "\txmin %#mS xmax %#mS ymin %#mS ymax %#mS\n",
1883 pl->xmin, pl->xmax, pl->ymin, pl->ymax);
1884 v = &pl->head;
1885 while (v)
1887 pcb_fprintf(stderr, "\t\tvnode: %#mD\n", v->point[0], v->point[1]);
1888 v = v->next;
1889 if (v == &pl->head)
1890 break;
1894 void
1895 debug_polyarea (POLYAREA *p)
1897 PLINE *pl;
1899 fprintf (stderr, "POLYAREA %p\n", p);
1900 for (pl=p->contours; pl; pl=pl->next)
1901 debug_pline (pl);
1904 void
1905 debug_polygon (PolygonType *p)
1907 Cardinal i;
1908 POLYAREA *pa;
1909 fprintf (stderr, "POLYGON %p %d pts\n", p, p->PointN);
1910 for (i=0; i<p->PointN; i++)
1911 pcb_fprintf(stderr, "\t%d: %#mD\n", i, p->Points[i].X, p->Points[i].Y);
1912 if (p->HoleIndexN)
1914 fprintf (stderr, "%d holes, starting at indices\n", p->HoleIndexN);
1915 for (i=0; i<p->HoleIndexN; i++)
1916 fprintf(stderr, "\t%d: %d\n", i, p->HoleIndex[i]);
1918 else
1919 fprintf (stderr, "it has no holes\n");
1920 pa = p->Clipped;
1921 while (pa)
1923 debug_polyarea (pa);
1924 pa = pa->f;
1925 if (pa == p->Clipped)
1926 break;
1930 /* Convert a POLYAREA (and all linked POLYAREA) to
1931 * raw PCB polygons on the given layer.
1933 void
1934 PolyToPolygonsOnLayer (DataType *Destination, LayerType *Layer,
1935 POLYAREA *Input, FlagType Flags)
1937 PolygonType *Polygon;
1938 POLYAREA *pa;
1939 PLINE *pline;
1940 VNODE *node;
1941 bool outer;
1943 if (Input == NULL)
1944 return;
1946 pa = Input;
1949 Polygon = CreateNewPolygon (Layer, Flags);
1951 pline = pa->contours;
1952 outer = true;
1956 if (!outer)
1957 CreateNewHoleInPolygon (Polygon);
1958 outer = false;
1960 node = &pline->head;
1963 CreateNewPointInPolygon (Polygon, node->point[0],
1964 node->point[1]);
1966 while ((node = node->next) != &pline->head);
1969 while ((pline = pline->next) != NULL);
1971 InitClip (Destination, Layer, Polygon);
1972 SetPolygonBoundingBox (Polygon);
1973 if (!Layer->polygon_tree)
1974 Layer->polygon_tree = r_create_tree (NULL, 0, 0);
1975 r_insert_entry (Layer->polygon_tree, (BoxType *) Polygon, 0);
1977 DrawPolygon (Layer, Polygon);
1978 /* add to undo list */
1979 AddObjectToCreateUndoList (POLYGON_TYPE, Layer, Polygon, Polygon);
1981 while ((pa = pa->f) != Input);
1983 SetChangedFlag (true);
1987 struct clip_outline_info {
1988 POLYAREA *poly;
1991 #define ROUTER_THICKNESS MIL_TO_COORD (10)
1992 //#define ROUTER_THICKNESS MIL_TO_COORD (0.1)
1994 static int
1995 arc_outline_callback (const BoxType * b, void *cl)
1997 ArcType *arc = (ArcType *)b;
1998 struct clip_outline_info *info = cl;
1999 POLYAREA *np, *res;
2001 if (!(np = ArcPoly (arc, ROUTER_THICKNESS)))
2002 return 0;
2004 poly_Boolean_free (info->poly, np, &res, PBO_SUB);
2005 info->poly = res;
2007 return 1;
2010 static int
2011 line_outline_callback (const BoxType * b, void *cl)
2013 LineType *line = (LineType *)b;
2014 struct clip_outline_info *info = cl;
2015 POLYAREA *np, *res;
2017 if (!(np = LinePoly (line, ROUTER_THICKNESS)))
2018 return 0;
2020 poly_Boolean_free (info->poly, np, &res, PBO_SUB);
2021 info->poly = res;
2023 return 1;
2026 static void
2027 delete_piece_cb (gpointer data, gpointer userdata)
2029 POLYAREA *piece = data;
2030 POLYAREA **res = userdata;
2032 /* If this item was the start of the list, advance that pointer */
2033 if (*res == piece)
2034 *res = (*res)->f;
2036 /* But reset it to NULL if it wraps around and hits us again */
2037 if (*res == piece)
2038 *res = NULL;
2040 piece->b->f = piece->f;
2041 piece->f->b = piece->b;
2042 piece->f = piece->b = piece;
2044 poly_Free (&piece);
2047 POLYAREA *board_outline_poly (void)
2049 int i;
2050 int count;
2051 int found_outline = 0;
2052 LayerType *Layer = NULL;
2053 BoxType region;
2054 struct clip_outline_info info;
2055 POLYAREA *whole_world;
2056 POLYAREA *clipped;
2057 POLYAREA *piece;
2058 POLYAREA *check;
2059 GList *pieces_to_delete = NULL;
2060 bool any_pieces_kept = false;
2062 #define BLOAT_WORLD MIL_TO_COORD (10)
2064 whole_world = RectPoly (-BLOAT_WORLD, BLOAT_WORLD + PCB->MaxWidth,
2065 -BLOAT_WORLD, BLOAT_WORLD + PCB->MaxHeight);
2067 for (i = 0; i < max_copper_layer; i++)
2069 Layer = PCB->Data->Layer + i;
2071 if (strcmp (Layer->Name, "outline") == 0 ||
2072 strcmp (Layer->Name, "route") == 0)
2074 found_outline = 1;
2075 break;
2079 if (!found_outline) {
2080 printf ("Didn't find outline\n");
2081 return whole_world;
2084 /* Do stuff to turn the outline layer into a polygon */
2086 /* Ideally, we just want to look at centre-lines, but that is hard!
2088 * Lets add all lines, arcs etc.. together to form a polygon comprising
2089 * the bits we presume a router would remove.
2091 * Then we need to subtract that from some notional "infinite" plane
2092 * polygon, leaving the remaining pieces.
2094 * OR.. we could just look at the holes in the resulting polygon?
2096 * Given these holes, we need to know which are inside and outside.
2097 * _____________
2098 * / ___________ \
2099 * || ||
2100 * || //=\\ ||
2101 * || || || ||
2102 * || \\=// ||
2103 * ||___________||
2104 * \_____________/
2107 info.poly = whole_world;
2109 region.X1 = 0;
2110 region.Y1 = 0;
2111 region.X2 = PCB->MaxWidth;
2112 region.Y2 = PCB->MaxHeight;
2114 r_search (Layer->line_tree, &region, NULL, line_outline_callback, &info);
2115 r_search (Layer->arc_tree, &region, NULL, arc_outline_callback, &info);
2117 clipped = info.poly;
2119 /* Now we just need to work out which pieces of polygon are inside
2120 and outside the board! */
2122 /* If there is no result, or only one piece, return that */
2123 if (clipped == NULL || clipped->f == clipped)
2124 return clipped;
2126 /* WARNING: This next check is O(n^2), where n is the number of clipped
2127 * pieces, hopefully the outline layer isn't too complex!
2130 piece = clipped;
2131 do { /* LOOP OVER POLYGON PIECES */
2133 if (piece->contours == NULL)
2134 printf ("WTF?\n");
2136 count = 0;
2137 check = clipped;
2138 do { /* LOOP OVER POLYGON PIECES */
2139 if (check == piece)
2140 continue;
2141 if (poly_ContourInContour (check->contours, piece->contours))
2142 count ++;
2143 } while ((check = check->f) != clipped);
2145 /* If the piece is inside an odd number of others, keep it */
2146 if ((count & 1) == 1)
2147 any_pieces_kept = true;
2148 else
2149 pieces_to_delete = g_list_prepend (pieces_to_delete, piece);
2151 } while ((piece = piece->f) != clipped);
2153 /* If we did not find an enclosed area (the board) trimmed from the world polygon,
2154 return the entire subtracted result. This keeps the behaviour similar to what
2155 you see when individual lines on the outline layer don't close to form an
2156 enclosed region. (This fixes being able to cope with such a case where the
2157 outline layer geometry lies outside the world rect polygon above, and divides
2158 that world rect into multiple pieces.
2160 if (any_pieces_kept)
2161 g_list_foreach (pieces_to_delete, delete_piece_cb, &clipped);
2163 g_list_free (pieces_to_delete);
2165 return clipped;