Revert "Fix locale-dependent gerber output"
[geda-pcb/whiteaudio.git] / src / polygon.c
blob14f239d4d74160764a0f57a3d5d20750a7516899
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 (PolygonTypePtr polygon, PointTypePtr 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 (PolygonTypePtr 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 (PolygonTypePtr 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 (PolygonTypePtr 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 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) * 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 / range - 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 ((PCBTypePtr) (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 solder;
877 POLYAREA *accumulate;
878 int batch_size;
879 jmp_buf env;
882 static void
883 subtract_accumulated (struct cpInfo *info, PolygonTypePtr 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 PinTypePtr pin = (PinTypePtr) b;
896 struct cpInfo *info = (struct cpInfo *) cl;
897 PolygonTypePtr 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 ((PCBTypePtr) (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 ArcTypePtr arc = (ArcTypePtr) b;
938 struct cpInfo *info = (struct cpInfo *) cl;
939 PolygonTypePtr 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 PadTypePtr pad = (PadTypePtr) b;
956 struct cpInfo *info = (struct cpInfo *) cl;
957 PolygonTypePtr 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->solder))
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 LineTypePtr line = (LineTypePtr) b;
978 struct cpInfo *info = (struct cpInfo *) cl;
979 PolygonTypePtr 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 TextTypePtr text = (TextTypePtr) b;
1007 struct cpInfo *info = (struct cpInfo *) cl;
1008 PolygonTypePtr 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 (DataTypePtr 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 (DataTypePtr Data, LayerTypePtr 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.solder = (group == Group (Data, solder_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.solder || group == Group (Data, component_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 (DataTypePtr Data, LayerTypePtr 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 (LayerTypePtr Layer, PolygonTypePtr Polygon)
1238 PointTypePtr 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 (PolygonTypePtr Polygon, Coord X, Coord Y)
1272 double mindistance = (double) MAX_COORD * MAX_COORD;
1273 PointTypePtr 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 PointTypePtr 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 PolygonTypePtr 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 LayerTypePtr layer;
1459 DataTypePtr data;
1460 int (*callback) (DataTypePtr, LayerTypePtr, PolygonTypePtr, int, void *,
1461 void *);
1464 static int
1465 subtract_plow (DataTypePtr Data, LayerTypePtr Layer, PolygonTypePtr Polygon,
1466 int type, void *ptr1, void *ptr2)
1468 if (!Polygon->Clipped)
1469 return 0;
1470 switch (type)
1472 case PIN_TYPE:
1473 case VIA_TYPE:
1474 SubtractPin (Data, (PinTypePtr) ptr2, Layer, Polygon);
1475 Polygon->NoHolesValid = 0;
1476 return 1;
1477 case LINE_TYPE:
1478 SubtractLine ((LineTypePtr) ptr2, Polygon);
1479 Polygon->NoHolesValid = 0;
1480 return 1;
1481 case ARC_TYPE:
1482 SubtractArc ((ArcTypePtr) ptr2, Polygon);
1483 Polygon->NoHolesValid = 0;
1484 return 1;
1485 case PAD_TYPE:
1486 SubtractPad ((PadTypePtr) ptr2, Polygon);
1487 Polygon->NoHolesValid = 0;
1488 return 1;
1489 case TEXT_TYPE:
1490 SubtractText ((TextTypePtr) ptr2, Polygon);
1491 Polygon->NoHolesValid = 0;
1492 return 1;
1494 return 0;
1497 static int
1498 add_plow (DataTypePtr Data, LayerTypePtr Layer, PolygonTypePtr Polygon,
1499 int type, void *ptr1, void *ptr2)
1501 switch (type)
1503 case PIN_TYPE:
1504 case VIA_TYPE:
1505 UnsubtractPin ((PinTypePtr) ptr2, Layer, Polygon);
1506 return 1;
1507 case LINE_TYPE:
1508 UnsubtractLine ((LineTypePtr) ptr2, Layer, Polygon);
1509 return 1;
1510 case ARC_TYPE:
1511 UnsubtractArc ((ArcTypePtr) ptr2, Layer, Polygon);
1512 return 1;
1513 case PAD_TYPE:
1514 UnsubtractPad ((PadTypePtr) ptr2, Layer, Polygon);
1515 return 1;
1516 case TEXT_TYPE:
1517 UnsubtractText ((TextTypePtr) ptr2, Layer, Polygon);
1518 return 1;
1520 return 0;
1523 static int
1524 plow_callback (const BoxType * b, void *cl)
1526 struct plow_info *plow = (struct plow_info *) cl;
1527 PolygonTypePtr polygon = (PolygonTypePtr) b;
1529 if (TEST_FLAG (CLEARPOLYFLAG, polygon))
1530 return plow->callback (plow->data, plow->layer, polygon, plow->type,
1531 plow->ptr1, plow->ptr2);
1532 return 0;
1536 PlowsPolygon (DataType * Data, int type, void *ptr1, void *ptr2,
1537 int (*call_back) (DataTypePtr data, LayerTypePtr lay,
1538 PolygonTypePtr poly, int type, void *ptr1,
1539 void *ptr2))
1541 BoxType sb = ((PinTypePtr) ptr2)->BoundingBox;
1542 int r = 0;
1543 struct plow_info info;
1545 info.type = type;
1546 info.ptr1 = ptr1;
1547 info.ptr2 = ptr2;
1548 info.data = Data;
1549 info.callback = call_back;
1550 switch (type)
1552 case PIN_TYPE:
1553 case VIA_TYPE:
1554 if (type == PIN_TYPE || ptr1 == ptr2 || ptr1 == NULL)
1556 LAYER_LOOP (Data, max_copper_layer);
1558 info.layer = layer;
1559 r +=
1560 r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
1562 END_LOOP;
1564 else
1566 GROUP_LOOP (Data, GetLayerGroupNumberByNumber (GetLayerNumber (Data,
1567 ((LayerTypePtr) ptr1))));
1569 info.layer = layer;
1570 r +=
1571 r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
1573 END_LOOP;
1575 break;
1576 case LINE_TYPE:
1577 case ARC_TYPE:
1578 case TEXT_TYPE:
1579 /* the cast works equally well for lines and arcs */
1580 if (!TEST_FLAG (CLEARLINEFLAG, (LineTypePtr) ptr2))
1581 return 0;
1582 /* silk doesn't plow */
1583 if (GetLayerNumber (Data, (LayerTypePtr)ptr1) >= max_copper_layer)
1584 return 0;
1585 GROUP_LOOP (Data, GetLayerGroupNumberByNumber (GetLayerNumber (Data,
1586 ((LayerTypePtr) ptr1))));
1588 info.layer = layer;
1589 r += r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
1591 END_LOOP;
1592 break;
1593 case PAD_TYPE:
1595 Cardinal group = GetLayerGroupNumberByNumber (
1596 TEST_FLAG (ONSOLDERFLAG, (PadType *) ptr2) ?
1597 solder_silk_layer : component_silk_layer);
1598 GROUP_LOOP (Data, group);
1600 info.layer = layer;
1601 r +=
1602 r_search (layer->polygon_tree, &sb, NULL, plow_callback, &info);
1604 END_LOOP;
1606 break;
1608 case ELEMENT_TYPE:
1610 PIN_LOOP ((ElementType *) ptr1);
1612 PlowsPolygon (Data, PIN_TYPE, ptr1, pin, call_back);
1614 END_LOOP;
1615 PAD_LOOP ((ElementType *) ptr1);
1617 PlowsPolygon (Data, PAD_TYPE, ptr1, pad, call_back);
1619 END_LOOP;
1621 break;
1623 return r;
1626 void
1627 RestoreToPolygon (DataType * Data, int type, void *ptr1, void *ptr2)
1629 if (type == POLYGON_TYPE)
1630 InitClip (PCB->Data, (LayerTypePtr) ptr1, (PolygonTypePtr) ptr2);
1631 else
1632 PlowsPolygon (Data, type, ptr1, ptr2, add_plow);
1635 void
1636 ClearFromPolygon (DataType * Data, int type, void *ptr1, void *ptr2)
1638 if (type == POLYGON_TYPE)
1639 InitClip (PCB->Data, (LayerTypePtr) ptr1, (PolygonTypePtr) ptr2);
1640 else
1641 PlowsPolygon (Data, type, ptr1, ptr2, subtract_plow);
1644 bool
1645 isects (POLYAREA * a, PolygonTypePtr p, bool fr)
1647 POLYAREA *x;
1648 bool ans;
1649 ans = Touching (a, p->Clipped);
1650 /* argument may be register, so we must copy it */
1651 x = a;
1652 if (fr)
1653 poly_Free (&x);
1654 return ans;
1658 bool
1659 IsPointInPolygon (Coord X, Coord Y, Coord r, PolygonTypePtr p)
1661 POLYAREA *c;
1662 Vector v;
1663 v[0] = X;
1664 v[1] = Y;
1665 if (poly_CheckInside (p->Clipped, v))
1666 return true;
1667 if (r < 1)
1668 return false;
1669 if (!(c = CirclePoly (X, Y, r)))
1670 return false;
1671 return isects (c, p, true);
1675 bool
1676 IsPointInPolygonIgnoreHoles (Coord X, Coord Y, PolygonTypePtr p)
1678 Vector v;
1679 v[0] = X;
1680 v[1] = Y;
1681 return poly_InsideContour (p->Clipped->contours, v);
1684 bool
1685 IsRectangleInPolygon (Coord X1, Coord Y1, Coord X2, Coord Y2, PolygonTypePtr p)
1687 POLYAREA *s;
1688 if (!
1689 (s = RectPoly (min (X1, X2), max (X1, X2), min (Y1, Y2), max (Y1, Y2))))
1690 return false;
1691 return isects (s, p, true);
1694 /* NB: This function will free the passed POLYAREA.
1695 * It must only be passed a single POLYAREA (pa->f == pa->b == pa)
1697 static void
1698 r_NoHolesPolygonDicer (POLYAREA * pa,
1699 void (*emit) (PLINE *, void *), void *user_data)
1701 PLINE *p = pa->contours;
1703 if (!pa->contours->next) /* no holes */
1705 pa->contours = NULL; /* The callback now owns the contour */
1706 /* Don't bother removing it from the POLYAREA's rtree
1707 since we're going to free the POLYAREA below anyway */
1708 emit (p, user_data);
1709 poly_Free (&pa);
1710 return;
1712 else
1714 POLYAREA *poly2, *left, *right;
1716 /* make a rectangle of the left region slicing through the middle of the first hole */
1717 poly2 = RectPoly (p->xmin, (p->next->xmin + p->next->xmax) / 2,
1718 p->ymin, p->ymax);
1719 poly_AndSubtract_free (pa, poly2, &left, &right);
1720 if (left)
1722 POLYAREA *cur, *next;
1723 cur = left;
1726 next = cur->f;
1727 cur->f = cur->b = cur; /* Detach this polygon piece */
1728 r_NoHolesPolygonDicer (cur, emit, user_data);
1729 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1731 while ((cur = next) != left);
1733 if (right)
1735 POLYAREA *cur, *next;
1736 cur = right;
1739 next = cur->f;
1740 cur->f = cur->b = cur; /* Detach this polygon piece */
1741 r_NoHolesPolygonDicer (cur, emit, user_data);
1742 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1744 while ((cur = next) != right);
1749 void
1750 NoHolesPolygonDicer (PolygonTypePtr p, const BoxType * clip,
1751 void (*emit) (PLINE *, void *), void *user_data)
1753 POLYAREA *main_contour, *cur, *next;
1755 main_contour = poly_Create ();
1756 /* copy the main poly only */
1757 poly_Copy1 (main_contour, p->Clipped);
1758 /* clip to the bounding box */
1759 if (clip)
1761 POLYAREA *cbox = RectPoly (clip->X1, clip->X2, clip->Y1, clip->Y2);
1762 poly_Boolean_free (main_contour, cbox, &main_contour, PBO_ISECT);
1764 if (main_contour == NULL)
1765 return;
1766 /* Now dice it up.
1767 * NB: Could be more than one piece (because of the clip above)
1769 cur = main_contour;
1772 next = cur->f;
1773 cur->f = cur->b = cur; /* Detach this polygon piece */
1774 r_NoHolesPolygonDicer (cur, emit, user_data);
1775 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1777 while ((cur = next) != main_contour);
1780 /* make a polygon split into multiple parts into multiple polygons */
1781 bool
1782 MorphPolygon (LayerTypePtr layer, PolygonTypePtr poly)
1784 POLYAREA *p, *start;
1785 bool many = false;
1786 FlagType flags;
1788 if (!poly->Clipped || TEST_FLAG (LOCKFLAG, poly))
1789 return false;
1790 if (poly->Clipped->f == poly->Clipped)
1791 return false;
1792 ErasePolygon (poly);
1793 start = p = poly->Clipped;
1794 /* This is ugly. The creation of the new polygons can cause
1795 * all of the polygon pointers (including the one we're called
1796 * with to change if there is a realloc in GetPolygonMemory().
1797 * That will invalidate our original "poly" argument, potentially
1798 * inside the loop. We need to steal the Clipped pointer and
1799 * hide it from the Remove call so that it still exists while
1800 * we do this dirty work.
1802 poly->Clipped = NULL;
1803 if (poly->NoHoles) printf ("Just leaked in MorpyPolygon\n");
1804 poly->NoHoles = NULL;
1805 flags = poly->Flags;
1806 RemovePolygon (layer, poly);
1807 inhibit = true;
1810 VNODE *v;
1811 PolygonTypePtr newone;
1813 if (p->contours->area > PCB->IsleArea)
1815 newone = CreateNewPolygon (layer, flags);
1816 if (!newone)
1817 return false;
1818 many = true;
1819 v = &p->contours->head;
1820 CreateNewPointInPolygon (newone, v->point[0], v->point[1]);
1821 for (v = v->next; v != &p->contours->head; v = v->next)
1822 CreateNewPointInPolygon (newone, v->point[0], v->point[1]);
1823 newone->BoundingBox.X1 = p->contours->xmin;
1824 newone->BoundingBox.X2 = p->contours->xmax + 1;
1825 newone->BoundingBox.Y1 = p->contours->ymin;
1826 newone->BoundingBox.Y2 = p->contours->ymax + 1;
1827 AddObjectToCreateUndoList (POLYGON_TYPE, layer, newone, newone);
1828 newone->Clipped = p;
1829 p = p->f; /* go to next pline */
1830 newone->Clipped->b = newone->Clipped->f = newone->Clipped; /* unlink from others */
1831 r_insert_entry (layer->polygon_tree, (BoxType *) newone, 0);
1832 DrawPolygon (layer, newone);
1834 else
1836 POLYAREA *t = p;
1838 p = p->f;
1839 poly_DelContour (&t->contours);
1840 free (t);
1843 while (p != start);
1844 inhibit = false;
1845 IncrementUndoSerialNumber ();
1846 return many;
1849 void debug_pline (PLINE *pl)
1851 VNODE *v;
1852 pcb_fprintf (stderr, "\txmin %#mS xmax %#mS ymin %#mS ymax %#mS\n",
1853 pl->xmin, pl->xmax, pl->ymin, pl->ymax);
1854 v = &pl->head;
1855 while (v)
1857 pcb_fprintf(stderr, "\t\tvnode: %#mD\n", v->point[0], v->point[1]);
1858 v = v->next;
1859 if (v == &pl->head)
1860 break;
1864 void
1865 debug_polyarea (POLYAREA *p)
1867 PLINE *pl;
1869 fprintf (stderr, "POLYAREA %p\n", p);
1870 for (pl=p->contours; pl; pl=pl->next)
1871 debug_pline (pl);
1874 void
1875 debug_polygon (PolygonType *p)
1877 Cardinal i;
1878 POLYAREA *pa;
1879 fprintf (stderr, "POLYGON %p %d pts\n", p, p->PointN);
1880 for (i=0; i<p->PointN; i++)
1881 pcb_fprintf(stderr, "\t%d: %#mD\n", i, p->Points[i].X, p->Points[i].Y);
1882 if (p->HoleIndexN)
1884 fprintf (stderr, "%d holes, starting at indices\n", p->HoleIndexN);
1885 for (i=0; i<p->HoleIndexN; i++)
1886 fprintf(stderr, "\t%d: %d\n", i, p->HoleIndex[i]);
1888 else
1889 fprintf (stderr, "it has no holes\n");
1890 pa = p->Clipped;
1891 while (pa)
1893 debug_polyarea (pa);
1894 pa = pa->f;
1895 if (pa == p->Clipped)
1896 break;
1900 /* Convert a POLYAREA (and all linked POLYAREA) to
1901 * raw PCB polygons on the given layer.
1903 void
1904 PolyToPolygonsOnLayer (DataType *Destination, LayerType *Layer,
1905 POLYAREA *Input, FlagType Flags)
1907 PolygonType *Polygon;
1908 POLYAREA *pa;
1909 PLINE *pline;
1910 VNODE *node;
1911 bool outer;
1913 if (Input == NULL)
1914 return;
1916 pa = Input;
1919 Polygon = CreateNewPolygon (Layer, Flags);
1921 pline = pa->contours;
1922 outer = true;
1926 if (!outer)
1927 CreateNewHoleInPolygon (Polygon);
1928 outer = false;
1930 node = &pline->head;
1933 CreateNewPointInPolygon (Polygon, node->point[0],
1934 node->point[1]);
1936 while ((node = node->next) != &pline->head);
1939 while ((pline = pline->next) != NULL);
1941 InitClip (Destination, Layer, Polygon);
1942 SetPolygonBoundingBox (Polygon);
1943 if (!Layer->polygon_tree)
1944 Layer->polygon_tree = r_create_tree (NULL, 0, 0);
1945 r_insert_entry (Layer->polygon_tree, (BoxType *) Polygon, 0);
1947 DrawPolygon (Layer, Polygon);
1948 /* add to undo list */
1949 AddObjectToCreateUndoList (POLYGON_TYPE, Layer, Polygon, Polygon);
1951 while ((pa = pa->f) != Input);
1953 SetChangedFlag (true);