pcb 4.1.1
An interactive printed circuit board layout editor.

teardrops.c

Go to the documentation of this file.
00001 
00029 #include <stdio.h>
00030 #include <math.h>
00031 
00032 #include "config.h"
00033 #include "global.h"
00034 #include "data.h"
00035 #include "hid.h"
00036 #include "misc.h"
00037 #include "create.h"
00038 #include "rtree.h"
00039 #include "undo.h"
00040 
00041 #define MIN_LINE_LENGTH 700
00042 #define MAX_DISTANCE 700
00043 
00044 static PinType * pin;
00045 static int layer;
00046 static int px, py;
00047 static LayerType * silk;
00048 
00049 static int new_arcs = 0;
00050 
00051 int
00052 distance_between_points(int x1,int y1, int x2, int y2)
00053 {
00054   int a;
00055   int b;
00056   int distance;
00057   a = (x1-x2);
00058   b = (y1-y2);
00059   distance = hypot (a, b);
00060   return distance; 
00061 }
00062 
00063 static int
00064 check_line_callback (const BoxType * box, void *cl)
00065 {
00066   LayerType * lay = & PCB->Data->Layer[layer];
00067   LineType * l = (LineType *) box;
00068   int x1, x2, y1, y2;
00069   double a, b, c, x, r, t;
00070   double dx, dy, len;
00071   double ax, ay, lx, ly, theta;
00072   double ldist, adist, radius;
00073   double vx, vy, vr, vl;
00074   int delta, aoffset, count;
00075   ArcType * arc;
00076 
00077   /* if our line is to short ignore it */
00078   if (distance_between_points(l->Point1.X,l->Point1.Y,l->Point2.X,l->Point2.Y) < MIN_LINE_LENGTH )
00079     {
00080       return 1;
00081     }
00082 
00083   if (distance_between_points(l->Point1.X,l->Point1.Y,px,py) < MAX_DISTANCE) 
00084     {
00085       x1 = l->Point1.X;
00086       y1 = l->Point1.Y;
00087       x2 = l->Point2.X;
00088       y2 = l->Point2.Y;
00089     } 
00090   else if (distance_between_points(l->Point2.X,l->Point2.Y,px,py) < MAX_DISTANCE)
00091     {
00092       x1 = l->Point2.X;
00093       y1 = l->Point2.Y;
00094       x2 = l->Point1.X;
00095       y2 = l->Point1.Y;
00096     } 
00097   else
00098     return 1;
00099 
00100   r = pin->Thickness / 2.0;
00101   t = l->Thickness / 2.0;
00102 
00103   if (t > r)
00104     return 1;
00105 
00106   a = 1;
00107   b = 4 * t - 2 * r;
00108   c = 2 * t * t - r * r;
00109 
00110   x = (-b + sqrt (b * b - 4 * a * c)) / (2 * a);
00111 
00112   len = sqrt (((double)x2-x1)*(x2-x1) + ((double)y2-y1)*(y2-y1));
00113 
00114   if (len > (x+t))
00115     {
00116       adist = ldist = x + t;
00117       radius = x + t;
00118       delta = 45;
00119 
00120       if (radius < r || radius < t)
00121         return 1;
00122     }
00123   else if (len > r + t)
00124     {
00125       /* special "short teardrop" code */
00126 
00127       x = (len*len - r*r + t*t) / (2 * (r - t));
00128       ldist = len;
00129       adist = x + t;
00130       radius = x + t;
00131       delta = atan2 (len, x + t) * 180.0/M_PI;
00132     }
00133   else
00134     return 1;
00135 
00136   dx = ((double)x2 - x1) / len;
00137   dy = ((double)y2 - y1) / len;
00138   theta = atan2 (y2 - y1, x1 - x2) * 180.0/M_PI;
00139 
00140   lx = px + dx * ldist;
00141   ly = py + dy * ldist;
00142 
00143   /* We need one up front to determine how many segments it will take
00144      to fill.  */
00145   ax = lx - dy * adist;
00146   ay = ly + dx * adist;
00147   vl = sqrt (r*r - t*t);
00148   vx = px + dx * vl;
00149   vy = py + dy * vl;
00150   vx -= dy * t;
00151   vy += dx * t;
00152   vr = sqrt ((ax-vx) * (ax-vx) + (ay-vy) * (ay-vy));
00153 
00154   aoffset = 0;
00155   count = 0;
00156   do {
00157     if (++count > 5)
00158       {
00159         printf("a %d,%d v %d,%d adist %g radius %g vr %g\n",
00160                (int)ax, (int)ay, (int)vx, (int)vy, adist, radius, vr);
00161         return 1;
00162       }
00163 
00164     ax = lx - dy * adist;
00165     ay = ly + dx * adist;
00166 
00167     arc = CreateNewArcOnLayer (lay, (int)ax, (int)ay, (int)radius, (int)radius,
00168                                (int)theta+90+aoffset, delta-aoffset,
00169                                l->Thickness, l->Clearance, l->Flags);
00170     if (arc)
00171       AddObjectToCreateUndoList (ARC_TYPE, lay, arc, arc);
00172 
00173     ax = lx + dy * (x+t);
00174     ay = ly - dx * (x+t);
00175 
00176     arc = CreateNewArcOnLayer (lay, (int)ax, (int)ay, (int)radius, (int)radius,
00177                                (int)theta-90-aoffset, -delta+aoffset,
00178                                l->Thickness, l->Clearance, l->Flags);
00179     if (arc)
00180       AddObjectToCreateUndoList (ARC_TYPE, lay, arc, arc);
00181 
00182     radius += t*1.9;
00183     aoffset = acos ((double)adist / radius) * 180.0 / M_PI;
00184 
00185     new_arcs ++;
00186   } while (vr > radius - t);
00187 
00188   return 1;
00189 }
00190 
00191 static void
00192 check_pin (PinType * _pin)
00193 {
00194   BoxType spot;
00195 
00196   pin = _pin;
00197 
00198   px = pin->X;
00199   py = pin->Y;
00200 
00201   spot.X1 = px - 10;
00202   spot.Y1 = py - 10;
00203   spot.X2 = px + 10;
00204   spot.Y2 = py + 10;
00205 
00206   for (layer = 0; layer < max_copper_layer; layer ++)
00207     {
00208       LayerType * l = &(PCB->Data->Layer[layer]);
00209       r_search (l->line_tree, &spot, NULL, check_line_callback, l);
00210     }
00211 }
00212 
00213 /* %start-doc actions Teardrops
00214 
00215 The @code{Teardrops()} action adds teardrops to the intersections
00216 between traces and pins/vias.
00217 
00218 This is a simplistic test, so there are cases where you'd think it would
00219 add them but doesn't.
00220 
00221 If the trace doesn't end at @emph{exactly} the same point as the pin/via, it
00222 will be skipped.
00223 
00224 This often happens with metric parts on an Imperial grid or visa-versa.
00225 
00226 If a trace passes through a pin/via but doesn't end there, there won't
00227 be any teardrops.
00228 
00229 Use @code{:djopt(none)} to split those lines into two segments, each of which
00230 ends at the pin/via.
00231 
00232 Usage:
00233 
00234 This action takes no parameters.
00235 
00236 To invoke it, use the command window, usually by typing ":".
00237 
00238 Example:
00239 
00240 @code{:Teardrops()}
00241 
00242 @center @image{td_ex1,,,Example of how Teardrops works,png}
00243 
00244 With the lesstif HID you can add this action to your menu or a hotkey by
00245 editing $HOME/.pcb/pcb-menu.res (grab a copy from the pcb source if you
00246 haven't one there yet). 
00247 
00248 Known Bugs:
00249 
00250 Square pins are teardropped too.
00251 
00252 Refdes silk is no longer visible.
00253 
00254 %end-doc */
00255 static int
00256 teardrops (int argc, char **argv, Coord x, Coord y)
00257 {
00258   silk = & PCB->Data->SILKLAYER;
00259 
00260   new_arcs = 0;
00261 
00262   VIA_LOOP (PCB->Data);
00263   {
00264     check_pin (via);
00265   }
00266   END_LOOP;
00267 
00268   ALLPIN_LOOP (PCB->Data);
00269   {
00270     check_pin (pin);
00271   }
00272   ENDALL_LOOP;
00273 
00274   gui->invalidate_all ();
00275 
00276   if (new_arcs)
00277     IncrementUndoSerialNumber ();
00278 
00279   return 0;
00280 }
00281 
00282 static HID_Action teardrops_action_list[] = {
00283   {"Teardrops", NULL, teardrops,
00284    NULL, NULL}
00285 };
00286 
00287 REGISTER_ACTIONS (teardrops_action_list)
00288 
00289 void
00290 hid_teardrops_init()
00291 {
00292   register_teardrops_action_list();
00293 }