pcb 4.1.1
An interactive printed circuit board layout editor.

puller.c

Go to the documentation of this file.
00001 
00048 #ifdef HAVE_CONFIG_H
00049 #include "config.h"
00050 #endif
00051 
00052 #include "global.h"
00053 
00054 #include <math.h>
00055 #include <memory.h>
00056 #include <limits.h>
00057 #include <setjmp.h>
00058 
00059 
00060 #include "create.h"
00061 #include "data.h"
00062 #include "draw.h"
00063 #include "misc.h"
00064 #include "move.h"
00065 #include "pcb-printf.h"
00066 #include "remove.h"
00067 #include "rtree.h"
00068 #include "strflags.h"
00069 #include "undo.h"
00070 #include "error.h"
00071 
00072 #ifdef HAVE_LIBDMALLOC
00073 #include <dmalloc.h>
00074 #endif
00075 
00076 #define abort1() fprintf(stderr, "abort at line %d\n", __LINE__), abort()
00077 
00078 #define TRACE0 0
00079 #define TRACE1 0
00080 
00082 #define SIN1D   0.0174524064372835
00083 
00084 static jmp_buf abort_buf;
00085 static int multi, line_exact, arc_exact;
00086 static LineType *the_line;
00087 static ArcType *the_arc;
00088 static double arc_dist;
00089 
00090 /* We canonicalize the arc and line such that the point to be moved is
00091    always Point2 for the line, and at start+delta for the arc.  */
00092 
00093 static Coord x, y;              /* the point we're moving */
00094 static Coord cx, cy;            /* centerpoint of the arc */
00095 static Coord ex, ey;            /* fixed end of the line */
00096 
00097 /* 0 is left (-x), 90 is down (+y), 180 is right (+x), 270 is up (-y) */
00098 
00099 static Coord
00100 within (Coord x1, Coord y1, Coord x2, Coord y2, Coord r)
00101 {
00102   return Distance (x1, y1, x2, y2) <= r / 2;
00103 }
00104 
00105 static int
00106 arc_endpoint_is (ArcType *a, int angle, Coord x, Coord y)
00107 {
00108   Coord ax = a->X, ay = a->Y;
00109 
00110   if (angle % 90 == 0)
00111     {
00112       int ai = (int) (angle / 90) & 3;
00113       switch (ai)
00114         {
00115         case 0:
00116           ax -= a->Width;
00117           break;
00118         case 1:
00119           ay += a->Height;
00120           break;
00121         case 2:
00122           ax += a->Width;
00123           break;
00124         case 3:
00125           ay -= a->Height;
00126           break;
00127         }
00128     }
00129   else
00130     {
00131       double rad = angle * M_PI / 180;
00132       ax -= a->Width * cos (rad);
00133       ay += a->Width * sin (rad);
00134     }
00135 #if TRACE1
00136   pcb_printf (" - arc endpoint %#mD\n", ax, ay);
00137 #endif
00138   arc_dist = Distance (ax, ay, x, y);
00139   if (arc_exact)
00140     return arc_dist < 2;
00141   return arc_dist < a->Thickness / 2;
00142 }
00143 
00147 static double
00148 cross2d (Coord cx, Coord cy, Coord ux, Coord uy, Coord vx, Coord vy)
00149 {
00150   ux -= cx;
00151   uy -= cy;
00152   vx -= cx;
00153   vy -= cy;
00154   return (double)ux * vy - (double)uy * vx;
00155 }
00156 
00160 static double
00161 dot2d (Coord cx, Coord cy, Coord ux, Coord uy, Coord vx, Coord vy)
00162 {
00163   ux -= cx;
00164   uy -= cy;
00165   vx -= cx;
00166   vy -= cy;
00167   return (double)ux * vx + (double)uy * vy;
00168 }
00169 
00170 #if 0
00171 
00177 static double
00178 angle2d (Coord cx, Coord cy, Coord ux, Coord uy, Coord vx, Coord vy)
00179 {
00180   double cross;
00181   double magu, magv, sintheta;
00182 #if TRACE1
00183   pcb_printf("angle2d %mD %mD %mD\n", cx, cy, ux, uy, vx, vy);
00184 #endif
00185   ux -= cx;
00186   uy -= cy;
00187   vx -= cx;
00188   vy -= cy;
00189 #if TRACE1
00190   pcb_printf(" = %mD %mD\n", ux, uy, vx, vy);
00191 #endif
00192   cross = (double)ux * vy - (double)uy * vx;
00193   magu = hypot(ux, uy);
00194   magv = hypot(vx, vy);
00195   sintheta = cross / (magu * magv);
00196 #if TRACE1
00197   printf(" = %f / (%f * %f) = %f\n", cross, magu, magv, sintheta);
00198 #endif
00199   return asin (sintheta);
00200 }
00201 #endif
00202 
00203 static int
00204 same_sign (double a, double b)
00205 {
00206   return (a * b >= 0);
00207 }
00208 
00209 static double
00210 r2d (double r)
00211 {
00212   return 180.0 * r / M_PI;
00213 }
00214 
00215 static double
00216 d2r (double d)
00217 {
00218   return M_PI * d / 180.0;
00219 }
00220 
00228 static double
00229 det (double a, double b, double c, double d)
00230 {
00231   return a * d - b * c;
00232 }
00233 
00241 static int
00242 intersection_of_lines (Coord x1, Coord y1, Coord x2, Coord y2,
00243                        Coord x3, Coord y3, Coord x4, Coord y4,
00244                        Coord *xr, Coord *yr)
00245 {
00246   double x, y, d;
00247   d = det (x1 - x2, y1 - y2, x3 - x4, y3 - y4);
00248   if (!d)
00249     return 0;
00250   x = (det (det (x1, y1, x2, y2), x1 - x2,
00251             det (x3, y3, x4, y4), x3 - x4) / d);
00252   y = (det (det (x1, y1, x2, y2), y1 - y2,
00253             det (x3, y3, x4, y4), y3 - y4) / d);
00254   *xr = (Coord) (x + 0.5);
00255   *yr = (Coord) (y + 0.5);
00256   return 1;
00257 }
00258 
00267 static int
00268 intersection_of_linesegs (Coord x1, Coord y1, Coord x2, Coord y2,
00269                           Coord x3, Coord y3, Coord x4, Coord y4,
00270                           Coord *xr, Coord *yr)
00271 {
00272   double x, y, d;
00273   d = det (x1 - x2, y1 - y2, x3 - x4, y3 - y4);
00274   if (!d)
00275     return 0;
00276   x = (det (det (x1, y1, x2, y2), x1 - x2,
00277             det (x3, y3, x4, y4), x3 - x4) / d);
00278   y = (det (det (x1, y1, x2, y2), y1 - y2,
00279             det (x3, y3, x4, y4), y3 - y4) / d);
00280   if (MIN (x1, x2) > x || x > MAX (x1, x2)
00281       || MIN (y1, y2) > y || y > MAX (y1, y2))
00282     return 0;
00283   if (MIN (x3, x4) > x || x > MAX (x3, x4)
00284       || MIN (y3, y4) > y || y > MAX (y3, y4))
00285     return 0;
00286   if (xr)
00287     *xr = (Coord) (x + 0.5);
00288   if (yr)
00289     *yr = (Coord) (y + 0.5);
00290   return 1;
00291 }
00292 
00296 static double
00297 dist_lp (Coord x1, Coord y1, Coord x2, Coord y2, Coord px, Coord py)
00298 {
00299   double den = Distance (x1, y1, x2, y2);
00300   double rv = (fabs (((double)x2 - x1) * ((double)y1 - py)
00301                      - ((double)x1 - px) * ((double)y2 - y1))
00302                / den);
00303 #if TRACE1
00304   pcb_printf("dist %#mD-%#mD to %#mD is %f\n",
00305          x1, y1, x2, y2, px, py, rv);
00306 #endif
00307   return rv;
00308 }
00309 
00313 static double
00314 dist_lsp (Coord x1, Coord y1, Coord x2, Coord y2, Coord px, Coord py)
00315 {
00316   double d;
00317   if (dot2d (x1, y1, x2, y2, px, py) < 0)
00318     return Distance (x1, y1, px, py);
00319   if (dot2d (x2, y2, x1, y1, px, py) < 0)
00320     return Distance (x2, y2, px, py);
00321   d = (fabs (((double)x2 - x1) * ((double)y1 - py)
00322              - ((double)x1 - px) * ((double)y2 - y1))
00323        / Distance (x1, y1, x2, y2));
00324   return d;
00325 }
00326 
00327 /*                       Single Point Puller                                 */
00328 
00329 static int
00330 line_callback (const BoxType * b, void *cl)
00331 {
00332   /* LayerType *layer = (LayerType *)cl; */
00333   LineType *l = (LineType *) b;
00334   double d1, d2, t;
00335 #if TRACE1
00336   pcb_printf ("line %#mD .. %#mD\n",
00337           l->Point1.X, l->Point1.Y, l->Point2.X, l->Point2.Y);
00338 #endif
00339   d1 = Distance (l->Point1.X, l->Point1.Y, x, y);
00340   d2 = Distance (l->Point2.X, l->Point2.Y, x, y);
00341   if ((d1 < 2 || d2 < 2) && !line_exact)
00342     {
00343       line_exact = 1;
00344       the_line = 0;
00345     }
00346   t = line_exact ? 2 : l->Thickness / 2;
00347   if (d1 < t || d2 < t)
00348     {
00349       if (the_line)
00350         multi = 1;
00351       the_line = l;
00352 #if TRACE1
00353       printf ("picked, exact %d\n", line_exact);
00354 #endif
00355     }
00356   return 1;
00357 }
00358 
00359 static int
00360 arc_callback (const BoxType * b, void *cl)
00361 {
00362   /* LayerType *layer = (LayerType *) cl; */
00363   ArcType *a = (ArcType *) b;
00364 
00365 #if TRACE1
00366   pcb_printf ("arc a %#mD r %#mS sa %ld d %ld\n", a->X, a->Y, a->Width,
00367           a->StartAngle, a->Delta);
00368 #endif
00369   if (!arc_endpoint_is (a, a->StartAngle, x, y)
00370       && !arc_endpoint_is (a, a->StartAngle + a->Delta, x, y))
00371     return 1;
00372   if (arc_dist < 2)
00373     {
00374       if (!arc_exact)
00375         {
00376           arc_exact = 1;
00377           the_arc = 0;
00378         }
00379       if (the_arc)
00380         multi = 1;
00381       the_arc = a;
00382 #if TRACE1
00383       printf ("picked, exact %d\n", arc_exact);
00384 #endif
00385     }
00386   else if (!arc_exact)
00387     {
00388       if (the_arc)
00389         multi = 1;
00390       the_arc = a;
00391 #if TRACE1
00392       printf ("picked, exact %d\n", arc_exact);
00393 #endif
00394     }
00395   return 1;
00396 }
00397 
00398 static int
00399 find_pair (Coord Px, Coord Py)
00400 {
00401   BoxType spot;
00402 
00403 #if TRACE1
00404   pcb_printf ("\nPuller find_pair at %#mD\n", Px, Py);
00405 #endif
00406 
00407   x = Px;
00408   y = Py;
00409   multi = 0;
00410   line_exact = arc_exact = 0;
00411   the_line = 0;
00412   the_arc = 0;
00413   spot.X1 = x - 1;
00414   spot.Y1 = y - 1;
00415   spot.X2 = x + 1;
00416   spot.Y2 = y + 1;
00417   r_search (CURRENT->line_tree, &spot, NULL, line_callback, CURRENT);
00418   r_search (CURRENT->arc_tree, &spot, NULL, arc_callback, CURRENT);
00419   if (the_line && the_arc && !multi)
00420     return 1;
00421   x = Px;
00422   y = Py;
00423   return 0;
00424 }
00425 
00426 
00427 static const char puller_syntax[] = "Puller()";
00428 
00429 static const char puller_help[] = "Pull an arc-line junction tight.";
00430 
00431 /* %start-doc actions Puller
00432 
00433 The @code{Puller()} action is a special-purpose optimization.  When
00434 invoked while the crosshair is over the junction of an arc and a line,
00435 it will adjust the arc's angle and the connecting line's endpoint such
00436 that the line intersects the arc at a tangent.  In the example below,
00437 the left side is ``before'' with the black target marking where to put
00438 the crosshair:
00439 
00440 @center @image{puller,,,Example of how puller works,png}
00441 
00442 The right side is ``after'' with the black target marking where the
00443 arc-line intersection was moved to.
00444 
00445 %end-doc */
00446 
00447 static int
00448 Puller (int argc, char **argv, Coord Ux, Coord Uy)
00449 {
00450   double arc_angle, base_angle;
00451 #if TRACE1
00452   double line_angle, rel_angle;
00453 #endif
00454   double tangent;
00455   int new_delta_angle;
00456 
00457   if (!find_pair (Crosshair.X, Crosshair.Y))
00458     if (!find_pair (Ux, Uy))
00459       return 0;
00460 
00461   if (within (the_line->Point1.X, the_line->Point1.Y,
00462               x, y, the_line->Thickness))
00463     {
00464       ex = the_line->Point2.X;
00465       ey = the_line->Point2.Y;
00466       the_line->Point2.X = the_line->Point1.X;
00467       the_line->Point2.Y = the_line->Point1.Y;
00468       the_line->Point1.X = ex;
00469       the_line->Point1.Y = ey;
00470     }
00471   else if (!within (the_line->Point2.X, the_line->Point2.Y,
00472                     x, y, the_line->Thickness))
00473     {
00474 #if TRACE1
00475       printf ("Line endpoint not at cursor\n");
00476 #endif
00477       return 1;
00478     }
00479   ex = the_line->Point1.X;
00480   ey = the_line->Point1.Y;
00481 
00482   cx = the_arc->X;
00483   cy = the_arc->Y;
00484   if (arc_endpoint_is (the_arc, the_arc->StartAngle, x, y))
00485     {
00486       ChangeArcAngles (CURRENT, the_arc, the_arc->StartAngle + the_arc->Delta,
00487                        -the_arc->Delta);
00488     }
00489   else if (!arc_endpoint_is (the_arc, the_arc->StartAngle + the_arc->Delta,
00490                              x, y))
00491     {
00492 #if TRACE1
00493       printf ("arc not endpoints\n");
00494 #endif
00495       return 1;
00496     }
00497 
00498   if (within (cx, cy, ex, ey, the_arc->Width * 2))
00499     {
00500 #if TRACE1
00501       printf ("line ends inside arc\n");
00502 #endif
00503       return 1;
00504     }
00505 
00506   if (the_arc->Delta > 0)
00507     arc_angle = the_arc->StartAngle + the_arc->Delta + 90;
00508   else
00509     arc_angle = the_arc->StartAngle + the_arc->Delta - 90;
00510   base_angle = r2d (atan2 (ey - cy, cx - ex));
00511 
00512   tangent = r2d (acos (the_arc->Width / Distance (cx, cy, ex, ey)));
00513 
00514 #if TRACE1
00515   line_angle = r2d (atan2 (ey - y, x - ex));
00516   rel_angle = line_angle - arc_angle;
00517   printf ("arc %g line %g rel %g base %g\n", arc_angle, line_angle, rel_angle,
00518           base_angle);
00519   printf ("tangent %g\n", tangent);
00520 
00521   printf ("arc was start %ld end %ld\n", the_arc->StartAngle,
00522           the_arc->StartAngle + the_arc->Delta);
00523 #endif
00524 
00525   if (the_arc->Delta > 0)
00526     arc_angle = base_angle - tangent;
00527   else
00528     arc_angle = base_angle + tangent;
00529 #if TRACE1
00530   printf ("new end angle %g\n", arc_angle);
00531 #endif
00532 
00533   new_delta_angle = arc_angle - the_arc->StartAngle;
00534   if (new_delta_angle > 180)
00535     new_delta_angle -= 360;
00536   if (new_delta_angle < -180)
00537     new_delta_angle += 360;
00538   ChangeArcAngles (CURRENT, the_arc, the_arc->StartAngle, new_delta_angle);
00539 
00540 #if TRACE1
00541   printf ("arc now start %ld end %ld\n", the_arc->StartAngle,
00542           the_arc->StartAngle + new_delta_angle);
00543 #endif
00544 
00545   arc_angle = the_arc->StartAngle + the_arc->Delta;
00546   x = the_arc->X - the_arc->Width * cos (d2r (arc_angle)) + 0.5;
00547   y = the_arc->Y + the_arc->Height * sin (d2r (arc_angle)) + 0.5;
00548 
00549   MoveObject (LINEPOINT_TYPE, CURRENT, the_line, &(the_line->Point2),
00550               x - the_line->Point2.X, y - the_line->Point2.Y);
00551 
00552   gui->invalidate_all ();
00553   IncrementUndoSerialNumber ();
00554 
00555   return 1;
00556 }
00557 
00558 /*                          Global Puller                                    */
00559 
00560 static const char globalpuller_syntax[] =
00561 "GlobalPuller()";
00562 
00563 static const char globalpuller_help[] =
00564 "Pull all traces tight.";
00565 
00566 /* %start-doc actions GlobalPuller
00567 
00568 %end-doc */
00569 
00570 /* Ok, here's the deal.  We look for the intersection of two traces.
00571    The triangle formed by those traces is searched for things we need
00572    to avoid. From the other two corners of the triangle, we compute
00573    the angle to each obstacle, and remember the ones closest to the
00574    start angles.  If the two traces hit the same obstacle, we put in
00575    the arc and we're done.  Else, we bring the traces up to the
00576    obstacles and start again.
00577 
00578    Note that we assume each start point is a tangent to an arc.  We
00579    start with a radius of zero, but future steps use the arcs we
00580    create as we go.
00581 
00582    For obstacles, we list each round pin, pad, via, and line/arc
00583    endpoints as points with a given radius.  For each square pin, pad,
00584    via, and polygon points, we list each corner with a zero radius.
00585    We also list arcs from their centerpoint.
00586 
00587    We don't currently do anything to move vias, or intersections of
00588    three or more traces.  In the future, three-way intersections will
00589    be handles similarly to two-way - calculate the range of angles
00590    valid from each of the three other endpoints, choose the angle
00591    closest to making 120 degree angles at the center.  For four-way or
00592    more intersections, we break them up into multiple three-way
00593    intersections.
00594 
00595    For simplicity, we only do the current layer at this time.  We will
00596    also edit the lines and arcs in place so that the intersection is
00597    always on the second point, and the other ends are always at
00598    start+delta for arcs.
00599 
00600    We also defer intersections which are blocked by other
00601    intersections yet to be moved; the idea is to wait until those have
00602    been moved so we don't end up with arcs that no longer wrap around
00603    things.  At a later point, we may choose to pull arced corners in
00604    also.
00605 
00606    You'll see lots of variables of the form "foo_sign" which keep
00607    track of which way things are pointing.  This is because everything
00608    is relative to corners and arcs, not absolute directions.
00609 */
00610 
00611 static int nloops, npulled;
00612 
00613 static void
00614 status ()
00615 {
00616   Message ("%6d loops, %d pulled   \r", nloops, npulled);
00617 }
00618 
00623 typedef struct End {
00624   /* These point to "multi_next" if there are more than one.  */
00625   struct Extra *next;
00626   void *pin;
00627   unsigned char in_pin:1;
00628   unsigned char at_pin:1;
00629   unsigned char is_pad:1;
00630   unsigned char pending:1; /* set if this may be moved later */
00631   Coord x, y; /* arc endpoint */
00632   /* If not NULL, points to End with pending==1 we're blocked on. */
00633   struct End *waiting_for;
00634 } End;
00635 
00636 typedef struct Extra {
00637   End start;
00638   End end;
00639   unsigned char found:1;
00640   unsigned char deleted:1;
00641   int type;
00642   union {
00643     LineType *line;
00644     ArcType *arc;
00645   } parent;
00646 } Extra;
00647 
00648 static Extra multi_next;
00649 static GHashTable *lines;
00650 static GHashTable *arcs;
00651 static int did_something;
00652 static int current_is_top, current_is_bottom;
00653 
00654 /* If set, these are the pins/pads/vias that this path ends on.  */
00655 /* static void *start_pin_pad, *end_pin_pad; */
00656 
00657 #if TRACE1
00658 static void trace_paths ();
00659 #endif
00660 static void mark_line_for_deletion (LineType *);
00661 
00662 #define LINE2EXTRA(l)    ((Extra *)g_hash_table_lookup (lines, l))
00663 #define ARC2EXTRA(a)     ((Extra *)g_hash_table_lookup (arcs, a))
00664 #define EXTRA2LINE(e)    (e->parent.line)
00665 #define EXTRA2ARC(e)     (e->parent.arc)
00666 #define EXTRA_IS_LINE(e) (e->type == LINE_TYPE)
00667 #define EXTRA_IS_ARC(e)  (e->type == ARC_TYPE)
00668 
00669 static void
00670 unlink_end (Extra *x, Extra **e)
00671 {
00672   if (*e)
00673     {
00674       if ((*e)->start.next == x)
00675         {
00676 #if TRACE1
00677           printf("%d: unlink_end, was %p\n", __LINE__, (*e)->start.next);
00678 #endif
00679           (*e)->start.next = &multi_next;
00680         }
00681       if ((*e)->end.next == x)
00682         {
00683 #if TRACE1
00684           printf("%d: unlink_end, was %p\n", __LINE__, (*e)->start.next);
00685 #endif
00686           (*e)->end.next = &multi_next;
00687         }
00688     }
00689 #if TRACE1
00690   printf("%d: unlink_end, was %p\n", __LINE__, (*e));
00691 #endif
00692   (*e) = &multi_next;
00693 }
00694 
00695 #if TRACE1
00696 
00697 static void
00698 clear_found_cb (AnyObjectType *ptr, Extra *extra, void *userdata)
00699 {
00700   extra->found = 0;
00701 }
00702 
00703 static void
00704 clear_found ()
00705 {
00706   g_hash_table_foreach (lines, (GHFunc)clear_found_cb, NULL);
00707   g_hash_table_foreach (arcs, (GHFunc)clear_found_cb, NULL);
00708 }
00709 #endif
00710 
00711 static void
00712 fix_arc_extra (ArcType *a, Extra *e)
00713 {
00714 #if TRACE1
00715   printf("new arc angles %ld %ld\n", a->StartAngle, a->Delta);
00716 #endif
00717   e->start.x = a->X - (a->Width * cos (d2r (a->StartAngle)) + 0.5);
00718   e->start.y = a->Y + (a->Height * sin (d2r (a->StartAngle)) + 0.5);
00719   e->end.x = a->X - (a->Width * cos (d2r (a->StartAngle+a->Delta)) + 0.5);
00720   e->end.y = a->Y + (a->Height * sin (d2r (a->StartAngle+a->Delta)) + 0.5);
00721 #if TRACE1
00722   pcb_printf("new X,Y is %#mD to %#mD\n", e->start.x, e->start.y, e->end.x, e->end.y);
00723 #endif
00724 }
00725 
00726 typedef struct {
00727   void *me;
00728   Coord x, y;
00729   int is_arc;
00730   Extra **extra_ptr;
00731 } FindPairCallbackStruct;
00732 
00733 #define NEAR(a,b) ((a) <= (b) + 2 && (a) >= (b) - 2)
00734 
00735 static int
00736 find_pair_line_callback (const BoxType * b, void *cl)
00737 {
00738   LineType *line = (LineType *) b;
00739 #if TRACE1
00740   Extra *e = LINE2EXTRA (line);
00741 #endif
00742   FindPairCallbackStruct *fpcs = (FindPairCallbackStruct *) cl;
00743 
00744   if (line == fpcs->me)
00745     return 0;
00746 #ifdef CHECK_LINE_PT_NEG
00747   if (line->Point1.X < 0)
00748     abort1();
00749 #endif
00750 #if TRACE1
00751   pcb_printf(" - %p line %#mD or %#mD\n", e, line->Point1.X, line->Point1.Y,
00752          line->Point2.X, line->Point2.Y);
00753 #endif
00754   if ((NEAR (line->Point1.X, fpcs->x) && NEAR (line->Point1.Y, fpcs->y))
00755       || (NEAR (line->Point2.X, fpcs->x) && NEAR (line->Point2.Y, fpcs->y)))
00756     {
00757       if (* fpcs->extra_ptr)
00758         {
00759 #if TRACE1
00760           printf("multiple, was %p\n", *fpcs->extra_ptr);
00761 #endif
00762           *fpcs->extra_ptr = & multi_next;
00763         }
00764       else
00765         {
00766           *fpcs->extra_ptr = LINE2EXTRA (line);
00767 #if TRACE1
00768           printf(" - next now %p\n", *fpcs->extra_ptr);
00769 #endif
00770         }
00771     }
00772   return 0;
00773 }
00774 
00775 static int
00776 find_pair_arc_callback (const BoxType * b, void *cl)
00777 {
00778   ArcType *arc = (ArcType *) b;
00779   Extra *e = ARC2EXTRA (arc);
00780   FindPairCallbackStruct *fpcs = (FindPairCallbackStruct *) cl;
00781 
00782   if (arc == fpcs->me)
00783     return 0;
00784 #if TRACE1
00785   pcb_printf(" - %p arc %#mD or %#mD\n", e, e->start.x, e->start.y, e->end.x, e->end.y);
00786 #endif
00787   if ((NEAR (e->start.x, fpcs->x) && NEAR (e->start.y, fpcs->y))
00788       || (NEAR (e->end.x, fpcs->x) && NEAR (e->end.y, fpcs->y)))
00789     {
00790       if (* fpcs->extra_ptr)
00791         {
00792 #if TRACE1
00793           printf("multiple, was %p\n", *fpcs->extra_ptr);
00794 #endif
00795           *fpcs->extra_ptr = & multi_next;
00796         }
00797       else
00798         *fpcs->extra_ptr = e;
00799     }
00800   return 0;
00801 }
00802 
00803 static void
00804 find_pairs_1 (void *me, Extra **e, Coord x, Coord y)
00805 {
00806   FindPairCallbackStruct fpcs;
00807   BoxType b;
00808 
00809   if (*e)
00810     return;
00811 
00812   fpcs.me = me;
00813   fpcs.extra_ptr = e;
00814   fpcs.x = x;
00815   fpcs.y = y;
00816 #if TRACE1
00817   pcb_printf("looking for %#mD\n", x, y);
00818 #endif
00819   b.X1 = x - 10;
00820   b.X2 = x + 10;
00821   b.Y1 = y - 10;
00822   b.Y2 = y + 10;
00823   r_search(CURRENT->line_tree, &b, NULL, find_pair_line_callback, &fpcs);
00824   r_search(CURRENT->arc_tree, &b, NULL, find_pair_arc_callback, &fpcs);
00825 }
00826 
00827 static int
00828 check_point_in_pin (PinType *pin, Coord x, Coord y, End *e)
00829 {
00830   int inside_p;
00831   Coord t = (PIN_SIZE(pin)+1)/2;
00832   if (TEST_FLAG (SQUAREFLAG, pin))
00833     inside_p = (x >= pin->X - t && x <= pin->X + t
00834                 && y >= pin->Y - t && y <= pin->Y + t);
00835   else
00836     inside_p = (Distance (pin->X, pin->Y, x, y) <= t);
00837 
00838   if (inside_p)
00839     {
00840       e->in_pin = 1;
00841       if (pin->X == x && pin->Y == y)
00842         e->at_pin = 1;
00843       e->pin = pin;
00844       return 1;
00845     }
00846   return 0;
00847 }
00848 
00849 static int
00850 find_pair_pinline_callback (const BoxType * b, void *cl)
00851 {
00852   LineType *line = (LineType *) b;
00853   PinType *pin = (PinType *) cl;
00854   Extra *e = LINE2EXTRA (line);
00855   int hits;
00856 
00857 #ifdef CHECK_LINE_PT_NEG
00858   if (line->Point1.X < 0)
00859     abort1();
00860 #endif
00861 
00862   hits = check_point_in_pin (pin, line->Point1.X, line->Point1.Y, &(e->start));
00863   hits += check_point_in_pin (pin, line->Point2.X, line->Point2.Y, &(e->end));
00864 
00865   if (hits)
00866     return 0;
00867 
00868   /* See if the line passes through this pin.  */
00871   if (dist_lsp (line->Point1.X, line->Point1.Y,
00872                 line->Point2.X, line->Point2.Y,
00873                 pin->X, pin->Y) <= PIN_SIZE(pin)/2)
00874     {
00875 #if TRACE1
00876       pcb_printf("splitting line %#mD-%#mD because it passes through pin %#mD r%d\n",
00877              line->Point1.X, line->Point1.Y,
00878              line->Point2.X, line->Point2.Y,
00879               pin->X, pin->Y, PIN_SIZE(pin)/2);
00880 #endif
00881       unlink_end (e, &e->start.next);
00882       unlink_end (e, &e->end.next);
00883     }
00884   return 0;
00885 }
00886 
00887 static int
00888 find_pair_pinarc_callback (const BoxType * b, void *cl)
00889 {
00890   ArcType *arc = (ArcType *) b;
00891   PinType *pin = (PinType *) cl;
00892   Extra *e = ARC2EXTRA (arc);
00893   int hits;
00894 
00895   hits = check_point_in_pin (pin, e->start.x, e->start.y, &(e->start));
00896   hits += check_point_in_pin (pin, e->end.x, e->end.y, &(e->end));
00897   return 0;
00898 }
00899 
00900 static int
00901 check_point_in_pad (PadType *pad, Coord x, Coord y, End *e)
00902 {
00903   int inside_p;
00904   Coord t;
00905 
00906   pcb_printf("pad %#mD - %#mD t %#mS  vs  %#mD\n", pad->Point1.X, pad->Point1.Y,
00907          pad->Point2.X, pad->Point2.Y, pad->Thickness, x, y);
00908   t = (pad->Thickness+1)/2;
00909   if (TEST_FLAG (SQUAREFLAG, pad))
00910     {
00911     inside_p = (x >= MIN (pad->Point1.X - t, pad->Point2.X - t)
00912                 && x <= MAX (pad->Point1.X + t, pad->Point2.X + t)
00913                 && y >= MIN (pad->Point1.Y - t, pad->Point2.Y - t)
00914                 && y <= MAX (pad->Point1.Y + t, pad->Point2.Y + t));
00915     printf(" - inside_p = %d\n", inside_p);
00916     }
00917   else
00918     {
00919       if (pad->Point1.X == pad->Point2.X)
00920         {
00921           inside_p = (x >= pad->Point1.X - t
00922                       && x <= pad->Point1.X + t
00923                       && y >= MIN (pad->Point1.Y, pad->Point2.Y)
00924                       && y <= MAX (pad->Point1.Y, pad->Point2.Y));
00925         }
00926       else
00927         {
00928           inside_p = (x >= MIN (pad->Point1.X, pad->Point2.X)
00929                       && x <= MAX (pad->Point1.X, pad->Point2.X)
00930                       && y >= pad->Point1.Y - t
00931                       && y <= pad->Point1.Y + t);
00932         }
00933       if (!inside_p)
00934         {
00935           if (Distance (pad->Point1.X, pad->Point1.Y, x, y) <= t
00936               || Distance (pad->Point2.X, pad->Point2.Y, x, y) <= t)
00937             inside_p = 1;
00938         }
00939     }
00940 
00941   if (inside_p)
00942     {
00943       e->in_pin = 1;
00944       if (pad->Point1.X == x && pad->Point1.Y == y)
00945         e->at_pin = 1;
00946       if (pad->Point2.X == x && pad->Point2.Y == y)
00947         e->at_pin = 1;
00948       e->pin = pad;
00949       e->is_pad = 1;
00950       return 1;
00951     }
00952   return 0;
00953 }
00954 
00955 static int
00956 find_pair_padline_callback (const BoxType * b, void *cl)
00957 {
00958   LineType *line = (LineType *) b;
00959   PadType *pad = (PadType *) cl;
00960   Extra *e = LINE2EXTRA (line);
00961   int hits;
00962   double t;
00963   int intersect;
00964   double p1_d, p2_d;
00965 
00966   if (TEST_FLAG (ONSOLDERFLAG, pad))
00967     {
00968       if (!current_is_bottom)
00969         return 0;
00970     }
00971   else
00972     {
00973       if (!current_is_top)
00974         return 0;
00975     }
00976 
00977 #ifdef CHECK_LINE_PT_NEG
00978   if (line->Point1.X < 0)
00979     abort1();
00980 #endif
00981 
00982   hits = check_point_in_pad (pad, line->Point1.X, line->Point1.Y, &(e->start));
00983   hits += check_point_in_pad (pad, line->Point2.X, line->Point2.Y, &(e->end));
00984 
00985   if (hits)
00986     return 0;
00987 
00988   /* Ok, something strange.  The line intersects our space, but
00989      doesn't end in our space.  See if it just passes through us, and
00990      mark it anyway.  */
00991 
00992   t = (pad->Thickness + 1)/2;
00995   intersect = intersection_of_linesegs (pad->Point1.X, pad->Point1.Y,
00996                                          pad->Point2.X, pad->Point2.Y,
00997                                          line->Point1.X, line->Point1.Y,
00998                                          line->Point2.X, line->Point2.Y,
00999                                          NULL, NULL);
01000   p1_d = dist_lsp(line->Point1.X, line->Point1.Y,
01001                   line->Point2.X, line->Point2.Y,
01002                   pad->Point1.X, pad->Point1.Y);
01003   p2_d = dist_lsp(line->Point1.X, line->Point1.Y,
01004                   line->Point2.X, line->Point2.Y,
01005                   pad->Point2.X, pad->Point2.Y);
01006 
01007   if (intersect || p1_d < t || p2_d < t)
01008     {
01009       /* It does.  */
01011 #if TRACE1
01012       pcb_printf("splitting line %#mD-%#mD because it passes through pad %#mD-%#mD r %#mS\n",
01013              line->Point1.X, line->Point1.Y,
01014              line->Point2.X, line->Point2.Y,
01015              pad->Point1.X, pad->Point1.Y,
01016              pad->Point2.X, pad->Point2.Y,
01017              pad->Thickness/2);
01018 #endif
01019       unlink_end (e, &e->start.next);
01020       unlink_end (e, &e->end.next);
01021     }
01022 
01023   return 0;
01024 }
01025 
01026 static int
01027 find_pair_padarc_callback (const BoxType * b, void *cl)
01028 {
01029   ArcType *arc = (ArcType *) b;
01030   PadType *pad = (PadType *) cl;
01031   Extra *e = ARC2EXTRA (arc);
01032   int hits;
01033 
01034   if (TEST_FLAG (ONSOLDERFLAG, pad))
01035     {
01036       if (!current_is_bottom)
01037         return 0;
01038     }
01039   else
01040     {
01041       if (!current_is_top)
01042         return 0;
01043     }
01044 
01045   hits = check_point_in_pad (pad, e->start.x, e->start.y, &(e->start));
01046   hits += check_point_in_pad (pad, e->end.x, e->end.y, &(e->end));
01047   return 0;
01048 }
01049 
01050 static void
01051 null_multi_next_ends (AnyObjectType *ptr, Extra *extra, void *userdata)
01052 {
01053   if (extra->start.next == &multi_next)
01054     extra->start.next = NULL;
01055 
01056   if (extra->end.next == &multi_next)
01057     extra->end.next = NULL;
01058 }
01059 
01060 static Extra *
01061 new_line_extra (LineType *line)
01062 {
01063   Extra *extra = g_slice_new0 (Extra);
01064   g_hash_table_insert (lines, line, extra);
01065   extra->parent.line = line;
01066   extra->type = LINE_TYPE;
01067   return extra;
01068 }
01069 
01070 static Extra *
01071 new_arc_extra (ArcType *arc)
01072 {
01073   Extra *extra = g_slice_new0 (Extra);
01074   g_hash_table_insert (arcs, arc, extra);
01075   extra->parent.arc = arc;
01076   extra->type = ARC_TYPE;
01077   return extra;
01078 }
01079 
01080 static void
01081 find_pairs ()
01082 {
01083   ARC_LOOP (CURRENT); {
01084     Extra *e = new_arc_extra (arc);
01085     fix_arc_extra (arc, e);
01086   } END_LOOP;
01087 
01088   LINE_LOOP (CURRENT); {
01089     new_line_extra (line);
01090   } END_LOOP;
01091 
01092   LINE_LOOP (CURRENT); {
01093     Extra *e = LINE2EXTRA (line);
01094     if (line->Point1.X >= 0)
01095       {
01096         find_pairs_1 (line, & e->start.next, line->Point1.X, line->Point1.Y);
01097         find_pairs_1 (line, & e->end.next, line->Point2.X, line->Point2.Y);
01098       }
01099   } END_LOOP;
01100 
01101   ARC_LOOP (CURRENT); {
01102     Extra *e = ARC2EXTRA (arc);
01103     if (!e->deleted)
01104       {
01105         find_pairs_1 (arc, & e->start.next, e->start.x, e->start.y);
01106         find_pairs_1 (arc, & e->end.next, e->end.x, e->end.y);
01107       }
01108   } END_LOOP;
01109 
01110   ALLPIN_LOOP (PCB->Data); {
01111     BoxType box;
01112     box.X1 = pin->X - PIN_SIZE(pin)/2;
01113     box.Y1 = pin->Y - PIN_SIZE(pin)/2;
01114     box.X2 = pin->X + PIN_SIZE(pin)/2;
01115     box.Y2 = pin->Y + PIN_SIZE(pin)/2;
01116     r_search (CURRENT->line_tree, &box, NULL, find_pair_pinline_callback, pin);
01117     r_search (CURRENT->arc_tree, &box, NULL, find_pair_pinarc_callback, pin);
01118   } ENDALL_LOOP;
01119 
01120   VIA_LOOP (PCB->Data); {
01121     BoxType box;
01122     box.X1 = via->X - PIN_SIZE(via)/2;
01123     box.Y1 = via->Y - PIN_SIZE(via)/2;
01124     box.X2 = via->X + PIN_SIZE(via)/2;
01125     box.Y2 = via->Y + PIN_SIZE(via)/2;
01126     r_search (CURRENT->line_tree, &box, NULL, find_pair_pinline_callback, via);
01127     r_search (CURRENT->arc_tree, &box, NULL, find_pair_pinarc_callback, via);
01128   } END_LOOP;
01129 
01130   ALLPAD_LOOP (PCB->Data); {
01131     BoxType box;
01132     box.X1 = MIN(pad->Point1.X, pad->Point2.X) - pad->Thickness/2;
01133     box.Y1 = MIN(pad->Point1.Y, pad->Point2.Y) - pad->Thickness/2;
01134     box.X2 = MAX(pad->Point1.X, pad->Point2.X) + pad->Thickness/2;
01135     box.Y2 = MAX(pad->Point1.Y, pad->Point2.Y) + pad->Thickness/2;
01136     r_search (CURRENT->line_tree, &box, NULL, find_pair_padline_callback, pad);
01137     r_search (CURRENT->arc_tree, &box, NULL, find_pair_padarc_callback, pad);
01138     
01139   } ENDALL_LOOP;
01140 
01141   g_hash_table_foreach (lines, (GHFunc)null_multi_next_ends, NULL);
01142   g_hash_table_foreach (arcs, (GHFunc)null_multi_next_ends, NULL);
01143 }
01144 
01145 #define PROP_NEXT(e,n,f)                \
01146       if (f->next->start.next == e) {   \
01147         e = f->next;                    \
01148         n = & e->start;                 \
01149         f = & e->end;                   \
01150       } else {                          \
01151         e = f->next;                    \
01152         n = & e->end;                   \
01153         f = & e->start; }
01154 
01155 static void
01156 propogate_ends_at (Extra *e, End *near, End *far)
01157 {
01158   while (far->in_pin && far->pin == near->pin)
01159     {
01160       far->in_pin = 0;
01161       if (!far->next)
01162         return;
01163       PROP_NEXT (e, near, far);
01164       near->in_pin = 0;
01165     }
01166 }
01167 
01168 static void
01169 propogate_end_pin (Extra *e, End *near, End *far)
01170 {
01171   void *pinpad = near->pin;
01172   int ispad = near->is_pad;
01173   while (far->next)
01174     {
01175       PROP_NEXT (e, near, far);
01176       if (near->pin == pinpad)
01177         break;
01178       near->pin = pinpad;
01179       near->is_pad = ispad;
01180     }
01181 }
01182 
01183 static void
01184 propogate_end_step1_cb (AnyObjectType *ptr, Extra *extra, void *userdata)
01185 {
01186   if (extra->start.next != NULL &&
01187       extra->start.next == extra->end.next)
01188     {
01189       extra->end.next = NULL;
01190       mark_line_for_deletion ((LineType *)ptr);
01191     }
01192 
01193   if (extra->start.at_pin)
01194     propogate_ends_at (extra, &extra->start, &extra->end);
01195 
01196   if (extra->end.at_pin)
01197     propogate_ends_at (extra, &extra->end, &extra->start);
01198 }
01199 
01200 static void
01201 propogate_end_step2_cb (AnyObjectType *ptr, Extra *extra, void *userdata)
01202 {
01203   if (extra->start.in_pin)
01204     {
01205 #if TRACE1
01206       printf("MULTI at %d: was %p\n", __LINE__, extra->start.next);
01207 #endif
01208       extra->start.next = NULL;
01209     }
01210   if (extra->end.in_pin)
01211     {
01212 #if TRACE1
01213       printf("MULTI at %d: was %p\n", __LINE__, extra->end.next);
01214 #endif
01215       extra->end.next = NULL;
01216     }
01217 }
01218 
01219 static void
01220 propogate_end_step3_cb (AnyObjectType *ptr, Extra *extra, void *userdata)
01221 {
01222   if (extra->start.next)
01223     propogate_end_pin (extra, &extra->end, &extra->start);
01224   if (extra->end.next)
01225     propogate_end_pin (extra, &extra->start, &extra->end);
01226 }
01227 
01228 static void
01229 propogate_ends ()
01230 {
01231   /* First, shut of "in pin" when we have an "at pin".  We also clean
01232      up zero-length lines.  */
01233   g_hash_table_foreach (lines, (GHFunc)propogate_end_step1_cb, NULL);
01234 
01235   /* Now end all paths at pins/pads.  */
01236   g_hash_table_foreach (lines, (GHFunc)propogate_end_step2_cb, NULL);
01237 
01238   /* Now, propogate the pin/pad/vias along paths.  */
01239   g_hash_table_foreach (lines, (GHFunc)propogate_end_step3_cb, NULL);
01240 }
01241 
01242 static Extra *last_pextra = 0;
01243 
01244 static void
01245 print_extra (Extra *e, Extra *prev)
01246 {
01247   int which = 0;
01248   if (e->start.next == last_pextra)
01249     which = 1;
01250   else if (e->end.next == last_pextra)
01251     which = 2;
01252   switch (which) {
01253   case 0:
01254     printf("%10p %10p %10p :", e, e->start.next, e->end.next);
01255     break;
01256   case 1:
01257     printf("%10p \033[33m%10p\033[0m %10p :", e, e->start.next, e->end.next);
01258     break;
01259   case 2:
01260     printf("%10p %10p \033[33m%10p\033[0m :", e, e->start.next, e->end.next);
01261     break;
01262   }
01263   last_pextra = e;
01264   printf(" %c%c",
01265          e->deleted ? 'd' : '-',
01266          e->found   ? 'f' : '-');
01267   printf(" s:%s%s%s%s",
01268          e->start.in_pin ? "I" : "-",
01269          e->start.at_pin ? "A" : "-",
01270          e->start.is_pad ? "P" : "-",
01271          e->start.pending ? "p" : "-");
01272   printf(" e:%s%s%s%s ",
01273          e->end.in_pin ? "I" : "-",
01274          e->end.at_pin ? "A" : "-",
01275          e->end.is_pad ? "P" : "-",
01276          e->end.pending ? "p" : "-");
01277          
01278   if (EXTRA_IS_LINE (e))
01279     {
01280       LineType *line = EXTRA2LINE (e);
01281       pcb_printf(" %p L %#mD-%#mD", line, line->Point1.X, line->Point1.Y, line->Point2.X, line->Point2.Y);
01282       printf("  %s %p %s %p\n",
01283              e->start.is_pad ? "pad" : "pin", e->start.pin,
01284              e->end.is_pad ? "pad" : "pin", e->end.pin);
01285     }
01286   else if (EXTRA_IS_ARC (e))
01287     {
01288       ArcType *arc = EXTRA2ARC (e);
01289       pcb_printf(" %p A %#mD-%#mD", arc, e->start.x, e->start.y, e->end.x, e->end.y);
01290       pcb_printf(" at %#mD ang %ld,%ld\n", arc->X, arc->Y, arc->StartAngle, arc->Delta);
01291     }
01292   else if (e == &multi_next)
01293     {
01294       printf("-- Multi-next\n");
01295     }
01296   else
01297     {
01298       printf("-- Unknown extra: %p\n", e);
01299     }
01300 }
01301 
01302 #if TRACE1
01303 static void
01304 trace_path (Extra *e)
01305 {
01306   Extra *prev = 0;
01307   if ((e->start.next && e->end.next)
01308       || (!e->start.next && !e->end.next))
01309     return;
01310   if (e->found)
01311     return;
01312   printf("- path -\n");
01313   last_pextra = 0;
01314   while (e)
01315     {
01316       e->found = 1;
01317       print_extra (e, prev);
01318       if (e->start.next == prev)
01319         {
01320           prev = e;
01321           e = e->end.next;
01322         }
01323       else
01324         {
01325           prev = e;
01326           e = e->start.next;
01327         }
01328     }
01329 }
01330 
01331 static void
01332 trace_paths ()
01333 {
01334   Extra *e;
01335 
01336   clear_found ();
01337   LINE_LOOP (CURRENT); {
01338     e = LINE2EXTRA (line);
01339     trace_path (e);
01340   } END_LOOP;
01341   ARC_LOOP (CURRENT); {
01342     e = ARC2EXTRA (arc);
01343     trace_path (e);
01344   } END_LOOP;
01345 }
01346 #endif
01347 
01348 static void
01349 reverse_line (LineType *line)
01350 {
01351   Extra *e = LINE2EXTRA (line);
01352   Coord x, y;
01353   End etmp;
01354 
01355   x = line->Point1.X;
01356   y = line->Point1.Y;
01357 #if 1
01358   MoveObject (LINEPOINT_TYPE, CURRENT, line, &(line->Point1),
01359               line->Point2.X - line->Point1.X,
01360               line->Point2.Y - line->Point1.Y);
01361   MoveObject (LINEPOINT_TYPE, CURRENT, line, &(line->Point2),
01362               x - line->Point2.X,
01363               y - line->Point2.Y);
01364 #else
01365   /* In theory, we should be using the above so that undo works.  */
01366   line->Point1.X = line->Point2.X;
01367   line->Point1.Y = line->Point2.Y;
01368   line->Point2.X = x;
01369   line->Point2.Y = y;
01370 #endif
01371   memcpy (&etmp, &e->start, sizeof (End));
01372   memcpy (&e->start, &e->end, sizeof (End));
01373   memcpy (&e->end, &etmp, sizeof (End));
01374 }
01375 
01376 static void
01377 reverse_arc (ArcType *arc)
01378 {
01379   Extra *e = ARC2EXTRA (arc);
01380   End etmp;
01381 
01382 #if 1
01383   ChangeArcAngles (CURRENT, arc,
01384                    arc->StartAngle + arc->Delta, -arc->Delta);
01385 #else
01386   /* Likewise, see above.  */
01387   arc->StartAngle += arc->Delta;
01388   arc->Delta *= -1;
01389 #endif
01390   memcpy (&etmp, &e->start, sizeof (End));
01391   memcpy (&e->start, &e->end, sizeof (End));
01392   memcpy (&e->end, &etmp, sizeof (End));
01393 }
01394 
01395 static void
01396 expand_box (BoxType *b, Coord x, Coord y, Coord t)
01397 {
01398   b->X1 = MIN (b->X1, x-t);
01399   b->X2 = MAX (b->X2, x+t);
01400   b->Y1 = MIN (b->Y1, y-t);
01401   b->Y2 = MAX (b->Y2, y+t);
01402 }
01403 
01404 /* ---------------------------------------------------------------------- */
01405 /* These are the state variables for the intersection we're currently
01406    working on. */
01407 
01408 /* what we're working with */
01409 static ArcType *start_arc;
01410 static LineType *start_line;
01411 static LineType *end_line;
01412 static ArcType *end_arc;
01413 static Extra *start_extra, *end_extra;
01414 static Extra *sarc_extra, *earc_extra;
01415 static void *start_pinpad, *end_pinpad;
01416 static Coord thickness;
01417 
01418 /* Pre-computed values.  Note that all values are computed according
01419    to CARTESIAN coordinates, not PCB coordinates.  Do an up-down board
01420    flip before wrapping your brain around the math.  */
01421 
01422 /* se_sign is positive when you make a right turn going from start to end. */
01423 /* sa_sign is positive when start's arc is on the same side of start as end.  */
01424 /* ea_sign is positive when end's arc is on the same side of end as start.  */
01425 /* sa_sign and ea_sign may be zero if there's no arc.  */
01426 static double se_sign, sa_sign, ea_sign;
01427 
01428 static double best_angle, start_angle, end_dist;
01429 /* arc radii are positive when they're on the same side as the things
01430    we're interested in. */
01431 static Coord sa_r, ea_r;
01432 static Coord sa_x, sa_y; /* start "arc" point */
01433 
01434 /* what we've found so far */
01435 static Coord fx, fy, fr;
01436 static int fp;
01437 static End *fp_end;
01438 static double fa; /* relative angle */
01439 
01440 #define gp_point(x,y,t,e) gp_point_2(x,y,t,e,0,0,__FUNCTION__)
01441 
01442 static int
01443 gp_point_force (Coord x, Coord y, Coord t, End *e, int esa, int eda, int force, const char *name)
01444 {
01445   double r, a, d;
01446   Coord scx, scy, sr;
01447   double base_angle, rel_angle, point_angle;
01448 
01449 #if TRACE1
01450   pcb_printf("\033[34mgp_point_force %#mD %#mS via %s\033[0m\n", x, y, t, name);
01451 #endif
01452 
01453   if (start_arc)
01454     {
01455       scx = start_arc->X;
01456       scy = start_arc->Y;
01457       sr = start_arc->Width;
01458     }
01459   else
01460     {
01461       scx = start_line->Point1.X;
01462       scy = start_line->Point1.Y;
01463       sr = 0;
01464     }
01465 
01466   r = t + thickness;
01467 
01468   /* See if the point is inside our start arc. */
01469   d = Distance (scx, scy, x, y);
01470 #if TRACE1
01471   pcb_printf("%f = dist #mD to %#mD\n", d, scx, scy, x, y);
01472   pcb_printf("sr %#mS r %f d %f\n", sr, r, d);
01473 #endif
01474   if (d  < sr - r)
01475     {
01476 #if TRACE1
01477       printf("inside start arc, %f < %f\n", d, sr-r);
01478 #endif
01479       return 0;
01480     }
01481   if (sr == 0 && d < r)
01482     {
01483 #if TRACE1
01484       printf("start is inside arc, %f < %f\n", d, r);
01485 #endif
01486       return 0;
01487     }
01488 
01489   /* Now for the same tricky math we needed for the single puller.
01490      sr and r are the radii for the two points scx,scy and x,y.  */
01491 
01492   /* angle between points (NOT pcb arc angles) */
01493   base_angle = atan2 (y - scy, x - scx);
01494 #if TRACE1
01495   pcb_printf("%.1f = atan2 (%#mS-%#mS = %#mS, %#mS-%#mS = %#mS)\n",
01496              r2d(base_angle), y, scy, y-scy, x, scx, x-scx);
01497 #endif
01498 
01499   if ((sa_sign * sr - r) / d > 1
01500       || (sa_sign * sr - r) / d < -1)
01501     return 0;
01502 
01503   /* Angle of tangent, relative to the angle between point centers.  */
01504   rel_angle = se_sign * asin ((sa_sign * sr - r) / d);
01505 #if TRACE1
01506   printf("%.1f = %d * asin ((%d * %d - %f) / %f)\n",
01507          r2d(rel_angle), (int)se_sign, (int)sa_sign, sr, r, d);
01508 #endif
01509 
01510   /* Absolute angle of tangent.  */
01511   point_angle = base_angle + rel_angle;
01512 #if TRACE1
01513   printf("base angle %.1f rel_angle %.1f point_angle %.1f\n",
01514          r2d(base_angle),
01515          r2d(rel_angle),
01516          r2d(point_angle));
01517 #endif
01518 
01519   if (eda)
01520     {
01521       /* Check arc angles */
01522       double pa = point_angle;
01523       double sa = d2r(180-esa);
01524       double da = d2r(-eda);
01525 
01526       if (da < 0)
01527         {
01528           sa = sa + da;
01529           da = -da;
01530         }
01531 
01532       pa -= se_sign * M_PI/2;
01533       while (sa+da < pa)
01534         sa += M_PI*2;
01535       while (sa > pa)
01536         sa -= M_PI*2;
01537       if (sa+da < pa)
01538         {
01539 #if TRACE1
01540           printf("arc doesn't apply: sa %.1f da %.1f pa %.1f\n",
01541                  r2d(sa), r2d(da), r2d(pa));
01542 #endif
01543           return 0;
01544         }
01545     }
01546 
01547   a = point_angle - start_angle;
01548   while (a > M_PI)
01549     a -= M_PI*2;
01550   while (a < -M_PI)
01551     a += M_PI*2;
01552 #if TRACE1
01553   printf(" - angle relative to S-E baseline is %.1f\n", r2d(a));
01554 #endif
01555 
01556   if (!force && a * se_sign < -0.007)
01557     {
01558       double new_r;
01559 #if TRACE1
01560       printf("skipping, would increase angle (%f * %f)\n", a, se_sign);
01561 #endif
01562       new_r = dist_lp (start_line->Point1.X, start_line->Point1.Y,
01563                        start_line->Point2.X, start_line->Point2.Y,
01564                        x, y);
01565 #if TRACE1
01566       pcb_printf("point %#mD dist %#mS vs thickness %#mS\n", x, y, new_r, thickness);
01567 #endif
01568       new_r -= thickness;
01569       new_r = (int)new_r - 1;
01570 #if TRACE1
01571       pcb_printf(" - new thickness %f old %#mS\n", new_r, t);
01572 #endif
01573       if (new_r < t)
01574         gp_point_force (x, y, new_r, e, esa, eda, 1, __FUNCTION__);
01575       return 0;
01576     }
01577 
01578 #if TRACE1
01579   printf("%f * %f < %f * %f ?\n", a, se_sign, best_angle, se_sign);
01580 #endif
01581   if (a * se_sign == best_angle * se_sign)
01582     {
01583       double old_d = Distance (start_line->Point1.X, start_line->Point1.Y,
01584                            fx, fy);
01585       double new_d = Distance (start_line->Point1.X, start_line->Point1.Y,
01586                            x, y);
01587       if (new_d > old_d)
01588         {
01589           best_angle = a;
01590           fx = x;
01591           fy = y;
01592           fr = r;
01593           fa = a;
01594           fp = e ? e->pending : 0;
01595           fp_end = e;
01596         }
01597     }
01598   else if (a * se_sign < best_angle * se_sign)
01599     {
01600       best_angle = a;
01601       fx = x;
01602       fy = y;
01603       fr = r;
01604       fa = a;
01605       fp = e ? e->pending : 0;
01606       fp_end = e;
01607     }
01608 
01609   return 1;
01610 }
01611 static int
01612 gp_point_2 (Coord x, Coord y, Coord t, End *e, int esa, int eda, const char *func)
01613 {
01614   double sc, ec;
01615   double sd, ed;
01616 
01617   if (x == sa_x && y ==sa_y)
01618     return 0;
01619 
01620 #if TRACE1
01621   pcb_printf("\033[34mgp_point %#mD %#mS via %s\033[0m\n", x, y, t, func);
01622 #endif
01623 
01624   /* There are two regions we care about.  For points inside our
01625      triangle, we check the crosses against start_line and end_line to
01626      make sure the point is "inside" the triangle.  For points on the
01627      other side of the s-e line of the triangle, we check the dots to
01628      make sure it's between our endpoints.  */
01629 
01630   /* See what side of the s-e line we're on */
01631   sc = cross2d (start_line->Point1.X, start_line->Point1.Y,
01632                 end_line->Point2.X, end_line->Point2.Y,
01633                 x, y);
01634 #if TRACE1
01635   printf("s-e cross = %f\n", sc);
01636 #endif
01637   if (t >= 0)
01638     {
01639       if (same_sign (sc, se_sign))
01640         {
01641           /* Outside, check dots.  */
01642 
01643           /* Ok, is it "in front of" our vectors? */
01644           sd = dot2d (start_line->Point1.X, start_line->Point1.Y,
01645                       end_line->Point2.X, end_line->Point2.Y,
01646                       x, y);
01647 #if TRACE1
01648           printf("sd = %f\n", sd);
01649 #endif
01650           if (sd <= 0)
01651             return 0;
01652 
01653           ed = dot2d (end_line->Point2.X, end_line->Point2.Y,
01654                       start_line->Point1.X, start_line->Point1.Y,
01655                       x, y);
01656 #if TRACE1
01657           printf("ed = %f\n", ed);
01658 #endif
01659           if (ed <= 0)
01660             return 0;
01661 
01662           sd = dist_lp (start_line->Point1.X, start_line->Point1.Y,
01663                         end_line->Point2.X, end_line->Point2.Y,
01664                         x, y);
01665           if (sd > t + thickness)
01666             return 0;
01667         }
01668       else
01669         {
01670           /* Inside, check crosses.  */
01671 
01672           /* First off, is it on the correct side of the start line? */
01673           sc = cross2d (start_line->Point1.X, start_line->Point1.Y,
01674                         start_line->Point2.X, start_line->Point2.Y,
01675                         x, y);
01676 #if TRACE1
01677           printf("sc = %f\n", sc);
01678 #endif
01679           if (! same_sign (sc, se_sign))
01680             return 0;
01681 
01682           /* Ok, is it on the correct side of the end line? */
01683           ec = cross2d (end_line->Point1.X, end_line->Point1.Y,
01684                         end_line->Point2.X, end_line->Point2.Y,
01685                         x, y);
01686 #if TRACE1
01687           printf("ec = %f\n", ec);
01688 #endif
01689           if (! same_sign (ec, se_sign))
01690             return 0;
01691         }
01692     }
01693 
01694 #if TRACE1
01695   printf("in range!\n");
01696 #endif
01697 
01698   return gp_point_force (x, y, t, e, esa, eda, 0, func);
01699 }
01700 
01701 static int
01702 gp_line_cb (const BoxType *b, void *cb)
01703 {
01704   const LineType *l = (LineType *) b;
01705   Extra *e = LINE2EXTRA(l);
01706   if (l == start_line || l == end_line)
01707     return 0;
01708   if (e->deleted)
01709     return 0;
01710 #ifdef CHECK_LINE_PT_NEG
01711   if (l->Point1.X < 0)
01712     abort1();
01713 #endif
01714   if (! e->start.next
01715       || ! EXTRA_IS_ARC (e->start.next))
01716     gp_point (l->Point1.X, l->Point1.Y, l->Thickness/2, &e->start);
01717   if (! e->end.next
01718       || ! EXTRA_IS_ARC (e->end.next))
01719     gp_point (l->Point2.X, l->Point2.Y, l->Thickness/2, &e->end);
01720   return 0;
01721 }
01722 
01723 static int
01724 gp_arc_cb (const BoxType *b, void *cb)
01725 {
01726   const ArcType *a = (ArcType *) b;
01727   Extra *e = ARC2EXTRA(a);
01728   if (a == start_arc || a == end_arc)
01729     return 0;
01730   if (e->deleted)
01731     return 0;
01732   gp_point_2 (a->X, a->Y, a->Width + a->Thickness/2, 0, a->StartAngle, a->Delta, __FUNCTION__);
01733   if (start_arc
01734       && a->X == start_arc->X
01735       && a->Y == start_arc->Y)
01736     return 0;
01737   if (end_arc
01738       && a->X != end_arc->X
01739       && a->Y != end_arc->Y)
01740      return 0;
01741 
01742   if (e->start.next || e->end.next)
01743     return 0;
01744     
01745   gp_point (e->start.x, e->start.y, a->Thickness/2, 0);
01746   gp_point (e->end.x, e->end.y, a->Thickness/2, 0);
01747   return 0;
01748 }
01749 
01750 static int
01751 gp_text_cb (const BoxType *b, void *cb)
01752 {
01753   const TextType *t = (TextType *) b;
01754   /* FIXME: drop in the actual text-line endpoints later. */
01755   gp_point (t->BoundingBox.X1, t->BoundingBox.Y1, 0, 0);
01756   gp_point (t->BoundingBox.X1, t->BoundingBox.Y2, 0, 0);
01757   gp_point (t->BoundingBox.X2, t->BoundingBox.Y2, 0, 0);
01758   gp_point (t->BoundingBox.X2, t->BoundingBox.Y1, 0, 0);
01759   return 0;
01760 }
01761 
01762 static int
01763 gp_poly_cb (const BoxType *b, void *cb)
01764 {
01765   int i;
01766   const PolygonType *p = (PolygonType *) b;
01767   for (i=0; i<p->PointN; i++)
01768     gp_point (p->Points[i].X, p->Points[i].Y, 0, 0);
01769   return 0;
01770 }
01771 
01772 static int
01773 gp_pin_cb (const BoxType *b, void *cb)
01774 {
01775   const PinType *p = (PinType *) b;
01776   Coord t2 = (PIN_SIZE(p)+1)/2;
01777 
01778   if (p == start_pinpad || p == end_pinpad)
01779     return 0;
01780 
01783   if (TEST_FLAG (SQUAREFLAG, p) || TEST_FLAG (OCTAGONFLAG, p))
01784     {
01785       gp_point (p->X - t2, p->Y - t2, 0, 0);
01786       gp_point (p->X - t2, p->Y + t2, 0, 0);
01787       gp_point (p->X + t2, p->Y + t2, 0, 0);
01788       gp_point (p->X + t2, p->Y - t2, 0, 0);
01789     }
01790   else
01791     {
01792       gp_point (p->X, p->Y, t2, 0);
01793     }
01794   return 0;
01795 }
01796 
01797 static int
01798 gp_pad_cb (const BoxType *b, void *cb)
01799 {
01800   const PadType *p = (PadType *) b;
01801   Coord t2 = (p->Thickness+1)/2;
01802 
01803   if (p == start_pinpad || p == end_pinpad)
01804     return 0;
01805 
01806   if (TEST_FLAG (ONSOLDERFLAG, p))
01807     {
01808       if (!current_is_bottom)
01809         return 0;
01810     }
01811   else
01812     {
01813       if (!current_is_top)
01814         return 0;
01815     }
01816 
01819   if (TEST_FLAG (SQUAREFLAG, p) || TEST_FLAG (OCTAGONFLAG, p))
01820     {
01821       if (p->Point1.X == p->Point2.X)
01822         {
01823           Coord y1 = MIN (p->Point1.Y, p->Point2.Y) - t2;
01824           Coord y2 = MAX (p->Point1.Y, p->Point2.Y) + t2;
01825 
01826           gp_point (p->Point1.X - t2, y1, 0, 0);
01827           gp_point (p->Point1.X - t2, y2, 0, 0);
01828           gp_point (p->Point1.X + t2, y1, 0, 0);
01829           gp_point (p->Point1.X + t2, y2, 0, 0);
01830         }
01831       else
01832         {
01833           Coord x1 = MIN (p->Point1.X, p->Point2.X) - t2;
01834           Coord x2 = MAX (p->Point1.X, p->Point2.X) + t2;
01835 
01836           gp_point (x1, p->Point1.Y - t2, 0, 0);
01837           gp_point (x2, p->Point1.Y - t2, 0, 0);
01838           gp_point (x1, p->Point1.Y + t2, 0, 0);
01839           gp_point (x2, p->Point1.Y + t2, 0, 0);
01840         }
01841     }
01842   else
01843     {
01844       gp_point (p->Point1.X, p->Point1.Y, t2, 0);
01845       gp_point (p->Point2.X, p->Point2.Y, t2, 0);
01846     }
01847   return 0;
01848 }
01849 
01850 static LineType *
01851 create_line (LineType *sample, Coord x1, Coord y1, Coord x2, Coord y2)
01852 {
01853 #if TRACE1
01854   Extra *e;
01855   pcb_printf("create_line from %#mD to %#mD\n", x1, y1, x2, y2);
01856 #endif
01857   LineType *line = CreateNewLineOnLayer (CURRENT, x1, y1, x2, y2,
01858                                            sample->Thickness, sample->Clearance, sample->Flags);
01859   AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
01860 
01861 #if TRACE1
01862   e =
01863 #endif
01864     new_line_extra (line);
01865 #if TRACE1
01866   printf(" - line extra is %p\n", e);
01867 #endif
01868   return line;
01869 }
01870 
01871 static ArcType *
01872 create_arc (LineType *sample, Coord x, Coord y, Coord r, Coord sa, Coord da)
01873 {
01874   Extra *e;
01875   ArcType *arc;
01876 
01877   if (r % 100 == 1)
01878     r--;
01879   if (r % 100 == 99)
01880     r++;
01881 #if TRACE1
01882   pcb_printf("create_arc at %#mD r %#mS sa %d delta %d\n", x, y, r, sa, da);
01883 #endif
01884   arc = CreateNewArcOnLayer (CURRENT, x, y, r, r, sa, da,
01885                                         sample->Thickness, sample->Clearance, sample->Flags);
01886   if (arc == 0)
01887     {
01888       arc = CreateNewArcOnLayer (CURRENT, x, y, r, r, sa, da*2,
01889                                  sample->Thickness, sample->Clearance, sample->Flags);
01890     }
01891   AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
01892 
01893   if (!arc)
01894     longjmp (abort_buf, 1);
01895 
01896   e = new_arc_extra (arc);
01897 #if TRACE1
01898   printf(" - arc extra is %p\n", e);
01899 #endif
01900   fix_arc_extra (arc, e);
01901   return arc;
01902 }
01903 
01904 static void
01905 unlink_extras (Extra *e)
01906 {
01907 #if TRACE1
01908   fprintf(stderr, "unlink %p\n", e);
01909   print_extra(e,0);
01910 #endif
01911   if (e->start.next)
01912     {
01913 #if TRACE1
01914       print_extra(e->start.next, 0);
01915 #endif
01916       if (e->start.next->start.next == e)
01917         {
01918 #if TRACE1
01919           fprintf(stderr, " - %p->start points to me\n", e->start.next);
01920 #endif
01921           e->start.next->start.next = e->end.next;
01922         }
01923       else if (e->start.next->end.next == e)
01924         {
01925 #if TRACE1
01926           fprintf(stderr, " - %p->end points to me\n", e->start.next);
01927 #endif
01928           e->start.next->end.next = e->end.next;
01929         }
01930       else
01931         {
01932           fprintf(stderr, " - %p doesn't point to me!\n", e->start.next);
01933           abort();
01934         }
01935     }
01936   if (e->end.next)
01937     {
01938 #if TRACE1
01939       print_extra(e->end.next, 0);
01940 #endif
01941       if (e->end.next->start.next == e)
01942         {
01943 #if TRACE1
01944           fprintf(stderr, " - %p->end points to me\n", e->end.next);
01945 #endif
01946           e->end.next->start.next = e->start.next;
01947         }
01948       else if (e->end.next->end.next == e)
01949         {
01950 #if TRACE1
01951           fprintf(stderr, " - %p->end points to me\n", e->end.next);
01952 #endif
01953           e->end.next->end.next = e->start.next;
01954         }
01955       else
01956         {
01957           fprintf(stderr, " - %p doesn't point to me!\n", e->end.next);
01958           abort();
01959         }
01960     }
01961   e->start.next = e->end.next = 0;
01962 }
01963 
01964 static void
01965 mark_line_for_deletion (LineType *l)
01966 {
01967   Extra *e = LINE2EXTRA(l);
01968   if (e->deleted)
01969     {
01970       fprintf(stderr, "double delete?\n");
01971       abort();
01972     }
01973   e->deleted = 1;
01974   unlink_extras (e);
01975 #if TRACE1
01976   pcb_printf("Marked line %p for deletion %#mD to %#mD\n",
01977          e, l->Point1.X, l->Point1.Y, l->Point2.X, l->Point2.Y);
01978 #endif
01979 #if 0
01980   if (l->Point1.X < 0)
01981     {
01982       fprintf(stderr, "double neg move?\n");
01983       abort();
01984     }
01985   MoveObject (LINEPOINT_TYPE, CURRENT, l, &(l->Point1),
01986               -1 - l->Point1.X,
01987               -1 - l->Point1.Y);
01988   MoveObject (LINEPOINT_TYPE, CURRENT, l, &(l->Point2),
01989               -1 - l->Point2.X,
01990               -1 - l->Point2.Y);
01991 #endif
01992 }
01993 
01994 static void
01995 mark_arc_for_deletion (ArcType *a)
01996 {
01997   Extra *e = ARC2EXTRA(a);
01998   e->deleted = 1;
01999   unlink_extras (e);
02000 #if TRACE1
02001   printf("Marked arc %p for deletion %ld < %ld\n",
02002          e, a->StartAngle, a->Delta);
02003 #endif
02004 }
02005 
02024 static void
02025 maybe_pull_1 (LineType *line)
02026 {
02027   BoxType box;
02028   /* Line half-thicknesses, including line space */
02029   Coord ex, ey;
02030   LineType *new_line;
02031   Extra *new_lextra;
02032   ArcType *new_arc;
02033   Extra *new_aextra;
02034   double abs_angle;
02035 
02036   start_line = line;
02037   start_extra = LINE2EXTRA (start_line);
02038   end_extra = start_extra->end.next;
02039   end_line = EXTRA2LINE (end_extra);
02040   if (end_extra->deleted)
02041     {
02042       start_extra->end.pending = 0;
02043       return;
02044     }
02045 
02046   if (end_extra->end.next == start_extra)
02047     reverse_line (end_line);
02048 
02049   if (start_extra->start.next
02050       && EXTRA_IS_ARC (start_extra->start.next))
02051     {
02052       sarc_extra = start_extra->start.next;
02053       start_arc = EXTRA2ARC (sarc_extra);
02054       if (sarc_extra->start.next == start_extra)
02055         reverse_arc (start_arc);
02056     }
02057   else
02058     {
02059       start_arc = 0;
02060       sarc_extra = 0;
02061     }
02062 
02063   if (end_extra->end.next
02064       && EXTRA_IS_ARC (end_extra->end.next))
02065     {
02066       earc_extra = end_extra->end.next;
02067       end_arc = EXTRA2ARC (earc_extra);
02068       if (earc_extra->start.next == end_extra)
02069         reverse_arc (end_arc);
02070     }
02071   else
02072     {
02073       end_arc = 0;
02074       earc_extra = 0;
02075     }
02076 
02077 #if TRACE1
02078   printf("maybe_pull_1 %p %p %p %p\n", sarc_extra, start_extra, end_extra, earc_extra);
02079   if (sarc_extra)
02080     print_extra(sarc_extra,0);
02081   print_extra(start_extra,0);
02082   print_extra(end_extra,0);
02083   if (earc_extra)
02084     print_extra(earc_extra,0);
02085 
02086   if (start_extra->deleted
02087       || end_extra->deleted
02088       || (sarc_extra && sarc_extra->deleted)
02089       || (earc_extra && earc_extra->deleted))
02090     {
02091       printf(" one is deleted?\n");
02092       fflush(stdout);
02093       abort();
02094     }
02095 #endif
02096 
02097   if (!start_extra->end.pending)
02098     return;
02099 #if 0
02100   if (start_extra->end.waiting_for
02101       && start_extra->end.waiting_for->pending)
02102     return;
02103 #endif
02104   
02105   if (start_line->Thickness != end_line->Thickness)
02106     return;
02107   thickness = (start_line->Thickness + 1)/2 + PCB->Bloat;
02108 
02109   /* At this point, our expectations are all met.  */
02110 
02111   box.X1 = start_line->Point1.X - thickness;
02112   box.X2 = start_line->Point1.X + thickness;
02113   box.Y1 = start_line->Point1.Y - thickness;
02114   box.Y2 = start_line->Point1.Y + thickness;
02115   expand_box (&box, start_line->Point2.X, start_line->Point2.Y, thickness);
02116   expand_box (&box, end_line->Point2.X, end_line->Point2.Y, thickness);
02117   if (start_arc)
02118     expand_box (&box, sarc_extra->start.x, sarc_extra->start.y, start_arc->Thickness/2);
02119   if (end_arc)
02120     expand_box (&box, earc_extra->start.x, earc_extra->start.y, end_arc->Thickness/2);
02121 
02122 
02123   se_sign = copysign (1, cross2d (start_line->Point1.X, start_line->Point1.Y,
02124                                   start_line->Point2.X, start_line->Point2.Y,
02125                                   end_line->Point2.X, end_line->Point2.Y));
02126   best_angle = se_sign * M_PI;
02127   if (start_arc)
02128     {
02129       sa_sign = copysign (1, -start_arc->Delta);
02130       sa_sign *= se_sign;
02131     }
02132   else
02133     sa_sign = 0;
02134   if (end_arc)
02135     {
02136       ea_sign = copysign (1, -end_arc->Delta);
02137       ea_sign *= -se_sign;
02138     }
02139   else
02140     ea_sign = 0;
02141 
02142   start_angle = atan2 (start_line->Point2.Y - start_line->Point1.Y,
02143                        start_line->Point2.X - start_line->Point1.X);
02144 #if TRACE1
02145   printf("se_sign %f sa_sign %f ea_sign %f best_angle %f start_angle %f\n",
02146          se_sign, sa_sign, ea_sign, r2d (best_angle), r2d(start_angle));
02147 #endif
02148 
02149   if (start_arc)
02150     {
02151       sa_x = start_arc->X;
02152       sa_y = start_arc->Y;
02153       if (same_sign (start_arc->Delta, se_sign))
02154         sa_r = - start_arc->Width;
02155       else
02156         sa_r = start_arc->Width;
02157     }
02158   else
02159     {
02160       sa_x = start_line->Point1.X;
02161       sa_y = start_line->Point1.Y;
02162       sa_r = 0;
02163     }
02164 
02165   if (end_arc)
02166     {
02167       if (ea_sign < 0)
02168         ea_r = end_arc->Width;
02169       else
02170         ea_r = - end_arc->Width;
02171     }
02172   else
02173     ea_r = 0;
02174 
02175 #if TRACE1
02176   trace_path (sarc_extra ? sarc_extra : start_extra);
02177 #endif
02178 
02179   if (end_arc)
02180     {
02181       gp_point_force (end_arc->X, end_arc->Y, -ea_r-thickness, 0, 0, 0, 1, "end arc");
02182       ex = end_arc->X;
02183       ey = end_arc->Y;
02184     }
02185   else
02186     {
02187       gp_point_force (end_line->Point2.X, end_line->Point2.Y, -thickness, 0, 0, 0, 1, "end arc");
02188       ex = end_line->Point2.X;
02189       ey = end_line->Point2.Y;
02190     }
02191 
02192   fx = ex;
02193   fy = ey;
02194   if (fx < 0)
02195     {
02196       pcb_fprintf(stderr, "end line corrupt? f is %#mD\n", fx, fy);
02197       print_extra (end_extra, 0);
02198       if (earc_extra)
02199         print_extra(earc_extra, 0);
02200       abort();
02201     }
02202 
02203   end_dist = Distance (end_line->Point1.X, end_line->Point1.Y,
02204                    end_line->Point2.X, end_line->Point2.Y);
02205 
02206   start_pinpad = start_extra->start.pin;
02207   end_pinpad = start_extra->end.pin;
02208   fp = 0;
02209 
02210   r_search(CURRENT->line_tree, &box, NULL, gp_line_cb, 0);
02211   r_search(CURRENT->arc_tree, &box, NULL, gp_arc_cb, 0);
02212   r_search(CURRENT->text_tree, &box, NULL, gp_text_cb, 0);
02213   r_search(CURRENT->polygon_tree, &box, NULL, gp_poly_cb, 0);
02214   r_search(PCB->Data->pin_tree, &box, NULL, gp_pin_cb, 0);
02215   r_search(PCB->Data->via_tree, &box, NULL, gp_pin_cb, 0);
02216   r_search(PCB->Data->pad_tree, &box, NULL, gp_pad_cb, 0);
02217 
02218   /* radians, absolute angle of (at the moment) the start_line */
02219   abs_angle = fa + start_angle;
02220 
02221 #if TRACE1
02222   pcb_printf("\033[43;30mBest: at %#mD r %#mS, angle %.1f fp %d\033[0m\n", fx, fy, fr, r2d(fa), fp);
02223 #endif
02224 
02225 #if 0
02226   if (fa > M_PI/2 || fa < -M_PI/2)
02227     {
02228       SET_FLAG (FOUNDFLAG, line);
02229       longjmp (abort_buf, 1);
02230     }
02231 #endif
02232 
02233   if (fp)
02234     {
02235       start_extra->end.waiting_for = fp_end;
02236       return;
02237     }
02238   start_extra->end.pending = 0;
02239 
02240   /* Step 0: check for merged arcs (special case).  */
02241 
02242   if (fx == ex && fy == ey
02243       && start_arc && end_arc
02244       && start_arc->X == end_arc->X && start_arc->Y == end_arc->Y)
02245     {
02246       /* Merge arcs */
02247       int new_delta;
02248 
02249       new_delta = end_arc->StartAngle - start_arc->StartAngle;
02250       if (start_arc->Delta > 0)
02251         {
02252           while (new_delta > 360)
02253             new_delta -= 360;
02254           while (new_delta < 0)
02255             new_delta += 360;
02256         }
02257       else
02258         {
02259           while (new_delta < -360)
02260             new_delta += 360;
02261           while (new_delta > 0)
02262             new_delta -= 360;
02263         }
02264 #if TRACE1
02265       pcb_printf("merging arcs at %#mS nd %d\n", start_arc->X, start_arc->Y, new_delta);
02266       print_extra(sarc_extra, 0);
02267       print_extra(earc_extra, 0);
02268 #endif
02269       mark_arc_for_deletion (end_arc);
02270       mark_line_for_deletion (start_line);
02271       mark_line_for_deletion (end_line);
02272       ChangeArcAngles (CURRENT, start_arc, start_arc->StartAngle, new_delta);
02273       fix_arc_extra (start_arc, sarc_extra);
02274       did_something ++;
02275       return;
02276     }
02277 
02278   /* Step 1: adjust start_arc's angles and move start_line's Point1 to
02279      match it. */
02280 
02281   if (start_arc)
02282     {
02283       double new_delta;
02284       int del_arc = 0;
02285 
02286       /* We must always round towards "larger arcs", whichever way that is. */
02287       new_delta = 180 - r2d(abs_angle);
02288 #if TRACE1
02289       printf("new_delta starts at %d vs %d < %d\n",
02290              (int)new_delta, (int)start_arc->StartAngle, (int)start_arc->Delta);
02291 #endif
02292       if (start_arc->Delta < 0)
02293         new_delta = (int)(new_delta) + 90;
02294       else
02295         new_delta = (int)new_delta - 90;
02296       new_delta =  new_delta - start_arc->StartAngle;
02297       while (new_delta > start_arc->Delta+180)
02298         new_delta -= 360;
02299       while (new_delta < start_arc->Delta-180)
02300         new_delta += 360;
02301 #if TRACE1
02302       printf("new_delta adjusts to %d\n", (int)new_delta);
02303       printf("fa = %f, new_delta ends at %.1f vs start %d\n",
02304              fa, new_delta, (int)start_arc->StartAngle);
02305 #endif
02306 
02307       if (new_delta * start_arc->Delta <= 0)
02308         del_arc = 1;
02309 
02310       ChangeArcAngles (CURRENT, start_arc, start_arc->StartAngle, new_delta);
02311       fix_arc_extra (start_arc, sarc_extra);
02312       MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point1),
02313                   sarc_extra->end.x - start_line->Point1.X,
02314                   sarc_extra->end.y - start_line->Point1.Y);
02315 
02316       if (del_arc)
02317         {
02318           MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point1),
02319                       sarc_extra->start.x - start_line->Point1.X,
02320                       sarc_extra->start.y - start_line->Point1.Y);
02321           mark_arc_for_deletion (start_arc);
02322         }
02323     }
02324 
02325   /* Step 1.5: If the "obstacle" is right at the end, ignore it.  */
02326 
02327   {
02328     double oa = start_angle+fa - M_PI/2*se_sign;
02329     double ox = fx + fr * cos(oa);
02330     double oy = fy + fr * sin(oa);
02331 #if TRACE1
02332     pcb_printf("obstacle at %#mD angle %d = arc starts at %#mD\n",
02333            fx, fy, (int)r2d(oa), (Coord)ox, (Coord)oy);
02334 #endif
02335 
02336     if (Distance (ox, oy, end_line->Point2.X, end_line->Point2.Y)
02337         < fr * SIN1D)
02338       {
02339         /* Pretend it doesn't exist.  */
02340         fx = ex;
02341         fy = ey;
02342       }
02343   }
02344 
02345   /* Step 2: If we have no obstacles, connect start and end.  */
02346 
02347 #if TRACE1
02348   pcb_printf("fx %#mS ex %#mS fy %#mS ey %#mS\n", fx, ex, fy, ey);
02349 #endif
02350   if (fx == ex && fy == ey)
02351     {
02352       /* No obstacles.  */
02353 #if TRACE1
02354       printf("\033[32mno obstacles\033[0m\n");
02355 #endif
02356       if (end_arc)
02357         {
02358           int del_arc = 0;
02359           int new_delta, end_angle, pcb_fa, end_change;
02360 
02361           end_angle = end_arc->StartAngle + end_arc->Delta;
02362           if (end_arc->Delta < 0)
02363             end_angle -= 90;
02364           else
02365             end_angle += 90;
02366 
02367           /* We must round so as to make the larger arc.  */
02368           if (end_arc->Delta < 0)
02369             pcb_fa = - r2d(start_angle + fa);
02370           else
02371             pcb_fa = 1 - r2d(start_angle + fa);
02372           end_change = pcb_fa - end_angle;
02373 
02374           while (end_change > 180)
02375             end_change -= 360;
02376           while (end_change < -180)
02377             end_change += 360;
02378           new_delta = end_arc->Delta + end_change;
02379 
02380           if (new_delta * end_arc->Delta <= 0)
02381             del_arc = 1;
02382 
02383           ChangeArcAngles (CURRENT, end_arc, end_arc->StartAngle, new_delta);
02384           fix_arc_extra (end_arc, earc_extra);
02385           MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point2),
02386                       earc_extra->end.x - start_line->Point2.X,
02387                       earc_extra->end.y - start_line->Point2.Y);
02388 
02389           if (del_arc)
02390             {
02391               MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point2),
02392                           earc_extra->start.x - start_line->Point2.X,
02393                           earc_extra->start.y - start_line->Point2.Y);
02394               mark_arc_for_deletion (end_arc);
02395             }
02396         }
02397       else
02398         {
02399           MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point2),
02400                       end_line->Point2.X - start_line->Point2.X,
02401                       end_line->Point2.Y - start_line->Point2.Y);
02402         }
02403       mark_line_for_deletion (end_line);
02404       start_extra->end.pending = 1;
02405 
02406 #if TRACE1
02407       printf("\033[35mdid_something: no obstacles\033[0m\n");
02408 #endif
02409       did_something ++;
02410       npulled ++;
02411       status();
02412       return;
02413     }
02414 
02415   /* Step 3: Compute the new intersection of start_line and end_line.  */
02416 
02417   ex = start_line->Point1.X + cos(start_angle + fa) * 10000.0;
02418   ey = start_line->Point1.Y + sin(start_angle + fa) * 10000.0;
02419 #if TRACE1
02420   pcb_printf("temp point %#mS\n", ex, ey);
02421   pcb_printf("intersect %#mS-%#mS with %#mS-%#mS\n",
02422          start_line->Point1.X, start_line->Point1.Y,
02423          ex, ey,
02424          end_line->Point1.X, end_line->Point1.Y,
02425          end_line->Point2.X, end_line->Point2.Y);
02426 #endif
02427   if (! intersection_of_lines (start_line->Point1.X, start_line->Point1.Y,
02428                                ex, ey,
02429                                end_line->Point1.X, end_line->Point1.Y,
02430                                end_line->Point2.X, end_line->Point2.Y,
02431                                &ex, &ey))
02432     {
02433       ex = end_line->Point2.X;
02434       ey = end_line->Point2.Y;
02435     }   
02436 #if TRACE1
02437   pcb_printf("new point %#mS\n", ex, ey);
02438 #endif
02439   MoveObject (LINEPOINT_TYPE, CURRENT, end_line, &(end_line->Point1),
02440               ex - end_line->Point1.X,
02441               ey - end_line->Point1.Y);
02442 
02443   /* Step 4: Split start_line at the obstacle and insert a zero-delta
02444      arc at it.  */
02445 
02446   new_arc = create_arc (start_line, fx, fy, fr,
02447                         90-(int)(r2d(start_angle+fa)+0.5) + 90 + 90*se_sign, -se_sign);
02448   new_aextra = ARC2EXTRA (new_arc);
02449 
02450   if (start_arc) sarc_extra = ARC2EXTRA (start_arc);
02451   if (end_arc) earc_extra = ARC2EXTRA (end_arc);
02452 
02453   MoveObject (LINEPOINT_TYPE, CURRENT, start_line, &(start_line->Point2),
02454               new_aextra->start.x - start_line->Point2.X,
02455               new_aextra->start.y - start_line->Point2.Y);
02456 
02457   new_line = create_line (start_line, new_aextra->end.x, new_aextra->end.y, ex, ey);
02458   start_extra = LINE2EXTRA (start_line);
02459   new_lextra = LINE2EXTRA (new_line);
02460   end_extra = LINE2EXTRA (end_line);
02461 
02462   new_lextra->start.pin = start_extra->start.pin;
02463   new_lextra->end.pin = start_extra->end.pin;
02464   new_lextra->start.pending = 1;
02465   new_lextra->end.pending = 1;
02466 
02467   start_extra->end.next = new_aextra;
02468   new_aextra->start.next = start_extra;
02469   new_aextra->end.next = new_lextra;
02470   new_lextra->start.next = new_aextra;
02471   new_lextra->end.next = end_extra;
02472   end_extra->start.next = new_lextra;
02473 
02474   /* Step 5: Recurse.  */
02475 
02476   did_something ++;
02477   npulled ++;
02478   status();
02479 #if TRACE0
02480   printf("\033[35mdid_something: recursing\033[0m\n");
02481   {
02482     int i = gui->confirm_dialog("recurse?", 0);
02483     printf("confirm = %d\n", i);
02484     if (i == 0)
02485       return;
02486   }
02487   printf("\n\033[33mRECURSING\033[0m\n\n");
02488   IncrementUndoSerialNumber();
02489 #endif
02490   maybe_pull_1 (new_line);
02491 }
02492 
02496 static void
02497 maybe_pull (LineType *line, Extra *e)
02498 {
02499 #if TRACE0
02500   printf("maybe_pull: ");
02501   print_extra (e, 0);
02502 #endif
02503   if (e->end.next && EXTRA_IS_LINE (e->end.next))
02504     {
02505       maybe_pull_1 (line);
02506     }
02507   else
02508     {
02509       e->end.pending = 0;
02510       if (e->start.next && EXTRA_IS_LINE (e->start.next))
02511         {
02512           reverse_line (line);
02513           maybe_pull_1 (line);
02514         }
02515       else
02516         e->start.pending = 0;
02517     }
02518 }
02519 
02520 static void
02521 validate_pair (Extra *e, End *end)
02522 {
02523   if (!end->next)
02524     return;
02525   if (end->next->start.next == e)
02526     return;
02527   if (end->next->end.next == e)
02528     return;
02529   fprintf(stderr, "no backlink!\n");
02530   print_extra (e, 0);
02531   print_extra (end->next, 0);
02532   abort();
02533 }
02534 
02535 static void
02536 validate_pair_cb (AnyObjectType *ptr, Extra *extra, void *userdata)
02537 {
02538   validate_pair (extra, &extra->start);
02539   validate_pair (extra, &extra->end);
02540 }
02541 
02542 static void
02543 validate_pairs ()
02544 {
02545   g_hash_table_foreach (lines, (GHFunc)validate_pair_cb, NULL);
02546 #if TRACE1
02547   printf("\narcs\n");
02548 #endif
02549   g_hash_table_foreach (arcs, (GHFunc)validate_pair_cb, NULL);
02550 }
02551 
02552 static void
02553 FreeExtra (Extra *extra)
02554 {
02555   g_slice_free (Extra, extra);
02556 }
02557 
02558 static void
02559 mark_ends_pending (LineType *line, Extra *extra, void *userdata)
02560 {
02561   int *select_flags = userdata;
02562   if (TEST_FLAGS (*select_flags, line))
02563     {
02564       extra->start.pending = 1;
02565       extra->end.pending = 1;
02566     }
02567 }
02568 
02569 #if TRACE1
02570 static void
02571 trace_print_extra (AnyObjectType *ptr, Extra *extra, void *userdata)
02572 {
02573     last_pextra = (Extra *)1;
02574     print_extra(extra, 0);
02575 }
02576 
02577 static void
02578 trace_print_lines_arcs (void)
02579 {
02580   printf("\nlines\n");
02581   g_hash_table_foreach (lines, (GHFunc)trace_print_extra, NULL);
02582 
02583   printf("\narcs\n");
02584   g_hash_table_foreach (arcs, (GHFunc)trace_print_extra, NULL);
02585 
02586   printf("\n");
02587 }
02588 #endif
02589 
02590 static int
02591 GlobalPuller(int argc, char **argv, Coord x, Coord y)
02592 {
02593   int select_flags = 0;
02594 
02595   setbuf(stdout, 0);
02596   nloops = 0;
02597   npulled = 0;
02598   Message ("puller! %s\n", argc > 0 ? argv[0] : "");
02599 
02600   if (argc > 0 && strcasecmp (argv[0], "selected") == 0)
02601     select_flags = SELECTEDFLAG;
02602   if (argc > 0 && strcasecmp (argv[0], "found") == 0)
02603     select_flags = FOUNDFLAG;
02604 
02605   Message ("optimizing...\n");
02606   /* This canonicalizes all the lines, and cleans up near-misses.  */
02607   /* hid_actionl ("djopt", "puller", 0); */
02608 
02609   current_is_bottom = (GetLayerGroupNumberByPointer(CURRENT)
02610                        == GetLayerGroupNumberBySide (BOTTOM_SIDE));
02611   current_is_top = (GetLayerGroupNumberByPointer(CURRENT)
02612                     == GetLayerGroupNumberBySide (TOP_SIDE));
02613 
02614   lines = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)FreeExtra);
02615   arcs  = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)FreeExtra);
02616 
02617   Message ("pairing...\n");
02618   find_pairs ();
02619   validate_pairs ();
02620 
02621   g_hash_table_foreach (lines, (GHFunc)mark_ends_pending, &select_flags);
02622 
02623 #if TRACE1
02624   trace_print_lines_arcs ();
02625 #endif
02626 
02627   propogate_ends ();
02628 
02629 #if TRACE1
02630   trace_print_lines_arcs ();
02631   trace_paths ();
02632 #endif
02633 
02634   Message ("pulling...\n");
02635   if (setjmp(abort_buf) == 0)
02636     {
02637 #if TRACE0
02638       int old_did_something = -1;
02639 #endif
02640       did_something = 1;
02641       while (did_something)
02642         {
02643           nloops ++;
02644           status();
02645           did_something = 0;
02646           LINE_LOOP (CURRENT); {
02647             Extra *e = LINE2EXTRA (line);
02648             if (e->deleted)
02649               continue;
02650 #ifdef CHECK_LINE_PT_NEG
02651             if (line->Point1.X < 0)
02652               abort1();
02653 #endif
02654             if (e->start.next || e->end.next)
02655               maybe_pull (line, e);
02656 #if TRACE0
02657             if (did_something != old_did_something)
02658               {
02659                 IncrementUndoSerialNumber();
02660                 old_did_something = did_something;
02661                 if (gui->confirm_dialog("more?", 0) == 0)
02662                   {
02663                     did_something = 0;
02664                     break;
02665                   }
02666               }
02667 #endif
02668             /*gui->progress(0,0,0);*/
02669           } END_LOOP;
02670         }
02671     }
02672 
02673 #if TRACE0
02674   printf("\nlines\n");
02675   g_hash_table_foreach (lines, (GHFunc)trace_print_extra, NULL);
02676   printf("\narcs\n");
02677   g_hash_table_foreach (arcs, (GHFunc)trace_print_extra, NULL);
02678   printf("\n");
02679   printf("\nlines\n");
02680 #endif
02681 
02682   LINE_LOOP (CURRENT);
02683     {
02684       if (LINE2EXTRA (line)->deleted)
02685         RemoveLine (CURRENT, line);
02686     }
02687   END_LOOP;
02688 
02689   ARC_LOOP (CURRENT);
02690     {
02691       if (ARC2EXTRA (arc)->deleted)
02692         RemoveArc (CURRENT, arc);
02693     }
02694   END_LOOP;
02695 
02696   g_hash_table_unref (lines);
02697   g_hash_table_unref (arcs);
02698 
02699   IncrementUndoSerialNumber();
02700   return 0;
02701 }
02702 
02703 /*                             Actions                                       */
02704 
02705 HID_Action puller_action_list[] = {
02706   {"Puller", "Click on a line-arc intersection or line segment", Puller,
02707    puller_help, puller_syntax},
02708   {"GlobalPuller", 0, GlobalPuller,
02709    globalpuller_help, globalpuller_syntax}
02710 };
02711 
02712 REGISTER_ACTIONS (puller_action_list)