pcb 4.1.1
An interactive printed circuit board layout editor.

smartdisperse.c

Go to the documentation of this file.
00001 
00043 #include <stdio.h>
00044 #include <math.h>
00045 
00046 #include "config.h"
00047 #include "global.h"
00048 #include "data.h"
00049 #include "hid.h"
00050 #include "misc.h"
00051 #include "create.h"
00052 #include "rtree.h"
00053 #include "undo.h"
00054 #include "rats.h"
00055 #include "error.h"
00056 #include "move.h"
00057 #include "draw.h"
00058 #include "set.h"
00059 
00060 #define GAP 10000
00061 static Coord minx;
00062 static Coord miny;
00063 static Coord maxx;
00064 static Coord maxy;
00065 
00073 static void
00074 place (ElementType *element)
00075 {
00076   Coord dx, dy;
00077 
00078   /* figure out how much to move the element */
00079   dx = minx - element->BoundingBox.X1;
00080   dy = miny - element->BoundingBox.Y1;
00081 
00082   /* snap to the grid */
00083   dx -= (element->MarkX + dx) % (long) (PCB->Grid);
00084   dx += (long) (PCB->Grid);
00085   dy -= (element->MarkY + dy) % (long) (PCB->Grid);
00086   dy += (long) (PCB->Grid);
00087 
00088   /*
00089    * and add one grid size so we make sure we always space by GAP or
00090    * more
00091    */
00092   dx += (long) (PCB->Grid);
00093 
00094   /* Figure out if this row has room.  If not, start a new row */
00095   if (minx != GAP && GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
00096   {
00097     miny = maxy + GAP;
00098     minx = GAP;
00099     place(element); /* recurse can't loop, now minx==GAP */
00100     return;
00101   }
00102 
00103   /* move the element */
00104   MoveElementLowLevel (PCB->Data, element, dx, dy);
00105 
00106   /* and add to the undo list so we can undo this operation */
00107   AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
00108 
00109   /* keep track of how tall this row is */
00110   minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
00111   if (maxy < element->BoundingBox.Y2)
00112   {
00113     maxy = element->BoundingBox.Y2;
00114   }
00115 }
00116 
00121 static Coord
00122 padDX (ConnectionType *conn)
00123 {
00124   ElementType *element = (ElementType *) conn->ptr1;
00125   AnyLineObjectType *line = (AnyLineObjectType *) conn->ptr2;
00126 
00127   return line->BoundingBox.X1 -
00128     (element->BoundingBox.X1 + element->BoundingBox.X2) / 2;
00129 }
00130 
00135 static int
00136 padorder (ConnectionType *conna, ConnectionType *connb)
00137 {
00138   Coord dxa, dxb;
00139 
00140   dxa = padDX (conna);
00141   dxb = padDX (connb);
00142   /* there are other cases that merit rotation, ignore them for now */
00143   if (dxa > 0 && dxb < 0)
00144     return 1;
00145   return 0;
00146 }
00147 
00148 /* ewww, these are actually arrays */
00149 #define ELEMENT_N(DATA,ELT)     ((ELT) - (DATA)->Element)
00150 #define VISITED(ELT)            (visited[ELEMENT_N(PCB->Data, (ELT))])
00151 #define IS_ELEMENT(CONN)        ((CONN)->type == PAD_TYPE || (CONN)->type == PIN_TYPE)
00152 
00153 #define ARG(n) (argc > (n) ? argv[n] : 0)
00154 
00155 static const char smartdisperse_syntax[] = "SmartDisperse([All|Selected])";
00156 
00157 /* %start-doc actions SmartDisperse
00158 
00159 The @code{SmartDisperse([All|Selected])} action is a special-purpose
00160 optimization for dispersing elements.
00161 
00162 Run with @code{:SmartDisperse()} or @code{:SmartDisperse(Selected)}
00163 (you can also say @code{:SmartDisperse(All)}, but that's the default).
00164 
00165 %end-doc */
00166 static int
00167 smartdisperse (int argc, char **argv, Coord x, Coord y)
00168 {
00169   char *function = ARG(0);
00170   NetListType *Nets;
00171   char *visited;
00172 //  PointerListType stack = { 0, 0, NULL };
00173   int all;
00174 //  int changed = 0;
00175 //  int i;
00176 
00177   if (! function)
00178   {
00179     all = 1;
00180   }
00181   else if (strcmp(function, "All") == 0)
00182   {
00183     all = 1;
00184   }
00185   else if (strcmp(function, "Selected") == 0)
00186   {
00187     all = 0;
00188   }
00189   else
00190   {
00191     AFAIL (smartdisperse);
00192   }
00193 
00194   Nets = ProcNetlist (&PCB->NetlistLib);
00195   if (! Nets)
00196   {
00197     Message (_("Can't use SmartDisperse because no netlist is loaded.\n"));
00198     return 0;
00199   }
00200 
00201   /* remember which elements we finish with */
00202   visited = calloc (PCB->Data->ElementN, sizeof(*visited));
00203 
00204   /* if we're not doing all, mark the unselected elements as "visited" */
00205   ELEMENT_LOOP (PCB->Data);
00206   {
00207     if (! (all || TEST_FLAG (SELECTEDFLAG, element)))
00208     {
00209       visited[n] = 1;
00210     }
00211   }
00212   END_LOOP;
00213 
00214   /* initialize variables for place() */
00215   minx = GAP;
00216   miny = GAP;
00217   maxx = GAP;
00218   maxy = GAP;
00219 
00220   /*
00221    * Pick nets with two connections.  This is the start of a more
00222    * elaborate algorithm to walk serial nets, but the datastructures
00223    * are too gross so I'm going with the 80% solution.
00224    */
00225   NET_LOOP (Nets);
00226   {
00227     ConnectionType *conna, *connb;
00228     ElementType *ea, *eb;
00229 //    ElementType *epp;
00230 
00231     if (net->ConnectionN != 2)
00232       continue;
00233 
00234     conna = &net->Connection[0];
00235     connb = &net->Connection[1];
00236     if (!IS_ELEMENT(conna) || !IS_ELEMENT(conna))
00237       continue;
00238 
00239     ea = (ElementType *) conna->ptr1;
00240     eb = (ElementType *) connb->ptr1;
00241 
00242     /* place this pair if possible */
00243     if (VISITED((GList *)ea) || VISITED((GList *)eb))
00244       continue;
00245     VISITED ((GList *)ea) = 1;
00246     VISITED ((GList *)eb) = 1;
00247 
00248     /* a weak attempt to get the linked pads side-by-side */
00249     if (padorder(conna, connb))
00250     {
00251       place ((ElementType *) ea);
00252       place ((ElementType *) eb);
00253     }
00254     else
00255     {
00256       place (eb);
00257       place (ea);
00258     }
00259   }
00260   END_LOOP;
00261 
00262   /* Place larger nets, still grouping by net */
00263   NET_LOOP (Nets);
00264   {
00265     CONNECTION_LOOP (net);
00266     {
00267       ElementType *element;
00268 
00269       if (! IS_ELEMENT(connection))
00270         continue;
00271 
00272       element = (ElementType *) connection->ptr1;
00273 
00274       /* place this one if needed */
00275       if (VISITED ((GList *) element))
00276         continue;
00277       VISITED ((GList *) element) = 1;
00278       place (element);
00279     }
00280     END_LOOP;
00281   }
00282   END_LOOP;
00283 
00284   /* Place up anything else */
00285   ELEMENT_LOOP (PCB->Data);
00286   {
00287     if (! visited[n])
00288     {
00289       place (element);
00290     }
00291   }
00292   END_LOOP;
00293 
00294   free (visited);
00295 
00296   IncrementUndoSerialNumber ();
00297   Redraw ();
00298   SetChangedFlag (1);
00299 
00300   return 0;
00301 }
00302 
00303 static HID_Action
00304 smartdisperse_action_list[] =
00305 {
00306   {"smartdisperse", NULL, smartdisperse, NULL, NULL}
00307 };
00308 
00309 REGISTER_ACTIONS (smartdisperse_action_list)
00310 
00311 void
00312 hid_smartdisperse_init ()
00313 {
00314   register_smartdisperse_action_list ();
00315 }