(no commit message)
[geda-pcb/pcjc2.git] / src / polygon.c
blob75e1b9ecb8989412234b880a41964a9da990f8c6
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 "pcb-printf.h"
94 #include "polygon.h"
95 #include "remove.h"
96 #include "rtree.h"
97 #include "search.h"
98 #include "set.h"
99 #include "thermal.h"
100 #include "undo.h"
102 #ifdef HAVE_LIBDMALLOC
103 #include <dmalloc.h>
104 #endif
106 #define ROUND(x) ((long)(((x) >= 0 ? (x) + 0.5 : (x) - 0.5)))
108 #define UNSUBTRACT_BLOAT 10
109 #define SUBTRACT_PIN_VIA_BATCH_SIZE 100
110 #define SUBTRACT_LINE_BATCH_SIZE 20
112 static double rotate_circle_seg[4];
114 void
115 polygon_init (void)
117 double cos_ang = cos (2.0 * M_PI / POLY_CIRC_SEGS_F);
118 double sin_ang = sin (2.0 * M_PI / POLY_CIRC_SEGS_F);
120 rotate_circle_seg[0] = cos_ang; rotate_circle_seg[1] = -sin_ang;
121 rotate_circle_seg[2] = sin_ang; rotate_circle_seg[3] = cos_ang;
124 Cardinal
125 polygon_point_idx (PolygonType *polygon, PointType *point)
127 assert (point >= polygon->Points);
128 assert (point <= polygon->Points + polygon->PointN);
129 return ((char *)point - (char *)polygon->Points) / sizeof (PointType);
132 /* Find contour number: 0 for outer, 1 for first hole etc.. */
133 Cardinal
134 polygon_point_contour (PolygonType *polygon, Cardinal point)
136 Cardinal i;
137 Cardinal contour = 0;
139 for (i = 0; i < polygon->HoleIndexN; i++)
140 if (point >= polygon->HoleIndex[i])
141 contour = i + 1;
142 return contour;
145 Cardinal
146 next_contour_point (PolygonType *polygon, Cardinal point)
148 Cardinal contour;
149 Cardinal this_contour_start;
150 Cardinal next_contour_start;
152 contour = polygon_point_contour (polygon, point);
154 this_contour_start = (contour == 0) ? 0 :
155 polygon->HoleIndex[contour - 1];
156 next_contour_start =
157 (contour == polygon->HoleIndexN) ? polygon->PointN :
158 polygon->HoleIndex[contour];
160 /* Wrap back to the start of the contour we're in if we pass the end */
161 if (++point == next_contour_start)
162 point = this_contour_start;
164 return point;
167 Cardinal
168 prev_contour_point (PolygonType *polygon, Cardinal point)
170 Cardinal contour;
171 Cardinal prev_contour_end;
172 Cardinal this_contour_end;
174 contour = polygon_point_contour (polygon, point);
176 prev_contour_end = (contour == 0) ? 0 :
177 polygon->HoleIndex[contour - 1];
178 this_contour_end =
179 (contour == polygon->HoleIndexN) ? polygon->PointN - 1:
180 polygon->HoleIndex[contour] - 1;
182 /* Wrap back to the start of the contour we're in if we pass the end */
183 if (point == prev_contour_end)
184 point = this_contour_end;
185 else
186 point--;
188 return point;
191 static void
192 add_noholes_polyarea (PLINE *pline, void *user_data)
194 PolygonType *poly = (PolygonType *)user_data;
196 /* Prepend the pline into the NoHoles linked list */
197 pline->next = poly->NoHoles;
198 poly->NoHoles = pline;
201 void
202 ComputeNoHoles (PolygonType *poly)
204 poly_FreeContours (&poly->NoHoles);
205 if (poly->Clipped)
206 NoHolesPolygonDicer (poly, NULL, add_noholes_polyarea, poly);
207 else
208 printf ("Compute_noholes caught poly->Clipped = NULL\n");
209 poly->NoHolesValid = 1;
212 static POLYAREA *
213 biggest (POLYAREA * p)
215 POLYAREA *n, *top = NULL;
216 PLINE *pl;
217 rtree_t *tree;
218 double big = -1;
219 if (!p)
220 return NULL;
221 n = p;
224 #if 0
225 if (n->contours->area < PCB->IsleArea)
227 n->b->f = n->f;
228 n->f->b = n->b;
229 poly_DelContour (&n->contours);
230 if (n == p)
231 p = n->f;
232 n = n->f;
233 if (!n->contours)
235 free (n);
236 return NULL;
239 #endif
240 if (n->contours->area > big)
242 top = n;
243 big = n->contours->area;
246 while ((n = n->f) != p);
247 assert (top);
248 if (top == p)
249 return p;
250 pl = top->contours;
251 tree = top->contour_tree;
252 top->contours = p->contours;
253 top->contour_tree = p->contour_tree;
254 p->contours = pl;
255 p->contour_tree = tree;
256 assert (pl);
257 assert (p->f);
258 assert (p->b);
259 return p;
262 POLYAREA *
263 ContourToPoly (PLINE * contour)
265 POLYAREA *p;
266 poly_PreContour (contour, TRUE);
267 assert (contour->Flags.orient == PLF_DIR);
268 if (!(p = poly_Create ()))
269 return NULL;
270 poly_InclContour (p, contour);
271 assert (poly_Valid (p));
272 return p;
275 static POLYAREA *
276 original_poly (PolygonType * p)
278 PLINE *contour = NULL;
279 POLYAREA *np = NULL;
280 Cardinal n;
281 Vector v;
282 int hole = 0;
284 if ((np = poly_Create ()) == NULL)
285 return NULL;
287 /* first make initial polygon contour */
288 for (n = 0; n < p->PointN; n++)
290 /* No current contour? Make a new one starting at point */
291 /* (or) Add point to existing contour */
293 v[0] = p->Points[n].X;
294 v[1] = p->Points[n].Y;
295 if (contour == NULL)
297 if ((contour = poly_NewContour (v)) == NULL)
298 return NULL;
300 else
302 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
305 /* Is current point last in contour? If so process it. */
306 if (n == p->PointN - 1 ||
307 (hole < p->HoleIndexN && n == p->HoleIndex[hole] - 1))
309 poly_PreContour (contour, TRUE);
311 /* make sure it is a positive contour (outer) or negative (hole) */
312 if (contour->Flags.orient != (hole ? PLF_INV : PLF_DIR))
313 poly_InvContour (contour);
314 assert (contour->Flags.orient == (hole ? PLF_INV : PLF_DIR));
316 poly_InclContour (np, contour);
317 contour = NULL;
318 assert (poly_Valid (np));
320 hole++;
323 return biggest (np);
326 POLYAREA *
327 PolygonToPoly (PolygonType *p)
329 return original_poly (p);
332 POLYAREA *
333 RectPoly (Coord x1, Coord x2, Coord y1, Coord y2)
335 PLINE *contour = NULL;
336 Vector v;
338 /* Return NULL for zero or negatively sized rectangles */
339 if (x2 <= x1 || y2 <= y1)
340 return NULL;
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 (Coord x, Coord y, Coord 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, Coord X, Coord Y, Vector v, int fraction)
399 double e1, e2, t1;
400 int i, range;
402 poly_InclVertex (c->head.prev, poly_CreateNode (v));
403 /* move vector to origin */
404 e1 = (v[0] - X) * POLY_CIRC_RADIUS_ADJ;
405 e2 = (v[1] - Y) * POLY_CIRC_RADIUS_ADJ;
407 /* NB: the caller adds the last vertex, hence the -1 */
408 range = POLY_CIRC_SEGS / fraction - 1;
409 for (i = 0; i < range; i++)
411 /* rotate the vector */
412 t1 = rotate_circle_seg[0] * e1 + rotate_circle_seg[1] * e2;
413 e2 = rotate_circle_seg[2] * e1 + rotate_circle_seg[3] * e2;
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 (Coord x, Coord y, Coord 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 (Coord x1, Coord x2, Coord y1, Coord y2, Coord t)
446 PLINE *contour = NULL;
447 Vector v;
449 assert (x2 > x1);
450 assert (y2 > y1);
451 v[0] = x1 - t;
452 v[1] = y1;
453 if ((contour = poly_NewContour (v)) == NULL)
454 return NULL;
455 frac_circle (contour, x1, y1, v, 4);
456 v[0] = x2;
457 v[1] = y1 - t;
458 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
459 frac_circle (contour, x2, y1, v, 4);
460 v[0] = x2 + t;
461 v[1] = y2;
462 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
463 frac_circle (contour, x2, y2, v, 4);
464 v[0] = x1;
465 v[1] = y2 + t;
466 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
467 frac_circle (contour, x1, y2, v, 4);
468 return ContourToPoly (contour);
471 #define ARC_ANGLE 5
472 static POLYAREA *
473 ArcPolyNoIntersect (ArcType * a, Coord thick)
475 PLINE *contour = NULL;
476 POLYAREA *np = NULL;
477 Vector v;
478 BoxType *ends;
479 int i, segs;
480 double ang, da, rx, ry;
481 long half;
482 double radius_adj;
484 if (thick <= 0)
485 return NULL;
486 if (a->Delta < 0)
488 a->StartAngle += a->Delta;
489 a->Delta = -a->Delta;
491 half = (thick + 1) / 2;
492 ends = GetArcEnds (a);
493 /* start with inner radius */
494 rx = MAX (a->Width - half, 0);
495 ry = MAX (a->Height - half, 0);
496 segs = 1;
497 if(thick > 0)
498 segs = MAX (segs, a->Delta * M_PI / 360 *
499 sqrt (sqrt ((double)rx * rx + (double)ry * ry) /
500 POLY_ARC_MAX_DEVIATION / 2 / thick));
501 segs = MAX(segs, a->Delta / ARC_ANGLE);
503 ang = a->StartAngle;
504 da = (1.0 * a->Delta) / segs;
505 radius_adj = (M_PI*da/360)*(M_PI*da/360)/2;
506 v[0] = a->X - rx * cos (ang * M180);
507 v[1] = a->Y + ry * sin (ang * M180);
508 if ((contour = poly_NewContour (v)) == NULL)
509 return 0;
510 for (i = 0; i < segs - 1; i++)
512 ang += da;
513 v[0] = a->X - rx * cos (ang * M180);
514 v[1] = a->Y + ry * sin (ang * M180);
515 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
517 /* find last point */
518 ang = a->StartAngle + a->Delta;
519 v[0] = a->X - rx * cos (ang * M180) * (1 - radius_adj);
520 v[1] = a->Y + ry * sin (ang * M180) * (1 - radius_adj);
521 /* add the round cap at the end */
522 frac_circle (contour, ends->X2, ends->Y2, v, 2);
523 /* and now do the outer arc (going backwards) */
524 rx = (a->Width + half) * (1+radius_adj);
525 ry = (a->Width + half) * (1+radius_adj);
526 da = -da;
527 for (i = 0; i < segs; i++)
529 v[0] = a->X - rx * cos (ang * M180);
530 v[1] = a->Y + ry * sin (ang * M180);
531 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
532 ang += da;
534 /* now add other round cap */
535 ang = a->StartAngle;
536 v[0] = a->X - rx * cos (ang * M180) * (1 - radius_adj);
537 v[1] = a->Y + ry * sin (ang * M180) * (1 - radius_adj);
538 frac_circle (contour, ends->X1, ends->Y1, v, 2);
539 /* now we have the whole contour */
540 if (!(np = ContourToPoly (contour)))
541 return NULL;
542 return np;
545 #define MIN_CLEARANCE_BEFORE_BISECT 10.
546 POLYAREA *
547 ArcPoly (ArcType * a, Coord thick)
549 double delta;
550 ArcType seg1, seg2;
551 POLYAREA *tmp1, *tmp2, *res;
553 delta = (a->Delta < 0) ? -a->Delta : a->Delta;
555 /* If the arc segment would self-intersect, we need to construct it as the union of
556 two non-intersecting segments */
558 if (2 * M_PI * a->Width * (1. - (double)delta / 360.) - thick < MIN_CLEARANCE_BEFORE_BISECT)
560 int half_delta = a->Delta / 2;
562 seg1 = seg2 = *a;
563 seg1.Delta = half_delta;
564 seg2.Delta -= half_delta;
565 seg2.StartAngle += half_delta;
567 tmp1 = ArcPolyNoIntersect (&seg1, thick);
568 tmp2 = ArcPolyNoIntersect (&seg2, thick);
569 poly_Boolean_free (tmp1, tmp2, &res, PBO_UNITE);
570 return res;
573 return ArcPolyNoIntersect (a, thick);
576 POLYAREA *
577 LinePoly (LineType * L, Coord thick)
579 PLINE *contour = NULL;
580 POLYAREA *np = NULL;
581 Vector v;
582 double d, dx, dy;
583 long half;LineType _l=*L,*l=&_l;
585 if (thick <= 0)
586 return NULL;
587 half = (thick + 1) / 2;
589 sqrt (SQUARE (l->Point1.X - l->Point2.X) +
590 SQUARE (l->Point1.Y - l->Point2.Y));
591 if (!TEST_FLAG (SQUAREFLAG,l))
592 if (d == 0) /* line is a point */
593 return CirclePoly (l->Point1.X, l->Point1.Y, half);
594 if (d != 0)
596 d = half / d;
597 dx = (l->Point1.Y - l->Point2.Y) * d;
598 dy = (l->Point2.X - l->Point1.X) * d;
600 else
602 dx = half;
603 dy = 0;
605 if (TEST_FLAG (SQUAREFLAG,l))/* take into account the ends */
607 l->Point1.X -= dy;
608 l->Point1.Y += dx;
609 l->Point2.X += dy;
610 l->Point2.Y -= dx;
612 v[0] = l->Point1.X - dx;
613 v[1] = l->Point1.Y - dy;
614 if ((contour = poly_NewContour (v)) == NULL)
615 return 0;
616 v[0] = l->Point2.X - dx;
617 v[1] = l->Point2.Y - dy;
618 if (TEST_FLAG (SQUAREFLAG,l))
619 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
620 else
621 frac_circle (contour, l->Point2.X, l->Point2.Y, v, 2);
622 v[0] = l->Point2.X + dx;
623 v[1] = l->Point2.Y + dy;
624 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
625 v[0] = l->Point1.X + dx;
626 v[1] = l->Point1.Y + dy;
627 if (TEST_FLAG (SQUAREFLAG,l))
628 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
629 else
630 frac_circle (contour, l->Point1.X, l->Point1.Y, v, 2);
631 /* now we have the line contour */
632 if (!(np = ContourToPoly (contour)))
633 return NULL;
634 return np;
637 /* make a rounded-corner rectangle */
638 POLYAREA *
639 SquarePadPoly (PadType * pad, Coord clear)
641 PLINE *contour = NULL;
642 POLYAREA *np = NULL;
643 Vector v;
644 double d;
645 double tx, ty;
646 double cx, cy;
647 PadType _t=*pad,*t=&_t;
648 PadType _c=*pad,*c=&_c;
649 int halfthick = (pad->Thickness + 1) / 2;
650 int halfclear = (clear + 1) / 2;
653 sqrt (SQUARE (pad->Point1.X - pad->Point2.X) +
654 SQUARE (pad->Point1.Y - pad->Point2.Y));
655 if (d != 0)
657 double a = halfthick / d;
658 tx = (t->Point1.Y - t->Point2.Y) * a;
659 ty = (t->Point2.X - t->Point1.X) * a;
660 a = halfclear / d;
661 cx = (c->Point1.Y - c->Point2.Y) * a;
662 cy = (c->Point2.X - c->Point1.X) * a;
664 t->Point1.X -= ty;
665 t->Point1.Y += tx;
666 t->Point2.X += ty;
667 t->Point2.Y -= tx;
668 c->Point1.X -= cy;
669 c->Point1.Y += cx;
670 c->Point2.X += cy;
671 c->Point2.Y -= cx;
673 else
675 tx = halfthick;
676 ty = 0;
677 cx = halfclear;
678 cy = 0;
680 t->Point1.Y += tx;
681 t->Point2.Y -= tx;
682 c->Point1.Y += cx;
683 c->Point2.Y -= cx;
686 v[0] = c->Point1.X - tx;
687 v[1] = c->Point1.Y - ty;
688 if ((contour = poly_NewContour (v)) == NULL)
689 return 0;
690 frac_circle (contour, (t->Point1.X - tx), (t->Point1.Y - ty), v, 4);
692 v[0] = t->Point2.X - cx;
693 v[1] = t->Point2.Y - cy;
694 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
695 frac_circle (contour, (t->Point2.X - tx), (t->Point2.Y - ty), v, 4);
697 v[0] = c->Point2.X + tx;
698 v[1] = c->Point2.Y + ty;
699 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
700 frac_circle (contour, (t->Point2.X + tx), (t->Point2.Y + ty), v, 4);
702 v[0] = t->Point1.X + cx;
703 v[1] = t->Point1.Y + cy;
704 poly_InclVertex (contour->head.prev, poly_CreateNode (v));
705 frac_circle (contour, (t->Point1.X + tx), (t->Point1.Y + ty), v, 4);
707 /* now we have the line contour */
708 if (!(np = ContourToPoly (contour)))
709 return NULL;
710 return np;
713 /* clear np1 from the polygon */
714 static int
715 Subtract (POLYAREA * np1, PolygonType * p, bool fnp)
717 POLYAREA *merged = NULL, *np = np1;
718 int x;
719 assert (np);
720 assert (p);
721 if (!p->Clipped)
723 if (fnp)
724 poly_Free (&np);
725 return 1;
727 assert (poly_Valid (p->Clipped));
728 assert (poly_Valid (np));
729 if (fnp)
730 x = poly_Boolean_free (p->Clipped, np, &merged, PBO_SUB);
731 else
733 x = poly_Boolean (p->Clipped, np, &merged, PBO_SUB);
734 poly_Free (&p->Clipped);
736 assert (!merged || poly_Valid (merged));
737 if (x != err_ok)
739 fprintf (stderr, "Error while clipping PBO_SUB: %d\n", x);
740 poly_Free (&merged);
741 p->Clipped = NULL;
742 if (p->NoHoles) printf ("Just leaked in Subtract\n");
743 p->NoHoles = NULL;
744 return -1;
746 p->Clipped = biggest (merged);
747 assert (!p->Clipped || poly_Valid (p->Clipped));
748 if (!p->Clipped)
749 Message ("Polygon cleared out of existence near (%d, %d)\n",
750 (p->BoundingBox.X1 + p->BoundingBox.X2) / 2,
751 (p->BoundingBox.Y1 + p->BoundingBox.Y2) / 2);
752 return 1;
755 /* create a polygon of the pin clearance */
756 POLYAREA *
757 PinPoly (PinType * pin, Coord thick, Coord clear)
759 int size;
761 if (TEST_FLAG (SQUAREFLAG, pin))
763 size = (thick + 1) / 2;
764 return RoundRect (pin->X - size, pin->X + size, pin->Y - size,
765 pin->Y + size, (clear + 1) / 2);
767 else
769 size = (thick + clear + 1) / 2;
770 if (TEST_FLAG (OCTAGONFLAG, pin))
772 return OctagonPoly (pin->X, pin->Y, size + size);
775 return CirclePoly (pin->X, pin->Y, size);
778 POLYAREA *
779 BoxPolyBloated (BoxType *box, Coord bloat)
781 return RectPoly (box->X1 - bloat, box->X2 + bloat,
782 box->Y1 - bloat, box->Y2 + bloat);
785 /* remove the pin clearance from the polygon */
786 static int
787 SubtractPin (DataType * d, PinType * pin, LayerType * l, PolygonType * p)
789 POLYAREA *np;
790 Cardinal i;
792 if (pin->Clearance == 0)
793 return 0;
794 i = GetLayerNumber (d, l);
795 if (TEST_THERM (i, pin))
797 np = ThermPoly ((PCBType *) (d->pcb), pin, i);
798 if (!np)
799 return 0;
801 else
803 np = PinPoly (pin, PIN_SIZE (pin), pin->Clearance);
804 if (!np)
805 return -1;
807 return Subtract (np, p, TRUE);
810 static int
811 SubtractLine (LineType * line, PolygonType * p)
813 POLYAREA *np;
815 if (!TEST_FLAG (CLEARLINEFLAG, line))
816 return 0;
817 if (!(np = LinePoly (line, line->Thickness + line->Clearance)))
818 return -1;
819 return Subtract (np, p, true);
822 static int
823 SubtractArc (ArcType * arc, PolygonType * p)
825 POLYAREA *np;
827 if (!TEST_FLAG (CLEARLINEFLAG, arc))
828 return 0;
829 if (!(np = ArcPoly (arc, arc->Thickness + arc->Clearance)))
830 return -1;
831 return Subtract (np, p, true);
834 static int
835 SubtractText (TextType * text, PolygonType * p)
837 POLYAREA *np;
838 const BoxType *b = &text->BoundingBox;
840 if (!TEST_FLAG (CLEARLINEFLAG, text))
841 return 0;
842 if (!(np = RoundRect (b->X1 + PCB->Bloat, b->X2 - PCB->Bloat,
843 b->Y1 + PCB->Bloat, b->Y2 - PCB->Bloat, PCB->Bloat)))
844 return -1;
845 return Subtract (np, p, true);
848 static int
849 SubtractPad (PadType * pad, PolygonType * p)
851 POLYAREA *np = NULL;
853 if (pad->Clearance == 0)
854 return 0;
855 if (TEST_FLAG (SQUAREFLAG, pad))
857 if (!
858 (np = SquarePadPoly (pad, pad->Thickness + pad->Clearance)))
859 return -1;
861 else
863 if (!
864 (np = LinePoly ((LineType *) pad, pad->Thickness + pad->Clearance)))
865 return -1;
867 return Subtract (np, p, true);
870 struct cpInfo
872 const BoxType *other;
873 DataType *data;
874 LayerType *layer;
875 PolygonType *polygon;
876 bool bottom;
877 POLYAREA *accumulate;
878 int batch_size;
879 jmp_buf env;
882 static void
883 subtract_accumulated (struct cpInfo *info, PolygonType *polygon)
885 if (info->accumulate == NULL)
886 return;
887 Subtract (info->accumulate, polygon, true);
888 info->accumulate = NULL;
889 info->batch_size = 0;
892 static int
893 pin_sub_callback (const BoxType * b, void *cl)
895 PinType *pin = (PinType *) b;
896 struct cpInfo *info = (struct cpInfo *) cl;
897 PolygonType *polygon;
898 POLYAREA *np;
899 POLYAREA *merged;
900 Cardinal i;
902 /* don't subtract the object that was put back! */
903 if (b == info->other)
904 return 0;
905 polygon = info->polygon;
907 if (pin->Clearance == 0)
908 return 0;
909 i = GetLayerNumber (info->data, info->layer);
910 if (TEST_THERM (i, pin))
912 np = ThermPoly ((PCBType *) (info->data->pcb), pin, i);
913 if (!np)
914 return 1;
916 else
918 np = PinPoly (pin, PIN_SIZE (pin), pin->Clearance);
919 if (!np)
920 longjmp (info->env, 1);
923 poly_Boolean_free (info->accumulate, np, &merged, PBO_UNITE);
924 info->accumulate = merged;
926 info->batch_size ++;
928 if (info->batch_size == SUBTRACT_PIN_VIA_BATCH_SIZE)
929 subtract_accumulated (info, polygon);
931 return 1;
934 static int
935 arc_sub_callback (const BoxType * b, void *cl)
937 ArcType *arc = (ArcType *) b;
938 struct cpInfo *info = (struct cpInfo *) cl;
939 PolygonType *polygon;
941 /* don't subtract the object that was put back! */
942 if (b == info->other)
943 return 0;
944 if (!TEST_FLAG (CLEARLINEFLAG, arc))
945 return 0;
946 polygon = info->polygon;
947 if (SubtractArc (arc, polygon) < 0)
948 longjmp (info->env, 1);
949 return 1;
952 static int
953 pad_sub_callback (const BoxType * b, void *cl)
955 PadType *pad = (PadType *) b;
956 struct cpInfo *info = (struct cpInfo *) cl;
957 PolygonType *polygon;
959 /* don't subtract the object that was put back! */
960 if (b == info->other)
961 return 0;
962 if (pad->Clearance == 0)
963 return 0;
964 polygon = info->polygon;
965 if (XOR (TEST_FLAG (ONSOLDERFLAG, pad), !info->bottom))
967 if (SubtractPad (pad, polygon) < 0)
968 longjmp (info->env, 1);
969 return 1;
971 return 0;
974 static int
975 line_sub_callback (const BoxType * b, void *cl)
977 LineType *line = (LineType *) b;
978 struct cpInfo *info = (struct cpInfo *) cl;
979 PolygonType *polygon;
980 POLYAREA *np;
981 POLYAREA *merged;
983 /* don't subtract the object that was put back! */
984 if (b == info->other)
985 return 0;
986 if (!TEST_FLAG (CLEARLINEFLAG, line))
987 return 0;
988 polygon = info->polygon;
990 if (!(np = LinePoly (line, line->Thickness + line->Clearance)))
991 longjmp (info->env, 1);
993 poly_Boolean_free (info->accumulate, np, &merged, PBO_UNITE);
994 info->accumulate = merged;
995 info->batch_size ++;
997 if (info->batch_size == SUBTRACT_LINE_BATCH_SIZE)
998 subtract_accumulated (info, polygon);
1000 return 1;
1003 static int
1004 text_sub_callback (const BoxType * b, void *cl)
1006 TextType *text = (TextType *) b;
1007 struct cpInfo *info = (struct cpInfo *) cl;
1008 PolygonType *polygon;
1010 /* don't subtract the object that was put back! */
1011 if (b == info->other)
1012 return 0;
1013 if (!TEST_FLAG (CLEARLINEFLAG, text))
1014 return 0;
1015 polygon = info->polygon;
1016 if (SubtractText (text, polygon) < 0)
1017 longjmp (info->env, 1);
1018 return 1;
1021 static int
1022 Group (DataType *Data, Cardinal layer)
1024 Cardinal i, j;
1025 for (i = 0; i < max_group; i++)
1026 for (j = 0; j < ((PCBType *) (Data->pcb))->LayerGroups.Number[i]; j++)
1027 if (layer == ((PCBType *) (Data->pcb))->LayerGroups.Entries[i][j])
1028 return i;
1029 return i;
1032 static int
1033 clearPoly (DataType *Data, LayerType *Layer, PolygonType * polygon,
1034 const BoxType * here, Coord expand)
1036 int r = 0;
1037 BoxType region;
1038 struct cpInfo info;
1039 Cardinal group;
1041 if (!TEST_FLAG (CLEARPOLYFLAG, polygon)
1042 || GetLayerNumber (Data, Layer) >= max_copper_layer)
1043 return 0;
1044 group = Group (Data, GetLayerNumber (Data, Layer));
1045 info.bottom = (group == Group (Data, bottom_silk_layer));
1046 info.data = Data;
1047 info.other = here;
1048 info.layer = Layer;
1049 info.polygon = polygon;
1050 if (here)
1051 region = clip_box (here, &polygon->BoundingBox);
1052 else
1053 region = polygon->BoundingBox;
1054 region = bloat_box (&region, expand);
1056 if (setjmp (info.env) == 0)
1058 r = 0;
1059 info.accumulate = NULL;
1060 info.batch_size = 0;
1061 if (info.bottom || group == Group (Data, top_silk_layer))
1062 r += r_search (Data->pad_tree, &region, NULL, pad_sub_callback, &info);
1063 GROUP_LOOP (Data, group);
1065 r +=
1066 r_search (layer->line_tree, &region, NULL, line_sub_callback,
1067 &info);
1068 subtract_accumulated (&info, polygon);
1069 r +=
1070 r_search (layer->arc_tree, &region, NULL, arc_sub_callback, &info);
1071 r +=
1072 r_search (layer->text_tree, &region, NULL, text_sub_callback, &info);
1074 END_LOOP;
1075 r += r_search (Data->via_tree, &region, NULL, pin_sub_callback, &info);
1076 r += r_search (Data->pin_tree, &region, NULL, pin_sub_callback, &info);
1077 subtract_accumulated (&info, polygon);
1079 polygon->NoHolesValid = 0;
1080 return r;
1083 static int
1084 Unsubtract (POLYAREA * np1, PolygonType * p)
1086 POLYAREA *merged = NULL, *np = np1;
1087 POLYAREA *orig_poly, *clipped_np;
1088 int x;
1089 assert (np);
1090 assert (p && p->Clipped);
1092 orig_poly = original_poly (p);
1094 x = poly_Boolean_free (np, orig_poly, &clipped_np, PBO_ISECT);
1095 if (x != err_ok)
1097 fprintf (stderr, "Error while clipping PBO_ISECT: %d\n", x);
1098 poly_Free (&clipped_np);
1099 goto fail;
1102 x = poly_Boolean_free (p->Clipped, clipped_np, &merged, PBO_UNITE);
1103 if (x != err_ok)
1105 fprintf (stderr, "Error while clipping PBO_UNITE: %d\n", x);
1106 poly_Free (&merged);
1107 goto fail;
1109 p->Clipped = biggest (merged);
1110 assert (!p->Clipped || poly_Valid (p->Clipped));
1111 return 1;
1113 fail:
1114 p->Clipped = NULL;
1115 if (p->NoHoles) printf ("Just leaked in Unsubtract\n");
1116 p->NoHoles = NULL;
1117 return 0;
1120 static int
1121 UnsubtractPin (PinType * pin, LayerType * l, PolygonType * p)
1123 POLYAREA *np;
1125 /* overlap a bit to prevent gaps from rounding errors */
1126 np = BoxPolyBloated (&pin->BoundingBox, UNSUBTRACT_BLOAT);
1128 if (!np)
1129 return 0;
1130 if (!Unsubtract (np, p))
1131 return 0;
1132 clearPoly (PCB->Data, l, p, (const BoxType *) pin, 2 * UNSUBTRACT_BLOAT);
1133 return 1;
1136 static int
1137 UnsubtractArc (ArcType * arc, LayerType * l, PolygonType * p)
1139 POLYAREA *np;
1141 if (!TEST_FLAG (CLEARLINEFLAG, arc))
1142 return 0;
1144 /* overlap a bit to prevent gaps from rounding errors */
1145 np = BoxPolyBloated (&arc->BoundingBox, UNSUBTRACT_BLOAT);
1147 if (!np)
1148 return 0;
1149 if (!Unsubtract (np, p))
1150 return 0;
1151 clearPoly (PCB->Data, l, p, (const BoxType *) arc, 2 * UNSUBTRACT_BLOAT);
1152 return 1;
1155 static int
1156 UnsubtractLine (LineType * line, LayerType * l, PolygonType * p)
1158 POLYAREA *np;
1160 if (!TEST_FLAG (CLEARLINEFLAG, line))
1161 return 0;
1163 /* overlap a bit to prevent notches from rounding errors */
1164 np = BoxPolyBloated (&line->BoundingBox, UNSUBTRACT_BLOAT);
1166 if (!np)
1167 return 0;
1168 if (!Unsubtract (np, p))
1169 return 0;
1170 clearPoly (PCB->Data, l, p, (const BoxType *) line, 2 * UNSUBTRACT_BLOAT);
1171 return 1;
1174 static int
1175 UnsubtractText (TextType * text, LayerType * l, PolygonType * p)
1177 POLYAREA *np;
1179 if (!TEST_FLAG (CLEARLINEFLAG, text))
1180 return 0;
1182 /* overlap a bit to prevent notches from rounding errors */
1183 np = BoxPolyBloated (&text->BoundingBox, UNSUBTRACT_BLOAT);
1185 if (!np)
1186 return -1;
1187 if (!Unsubtract (np, p))
1188 return 0;
1189 clearPoly (PCB->Data, l, p, (const BoxType *) text, 2 * UNSUBTRACT_BLOAT);
1190 return 1;
1193 static int
1194 UnsubtractPad (PadType * pad, LayerType * l, PolygonType * p)
1196 POLYAREA *np;
1198 /* overlap a bit to prevent notches from rounding errors */
1199 np = BoxPolyBloated (&pad->BoundingBox, UNSUBTRACT_BLOAT);
1201 if (!np)
1202 return 0;
1203 if (!Unsubtract (np, p))
1204 return 0;
1205 clearPoly (PCB->Data, l, p, (const BoxType *) pad, 2 * UNSUBTRACT_BLOAT);
1206 return 1;
1209 static bool inhibit = false;
1212 InitClip (DataType *Data, LayerType *layer, PolygonType * p)
1214 if (inhibit)
1215 return 0;
1216 if (p->Clipped)
1217 poly_Free (&p->Clipped);
1218 p->Clipped = original_poly (p);
1219 poly_FreeContours (&p->NoHoles);
1220 if (!p->Clipped)
1221 return 0;
1222 assert (poly_Valid (p->Clipped));
1223 if (TEST_FLAG (CLEARPOLYFLAG, p))
1224 clearPoly (Data, layer, p, NULL, 0);
1225 else
1226 p->NoHolesValid = 0;
1227 return 1;
1230 /* --------------------------------------------------------------------------
1231 * remove redundant polygon points. Any point that lies on the straight
1232 * line between the points on either side of it is redundant.
1233 * returns true if any points are removed
1235 bool
1236 RemoveExcessPolygonPoints (LayerType *Layer, PolygonType *Polygon)
1238 PointType *p;
1239 Cardinal n, prev, next;
1240 LineType line;
1241 bool changed = false;
1243 if (Undoing ())
1244 return (false);
1246 for (n = 0; n < Polygon->PointN; n++)
1248 prev = prev_contour_point (Polygon, n);
1249 next = next_contour_point (Polygon, n);
1250 p = &Polygon->Points[n];
1252 line.Point1 = Polygon->Points[prev];
1253 line.Point2 = Polygon->Points[next];
1254 line.Thickness = 0;
1255 if (IsPointOnLine (p->X, p->Y, 0.0, &line))
1257 RemoveObject (POLYGONPOINT_TYPE, Layer, Polygon, p);
1258 changed = true;
1261 return (changed);
1264 /* ---------------------------------------------------------------------------
1265 * returns the index of the polygon point which is the end
1266 * point of the segment with the lowest distance to the passed
1267 * coordinates
1269 Cardinal
1270 GetLowestDistancePolygonPoint (PolygonType *Polygon, Coord X, Coord Y)
1272 double mindistance = (double) MAX_COORD * MAX_COORD;
1273 PointType *ptr1, *ptr2;
1274 Cardinal n, result = 0;
1276 /* we calculate the distance to each segment and choose the
1277 * shortest distance. If the closest approach between the
1278 * given point and the projected line (i.e. the segment extended)
1279 * is not on the segment, then the distance is the distance
1280 * to the segment end point.
1283 for (n = 0; n < Polygon->PointN; n++)
1285 double u, dx, dy;
1286 ptr1 = &Polygon->Points[prev_contour_point (Polygon, n)];
1287 ptr2 = &Polygon->Points[n];
1289 dx = ptr2->X - ptr1->X;
1290 dy = ptr2->Y - ptr1->Y;
1291 if (dx != 0.0 || dy != 0.0)
1293 /* projected intersection is at P1 + u(P2 - P1) */
1294 u = ((X - ptr1->X) * dx + (Y - ptr1->Y) * dy) / (dx * dx + dy * dy);
1296 if (u < 0.0)
1297 { /* ptr1 is closest point */
1298 u = SQUARE (X - ptr1->X) + SQUARE (Y - ptr1->Y);
1300 else if (u > 1.0)
1301 { /* ptr2 is closest point */
1302 u = SQUARE (X - ptr2->X) + SQUARE (Y - ptr2->Y);
1304 else
1305 { /* projected intersection is closest point */
1306 u = SQUARE (X - ptr1->X * (1.0 - u) - u * ptr2->X) +
1307 SQUARE (Y - ptr1->Y * (1.0 - u) - u * ptr2->Y);
1309 if (u < mindistance)
1311 mindistance = u;
1312 result = n;
1316 return (result);
1319 /* ---------------------------------------------------------------------------
1320 * go back to the previous point of the polygon
1322 void
1323 GoToPreviousPoint (void)
1325 switch (Crosshair.AttachedPolygon.PointN)
1327 /* do nothing if mode has just been entered */
1328 case 0:
1329 break;
1331 /* reset number of points and 'LINE_MODE' state */
1332 case 1:
1333 Crosshair.AttachedPolygon.PointN = 0;
1334 Crosshair.AttachedLine.State = STATE_FIRST;
1335 addedLines = 0;
1336 break;
1338 /* back-up one point */
1339 default:
1341 PointType *points = Crosshair.AttachedPolygon.Points;
1342 Cardinal n = Crosshair.AttachedPolygon.PointN - 2;
1344 Crosshair.AttachedPolygon.PointN--;
1345 Crosshair.AttachedLine.Point1.X = points[n].X;
1346 Crosshair.AttachedLine.Point1.Y = points[n].Y;
1347 break;
1352 /* ---------------------------------------------------------------------------
1353 * close polygon if possible
1355 void
1356 ClosePolygon (void)
1358 Cardinal n = Crosshair.AttachedPolygon.PointN;
1360 /* check number of points */
1361 if (n >= 3)
1363 /* if 45 degree lines are what we want do a quick check
1364 * if closing the polygon makes sense
1366 if (!TEST_FLAG (ALLDIRECTIONFLAG, PCB))
1368 Coord dx, dy;
1370 dx = abs (Crosshair.AttachedPolygon.Points[n - 1].X -
1371 Crosshair.AttachedPolygon.Points[0].X);
1372 dy = abs (Crosshair.AttachedPolygon.Points[n - 1].Y -
1373 Crosshair.AttachedPolygon.Points[0].Y);
1374 if (!(dx == 0 || dy == 0 || dx == dy))
1376 Message
1378 ("Cannot close polygon because 45 degree lines are requested.\n"));
1379 return;
1382 CopyAttachedPolygonToLayer ();
1383 Draw ();
1385 else
1386 Message (_("A polygon has to have at least 3 points\n"));
1389 /* ---------------------------------------------------------------------------
1390 * moves the data of the attached (new) polygon to the current layer
1392 void
1393 CopyAttachedPolygonToLayer (void)
1395 PolygonType *polygon;
1396 int saveID;
1398 /* move data to layer and clear attached struct */
1399 polygon = CreateNewPolygon (CURRENT, NoFlags ());
1400 saveID = polygon->ID;
1401 *polygon = Crosshair.AttachedPolygon;
1402 polygon->ID = saveID;
1403 SET_FLAG (CLEARPOLYFLAG, polygon);
1404 if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1405 SET_FLAG (FULLPOLYFLAG, polygon);
1406 memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
1407 SetPolygonBoundingBox (polygon);
1408 if (!CURRENT->polygon_tree)
1409 CURRENT->polygon_tree = r_create_tree (NULL, 0, 0);
1410 r_insert_entry (CURRENT->polygon_tree, (BoxType *) polygon, 0);
1411 InitClip (PCB->Data, CURRENT, polygon);
1412 DrawPolygon (CURRENT, polygon);
1413 SetChangedFlag (true);
1415 /* reset state of attached line */
1416 Crosshair.AttachedLine.State = STATE_FIRST;
1417 addedLines = 0;
1419 /* add to undo list */
1420 AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT, polygon, polygon);
1421 IncrementUndoSerialNumber ();
1424 /* find polygon holes in range, then call the callback function for
1425 * each hole. If the callback returns non-zero, stop
1426 * the search.
1429 PolygonHoles (PolygonType *polygon, const BoxType *range,
1430 int (*callback) (PLINE *contour, void *user_data),
1431 void *user_data)
1433 POLYAREA *pa = polygon->Clipped;
1434 PLINE *pl;
1435 /* If this hole is so big the polygon doesn't exist, then it's not
1436 * really a hole.
1438 if (!pa)
1439 return 0;
1440 for (pl = pa->contours->next; pl; pl = pl->next)
1442 if (range != NULL &&
1443 (pl->xmin > range->X2 || pl->xmax < range->X1 ||
1444 pl->ymin > range->Y2 || pl->ymax < range->Y1))
1445 continue;
1446 if (callback (pl, user_data))
1448 return 1;
1451 return 0;
1454 struct plow_info
1456 int type;
1457 void *ptr1, *ptr2;
1458 LayerType *layer;
1459 DataType *data;
1460 int (*callback) (DataType *, LayerType *, PolygonType *, int, void *,
1461 void *, void *);
1462 void *userdata;
1465 static int
1466 subtract_plow (DataType *Data, LayerType *Layer, PolygonType *Polygon,
1467 int type, void *ptr1, void *ptr2, void *userdata)
1469 if (!Polygon->Clipped)
1470 return 0;
1471 switch (type)
1473 case PIN_TYPE:
1474 case VIA_TYPE:
1475 SubtractPin (Data, (PinType *) ptr2, Layer, Polygon);
1476 Polygon->NoHolesValid = 0;
1477 return 1;
1478 case LINE_TYPE:
1479 SubtractLine ((LineType *) ptr2, Polygon);
1480 Polygon->NoHolesValid = 0;
1481 return 1;
1482 case ARC_TYPE:
1483 SubtractArc ((ArcType *) ptr2, Polygon);
1484 Polygon->NoHolesValid = 0;
1485 return 1;
1486 case PAD_TYPE:
1487 SubtractPad ((PadType *) ptr2, Polygon);
1488 Polygon->NoHolesValid = 0;
1489 return 1;
1490 case TEXT_TYPE:
1491 SubtractText ((TextType *) ptr2, Polygon);
1492 Polygon->NoHolesValid = 0;
1493 return 1;
1495 return 0;
1498 static int
1499 add_plow (DataType *Data, LayerType *Layer, PolygonType *Polygon,
1500 int type, void *ptr1, void *ptr2, void *userdata)
1502 switch (type)
1504 case PIN_TYPE:
1505 case VIA_TYPE:
1506 UnsubtractPin ((PinType *) ptr2, Layer, Polygon);
1507 return 1;
1508 case LINE_TYPE:
1509 UnsubtractLine ((LineType *) ptr2, Layer, Polygon);
1510 return 1;
1511 case ARC_TYPE:
1512 UnsubtractArc ((ArcType *) ptr2, Layer, Polygon);
1513 return 1;
1514 case PAD_TYPE:
1515 UnsubtractPad ((PadType *) ptr2, Layer, Polygon);
1516 return 1;
1517 case TEXT_TYPE:
1518 UnsubtractText ((TextType *) ptr2, Layer, Polygon);
1519 return 1;
1521 return 0;
1524 static int
1525 plow_callback (const BoxType * b, void *cl)
1527 struct plow_info *plow = (struct plow_info *) cl;
1528 PolygonType *polygon = (PolygonType *) b;
1530 if (TEST_FLAG (CLEARPOLYFLAG, polygon))
1531 return plow->callback (plow->data, plow->layer, polygon, plow->type,
1532 plow->ptr1, plow->ptr2, plow->userdata);
1533 return 0;
1537 PlowsPolygon (DataType * Data, int type, void *ptr1, void *ptr2,
1538 int (*call_back) (DataType *data, LayerType *lay,
1539 PolygonType *poly, int type, void *ptr1,
1540 void *ptr2, void *userdata),
1541 void *userdata)
1543 BoxType sb = ((PinType *) ptr2)->BoundingBox;
1544 int r = 0;
1545 struct plow_info info;
1547 info.type = type;
1548 info.ptr1 = ptr1;
1549 info.ptr2 = ptr2;
1550 info.data = Data;
1551 info.callback = call_back;
1552 info.userdata = userdata;
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 ((LayerType *) 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, (LineType *) ptr2))
1584 return 0;
1585 /* silk doesn't plow */
1586 if (GetLayerNumber (Data, (LayerType *)ptr1) >= max_copper_layer)
1587 return 0;
1588 GROUP_LOOP (Data, GetLayerGroupNumberByNumber (GetLayerNumber (Data,
1589 ((LayerType *) 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 bottom_silk_layer : top_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, userdata);
1617 END_LOOP;
1618 PAD_LOOP ((ElementType *) ptr1);
1620 PlowsPolygon (Data, PAD_TYPE, ptr1, pad, call_back, userdata);
1622 END_LOOP;
1624 break;
1626 return r;
1629 void
1630 RestoreToPolygon (DataType * Data, int type, void *ptr1, void *ptr2)
1632 if (!Data->polyClip)
1633 return;
1635 if (type == POLYGON_TYPE)
1636 InitClip (PCB->Data, (LayerType *) ptr1, (PolygonType *) ptr2);
1637 else
1638 PlowsPolygon (Data, type, ptr1, ptr2, add_plow, NULL);
1641 void
1642 ClearFromPolygon (DataType * Data, int type, void *ptr1, void *ptr2)
1644 if (!Data->polyClip)
1645 return;
1647 if (type == POLYGON_TYPE)
1648 InitClip (PCB->Data, (LayerType *) ptr1, (PolygonType *) ptr2);
1649 else
1650 PlowsPolygon (Data, type, ptr1, ptr2, subtract_plow, NULL);
1653 bool
1654 isects (POLYAREA * a, PolygonType *p, bool fr)
1656 POLYAREA *x;
1657 bool ans;
1658 ans = Touching (a, p->Clipped);
1659 /* argument may be register, so we must copy it */
1660 x = a;
1661 if (fr)
1662 poly_Free (&x);
1663 return ans;
1667 bool
1668 IsPointInPolygon (Coord X, Coord Y, Coord r, PolygonType *p)
1670 POLYAREA *c;
1671 Vector v;
1672 v[0] = X;
1673 v[1] = Y;
1674 if (poly_CheckInside (p->Clipped, v))
1675 return true;
1676 if (r < 1)
1677 return false;
1678 if (!(c = CirclePoly (X, Y, r)))
1679 return false;
1680 return isects (c, p, true);
1684 bool
1685 IsPointInPolygonIgnoreHoles (Coord X, Coord Y, PolygonType *p)
1687 Vector v;
1688 v[0] = X;
1689 v[1] = Y;
1690 return poly_InsideContour (p->Clipped->contours, v);
1693 bool
1694 IsRectangleInPolygon (Coord X1, Coord Y1, Coord X2, Coord Y2, PolygonType *p)
1696 POLYAREA *s;
1697 if (!
1698 (s = RectPoly (min (X1, X2), max (X1, X2), min (Y1, Y2), max (Y1, Y2))))
1699 return false;
1700 return isects (s, p, true);
1703 /* NB: This function will free the passed POLYAREA.
1704 * It must only be passed a single POLYAREA (pa->f == pa->b == pa)
1706 static void
1707 r_NoHolesPolygonDicer (POLYAREA * pa,
1708 void (*emit) (PLINE *, void *), void *user_data)
1710 PLINE *p = pa->contours;
1712 if (!pa->contours->next) /* no holes */
1714 pa->contours = NULL; /* The callback now owns the contour */
1715 /* Don't bother removing it from the POLYAREA's rtree
1716 since we're going to free the POLYAREA below anyway */
1717 emit (p, user_data);
1718 poly_Free (&pa);
1719 return;
1721 else
1723 POLYAREA *poly2, *left, *right;
1725 /* make a rectangle of the left region slicing through the middle of the first hole */
1726 poly2 = RectPoly (p->xmin, (p->next->xmin + p->next->xmax) / 2,
1727 p->ymin, p->ymax);
1728 poly_AndSubtract_free (pa, poly2, &left, &right);
1729 if (left)
1731 POLYAREA *cur, *next;
1732 cur = left;
1735 next = cur->f;
1736 cur->f = cur->b = cur; /* Detach this polygon piece */
1737 r_NoHolesPolygonDicer (cur, emit, user_data);
1738 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1740 while ((cur = next) != left);
1742 if (right)
1744 POLYAREA *cur, *next;
1745 cur = right;
1748 next = cur->f;
1749 cur->f = cur->b = cur; /* Detach this polygon piece */
1750 r_NoHolesPolygonDicer (cur, emit, user_data);
1751 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1753 while ((cur = next) != right);
1758 void
1759 NoHolesPolygonDicer (PolygonType *p, const BoxType * clip,
1760 void (*emit) (PLINE *, void *), void *user_data)
1762 POLYAREA *main_contour, *cur, *next;
1764 main_contour = poly_Create ();
1765 /* copy the main poly only */
1766 poly_Copy1 (main_contour, p->Clipped);
1767 /* clip to the bounding box */
1768 if (clip)
1770 POLYAREA *cbox = RectPoly (clip->X1, clip->X2, clip->Y1, clip->Y2);
1771 poly_Boolean_free (main_contour, cbox, &main_contour, PBO_ISECT);
1773 if (main_contour == NULL)
1774 return;
1775 /* Now dice it up.
1776 * NB: Could be more than one piece (because of the clip above)
1778 cur = main_contour;
1781 next = cur->f;
1782 cur->f = cur->b = cur; /* Detach this polygon piece */
1783 r_NoHolesPolygonDicer (cur, emit, user_data);
1784 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1786 while ((cur = next) != main_contour);
1789 /* make a polygon split into multiple parts into multiple polygons */
1790 bool
1791 MorphPolygon (LayerType *layer, PolygonType *poly)
1793 POLYAREA *p, *start;
1794 bool many = false;
1795 FlagType flags;
1797 if (!poly->Clipped || TEST_FLAG (LOCKFLAG, poly))
1798 return false;
1799 if (poly->Clipped->f == poly->Clipped)
1800 return false;
1801 ErasePolygon (poly);
1802 start = p = poly->Clipped;
1803 /* This is ugly. The creation of the new polygons can cause
1804 * all of the polygon pointers (including the one we're called
1805 * with to change if there is a realloc in GetPolygonMemory().
1806 * That will invalidate our original "poly" argument, potentially
1807 * inside the loop. We need to steal the Clipped pointer and
1808 * hide it from the Remove call so that it still exists while
1809 * we do this dirty work.
1811 poly->Clipped = NULL;
1812 if (poly->NoHoles) printf ("Just leaked in MorpyPolygon\n");
1813 poly->NoHoles = NULL;
1814 flags = poly->Flags;
1815 RemovePolygon (layer, poly);
1816 inhibit = true;
1819 VNODE *v;
1820 PolygonType *newone;
1822 if (p->contours->area > PCB->IsleArea)
1824 newone = CreateNewPolygon (layer, flags);
1825 if (!newone)
1826 return false;
1827 many = true;
1828 v = &p->contours->head;
1829 CreateNewPointInPolygon (newone, v->point[0], v->point[1]);
1830 for (v = v->next; v != &p->contours->head; v = v->next)
1831 CreateNewPointInPolygon (newone, v->point[0], v->point[1]);
1832 newone->BoundingBox.X1 = p->contours->xmin;
1833 newone->BoundingBox.X2 = p->contours->xmax + 1;
1834 newone->BoundingBox.Y1 = p->contours->ymin;
1835 newone->BoundingBox.Y2 = p->contours->ymax + 1;
1836 AddObjectToCreateUndoList (POLYGON_TYPE, layer, newone, newone);
1837 newone->Clipped = p;
1838 p = p->f; /* go to next pline */
1839 newone->Clipped->b = newone->Clipped->f = newone->Clipped; /* unlink from others */
1840 r_insert_entry (layer->polygon_tree, (BoxType *) newone, 0);
1841 DrawPolygon (layer, newone);
1843 else
1845 POLYAREA *t = p;
1847 p = p->f;
1848 poly_DelContour (&t->contours);
1849 free (t);
1852 while (p != start);
1853 inhibit = false;
1854 IncrementUndoSerialNumber ();
1855 return many;
1858 void debug_pline (PLINE *pl)
1860 VNODE *v;
1861 pcb_fprintf (stderr, "\txmin %#mS xmax %#mS ymin %#mS ymax %#mS\n",
1862 pl->xmin, pl->xmax, pl->ymin, pl->ymax);
1863 v = &pl->head;
1864 while (v)
1866 pcb_fprintf(stderr, "\t\tvnode: %#mD\n", v->point[0], v->point[1]);
1867 v = v->next;
1868 if (v == &pl->head)
1869 break;
1873 void
1874 debug_polyarea (POLYAREA *p)
1876 PLINE *pl;
1878 fprintf (stderr, "POLYAREA %p\n", p);
1879 for (pl=p->contours; pl; pl=pl->next)
1880 debug_pline (pl);
1883 void
1884 debug_polygon (PolygonType *p)
1886 Cardinal i;
1887 POLYAREA *pa;
1888 fprintf (stderr, "POLYGON %p %d pts\n", p, p->PointN);
1889 for (i=0; i<p->PointN; i++)
1890 pcb_fprintf(stderr, "\t%d: %#mD\n", i, p->Points[i].X, p->Points[i].Y);
1891 if (p->HoleIndexN)
1893 fprintf (stderr, "%d holes, starting at indices\n", p->HoleIndexN);
1894 for (i=0; i<p->HoleIndexN; i++)
1895 fprintf(stderr, "\t%d: %d\n", i, p->HoleIndex[i]);
1897 else
1898 fprintf (stderr, "it has no holes\n");
1899 pa = p->Clipped;
1900 while (pa)
1902 debug_polyarea (pa);
1903 pa = pa->f;
1904 if (pa == p->Clipped)
1905 break;
1909 /* Convert a POLYAREA (and all linked POLYAREA) to
1910 * raw PCB polygons on the given layer.
1912 void
1913 PolyToPolygonsOnLayer (DataType *Destination, LayerType *Layer,
1914 POLYAREA *Input, FlagType Flags)
1916 PolygonType *Polygon;
1917 POLYAREA *pa;
1918 PLINE *pline;
1919 VNODE *node;
1920 bool outer;
1922 if (Input == NULL)
1923 return;
1925 pa = Input;
1928 Polygon = CreateNewPolygon (Layer, Flags);
1930 pline = pa->contours;
1931 outer = true;
1935 if (!outer)
1936 CreateNewHoleInPolygon (Polygon);
1937 outer = false;
1939 node = &pline->head;
1942 CreateNewPointInPolygon (Polygon, node->point[0],
1943 node->point[1]);
1945 while ((node = node->next) != &pline->head);
1948 while ((pline = pline->next) != NULL);
1950 InitClip (Destination, Layer, Polygon);
1951 SetPolygonBoundingBox (Polygon);
1952 if (!Layer->polygon_tree)
1953 Layer->polygon_tree = r_create_tree (NULL, 0, 0);
1954 r_insert_entry (Layer->polygon_tree, (BoxType *) Polygon, 0);
1956 DrawPolygon (Layer, Polygon);
1957 /* add to undo list */
1958 AddObjectToCreateUndoList (POLYGON_TYPE, Layer, Polygon, Polygon);
1960 while ((pa = pa->f) != Input);
1962 SetChangedFlag (true);