Remove unused functions {LO,PV}TouchesLine() and their callbacks
[geda-pcb/pcjc2.git] / src / polygon.c
blob2d3e9e4698c5926b5ae51da738dd7ade87212fb3
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 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 ((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 solder;
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->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 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.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 (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 *);
1464 static int
1465 subtract_plow (DataType *Data, LayerType *Layer, PolygonType *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, (PinType *) ptr2, Layer, Polygon);
1475 Polygon->NoHolesValid = 0;
1476 return 1;
1477 case LINE_TYPE:
1478 SubtractLine ((LineType *) ptr2, Polygon);
1479 Polygon->NoHolesValid = 0;
1480 return 1;
1481 case ARC_TYPE:
1482 SubtractArc ((ArcType *) ptr2, Polygon);
1483 Polygon->NoHolesValid = 0;
1484 return 1;
1485 case PAD_TYPE:
1486 SubtractPad ((PadType *) ptr2, Polygon);
1487 Polygon->NoHolesValid = 0;
1488 return 1;
1489 case TEXT_TYPE:
1490 SubtractText ((TextType *) ptr2, Polygon);
1491 Polygon->NoHolesValid = 0;
1492 return 1;
1494 return 0;
1497 static int
1498 add_plow (DataType *Data, LayerType *Layer, PolygonType *Polygon,
1499 int type, void *ptr1, void *ptr2)
1501 switch (type)
1503 case PIN_TYPE:
1504 case VIA_TYPE:
1505 UnsubtractPin ((PinType *) ptr2, Layer, Polygon);
1506 return 1;
1507 case LINE_TYPE:
1508 UnsubtractLine ((LineType *) ptr2, Layer, Polygon);
1509 return 1;
1510 case ARC_TYPE:
1511 UnsubtractArc ((ArcType *) ptr2, Layer, Polygon);
1512 return 1;
1513 case PAD_TYPE:
1514 UnsubtractPad ((PadType *) ptr2, Layer, Polygon);
1515 return 1;
1516 case TEXT_TYPE:
1517 UnsubtractText ((TextType *) 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 PolygonType *polygon = (PolygonType *) 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) (DataType *data, LayerType *lay,
1538 PolygonType *poly, int type, void *ptr1,
1539 void *ptr2))
1541 BoxType sb = ((PinType *) 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 ((LayerType *) 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, (LineType *) ptr2))
1581 return 0;
1582 /* silk doesn't plow */
1583 if (GetLayerNumber (Data, (LayerType *)ptr1) >= max_copper_layer)
1584 return 0;
1585 GROUP_LOOP (Data, GetLayerGroupNumberByNumber (GetLayerNumber (Data,
1586 ((LayerType *) 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 (!Data->polyClip)
1630 return;
1632 if (type == POLYGON_TYPE)
1633 InitClip (PCB->Data, (LayerType *) ptr1, (PolygonType *) ptr2);
1634 else
1635 PlowsPolygon (Data, type, ptr1, ptr2, add_plow);
1638 void
1639 ClearFromPolygon (DataType * Data, int type, void *ptr1, void *ptr2)
1641 if (!Data->polyClip)
1642 return;
1644 if (type == POLYGON_TYPE)
1645 InitClip (PCB->Data, (LayerType *) ptr1, (PolygonType *) ptr2);
1646 else
1647 PlowsPolygon (Data, type, ptr1, ptr2, subtract_plow);
1650 bool
1651 isects (POLYAREA * a, PolygonType *p, bool fr)
1653 POLYAREA *x;
1654 bool ans;
1655 ans = Touching (a, p->Clipped);
1656 /* argument may be register, so we must copy it */
1657 x = a;
1658 if (fr)
1659 poly_Free (&x);
1660 return ans;
1664 bool
1665 IsPointInPolygon (Coord X, Coord Y, Coord r, PolygonType *p)
1667 POLYAREA *c;
1668 Vector v;
1669 v[0] = X;
1670 v[1] = Y;
1671 if (poly_CheckInside (p->Clipped, v))
1672 return true;
1673 if (r < 1)
1674 return false;
1675 if (!(c = CirclePoly (X, Y, r)))
1676 return false;
1677 return isects (c, p, true);
1681 bool
1682 IsPointInPolygonIgnoreHoles (Coord X, Coord Y, PolygonType *p)
1684 Vector v;
1685 v[0] = X;
1686 v[1] = Y;
1687 return poly_InsideContour (p->Clipped->contours, v);
1690 bool
1691 IsRectangleInPolygon (Coord X1, Coord Y1, Coord X2, Coord Y2, PolygonType *p)
1693 POLYAREA *s;
1694 if (!
1695 (s = RectPoly (min (X1, X2), max (X1, X2), min (Y1, Y2), max (Y1, Y2))))
1696 return false;
1697 return isects (s, p, true);
1700 /* NB: This function will free the passed POLYAREA.
1701 * It must only be passed a single POLYAREA (pa->f == pa->b == pa)
1703 static void
1704 r_NoHolesPolygonDicer (POLYAREA * pa,
1705 void (*emit) (PLINE *, void *), void *user_data)
1707 PLINE *p = pa->contours;
1709 if (!pa->contours->next) /* no holes */
1711 pa->contours = NULL; /* The callback now owns the contour */
1712 /* Don't bother removing it from the POLYAREA's rtree
1713 since we're going to free the POLYAREA below anyway */
1714 emit (p, user_data);
1715 poly_Free (&pa);
1716 return;
1718 else
1720 POLYAREA *poly2, *left, *right;
1722 /* make a rectangle of the left region slicing through the middle of the first hole */
1723 poly2 = RectPoly (p->xmin, (p->next->xmin + p->next->xmax) / 2,
1724 p->ymin, p->ymax);
1725 poly_AndSubtract_free (pa, poly2, &left, &right);
1726 if (left)
1728 POLYAREA *cur, *next;
1729 cur = left;
1732 next = cur->f;
1733 cur->f = cur->b = cur; /* Detach this polygon piece */
1734 r_NoHolesPolygonDicer (cur, emit, user_data);
1735 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1737 while ((cur = next) != left);
1739 if (right)
1741 POLYAREA *cur, *next;
1742 cur = right;
1745 next = cur->f;
1746 cur->f = cur->b = cur; /* Detach this polygon piece */
1747 r_NoHolesPolygonDicer (cur, emit, user_data);
1748 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1750 while ((cur = next) != right);
1755 void
1756 NoHolesPolygonDicer (PolygonType *p, const BoxType * clip,
1757 void (*emit) (PLINE *, void *), void *user_data)
1759 POLYAREA *main_contour, *cur, *next;
1761 main_contour = poly_Create ();
1762 /* copy the main poly only */
1763 poly_Copy1 (main_contour, p->Clipped);
1764 /* clip to the bounding box */
1765 if (clip)
1767 POLYAREA *cbox = RectPoly (clip->X1, clip->X2, clip->Y1, clip->Y2);
1768 poly_Boolean_free (main_contour, cbox, &main_contour, PBO_ISECT);
1770 if (main_contour == NULL)
1771 return;
1772 /* Now dice it up.
1773 * NB: Could be more than one piece (because of the clip above)
1775 cur = main_contour;
1778 next = cur->f;
1779 cur->f = cur->b = cur; /* Detach this polygon piece */
1780 r_NoHolesPolygonDicer (cur, emit, user_data);
1781 /* NB: The POLYAREA was freed by its use in the recursive dicer */
1783 while ((cur = next) != main_contour);
1786 /* make a polygon split into multiple parts into multiple polygons */
1787 bool
1788 MorphPolygon (LayerType *layer, PolygonType *poly)
1790 POLYAREA *p, *start;
1791 bool many = false;
1792 FlagType flags;
1794 if (!poly->Clipped || TEST_FLAG (LOCKFLAG, poly))
1795 return false;
1796 if (poly->Clipped->f == poly->Clipped)
1797 return false;
1798 ErasePolygon (poly);
1799 start = p = poly->Clipped;
1800 /* This is ugly. The creation of the new polygons can cause
1801 * all of the polygon pointers (including the one we're called
1802 * with to change if there is a realloc in GetPolygonMemory().
1803 * That will invalidate our original "poly" argument, potentially
1804 * inside the loop. We need to steal the Clipped pointer and
1805 * hide it from the Remove call so that it still exists while
1806 * we do this dirty work.
1808 poly->Clipped = NULL;
1809 if (poly->NoHoles) printf ("Just leaked in MorpyPolygon\n");
1810 poly->NoHoles = NULL;
1811 flags = poly->Flags;
1812 RemovePolygon (layer, poly);
1813 inhibit = true;
1816 VNODE *v;
1817 PolygonType *newone;
1819 if (p->contours->area > PCB->IsleArea)
1821 newone = CreateNewPolygon (layer, flags);
1822 if (!newone)
1823 return false;
1824 many = true;
1825 v = &p->contours->head;
1826 CreateNewPointInPolygon (newone, v->point[0], v->point[1]);
1827 for (v = v->next; v != &p->contours->head; v = v->next)
1828 CreateNewPointInPolygon (newone, v->point[0], v->point[1]);
1829 newone->BoundingBox.X1 = p->contours->xmin;
1830 newone->BoundingBox.X2 = p->contours->xmax + 1;
1831 newone->BoundingBox.Y1 = p->contours->ymin;
1832 newone->BoundingBox.Y2 = p->contours->ymax + 1;
1833 AddObjectToCreateUndoList (POLYGON_TYPE, layer, newone, newone);
1834 newone->Clipped = p;
1835 p = p->f; /* go to next pline */
1836 newone->Clipped->b = newone->Clipped->f = newone->Clipped; /* unlink from others */
1837 r_insert_entry (layer->polygon_tree, (BoxType *) newone, 0);
1838 DrawPolygon (layer, newone);
1840 else
1842 POLYAREA *t = p;
1844 p = p->f;
1845 poly_DelContour (&t->contours);
1846 free (t);
1849 while (p != start);
1850 inhibit = false;
1851 IncrementUndoSerialNumber ();
1852 return many;
1855 void debug_pline (PLINE *pl)
1857 VNODE *v;
1858 pcb_fprintf (stderr, "\txmin %#mS xmax %#mS ymin %#mS ymax %#mS\n",
1859 pl->xmin, pl->xmax, pl->ymin, pl->ymax);
1860 v = &pl->head;
1861 while (v)
1863 pcb_fprintf(stderr, "\t\tvnode: %#mD\n", v->point[0], v->point[1]);
1864 v = v->next;
1865 if (v == &pl->head)
1866 break;
1870 void
1871 debug_polyarea (POLYAREA *p)
1873 PLINE *pl;
1875 fprintf (stderr, "POLYAREA %p\n", p);
1876 for (pl=p->contours; pl; pl=pl->next)
1877 debug_pline (pl);
1880 void
1881 debug_polygon (PolygonType *p)
1883 Cardinal i;
1884 POLYAREA *pa;
1885 fprintf (stderr, "POLYGON %p %d pts\n", p, p->PointN);
1886 for (i=0; i<p->PointN; i++)
1887 pcb_fprintf(stderr, "\t%d: %#mD\n", i, p->Points[i].X, p->Points[i].Y);
1888 if (p->HoleIndexN)
1890 fprintf (stderr, "%d holes, starting at indices\n", p->HoleIndexN);
1891 for (i=0; i<p->HoleIndexN; i++)
1892 fprintf(stderr, "\t%d: %d\n", i, p->HoleIndex[i]);
1894 else
1895 fprintf (stderr, "it has no holes\n");
1896 pa = p->Clipped;
1897 while (pa)
1899 debug_polyarea (pa);
1900 pa = pa->f;
1901 if (pa == p->Clipped)
1902 break;
1906 /* Convert a POLYAREA (and all linked POLYAREA) to
1907 * raw PCB polygons on the given layer.
1909 void
1910 PolyToPolygonsOnLayer (DataType *Destination, LayerType *Layer,
1911 POLYAREA *Input, FlagType Flags)
1913 PolygonType *Polygon;
1914 POLYAREA *pa;
1915 PLINE *pline;
1916 VNODE *node;
1917 bool outer;
1919 if (Input == NULL)
1920 return;
1922 pa = Input;
1925 Polygon = CreateNewPolygon (Layer, Flags);
1927 pline = pa->contours;
1928 outer = true;
1932 if (!outer)
1933 CreateNewHoleInPolygon (Polygon);
1934 outer = false;
1936 node = &pline->head;
1939 CreateNewPointInPolygon (Polygon, node->point[0],
1940 node->point[1]);
1942 while ((node = node->next) != &pline->head);
1945 while ((pline = pline->next) != NULL);
1947 InitClip (Destination, Layer, Polygon);
1948 SetPolygonBoundingBox (Polygon);
1949 if (!Layer->polygon_tree)
1950 Layer->polygon_tree = r_create_tree (NULL, 0, 0);
1951 r_insert_entry (Layer->polygon_tree, (BoxType *) Polygon, 0);
1953 DrawPolygon (Layer, Polygon);
1954 /* add to undo list */
1955 AddObjectToCreateUndoList (POLYGON_TYPE, Layer, Polygon, Polygon);
1957 while ((pa = pa->f) != Input);
1959 SetChangedFlag (true);