select.c: Remove Draw() call from SelectConnection
[geda-pcb/leaky.git] / src / polygon.c
blob72d92c9469720b9a2dca52e3a2adbd5e517cc6c4
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>
83 #include "global.h"
84 #include "box.h"
85 #include "create.h"
86 #include "crosshair.h"
87 #include "data.h"
88 #include "draw.h"
89 #include "error.h"
90 #include "find.h"
91 #include "misc.h"
92 #include "move.h"
93 #include "polygon.h"
94 #include "remove.h"
95 #include "rtree.h"
96 #include "search.h"
97 #include "set.h"
98 #include "thermal.h"
99 #include "undo.h"
101 #ifdef HAVE_LIBDMALLOC
102 #include <dmalloc.h>
103 #endif
105 #define ROUND(x) ((long)(((x) >= 0 ? (x) + 0.5 : (x) - 0.5)))
107 #define UNSUBTRACT_BLOAT 10
108 #define SUBTRACT_PIN_VIA_BATCH_SIZE 100
109 #define SUBTRACT_LINE_BATCH_SIZE 20
111 /* ---------------------------------------------------------------------------
112 * local prototypes
115 #define CIRC_SEGS 40
116 static double circleVerticies[] = {
117 1.0, 0.0,
118 0.98768834059513777, 0.15643446504023087,
120 /* adjustment to make the segments
121 * outline the circle rather than connect
122 * points on the circle
123 * 1 - cos(\alpha/2) < (\alpha/2)^2/2 */
124 static double radius_adjustment = (M_PI/CIRC_SEGS)*(M_PI/CIRC_SEGS)/2;
126 Cardinal
127 polygon_point_idx (PolygonTypePtr polygon, PointTypePtr point)
129 assert (point >= polygon->Points);
130 assert (point <= polygon->Points + polygon->PointN);
131 return ((char *)point - (char *)polygon->Points) / sizeof (PointType);
134 /* Find contour number: 0 for outer, 1 for first hole etc.. */
135 Cardinal
136 polygon_point_contour (PolygonTypePtr polygon, Cardinal point)
138 Cardinal i;
139 Cardinal contour = 0;
141 for (i = 0; i < polygon->HoleIndexN; i++)
142 if (point >= polygon->HoleIndex[i])
143 contour = i + 1;
144 return contour;
147 Cardinal
148 next_contour_point (PolygonTypePtr polygon, Cardinal point)
150 Cardinal contour;
151 Cardinal this_contour_start;
152 Cardinal next_contour_start;
154 contour = polygon_point_contour (polygon, point);
156 this_contour_start = (contour == 0) ? 0 :
157 polygon->HoleIndex[contour - 1];
158 next_contour_start =
159 (contour == polygon->HoleIndexN) ? polygon->PointN :
160 polygon->HoleIndex[contour];
162 /* Wrap back to the start of the contour we're in if we pass the end */
163 if (++point == next_contour_start)
164 point = this_contour_start;
166 return point;
169 Cardinal
170 prev_contour_point (PolygonTypePtr polygon, Cardinal point)
172 Cardinal contour;
173 Cardinal prev_contour_end;
174 Cardinal this_contour_end;
176 contour = polygon_point_contour (polygon, point);
178 prev_contour_end = (contour == 0) ? 0 :
179 polygon->HoleIndex[contour - 1];
180 this_contour_end =
181 (contour == polygon->HoleIndexN) ? polygon->PointN - 1:
182 polygon->HoleIndex[contour] - 1;
184 /* Wrap back to the start of the contour we're in if we pass the end */
185 if (point == prev_contour_end)
186 point = this_contour_end;
187 else
188 point--;
190 return point;
193 static void
194 add_noholes_polyarea (PLINE *pline, void *user_data)
196 PolygonType *poly = user_data;
198 /* Prepend the pline into the NoHoles linked list */
199 pline->next = poly->NoHoles;
200 poly->NoHoles = pline;
203 void
204 ComputeNoHoles (PolygonType *poly)
206 poly_FreeContours (&poly->NoHoles);
207 if (poly->Clipped)
208 NoHolesPolygonDicer (poly, NULL, add_noholes_polyarea, poly);
209 else
210 printf ("Compute_noholes caught poly->Clipped = NULL\n");
211 poly->NoHolesValid = 1;
214 static POLYAREA *
215 biggest (POLYAREA * p)
217 POLYAREA *n, *top = NULL;
218 PLINE *pl;
219 rtree_t *tree;
220 double big = -1;
221 if (!p)
222 return NULL;
223 n = p;
226 #if 0
227 if (n->contours->area < PCB->IsleArea)
229 n->b->f = n->f;
230 n->f->b = n->b;
231 poly_DelContour (&n->contours);
232 if (n == p)
233 p = n->f;
234 n = n->f;
235 if (!n->contours)
237 free (n);
238 return NULL;
241 #endif
242 if (n->contours->area > big)
244 top = n;
245 big = n->contours->area;
248 while ((n = n->f) != p);
249 assert (top);
250 if (top == p)
251 return p;
252 pl = top->contours;
253 tree = top->contour_tree;
254 top->contours = p->contours;
255 top->contour_tree = p->contour_tree;
256 p->contours = pl;
257 p->contour_tree = tree;
258 assert (pl);
259 assert (p->f);
260 assert (p->b);
261 return p;
264 POLYAREA *
265 ContourToPoly (PLINE * contour)
267 POLYAREA *p;
268 poly_PreContour (contour, TRUE);
269 assert (contour->Flags.orient == PLF_DIR);
270 if (!(p = poly_Create ()))
271 return NULL;
272 poly_InclContour (p, contour);
273 assert (poly_Valid (p));
274 return p;
277 static POLYAREA *
278 original_poly (PolygonType * p)
280 PLINE *contour = NULL;
281 POLYAREA *np = NULL;
282 Cardinal n;
283 Vector v;
284 int hole = 0;
286 if ((np = poly_Create ()) == NULL)
287 return NULL;
289 /* first make initial polygon contour */
290 for (n = 0; n < p->PointN; n++)
292 /* No current contour? Make a new one starting at point */
293 /* (or) Add point to existing contour */
295 v[0] = p->Points[n].X;
296 v[1] = p->Points[n].Y;
297 if (contour == NULL)
299 if ((contour = poly_NewContour (v)) == NULL)
300 return NULL;
302 else
304 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
307 /* Is current point last in contour? If so process it. */
308 if (n == p->PointN - 1 ||
309 (hole < p->HoleIndexN && n == p->HoleIndex[hole] - 1))
311 poly_PreContour (contour, TRUE);
313 /* make sure it is a positive contour (outer) or negative (hole) */
314 if (contour->Flags.orient != (hole ? PLF_INV : PLF_DIR))
315 poly_InvContour (contour);
316 assert (contour->Flags.orient == (hole ? PLF_INV : PLF_DIR));
318 poly_InclContour (np, contour);
319 contour = NULL;
320 assert (poly_Valid (np));
322 hole++;
325 return biggest (np);
328 POLYAREA *
329 PolygonToPoly (PolygonType *p)
331 return original_poly (p);
334 POLYAREA *
335 RectPoly (LocationType x1, LocationType x2, LocationType y1, LocationType y2)
337 PLINE *contour = NULL;
338 Vector v;
340 assert (x2 > x1);
341 assert (y2 > y1);
342 v[0] = x1;
343 v[1] = y1;
344 if ((contour = poly_NewContour (v)) == NULL)
345 return NULL;
346 v[0] = x2;
347 v[1] = y1;
348 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
349 v[0] = x2;
350 v[1] = y2;
351 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
352 v[0] = x1;
353 v[1] = y2;
354 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
355 return ContourToPoly (contour);
358 POLYAREA *
359 OctagonPoly (LocationType x, LocationType y, BDimension radius)
361 PLINE *contour = NULL;
362 Vector v;
364 v[0] = x + ROUND (radius * 0.5);
365 v[1] = y + ROUND (radius * TAN_22_5_DEGREE_2);
366 if ((contour = poly_NewContour (v)) == NULL)
367 return NULL;
368 v[0] = x + ROUND (radius * TAN_22_5_DEGREE_2);
369 v[1] = y + ROUND (radius * 0.5);
370 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
371 v[0] = x - (v[0] - x);
372 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
373 v[0] = x - ROUND (radius * 0.5);
374 v[1] = y + ROUND (radius * TAN_22_5_DEGREE_2);
375 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
376 v[1] = y - (v[1] - y);
377 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
378 v[0] = x - ROUND (radius * TAN_22_5_DEGREE_2);
379 v[1] = y - ROUND (radius * 0.5);
380 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
381 v[0] = x - (v[0] - x);
382 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
383 v[0] = x + ROUND (radius * 0.5);
384 v[1] = y - ROUND (radius * TAN_22_5_DEGREE_2);
385 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
386 return ContourToPoly (contour);
389 /* add verticies in a fractional-circle starting from v
390 * centered at X, Y and going counter-clockwise
391 * does not include the first point
392 * last argument is 1 for a full circle
393 * 2 for a half circle
394 * or 4 for a quarter circle
396 void
397 frac_circle (PLINE * c, LocationType X, LocationType Y, Vector v, int range)
399 double e1, e2, t1;
400 int i;
402 poly_InclVertex (c->head.prev, poly_CreateNode (v));
403 /* move vector to origin */
404 e1 = (v[0] - X) * (1 + radius_adjustment);
405 e2 = (v[1] - Y) * (1 + radius_adjustment);
407 /* NB: the caller adds the last vertex, hence the -1 */
408 range = range == 1 ? CIRC_SEGS-1 : (CIRC_SEGS / range) - 1;
409 for (i = 0; i < range; i++)
411 /* rotate the vector */
412 t1 = e1 * circleVerticies[2] - e2 * circleVerticies[3];
413 e2 = e1 * circleVerticies[3] + e2 * circleVerticies[2];
414 e1 = t1;
415 v[0] = X + ROUND (e1);
416 v[1] = Y + ROUND (e2);
417 poly_InclVertex (c->head.prev, poly_CreateNode (v));
421 /* create a circle approximation from lines */
422 POLYAREA *
423 CirclePoly (LocationType x, LocationType y, BDimension radius)
425 PLINE *contour;
426 Vector v;
428 if (radius <= 0)
429 return NULL;
430 v[0] = x + radius;
431 v[1] = y;
432 if ((contour = poly_NewContour (v)) == NULL)
433 return NULL;
434 frac_circle (contour, x, y, v, 1);
435 contour->is_round = TRUE;
436 contour->cx = x;
437 contour->cy = y;
438 contour->radius = radius;
439 return ContourToPoly (contour);
442 /* make a rounded-corner rectangle with radius t beyond x1,x2,y1,y2 rectangle */
443 POLYAREA *
444 RoundRect (LocationType x1, LocationType x2, LocationType y1, LocationType y2,
445 BDimension 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, BDimension 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 static const double delta_th = 0.02; /* polygon diverges from modelled arc
484 no more than delta_th * thick */
485 double radius_adj;
487 if (thick <= 0)
488 return NULL;
489 if (a->Delta < 0)
491 a->StartAngle += a->Delta;
492 a->Delta = -a->Delta;
494 half = (thick + 1) / 2;
495 ends = GetArcEnds (a);
496 /* start with inner radius */
497 rx = MAX (a->Width - half, 0);
498 ry = MAX (a->Height - half, 0);
499 segs = 1;
500 if(thick > 0)
501 segs = a->Delta * M_PI / 360
502 * sqrt(sqrt((double)rx*rx + (double)ry*ry)/delta_th/2/thick);
503 segs = MAX(segs, a->Delta / ARC_ANGLE);
505 ang = a->StartAngle;
506 da = (1.0 * a->Delta) / segs;
507 radius_adj = (M_PI*da/360)*(M_PI*da/360)/2;
508 v[0] = a->X - rx * cos (ang * M180);
509 v[1] = a->Y + ry * sin (ang * M180);
510 if ((contour = poly_NewContour (v)) == NULL)
511 return 0;
512 for (i = 0; i < segs - 1; i++)
514 ang += da;
515 v[0] = a->X - rx * cos (ang * M180);
516 v[1] = a->Y + ry * sin (ang * M180);
517 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
519 /* find last point */
520 ang = a->StartAngle + a->Delta;
521 v[0] = a->X - rx * cos (ang * M180) * (1 - radius_adj);
522 v[1] = a->Y + ry * sin (ang * M180) * (1 - radius_adj);
523 /* add the round cap at the end */
524 frac_circle (contour, ends->X2, ends->Y2, v, 2);
525 /* and now do the outer arc (going backwards) */
526 rx = (a->Width + half) * (1+radius_adj);
527 ry = (a->Width + half) * (1+radius_adj);
528 da = -da;
529 for (i = 0; i < segs; i++)
531 v[0] = a->X - rx * cos (ang * M180);
532 v[1] = a->Y + ry * sin (ang * M180);
533 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
534 ang += da;
536 /* now add other round cap */
537 ang = a->StartAngle;
538 v[0] = a->X - rx * cos (ang * M180) * (1 - radius_adj);
539 v[1] = a->Y + ry * sin (ang * M180) * (1 - radius_adj);
540 frac_circle (contour, ends->X1, ends->Y1, v, 2);
541 /* now we have the whole contour */
542 if (!(np = ContourToPoly (contour)))
543 return NULL;
544 return np;
547 #define MIN_CLEARANCE_BEFORE_BISECT 10.
548 POLYAREA *
549 ArcPoly (ArcType * a, BDimension thick)
551 double delta;
552 ArcType seg1, seg2;
553 POLYAREA *tmp1, *tmp2, *res;
555 delta = (a->Delta < 0) ? -a->Delta : a->Delta;
557 /* If the arc segment would self-intersect, we need to construct it as the union of
558 two non-intersecting segments */
560 if (2 * M_PI * a->Width * (1. - (double)delta / 360.) - thick < MIN_CLEARANCE_BEFORE_BISECT)
562 int half_delta = a->Delta / 2;
564 seg1 = seg2 = *a;
565 seg1.Delta = half_delta;
566 seg2.Delta -= half_delta;
567 seg2.StartAngle += half_delta;
569 tmp1 = ArcPolyNoIntersect (&seg1, thick);
570 tmp2 = ArcPolyNoIntersect (&seg2, thick);
571 poly_Boolean_free (tmp1, tmp2, &res, PBO_UNITE);
572 return res;
575 return ArcPolyNoIntersect (a, thick);
578 POLYAREA *
579 LinePoly (LineType * L, BDimension thick)
581 PLINE *contour = NULL;
582 POLYAREA *np = NULL;
583 Vector v;
584 double d, dx, dy;
585 long half;LineType _l=*L,*l=&_l;
587 if (thick <= 0)
588 return NULL;
589 half = (thick + 1) / 2;
591 sqrt (SQUARE (l->Point1.X - l->Point2.X) +
592 SQUARE (l->Point1.Y - l->Point2.Y));
593 if (!TEST_FLAG (SQUAREFLAG,l))
594 if (d == 0) /* line is a point */
595 return CirclePoly (l->Point1.X, l->Point1.Y, half);
596 if (d != 0)
598 d = half / d;
599 dx = (l->Point1.Y - l->Point2.Y) * d;
600 dy = (l->Point2.X - l->Point1.X) * d;
602 else
604 dx = half;
605 dy = 0;
607 if (TEST_FLAG (SQUAREFLAG,l))/* take into account the ends */
609 l->Point1.X -= dy;
610 l->Point1.Y += dx;
611 l->Point2.X += dy;
612 l->Point2.Y -= dx;
614 v[0] = l->Point1.X - dx;
615 v[1] = l->Point1.Y - dy;
616 if ((contour = poly_NewContour (v)) == NULL)
617 return 0;
618 v[0] = l->Point2.X - dx;
619 v[1] = l->Point2.Y - dy;
620 if (TEST_FLAG (SQUAREFLAG,l))
621 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
622 else
623 frac_circle (contour, l->Point2.X, l->Point2.Y, v, 2);
624 v[0] = l->Point2.X + dx;
625 v[1] = l->Point2.Y + dy;
626 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
627 v[0] = l->Point1.X + dx;
628 v[1] = l->Point1.Y + dy;
629 if (TEST_FLAG (SQUAREFLAG,l))
630 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
631 else
632 frac_circle (contour, l->Point1.X, l->Point1.Y, v, 2);
633 /* now we have the line contour */
634 if (!(np = ContourToPoly (contour)))
635 return NULL;
636 return np;
639 /* make a rounded-corner rectangle */
640 POLYAREA *
641 SquarePadPoly (PadType * pad, BDimension clear)
643 PLINE *contour = NULL;
644 POLYAREA *np = NULL;
645 Vector v;
646 double d;
647 double tx, ty;
648 double cx, cy;
649 PadType _t=*pad,*t=&_t;
650 PadType _c=*pad,*c=&_c;
651 int halfthick = (pad->Thickness + 1) / 2;
652 int halfclear = (clear + 1) / 2;
655 sqrt (SQUARE (pad->Point1.X - pad->Point2.X) +
656 SQUARE (pad->Point1.Y - pad->Point2.Y));
657 if (d != 0)
659 double a = halfthick / d;
660 tx = (t->Point1.Y - t->Point2.Y) * a;
661 ty = (t->Point2.X - t->Point1.X) * a;
662 a = halfclear / d;
663 cx = (c->Point1.Y - c->Point2.Y) * a;
664 cy = (c->Point2.X - c->Point1.X) * a;
666 t->Point1.X -= ty;
667 t->Point1.Y += tx;
668 t->Point2.X += ty;
669 t->Point2.Y -= tx;
670 c->Point1.X -= cy;
671 c->Point1.Y += cx;
672 c->Point2.X += cy;
673 c->Point2.Y -= cx;
675 else
677 tx = halfthick;
678 ty = 0;
679 cx = halfclear;
680 cy = 0;
682 t->Point1.Y += tx;
683 t->Point2.Y -= tx;
684 c->Point1.Y += cx;
685 c->Point2.Y -= cx;
688 v[0] = c->Point1.X - tx;
689 v[1] = c->Point1.Y - ty;
690 if ((contour = poly_NewContour (v)) == NULL)
691 return 0;
692 frac_circle (contour, (t->Point1.X - tx), (t->Point1.Y - ty), v, 4);
694 v[0] = t->Point2.X - cx;
695 v[1] = t->Point2.Y - cy;
696 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
697 frac_circle (contour, (t->Point2.X - tx), (t->Point2.Y - ty), v, 4);
699 v[0] = c->Point2.X + tx;
700 v[1] = c->Point2.Y + ty;
701 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
702 frac_circle (contour, (t->Point2.X + tx), (t->Point2.Y + ty), v, 4);
704 v[0] = t->Point1.X + cx;
705 v[1] = t->Point1.Y + cy;
706 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
707 frac_circle (contour, (t->Point1.X + tx), (t->Point1.Y + ty), v, 4);
709 /* now we have the line contour */
710 if (!(np = ContourToPoly (contour)))
711 return NULL;
712 return np;
715 /* clear np1 from the polygon */
716 static int
717 Subtract (POLYAREA * np1, PolygonType * p, bool fnp)
719 POLYAREA *merged = NULL, *np = np1;
720 int x;
721 assert (np);
722 assert (p);
723 if (!p->Clipped)
725 if (fnp)
726 poly_Free (&np);
727 return 1;
729 assert (poly_Valid (p->Clipped));
730 assert (poly_Valid (np));
731 if (fnp)
732 x = poly_Boolean_free (p->Clipped, np, &merged, PBO_SUB);
733 else
735 x = poly_Boolean (p->Clipped, np, &merged, PBO_SUB);
736 poly_Free (&p->Clipped);
738 assert (!merged || poly_Valid (merged));
739 if (x != err_ok)
741 fprintf (stderr, "Error while clipping PBO_SUB: %d\n", x);
742 poly_Free (&merged);
743 p->Clipped = NULL;
744 if (p->NoHoles) printf ("Just leaked in Subtract\n");
745 p->NoHoles = NULL;
746 return -1;
748 p->Clipped = biggest (merged);
749 assert (!p->Clipped || poly_Valid (p->Clipped));
750 if (!p->Clipped)
751 Message ("Polygon cleared out of existence near (%d, %d)\n",
752 (p->BoundingBox.X1 + p->BoundingBox.X2) / 2,
753 (p->BoundingBox.Y1 + p->BoundingBox.Y2) / 2);
754 return 1;
757 /* create a polygon of the pin clearance */
758 POLYAREA *
759 PinPoly (PinType * pin, BDimension thick, BDimension clear)
761 int size;
763 if (TEST_FLAG (SQUAREFLAG, pin))
765 size = (thick + 1) / 2;
766 return RoundRect (pin->X - size, pin->X + size, pin->Y - size,
767 pin->Y + size, (clear + 1) / 2);
769 else
771 size = (thick + clear + 1) / 2;
772 if (TEST_FLAG (OCTAGONFLAG, pin))
774 return OctagonPoly (pin->X, pin->Y, size + size);
777 return CirclePoly (pin->X, pin->Y, size);
780 POLYAREA *
781 BoxPolyBloated (BoxType *box, BDimension bloat)
783 return RectPoly (box->X1 - bloat, box->X2 + bloat,
784 box->Y1 - bloat, box->Y2 + bloat);
787 /* remove the pin clearance from the polygon */
788 static int
789 SubtractPin (DataType * d, PinType * pin, LayerType * l, PolygonType * p)
791 POLYAREA *np;
792 Cardinal i;
794 if (pin->Clearance == 0)
795 return 0;
796 i = GetLayerNumber (d, l);
797 if (TEST_THERM (i, pin))
799 np = ThermPoly ((PCBTypePtr) (d->pcb), pin, i);
800 if (!np)
801 return 0;
803 else
805 np = PinPoly (pin, pin->Thickness, pin->Clearance);
806 if (!np)
807 return -1;
809 return Subtract (np, p, TRUE);
812 static int
813 SubtractLine (LineType * line, PolygonType * p)
815 POLYAREA *np;
817 if (!TEST_FLAG (CLEARLINEFLAG, line))
818 return 0;
819 if (!(np = LinePoly (line, line->Thickness + line->Clearance)))
820 return -1;
821 return Subtract (np, p, true);
824 static int
825 SubtractArc (ArcType * arc, PolygonType * p)
827 POLYAREA *np;
829 if (!TEST_FLAG (CLEARLINEFLAG, arc))
830 return 0;
831 if (!(np = ArcPoly (arc, arc->Thickness + arc->Clearance)))
832 return -1;
833 return Subtract (np, p, true);
836 static int
837 SubtractText (TextType * text, PolygonType * p)
839 POLYAREA *np;
840 const BoxType *b = &text->BoundingBox;
842 if (!TEST_FLAG (CLEARLINEFLAG, text))
843 return 0;
844 if (!(np = RoundRect (b->X1 + PCB->Bloat, b->X2 - PCB->Bloat,
845 b->Y1 + PCB->Bloat, b->Y2 - PCB->Bloat, PCB->Bloat)))
846 return -1;
847 return Subtract (np, p, true);
850 static int
851 SubtractPad (PadType * pad, PolygonType * p)
853 POLYAREA *np = NULL;
855 if (pad->Clearance == 0)
856 return 0;
857 if (TEST_FLAG (SQUAREFLAG, pad))
859 if (!
860 (np = SquarePadPoly (pad, pad->Thickness + pad->Clearance)))
861 return -1;
863 else
865 if (!
866 (np = LinePoly ((LineType *) pad, pad->Thickness + pad->Clearance)))
867 return -1;
869 return Subtract (np, p, true);
872 struct cpInfo
874 const BoxType *other;
875 DataType *data;
876 LayerType *layer;
877 PolygonType *polygon;
878 bool solder;
879 POLYAREA *accumulate;
880 int batch_size;
881 jmp_buf env;
884 static void
885 subtract_accumulated (struct cpInfo *info, PolygonTypePtr polygon)
887 if (info->accumulate == NULL)
888 return;
889 Subtract (info->accumulate, polygon, true);
890 info->accumulate = NULL;
891 info->batch_size = 0;
894 static int
895 pin_sub_callback (const BoxType * b, void *cl)
897 PinTypePtr pin = (PinTypePtr) b;
898 struct cpInfo *info = (struct cpInfo *) cl;
899 PolygonTypePtr polygon;
900 POLYAREA *np;
901 POLYAREA *merged;
902 Cardinal i;
904 /* don't subtract the object that was put back! */
905 if (b == info->other)
906 return 0;
907 polygon = info->polygon;
909 if (pin->Clearance == 0)
910 return 0;
911 i = GetLayerNumber (info->data, info->layer);
912 if (TEST_THERM (i, pin))
914 np = ThermPoly ((PCBTypePtr) (info->data->pcb), pin, i);
915 if (!np)
916 return 1;
918 else
920 np = PinPoly (pin, pin->Thickness, pin->Clearance);
921 if (!np)
922 longjmp (info->env, 1);
925 poly_Boolean_free (info->accumulate, np, &merged, PBO_UNITE);
926 info->accumulate = merged;
928 info->batch_size ++;
930 if (info->batch_size == SUBTRACT_PIN_VIA_BATCH_SIZE)
931 subtract_accumulated (info, polygon);
933 return 1;
936 static int
937 arc_sub_callback (const BoxType * b, void *cl)
939 ArcTypePtr arc = (ArcTypePtr) b;
940 struct cpInfo *info = (struct cpInfo *) cl;
941 PolygonTypePtr polygon;
943 /* don't subtract the object that was put back! */
944 if (b == info->other)
945 return 0;
946 if (!TEST_FLAG (CLEARLINEFLAG, arc))
947 return 0;
948 polygon = info->polygon;
949 if (SubtractArc (arc, polygon) < 0)
950 longjmp (info->env, 1);
951 return 1;
954 static int
955 pad_sub_callback (const BoxType * b, void *cl)
957 PadTypePtr pad = (PadTypePtr) b;
958 struct cpInfo *info = (struct cpInfo *) cl;
959 PolygonTypePtr polygon;
961 /* don't subtract the object that was put back! */
962 if (b == info->other)
963 return 0;
964 if (pad->Clearance == 0)
965 return 0;
966 polygon = info->polygon;
967 if (XOR (TEST_FLAG (ONSOLDERFLAG, pad), !info->solder))
969 if (SubtractPad (pad, polygon) < 0)
970 longjmp (info->env, 1);
971 return 1;
973 return 0;
976 static int
977 line_sub_callback (const BoxType * b, void *cl)
979 LineTypePtr line = (LineTypePtr) b;
980 struct cpInfo *info = (struct cpInfo *) cl;
981 PolygonTypePtr polygon;
982 POLYAREA *np;
983 POLYAREA *merged;
985 /* don't subtract the object that was put back! */
986 if (b == info->other)
987 return 0;
988 if (!TEST_FLAG (CLEARLINEFLAG, line))
989 return 0;
990 polygon = info->polygon;
992 if (!(np = LinePoly (line, line->Thickness + line->Clearance)))
993 longjmp (info->env, 1);
995 poly_Boolean_free (info->accumulate, np, &merged, PBO_UNITE);
996 info->accumulate = merged;
997 info->batch_size ++;
999 if (info->batch_size == SUBTRACT_LINE_BATCH_SIZE)
1000 subtract_accumulated (info, polygon);
1002 return 1;
1005 static int
1006 text_sub_callback (const BoxType * b, void *cl)
1008 TextTypePtr text = (TextTypePtr) b;
1009 struct cpInfo *info = (struct cpInfo *) cl;
1010 PolygonTypePtr polygon;
1012 /* don't subtract the object that was put back! */
1013 if (b == info->other)
1014 return 0;
1015 if (!TEST_FLAG (CLEARLINEFLAG, text))
1016 return 0;
1017 polygon = info->polygon;
1018 if (SubtractText (text, polygon) < 0)
1019 longjmp (info->env, 1);
1020 return 1;
1023 static int
1024 Group (DataTypePtr Data, Cardinal layer)
1026 Cardinal i, j;
1027 for (i = 0; i < max_group; i++)
1028 for (j = 0; j < ((PCBType *) (Data->pcb))->LayerGroups.Number[i]; j++)
1029 if (layer == ((PCBType *) (Data->pcb))->LayerGroups.Entries[i][j])
1030 return i;
1031 return i;
1034 static int
1035 clearPoly (DataTypePtr Data, LayerTypePtr Layer, PolygonType * polygon,
1036 const BoxType * here, BDimension expand)
1038 int r = 0;
1039 BoxType region;
1040 struct cpInfo info;
1041 Cardinal group;
1043 if (!TEST_FLAG (CLEARPOLYFLAG, polygon)
1044 || GetLayerNumber (Data, Layer) >= max_copper_layer)
1045 return 0;
1046 group = Group (Data, GetLayerNumber (Data, Layer));
1047 info.solder = (group == Group (Data, solder_silk_layer));
1048 info.data = Data;
1049 info.other = here;
1050 info.layer = Layer;
1051 info.polygon = polygon;
1052 if (here)
1053 region = clip_box (here, &polygon->BoundingBox);
1054 else
1055 region = polygon->BoundingBox;
1056 region = bloat_box (&region, expand);
1058 if (setjmp (info.env) == 0)
1060 r = 0;
1061 info.accumulate = NULL;
1062 info.batch_size = 0;
1063 if (info.solder || group == Group (Data, component_silk_layer))
1064 r += r_search (Data->pad_tree, &region, NULL, pad_sub_callback, &info);
1065 GROUP_LOOP (Data, group);
1067 r +=
1068 r_search (layer->line_tree, &region, NULL, line_sub_callback,
1069 &info);
1070 subtract_accumulated (&info, polygon);
1071 r +=
1072 r_search (layer->arc_tree, &region, NULL, arc_sub_callback, &info);
1073 r +=
1074 r_search (layer->text_tree, &region, NULL, text_sub_callback, &info);
1076 END_LOOP;
1077 r += r_search (Data->via_tree, &region, NULL, pin_sub_callback, &info);
1078 r += r_search (Data->pin_tree, &region, NULL, pin_sub_callback, &info);
1079 subtract_accumulated (&info, polygon);
1081 polygon->NoHolesValid = 0;
1082 return r;
1085 static int
1086 Unsubtract (POLYAREA * np1, PolygonType * p)
1088 POLYAREA *merged = NULL, *np = np1;
1089 POLYAREA *orig_poly, *clipped_np;
1090 int x;
1091 assert (np);
1092 assert (p && p->Clipped);
1094 orig_poly = original_poly (p);
1096 x = poly_Boolean_free (np, orig_poly, &clipped_np, PBO_ISECT);
1097 if (x != err_ok)
1099 fprintf (stderr, "Error while clipping PBO_ISECT: %d\n", x);
1100 poly_Free (&clipped_np);
1101 goto fail;
1104 x = poly_Boolean_free (p->Clipped, clipped_np, &merged, PBO_UNITE);
1105 if (x != err_ok)
1107 fprintf (stderr, "Error while clipping PBO_UNITE: %d\n", x);
1108 poly_Free (&merged);
1109 goto fail;
1111 p->Clipped = biggest (merged);
1112 assert (!p->Clipped || poly_Valid (p->Clipped));
1113 return 1;
1115 fail:
1116 p->Clipped = NULL;
1117 if (p->NoHoles) printf ("Just leaked in Unsubtract\n");
1118 p->NoHoles = NULL;
1119 return 0;
1122 static int
1123 UnsubtractPin (PinType * pin, LayerType * l, PolygonType * p)
1125 POLYAREA *np;
1127 /* overlap a bit to prevent gaps from rounding errors */
1128 np = BoxPolyBloated (&pin->BoundingBox, UNSUBTRACT_BLOAT);
1130 if (!np)
1131 return 0;
1132 if (!Unsubtract (np, p))
1133 return 0;
1134 clearPoly (PCB->Data, l, p, (const BoxType *) pin, 2 * UNSUBTRACT_BLOAT);
1135 return 1;
1138 static int
1139 UnsubtractArc (ArcType * arc, LayerType * l, PolygonType * p)
1141 POLYAREA *np;
1143 if (!TEST_FLAG (CLEARLINEFLAG, arc))
1144 return 0;
1146 /* overlap a bit to prevent gaps from rounding errors */
1147 np = BoxPolyBloated (&arc->BoundingBox, UNSUBTRACT_BLOAT);
1149 if (!np)
1150 return 0;
1151 if (!Unsubtract (np, p))
1152 return 0;
1153 clearPoly (PCB->Data, l, p, (const BoxType *) arc, 2 * UNSUBTRACT_BLOAT);
1154 return 1;
1157 static int
1158 UnsubtractLine (LineType * line, LayerType * l, PolygonType * p)
1160 POLYAREA *np;
1162 if (!TEST_FLAG (CLEARLINEFLAG, line))
1163 return 0;
1165 /* overlap a bit to prevent notches from rounding errors */
1166 np = BoxPolyBloated (&line->BoundingBox, UNSUBTRACT_BLOAT);
1168 if (!np)
1169 return 0;
1170 if (!Unsubtract (np, p))
1171 return 0;
1172 clearPoly (PCB->Data, l, p, (const BoxType *) line, 2 * UNSUBTRACT_BLOAT);
1173 return 1;
1176 static int
1177 UnsubtractText (TextType * text, LayerType * l, PolygonType * p)
1179 POLYAREA *np;
1181 if (!TEST_FLAG (CLEARLINEFLAG, text))
1182 return 0;
1184 /* overlap a bit to prevent notches from rounding errors */
1185 np = BoxPolyBloated (&text->BoundingBox, UNSUBTRACT_BLOAT);
1187 if (!np)
1188 return -1;
1189 if (!Unsubtract (np, p))
1190 return 0;
1191 clearPoly (PCB->Data, l, p, (const BoxType *) text, 2 * UNSUBTRACT_BLOAT);
1192 return 1;
1195 static int
1196 UnsubtractPad (PadType * pad, LayerType * l, PolygonType * p)
1198 POLYAREA *np;
1200 /* overlap a bit to prevent notches from rounding errors */
1201 np = BoxPolyBloated (&pad->BoundingBox, UNSUBTRACT_BLOAT);
1203 if (!np)
1204 return 0;
1205 if (!Unsubtract (np, p))
1206 return 0;
1207 clearPoly (PCB->Data, l, p, (const BoxType *) pad, 2 * UNSUBTRACT_BLOAT);
1208 return 1;
1211 static bool inhibit = false;
1214 InitClip (DataTypePtr Data, LayerTypePtr layer, PolygonType * p)
1216 if (inhibit)
1217 return 0;
1218 if (p->Clipped)
1219 poly_Free (&p->Clipped);
1220 p->Clipped = original_poly (p);
1221 poly_FreeContours (&p->NoHoles);
1222 if (!p->Clipped)
1223 return 0;
1224 assert (poly_Valid (p->Clipped));
1225 if (TEST_FLAG (CLEARPOLYFLAG, p))
1226 clearPoly (Data, layer, p, NULL, 0);
1227 else
1228 p->NoHolesValid = 0;
1229 return 1;
1232 /* --------------------------------------------------------------------------
1233 * remove redundant polygon points. Any point that lies on the straight
1234 * line between the points on either side of it is redundant.
1235 * returns true if any points are removed
1237 bool
1238 RemoveExcessPolygonPoints (LayerTypePtr Layer, PolygonTypePtr Polygon)
1240 PointTypePtr p;
1241 Cardinal n, prev, next;
1242 LineType line;
1243 bool changed = false;
1245 if (Undoing ())
1246 return (false);
1248 for (n = 0; n < Polygon->PointN; n++)
1250 prev = prev_contour_point (Polygon, n);
1251 next = next_contour_point (Polygon, n);
1252 p = &Polygon->Points[n];
1254 line.Point1 = Polygon->Points[prev];
1255 line.Point2 = Polygon->Points[next];
1256 line.Thickness = 0;
1257 if (IsPointOnLine ((float) p->X, (float) p->Y, 0.0, &line))
1259 RemoveObject (POLYGONPOINT_TYPE, Layer, Polygon, p);
1260 changed = true;
1263 return (changed);
1266 /* ---------------------------------------------------------------------------
1267 * returns the index of the polygon point which is the end
1268 * point of the segment with the lowest distance to the passed
1269 * coordinates
1271 Cardinal
1272 GetLowestDistancePolygonPoint (PolygonTypePtr Polygon, LocationType X,
1273 LocationType Y)
1275 double mindistance = (double) MAX_COORD * MAX_COORD;
1276 PointTypePtr ptr1, ptr2;
1277 Cardinal n, result = 0;
1279 /* we calculate the distance to each segment and choose the
1280 * shortest distance. If the closest approach between the
1281 * given point and the projected line (i.e. the segment extended)
1282 * is not on the segment, then the distance is the distance
1283 * to the segment end point.
1286 for (n = 0; n < Polygon->PointN; n++)
1288 register double u, dx, dy;
1289 ptr1 = &Polygon->Points[prev_contour_point (Polygon, n)];
1290 ptr2 = &Polygon->Points[n];
1292 dx = ptr2->X - ptr1->X;
1293 dy = ptr2->Y - ptr1->Y;
1294 if (dx != 0.0 || dy != 0.0)
1296 /* projected intersection is at P1 + u(P2 - P1) */
1297 u = ((X - ptr1->X) * dx + (Y - ptr1->Y) * dy) / (dx * dx + dy * dy);
1299 if (u < 0.0)
1300 { /* ptr1 is closest point */
1301 u = SQUARE (X - ptr1->X) + SQUARE (Y - ptr1->Y);
1303 else if (u > 1.0)
1304 { /* ptr2 is closest point */
1305 u = SQUARE (X - ptr2->X) + SQUARE (Y - ptr2->Y);
1307 else
1308 { /* projected intersection is closest point */
1309 u = SQUARE (X - ptr1->X * (1.0 - u) - u * ptr2->X) +
1310 SQUARE (Y - ptr1->Y * (1.0 - u) - u * ptr2->Y);
1312 if (u < mindistance)
1314 mindistance = u;
1315 result = n;
1319 return (result);
1322 /* ---------------------------------------------------------------------------
1323 * go back to the previous point of the polygon
1325 void
1326 GoToPreviousPoint (void)
1328 switch (Crosshair.AttachedPolygon.PointN)
1330 /* do nothing if mode has just been entered */
1331 case 0:
1332 break;
1334 /* reset number of points and 'LINE_MODE' state */
1335 case 1:
1336 Crosshair.AttachedPolygon.PointN = 0;
1337 Crosshair.AttachedLine.State = STATE_FIRST;
1338 addedLines = 0;
1339 break;
1341 /* back-up one point */
1342 default:
1344 PointTypePtr points = Crosshair.AttachedPolygon.Points;
1345 Cardinal n = Crosshair.AttachedPolygon.PointN - 2;
1347 Crosshair.AttachedPolygon.PointN--;
1348 Crosshair.AttachedLine.Point1.X = points[n].X;
1349 Crosshair.AttachedLine.Point1.Y = points[n].Y;
1350 break;
1355 /* ---------------------------------------------------------------------------
1356 * close polygon if possible
1358 void
1359 ClosePolygon (void)
1361 Cardinal n = Crosshair.AttachedPolygon.PointN;
1363 /* check number of points */
1364 if (n >= 3)
1366 /* if 45 degree lines are what we want do a quick check
1367 * if closing the polygon makes sense
1369 if (!TEST_FLAG (ALLDIRECTIONFLAG, PCB))
1371 BDimension dx, dy;
1373 dx = abs (Crosshair.AttachedPolygon.Points[n - 1].X -
1374 Crosshair.AttachedPolygon.Points[0].X);
1375 dy = abs (Crosshair.AttachedPolygon.Points[n - 1].Y -
1376 Crosshair.AttachedPolygon.Points[0].Y);
1377 if (!(dx == 0 || dy == 0 || dx == dy))
1379 Message
1381 ("Cannot close polygon because 45 degree lines are requested.\n"));
1382 return;
1385 CopyAttachedPolygonToLayer ();
1386 Draw ();
1388 else
1389 Message (_("A polygon has to have at least 3 points\n"));
1392 /* ---------------------------------------------------------------------------
1393 * moves the data of the attached (new) polygon to the current layer
1395 void
1396 CopyAttachedPolygonToLayer (void)
1398 PolygonTypePtr polygon;
1399 int saveID;
1401 /* move data to layer and clear attached struct */
1402 polygon = CreateNewPolygon (CURRENT, NoFlags ());
1403 saveID = polygon->ID;
1404 *polygon = Crosshair.AttachedPolygon;
1405 polygon->ID = saveID;
1406 SET_FLAG (CLEARPOLYFLAG, polygon);
1407 if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1408 SET_FLAG (FULLPOLYFLAG, polygon);
1409 memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
1410 SetPolygonBoundingBox (polygon);
1411 if (!CURRENT->polygon_tree)
1412 CURRENT->polygon_tree = r_create_tree (NULL, 0, 0);
1413 r_insert_entry (CURRENT->polygon_tree, (BoxType *) polygon, 0);
1414 InitClip (PCB->Data, CURRENT, polygon);
1415 DrawPolygon (CURRENT, polygon, 0);
1416 SetChangedFlag (true);
1418 /* reset state of attached line */
1419 Crosshair.AttachedLine.State = STATE_FIRST;
1420 addedLines = 0;
1422 /* add to undo list */
1423 AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT, polygon, polygon);
1424 IncrementUndoSerialNumber ();
1427 /* find polygon holes in range, then call the callback function for
1428 * each hole. If the callback returns non-zero, stop
1429 * the search.
1432 PolygonHoles (PolygonType *polygon, const BoxType *range,
1433 int (*callback) (PLINE *contour, void *user_data),
1434 void *user_data)
1436 POLYAREA *pa = polygon->Clipped;
1437 PLINE *pl;
1438 /* If this hole is so big the polygon doesn't exist, then it's not
1439 * really a hole.
1441 if (!pa)
1442 return 0;
1443 for (pl = pa->contours->next; pl; pl = pl->next)
1445 if (range != NULL &&
1446 (pl->xmin > range->X2 || pl->xmax < range->X1 ||
1447 pl->ymin > range->Y2 || pl->ymax < range->Y1))
1448 continue;
1449 if (callback (pl, user_data))
1451 return 1;
1454 return 0;
1457 struct plow_info
1459 int type;
1460 void *ptr1, *ptr2;
1461 LayerTypePtr layer;
1462 DataTypePtr data;
1463 int (*callback) (DataTypePtr, LayerTypePtr, PolygonTypePtr, int, void *,
1464 void *);
1467 static int
1468 subtract_plow (DataTypePtr Data, LayerTypePtr Layer, PolygonTypePtr Polygon,
1469 int type, void *ptr1, void *ptr2)
1471 if (!Polygon->Clipped)
1472 return 0;
1473 switch (type)
1475 case PIN_TYPE:
1476 case VIA_TYPE:
1477 SubtractPin (Data, (PinTypePtr) ptr2, Layer, Polygon);
1478 Polygon->NoHolesValid = 0;
1479 return 1;
1480 case LINE_TYPE:
1481 SubtractLine ((LineTypePtr) ptr2, Polygon);
1482 Polygon->NoHolesValid = 0;
1483 return 1;
1484 case ARC_TYPE:
1485 SubtractArc ((ArcTypePtr) ptr2, Polygon);
1486 Polygon->NoHolesValid = 0;
1487 return 1;
1488 case PAD_TYPE:
1489 SubtractPad ((PadTypePtr) ptr2, Polygon);
1490 Polygon->NoHolesValid = 0;
1491 return 1;
1492 case TEXT_TYPE:
1493 SubtractText ((TextTypePtr) ptr2, Polygon);
1494 Polygon->NoHolesValid = 0;
1495 return 1;
1497 return 0;
1500 static int
1501 add_plow (DataTypePtr Data, LayerTypePtr Layer, PolygonTypePtr Polygon,
1502 int type, void *ptr1, void *ptr2)
1504 switch (type)
1506 case PIN_TYPE:
1507 case VIA_TYPE:
1508 UnsubtractPin ((PinTypePtr) ptr2, Layer, Polygon);
1509 return 1;
1510 case LINE_TYPE:
1511 UnsubtractLine ((LineTypePtr) ptr2, Layer, Polygon);
1512 return 1;
1513 case ARC_TYPE:
1514 UnsubtractArc ((ArcTypePtr) ptr2, Layer, Polygon);
1515 return 1;
1516 case PAD_TYPE:
1517 UnsubtractPad ((PadTypePtr) ptr2, Layer, Polygon);
1518 return 1;
1519 case TEXT_TYPE:
1520 UnsubtractText ((TextTypePtr) ptr2, Layer, Polygon);
1521 return 1;
1523 return 0;
1526 static int
1527 plow_callback (const BoxType * b, void *cl)
1529 struct plow_info *plow = (struct plow_info *) cl;
1530 PolygonTypePtr polygon = (PolygonTypePtr) b;
1532 if (TEST_FLAG (CLEARPOLYFLAG, polygon))
1533 return plow->callback (plow->data, plow->layer, polygon, plow->type,
1534 plow->ptr1, plow->ptr2);
1535 return 0;
1539 PlowsPolygon (DataType * Data, int type, void *ptr1, void *ptr2,
1540 int (*call_back) (DataTypePtr data, LayerTypePtr lay,
1541 PolygonTypePtr poly, int type, void *ptr1,
1542 void *ptr2))
1544 BoxType sb = ((PinTypePtr) 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 switch (type)
1555 case PIN_TYPE:
1556 case VIA_TYPE:
1557 if (type == PIN_TYPE || ptr1 == ptr2 || ptr1 == NULL)
1559 LAYER_LOOP (Data, max_copper_layer);
1561 info.layer = layer;
1562 r +=
1563 r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
1565 END_LOOP;
1567 else
1569 GROUP_LOOP (Data, GetLayerGroupNumberByNumber (GetLayerNumber (Data,
1570 ((LayerTypePtr) ptr1))));
1572 info.layer = layer;
1573 r +=
1574 r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
1576 END_LOOP;
1578 break;
1579 case LINE_TYPE:
1580 case ARC_TYPE:
1581 case TEXT_TYPE:
1582 /* the cast works equally well for lines and arcs */
1583 if (!TEST_FLAG (CLEARLINEFLAG, (LineTypePtr) ptr2))
1584 return 0;
1585 /* silk doesn't plow */
1586 if (GetLayerNumber (Data, ptr1) >= max_copper_layer)
1587 return 0;
1588 GROUP_LOOP (Data, GetLayerGroupNumberByNumber (GetLayerNumber (Data,
1589 ((LayerTypePtr) ptr1))));
1591 info.layer = layer;
1592 r += r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
1594 END_LOOP;
1595 break;
1596 case PAD_TYPE:
1598 Cardinal group = GetLayerGroupNumberByNumber (
1599 TEST_FLAG (ONSOLDERFLAG, (PadType *) ptr2) ?
1600 solder_silk_layer : component_silk_layer);
1601 GROUP_LOOP (Data, group);
1603 info.layer = layer;
1604 r +=
1605 r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
1607 END_LOOP;
1609 break;
1611 case ELEMENT_TYPE:
1613 PIN_LOOP ((ElementType *) ptr1);
1615 PlowsPolygon (Data, PIN_TYPE, ptr1, pin, call_back);
1617 END_LOOP;
1618 PAD_LOOP ((ElementType *) ptr1);
1620 PlowsPolygon (Data, PAD_TYPE, ptr1, pad, call_back);
1622 END_LOOP;
1624 break;
1626 return r;
1629 void
1630 RestoreToPolygon (DataType * Data, int type, void *ptr1, void *ptr2)
1632 if (type == POLYGON_TYPE)
1633 InitClip (PCB->Data, (LayerTypePtr) ptr1, (PolygonTypePtr) ptr2);
1634 else
1635 PlowsPolygon (Data, type, ptr1, ptr2, add_plow);
1638 void
1639 ClearFromPolygon (DataType * Data, int type, void *ptr1, void *ptr2)
1641 if (type == POLYGON_TYPE)
1642 InitClip (PCB->Data, (LayerTypePtr) ptr1, (PolygonTypePtr) ptr2);
1643 else
1644 PlowsPolygon (Data, type, ptr1, ptr2, subtract_plow);
1647 bool
1648 isects (POLYAREA * a, PolygonTypePtr p, bool fr)
1650 POLYAREA *x;
1651 bool ans;
1652 ans = Touching (a, p->Clipped);
1653 /* argument may be register, so we must copy it */
1654 x = a;
1655 if (fr)
1656 poly_Free (&x);
1657 return ans;
1661 bool
1662 IsPointInPolygon (LocationType X, LocationType Y, BDimension r,
1663 PolygonTypePtr p)
1665 POLYAREA *c;
1666 Vector v;
1667 v[0] = X;
1668 v[1] = Y;
1669 if (poly_CheckInside (p->Clipped, v))
1670 return true;
1671 if (r < 1)
1672 return false;
1673 if (!(c = CirclePoly (X, Y, r)))
1674 return false;
1675 return isects (c, p, true);
1679 bool
1680 IsPointInPolygonIgnoreHoles (LocationType X, LocationType Y, PolygonTypePtr p)
1682 Vector v;
1683 v[0] = X;
1684 v[1] = Y;
1685 return poly_InsideContour (p->Clipped->contours, v);
1688 bool
1689 IsRectangleInPolygon (LocationType X1, LocationType Y1, LocationType X2,
1690 LocationType Y2, PolygonTypePtr p)
1692 POLYAREA *s;
1693 if (!
1694 (s = RectPoly (min (X1, X2), max (X1, X2), min (Y1, Y2), max (Y1, Y2))))
1695 return false;
1696 return isects (s, p, true);
1699 /* NB: This function will free the passed POLYAREA.
1700 * It must only be passed a single POLYAREA (pa->f == pa->b == pa)
1702 static void
1703 r_NoHolesPolygonDicer (POLYAREA * pa,
1704 void (*emit) (PLINE *, void *), void *user_data)
1706 PLINE *p = pa->contours;
1708 if (!pa->contours->next) /* no holes */
1710 pa->contours = NULL; /* The callback now owns the contour */
1711 /* Don't bother removing it from the POLYAREA's rtree
1712 since we're going to free the POLYAREA below anyway */
1713 emit (p, user_data);
1714 poly_Free (&pa);
1715 return;
1717 else
1719 POLYAREA *poly2, *left, *right;
1721 /* make a rectangle of the left region slicing through the middle of the first hole */
1722 poly2 = RectPoly (p->xmin, (p->next->xmin + p->next->xmax) / 2,
1723 p->ymin, p->ymax);
1724 poly_AndSubtract_free (pa, poly2, &left, &right);
1725 if (left)
1727 POLYAREA *cur, *next;
1728 cur = left;
1731 next = cur->f;
1732 cur->f = cur->b = cur; /* Detach this polygon piece */
1733 r_NoHolesPolygonDicer (cur, emit, user_data);
1734 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1736 while ((cur = next) != left);
1738 if (right)
1740 POLYAREA *cur, *next;
1741 cur = right;
1744 next = cur->f;
1745 cur->f = cur->b = cur; /* Detach this polygon piece */
1746 r_NoHolesPolygonDicer (cur, emit, user_data);
1747 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1749 while ((cur = next) != right);
1754 void
1755 NoHolesPolygonDicer (PolygonTypePtr p, const BoxType * clip,
1756 void (*emit) (PLINE *, void *), void *user_data)
1758 POLYAREA *save, *ans, *cur, *next;
1760 ans = save = poly_Create ();
1761 /* copy the main poly only */
1762 poly_Copy1 (save, p->Clipped);
1763 /* clip to the bounding box */
1764 if (clip)
1766 POLYAREA *cbox = RectPoly (clip->X1, clip->X2, clip->Y1, clip->Y2);
1767 if (cbox)
1769 int r = poly_Boolean_free (save, cbox, &ans, PBO_ISECT);
1770 save = ans;
1771 if (r != err_ok)
1772 save = NULL;
1775 if (!save)
1776 return;
1777 /* Now dice it up.
1778 * NB: Could be more than one piece (because of the clip above)
1780 cur = save;
1783 next = cur->f;
1784 cur->f = cur->b = cur; /* Detach this polygon piece */
1785 r_NoHolesPolygonDicer (cur, emit, user_data);
1786 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1788 while ((cur = next) != save);
1791 /* make a polygon split into multiple parts into multiple polygons */
1792 bool
1793 MorphPolygon (LayerTypePtr layer, PolygonTypePtr poly)
1795 POLYAREA *p, *start;
1796 bool many = false;
1797 FlagType flags;
1799 if (!poly->Clipped || TEST_FLAG (LOCKFLAG, poly))
1800 return false;
1801 if (poly->Clipped->f == poly->Clipped)
1802 return false;
1803 ErasePolygon (poly);
1804 start = p = poly->Clipped;
1805 /* This is ugly. The creation of the new polygons can cause
1806 * all of the polygon pointers (including the one we're called
1807 * with to change if there is a realloc in GetPolygonMemory().
1808 * That will invalidate our original "poly" argument, potentially
1809 * inside the loop. We need to steal the Clipped pointer and
1810 * hide it from the Remove call so that it still exists while
1811 * we do this dirty work.
1813 poly->Clipped = NULL;
1814 if (poly->NoHoles) printf ("Just leaked in MorpyPolygon\n");
1815 poly->NoHoles = NULL;
1816 flags = poly->Flags;
1817 RemovePolygon (layer, poly);
1818 inhibit = true;
1821 VNODE *v;
1822 PolygonTypePtr new;
1824 if (p->contours->area > PCB->IsleArea)
1826 new = CreateNewPolygon (layer, flags);
1827 if (!new)
1828 return false;
1829 many = true;
1830 v = &p->contours->head;
1831 CreateNewPointInPolygon (new, v->point[0], v->point[1]);
1832 for (v = v->next; v != &p->contours->head; v = v->next)
1833 CreateNewPointInPolygon (new, v->point[0], v->point[1]);
1834 new->BoundingBox.X1 = p->contours->xmin;
1835 new->BoundingBox.X2 = p->contours->xmax + 1;
1836 new->BoundingBox.Y1 = p->contours->ymin;
1837 new->BoundingBox.Y2 = p->contours->ymax + 1;
1838 AddObjectToCreateUndoList (POLYGON_TYPE, layer, new, new);
1839 new->Clipped = p;
1840 p = p->f; /* go to next pline */
1841 new->Clipped->b = new->Clipped->f = new->Clipped; /* unlink from others */
1842 r_insert_entry (layer->polygon_tree, (BoxType *) new, 0);
1843 DrawPolygon (layer, new, 0);
1845 else
1847 POLYAREA *t = p;
1849 p = p->f;
1850 poly_DelContour (&t->contours);
1851 free (t);
1854 while (p != start);
1855 inhibit = false;
1856 IncrementUndoSerialNumber ();
1857 return many;
1860 void debug_pline (PLINE *pl)
1862 VNODE *v;
1863 fprintf (stderr, "\txmin %d xmax %d ymin %d ymax %d\n",
1864 pl->xmin, pl->xmax, pl->ymin, pl->ymax);
1865 v = &pl->head;
1866 while (v)
1868 fprintf(stderr, "\t\tvnode: %d,%d\n", v->point[0], v->point[1]);
1869 v = v->next;
1870 if (v == &pl->head)
1871 break;
1875 void
1876 debug_polyarea (POLYAREA *p)
1878 PLINE *pl;
1880 fprintf (stderr, "POLYAREA %p\n", p);
1881 for (pl=p->contours; pl; pl=pl->next)
1882 debug_pline (pl);
1885 void
1886 debug_polygon (PolygonType *p)
1888 Cardinal i;
1889 POLYAREA *pa;
1890 fprintf (stderr, "POLYGON %p %d pts\n", p, p->PointN);
1891 for (i=0; i<p->PointN; i++)
1892 fprintf(stderr, "\t%d: %d, %d\n", i, p->Points[i].X, p->Points[i].Y);
1893 if (p->HoleIndexN)
1895 fprintf (stderr, "%d holes, starting at indices\n", p->HoleIndexN);
1896 for (i=0; i<p->HoleIndexN; i++)
1897 fprintf(stderr, "\t%d: %d\n", i, p->HoleIndex[i]);
1899 else
1900 fprintf (stderr, "it has no holes\n");
1901 pa = p->Clipped;
1902 while (pa)
1904 debug_polyarea (pa);
1905 pa = pa->f;
1906 if (pa == p->Clipped)
1907 break;
1911 /* Convert a POLYAREA (and all linked POLYAREA) to
1912 * raw PCB polygons on the given layer.
1914 void
1915 PolyToPolygonsOnLayer (DataType *Destination, LayerType *Layer,
1916 POLYAREA *Input, FlagType Flags)
1918 PolygonType *Polygon;
1919 POLYAREA *pa;
1920 PLINE *pline;
1921 VNODE *node;
1922 bool outer;
1924 if (Input == NULL)
1925 return;
1927 pa = Input;
1930 Polygon = CreateNewPolygon (Layer, Flags);
1932 pline = pa->contours;
1933 outer = true;
1937 if (!outer)
1938 CreateNewHoleInPolygon (Polygon);
1939 outer = false;
1941 node = &pline->head;
1944 CreateNewPointInPolygon (Polygon, node->point[0],
1945 node->point[1]);
1947 while ((node = node->next) != &pline->head);
1950 while ((pline = pline->next) != NULL);
1952 InitClip (Destination, Layer, Polygon);
1953 SetPolygonBoundingBox (Polygon);
1954 if (!Layer->polygon_tree)
1955 Layer->polygon_tree = r_create_tree (NULL, 0, 0);
1956 r_insert_entry (Layer->polygon_tree, (BoxType *) Polygon, 0);
1958 DrawPolygon (Layer, Polygon, 0);
1959 /* add to undo list */
1960 AddObjectToCreateUndoList (POLYGON_TYPE, Layer, Polygon, Polygon);
1962 while ((pa = pa->f) != Input);
1964 SetChangedFlag (true);