Tag physically/logically connected items differently in the "Find" action
[geda-pcb/pcjc2.git] / src / puller.c
blob7c9f0f922ee74a8185fcb19a784446d8f5b9d391
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 2006 DJ Delorie
6 * Copyright (C) 2011 PCB Contributers (See ChangeLog for details)
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * Contact addresses for paper mail and Email:
23 * DJ Delorie, 334 North Road, Deerfield NH 03037-1110, USA
24 * dj@delorie.com
28 /* FIXME: Things that need to be fixed before this is "perfect".
29 Add to this list as we find things.
31 - respect the outline layer.
33 - don't consider points that are perpendicular to our start_arc.
34 I.e. when we have busses going around corners, we have a *lot* of
35 arcs and endpoints that are all in the same direction and all
36 equally "good", but rounding the arc angles to integers causes
37 all sorts of tiny differences that result in bumps, reversals,
38 and other messes.
40 - Store the X,Y values in our shadow struct so we don't fill up the
41 undo buffer with all our line reversals.
43 - at least check the other layers in our layer group.
47 #ifdef HAVE_CONFIG_H
48 #include "config.h"
49 #endif
51 #include "global.h"
53 #include <math.h>
54 #include <memory.h>
55 #include <limits.h>
56 #include <setjmp.h>
59 #include "create.h"
60 #include "data.h"
61 #include "draw.h"
62 #include "misc.h"
63 #include "move.h"
64 #include "pcb-printf.h"
65 #include "remove.h"
66 #include "rtree.h"
67 #include "strflags.h"
68 #include "undo.h"
70 #ifdef HAVE_LIBDMALLOC
71 #include <dmalloc.h>
72 #endif
74 #define abort1() fprintf(stderr, "abort at line %d\n", __LINE__), abort()
76 #define TRACE0 0
77 #define TRACE1 0
79 /* sine of one degree */
80 #define SIN1D 0.0174524064372835
82 static jmp_buf abort_buf;
84 #define sqr(x) (1.0*(x)*(x))
86 static int multi, line_exact, arc_exact;
87 static LineType *the_line;
88 static ArcType *the_arc;
89 static double arc_dist;
91 /* We canonicalize the arc and line such that the point to be moved is
92 always Point2 for the line, and at start+delta for the arc. */
94 static int x, y; /* the point we're moving */
95 static int cx, cy; /* centerpoint of the arc */
96 static int ex, ey; /* fixed end of the line */
98 /* 0 is left (-x), 90 is down (+y), 180 is right (+x), 270 is up (-y) */
100 static int
101 within (int x1, int y1, int x2, int y2, int r)
103 return Distance (x1, y1, x2, y2) <= r / 2;
106 static int
107 arc_endpoint_is (ArcType *a, int angle, int x, int y)
109 int ax = a->X, ay = a->Y;
111 if (angle % 90 == 0)
113 int ai = (int) (angle / 90) & 3;
114 switch (ai)
116 case 0:
117 ax -= a->Width;
118 break;
119 case 1:
120 ay += a->Height;
121 break;
122 case 2:
123 ax += a->Width;
124 break;
125 case 3:
126 ay -= a->Height;
127 break;
130 else
132 double rad = angle * M_PI / 180;
133 ax -= a->Width * cos (rad);
134 ay += a->Width * sin (rad);
136 #if TRACE1
137 pcb_printf (" - arc endpoint %#mD\n", ax, ay);
138 #endif
139 arc_dist = Distance (ax, ay, x, y);
140 if (arc_exact)
141 return arc_dist < 2;
142 return arc_dist < a->Thickness / 2;
145 /* Cross c->u and c->v, return the magnitute */
146 static double
147 cross2d (int cx, int cy, int ux, int uy, int vx, int vy)
149 ux -= cx;
150 uy -= cy;
151 vx -= cx;
152 vy -= cy;
153 return (double)ux * vy - (double)uy * vx;
156 /* Likewise, for dot product. */
157 static double
158 dot2d (int cx, int cy, int ux, int uy, int vx, int vy)
160 ux -= cx;
161 uy -= cy;
162 vx -= cx;
163 vy -= cy;
164 return (double)ux * vx + (double)uy * vy;
167 #if 0
168 /* angle of c->v, relative to c->u, in radians. Range is -pi..pi */
169 static double
170 angle2d (int cx, int cy, int ux, int uy, int vx, int vy)
172 double cross;
173 double magu, magv, sintheta;
174 #if TRACE1
175 printf("angle2d %d,%d %d,%d %d,%d\n", cx, cy, ux, uy, vx, vy);
176 #endif
177 ux -= cx;
178 uy -= cy;
179 vx -= cx;
180 vy -= cy;
181 #if TRACE1
182 printf(" = %d,%d %d,%d\n", ux, uy, vx, vy);
183 #endif
184 cross = (double)ux * vy - (double)uy * vx;
185 magu = sqrt((double)ux*ux + (double)uy*uy);
186 magv = sqrt((double)vx*vx + (double)vy*vy);
187 sintheta = cross / (magu * magv);
188 #if TRACE1
189 printf(" = %f / (%f * %f) = %f\n", cross, magu, magv, sintheta);
190 #endif
191 return asin (sintheta);
193 #endif
195 static int
196 same_sign (double a, double b)
198 return (a * b >= 0);
201 static double
202 r2d (double r)
204 return 180.0 * r / M_PI;
207 static double
208 d2r (double d)
210 return M_PI * d / 180.0;
213 /* | a b |
214 | c d | */
215 static double
216 det (double a, double b, double c, double d)
218 return a * d - b * c;
221 /* The lines are x1y1-x2y2 and x3y3-x4y4. Returns true if they
222 intersect. */
223 static int
224 intersection_of_lines (int x1, int y1, int x2, int y2,
225 int x3, int y3, int x4, int y4,
226 int *xr, int *yr)
228 double x, y, d;
229 d = det (x1 - x2, y1 - y2, x3 - x4, y3 - y4);
230 if (!d)
231 return 0;
232 x = (det (det (x1, y1, x2, y2), x1 - x2,
233 det (x3, y3, x4, y4), x3 - x4) / d);
234 y = (det (det (x1, y1, x2, y2), y1 - y2,
235 det (x3, y3, x4, y4), y3 - y4) / d);
236 *xr = (int) (x + 0.5);
237 *yr = (int) (y + 0.5);
238 return 1;
241 /* Same, for line segments. Returns true if they intersect. For this
242 function, xr and yr may be NULL if you don't need the values. */
243 static int
244 intersection_of_linesegs (int x1, int y1, int x2, int y2,
245 int x3, int y3, int x4, int y4,
246 int *xr, int *yr)
248 double x, y, d;
249 d = det (x1 - x2, y1 - y2, x3 - x4, y3 - y4);
250 if (!d)
251 return 0;
252 x = (det (det (x1, y1, x2, y2), x1 - x2,
253 det (x3, y3, x4, y4), x3 - x4) / d);
254 y = (det (det (x1, y1, x2, y2), y1 - y2,
255 det (x3, y3, x4, y4), y3 - y4) / d);
256 if (MIN (x1, x2) > x || x > MAX (x1, x2)
257 || MIN (y1, y2) > y || y > MAX (y1, y2))
258 return 0;
259 if (MIN (x3, x4) > x || x > MAX (x3, x4)
260 || MIN (y3, y4) > y || y > MAX (y3, y4))
261 return 0;
262 if (xr)
263 *xr = (int) (x + 0.5);
264 if (yr)
265 *yr = (int) (y + 0.5);
266 return 1;
269 /* distance between a line and a point */
270 static double
271 dist_lp (int x1, int y1, int x2, int y2, int px, int py)
273 double den = Distance (x1, y1, x2, y2);
274 double rv = (fabs (((double)x2 - x1) * ((double)y1 - py)
275 - ((double)x1 - px) * ((double)y2 - y1))
276 / den);
277 #if TRACE1
278 pcb_printf("dist %#mD-%#mD to %#mD is %f\n",
279 x1, y1, x2, y2, px, py, rv);
280 #endif
281 return rv;
284 /* distance between a line segment and a point */
285 static double
286 dist_lsp (int x1, int y1, int x2, int y2, int px, int py)
288 double d;
289 if (dot2d (x1, y1, x2, y2, px, py) < 0)
290 return Distance (x1, y1, px, py);
291 if (dot2d (x2, y2, x1, y1, px, py) < 0)
292 return Distance (x2, y2, px, py);
293 d = (fabs (((double)x2 - x1) * ((double)y1 - py)
294 - ((double)x1 - px) * ((double)y2 - y1))
295 / Distance (x1, y1, x2, y2));
296 return d;
299 /*****************************************************************************/
300 /* */
301 /* Single Point Puller */
302 /* */
303 /*****************************************************************************/
305 static int
306 line_callback (const BoxType * b, void *cl)
308 /* LayerType *layer = (LayerType *)cl; */
309 LineType *l = (LineType *) b;
310 double d1, d2, t;
311 #if TRACE1
312 pcb_printf ("line %#mD .. %#mD\n",
313 l->Point1.X, l->Point1.Y, l->Point2.X, l->Point2.Y);
314 #endif
315 d1 = Distance (l->Point1.X, l->Point1.Y, x, y);
316 d2 = Distance (l->Point2.X, l->Point2.Y, x, y);
317 if ((d1 < 2 || d2 < 2) && !line_exact)
319 line_exact = 1;
320 the_line = 0;
322 t = line_exact ? 2 : l->Thickness / 2;
323 if (d1 < t || d2 < t)
325 if (the_line)
326 multi = 1;
327 the_line = l;
328 #if TRACE1
329 printf ("picked, exact %d\n", line_exact);
330 #endif
332 return 1;
335 static int
336 arc_callback (const BoxType * b, void *cl)
338 /* LayerType *layer = (LayerType *) cl; */
339 ArcType *a = (ArcType *) b;
341 #if TRACE1
342 pcb_printf ("arc a %#mD r %#mS sa %ld d %ld\n", a->X, a->Y, a->Width,
343 a->StartAngle, a->Delta);
344 #endif
345 if (!arc_endpoint_is (a, a->StartAngle, x, y)
346 && !arc_endpoint_is (a, a->StartAngle + a->Delta, x, y))
347 return 1;
348 if (arc_dist < 2)
350 if (!arc_exact)
352 arc_exact = 1;
353 the_arc = 0;
355 if (the_arc)
356 multi = 1;
357 the_arc = a;
358 #if TRACE1
359 printf ("picked, exact %d\n", arc_exact);
360 #endif
362 else if (!arc_exact)
364 if (the_arc)
365 multi = 1;
366 the_arc = a;
367 #if TRACE1
368 printf ("picked, exact %d\n", arc_exact);
369 #endif
371 return 1;
374 static int
375 find_pair (int Px, int Py)
377 BoxType spot;
379 #if TRACE1
380 pcb_printf ("\nPuller find_pair at %#mD\n", Crosshair.X, Crosshair.Y);
381 #endif
383 x = Px;
384 y = Py;
385 multi = 0;
386 line_exact = arc_exact = 0;
387 the_line = 0;
388 the_arc = 0;
389 spot.X1 = x - 1;
390 spot.Y1 = y - 1;
391 spot.X2 = x + 1;
392 spot.Y2 = y + 1;
393 r_search (CURRENT->line_tree, &spot, NULL, line_callback, CURRENT);
394 r_search (CURRENT->arc_tree, &spot, NULL, arc_callback, CURRENT);
395 if (the_line && the_arc && !multi)
396 return 1;
397 x = Px;
398 y = Py;
399 return 0;
403 static const char puller_syntax[] = "Puller()";
405 static const char puller_help[] = "Pull an arc-line junction tight.";
407 /* %start-doc actions Puller
409 The @code{Puller()} action is a special-purpose optimization. When
410 invoked while the crosshair is over the junction of an arc and a line,
411 it will adjust the arc's angle and the connecting line's endpoint such
412 that the line intersects the arc at a tangent. In the example below,
413 the left side is ``before'' with the black target marking where to put
414 the crosshair:
416 @center @image{puller,,,Example of how puller works,png}
418 The right side is ``after'' with the black target marking where the
419 arc-line intersection was moved to.
421 %end-doc */
423 static int
424 Puller (int argc, char **argv, Coord Ux, Coord Uy)
426 double arc_angle, base_angle;
427 #if TRACE1
428 double line_angle, rel_angle;
429 #endif
430 double tangent;
431 int new_delta_angle;
433 if (!find_pair (Crosshair.X, Crosshair.Y))
434 if (!find_pair (Ux, Uy))
435 return 0;
437 if (within (the_line->Point1.X, the_line->Point1.Y,
438 x, y, the_line->Thickness))
440 ex = the_line->Point2.X;
441 ey = the_line->Point2.Y;
442 the_line->Point2.X = the_line->Point1.X;
443 the_line->Point2.Y = the_line->Point1.Y;
444 the_line->Point1.X = ex;
445 the_line->Point1.Y = ey;
447 else if (!within (the_line->Point2.X, the_line->Point2.Y,
448 x, y, the_line->Thickness))
450 #if TRACE1
451 printf ("Line endpoint not at cursor\n");
452 #endif
453 return 1;
455 ex = the_line->Point1.X;
456 ey = the_line->Point1.Y;
458 cx = the_arc->X;
459 cy = the_arc->Y;
460 if (arc_endpoint_is (the_arc, the_arc->StartAngle, x, y))
462 ChangeArcAngles (CURRENT, the_arc, the_arc->StartAngle + the_arc->Delta,
463 -the_arc->Delta);
465 else if (!arc_endpoint_is (the_arc, the_arc->StartAngle + the_arc->Delta,
466 x, y))
468 #if TRACE1
469 printf ("arc not endpoints\n");
470 #endif
471 return 1;
474 if (within (cx, cy, ex, ey, the_arc->Width * 2))
476 #if TRACE1
477 printf ("line ends inside arc\n");
478 #endif
479 return 1;
482 if (the_arc->Delta > 0)
483 arc_angle = the_arc->StartAngle + the_arc->Delta + 90;
484 else
485 arc_angle = the_arc->StartAngle + the_arc->Delta - 90;
486 base_angle = r2d (atan2 (ey - cy, cx - ex));
488 tangent = r2d (acos (the_arc->Width / Distance (cx, cy, ex, ey)));
490 #if TRACE1
491 line_angle = r2d (atan2 (ey - y, x - ex));
492 rel_angle = line_angle - arc_angle;
493 printf ("arc %g line %g rel %g base %g\n", arc_angle, line_angle, rel_angle,
494 base_angle);
495 printf ("tangent %g\n", tangent);
497 printf ("arc was start %ld end %ld\n", the_arc->StartAngle,
498 the_arc->StartAngle + the_arc->Delta);
499 #endif
501 if (the_arc->Delta > 0)
502 arc_angle = base_angle - tangent;
503 else
504 arc_angle = base_angle + tangent;
505 #if TRACE1
506 printf ("new end angle %g\n", arc_angle);
507 #endif
509 new_delta_angle = arc_angle - the_arc->StartAngle;
510 if (new_delta_angle > 180)
511 new_delta_angle -= 360;
512 if (new_delta_angle < -180)
513 new_delta_angle += 360;
514 ChangeArcAngles (CURRENT, the_arc, the_arc->StartAngle, new_delta_angle);
516 #if TRACE1
517 printf ("arc now start %ld end %ld\n", the_arc->StartAngle,
518 the_arc->StartAngle + new_delta_angle);
519 #endif
521 arc_angle = the_arc->StartAngle + the_arc->Delta;
522 x = the_arc->X - the_arc->Width * cos (d2r (arc_angle)) + 0.5;
523 y = the_arc->Y + the_arc->Height * sin (d2r (arc_angle)) + 0.5;
525 MoveObject (LINEPOINT_TYPE, CURRENT, the_line, &(the_line->Point2),
526 x - the_line->Point2.X, y - the_line->Point2.Y);
528 gui->invalidate_all ();
529 IncrementUndoSerialNumber ();
531 return 1;
534 /*****************************************************************************/
535 /* */
536 /* Global Puller */
537 /* */
538 /*****************************************************************************/
540 static const char globalpuller_syntax[] =
541 "GlobalPuller()";
543 static const char globalpuller_help[] =
544 "Pull all traces tight.";
546 /* %start-doc actions GlobalPuller
548 %end-doc */
550 /* Ok, here's the deal. We look for the intersection of two traces.
551 The triangle formed by those traces is searched for things we need
552 to avoid. From the other two corners of the triangle, we compute
553 the angle to each obstacle, and remember the ones closest to the
554 start angles. If the two traces hit the same obstacle, we put in
555 the arc and we're done. Else, we bring the traces up to the
556 obstacles and start again.
558 Note that we assume each start point is a tangent to an arc. We
559 start with a radius of zero, but future steps use the arcs we
560 create as we go.
562 For obstacles, we list each round pin, pad, via, and line/arc
563 endpoints as points with a given radius. For each square pin, pad,
564 via, and polygon points, we list each corner with a zero radius.
565 We also list arcs from their centerpoint.
567 We don't currently do anything to move vias, or intersections of
568 three or more traces. In the future, three-way intersections will
569 be handles similarly to two-way - calculate the range of angles
570 valid from each of the three other endpoints, choose the angle
571 closest to making 120 degree angles at the center. For four-way or
572 more intersections, we break them up into multiple three-way
573 intersections.
575 For simplicity, we only do the current layer at this time. We will
576 also edit the lines and arcs in place so that the intersection is
577 always on the second point, and the other ends are always at
578 start+delta for arcs.
580 We also defer intersections which are blocked by other
581 intersections yet to be moved; the idea is to wait until those have
582 been moved so we don't end up with arcs that no longer wrap around
583 things. At a later point, we may choose to pull arced corners in
584 also.
586 You'll see lots of variables of the form "foo_sign" which keep
587 track of which way things are pointing. This is because everything
588 is relative to corners and arcs, not absolute directions.
591 static int nloops, npulled;
593 static void
594 status ()
596 fprintf(stderr, "%6d loops, %d pulled \r", nloops, npulled);
599 /* Extra data we need to temporarily attach to all lines and arcs. */
600 typedef struct End {
601 /* These point to "multi_next" if there are more than one. */
602 struct Extra *next;
603 void *pin;
604 unsigned char in_pin:1;
605 unsigned char at_pin:1;
606 unsigned char is_pad:1;
607 unsigned char pending:1; /* set if this may be moved later */
608 int x, y; /* arc endpoint */
609 /* If not NULL, points to End with pending==1 we're blocked on. */
610 struct End *waiting_for;
611 } End;
613 typedef struct Extra {
614 End start;
615 End end;
616 unsigned char found:1;
617 unsigned char deleted:1;
618 int type;
619 union {
620 LineType *line;
621 ArcType *arc;
622 } parent;
623 } Extra;
625 static Extra multi_next;
626 static GHashTable *lines;
627 static GHashTable *arcs;
628 static int did_something;
629 static int current_is_component, current_is_solder;
631 /* If set, these are the pins/pads/vias that this path ends on. */
632 /* static void *start_pin_pad, *end_pin_pad; */
634 #if TRACE1
635 static void trace_paths ();
636 #endif
637 static void mark_line_for_deletion (LineType *);
639 #define LINE2EXTRA(l) ((Extra *)g_hash_table_lookup (lines, l))
640 #define ARC2EXTRA(a) ((Extra *)g_hash_table_lookup (arcs, a))
641 #define EXTRA2LINE(e) (e->parent.line)
642 #define EXTRA2ARC(e) (e->parent.arc)
643 #define EXTRA_IS_LINE(e) (e->type == LINE_TYPE)
644 #define EXTRA_IS_ARC(e) (e->type == ARC_TYPE)
646 static void
647 unlink_end (Extra *x, Extra **e)
649 if (*e)
651 if ((*e)->start.next == x)
653 #if TRACE1
654 printf("%d: unlink_end, was %p\n", __LINE__, (*e)->start.next);
655 #endif
656 (*e)->start.next = &multi_next;
658 if ((*e)->end.next == x)
660 #if TRACE1
661 printf("%d: unlink_end, was %p\n", __LINE__, (*e)->start.next);
662 #endif
663 (*e)->end.next = &multi_next;
666 #if TRACE1
667 printf("%d: unlink_end, was %p\n", __LINE__, (*e));
668 #endif
669 (*e) = &multi_next;
672 #if TRACE1
674 static void
675 clear_found_cb (AnyObjectType *ptr, Extra *extra, void *userdata)
677 extra->found = 0;
680 static void
681 clear_found ()
683 g_hash_table_foreach (lines, (GHFunc)clear_found_cb, NULL);
684 g_hash_table_foreach (arcs, (GHFunc)clear_found_cb, NULL);
686 #endif
688 static void
689 fix_arc_extra (ArcType *a, Extra *e)
691 #if TRACE1
692 printf("new arc angles %ld %ld\n", a->StartAngle, a->Delta);
693 #endif
694 e->start.x = a->X - (a->Width * cos (d2r (a->StartAngle)) + 0.5);
695 e->start.y = a->Y + (a->Height * sin (d2r (a->StartAngle)) + 0.5);
696 e->end.x = a->X - (a->Width * cos (d2r (a->StartAngle+a->Delta)) + 0.5);
697 e->end.y = a->Y + (a->Height * sin (d2r (a->StartAngle+a->Delta)) + 0.5);
698 #if TRACE1
699 pcb_printf("new X,Y is %#mD to %#mD\n", e->start.x, e->start.y, e->end.x, e->end.y);
700 #endif
703 typedef struct {
704 void *me;
705 int x, y;
706 int is_arc;
707 Extra **extra_ptr;
708 } FindPairCallbackStruct;
710 #define NEAR(a,b) ((a) <= (b) + 2 && (a) >= (b) - 2)
712 static int
713 find_pair_line_callback (const BoxType * b, void *cl)
715 LineType *line = (LineType *) b;
716 #if TRACE1
717 Extra *e = LINE2EXTRA (line);
718 #endif
719 FindPairCallbackStruct *fpcs = (FindPairCallbackStruct *) cl;
721 if (line == fpcs->me)
722 return 0;
723 #ifdef CHECK_LINE_PT_NEG
724 if (line->Point1.X < 0)
725 abort1();
726 #endif
727 #if TRACE1
728 pcb_printf(" - %p line %#mD or %#mD\n", e, line->Point1.X, line->Point1.Y,
729 line->Point2.X, line->Point2.Y);
730 #endif
731 if ((NEAR (line->Point1.X, fpcs->x) && NEAR (line->Point1.Y, fpcs->y))
732 || (NEAR (line->Point2.X, fpcs->x) && NEAR (line->Point2.Y, fpcs->y)))
734 if (* fpcs->extra_ptr)
736 #if TRACE1
737 printf("multiple, was %p\n", *fpcs->extra_ptr);
738 #endif
739 *fpcs->extra_ptr = & multi_next;
741 else
743 *fpcs->extra_ptr = LINE2EXTRA (line);
744 #if TRACE1
745 printf(" - next now %p\n", *fpcs->extra_ptr);
746 #endif
749 return 0;
752 static int
753 find_pair_arc_callback (const BoxType * b, void *cl)
755 ArcType *arc = (ArcType *) b;
756 Extra *e = ARC2EXTRA (arc);
757 FindPairCallbackStruct *fpcs = (FindPairCallbackStruct *) cl;
759 if (arc == fpcs->me)
760 return 0;
761 #if TRACE1
762 pcb_printf(" - %p arc %#mD or %#mD\n", e, e->start.x, e->start.y, e->end.x, e->end.y);
763 #endif
764 if ((NEAR (e->start.x, fpcs->x) && NEAR (e->start.y, fpcs->y))
765 || (NEAR (e->end.x, fpcs->x) && NEAR (e->end.y, fpcs->y)))
767 if (* fpcs->extra_ptr)
769 #if TRACE1
770 printf("multiple, was %p\n", *fpcs->extra_ptr);
771 #endif
772 *fpcs->extra_ptr = & multi_next;
774 else
775 *fpcs->extra_ptr = e;
777 return 0;
780 static void
781 find_pairs_1 (void *me, Extra **e, int x, int y)
783 FindPairCallbackStruct fpcs;
784 BoxType b;
786 if (*e)
787 return;
789 fpcs.me = me;
790 fpcs.extra_ptr = e;
791 fpcs.x = x;
792 fpcs.y = y;
793 #if TRACE1
794 pcb_printf("looking for %#mD\n", x, y);
795 #endif
796 b.X1 = x - 10;
797 b.X2 = x + 10;
798 b.Y1 = y - 10;
799 b.Y2 = y + 10;
800 r_search(CURRENT->line_tree, &b, NULL, find_pair_line_callback, &fpcs);
801 r_search(CURRENT->arc_tree, &b, NULL, find_pair_arc_callback, &fpcs);
804 static int
805 check_point_in_pin (PinType *pin, int x, int y, End *e)
807 int inside_p;
808 int t = (pin->Thickness+1)/2;
809 if (TEST_FLAG (SQUAREFLAG, pin))
810 inside_p = (x >= pin->X - t && x <= pin->X + t
811 && y >= pin->Y - t && y <= pin->Y + t);
812 else
813 inside_p = (Distance (pin->X, pin->Y, x, y) <= t);
815 if (inside_p)
817 e->in_pin = 1;
818 if (pin->X == x && pin->Y == y)
819 e->at_pin = 1;
820 e->pin = pin;
821 return 1;
823 return 0;
826 static int
827 find_pair_pinline_callback (const BoxType * b, void *cl)
829 LineType *line = (LineType *) b;
830 PinType *pin = (PinType *) cl;
831 Extra *e = LINE2EXTRA (line);
832 int hits;
834 #ifdef CHECK_LINE_PT_NEG
835 if (line->Point1.X < 0)
836 abort1();
837 #endif
839 hits = check_point_in_pin (pin, line->Point1.X, line->Point1.Y, &(e->start));
840 hits += check_point_in_pin (pin, line->Point2.X, line->Point2.Y, &(e->end));
842 if (hits)
843 return 0;
845 /* See if the line passes through this pin. */
846 /* FIXME: this assumes round pads, but it's good enough for square
847 ones for now. */
848 if (dist_lsp (line->Point1.X, line->Point1.Y,
849 line->Point2.X, line->Point2.Y,
850 pin->X, pin->Y) <= pin->Thickness/2)
852 #if TRACE1
853 pcb_printf("splitting line %#mD-%#mD because it passes through pin %#mD r%d\n",
854 line->Point1.X, line->Point1.Y,
855 line->Point2.X, line->Point2.Y,
856 pin->X, pin->Y, pin->Thickness/2);
857 #endif
858 unlink_end (e, &e->start.next);
859 unlink_end (e, &e->end.next);
861 return 0;
864 static int
865 find_pair_pinarc_callback (const BoxType * b, void *cl)
867 ArcType *arc = (ArcType *) b;
868 PinType *pin = (PinType *) cl;
869 Extra *e = ARC2EXTRA (arc);
870 int hits;
872 hits = check_point_in_pin (pin, e->start.x, e->start.y, &(e->start));
873 hits += check_point_in_pin (pin, e->end.x, e->end.y, &(e->end));
874 return 0;
877 static int
878 check_point_in_pad (PadType *pad, int x, int y, End *e)
880 int inside_p;
881 int t;
883 pcb_printf("pad %#mD - %#mD t %#mS vs %#mD\n", pad->Point1.X, pad->Point1.Y,
884 pad->Point2.X, pad->Point2.Y, pad->Thickness, x, y);
885 t = (pad->Thickness+1)/2;
886 if (TEST_FLAG (SQUAREFLAG, pad))
888 inside_p = (x >= MIN (pad->Point1.X - t, pad->Point2.X - t)
889 && x <= MAX (pad->Point1.X + t, pad->Point2.X + t)
890 && y >= MIN (pad->Point1.Y - t, pad->Point2.Y - t)
891 && y <= MAX (pad->Point1.Y + t, pad->Point2.Y + t));
892 printf(" - inside_p = %d\n", inside_p);
894 else
896 if (pad->Point1.X == pad->Point2.X)
898 inside_p = (x >= pad->Point1.X - t
899 && x <= pad->Point1.X + t
900 && y >= MIN (pad->Point1.Y, pad->Point2.Y)
901 && y <= MAX (pad->Point1.Y, pad->Point2.Y));
903 else
905 inside_p = (x >= MIN (pad->Point1.X, pad->Point2.X)
906 && x <= MAX (pad->Point1.X, pad->Point2.X)
907 && y >= pad->Point1.Y - t
908 && y <= pad->Point1.Y + t);
910 if (!inside_p)
912 if (Distance (pad->Point1.X, pad->Point1.Y, x, y) <= t
913 || Distance (pad->Point2.X, pad->Point2.Y, x, y) <= t)
914 inside_p = 1;
918 if (inside_p)
920 e->in_pin = 1;
921 if (pad->Point1.X == x && pad->Point1.Y == y)
922 e->at_pin = 1;
923 if (pad->Point2.X == x && pad->Point2.Y == y)
924 e->at_pin = 1;
925 e->pin = pad;
926 e->is_pad = 1;
927 return 1;
929 return 0;
932 static int
933 find_pair_padline_callback (const BoxType * b, void *cl)
935 LineType *line = (LineType *) b;
936 PadType *pad = (PadType *) cl;
937 Extra *e = LINE2EXTRA (line);
938 int hits;
939 double t;
940 int intersect;
941 double p1_d, p2_d;
943 if (TEST_FLAG (ONSOLDERFLAG, pad))
945 if (!current_is_solder)
946 return 0;
948 else
950 if (!current_is_component)
951 return 0;
954 #ifdef CHECK_LINE_PT_NEG
955 if (line->Point1.X < 0)
956 abort1();
957 #endif
959 hits = check_point_in_pad (pad, line->Point1.X, line->Point1.Y, &(e->start));
960 hits += check_point_in_pad (pad, line->Point2.X, line->Point2.Y, &(e->end));
962 if (hits)
963 return 0;
965 /* Ok, something strange. The line intersects our space, but
966 doesn't end in our space. See if it just passes through us, and
967 mark it anyway. */
969 t = (pad->Thickness + 1)/2;
970 /* FIXME: this is for round pads. Good enough for now, but add
971 square pad support later. */
972 intersect = intersection_of_linesegs (pad->Point1.X, pad->Point1.Y,
973 pad->Point2.X, pad->Point2.Y,
974 line->Point1.X, line->Point1.Y,
975 line->Point2.X, line->Point2.Y,
976 NULL, NULL);
977 p1_d = dist_lsp(line->Point1.X, line->Point1.Y,
978 line->Point2.X, line->Point2.Y,
979 pad->Point1.X, pad->Point1.Y);
980 p2_d = dist_lsp(line->Point1.X, line->Point1.Y,
981 line->Point2.X, line->Point2.Y,
982 pad->Point2.X, pad->Point2.Y);
984 if (intersect || p1_d < t || p2_d < t)
986 /* It does. */
987 /* FIXME: we should split the line. */
988 #if TRACE1
989 pcb_printf("splitting line %#mD-%#mD because it passes through pad %#mD-%#mD r %#mS\n",
990 line->Point1.X, line->Point1.Y,
991 line->Point2.X, line->Point2.Y,
992 pad->Point1.X, pad->Point1.Y,
993 pad->Point2.X, pad->Point2.Y,
994 pad->Thickness/2);
995 #endif
996 unlink_end (e, &e->start.next);
997 unlink_end (e, &e->end.next);
1000 return 0;
1003 static int
1004 find_pair_padarc_callback (const BoxType * b, void *cl)
1006 ArcType *arc = (ArcType *) b;
1007 PadType *pad = (PadType *) cl;
1008 Extra *e = ARC2EXTRA (arc);
1009 int hits;
1011 if (TEST_FLAG (ONSOLDERFLAG, pad))
1013 if (!current_is_solder)
1014 return 0;
1016 else
1018 if (!current_is_component)
1019 return 0;
1022 hits = check_point_in_pad (pad, e->start.x, e->start.y, &(e->start));
1023 hits += check_point_in_pad (pad, e->end.x, e->end.y, &(e->end));
1024 return 0;
1027 static void
1028 null_multi_next_ends (AnyObjectType *ptr, Extra *extra, void *userdata)
1030 if (extra->start.next == &multi_next)
1031 extra->start.next = NULL;
1033 if (extra->end.next == &multi_next)
1034 extra->end.next = NULL;
1037 static Extra *
1038 new_line_extra (LineType *line)
1040 Extra *extra = g_slice_new0 (Extra);
1041 g_hash_table_insert (lines, line, extra);
1042 extra->parent.line = line;
1043 extra->type = LINE_TYPE;
1044 return extra;
1047 static Extra *
1048 new_arc_extra (ArcType *arc)
1050 Extra *extra = g_slice_new0 (Extra);
1051 g_hash_table_insert (arcs, arc, extra);
1052 extra->parent.arc = arc;
1053 extra->type = ARC_TYPE;
1054 return extra;
1057 static void
1058 find_pairs ()
1060 ARC_LOOP (CURRENT); {
1061 Extra *e = new_arc_extra (arc);
1062 fix_arc_extra (arc, e);
1063 } END_LOOP;
1065 LINE_LOOP (CURRENT); {
1066 new_line_extra (line);
1067 } END_LOOP;
1069 LINE_LOOP (CURRENT); {
1070 Extra *e = LINE2EXTRA (line);
1071 if (line->Point1.X >= 0)
1073 find_pairs_1 (line, & e->start.next, line->Point1.X, line->Point1.Y);
1074 find_pairs_1 (line, & e->end.next, line->Point2.X, line->Point2.Y);
1076 } END_LOOP;
1078 ARC_LOOP (CURRENT); {
1079 Extra *e = ARC2EXTRA (arc);
1080 if (!e->deleted)
1082 find_pairs_1 (arc, & e->start.next, e->start.x, e->start.y);
1083 find_pairs_1 (arc, & e->end.next, e->end.x, e->end.y);
1085 } END_LOOP;
1087 ALLPIN_LOOP (PCB->Data); {
1088 BoxType box;
1089 box.X1 = pin->X - pin->Thickness/2;
1090 box.Y1 = pin->Y - pin->Thickness/2;
1091 box.X2 = pin->X + pin->Thickness/2;
1092 box.Y2 = pin->Y + pin->Thickness/2;
1093 r_search (CURRENT->line_tree, &box, NULL, find_pair_pinline_callback, pin);
1094 r_search (CURRENT->arc_tree, &box, NULL, find_pair_pinarc_callback, pin);
1095 } ENDALL_LOOP;
1097 VIA_LOOP (PCB->Data); {
1098 BoxType box;
1099 box.X1 = via->X - via->Thickness/2;
1100 box.Y1 = via->Y - via->Thickness/2;
1101 box.X2 = via->X + via->Thickness/2;
1102 box.Y2 = via->Y + via->Thickness/2;
1103 r_search (CURRENT->line_tree, &box, NULL, find_pair_pinline_callback, via);
1104 r_search (CURRENT->arc_tree, &box, NULL, find_pair_pinarc_callback, via);
1105 } END_LOOP;
1107 ALLPAD_LOOP (PCB->Data); {
1108 BoxType box;
1109 box.X1 = MIN(pad->Point1.X, pad->Point2.X) - pad->Thickness/2;
1110 box.Y1 = MIN(pad->Point1.Y, pad->Point2.Y) - pad->Thickness/2;
1111 box.X2 = MAX(pad->Point1.X, pad->Point2.X) + pad->Thickness/2;
1112 box.Y2 = MAX(pad->Point1.Y, pad->Point2.Y) + pad->Thickness/2;
1113 r_search (CURRENT->line_tree, &box, NULL, find_pair_padline_callback, pad);
1114 r_search (CURRENT->arc_tree, &box, NULL, find_pair_padarc_callback, pad);
1116 } ENDALL_LOOP;
1118 g_hash_table_foreach (lines, (GHFunc)null_multi_next_ends, NULL);
1119 g_hash_table_foreach (arcs, (GHFunc)null_multi_next_ends, NULL);
1122 #define PROP_NEXT(e,n,f) \
1123 if (f->next->start.next == e) { \
1124 e = f->next; \
1125 n = & e->start; \
1126 f = & e->end; \
1127 } else { \
1128 e = f->next; \
1129 n = & e->end; \
1130 f = & e->start; }
1132 static void
1133 propogate_ends_at (Extra *e, End *near, End *far)
1135 while (far->in_pin && far->pin == near->pin)
1137 far->in_pin = 0;
1138 if (!far->next)
1139 return;
1140 PROP_NEXT (e, near, far);
1141 near->in_pin = 0;
1145 static void
1146 propogate_end_pin (Extra *e, End *near, End *far)
1148 void *pinpad = near->pin;
1149 int ispad = near->is_pad;
1150 while (far->next)
1152 PROP_NEXT (e, near, far);
1153 if (near->pin == pinpad)
1154 break;
1155 near->pin = pinpad;
1156 near->is_pad = ispad;
1160 static void
1161 propogate_end_step1_cb (AnyObjectType *ptr, Extra *extra, void *userdata)
1163 if (extra->start.next != NULL &&
1164 extra->start.next == extra->end.next)
1166 extra->end.next = NULL;
1167 mark_line_for_deletion ((LineType *)ptr);
1170 if (extra->start.at_pin)
1171 propogate_ends_at (extra, &extra->start, &extra->end);
1173 if (extra->end.at_pin)
1174 propogate_ends_at (extra, &extra->end, &extra->start);
1177 static void
1178 propogate_end_step2_cb (AnyObjectType *ptr, Extra *extra, void *userdata)
1180 if (extra->start.in_pin)
1182 #if TRACE1
1183 printf("MULTI at %d: was %p\n", __LINE__, extra->start.next);
1184 #endif
1185 extra->start.next = NULL;
1187 if (extra->end.in_pin)
1189 #if TRACE1
1190 printf("MULTI at %d: was %p\n", __LINE__, extra->end.next);
1191 #endif
1192 extra->end.next = NULL;
1196 static void
1197 propogate_end_step3_cb (AnyObjectType *ptr, Extra *extra, void *userdata)
1199 if (extra->start.next)
1200 propogate_end_pin (extra, &extra->end, &extra->start);
1201 if (extra->end.next)
1202 propogate_end_pin (extra, &extra->start, &extra->end);
1205 static void
1206 propogate_ends ()
1208 /* First, shut of "in pin" when we have an "at pin". We also clean
1209 up zero-length lines. */
1210 g_hash_table_foreach (lines, (GHFunc)propogate_end_step1_cb, NULL);
1212 /* Now end all paths at pins/pads. */
1213 g_hash_table_foreach (lines, (GHFunc)propogate_end_step2_cb, NULL);
1215 /* Now, propogate the pin/pad/vias along paths. */
1216 g_hash_table_foreach (lines, (GHFunc)propogate_end_step3_cb, NULL);
1219 static Extra *last_pextra = 0;
1221 static void
1222 print_extra (Extra *e, Extra *prev)
1224 int which = 0;
1225 if (e->start.next == last_pextra)
1226 which = 1;
1227 else if (e->end.next == last_pextra)
1228 which = 2;
1229 switch (which) {
1230 case 0:
1231 printf("%10p %10p %10p :", e, e->start.next, e->end.next);
1232 break;
1233 case 1:
1234 printf("%10p \033[33m%10p\033[0m %10p :", e, e->start.next, e->end.next);
1235 break;
1236 case 2:
1237 printf("%10p %10p \033[33m%10p\033[0m :", e, e->start.next, e->end.next);
1238 break;
1240 last_pextra = e;
1241 printf(" %c%c",
1242 e->deleted ? 'd' : '-',
1243 e->found ? 'f' : '-');
1244 printf(" s:%s%s%s%s",
1245 e->start.in_pin ? "I" : "-",
1246 e->start.at_pin ? "A" : "-",
1247 e->start.is_pad ? "P" : "-",
1248 e->start.pending ? "p" : "-");
1249 printf(" e:%s%s%s%s ",
1250 e->end.in_pin ? "I" : "-",
1251 e->end.at_pin ? "A" : "-",
1252 e->end.is_pad ? "P" : "-",
1253 e->end.pending ? "p" : "-");
1255 if (EXTRA_IS_LINE (e))
1257 LineType *line = EXTRA2LINE (e);
1258 pcb_printf(" %p L %#mD-%#mD", line, line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y);
1259 printf(" %s %p %s %p\n",
1260 e->start.is_pad ? "pad" : "pin", e->start.pin,
1261 e->end.is_pad ? "pad" : "pin", e->end.pin);
1263 else if (EXTRA_IS_ARC (e))
1265 ArcType *arc = EXTRA2ARC (e);
1266 pcb_printf(" %p A %#mD-%#mD", arc, e->start.x, e->start.y, e->end.x, e->end.y);
1267 pcb_printf(" at %#mD ang %ld,%ld\n", arc->X, arc->Y, arc->StartAngle, arc->Delta);
1269 else if (e == &multi_next)
1271 printf("-- Multi-next\n");
1273 else
1275 printf("-- Unknown extra: %p\n", e);
1279 #if TRACE1
1280 static void
1281 trace_path (Extra *e)
1283 Extra *prev = 0;
1284 if ((e->start.next && e->end.next)
1285 || (!e->start.next && !e->end.next))
1286 return;
1287 if (e->found)
1288 return;
1289 printf("- path -\n");
1290 last_pextra = 0;
1291 while (e)
1293 e->found = 1;
1294 print_extra (e, prev);
1295 if (e->start.next == prev)
1297 prev = e;
1298 e = e->end.next;
1300 else
1302 prev = e;
1303 e = e->start.next;
1308 static void
1309 trace_paths ()
1311 Extra *e;
1313 clear_found ();
1314 LINE_LOOP (CURRENT); {
1315 e = LINE2EXTRA (line);
1316 trace_path (e);
1317 } END_LOOP;
1318 ARC_LOOP (CURRENT); {
1319 e = ARC2EXTRA (arc);
1320 trace_path (e);
1321 } END_LOOP;
1323 #endif
1325 static void
1326 reverse_line (LineType *line)
1328 Extra *e = LINE2EXTRA (line);
1329 int x, y;
1330 End etmp;
1332 x = line->Point1.X;
1333 y = line->Point1.Y;
1334 #if 1
1335 MoveObject (LINEPOINT_TYPE, CURRENT, line, &(line->Point1),
1336 line->Point2.X - line->Point1.X,
1337 line->Point2.Y - line->Point1.Y);
1338 MoveObject (LINEPOINT_TYPE, CURRENT, line, &(line->Point2),
1339 x - line->Point2.X,
1340 y - line->Point2.Y);
1341 #else
1342 /* In theory, we should be using the above so that undo works. */
1343 line->Point1.X = line->Point2.X;
1344 line->Point1.Y = line->Point2.Y;
1345 line->Point2.X = x;
1346 line->Point2.Y = y;
1347 #endif
1348 memcpy (&etmp, &e->start, sizeof (End));
1349 memcpy (&e->start, &e->end, sizeof (End));
1350 memcpy (&e->end, &etmp, sizeof (End));
1353 static void
1354 reverse_arc (ArcType *arc)
1356 Extra *e = ARC2EXTRA (arc);
1357 End etmp;
1359 #if 1
1360 ChangeArcAngles (CURRENT, arc,
1361 arc->StartAngle + arc->Delta, -arc->Delta);
1362 #else
1363 /* Likewise, see above. */
1364 arc->StartAngle += arc->Delta;
1365 arc->Delta *= -1;
1366 #endif
1367 memcpy (&etmp, &e->start, sizeof (End));
1368 memcpy (&e->start, &e->end, sizeof (End));
1369 memcpy (&e->end, &etmp, sizeof (End));
1372 static void
1373 expand_box (BoxType *b, int x, int y, int t)
1375 b->X1 = MIN (b->X1, x-t);
1376 b->X2 = MAX (b->X2, x+t);
1377 b->Y1 = MIN (b->Y1, y-t);
1378 b->Y2 = MAX (b->Y2, y+t);
1381 /* ---------------------------------------------------------------------- */
1382 /* These are the state variables for the intersection we're currently
1383 working on. */
1385 /* what we're working with */
1386 static ArcType *start_arc;
1387 static LineType *start_line;
1388 static LineType *end_line;
1389 static ArcType *end_arc;
1390 static Extra *start_extra, *end_extra;
1391 static Extra *sarc_extra, *earc_extra;
1392 static void *start_pinpad, *end_pinpad;
1393 static int thickness;
1395 /* Pre-computed values. Note that all values are computed according
1396 to CARTESIAN coordinates, not PCB coordinates. Do an up-down board
1397 flip before wrapping your brain around the math. */
1399 /* se_sign is positive when you make a right turn going from start to end. */
1400 /* sa_sign is positive when start's arc is on the same side of start as end. */
1401 /* ea_sign is positive when end's arc is on the same side of end as start. */
1402 /* sa_sign and ea_sign may be zero if there's no arc. */
1403 static double se_sign, sa_sign, ea_sign;
1405 static double best_angle, start_angle, end_dist;
1406 /* arc radii are positive when they're on the same side as the things
1407 we're interested in. */
1408 static int sa_r, ea_r;
1409 static int sa_x, sa_y; /* start "arc" point */
1411 /* what we've found so far */
1412 static int fx, fy, fr, fp;
1413 static End *fp_end;
1414 static double fa; /* relative angle */
1416 #define gp_point(x,y,t,e) gp_point_2(x,y,t,e,0,0,__FUNCTION__)
1418 static int
1419 gp_point_force (int x, int y, int t, End *e, int esa, int eda, int force, const char *name)
1421 double r, a, d;
1422 int scx, scy, sr;
1423 double base_angle, rel_angle, point_angle;
1425 #if TRACE1
1426 pcb_printf("\033[34mgp_point_force %#mD %#mS via %s\033[0m\n", x, y, t, name);
1427 #endif
1429 if (start_arc)
1431 scx = start_arc->X;
1432 scy = start_arc->Y;
1433 sr = start_arc->Width;
1435 else
1437 scx = start_line->Point1.X;
1438 scy = start_line->Point1.Y;
1439 sr = 0;
1442 r = t + thickness;
1444 /* See if the point is inside our start arc. */
1445 d = Distance (scx, scy, x, y);
1446 #if TRACE1
1447 pcb_printf("%f = dist #mD to %#mD\n", d, scx, scy, x, y);
1448 pcb_printf("sr %#mS r %f d %f\n", sr, r, d);
1449 #endif
1450 if (d < sr - r)
1452 #if TRACE1
1453 printf("inside start arc, %f < %f\n", d, sr-r);
1454 #endif
1455 return 0;
1457 if (sr == 0 && d < r)
1459 #if TRACE1
1460 printf("start is inside arc, %f < %f\n", d, r);
1461 #endif
1462 return 0;
1465 /* Now for the same tricky math we needed for the single puller.
1466 sr and r are the radii for the two points scx,scy and x,y. */
1468 /* angle between points (NOT pcb arc angles) */
1469 base_angle = atan2 (y - scy, x - scx);
1470 #if TRACE1
1471 pcb_printf("%.1f = atan2 (%#mS-%#mS = %#mS, %#mS-%#mS = %#mS)\n",
1472 r2d(base_angle), y, scy, y-scy, x, scx, x-scx);
1473 #endif
1475 if ((sa_sign * sr - r) / d > 1
1476 || (sa_sign * sr - r) / d < -1)
1477 return 0;
1479 /* Angle of tangent, relative to the angle between point centers. */
1480 rel_angle = se_sign * asin ((sa_sign * sr - r) / d);
1481 #if TRACE1
1482 printf("%.1f = %d * asin ((%d * %d - %f) / %f)\n",
1483 r2d(rel_angle), (int)se_sign, (int)sa_sign, sr, r, d);
1484 #endif
1486 /* Absolute angle of tangent. */
1487 point_angle = base_angle + rel_angle;
1488 #if TRACE1
1489 printf("base angle %.1f rel_angle %.1f point_angle %.1f\n",
1490 r2d(base_angle),
1491 r2d(rel_angle),
1492 r2d(point_angle));
1493 #endif
1495 if (eda)
1497 /* Check arc angles */
1498 double pa = point_angle;
1499 double sa = d2r(180-esa);
1500 double da = d2r(-eda);
1502 if (da < 0)
1504 sa = sa + da;
1505 da = -da;
1508 pa -= se_sign * M_PI/2;
1509 while (sa+da < pa)
1510 sa += M_PI*2;
1511 while (sa > pa)
1512 sa -= M_PI*2;
1513 if (sa+da < pa)
1515 #if TRACE1
1516 printf("arc doesn't apply: sa %.1f da %.1f pa %.1f\n",
1517 r2d(sa), r2d(da), r2d(pa));
1518 #endif
1519 return 0;
1523 a = point_angle - start_angle;
1524 while (a > M_PI)
1525 a -= M_PI*2;
1526 while (a < -M_PI)
1527 a += M_PI*2;
1528 #if TRACE1
1529 printf(" - angle relative to S-E baseline is %.1f\n", r2d(a));
1530 #endif
1532 if (!force && a * se_sign < -0.007)
1534 double new_r;
1535 #if TRACE1
1536 printf("skipping, would increase angle (%f * %f)\n", a, se_sign);
1537 #endif
1538 new_r = dist_lp (start_line->Point1.X, start_line->Point1.Y,
1539 start_line->Point2.X, start_line->Point2.Y,
1540 x, y);
1541 #if TRACE1
1542 pcb_printf("point %#mD dist %#mS vs thickness %#mS\n", x, y, new_r, thickness);
1543 #endif
1544 new_r -= thickness;
1545 new_r = (int)new_r - 1;
1546 #if TRACE1
1547 pcb_printf(" - new thickness %f old %#mS\n", new_r, t);
1548 #endif
1549 if (new_r < t)
1550 gp_point_force (x, y, new_r, e, esa, eda, 1, __FUNCTION__);
1551 return 0;
1554 #if TRACE1
1555 printf("%f * %f < %f * %f ?\n", a, se_sign, best_angle, se_sign);
1556 #endif
1557 if (a * se_sign == best_angle * se_sign)
1559 double old_d = Distance (start_line->Point1.X, start_line->Point1.Y,
1560 fx, fy);
1561 double new_d = Distance (start_line->Point1.X, start_line->Point1.Y,
1562 x, y);
1563 if (new_d > old_d)
1565 best_angle = a;
1566 fx = x;
1567 fy = y;
1568 fr = r;
1569 fa = a;
1570 fp = e ? e->pending : 0;
1571 fp_end = e;
1574 else if (a * se_sign < best_angle * se_sign)
1576 best_angle = a;
1577 fx = x;
1578 fy = y;
1579 fr = r;
1580 fa = a;
1581 fp = e ? e->pending : 0;
1582 fp_end = e;
1585 return 1;
1587 static int
1588 gp_point_2 (int x, int y, int t, End *e, int esa, int eda, const char *func)
1590 double sc, ec;
1591 double sd, ed;
1593 if (x == sa_x && y ==sa_y)
1594 return 0;
1596 #if TRACE1
1597 pcb_printf("\033[34mgp_point %#mD %#mS via %s\033[0m\n", x, y, t, func);
1598 #endif
1600 /* There are two regions we care about. For points inside our
1601 triangle, we check the crosses against start_line and end_line to
1602 make sure the point is "inside" the triangle. For points on the
1603 other side of the s-e line of the triangle, we check the dots to
1604 make sure it's between our endpoints. */
1606 /* See what side of the s-e line we're on */
1607 sc = cross2d (start_line->Point1.X, start_line->Point1.Y,
1608 end_line->Point2.X, end_line->Point2.Y,
1609 x, y);
1610 #if TRACE1
1611 printf("s-e cross = %f\n", sc);
1612 #endif
1613 if (t >= 0)
1615 if (same_sign (sc, se_sign))
1617 /* Outside, check dots. */
1619 /* Ok, is it "in front of" our vectors? */
1620 sd = dot2d (start_line->Point1.X, start_line->Point1.Y,
1621 end_line->Point2.X, end_line->Point2.Y,
1622 x, y);
1623 #if TRACE1
1624 printf("sd = %f\n", sd);
1625 #endif
1626 if (sd <= 0)
1627 return 0;
1629 ed = dot2d (end_line->Point2.X, end_line->Point2.Y,
1630 start_line->Point1.X, start_line->Point1.Y,
1631 x, y);
1632 #if TRACE1
1633 printf("ed = %f\n", ed);
1634 #endif
1635 if (ed <= 0)
1636 return 0;
1638 sd = dist_lp (start_line->Point1.X, start_line->Point1.Y,
1639 end_line->Point2.X, end_line->Point2.Y,
1640 x, y);
1641 if (sd > t + thickness)
1642 return 0;
1644 else
1646 /* Inside, check crosses. */
1648 /* First off, is it on the correct side of the start line? */
1649 sc = cross2d (start_line->Point1.X, start_line->Point1.Y,
1650 start_line->Point2.X, start_line->Point2.Y,
1651 x, y);
1652 #if TRACE1
1653 printf("sc = %f\n", sc);
1654 #endif
1655 if (! same_sign (sc, se_sign))
1656 return 0;
1658 /* Ok, is it on the correct side of the end line? */
1659 ec = cross2d (end_line->Point1.X, end_line->Point1.Y,
1660 end_line->Point2.X, end_line->Point2.Y,
1661 x, y);
1662 #if TRACE1
1663 printf("ec = %f\n", ec);
1664 #endif
1665 if (! same_sign (ec, se_sign))
1666 return 0;
1670 #if TRACE1
1671 printf("in range!\n");
1672 #endif
1674 return gp_point_force (x, y, t, e, esa, eda, 0, func);
1677 static int
1678 gp_line_cb (const BoxType *b, void *cb)
1680 const LineType *l = (LineType *) b;
1681 Extra *e = LINE2EXTRA(l);
1682 if (l == start_line || l == end_line)
1683 return 0;
1684 if (e->deleted)
1685 return 0;
1686 #ifdef CHECK_LINE_PT_NEG
1687 if (l->Point1.X < 0)
1688 abort1();
1689 #endif
1690 if (! e->start.next
1691 || ! EXTRA_IS_ARC (e->start.next))
1692 gp_point (l->Point1.X, l->Point1.Y, l->Thickness/2, &e->start);
1693 if (! e->end.next
1694 || ! EXTRA_IS_ARC (e->end.next))
1695 gp_point (l->Point2.X, l->Point2.Y, l->Thickness/2, &e->end);
1696 return 0;
1699 static int
1700 gp_arc_cb (const BoxType *b, void *cb)
1702 const ArcType *a = (ArcType *) b;
1703 Extra *e = ARC2EXTRA(a);
1704 if (a == start_arc || a == end_arc)
1705 return 0;
1706 if (e->deleted)
1707 return 0;
1708 gp_point_2 (a->X, a->Y, a->Width + a->Thickness/2, 0, a->StartAngle, a->Delta, __FUNCTION__);
1709 if (start_arc
1710 && a->X == start_arc->X
1711 && a->Y == start_arc->Y)
1712 return 0;
1713 if (end_arc
1714 && a->X != end_arc->X
1715 && a->Y != end_arc->Y)
1716 return 0;
1718 if (e->start.next || e->end.next)
1719 return 0;
1721 gp_point (e->start.x, e->start.y, a->Thickness/2, 0);
1722 gp_point (e->end.x, e->end.y, a->Thickness/2, 0);
1723 return 0;
1726 static int
1727 gp_text_cb (const BoxType *b, void *cb)
1729 const TextType *t = (TextType *) b;
1730 /* FIXME: drop in the actual text-line endpoints later. */
1731 gp_point (t->BoundingBox.X1, t->BoundingBox.Y1, 0, 0);
1732 gp_point (t->BoundingBox.X1, t->BoundingBox.Y2, 0, 0);
1733 gp_point (t->BoundingBox.X2, t->BoundingBox.Y2, 0, 0);
1734 gp_point (t->BoundingBox.X2, t->BoundingBox.Y1, 0, 0);
1735 return 0;
1738 static int
1739 gp_poly_cb (const BoxType *b, void *cb)
1741 int i;
1742 const PolygonType *p = (PolygonType *) b;
1743 for (i=0; i<p->PointN; i++)
1744 gp_point (p->Points[i].X, p->Points[i].Y, 0, 0);
1745 return 0;
1748 static int
1749 gp_pin_cb (const BoxType *b, void *cb)
1751 const PinType *p = (PinType *) b;
1752 int t2 = (p->Thickness+1)/2;
1754 if (p == start_pinpad || p == end_pinpad)
1755 return 0;
1757 /* FIXME: we lump octagonal pins in with square; safe, but not
1758 optimal. */
1759 if (TEST_FLAG (SQUAREFLAG, p) || TEST_FLAG (OCTAGONFLAG, p))
1761 gp_point (p->X - t2, p->Y - t2, 0, 0);
1762 gp_point (p->X - t2, p->Y + t2, 0, 0);
1763 gp_point (p->X + t2, p->Y + t2, 0, 0);
1764 gp_point (p->X + t2, p->Y - t2, 0, 0);
1766 else
1768 gp_point (p->X, p->Y, t2, 0);
1770 return 0;
1773 static int
1774 gp_pad_cb (const BoxType *b, void *cb)
1776 const PadType *p = (PadType *) b;
1777 int t2 = (p->Thickness+1)/2;
1779 if (p == start_pinpad || p == end_pinpad)
1780 return 0;
1782 if (TEST_FLAG (ONSOLDERFLAG, p))
1784 if (!current_is_solder)
1785 return 0;
1787 else
1789 if (!current_is_component)
1790 return 0;
1793 /* FIXME: we lump octagonal pads in with square; safe, but not
1794 optimal. I don't think we even support octagonal pads. */
1795 if (TEST_FLAG (SQUAREFLAG, p) || TEST_FLAG (OCTAGONFLAG, p))
1797 if (p->Point1.X == p->Point2.X)
1799 int y1 = MIN (p->Point1.Y, p->Point2.Y) - t2;
1800 int y2 = MAX (p->Point1.Y, p->Point2.Y) + t2;
1802 gp_point (p->Point1.X - t2, y1, 0, 0);
1803 gp_point (p->Point1.X - t2, y2, 0, 0);
1804 gp_point (p->Point1.X + t2, y1, 0, 0);
1805 gp_point (p->Point1.X + t2, y2, 0, 0);
1807 else
1809 int x1 = MIN (p->Point1.X, p->Point2.X) - t2;
1810 int x2 = MAX (p->Point1.X, p->Point2.X) + t2;
1812 gp_point (x1, p->Point1.Y - t2, 0, 0);
1813 gp_point (x2, p->Point1.Y - t2, 0, 0);
1814 gp_point (x1, p->Point1.Y + t2, 0, 0);
1815 gp_point (x2, p->Point1.Y + t2, 0, 0);
1818 else
1820 gp_point (p->Point1.X, p->Point1.Y, t2, 0);
1821 gp_point (p->Point2.X, p->Point2.Y, t2, 0);
1823 return 0;
1826 static LineType *
1827 create_line (LineType *sample, int x1, int y1, int x2, int y2)
1829 #if TRACE1
1830 Extra *e;
1831 pcb_printf("create_line from %#mD to %#mD\n", x1, y1, x2, y2);
1832 #endif
1833 LineType *line = CreateNewLineOnLayer (CURRENT, x1, y1, x2, y2,
1834 sample->Thickness, sample->Clearance, sample->Flags);
1835 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1837 #if TRACE1
1839 #endif
1840 new_line_extra (line);
1841 #if TRACE1
1842 printf(" - line extra is %p\n", e);
1843 #endif
1844 return line;
1847 static ArcType *
1848 create_arc (LineType *sample, int x, int y, int r, int sa, int da)
1850 Extra *e;
1851 ArcType *arc;
1853 if (r % 100 == 1)
1854 r--;
1855 if (r % 100 == 99)
1856 r++;
1857 #if TRACE1
1858 pcb_printf("create_arc at %#mD r %#mS sa %d delta %d\n", x, y, r, sa, da);
1859 #endif
1860 arc = CreateNewArcOnLayer (CURRENT, x, y, r, r, sa, da,
1861 sample->Thickness, sample->Clearance, sample->Flags);
1862 if (arc == 0)
1864 arc = CreateNewArcOnLayer (CURRENT, x, y, r, r, sa, da*2,
1865 sample->Thickness, sample->Clearance, sample->Flags);
1867 AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
1869 if (!arc)
1870 longjmp (abort_buf, 1);
1872 e = new_arc_extra (arc);
1873 #if TRACE1
1874 printf(" - arc extra is %p\n", e);
1875 #endif
1876 fix_arc_extra (arc, e);
1877 return arc;
1880 static void
1881 unlink_extras (Extra *e)
1883 #if TRACE1
1884 fprintf(stderr, "unlink %p\n", e);
1885 print_extra(e,0);
1886 #endif
1887 if (e->start.next)
1889 #if TRACE1
1890 print_extra(e->start.next, 0);
1891 #endif
1892 if (e->start.next->start.next == e)
1894 #if TRACE1
1895 fprintf(stderr, " - %p->start points to me\n", e->start.next);
1896 #endif
1897 e->start.next->start.next = e->end.next;
1899 else if (e->start.next->end.next == e)
1901 #if TRACE1
1902 fprintf(stderr, " - %p->end points to me\n", e->start.next);
1903 #endif
1904 e->start.next->end.next = e->end.next;
1906 else
1908 fprintf(stderr, " - %p doesn't point to me!\n", e->start.next);
1909 abort();
1912 if (e->end.next)
1914 #if TRACE1
1915 print_extra(e->end.next, 0);
1916 #endif
1917 if (e->end.next->start.next == e)
1919 #if TRACE1
1920 fprintf(stderr, " - %p->end points to me\n", e->end.next);
1921 #endif
1922 e->end.next->start.next = e->start.next;
1924 else if (e->end.next->end.next == e)
1926 #if TRACE1
1927 fprintf(stderr, " - %p->end points to me\n", e->end.next);
1928 #endif
1929 e->end.next->end.next = e->start.next;
1931 else
1933 fprintf(stderr, " - %p doesn't point to me!\n", e->end.next);
1934 abort();
1937 e->start.next = e->end.next = 0;
1940 static void
1941 mark_line_for_deletion (LineType *l)
1943 Extra *e = LINE2EXTRA(l);
1944 if (e->deleted)
1946 fprintf(stderr, "double delete?\n");
1947 abort();
1949 e->deleted = 1;
1950 unlink_extras (e);
1951 #if TRACE1
1952 pcb_printf("Marked line %p for deletion %#mD to %#mD\n",
1953 e, l->Point1.X, l->Point1.Y, l->Point2.X, l->Point2.Y);
1954 #endif
1955 #if 0
1956 if (l->Point1.X < 0)
1958 fprintf(stderr, "double neg move?\n");
1959 abort();
1961 MoveObject (LINEPOINT_TYPE, CURRENT, l, &(l->Point1),
1962 -1 - l->Point1.X,
1963 -1 - l->Point1.Y);
1964 MoveObject (LINEPOINT_TYPE, CURRENT, l, &(l->Point2),
1965 -1 - l->Point2.X,
1966 -1 - l->Point2.Y);
1967 #endif
1970 static void
1971 mark_arc_for_deletion (ArcType *a)
1973 Extra *e = ARC2EXTRA(a);
1974 e->deleted = 1;
1975 unlink_extras (e);
1976 #if TRACE1
1977 printf("Marked arc %p for deletion %ld < %ld\n",
1978 e, a->StartAngle, a->Delta);
1979 #endif
1982 /* Given a starting line, which may be attached to an arc, and which
1983 intersects with an ending line, which also may be attached to an
1984 arc, maybe pull them. We assume start_line is attached to the arc
1985 via Point1, and attached to the end line via Point2. Likewise, we
1986 make end_line attach to the start_line via Point1 and the arc via
1987 Point 2. We also make the arcs attach on the Delta end, not the
1988 Start end. Here's a picture:
1990 S S+D P1 P2 P1 P2 S+D S
1991 *--- start_arc ---*--- start_line ---*--- end_line ---*--- end_arc ---*
1992 S E S E S E E S
1995 static void
1996 maybe_pull_1 (LineType *line)
1998 BoxType box;
1999 /* Line half-thicknesses, including line space */
2000 int ex, ey;
2001 LineType *new_line;
2002 Extra *new_lextra;
2003 ArcType *new_arc;
2004 Extra *new_aextra;
2005 double abs_angle;
2007 start_line = line;
2008 start_extra = LINE2EXTRA (start_line);
2009 end_extra = start_extra->end.next;
2010 end_line = EXTRA2LINE (end_extra);
2011 if (end_extra->deleted)
2013 start_extra->end.pending = 0;
2014 return;
2017 if (end_extra->end.next == start_extra)
2018 reverse_line (end_line);
2020 if (start_extra->start.next
2021 && EXTRA_IS_ARC (start_extra->start.next))
2023 sarc_extra = start_extra->start.next;
2024 start_arc = EXTRA2ARC (sarc_extra);
2025 if (sarc_extra->start.next == start_extra)
2026 reverse_arc (start_arc);
2028 else
2030 start_arc = 0;
2031 sarc_extra = 0;
2034 if (end_extra->end.next
2035 && EXTRA_IS_ARC (end_extra->end.next))
2037 earc_extra = end_extra->end.next;
2038 end_arc = EXTRA2ARC (earc_extra);
2039 if (earc_extra->start.next == end_extra)
2040 reverse_arc (end_arc);
2042 else
2044 end_arc = 0;
2045 earc_extra = 0;
2048 #if TRACE1
2049 printf("maybe_pull_1 %p %p %p %p\n", sarc_extra, start_extra, end_extra, earc_extra);
2050 if (sarc_extra)
2051 print_extra(sarc_extra,0);
2052 print_extra(start_extra,0);
2053 print_extra(end_extra,0);
2054 if (earc_extra)
2055 print_extra(earc_extra,0);
2057 if (start_extra->deleted
2058 || end_extra->deleted
2059 || (sarc_extra && sarc_extra->deleted)
2060 || (earc_extra && earc_extra->deleted))
2062 printf(" one is deleted?\n");
2063 fflush(stdout);
2064 abort();
2066 #endif
2068 if (!start_extra->end.pending)
2069 return;
2070 #if 0
2071 if (start_extra->end.waiting_for
2072 && start_extra->end.waiting_for->pending)
2073 return;
2074 #endif
2076 if (start_line->Thickness != end_line->Thickness)
2077 return;
2078 thickness = (start_line->Thickness + 1)/2 + PCB->Bloat;
2080 /* At this point, our expectations are all met. */
2082 box.X1 = start_line->Point1.X - thickness;
2083 box.X2 = start_line->Point1.X + thickness;
2084 box.Y1 = start_line->Point1.Y - thickness;
2085 box.Y2 = start_line->Point1.Y + thickness;
2086 expand_box (&box, start_line->Point2.X, start_line->Point2.Y, thickness);
2087 expand_box (&box, end_line->Point2.X, end_line->Point2.Y, thickness);
2088 if (start_arc)
2089 expand_box (&box, sarc_extra->start.x, sarc_extra->start.y, start_arc->Thickness/2);
2090 if (end_arc)
2091 expand_box (&box, earc_extra->start.x, earc_extra->start.y, end_arc->Thickness/2);
2094 se_sign = copysign (1, cross2d (start_line->Point1.X, start_line->Point1.Y,
2095 start_line->Point2.X, start_line->Point2.Y,
2096 end_line->Point2.X, end_line->Point2.Y));
2097 best_angle = se_sign * M_PI;
2098 if (start_arc)
2100 sa_sign = copysign (1, -start_arc->Delta);
2101 sa_sign *= se_sign;
2103 else
2104 sa_sign = 0;
2105 if (end_arc)
2107 ea_sign = copysign (1, -end_arc->Delta);
2108 ea_sign *= -se_sign;
2110 else
2111 ea_sign = 0;
2113 start_angle = atan2 (start_line->Point2.Y - start_line->Point1.Y,
2114 start_line->Point2.X - start_line->Point1.X);
2115 #if TRACE1
2116 printf("se_sign %f sa_sign %f ea_sign %f best_angle %f start_angle %f\n",
2117 se_sign, sa_sign, ea_sign, r2d (best_angle), r2d(start_angle));
2118 #endif
2120 if (start_arc)
2122 sa_x = start_arc->X;
2123 sa_y = start_arc->Y;
2124 if (same_sign (start_arc->Delta, se_sign))
2125 sa_r = - start_arc->Width;
2126 else
2127 sa_r = start_arc->Width;
2129 else
2131 sa_x = start_line->Point1.X;
2132 sa_y = start_line->Point1.Y;
2133 sa_r = 0;
2136 if (end_arc)
2138 if (ea_sign < 0)
2139 ea_r = end_arc->Width;
2140 else
2141 ea_r = - end_arc->Width;
2143 else
2144 ea_r = 0;
2146 #if TRACE1
2147 trace_path (sarc_extra ? sarc_extra : start_extra);
2148 #endif
2150 if (end_arc)
2152 gp_point_force (end_arc->X, end_arc->Y, -ea_r-thickness, 0, 0, 0, 1, "end arc");
2153 ex = end_arc->X;
2154 ey = end_arc->Y;
2156 else
2158 gp_point_force (end_line->Point2.X, end_line->Point2.Y, -thickness, 0, 0, 0, 1, "end arc");
2159 ex = end_line->Point2.X;
2160 ey = end_line->Point2.Y;
2163 fx = ex;
2164 fy = ey;
2165 if (fx < 0)
2167 pcb_fprintf(stderr, "end line corrupt? f is %#mD\n", fx, fy);
2168 print_extra (end_extra, 0);
2169 if (earc_extra)
2170 print_extra(earc_extra, 0);
2171 abort();
2174 end_dist = Distance (end_line->Point1.X, end_line->Point1.Y,
2175 end_line->Point2.X, end_line->Point2.Y);
2177 start_pinpad = start_extra->start.pin;
2178 end_pinpad = start_extra->end.pin;
2179 fp = 0;
2181 r_search(CURRENT->line_tree, &box, NULL, gp_line_cb, 0);
2182 r_search(CURRENT->arc_tree, &box, NULL, gp_arc_cb, 0);
2183 r_search(CURRENT->text_tree, &box, NULL, gp_text_cb, 0);
2184 r_search(CURRENT->polygon_tree, &box, NULL, gp_poly_cb, 0);
2185 r_search(PCB->Data->pin_tree, &box, NULL, gp_pin_cb, 0);
2186 r_search(PCB->Data->via_tree, &box, NULL, gp_pin_cb, 0);
2187 r_search(PCB->Data->pad_tree, &box, NULL, gp_pad_cb, 0);
2189 /* radians, absolute angle of (at the moment) the start_line */
2190 abs_angle = fa + start_angle;
2192 #if TRACE1
2193 pcb_printf("\033[43;30mBest: at %#mD r %#mS, angle %.1f fp %d\033[0m\n", fx, fy, fr, r2d(fa), fp);
2194 #endif
2196 #if 0
2197 if (fa > M_PI/2 || fa < -M_PI/2)
2199 SET_FLAG (FOUNDFLAG, line);
2200 longjmp (abort_buf, 1);
2202 #endif
2204 if (fp)
2206 start_extra->end.waiting_for = fp_end;
2207 return;
2209 start_extra->end.pending = 0;
2211 /* Step 0: check for merged arcs (special case). */
2213 if (fx == ex && fy == ey
2214 && start_arc && end_arc
2215 && start_arc->X == end_arc->X && start_arc->Y == end_arc->Y)
2217 /* Merge arcs */
2218 int new_delta;
2220 new_delta = end_arc->StartAngle - start_arc->StartAngle;
2221 if (start_arc->Delta > 0)
2223 while (new_delta > 360)
2224 new_delta -= 360;
2225 while (new_delta < 0)
2226 new_delta += 360;
2228 else
2230 while (new_delta < -360)
2231 new_delta += 360;
2232 while (new_delta > 0)
2233 new_delta -= 360;
2235 #if TRACE1
2236 pcb_printf("merging arcs at %#mS nd %d\n", start_arc->X, start_arc->Y, new_delta);
2237 print_extra(sarc_extra, 0);
2238 print_extra(earc_extra, 0);
2239 #endif
2240 mark_arc_for_deletion (end_arc);
2241 mark_line_for_deletion (start_line);
2242 mark_line_for_deletion (end_line);
2243 ChangeArcAngles (CURRENT, start_arc, start_arc->StartAngle, new_delta);
2244 fix_arc_extra (start_arc, sarc_extra);
2245 did_something ++;
2246 return;
2249 /* Step 1: adjust start_arc's angles and move start_line's Point1 to
2250 match it. */
2252 if (start_arc)
2254 double new_delta;
2255 int del_arc = 0;
2257 /* We must always round towards "larger arcs", whichever way that is. */
2258 new_delta = 180 - r2d(abs_angle);
2259 #if TRACE1
2260 printf("new_delta starts at %d vs %d < %d\n",
2261 (int)new_delta, (int)start_arc->StartAngle, (int)start_arc->Delta);
2262 #endif
2263 if (start_arc->Delta < 0)
2264 new_delta = (int)(new_delta) + 90;
2265 else
2266 new_delta = (int)new_delta - 90;
2267 new_delta = new_delta - start_arc->StartAngle;
2268 while (new_delta > start_arc->Delta+180)
2269 new_delta -= 360;
2270 while (new_delta < start_arc->Delta-180)
2271 new_delta += 360;
2272 #if TRACE1
2273 printf("new_delta adjusts to %d\n", (int)new_delta);
2274 printf("fa = %f, new_delta ends at %.1f vs start %d\n",
2275 fa, new_delta, (int)start_arc->StartAngle);
2276 #endif
2278 if (new_delta * start_arc->Delta <= 0)
2279 del_arc = 1;
2281 ChangeArcAngles (CURRENT, start_arc, start_arc->StartAngle, new_delta);
2282 fix_arc_extra (start_arc, sarc_extra);
2283 MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point1),
2284 sarc_extra->end.x - start_line->Point1.X,
2285 sarc_extra->end.y - start_line->Point1.Y);
2287 if (del_arc)
2289 MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point1),
2290 sarc_extra->start.x - start_line->Point1.X,
2291 sarc_extra->start.y - start_line->Point1.Y);
2292 mark_arc_for_deletion (start_arc);
2296 /* Step 1.5: If the "obstacle" is right at the end, ignore it. */
2299 double oa = start_angle+fa - M_PI/2*se_sign;
2300 double ox = fx + fr * cos(oa);
2301 double oy = fy + fr * sin(oa);
2302 #if TRACE1
2303 pcb_printf("obstacle at %#mD angle %d = arc starts at %#mD\n",
2304 fx, fy, (int)r2d(oa), (int)ox, (int)oy);
2305 #endif
2307 if (Distance (ox, oy, end_line->Point2.X, end_line->Point2.Y)
2308 < fr * SIN1D)
2310 /* Pretend it doesn't exist. */
2311 fx = ex;
2312 fy = ey;
2316 /* Step 2: If we have no obstacles, connect start and end. */
2318 #if TRACE1
2319 pcb_printf("fx %#mS ex %#mS fy %#mS ey %#mS\n", fx, ex, fy, ey);
2320 #endif
2321 if (fx == ex && fy == ey)
2323 /* No obstacles. */
2324 #if TRACE1
2325 printf("\033[32mno obstacles\033[0m\n");
2326 #endif
2327 if (end_arc)
2329 int del_arc = 0;
2330 int new_delta, end_angle, pcb_fa, end_change;
2332 end_angle = end_arc->StartAngle + end_arc->Delta;
2333 if (end_arc->Delta < 0)
2334 end_angle -= 90;
2335 else
2336 end_angle += 90;
2338 /* We must round so as to make the larger arc. */
2339 if (end_arc->Delta < 0)
2340 pcb_fa = - r2d(start_angle + fa);
2341 else
2342 pcb_fa = 1 - r2d(start_angle + fa);
2343 end_change = pcb_fa - end_angle;
2345 while (end_change > 180)
2346 end_change -= 360;
2347 while (end_change < -180)
2348 end_change += 360;
2349 new_delta = end_arc->Delta + end_change;
2351 if (new_delta * end_arc->Delta <= 0)
2352 del_arc = 1;
2354 ChangeArcAngles (CURRENT, end_arc, end_arc->StartAngle, new_delta);
2355 fix_arc_extra (end_arc, earc_extra);
2356 MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point2),
2357 earc_extra->end.x - start_line->Point2.X,
2358 earc_extra->end.y - start_line->Point2.Y);
2360 if (del_arc)
2362 MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point2),
2363 earc_extra->start.x - start_line->Point2.X,
2364 earc_extra->start.y - start_line->Point2.Y);
2365 mark_arc_for_deletion (end_arc);
2368 else
2370 MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point2),
2371 end_line->Point2.X - start_line->Point2.X,
2372 end_line->Point2.Y - start_line->Point2.Y);
2374 mark_line_for_deletion (end_line);
2375 start_extra->end.pending = 1;
2377 #if TRACE1
2378 printf("\033[35mdid_something: no obstacles\033[0m\n");
2379 #endif
2380 did_something ++;
2381 npulled ++;
2382 status();
2383 return;
2386 /* Step 3: Compute the new intersection of start_line and end_line. */
2388 ex = start_line->Point1.X + cos(start_angle + fa) * 10000.0;
2389 ey = start_line->Point1.Y + sin(start_angle + fa) * 10000.0;
2390 #if TRACE1
2391 pcb_printf("temp point %#mS\n", ex, ey);
2392 pcb_printf("intersect %#mS-%#mS with %#mS-%#mS\n",
2393 start_line->Point1.X, start_line->Point1.Y,
2394 ex, ey,
2395 end_line->Point1.X, end_line->Point1.Y,
2396 end_line->Point2.X, end_line->Point2.Y);
2397 #endif
2398 if (! intersection_of_lines (start_line->Point1.X, start_line->Point1.Y,
2399 ex, ey,
2400 end_line->Point1.X, end_line->Point1.Y,
2401 end_line->Point2.X, end_line->Point2.Y,
2402 &ex, &ey))
2404 ex = end_line->Point2.X;
2405 ey = end_line->Point2.Y;
2407 #if TRACE1
2408 pcb_printf("new point %#mS\n", ex, ey);
2409 #endif
2410 MoveObject (LINEPOINT_TYPE, CURRENT, end_line, &(end_line->Point1),
2411 ex - end_line->Point1.X,
2412 ey - end_line->Point1.Y);
2414 /* Step 4: Split start_line at the obstacle and insert a zero-delta
2415 arc at it. */
2417 new_arc = create_arc (start_line, fx, fy, fr,
2418 90-(int)(r2d(start_angle+fa)+0.5) + 90 + 90*se_sign, -se_sign);
2419 new_aextra = ARC2EXTRA (new_arc);
2421 if (start_arc) sarc_extra = ARC2EXTRA (start_arc);
2422 if (end_arc) earc_extra = ARC2EXTRA (end_arc);
2424 MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point2),
2425 new_aextra->start.x - start_line->Point2.X,
2426 new_aextra->start.y - start_line->Point2.Y);
2428 new_line = create_line (start_line, new_aextra->end.x, new_aextra->end.y, ex, ey);
2429 start_extra = LINE2EXTRA (start_line);
2430 new_lextra = LINE2EXTRA (new_line);
2431 end_extra = LINE2EXTRA (end_line);
2433 new_lextra->start.pin = start_extra->start.pin;
2434 new_lextra->end.pin = start_extra->end.pin;
2435 new_lextra->start.pending = 1;
2436 new_lextra->end.pending = 1;
2438 start_extra->end.next = new_aextra;
2439 new_aextra->start.next = start_extra;
2440 new_aextra->end.next = new_lextra;
2441 new_lextra->start.next = new_aextra;
2442 new_lextra->end.next = end_extra;
2443 end_extra->start.next = new_lextra;
2445 /* Step 5: Recurse. */
2447 did_something ++;
2448 npulled ++;
2449 status();
2450 #if TRACE0
2451 printf("\033[35mdid_something: recursing\033[0m\n");
2453 int i = gui->confirm_dialog("recurse?", 0);
2454 printf("confirm = %d\n", i);
2455 if (i == 0)
2456 return;
2458 printf("\n\033[33mRECURSING\033[0m\n\n");
2459 IncrementUndoSerialNumber();
2460 #endif
2461 maybe_pull_1 (new_line);
2464 /* Given a line with a end_next, attempt to pull both ends. */
2465 static void
2466 maybe_pull (LineType *line, Extra *e)
2468 #if TRACE0
2469 printf("maybe_pull: ");
2470 print_extra (e, 0);
2471 #endif
2472 if (e->end.next && EXTRA_IS_LINE (e->end.next))
2474 maybe_pull_1 (line);
2476 else
2478 e->end.pending = 0;
2479 if (e->start.next && EXTRA_IS_LINE (e->start.next))
2481 reverse_line (line);
2482 maybe_pull_1 (line);
2484 else
2485 e->start.pending = 0;
2489 static void
2490 validate_pair (Extra *e, End *end)
2492 if (!end->next)
2493 return;
2494 if (end->next->start.next == e)
2495 return;
2496 if (end->next->end.next == e)
2497 return;
2498 fprintf(stderr, "no backlink!\n");
2499 print_extra (e, 0);
2500 print_extra (end->next, 0);
2501 abort();
2504 static void
2505 validate_pair_cb (AnyObjectType *ptr, Extra *extra, void *userdata)
2507 validate_pair (extra, &extra->start);
2508 validate_pair (extra, &extra->end);
2511 static void
2512 validate_pairs ()
2514 g_hash_table_foreach (lines, (GHFunc)validate_pair_cb, NULL);
2515 #if TRACE1
2516 printf("\narcs\n");
2517 #endif
2518 g_hash_table_foreach (arcs, (GHFunc)validate_pair_cb, NULL);
2521 static void
2522 FreeExtra (Extra *extra)
2524 g_slice_free (Extra, extra);
2527 static void
2528 mark_ends_pending (LineType *line, Extra *extra, void *userdata)
2530 int *select_flags = userdata;
2531 if (TEST_FLAGS (*select_flags, line))
2533 extra->start.pending = 1;
2534 extra->end.pending = 1;
2538 #if TRACE1
2539 static void
2540 trace_print_extra (AnyObjectType *ptr, Extra *extra, void *userdata)
2542 last_pextra = (Extra *)1;
2543 print_extra(extra, 0);
2546 static void
2547 trace_print_lines_arcs (void)
2549 printf("\nlines\n");
2550 g_hash_table_foreach (lines, (GHFunc)trace_print_extra, NULL);
2552 printf("\narcs\n");
2553 g_hash_table_foreach (arcs, (GHFunc)trace_print_extra, NULL);
2555 printf("\n");
2557 #endif
2559 static int
2560 GlobalPuller(int argc, char **argv, Coord x, Coord y)
2562 int select_flags = 0;
2564 setbuf(stdout, 0);
2565 nloops = 0;
2566 npulled = 0;
2567 printf("puller! %s\n", argc > 0 ? argv[0] : "");
2569 if (argc > 0 && strcasecmp (argv[0], "selected") == 0)
2570 select_flags = SELECTEDFLAG;
2571 if (argc > 0 && strcasecmp (argv[0], "found") == 0)
2572 select_flags = FOUNDFLAG;
2574 printf("optimizing...\n");
2575 /* This canonicalizes all the lines, and cleans up near-misses. */
2576 /* hid_actionl ("djopt", "puller", 0); */
2578 current_is_solder = (GetLayerGroupNumberByPointer(CURRENT)
2579 == GetLayerGroupNumberByNumber (solder_silk_layer));
2580 current_is_component = (GetLayerGroupNumberByPointer(CURRENT)
2581 == GetLayerGroupNumberByNumber (component_silk_layer));
2583 lines = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)FreeExtra);
2584 arcs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)FreeExtra);
2586 printf("pairing...\n");
2587 find_pairs ();
2588 validate_pairs ();
2590 g_hash_table_foreach (lines, (GHFunc)mark_ends_pending, &select_flags);
2592 #if TRACE1
2593 trace_print_lines_arcs ();
2594 #endif
2596 propogate_ends ();
2598 #if TRACE1
2599 trace_print_lines_arcs ();
2600 trace_paths ();
2601 #endif
2603 printf("pulling...\n");
2604 if (setjmp(abort_buf) == 0)
2606 #if TRACE0
2607 int old_did_something = -1;
2608 #endif
2609 did_something = 1;
2610 while (did_something)
2612 nloops ++;
2613 status();
2614 did_something = 0;
2615 LINE_LOOP (CURRENT); {
2616 Extra *e = LINE2EXTRA (line);
2617 if (e->deleted)
2618 continue;
2619 #ifdef CHECK_LINE_PT_NEG
2620 if (line->Point1.X < 0)
2621 abort1();
2622 #endif
2623 if (e->start.next || e->end.next)
2624 maybe_pull (line, e);
2625 #if TRACE0
2626 if (did_something != old_did_something)
2628 IncrementUndoSerialNumber();
2629 old_did_something = did_something;
2630 if (gui->confirm_dialog("more?", 0) == 0)
2632 did_something = 0;
2633 break;
2636 #endif
2637 /*gui->progress(0,0,0);*/
2638 } END_LOOP;
2642 #if TRACE0
2643 printf("\nlines\n");
2644 g_hash_table_foreach (lines, (GHFunc)trace_print_extra, NULL);
2645 printf("\narcs\n");
2646 g_hash_table_foreach (arcs, (GHFunc)trace_print_extra, NULL);
2647 printf("\n");
2648 printf("\nlines\n");
2649 #endif
2651 LINE_LOOP (CURRENT);
2653 if (LINE2EXTRA (line)->deleted)
2654 RemoveLine (CURRENT, line);
2656 END_LOOP;
2658 ARC_LOOP (CURRENT);
2660 if (ARC2EXTRA (arc)->deleted)
2661 RemoveArc (CURRENT, arc);
2663 END_LOOP;
2665 g_hash_table_unref (lines);
2666 g_hash_table_unref (arcs);
2668 IncrementUndoSerialNumber();
2669 return 0;
2672 /*****************************************************************************/
2673 /* */
2674 /* Actions */
2675 /* */
2676 /*****************************************************************************/
2678 HID_Action puller_action_list[] = {
2679 {"Puller", "Click on a line-arc intersection or line segment", Puller,
2680 puller_help, puller_syntax},
2681 {"GlobalPuller", 0, GlobalPuller,
2682 globalpuller_help, globalpuller_syntax}
2685 REGISTER_ACTIONS (puller_action_list)