More stackup changes
[geda-pcb/pcjc2/v2.git] / src / puller.c
blob4cda82098f9222e4fde979592c500ab7bebd5e0f
1 /*!
2 * \file src/puller.c
4 * \brief .
6 * \todo Things that need to be fixed before this is "perfect".\n
7 * Add to this list as we find things.
8 * - respect the outline layer.
9 * - don't consider points that are perpendicular to our start_arc.
10 * I.e. when we have busses going around corners, we have a *lot* of
11 * arcs and endpoints that are all in the same direction and all
12 * equally "good", but rounding the arc angles to integers causes
13 * all sorts of tiny differences that result in bumps, reversals,
14 * and other messes.
15 * - Store the X,Y values in our shadow struct so we don't fill up the
16 * undo buffer with all our line reversals.
17 * - at least check the other layers in our layer group.
19 * <hr>
21 * <h1><b>Copyright.</b></h1>\n
23 * PCB, interactive printed circuit board design
25 * Copyright (C) 2006 DJ Delorie
27 * Copyright (C) 2011 PCB Contributers (See ChangeLog for details)
29 * This program is free software; you can redistribute it and/or modify
30 * it under the terms of the GNU General Public License as published by
31 * the Free Software Foundation; either version 2 of the License, or
32 * (at your option) any later version.
34 * This program is distributed in the hope that it will be useful,
35 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 * GNU General Public License for more details.
39 * You should have received a copy of the GNU General Public License
40 * along with this program; if not, write to the Free Software
41 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
43 * Contact addresses for paper mail and Email:
44 * DJ Delorie, 334 North Road, Deerfield NH 03037-1110, USA
45 * dj@delorie.com
48 #ifdef HAVE_CONFIG_H
49 #include "config.h"
50 #endif
52 #include "global.h"
54 #include <math.h>
55 #include <memory.h>
56 #include <limits.h>
57 #include <setjmp.h>
60 #include "create.h"
61 #include "data.h"
62 #include "draw.h"
63 #include "misc.h"
64 #include "move.h"
65 #include "pcb-printf.h"
66 #include "remove.h"
67 #include "rtree.h"
68 #include "strflags.h"
69 #include "undo.h"
70 #include "error.h"
72 #ifdef HAVE_LIBDMALLOC
73 #include <dmalloc.h>
74 #endif
76 #define abort1() fprintf(stderr, "abort at line %d\n", __LINE__), abort()
78 #define TRACE0 0
79 #define TRACE1 0
81 /*! Sine of one degree. */
82 #define SIN1D 0.0174524064372835
84 static jmp_buf abort_buf;
85 static int multi, line_exact, arc_exact;
86 static LineType *the_line;
87 static ArcType *the_arc;
88 static double arc_dist;
90 /* We canonicalize the arc and line such that the point to be moved is
91 always Point2 for the line, and at start+delta for the arc. */
93 static Coord x, y; /* the point we're moving */
94 static Coord cx, cy; /* centerpoint of the arc */
95 static Coord ex, ey; /* fixed end of the line */
97 /* 0 is left (-x), 90 is down (+y), 180 is right (+x), 270 is up (-y) */
99 static Coord
100 within (Coord x1, Coord y1, Coord x2, Coord y2, Coord r)
102 return Distance (x1, y1, x2, y2) <= r / 2;
105 static int
106 arc_endpoint_is (ArcType *a, int angle, Coord x, Coord y)
108 Coord ax = a->X, ay = a->Y;
110 if (angle % 90 == 0)
112 int ai = (int) (angle / 90) & 3;
113 switch (ai)
115 case 0:
116 ax -= a->Width;
117 break;
118 case 1:
119 ay += a->Height;
120 break;
121 case 2:
122 ax += a->Width;
123 break;
124 case 3:
125 ay -= a->Height;
126 break;
129 else
131 double rad = angle * M_PI / 180;
132 ax -= a->Width * cos (rad);
133 ay += a->Width * sin (rad);
135 #if TRACE1
136 pcb_printf (" - arc endpoint %#mD\n", ax, ay);
137 #endif
138 arc_dist = Distance (ax, ay, x, y);
139 if (arc_exact)
140 return arc_dist < 2;
141 return arc_dist < a->Thickness / 2;
145 * brief Cross c->u and c->v, return the magnitude.
147 static double
148 cross2d (Coord cx, Coord cy, Coord ux, Coord uy, Coord vx, Coord vy)
150 ux -= cx;
151 uy -= cy;
152 vx -= cx;
153 vy -= cy;
154 return (double)ux * vy - (double)uy * vx;
158 * \brief Likewise, for dot product.
160 static double
161 dot2d (Coord cx, Coord cy, Coord ux, Coord uy, Coord vx, Coord vy)
163 ux -= cx;
164 uy -= cy;
165 vx -= cx;
166 vy -= cy;
167 return (double)ux * vx + (double)uy * vy;
170 #if 0
172 * \brief .
174 * angle of c->v, relative to c->u, in radians.
175 * Range is -pi..pi.
177 static double
178 angle2d (Coord cx, Coord cy, Coord ux, Coord uy, Coord vx, Coord vy)
180 double cross;
181 double magu, magv, sintheta;
182 #if TRACE1
183 pcb_printf("angle2d %mD %mD %mD\n", cx, cy, ux, uy, vx, vy);
184 #endif
185 ux -= cx;
186 uy -= cy;
187 vx -= cx;
188 vy -= cy;
189 #if TRACE1
190 pcb_printf(" = %mD %mD\n", ux, uy, vx, vy);
191 #endif
192 cross = (double)ux * vy - (double)uy * vx;
193 magu = hypot(ux, uy);
194 magv = hypot(vx, vy);
195 sintheta = cross / (magu * magv);
196 #if TRACE1
197 printf(" = %f / (%f * %f) = %f\n", cross, magu, magv, sintheta);
198 #endif
199 return asin (sintheta);
201 #endif
203 static int
204 same_sign (double a, double b)
206 return (a * b >= 0);
209 static double
210 r2d (double r)
212 return 180.0 * r / M_PI;
215 static double
216 d2r (double d)
218 return M_PI * d / 180.0;
222 * \brief .
224 * | a b |
226 * | c d |
228 static double
229 det (double a, double b, double c, double d)
231 return a * d - b * c;
235 * \brief .
237 * The lines are \f$ x_1y_1-x_2y_2 \f$ and \f$ x_3y_3-x_4y_4 \f$.
239 * \return True if they intersect.
241 static int
242 intersection_of_lines (Coord x1, Coord y1, Coord x2, Coord y2,
243 Coord x3, Coord y3, Coord x4, Coord y4,
244 Coord *xr, Coord *yr)
246 double x, y, d;
247 d = det (x1 - x2, y1 - y2, x3 - x4, y3 - y4);
248 if (!d)
249 return 0;
250 x = (det (det (x1, y1, x2, y2), x1 - x2,
251 det (x3, y3, x4, y4), x3 - x4) / d);
252 y = (det (det (x1, y1, x2, y2), y1 - y2,
253 det (x3, y3, x4, y4), y3 - y4) / d);
254 *xr = (Coord) (x + 0.5);
255 *yr = (Coord) (y + 0.5);
256 return 1;
260 * \brief Same, for line segments.
262 * \return True if they intersect.
264 * For this function, \c xr and \c yr may be \c NULL if you don't need
265 * the values.
267 static int
268 intersection_of_linesegs (Coord x1, Coord y1, Coord x2, Coord y2,
269 Coord x3, Coord y3, Coord x4, Coord y4,
270 Coord *xr, Coord *yr)
272 double x, y, d;
273 d = det (x1 - x2, y1 - y2, x3 - x4, y3 - y4);
274 if (!d)
275 return 0;
276 x = (det (det (x1, y1, x2, y2), x1 - x2,
277 det (x3, y3, x4, y4), x3 - x4) / d);
278 y = (det (det (x1, y1, x2, y2), y1 - y2,
279 det (x3, y3, x4, y4), y3 - y4) / d);
280 if (MIN (x1, x2) > x || x > MAX (x1, x2)
281 || MIN (y1, y2) > y || y > MAX (y1, y2))
282 return 0;
283 if (MIN (x3, x4) > x || x > MAX (x3, x4)
284 || MIN (y3, y4) > y || y > MAX (y3, y4))
285 return 0;
286 if (xr)
287 *xr = (Coord) (x + 0.5);
288 if (yr)
289 *yr = (Coord) (y + 0.5);
290 return 1;
294 * \brief Distance between a line and a point.
296 static double
297 dist_lp (Coord x1, Coord y1, Coord x2, Coord y2, Coord px, Coord py)
299 double den = Distance (x1, y1, x2, y2);
300 double rv = (fabs (((double)x2 - x1) * ((double)y1 - py)
301 - ((double)x1 - px) * ((double)y2 - y1))
302 / den);
303 #if TRACE1
304 pcb_printf("dist %#mD-%#mD to %#mD is %f\n",
305 x1, y1, x2, y2, px, py, rv);
306 #endif
307 return rv;
311 * \brief Distance between a line segment and a point.
313 static double
314 dist_lsp (Coord x1, Coord y1, Coord x2, Coord y2, Coord px, Coord py)
316 double d;
317 if (dot2d (x1, y1, x2, y2, px, py) < 0)
318 return Distance (x1, y1, px, py);
319 if (dot2d (x2, y2, x1, y1, px, py) < 0)
320 return Distance (x2, y2, px, py);
321 d = (fabs (((double)x2 - x1) * ((double)y1 - py)
322 - ((double)x1 - px) * ((double)y2 - y1))
323 / Distance (x1, y1, x2, y2));
324 return d;
327 /* Single Point Puller */
329 static int
330 line_callback (const BoxType * b, void *cl)
332 /* LayerType *layer = (LayerType *)cl; */
333 LineType *l = (LineType *) b;
334 double d1, d2, t;
335 #if TRACE1
336 pcb_printf ("line %#mD .. %#mD\n",
337 l->Point1.X, l->Point1.Y, l->Point2.X, l->Point2.Y);
338 #endif
339 d1 = Distance (l->Point1.X, l->Point1.Y, x, y);
340 d2 = Distance (l->Point2.X, l->Point2.Y, x, y);
341 if ((d1 < 2 || d2 < 2) && !line_exact)
343 line_exact = 1;
344 the_line = 0;
346 t = line_exact ? 2 : l->Thickness / 2;
347 if (d1 < t || d2 < t)
349 if (the_line)
350 multi = 1;
351 the_line = l;
352 #if TRACE1
353 printf ("picked, exact %d\n", line_exact);
354 #endif
356 return 1;
359 static int
360 arc_callback (const BoxType * b, void *cl)
362 /* LayerType *layer = (LayerType *) cl; */
363 ArcType *a = (ArcType *) b;
365 #if TRACE1
366 pcb_printf ("arc a %#mD r %#mS sa %ld d %ld\n", a->X, a->Y, a->Width,
367 a->StartAngle, a->Delta);
368 #endif
369 if (!arc_endpoint_is (a, a->StartAngle, x, y)
370 && !arc_endpoint_is (a, a->StartAngle + a->Delta, x, y))
371 return 1;
372 if (arc_dist < 2)
374 if (!arc_exact)
376 arc_exact = 1;
377 the_arc = 0;
379 if (the_arc)
380 multi = 1;
381 the_arc = a;
382 #if TRACE1
383 printf ("picked, exact %d\n", arc_exact);
384 #endif
386 else if (!arc_exact)
388 if (the_arc)
389 multi = 1;
390 the_arc = a;
391 #if TRACE1
392 printf ("picked, exact %d\n", arc_exact);
393 #endif
395 return 1;
398 static int
399 find_pair (Coord Px, Coord Py)
401 BoxType spot;
403 #if TRACE1
404 pcb_printf ("\nPuller find_pair at %#mD\n", Px, Py);
405 #endif
407 x = Px;
408 y = Py;
409 multi = 0;
410 line_exact = arc_exact = 0;
411 the_line = 0;
412 the_arc = 0;
413 spot.X1 = x - 1;
414 spot.Y1 = y - 1;
415 spot.X2 = x + 1;
416 spot.Y2 = y + 1;
417 r_search (CURRENT->line_tree, &spot, NULL, line_callback, CURRENT);
418 r_search (CURRENT->arc_tree, &spot, NULL, arc_callback, CURRENT);
419 if (the_line && the_arc && !multi)
420 return 1;
421 x = Px;
422 y = Py;
423 return 0;
427 static const char puller_syntax[] = "Puller()";
429 static const char puller_help[] = "Pull an arc-line junction tight.";
431 /* %start-doc actions Puller
433 The @code{Puller()} action is a special-purpose optimization. When
434 invoked while the crosshair is over the junction of an arc and a line,
435 it will adjust the arc's angle and the connecting line's endpoint such
436 that the line intersects the arc at a tangent. In the example below,
437 the left side is ``before'' with the black target marking where to put
438 the crosshair:
440 @center @image{puller,,,Example of how puller works,png}
442 The right side is ``after'' with the black target marking where the
443 arc-line intersection was moved to.
445 %end-doc */
447 static int
448 Puller (int argc, char **argv, Coord Ux, Coord Uy)
450 double arc_angle, base_angle;
451 #if TRACE1
452 double line_angle, rel_angle;
453 #endif
454 double tangent;
455 int new_delta_angle;
457 if (!find_pair (Crosshair.X, Crosshair.Y))
458 if (!find_pair (Ux, Uy))
459 return 0;
461 if (within (the_line->Point1.X, the_line->Point1.Y,
462 x, y, the_line->Thickness))
464 ex = the_line->Point2.X;
465 ey = the_line->Point2.Y;
466 the_line->Point2.X = the_line->Point1.X;
467 the_line->Point2.Y = the_line->Point1.Y;
468 the_line->Point1.X = ex;
469 the_line->Point1.Y = ey;
471 else if (!within (the_line->Point2.X, the_line->Point2.Y,
472 x, y, the_line->Thickness))
474 #if TRACE1
475 printf ("Line endpoint not at cursor\n");
476 #endif
477 return 1;
479 ex = the_line->Point1.X;
480 ey = the_line->Point1.Y;
482 cx = the_arc->X;
483 cy = the_arc->Y;
484 if (arc_endpoint_is (the_arc, the_arc->StartAngle, x, y))
486 ChangeArcAngles (CURRENT, the_arc, the_arc->StartAngle + the_arc->Delta,
487 -the_arc->Delta);
489 else if (!arc_endpoint_is (the_arc, the_arc->StartAngle + the_arc->Delta,
490 x, y))
492 #if TRACE1
493 printf ("arc not endpoints\n");
494 #endif
495 return 1;
498 if (within (cx, cy, ex, ey, the_arc->Width * 2))
500 #if TRACE1
501 printf ("line ends inside arc\n");
502 #endif
503 return 1;
506 if (the_arc->Delta > 0)
507 arc_angle = the_arc->StartAngle + the_arc->Delta + 90;
508 else
509 arc_angle = the_arc->StartAngle + the_arc->Delta - 90;
510 base_angle = r2d (atan2 (ey - cy, cx - ex));
512 tangent = r2d (acos (the_arc->Width / Distance (cx, cy, ex, ey)));
514 #if TRACE1
515 line_angle = r2d (atan2 (ey - y, x - ex));
516 rel_angle = line_angle - arc_angle;
517 printf ("arc %g line %g rel %g base %g\n", arc_angle, line_angle, rel_angle,
518 base_angle);
519 printf ("tangent %g\n", tangent);
521 printf ("arc was start %ld end %ld\n", the_arc->StartAngle,
522 the_arc->StartAngle + the_arc->Delta);
523 #endif
525 if (the_arc->Delta > 0)
526 arc_angle = base_angle - tangent;
527 else
528 arc_angle = base_angle + tangent;
529 #if TRACE1
530 printf ("new end angle %g\n", arc_angle);
531 #endif
533 new_delta_angle = arc_angle - the_arc->StartAngle;
534 if (new_delta_angle > 180)
535 new_delta_angle -= 360;
536 if (new_delta_angle < -180)
537 new_delta_angle += 360;
538 ChangeArcAngles (CURRENT, the_arc, the_arc->StartAngle, new_delta_angle);
540 #if TRACE1
541 printf ("arc now start %ld end %ld\n", the_arc->StartAngle,
542 the_arc->StartAngle + new_delta_angle);
543 #endif
545 arc_angle = the_arc->StartAngle + the_arc->Delta;
546 x = the_arc->X - the_arc->Width * cos (d2r (arc_angle)) + 0.5;
547 y = the_arc->Y + the_arc->Height * sin (d2r (arc_angle)) + 0.5;
549 MoveObject (LINEPOINT_TYPE, CURRENT, the_line, &(the_line->Point2),
550 x - the_line->Point2.X, y - the_line->Point2.Y);
552 gui->invalidate_all ();
553 IncrementUndoSerialNumber ();
555 return 1;
558 /* Global Puller */
560 static const char globalpuller_syntax[] =
561 "GlobalPuller()";
563 static const char globalpuller_help[] =
564 "Pull all traces tight.";
566 /* %start-doc actions GlobalPuller
568 %end-doc */
570 /* Ok, here's the deal. We look for the intersection of two traces.
571 The triangle formed by those traces is searched for things we need
572 to avoid. From the other two corners of the triangle, we compute
573 the angle to each obstacle, and remember the ones closest to the
574 start angles. If the two traces hit the same obstacle, we put in
575 the arc and we're done. Else, we bring the traces up to the
576 obstacles and start again.
578 Note that we assume each start point is a tangent to an arc. We
579 start with a radius of zero, but future steps use the arcs we
580 create as we go.
582 For obstacles, we list each round pin, pad, via, and line/arc
583 endpoints as points with a given radius. For each square pin, pad,
584 via, and polygon points, we list each corner with a zero radius.
585 We also list arcs from their centerpoint.
587 We don't currently do anything to move vias, or intersections of
588 three or more traces. In the future, three-way intersections will
589 be handles similarly to two-way - calculate the range of angles
590 valid from each of the three other endpoints, choose the angle
591 closest to making 120 degree angles at the center. For four-way or
592 more intersections, we break them up into multiple three-way
593 intersections.
595 For simplicity, we only do the current layer at this time. We will
596 also edit the lines and arcs in place so that the intersection is
597 always on the second point, and the other ends are always at
598 start+delta for arcs.
600 We also defer intersections which are blocked by other
601 intersections yet to be moved; the idea is to wait until those have
602 been moved so we don't end up with arcs that no longer wrap around
603 things. At a later point, we may choose to pull arced corners in
604 also.
606 You'll see lots of variables of the form "foo_sign" which keep
607 track of which way things are pointing. This is because everything
608 is relative to corners and arcs, not absolute directions.
611 static int nloops, npulled;
613 static void
614 status ()
616 Message ("%6d loops, %d pulled \r", nloops, npulled);
620 * \brief Extra data we need to temporarily attach to all lines and
621 * arcs.
623 typedef struct End {
624 /* These point to "multi_next" if there are more than one. */
625 struct Extra *next;
626 void *pin;
627 unsigned char in_pin:1;
628 unsigned char at_pin:1;
629 unsigned char is_pad:1;
630 unsigned char pending:1; /* set if this may be moved later */
631 Coord x, y; /* arc endpoint */
632 /* If not NULL, points to End with pending==1 we're blocked on. */
633 struct End *waiting_for;
634 } End;
636 typedef struct Extra {
637 End start;
638 End end;
639 unsigned char found:1;
640 unsigned char deleted:1;
641 int type;
642 union {
643 LineType *line;
644 ArcType *arc;
645 } parent;
646 } Extra;
648 static Extra multi_next;
649 static GHashTable *lines;
650 static GHashTable *arcs;
651 static int did_something;
652 static int current_is_top, current_is_bottom;
654 /* If set, these are the pins/pads/vias that this path ends on. */
655 /* static void *start_pin_pad, *end_pin_pad; */
657 #if TRACE1
658 static void trace_paths ();
659 #endif
660 static void mark_line_for_deletion (LineType *);
662 #define LINE2EXTRA(l) ((Extra *)g_hash_table_lookup (lines, l))
663 #define ARC2EXTRA(a) ((Extra *)g_hash_table_lookup (arcs, a))
664 #define EXTRA2LINE(e) (e->parent.line)
665 #define EXTRA2ARC(e) (e->parent.arc)
666 #define EXTRA_IS_LINE(e) (e->type == LINE_TYPE)
667 #define EXTRA_IS_ARC(e) (e->type == ARC_TYPE)
669 static void
670 unlink_end (Extra *x, Extra **e)
672 if (*e)
674 if ((*e)->start.next == x)
676 #if TRACE1
677 printf("%d: unlink_end, was %p\n", __LINE__, (*e)->start.next);
678 #endif
679 (*e)->start.next = &multi_next;
681 if ((*e)->end.next == x)
683 #if TRACE1
684 printf("%d: unlink_end, was %p\n", __LINE__, (*e)->start.next);
685 #endif
686 (*e)->end.next = &multi_next;
689 #if TRACE1
690 printf("%d: unlink_end, was %p\n", __LINE__, (*e));
691 #endif
692 (*e) = &multi_next;
695 #if TRACE1
697 static void
698 clear_found_cb (AnyObjectType *ptr, Extra *extra, void *userdata)
700 extra->found = 0;
703 static void
704 clear_found ()
706 g_hash_table_foreach (lines, (GHFunc)clear_found_cb, NULL);
707 g_hash_table_foreach (arcs, (GHFunc)clear_found_cb, NULL);
709 #endif
711 static void
712 fix_arc_extra (ArcType *a, Extra *e)
714 #if TRACE1
715 printf("new arc angles %ld %ld\n", a->StartAngle, a->Delta);
716 #endif
717 e->start.x = a->X - (a->Width * cos (d2r (a->StartAngle)) + 0.5);
718 e->start.y = a->Y + (a->Height * sin (d2r (a->StartAngle)) + 0.5);
719 e->end.x = a->X - (a->Width * cos (d2r (a->StartAngle+a->Delta)) + 0.5);
720 e->end.y = a->Y + (a->Height * sin (d2r (a->StartAngle+a->Delta)) + 0.5);
721 #if TRACE1
722 pcb_printf("new X,Y is %#mD to %#mD\n", e->start.x, e->start.y, e->end.x, e->end.y);
723 #endif
726 typedef struct {
727 void *me;
728 Coord x, y;
729 int is_arc;
730 Extra **extra_ptr;
731 } FindPairCallbackStruct;
733 #define NEAR(a,b) ((a) <= (b) + 2 && (a) >= (b) - 2)
735 static int
736 find_pair_line_callback (const BoxType * b, void *cl)
738 LineType *line = (LineType *) b;
739 #if TRACE1
740 Extra *e = LINE2EXTRA (line);
741 #endif
742 FindPairCallbackStruct *fpcs = (FindPairCallbackStruct *) cl;
744 if (line == fpcs->me)
745 return 0;
746 #ifdef CHECK_LINE_PT_NEG
747 if (line->Point1.X < 0)
748 abort1();
749 #endif
750 #if TRACE1
751 pcb_printf(" - %p line %#mD or %#mD\n", e, line->Point1.X, line->Point1.Y,
752 line->Point2.X, line->Point2.Y);
753 #endif
754 if ((NEAR (line->Point1.X, fpcs->x) && NEAR (line->Point1.Y, fpcs->y))
755 || (NEAR (line->Point2.X, fpcs->x) && NEAR (line->Point2.Y, fpcs->y)))
757 if (* fpcs->extra_ptr)
759 #if TRACE1
760 printf("multiple, was %p\n", *fpcs->extra_ptr);
761 #endif
762 *fpcs->extra_ptr = & multi_next;
764 else
766 *fpcs->extra_ptr = LINE2EXTRA (line);
767 #if TRACE1
768 printf(" - next now %p\n", *fpcs->extra_ptr);
769 #endif
772 return 0;
775 static int
776 find_pair_arc_callback (const BoxType * b, void *cl)
778 ArcType *arc = (ArcType *) b;
779 Extra *e = ARC2EXTRA (arc);
780 FindPairCallbackStruct *fpcs = (FindPairCallbackStruct *) cl;
782 if (arc == fpcs->me)
783 return 0;
784 #if TRACE1
785 pcb_printf(" - %p arc %#mD or %#mD\n", e, e->start.x, e->start.y, e->end.x, e->end.y);
786 #endif
787 if ((NEAR (e->start.x, fpcs->x) && NEAR (e->start.y, fpcs->y))
788 || (NEAR (e->end.x, fpcs->x) && NEAR (e->end.y, fpcs->y)))
790 if (* fpcs->extra_ptr)
792 #if TRACE1
793 printf("multiple, was %p\n", *fpcs->extra_ptr);
794 #endif
795 *fpcs->extra_ptr = & multi_next;
797 else
798 *fpcs->extra_ptr = e;
800 return 0;
803 static void
804 find_pairs_1 (void *me, Extra **e, Coord x, Coord y)
806 FindPairCallbackStruct fpcs;
807 BoxType b;
809 if (*e)
810 return;
812 fpcs.me = me;
813 fpcs.extra_ptr = e;
814 fpcs.x = x;
815 fpcs.y = y;
816 #if TRACE1
817 pcb_printf("looking for %#mD\n", x, y);
818 #endif
819 b.X1 = x - 10;
820 b.X2 = x + 10;
821 b.Y1 = y - 10;
822 b.Y2 = y + 10;
823 r_search(CURRENT->line_tree, &b, NULL, find_pair_line_callback, &fpcs);
824 r_search(CURRENT->arc_tree, &b, NULL, find_pair_arc_callback, &fpcs);
827 static int
828 check_point_in_pin (PinType *pin, Coord x, Coord y, End *e)
830 int inside_p;
831 Coord t = (PIN_SIZE(pin)+1)/2;
832 if (TEST_FLAG (SQUAREFLAG, pin))
833 inside_p = (x >= pin->X - t && x <= pin->X + t
834 && y >= pin->Y - t && y <= pin->Y + t);
835 else
836 inside_p = (Distance (pin->X, pin->Y, x, y) <= t);
838 if (inside_p)
840 e->in_pin = 1;
841 if (pin->X == x && pin->Y == y)
842 e->at_pin = 1;
843 e->pin = pin;
844 return 1;
846 return 0;
849 static int
850 find_pair_pinline_callback (const BoxType * b, void *cl)
852 LineType *line = (LineType *) b;
853 PinType *pin = (PinType *) cl;
854 Extra *e = LINE2EXTRA (line);
855 int hits;
857 #ifdef CHECK_LINE_PT_NEG
858 if (line->Point1.X < 0)
859 abort1();
860 #endif
862 hits = check_point_in_pin (pin, line->Point1.X, line->Point1.Y, &(e->start));
863 hits += check_point_in_pin (pin, line->Point2.X, line->Point2.Y, &(e->end));
865 if (hits)
866 return 0;
868 /* See if the line passes through this pin. */
869 /*! \todo This assumes round pads, but it's good enough for square
870 * ones for now. */
871 if (dist_lsp (line->Point1.X, line->Point1.Y,
872 line->Point2.X, line->Point2.Y,
873 pin->X, pin->Y) <= PIN_SIZE(pin)/2)
875 #if TRACE1
876 pcb_printf("splitting line %#mD-%#mD because it passes through pin %#mD r%d\n",
877 line->Point1.X, line->Point1.Y,
878 line->Point2.X, line->Point2.Y,
879 pin->X, pin->Y, PIN_SIZE(pin)/2);
880 #endif
881 unlink_end (e, &e->start.next);
882 unlink_end (e, &e->end.next);
884 return 0;
887 static int
888 find_pair_pinarc_callback (const BoxType * b, void *cl)
890 ArcType *arc = (ArcType *) b;
891 PinType *pin = (PinType *) cl;
892 Extra *e = ARC2EXTRA (arc);
893 int hits;
895 hits = check_point_in_pin (pin, e->start.x, e->start.y, &(e->start));
896 hits += check_point_in_pin (pin, e->end.x, e->end.y, &(e->end));
897 return 0;
900 static int
901 check_point_in_pad (PadType *pad, Coord x, Coord y, End *e)
903 int inside_p;
904 Coord t;
906 pcb_printf("pad %#mD - %#mD t %#mS vs %#mD\n", pad->Point1.X, pad->Point1.Y,
907 pad->Point2.X, pad->Point2.Y, pad->Thickness, x, y);
908 t = (pad->Thickness+1)/2;
909 if (TEST_FLAG (SQUAREFLAG, pad))
911 inside_p = (x >= MIN (pad->Point1.X - t, pad->Point2.X - t)
912 && x <= MAX (pad->Point1.X + t, pad->Point2.X + t)
913 && y >= MIN (pad->Point1.Y - t, pad->Point2.Y - t)
914 && y <= MAX (pad->Point1.Y + t, pad->Point2.Y + t));
915 printf(" - inside_p = %d\n", inside_p);
917 else
919 if (pad->Point1.X == pad->Point2.X)
921 inside_p = (x >= pad->Point1.X - t
922 && x <= pad->Point1.X + t
923 && y >= MIN (pad->Point1.Y, pad->Point2.Y)
924 && y <= MAX (pad->Point1.Y, pad->Point2.Y));
926 else
928 inside_p = (x >= MIN (pad->Point1.X, pad->Point2.X)
929 && x <= MAX (pad->Point1.X, pad->Point2.X)
930 && y >= pad->Point1.Y - t
931 && y <= pad->Point1.Y + t);
933 if (!inside_p)
935 if (Distance (pad->Point1.X, pad->Point1.Y, x, y) <= t
936 || Distance (pad->Point2.X, pad->Point2.Y, x, y) <= t)
937 inside_p = 1;
941 if (inside_p)
943 e->in_pin = 1;
944 if (pad->Point1.X == x && pad->Point1.Y == y)
945 e->at_pin = 1;
946 if (pad->Point2.X == x && pad->Point2.Y == y)
947 e->at_pin = 1;
948 e->pin = pad;
949 e->is_pad = 1;
950 return 1;
952 return 0;
955 static int
956 find_pair_padline_callback (const BoxType * b, void *cl)
958 LineType *line = (LineType *) b;
959 PadType *pad = (PadType *) cl;
960 Extra *e = LINE2EXTRA (line);
961 int hits;
962 double t;
963 int intersect;
964 double p1_d, p2_d;
966 if (TEST_FLAG (ONSOLDERFLAG, pad))
968 if (!current_is_bottom)
969 return 0;
971 else
973 if (!current_is_top)
974 return 0;
977 #ifdef CHECK_LINE_PT_NEG
978 if (line->Point1.X < 0)
979 abort1();
980 #endif
982 hits = check_point_in_pad (pad, line->Point1.X, line->Point1.Y, &(e->start));
983 hits += check_point_in_pad (pad, line->Point2.X, line->Point2.Y, &(e->end));
985 if (hits)
986 return 0;
988 /* Ok, something strange. The line intersects our space, but
989 doesn't end in our space. See if it just passes through us, and
990 mark it anyway. */
992 t = (pad->Thickness + 1)/2;
993 /*! \todo This is for round pads. Good enough for now, but add
994 * square pad support later. */
995 intersect = intersection_of_linesegs (pad->Point1.X, pad->Point1.Y,
996 pad->Point2.X, pad->Point2.Y,
997 line->Point1.X, line->Point1.Y,
998 line->Point2.X, line->Point2.Y,
999 NULL, NULL);
1000 p1_d = dist_lsp(line->Point1.X, line->Point1.Y,
1001 line->Point2.X, line->Point2.Y,
1002 pad->Point1.X, pad->Point1.Y);
1003 p2_d = dist_lsp(line->Point1.X, line->Point1.Y,
1004 line->Point2.X, line->Point2.Y,
1005 pad->Point2.X, pad->Point2.Y);
1007 if (intersect || p1_d < t || p2_d < t)
1009 /* It does. */
1010 /*! \todo We should split the line. */
1011 #if TRACE1
1012 pcb_printf("splitting line %#mD-%#mD because it passes through pad %#mD-%#mD r %#mS\n",
1013 line->Point1.X, line->Point1.Y,
1014 line->Point2.X, line->Point2.Y,
1015 pad->Point1.X, pad->Point1.Y,
1016 pad->Point2.X, pad->Point2.Y,
1017 pad->Thickness/2);
1018 #endif
1019 unlink_end (e, &e->start.next);
1020 unlink_end (e, &e->end.next);
1023 return 0;
1026 static int
1027 find_pair_padarc_callback (const BoxType * b, void *cl)
1029 ArcType *arc = (ArcType *) b;
1030 PadType *pad = (PadType *) cl;
1031 Extra *e = ARC2EXTRA (arc);
1032 int hits;
1034 if (TEST_FLAG (ONSOLDERFLAG, pad))
1036 if (!current_is_bottom)
1037 return 0;
1039 else
1041 if (!current_is_top)
1042 return 0;
1045 hits = check_point_in_pad (pad, e->start.x, e->start.y, &(e->start));
1046 hits += check_point_in_pad (pad, e->end.x, e->end.y, &(e->end));
1047 return 0;
1050 static void
1051 null_multi_next_ends (AnyObjectType *ptr, Extra *extra, void *userdata)
1053 if (extra->start.next == &multi_next)
1054 extra->start.next = NULL;
1056 if (extra->end.next == &multi_next)
1057 extra->end.next = NULL;
1060 static Extra *
1061 new_line_extra (LineType *line)
1063 Extra *extra = g_slice_new0 (Extra);
1064 g_hash_table_insert (lines, line, extra);
1065 extra->parent.line = line;
1066 extra->type = LINE_TYPE;
1067 return extra;
1070 static Extra *
1071 new_arc_extra (ArcType *arc)
1073 Extra *extra = g_slice_new0 (Extra);
1074 g_hash_table_insert (arcs, arc, extra);
1075 extra->parent.arc = arc;
1076 extra->type = ARC_TYPE;
1077 return extra;
1080 static void
1081 find_pairs ()
1083 ARC_LOOP (CURRENT); {
1084 Extra *e = new_arc_extra (arc);
1085 fix_arc_extra (arc, e);
1086 } END_LOOP;
1088 LINE_LOOP (CURRENT); {
1089 new_line_extra (line);
1090 } END_LOOP;
1092 LINE_LOOP (CURRENT); {
1093 Extra *e = LINE2EXTRA (line);
1094 if (line->Point1.X >= 0)
1096 find_pairs_1 (line, & e->start.next, line->Point1.X, line->Point1.Y);
1097 find_pairs_1 (line, & e->end.next, line->Point2.X, line->Point2.Y);
1099 } END_LOOP;
1101 ARC_LOOP (CURRENT); {
1102 Extra *e = ARC2EXTRA (arc);
1103 if (!e->deleted)
1105 find_pairs_1 (arc, & e->start.next, e->start.x, e->start.y);
1106 find_pairs_1 (arc, & e->end.next, e->end.x, e->end.y);
1108 } END_LOOP;
1110 ALLPIN_LOOP (PCB->Data); {
1111 BoxType box;
1112 box.X1 = pin->X - PIN_SIZE(pin)/2;
1113 box.Y1 = pin->Y - PIN_SIZE(pin)/2;
1114 box.X2 = pin->X + PIN_SIZE(pin)/2;
1115 box.Y2 = pin->Y + PIN_SIZE(pin)/2;
1116 r_search (CURRENT->line_tree, &box, NULL, find_pair_pinline_callback, pin);
1117 r_search (CURRENT->arc_tree, &box, NULL, find_pair_pinarc_callback, pin);
1118 } ENDALL_LOOP;
1120 VIA_LOOP (PCB->Data); {
1121 BoxType box;
1122 box.X1 = via->X - PIN_SIZE(via)/2;
1123 box.Y1 = via->Y - PIN_SIZE(via)/2;
1124 box.X2 = via->X + PIN_SIZE(via)/2;
1125 box.Y2 = via->Y + PIN_SIZE(via)/2;
1126 r_search (CURRENT->line_tree, &box, NULL, find_pair_pinline_callback, via);
1127 r_search (CURRENT->arc_tree, &box, NULL, find_pair_pinarc_callback, via);
1128 } END_LOOP;
1130 ALLPAD_LOOP (PCB->Data); {
1131 BoxType box;
1132 box.X1 = MIN(pad->Point1.X, pad->Point2.X) - pad->Thickness/2;
1133 box.Y1 = MIN(pad->Point1.Y, pad->Point2.Y) - pad->Thickness/2;
1134 box.X2 = MAX(pad->Point1.X, pad->Point2.X) + pad->Thickness/2;
1135 box.Y2 = MAX(pad->Point1.Y, pad->Point2.Y) + pad->Thickness/2;
1136 r_search (CURRENT->line_tree, &box, NULL, find_pair_padline_callback, pad);
1137 r_search (CURRENT->arc_tree, &box, NULL, find_pair_padarc_callback, pad);
1139 } ENDALL_LOOP;
1141 g_hash_table_foreach (lines, (GHFunc)null_multi_next_ends, NULL);
1142 g_hash_table_foreach (arcs, (GHFunc)null_multi_next_ends, NULL);
1145 #define PROP_NEXT(e,n,f) \
1146 if (f->next->start.next == e) { \
1147 e = f->next; \
1148 n = & e->start; \
1149 f = & e->end; \
1150 } else { \
1151 e = f->next; \
1152 n = & e->end; \
1153 f = & e->start; }
1155 static void
1156 propogate_ends_at (Extra *e, End *near, End *far)
1158 while (far->in_pin && far->pin == near->pin)
1160 far->in_pin = 0;
1161 if (!far->next)
1162 return;
1163 PROP_NEXT (e, near, far);
1164 near->in_pin = 0;
1168 static void
1169 propogate_end_pin (Extra *e, End *near, End *far)
1171 void *pinpad = near->pin;
1172 int ispad = near->is_pad;
1173 while (far->next)
1175 PROP_NEXT (e, near, far);
1176 if (near->pin == pinpad)
1177 break;
1178 near->pin = pinpad;
1179 near->is_pad = ispad;
1183 static void
1184 propogate_end_step1_cb (AnyObjectType *ptr, Extra *extra, void *userdata)
1186 if (extra->start.next != NULL &&
1187 extra->start.next == extra->end.next)
1189 extra->end.next = NULL;
1190 mark_line_for_deletion ((LineType *)ptr);
1193 if (extra->start.at_pin)
1194 propogate_ends_at (extra, &extra->start, &extra->end);
1196 if (extra->end.at_pin)
1197 propogate_ends_at (extra, &extra->end, &extra->start);
1200 static void
1201 propogate_end_step2_cb (AnyObjectType *ptr, Extra *extra, void *userdata)
1203 if (extra->start.in_pin)
1205 #if TRACE1
1206 printf("MULTI at %d: was %p\n", __LINE__, extra->start.next);
1207 #endif
1208 extra->start.next = NULL;
1210 if (extra->end.in_pin)
1212 #if TRACE1
1213 printf("MULTI at %d: was %p\n", __LINE__, extra->end.next);
1214 #endif
1215 extra->end.next = NULL;
1219 static void
1220 propogate_end_step3_cb (AnyObjectType *ptr, Extra *extra, void *userdata)
1222 if (extra->start.next)
1223 propogate_end_pin (extra, &extra->end, &extra->start);
1224 if (extra->end.next)
1225 propogate_end_pin (extra, &extra->start, &extra->end);
1228 static void
1229 propogate_ends ()
1231 /* First, shut of "in pin" when we have an "at pin". We also clean
1232 up zero-length lines. */
1233 g_hash_table_foreach (lines, (GHFunc)propogate_end_step1_cb, NULL);
1235 /* Now end all paths at pins/pads. */
1236 g_hash_table_foreach (lines, (GHFunc)propogate_end_step2_cb, NULL);
1238 /* Now, propogate the pin/pad/vias along paths. */
1239 g_hash_table_foreach (lines, (GHFunc)propogate_end_step3_cb, NULL);
1242 static Extra *last_pextra = 0;
1244 static void
1245 print_extra (Extra *e, Extra *prev)
1247 int which = 0;
1248 if (e->start.next == last_pextra)
1249 which = 1;
1250 else if (e->end.next == last_pextra)
1251 which = 2;
1252 switch (which) {
1253 case 0:
1254 printf("%10p %10p %10p :", e, e->start.next, e->end.next);
1255 break;
1256 case 1:
1257 printf("%10p \033[33m%10p\033[0m %10p :", e, e->start.next, e->end.next);
1258 break;
1259 case 2:
1260 printf("%10p %10p \033[33m%10p\033[0m :", e, e->start.next, e->end.next);
1261 break;
1263 last_pextra = e;
1264 printf(" %c%c",
1265 e->deleted ? 'd' : '-',
1266 e->found ? 'f' : '-');
1267 printf(" s:%s%s%s%s",
1268 e->start.in_pin ? "I" : "-",
1269 e->start.at_pin ? "A" : "-",
1270 e->start.is_pad ? "P" : "-",
1271 e->start.pending ? "p" : "-");
1272 printf(" e:%s%s%s%s ",
1273 e->end.in_pin ? "I" : "-",
1274 e->end.at_pin ? "A" : "-",
1275 e->end.is_pad ? "P" : "-",
1276 e->end.pending ? "p" : "-");
1278 if (EXTRA_IS_LINE (e))
1280 LineType *line = EXTRA2LINE (e);
1281 pcb_printf(" %p L %#mD-%#mD", line, line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y);
1282 printf(" %s %p %s %p\n",
1283 e->start.is_pad ? "pad" : "pin", e->start.pin,
1284 e->end.is_pad ? "pad" : "pin", e->end.pin);
1286 else if (EXTRA_IS_ARC (e))
1288 ArcType *arc = EXTRA2ARC (e);
1289 pcb_printf(" %p A %#mD-%#mD", arc, e->start.x, e->start.y, e->end.x, e->end.y);
1290 pcb_printf(" at %#mD ang %ld,%ld\n", arc->X, arc->Y, arc->StartAngle, arc->Delta);
1292 else if (e == &multi_next)
1294 printf("-- Multi-next\n");
1296 else
1298 printf("-- Unknown extra: %p\n", e);
1302 #if TRACE1
1303 static void
1304 trace_path (Extra *e)
1306 Extra *prev = 0;
1307 if ((e->start.next && e->end.next)
1308 || (!e->start.next && !e->end.next))
1309 return;
1310 if (e->found)
1311 return;
1312 printf("- path -\n");
1313 last_pextra = 0;
1314 while (e)
1316 e->found = 1;
1317 print_extra (e, prev);
1318 if (e->start.next == prev)
1320 prev = e;
1321 e = e->end.next;
1323 else
1325 prev = e;
1326 e = e->start.next;
1331 static void
1332 trace_paths ()
1334 Extra *e;
1336 clear_found ();
1337 LINE_LOOP (CURRENT); {
1338 e = LINE2EXTRA (line);
1339 trace_path (e);
1340 } END_LOOP;
1341 ARC_LOOP (CURRENT); {
1342 e = ARC2EXTRA (arc);
1343 trace_path (e);
1344 } END_LOOP;
1346 #endif
1348 static void
1349 reverse_line (LineType *line)
1351 Extra *e = LINE2EXTRA (line);
1352 Coord x, y;
1353 End etmp;
1355 x = line->Point1.X;
1356 y = line->Point1.Y;
1357 #if 1
1358 MoveObject (LINEPOINT_TYPE, CURRENT, line, &(line->Point1),
1359 line->Point2.X - line->Point1.X,
1360 line->Point2.Y - line->Point1.Y);
1361 MoveObject (LINEPOINT_TYPE, CURRENT, line, &(line->Point2),
1362 x - line->Point2.X,
1363 y - line->Point2.Y);
1364 #else
1365 /* In theory, we should be using the above so that undo works. */
1366 line->Point1.X = line->Point2.X;
1367 line->Point1.Y = line->Point2.Y;
1368 line->Point2.X = x;
1369 line->Point2.Y = y;
1370 #endif
1371 memcpy (&etmp, &e->start, sizeof (End));
1372 memcpy (&e->start, &e->end, sizeof (End));
1373 memcpy (&e->end, &etmp, sizeof (End));
1376 static void
1377 reverse_arc (ArcType *arc)
1379 Extra *e = ARC2EXTRA (arc);
1380 End etmp;
1382 #if 1
1383 ChangeArcAngles (CURRENT, arc,
1384 arc->StartAngle + arc->Delta, -arc->Delta);
1385 #else
1386 /* Likewise, see above. */
1387 arc->StartAngle += arc->Delta;
1388 arc->Delta *= -1;
1389 #endif
1390 memcpy (&etmp, &e->start, sizeof (End));
1391 memcpy (&e->start, &e->end, sizeof (End));
1392 memcpy (&e->end, &etmp, sizeof (End));
1395 static void
1396 expand_box (BoxType *b, Coord x, Coord y, Coord t)
1398 b->X1 = MIN (b->X1, x-t);
1399 b->X2 = MAX (b->X2, x+t);
1400 b->Y1 = MIN (b->Y1, y-t);
1401 b->Y2 = MAX (b->Y2, y+t);
1404 /* ---------------------------------------------------------------------- */
1405 /* These are the state variables for the intersection we're currently
1406 working on. */
1408 /* what we're working with */
1409 static ArcType *start_arc;
1410 static LineType *start_line;
1411 static LineType *end_line;
1412 static ArcType *end_arc;
1413 static Extra *start_extra, *end_extra;
1414 static Extra *sarc_extra, *earc_extra;
1415 static void *start_pinpad, *end_pinpad;
1416 static Coord thickness;
1418 /* Pre-computed values. Note that all values are computed according
1419 to CARTESIAN coordinates, not PCB coordinates. Do an up-down board
1420 flip before wrapping your brain around the math. */
1422 /* se_sign is positive when you make a right turn going from start to end. */
1423 /* sa_sign is positive when start's arc is on the same side of start as end. */
1424 /* ea_sign is positive when end's arc is on the same side of end as start. */
1425 /* sa_sign and ea_sign may be zero if there's no arc. */
1426 static double se_sign, sa_sign, ea_sign;
1428 static double best_angle, start_angle, end_dist;
1429 /* arc radii are positive when they're on the same side as the things
1430 we're interested in. */
1431 static Coord sa_r, ea_r;
1432 static Coord sa_x, sa_y; /* start "arc" point */
1434 /* what we've found so far */
1435 static Coord fx, fy, fr;
1436 static int fp;
1437 static End *fp_end;
1438 static double fa; /* relative angle */
1440 #define gp_point(x,y,t,e) gp_point_2(x,y,t,e,0,0,__FUNCTION__)
1442 static int
1443 gp_point_force (Coord x, Coord y, Coord t, End *e, int esa, int eda, int force, const char *name)
1445 double r, a, d;
1446 Coord scx, scy, sr;
1447 double base_angle, rel_angle, point_angle;
1449 #if TRACE1
1450 pcb_printf("\033[34mgp_point_force %#mD %#mS via %s\033[0m\n", x, y, t, name);
1451 #endif
1453 if (start_arc)
1455 scx = start_arc->X;
1456 scy = start_arc->Y;
1457 sr = start_arc->Width;
1459 else
1461 scx = start_line->Point1.X;
1462 scy = start_line->Point1.Y;
1463 sr = 0;
1466 r = t + thickness;
1468 /* See if the point is inside our start arc. */
1469 d = Distance (scx, scy, x, y);
1470 #if TRACE1
1471 pcb_printf("%f = dist #mD to %#mD\n", d, scx, scy, x, y);
1472 pcb_printf("sr %#mS r %f d %f\n", sr, r, d);
1473 #endif
1474 if (d < sr - r)
1476 #if TRACE1
1477 printf("inside start arc, %f < %f\n", d, sr-r);
1478 #endif
1479 return 0;
1481 if (sr == 0 && d < r)
1483 #if TRACE1
1484 printf("start is inside arc, %f < %f\n", d, r);
1485 #endif
1486 return 0;
1489 /* Now for the same tricky math we needed for the single puller.
1490 sr and r are the radii for the two points scx,scy and x,y. */
1492 /* angle between points (NOT pcb arc angles) */
1493 base_angle = atan2 (y - scy, x - scx);
1494 #if TRACE1
1495 pcb_printf("%.1f = atan2 (%#mS-%#mS = %#mS, %#mS-%#mS = %#mS)\n",
1496 r2d(base_angle), y, scy, y-scy, x, scx, x-scx);
1497 #endif
1499 if ((sa_sign * sr - r) / d > 1
1500 || (sa_sign * sr - r) / d < -1)
1501 return 0;
1503 /* Angle of tangent, relative to the angle between point centers. */
1504 rel_angle = se_sign * asin ((sa_sign * sr - r) / d);
1505 #if TRACE1
1506 printf("%.1f = %d * asin ((%d * %d - %f) / %f)\n",
1507 r2d(rel_angle), (int)se_sign, (int)sa_sign, sr, r, d);
1508 #endif
1510 /* Absolute angle of tangent. */
1511 point_angle = base_angle + rel_angle;
1512 #if TRACE1
1513 printf("base angle %.1f rel_angle %.1f point_angle %.1f\n",
1514 r2d(base_angle),
1515 r2d(rel_angle),
1516 r2d(point_angle));
1517 #endif
1519 if (eda)
1521 /* Check arc angles */
1522 double pa = point_angle;
1523 double sa = d2r(180-esa);
1524 double da = d2r(-eda);
1526 if (da < 0)
1528 sa = sa + da;
1529 da = -da;
1532 pa -= se_sign * M_PI/2;
1533 while (sa+da < pa)
1534 sa += M_PI*2;
1535 while (sa > pa)
1536 sa -= M_PI*2;
1537 if (sa+da < pa)
1539 #if TRACE1
1540 printf("arc doesn't apply: sa %.1f da %.1f pa %.1f\n",
1541 r2d(sa), r2d(da), r2d(pa));
1542 #endif
1543 return 0;
1547 a = point_angle - start_angle;
1548 while (a > M_PI)
1549 a -= M_PI*2;
1550 while (a < -M_PI)
1551 a += M_PI*2;
1552 #if TRACE1
1553 printf(" - angle relative to S-E baseline is %.1f\n", r2d(a));
1554 #endif
1556 if (!force && a * se_sign < -0.007)
1558 double new_r;
1559 #if TRACE1
1560 printf("skipping, would increase angle (%f * %f)\n", a, se_sign);
1561 #endif
1562 new_r = dist_lp (start_line->Point1.X, start_line->Point1.Y,
1563 start_line->Point2.X, start_line->Point2.Y,
1564 x, y);
1565 #if TRACE1
1566 pcb_printf("point %#mD dist %#mS vs thickness %#mS\n", x, y, new_r, thickness);
1567 #endif
1568 new_r -= thickness;
1569 new_r = (int)new_r - 1;
1570 #if TRACE1
1571 pcb_printf(" - new thickness %f old %#mS\n", new_r, t);
1572 #endif
1573 if (new_r < t)
1574 gp_point_force (x, y, new_r, e, esa, eda, 1, __FUNCTION__);
1575 return 0;
1578 #if TRACE1
1579 printf("%f * %f < %f * %f ?\n", a, se_sign, best_angle, se_sign);
1580 #endif
1581 if (a * se_sign == best_angle * se_sign)
1583 double old_d = Distance (start_line->Point1.X, start_line->Point1.Y,
1584 fx, fy);
1585 double new_d = Distance (start_line->Point1.X, start_line->Point1.Y,
1586 x, y);
1587 if (new_d > old_d)
1589 best_angle = a;
1590 fx = x;
1591 fy = y;
1592 fr = r;
1593 fa = a;
1594 fp = e ? e->pending : 0;
1595 fp_end = e;
1598 else if (a * se_sign < best_angle * se_sign)
1600 best_angle = a;
1601 fx = x;
1602 fy = y;
1603 fr = r;
1604 fa = a;
1605 fp = e ? e->pending : 0;
1606 fp_end = e;
1609 return 1;
1611 static int
1612 gp_point_2 (Coord x, Coord y, Coord t, End *e, int esa, int eda, const char *func)
1614 double sc, ec;
1615 double sd, ed;
1617 if (x == sa_x && y ==sa_y)
1618 return 0;
1620 #if TRACE1
1621 pcb_printf("\033[34mgp_point %#mD %#mS via %s\033[0m\n", x, y, t, func);
1622 #endif
1624 /* There are two regions we care about. For points inside our
1625 triangle, we check the crosses against start_line and end_line to
1626 make sure the point is "inside" the triangle. For points on the
1627 other side of the s-e line of the triangle, we check the dots to
1628 make sure it's between our endpoints. */
1630 /* See what side of the s-e line we're on */
1631 sc = cross2d (start_line->Point1.X, start_line->Point1.Y,
1632 end_line->Point2.X, end_line->Point2.Y,
1633 x, y);
1634 #if TRACE1
1635 printf("s-e cross = %f\n", sc);
1636 #endif
1637 if (t >= 0)
1639 if (same_sign (sc, se_sign))
1641 /* Outside, check dots. */
1643 /* Ok, is it "in front of" our vectors? */
1644 sd = dot2d (start_line->Point1.X, start_line->Point1.Y,
1645 end_line->Point2.X, end_line->Point2.Y,
1646 x, y);
1647 #if TRACE1
1648 printf("sd = %f\n", sd);
1649 #endif
1650 if (sd <= 0)
1651 return 0;
1653 ed = dot2d (end_line->Point2.X, end_line->Point2.Y,
1654 start_line->Point1.X, start_line->Point1.Y,
1655 x, y);
1656 #if TRACE1
1657 printf("ed = %f\n", ed);
1658 #endif
1659 if (ed <= 0)
1660 return 0;
1662 sd = dist_lp (start_line->Point1.X, start_line->Point1.Y,
1663 end_line->Point2.X, end_line->Point2.Y,
1664 x, y);
1665 if (sd > t + thickness)
1666 return 0;
1668 else
1670 /* Inside, check crosses. */
1672 /* First off, is it on the correct side of the start line? */
1673 sc = cross2d (start_line->Point1.X, start_line->Point1.Y,
1674 start_line->Point2.X, start_line->Point2.Y,
1675 x, y);
1676 #if TRACE1
1677 printf("sc = %f\n", sc);
1678 #endif
1679 if (! same_sign (sc, se_sign))
1680 return 0;
1682 /* Ok, is it on the correct side of the end line? */
1683 ec = cross2d (end_line->Point1.X, end_line->Point1.Y,
1684 end_line->Point2.X, end_line->Point2.Y,
1685 x, y);
1686 #if TRACE1
1687 printf("ec = %f\n", ec);
1688 #endif
1689 if (! same_sign (ec, se_sign))
1690 return 0;
1694 #if TRACE1
1695 printf("in range!\n");
1696 #endif
1698 return gp_point_force (x, y, t, e, esa, eda, 0, func);
1701 static int
1702 gp_line_cb (const BoxType *b, void *cb)
1704 const LineType *l = (LineType *) b;
1705 Extra *e = LINE2EXTRA(l);
1706 if (l == start_line || l == end_line)
1707 return 0;
1708 if (e->deleted)
1709 return 0;
1710 #ifdef CHECK_LINE_PT_NEG
1711 if (l->Point1.X < 0)
1712 abort1();
1713 #endif
1714 if (! e->start.next
1715 || ! EXTRA_IS_ARC (e->start.next))
1716 gp_point (l->Point1.X, l->Point1.Y, l->Thickness/2, &e->start);
1717 if (! e->end.next
1718 || ! EXTRA_IS_ARC (e->end.next))
1719 gp_point (l->Point2.X, l->Point2.Y, l->Thickness/2, &e->end);
1720 return 0;
1723 static int
1724 gp_arc_cb (const BoxType *b, void *cb)
1726 const ArcType *a = (ArcType *) b;
1727 Extra *e = ARC2EXTRA(a);
1728 if (a == start_arc || a == end_arc)
1729 return 0;
1730 if (e->deleted)
1731 return 0;
1732 gp_point_2 (a->X, a->Y, a->Width + a->Thickness/2, 0, a->StartAngle, a->Delta, __FUNCTION__);
1733 if (start_arc
1734 && a->X == start_arc->X
1735 && a->Y == start_arc->Y)
1736 return 0;
1737 if (end_arc
1738 && a->X != end_arc->X
1739 && a->Y != end_arc->Y)
1740 return 0;
1742 if (e->start.next || e->end.next)
1743 return 0;
1745 gp_point (e->start.x, e->start.y, a->Thickness/2, 0);
1746 gp_point (e->end.x, e->end.y, a->Thickness/2, 0);
1747 return 0;
1750 static int
1751 gp_text_cb (const BoxType *b, void *cb)
1753 const TextType *t = (TextType *) b;
1754 /* FIXME: drop in the actual text-line endpoints later. */
1755 gp_point (t->BoundingBox.X1, t->BoundingBox.Y1, 0, 0);
1756 gp_point (t->BoundingBox.X1, t->BoundingBox.Y2, 0, 0);
1757 gp_point (t->BoundingBox.X2, t->BoundingBox.Y2, 0, 0);
1758 gp_point (t->BoundingBox.X2, t->BoundingBox.Y1, 0, 0);
1759 return 0;
1762 static int
1763 gp_poly_cb (const BoxType *b, void *cb)
1765 int i;
1766 const PolygonType *p = (PolygonType *) b;
1767 for (i=0; i<p->PointN; i++)
1768 gp_point (p->Points[i].X, p->Points[i].Y, 0, 0);
1769 return 0;
1772 static int
1773 gp_pin_cb (const BoxType *b, void *cb)
1775 const PinType *p = (PinType *) b;
1776 Coord t2 = (PIN_SIZE(p)+1)/2;
1778 if (p == start_pinpad || p == end_pinpad)
1779 return 0;
1781 /*! \todo We lump octagonal pins in with square; safe, but not
1782 * optimal. */
1783 if (TEST_FLAG (SQUAREFLAG, p) || TEST_FLAG (OCTAGONFLAG, p))
1785 gp_point (p->X - t2, p->Y - t2, 0, 0);
1786 gp_point (p->X - t2, p->Y + t2, 0, 0);
1787 gp_point (p->X + t2, p->Y + t2, 0, 0);
1788 gp_point (p->X + t2, p->Y - t2, 0, 0);
1790 else
1792 gp_point (p->X, p->Y, t2, 0);
1794 return 0;
1797 static int
1798 gp_pad_cb (const BoxType *b, void *cb)
1800 const PadType *p = (PadType *) b;
1801 Coord t2 = (p->Thickness+1)/2;
1803 if (p == start_pinpad || p == end_pinpad)
1804 return 0;
1806 if (TEST_FLAG (ONSOLDERFLAG, p))
1808 if (!current_is_bottom)
1809 return 0;
1811 else
1813 if (!current_is_top)
1814 return 0;
1817 /*! \todo We lump octagonal pads in with square; safe, but not
1818 optimal. I don't think we even support octagonal pads. */
1819 if (TEST_FLAG (SQUAREFLAG, p) || TEST_FLAG (OCTAGONFLAG, p))
1821 if (p->Point1.X == p->Point2.X)
1823 Coord y1 = MIN (p->Point1.Y, p->Point2.Y) - t2;
1824 Coord y2 = MAX (p->Point1.Y, p->Point2.Y) + t2;
1826 gp_point (p->Point1.X - t2, y1, 0, 0);
1827 gp_point (p->Point1.X - t2, y2, 0, 0);
1828 gp_point (p->Point1.X + t2, y1, 0, 0);
1829 gp_point (p->Point1.X + t2, y2, 0, 0);
1831 else
1833 Coord x1 = MIN (p->Point1.X, p->Point2.X) - t2;
1834 Coord x2 = MAX (p->Point1.X, p->Point2.X) + t2;
1836 gp_point (x1, p->Point1.Y - t2, 0, 0);
1837 gp_point (x2, p->Point1.Y - t2, 0, 0);
1838 gp_point (x1, p->Point1.Y + t2, 0, 0);
1839 gp_point (x2, p->Point1.Y + t2, 0, 0);
1842 else
1844 gp_point (p->Point1.X, p->Point1.Y, t2, 0);
1845 gp_point (p->Point2.X, p->Point2.Y, t2, 0);
1847 return 0;
1850 static LineType *
1851 create_line (LineType *sample, Coord x1, Coord y1, Coord x2, Coord y2)
1853 #if TRACE1
1854 Extra *e;
1855 pcb_printf("create_line from %#mD to %#mD\n", x1, y1, x2, y2);
1856 #endif
1857 LineType *line = CreateNewLineOnLayer (CURRENT, x1, y1, x2, y2,
1858 sample->Thickness, sample->Clearance, sample->Flags);
1859 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1861 #if TRACE1
1863 #endif
1864 new_line_extra (line);
1865 #if TRACE1
1866 printf(" - line extra is %p\n", e);
1867 #endif
1868 return line;
1871 static ArcType *
1872 create_arc (LineType *sample, Coord x, Coord y, Coord r, Coord sa, Coord da)
1874 Extra *e;
1875 ArcType *arc;
1877 if (r % 100 == 1)
1878 r--;
1879 if (r % 100 == 99)
1880 r++;
1881 #if TRACE1
1882 pcb_printf("create_arc at %#mD r %#mS sa %d delta %d\n", x, y, r, sa, da);
1883 #endif
1884 arc = CreateNewArcOnLayer (CURRENT, x, y, r, r, sa, da,
1885 sample->Thickness, sample->Clearance, sample->Flags);
1886 if (arc == 0)
1888 arc = CreateNewArcOnLayer (CURRENT, x, y, r, r, sa, da*2,
1889 sample->Thickness, sample->Clearance, sample->Flags);
1891 AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
1893 if (!arc)
1894 longjmp (abort_buf, 1);
1896 e = new_arc_extra (arc);
1897 #if TRACE1
1898 printf(" - arc extra is %p\n", e);
1899 #endif
1900 fix_arc_extra (arc, e);
1901 return arc;
1904 static void
1905 unlink_extras (Extra *e)
1907 #if TRACE1
1908 fprintf(stderr, "unlink %p\n", e);
1909 print_extra(e,0);
1910 #endif
1911 if (e->start.next)
1913 #if TRACE1
1914 print_extra(e->start.next, 0);
1915 #endif
1916 if (e->start.next->start.next == e)
1918 #if TRACE1
1919 fprintf(stderr, " - %p->start points to me\n", e->start.next);
1920 #endif
1921 e->start.next->start.next = e->end.next;
1923 else if (e->start.next->end.next == e)
1925 #if TRACE1
1926 fprintf(stderr, " - %p->end points to me\n", e->start.next);
1927 #endif
1928 e->start.next->end.next = e->end.next;
1930 else
1932 fprintf(stderr, " - %p doesn't point to me!\n", e->start.next);
1933 abort();
1936 if (e->end.next)
1938 #if TRACE1
1939 print_extra(e->end.next, 0);
1940 #endif
1941 if (e->end.next->start.next == e)
1943 #if TRACE1
1944 fprintf(stderr, " - %p->end points to me\n", e->end.next);
1945 #endif
1946 e->end.next->start.next = e->start.next;
1948 else if (e->end.next->end.next == e)
1950 #if TRACE1
1951 fprintf(stderr, " - %p->end points to me\n", e->end.next);
1952 #endif
1953 e->end.next->end.next = e->start.next;
1955 else
1957 fprintf(stderr, " - %p doesn't point to me!\n", e->end.next);
1958 abort();
1961 e->start.next = e->end.next = 0;
1964 static void
1965 mark_line_for_deletion (LineType *l)
1967 Extra *e = LINE2EXTRA(l);
1968 if (e->deleted)
1970 fprintf(stderr, "double delete?\n");
1971 abort();
1973 e->deleted = 1;
1974 unlink_extras (e);
1975 #if TRACE1
1976 pcb_printf("Marked line %p for deletion %#mD to %#mD\n",
1977 e, l->Point1.X, l->Point1.Y, l->Point2.X, l->Point2.Y);
1978 #endif
1979 #if 0
1980 if (l->Point1.X < 0)
1982 fprintf(stderr, "double neg move?\n");
1983 abort();
1985 MoveObject (LINEPOINT_TYPE, CURRENT, l, &(l->Point1),
1986 -1 - l->Point1.X,
1987 -1 - l->Point1.Y);
1988 MoveObject (LINEPOINT_TYPE, CURRENT, l, &(l->Point2),
1989 -1 - l->Point2.X,
1990 -1 - l->Point2.Y);
1991 #endif
1994 static void
1995 mark_arc_for_deletion (ArcType *a)
1997 Extra *e = ARC2EXTRA(a);
1998 e->deleted = 1;
1999 unlink_extras (e);
2000 #if TRACE1
2001 printf("Marked arc %p for deletion %ld < %ld\n",
2002 e, a->StartAngle, a->Delta);
2003 #endif
2007 * \brief .
2009 * Given a starting line, which may be attached to an arc, and which
2010 * intersects with an ending line, which also may be attached to an
2011 * arc, maybe pull them.\n
2012 * We assume start_line is attached to the arc via Point1, and attached
2013 * to the end line via Point2.\n
2014 * Likewise, we make end_line attach to the start_line via Point1 and
2015 * the arc via Point 2.\n
2016 * We also make the arcs attach on the Delta end, not the Start end.\n
2017 * Here's a picture:
2018 * <pre>
2019 S S+D P1 P2 P1 P2 S+D S
2020 *--- start_arc ---*--- start_line ---*--- end_line ---*--- end_arc ---*
2021 S E S E S E E S
2022 * </pre>
2024 static void
2025 maybe_pull_1 (LineType *line)
2027 BoxType box;
2028 /* Line half-thicknesses, including line space */
2029 Coord ex, ey;
2030 LineType *new_line;
2031 Extra *new_lextra;
2032 ArcType *new_arc;
2033 Extra *new_aextra;
2034 double abs_angle;
2036 start_line = line;
2037 start_extra = LINE2EXTRA (start_line);
2038 end_extra = start_extra->end.next;
2039 end_line = EXTRA2LINE (end_extra);
2040 if (end_extra->deleted)
2042 start_extra->end.pending = 0;
2043 return;
2046 if (end_extra->end.next == start_extra)
2047 reverse_line (end_line);
2049 if (start_extra->start.next
2050 && EXTRA_IS_ARC (start_extra->start.next))
2052 sarc_extra = start_extra->start.next;
2053 start_arc = EXTRA2ARC (sarc_extra);
2054 if (sarc_extra->start.next == start_extra)
2055 reverse_arc (start_arc);
2057 else
2059 start_arc = 0;
2060 sarc_extra = 0;
2063 if (end_extra->end.next
2064 && EXTRA_IS_ARC (end_extra->end.next))
2066 earc_extra = end_extra->end.next;
2067 end_arc = EXTRA2ARC (earc_extra);
2068 if (earc_extra->start.next == end_extra)
2069 reverse_arc (end_arc);
2071 else
2073 end_arc = 0;
2074 earc_extra = 0;
2077 #if TRACE1
2078 printf("maybe_pull_1 %p %p %p %p\n", sarc_extra, start_extra, end_extra, earc_extra);
2079 if (sarc_extra)
2080 print_extra(sarc_extra,0);
2081 print_extra(start_extra,0);
2082 print_extra(end_extra,0);
2083 if (earc_extra)
2084 print_extra(earc_extra,0);
2086 if (start_extra->deleted
2087 || end_extra->deleted
2088 || (sarc_extra && sarc_extra->deleted)
2089 || (earc_extra && earc_extra->deleted))
2091 printf(" one is deleted?\n");
2092 fflush(stdout);
2093 abort();
2095 #endif
2097 if (!start_extra->end.pending)
2098 return;
2099 #if 0
2100 if (start_extra->end.waiting_for
2101 && start_extra->end.waiting_for->pending)
2102 return;
2103 #endif
2105 if (start_line->Thickness != end_line->Thickness)
2106 return;
2107 thickness = (start_line->Thickness + 1)/2 + PCB->Bloat;
2109 /* At this point, our expectations are all met. */
2111 box.X1 = start_line->Point1.X - thickness;
2112 box.X2 = start_line->Point1.X + thickness;
2113 box.Y1 = start_line->Point1.Y - thickness;
2114 box.Y2 = start_line->Point1.Y + thickness;
2115 expand_box (&box, start_line->Point2.X, start_line->Point2.Y, thickness);
2116 expand_box (&box, end_line->Point2.X, end_line->Point2.Y, thickness);
2117 if (start_arc)
2118 expand_box (&box, sarc_extra->start.x, sarc_extra->start.y, start_arc->Thickness/2);
2119 if (end_arc)
2120 expand_box (&box, earc_extra->start.x, earc_extra->start.y, end_arc->Thickness/2);
2123 se_sign = copysign (1, cross2d (start_line->Point1.X, start_line->Point1.Y,
2124 start_line->Point2.X, start_line->Point2.Y,
2125 end_line->Point2.X, end_line->Point2.Y));
2126 best_angle = se_sign * M_PI;
2127 if (start_arc)
2129 sa_sign = copysign (1, -start_arc->Delta);
2130 sa_sign *= se_sign;
2132 else
2133 sa_sign = 0;
2134 if (end_arc)
2136 ea_sign = copysign (1, -end_arc->Delta);
2137 ea_sign *= -se_sign;
2139 else
2140 ea_sign = 0;
2142 start_angle = atan2 (start_line->Point2.Y - start_line->Point1.Y,
2143 start_line->Point2.X - start_line->Point1.X);
2144 #if TRACE1
2145 printf("se_sign %f sa_sign %f ea_sign %f best_angle %f start_angle %f\n",
2146 se_sign, sa_sign, ea_sign, r2d (best_angle), r2d(start_angle));
2147 #endif
2149 if (start_arc)
2151 sa_x = start_arc->X;
2152 sa_y = start_arc->Y;
2153 if (same_sign (start_arc->Delta, se_sign))
2154 sa_r = - start_arc->Width;
2155 else
2156 sa_r = start_arc->Width;
2158 else
2160 sa_x = start_line->Point1.X;
2161 sa_y = start_line->Point1.Y;
2162 sa_r = 0;
2165 if (end_arc)
2167 if (ea_sign < 0)
2168 ea_r = end_arc->Width;
2169 else
2170 ea_r = - end_arc->Width;
2172 else
2173 ea_r = 0;
2175 #if TRACE1
2176 trace_path (sarc_extra ? sarc_extra : start_extra);
2177 #endif
2179 if (end_arc)
2181 gp_point_force (end_arc->X, end_arc->Y, -ea_r-thickness, 0, 0, 0, 1, "end arc");
2182 ex = end_arc->X;
2183 ey = end_arc->Y;
2185 else
2187 gp_point_force (end_line->Point2.X, end_line->Point2.Y, -thickness, 0, 0, 0, 1, "end arc");
2188 ex = end_line->Point2.X;
2189 ey = end_line->Point2.Y;
2192 fx = ex;
2193 fy = ey;
2194 if (fx < 0)
2196 pcb_fprintf(stderr, "end line corrupt? f is %#mD\n", fx, fy);
2197 print_extra (end_extra, 0);
2198 if (earc_extra)
2199 print_extra(earc_extra, 0);
2200 abort();
2203 end_dist = Distance (end_line->Point1.X, end_line->Point1.Y,
2204 end_line->Point2.X, end_line->Point2.Y);
2206 start_pinpad = start_extra->start.pin;
2207 end_pinpad = start_extra->end.pin;
2208 fp = 0;
2210 r_search(CURRENT->line_tree, &box, NULL, gp_line_cb, 0);
2211 r_search(CURRENT->arc_tree, &box, NULL, gp_arc_cb, 0);
2212 r_search(CURRENT->text_tree, &box, NULL, gp_text_cb, 0);
2213 r_search(CURRENT->polygon_tree, &box, NULL, gp_poly_cb, 0);
2214 r_search(PCB->Data->pin_tree, &box, NULL, gp_pin_cb, 0);
2215 r_search(PCB->Data->via_tree, &box, NULL, gp_pin_cb, 0);
2216 r_search(PCB->Data->pad_tree, &box, NULL, gp_pad_cb, 0);
2218 /* radians, absolute angle of (at the moment) the start_line */
2219 abs_angle = fa + start_angle;
2221 #if TRACE1
2222 pcb_printf("\033[43;30mBest: at %#mD r %#mS, angle %.1f fp %d\033[0m\n", fx, fy, fr, r2d(fa), fp);
2223 #endif
2225 #if 0
2226 if (fa > M_PI/2 || fa < -M_PI/2)
2228 SET_FLAG (FOUNDFLAG, line);
2229 longjmp (abort_buf, 1);
2231 #endif
2233 if (fp)
2235 start_extra->end.waiting_for = fp_end;
2236 return;
2238 start_extra->end.pending = 0;
2240 /* Step 0: check for merged arcs (special case). */
2242 if (fx == ex && fy == ey
2243 && start_arc && end_arc
2244 && start_arc->X == end_arc->X && start_arc->Y == end_arc->Y)
2246 /* Merge arcs */
2247 int new_delta;
2249 new_delta = end_arc->StartAngle - start_arc->StartAngle;
2250 if (start_arc->Delta > 0)
2252 while (new_delta > 360)
2253 new_delta -= 360;
2254 while (new_delta < 0)
2255 new_delta += 360;
2257 else
2259 while (new_delta < -360)
2260 new_delta += 360;
2261 while (new_delta > 0)
2262 new_delta -= 360;
2264 #if TRACE1
2265 pcb_printf("merging arcs at %#mS nd %d\n", start_arc->X, start_arc->Y, new_delta);
2266 print_extra(sarc_extra, 0);
2267 print_extra(earc_extra, 0);
2268 #endif
2269 mark_arc_for_deletion (end_arc);
2270 mark_line_for_deletion (start_line);
2271 mark_line_for_deletion (end_line);
2272 ChangeArcAngles (CURRENT, start_arc, start_arc->StartAngle, new_delta);
2273 fix_arc_extra (start_arc, sarc_extra);
2274 did_something ++;
2275 return;
2278 /* Step 1: adjust start_arc's angles and move start_line's Point1 to
2279 match it. */
2281 if (start_arc)
2283 double new_delta;
2284 int del_arc = 0;
2286 /* We must always round towards "larger arcs", whichever way that is. */
2287 new_delta = 180 - r2d(abs_angle);
2288 #if TRACE1
2289 printf("new_delta starts at %d vs %d < %d\n",
2290 (int)new_delta, (int)start_arc->StartAngle, (int)start_arc->Delta);
2291 #endif
2292 if (start_arc->Delta < 0)
2293 new_delta = (int)(new_delta) + 90;
2294 else
2295 new_delta = (int)new_delta - 90;
2296 new_delta = new_delta - start_arc->StartAngle;
2297 while (new_delta > start_arc->Delta+180)
2298 new_delta -= 360;
2299 while (new_delta < start_arc->Delta-180)
2300 new_delta += 360;
2301 #if TRACE1
2302 printf("new_delta adjusts to %d\n", (int)new_delta);
2303 printf("fa = %f, new_delta ends at %.1f vs start %d\n",
2304 fa, new_delta, (int)start_arc->StartAngle);
2305 #endif
2307 if (new_delta * start_arc->Delta <= 0)
2308 del_arc = 1;
2310 ChangeArcAngles (CURRENT, start_arc, start_arc->StartAngle, new_delta);
2311 fix_arc_extra (start_arc, sarc_extra);
2312 MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point1),
2313 sarc_extra->end.x - start_line->Point1.X,
2314 sarc_extra->end.y - start_line->Point1.Y);
2316 if (del_arc)
2318 MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point1),
2319 sarc_extra->start.x - start_line->Point1.X,
2320 sarc_extra->start.y - start_line->Point1.Y);
2321 mark_arc_for_deletion (start_arc);
2325 /* Step 1.5: If the "obstacle" is right at the end, ignore it. */
2328 double oa = start_angle+fa - M_PI/2*se_sign;
2329 double ox = fx + fr * cos(oa);
2330 double oy = fy + fr * sin(oa);
2331 #if TRACE1
2332 pcb_printf("obstacle at %#mD angle %d = arc starts at %#mD\n",
2333 fx, fy, (int)r2d(oa), (Coord)ox, (Coord)oy);
2334 #endif
2336 if (Distance (ox, oy, end_line->Point2.X, end_line->Point2.Y)
2337 < fr * SIN1D)
2339 /* Pretend it doesn't exist. */
2340 fx = ex;
2341 fy = ey;
2345 /* Step 2: If we have no obstacles, connect start and end. */
2347 #if TRACE1
2348 pcb_printf("fx %#mS ex %#mS fy %#mS ey %#mS\n", fx, ex, fy, ey);
2349 #endif
2350 if (fx == ex && fy == ey)
2352 /* No obstacles. */
2353 #if TRACE1
2354 printf("\033[32mno obstacles\033[0m\n");
2355 #endif
2356 if (end_arc)
2358 int del_arc = 0;
2359 int new_delta, end_angle, pcb_fa, end_change;
2361 end_angle = end_arc->StartAngle + end_arc->Delta;
2362 if (end_arc->Delta < 0)
2363 end_angle -= 90;
2364 else
2365 end_angle += 90;
2367 /* We must round so as to make the larger arc. */
2368 if (end_arc->Delta < 0)
2369 pcb_fa = - r2d(start_angle + fa);
2370 else
2371 pcb_fa = 1 - r2d(start_angle + fa);
2372 end_change = pcb_fa - end_angle;
2374 while (end_change > 180)
2375 end_change -= 360;
2376 while (end_change < -180)
2377 end_change += 360;
2378 new_delta = end_arc->Delta + end_change;
2380 if (new_delta * end_arc->Delta <= 0)
2381 del_arc = 1;
2383 ChangeArcAngles (CURRENT, end_arc, end_arc->StartAngle, new_delta);
2384 fix_arc_extra (end_arc, earc_extra);
2385 MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point2),
2386 earc_extra->end.x - start_line->Point2.X,
2387 earc_extra->end.y - start_line->Point2.Y);
2389 if (del_arc)
2391 MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point2),
2392 earc_extra->start.x - start_line->Point2.X,
2393 earc_extra->start.y - start_line->Point2.Y);
2394 mark_arc_for_deletion (end_arc);
2397 else
2399 MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point2),
2400 end_line->Point2.X - start_line->Point2.X,
2401 end_line->Point2.Y - start_line->Point2.Y);
2403 mark_line_for_deletion (end_line);
2404 start_extra->end.pending = 1;
2406 #if TRACE1
2407 printf("\033[35mdid_something: no obstacles\033[0m\n");
2408 #endif
2409 did_something ++;
2410 npulled ++;
2411 status();
2412 return;
2415 /* Step 3: Compute the new intersection of start_line and end_line. */
2417 ex = start_line->Point1.X + cos(start_angle + fa) * 10000.0;
2418 ey = start_line->Point1.Y + sin(start_angle + fa) * 10000.0;
2419 #if TRACE1
2420 pcb_printf("temp point %#mS\n", ex, ey);
2421 pcb_printf("intersect %#mS-%#mS with %#mS-%#mS\n",
2422 start_line->Point1.X, start_line->Point1.Y,
2423 ex, ey,
2424 end_line->Point1.X, end_line->Point1.Y,
2425 end_line->Point2.X, end_line->Point2.Y);
2426 #endif
2427 if (! intersection_of_lines (start_line->Point1.X, start_line->Point1.Y,
2428 ex, ey,
2429 end_line->Point1.X, end_line->Point1.Y,
2430 end_line->Point2.X, end_line->Point2.Y,
2431 &ex, &ey))
2433 ex = end_line->Point2.X;
2434 ey = end_line->Point2.Y;
2436 #if TRACE1
2437 pcb_printf("new point %#mS\n", ex, ey);
2438 #endif
2439 MoveObject (LINEPOINT_TYPE, CURRENT, end_line, &(end_line->Point1),
2440 ex - end_line->Point1.X,
2441 ey - end_line->Point1.Y);
2443 /* Step 4: Split start_line at the obstacle and insert a zero-delta
2444 arc at it. */
2446 new_arc = create_arc (start_line, fx, fy, fr,
2447 90-(int)(r2d(start_angle+fa)+0.5) + 90 + 90*se_sign, -se_sign);
2448 new_aextra = ARC2EXTRA (new_arc);
2450 if (start_arc) sarc_extra = ARC2EXTRA (start_arc);
2451 if (end_arc) earc_extra = ARC2EXTRA (end_arc);
2453 MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point2),
2454 new_aextra->start.x - start_line->Point2.X,
2455 new_aextra->start.y - start_line->Point2.Y);
2457 new_line = create_line (start_line, new_aextra->end.x, new_aextra->end.y, ex, ey);
2458 start_extra = LINE2EXTRA (start_line);
2459 new_lextra = LINE2EXTRA (new_line);
2460 end_extra = LINE2EXTRA (end_line);
2462 new_lextra->start.pin = start_extra->start.pin;
2463 new_lextra->end.pin = start_extra->end.pin;
2464 new_lextra->start.pending = 1;
2465 new_lextra->end.pending = 1;
2467 start_extra->end.next = new_aextra;
2468 new_aextra->start.next = start_extra;
2469 new_aextra->end.next = new_lextra;
2470 new_lextra->start.next = new_aextra;
2471 new_lextra->end.next = end_extra;
2472 end_extra->start.next = new_lextra;
2474 /* Step 5: Recurse. */
2476 did_something ++;
2477 npulled ++;
2478 status();
2479 #if TRACE0
2480 printf("\033[35mdid_something: recursing\033[0m\n");
2482 int i = gui->confirm_dialog("recurse?", 0);
2483 printf("confirm = %d\n", i);
2484 if (i == 0)
2485 return;
2487 printf("\n\033[33mRECURSING\033[0m\n\n");
2488 IncrementUndoSerialNumber();
2489 #endif
2490 maybe_pull_1 (new_line);
2494 * \brief Given a line with a end_next, attempt to pull both ends.
2496 static void
2497 maybe_pull (LineType *line, Extra *e)
2499 #if TRACE0
2500 printf("maybe_pull: ");
2501 print_extra (e, 0);
2502 #endif
2503 if (e->end.next && EXTRA_IS_LINE (e->end.next))
2505 maybe_pull_1 (line);
2507 else
2509 e->end.pending = 0;
2510 if (e->start.next && EXTRA_IS_LINE (e->start.next))
2512 reverse_line (line);
2513 maybe_pull_1 (line);
2515 else
2516 e->start.pending = 0;
2520 static void
2521 validate_pair (Extra *e, End *end)
2523 if (!end->next)
2524 return;
2525 if (end->next->start.next == e)
2526 return;
2527 if (end->next->end.next == e)
2528 return;
2529 fprintf(stderr, "no backlink!\n");
2530 print_extra (e, 0);
2531 print_extra (end->next, 0);
2532 abort();
2535 static void
2536 validate_pair_cb (AnyObjectType *ptr, Extra *extra, void *userdata)
2538 validate_pair (extra, &extra->start);
2539 validate_pair (extra, &extra->end);
2542 static void
2543 validate_pairs ()
2545 g_hash_table_foreach (lines, (GHFunc)validate_pair_cb, NULL);
2546 #if TRACE1
2547 printf("\narcs\n");
2548 #endif
2549 g_hash_table_foreach (arcs, (GHFunc)validate_pair_cb, NULL);
2552 static void
2553 FreeExtra (Extra *extra)
2555 g_slice_free (Extra, extra);
2558 static void
2559 mark_ends_pending (LineType *line, Extra *extra, void *userdata)
2561 int *select_flags = userdata;
2562 if (TEST_FLAGS (*select_flags, line))
2564 extra->start.pending = 1;
2565 extra->end.pending = 1;
2569 #if TRACE1
2570 static void
2571 trace_print_extra (AnyObjectType *ptr, Extra *extra, void *userdata)
2573 last_pextra = (Extra *)1;
2574 print_extra(extra, 0);
2577 static void
2578 trace_print_lines_arcs (void)
2580 printf("\nlines\n");
2581 g_hash_table_foreach (lines, (GHFunc)trace_print_extra, NULL);
2583 printf("\narcs\n");
2584 g_hash_table_foreach (arcs, (GHFunc)trace_print_extra, NULL);
2586 printf("\n");
2588 #endif
2590 static int
2591 GlobalPuller(int argc, char **argv, Coord x, Coord y)
2593 int select_flags = 0;
2595 setbuf(stdout, 0);
2596 nloops = 0;
2597 npulled = 0;
2598 Message ("puller! %s\n", argc > 0 ? argv[0] : "");
2600 if (argc > 0 && strcasecmp (argv[0], "selected") == 0)
2601 select_flags = SELECTEDFLAG;
2602 if (argc > 0 && strcasecmp (argv[0], "found") == 0)
2603 select_flags = FOUNDFLAG;
2605 Message ("optimizing...\n");
2606 /* This canonicalizes all the lines, and cleans up near-misses. */
2607 /* hid_actionl ("djopt", "puller", 0); */
2609 current_is_bottom = (GetLayerGroupNumberByPointer(CURRENT)
2610 == GetLayerGroupNumberBySide (BOTTOM_SIDE));
2611 current_is_top = (GetLayerGroupNumberByPointer(CURRENT)
2612 == GetLayerGroupNumberBySide (TOP_SIDE));
2614 lines = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)FreeExtra);
2615 arcs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)FreeExtra);
2617 Message ("pairing...\n");
2618 find_pairs ();
2619 validate_pairs ();
2621 g_hash_table_foreach (lines, (GHFunc)mark_ends_pending, &select_flags);
2623 #if TRACE1
2624 trace_print_lines_arcs ();
2625 #endif
2627 propogate_ends ();
2629 #if TRACE1
2630 trace_print_lines_arcs ();
2631 trace_paths ();
2632 #endif
2634 Message ("pulling...\n");
2635 if (setjmp(abort_buf) == 0)
2637 #if TRACE0
2638 int old_did_something = -1;
2639 #endif
2640 did_something = 1;
2641 while (did_something)
2643 nloops ++;
2644 status();
2645 did_something = 0;
2646 LINE_LOOP (CURRENT); {
2647 Extra *e = LINE2EXTRA (line);
2648 if (e->deleted)
2649 continue;
2650 #ifdef CHECK_LINE_PT_NEG
2651 if (line->Point1.X < 0)
2652 abort1();
2653 #endif
2654 if (e->start.next || e->end.next)
2655 maybe_pull (line, e);
2656 #if TRACE0
2657 if (did_something != old_did_something)
2659 IncrementUndoSerialNumber();
2660 old_did_something = did_something;
2661 if (gui->confirm_dialog("more?", 0) == 0)
2663 did_something = 0;
2664 break;
2667 #endif
2668 /*gui->progress(0,0,0);*/
2669 } END_LOOP;
2673 #if TRACE0
2674 printf("\nlines\n");
2675 g_hash_table_foreach (lines, (GHFunc)trace_print_extra, NULL);
2676 printf("\narcs\n");
2677 g_hash_table_foreach (arcs, (GHFunc)trace_print_extra, NULL);
2678 printf("\n");
2679 printf("\nlines\n");
2680 #endif
2682 LINE_LOOP (CURRENT);
2684 if (LINE2EXTRA (line)->deleted)
2685 RemoveLine (CURRENT, line);
2687 END_LOOP;
2689 ARC_LOOP (CURRENT);
2691 if (ARC2EXTRA (arc)->deleted)
2692 RemoveArc (CURRENT, arc);
2694 END_LOOP;
2696 g_hash_table_unref (lines);
2697 g_hash_table_unref (arcs);
2699 IncrementUndoSerialNumber();
2700 return 0;
2703 /* Actions */
2705 HID_Action puller_action_list[] = {
2706 {"Puller", "Click on a line-arc intersection or line segment", Puller,
2707 puller_help, puller_syntax},
2708 {"GlobalPuller", 0, GlobalPuller,
2709 globalpuller_help, globalpuller_syntax}
2712 REGISTER_ACTIONS (puller_action_list)