smartdisperse.c

00001 /* smartdisperse plug-in for PCB
00002 
00003    Improve the initial dispersion of elements by choosing an order based
00004    on the netlist, rather than the arbitrary element order.  This isn't
00005    the same as a global autoplace, it's more of a linear autoplace.  It
00006    might make some useful local groupings.  For example, you should not
00007    have to chase all over the board to find the resistor that goes with
00008    a given LED.
00009 
00010    Copyright (C) 2007 Ben Jackson <ben@ben.com> based on teardrops.c by
00011    Copyright (C) 2006 DJ Delorie <dj@delorie.com>
00012    as well as the original action.c, and autoplace.c
00013 
00014    Licensed under the terms of the GNU General Public License, version
00015    2 or later.
00016 */
00017 
00018 #include <stdio.h>
00019 #include <math.h>
00020 
00021 #include "global.h"
00022 #include "data.h"
00023 #include "hid.h"
00024 #include "misc.h"
00025 #include "create.h"
00026 #include "rtree.h"
00027 #include "undo.h"
00028 #include "rats.h"
00029 #include "error.h"
00030 #include "move.h"
00031 #include "draw.h"
00032 #include "set.h"
00033 
00034 #define GAP 10000
00035 static LocationType minx;
00036 static LocationType miny;
00037 static LocationType maxx;
00038 static LocationType maxy;
00039 
00040 /*
00041  * Place one element.  Must initialize statics above before calling for
00042  * the first time.
00043  *
00044  * This is taken almost entirely from ActionDisperseElements, with cleanup
00045  */
00046 static void
00047 place(ElementTypePtr element)
00048 {
00049         LocationType dx, dy;
00050 
00051         /* figure out how much to move the element */
00052         dx = minx - element->BoundingBox.X1;
00053         dy = miny - element->BoundingBox.Y1;
00054 
00055         /* snap to the grid */
00056         dx -= (element->MarkX + dx) % (long) (PCB->Grid);
00057         dx += (long) (PCB->Grid);
00058         dy -= (element->MarkY + dy) % (long) (PCB->Grid);
00059         dy += (long) (PCB->Grid);
00060 
00061         /*
00062          * and add one grid size so we make sure we always space by GAP or
00063          * more
00064          */
00065         dx += (long) (PCB->Grid);
00066 
00067         /* Figure out if this row has room.  If not, start a new row */
00068         if (minx != GAP && GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth) {
00069                 miny = maxy + GAP;
00070                 minx = GAP;
00071                 place(element);         /* recurse can't loop, now minx==GAP */
00072                 return;
00073         }
00074 
00075         /* move the element */
00076         MoveElementLowLevel (PCB->Data, element, dx, dy);
00077 
00078         /* and add to the undo list so we can undo this operation */
00079         AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
00080 
00081         /* keep track of how tall this row is */
00082         minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
00083         if (maxy < element->BoundingBox.Y2) {
00084                 maxy = element->BoundingBox.Y2;
00085         }
00086 }
00087 
00088 /*
00089  * Return the X location of a connection's pad or pin within its element
00090  */
00091 static LocationType
00092 padDX(ConnectionTypePtr conn)
00093 {
00094         ElementTypePtr element = (ElementTypePtr) conn->ptr1;
00095         AnyLineObjectTypePtr line = (AnyLineObjectTypePtr) conn->ptr2;
00096 
00097         return line->BoundingBox.X1 -
00098                         (element->BoundingBox.X1 + element->BoundingBox.X2) / 2;
00099 }
00100 
00101 /* return true if ea,eb would be the best order, else eb,ea, based on pad loc */
00102 static int
00103 padorder(ConnectionTypePtr conna, ConnectionTypePtr connb)
00104 {
00105         LocationType dxa, dxb;
00106 
00107         dxa = padDX(conna);
00108         dxb = padDX(connb);
00109         /* there are other cases that merit rotation, ignore them for now */
00110         if (dxa > 0 && dxb < 0)
00111                 return 1;
00112         return 0;
00113 }
00114 
00115 /* ewww, these are actually arrays */
00116 #define ELEMENT_N(DATA,ELT)     ((ELT) - (DATA)->Element)
00117 #define VISITED(ELT)            (visited[ELEMENT_N(PCB->Data, (ELT))])
00118 #define IS_ELEMENT(CONN)        ((CONN)->type == PAD_TYPE || (CONN)->type == PIN_TYPE)
00119 
00120 #define ARG(n) (argc > (n) ? argv[n] : 0)
00121 
00122 static const char smartdisperse_syntax[] = "SmartDisperse([All|Selected])";
00123 
00124 static int
00125 smartdisperse (int argc, char **argv, int x, int y)
00126 {
00127         char *function = ARG(0);
00128         NetListTypePtr Nets;
00129         char *visited;
00130 //      PointerListType stack = { 0, 0, NULL };
00131         int all;
00132 //      int changed = 0;
00133 //      int i;
00134 
00135         if (! function) {
00136                 all = 1;
00137         } else if (strcmp(function, "All") == 0) {
00138                 all = 1;
00139         } else if (strcmp(function, "Selected") == 0) {
00140                 all = 0;
00141         } else {
00142                 AFAIL(smartdisperse);
00143         }
00144 
00145         Nets = ProcNetlist (&PCB->NetlistLib);
00146         if (! Nets) {
00147                 Message (_("Can't use SmartDisperse because no netlist is loaded.\n"));
00148                 return 0;
00149         }
00150 
00151         /* remember which elements we finish with */
00152         visited = MyCalloc(PCB->Data->ElementN, sizeof(*visited),
00153                                 "smartdisperse()");
00154 
00155         /* if we're not doing all, mark the unselected elements as "visited" */
00156         ELEMENT_LOOP(PCB->Data);
00157         {
00158                 if (! (all || TEST_FLAG (SELECTEDFLAG, element))) {
00159                         visited[n] = 1;
00160                 }
00161         }
00162         END_LOOP;
00163 
00164         /* initialize variables for place() */
00165         minx = GAP;
00166         miny = GAP;
00167         maxx = GAP;
00168         maxy = GAP;
00169 
00170         /*
00171          * Pick nets with two connections.  This is the start of a more
00172          * elaborate algorithm to walk serial nets, but the datastructures
00173          * are too gross so I'm going with the 80% solution.
00174          */
00175         NET_LOOP(Nets);
00176         {
00177                 ConnectionTypePtr       conna, connb;
00178                 ElementTypePtr          ea, eb;
00179 //              ElementTypePtr          *epp;
00180 
00181                 if (net->ConnectionN != 2)
00182                         continue;
00183 
00184                 conna = &net->Connection[0];
00185                 connb = &net->Connection[1];
00186                 if (!IS_ELEMENT(conna) || !IS_ELEMENT(conna))
00187                         continue;
00188 
00189                 ea = (ElementTypePtr) conna->ptr1;
00190                 eb = (ElementTypePtr) connb->ptr1;
00191 
00192                 /* place this pair if possible */
00193                 if (VISITED(ea) || VISITED(eb))
00194                         continue;
00195                 VISITED(ea) = 1;
00196                 VISITED(eb) = 1;
00197 
00198                 /* a weak attempt to get the linked pads side-by-side */
00199                 if (padorder(conna, connb)) {
00200                         place(ea);
00201                         place(eb);
00202                 } else {
00203                         place(eb);
00204                         place(ea);
00205                 }
00206         }
00207         END_LOOP;
00208 
00209         /* Place larger nets, still grouping by net */
00210         NET_LOOP(Nets);
00211         {
00212                 CONNECTION_LOOP(net);
00213                 {
00214                         ElementTypePtr element;
00215 
00216                         if (! IS_ELEMENT(connection))
00217                                 continue;
00218 
00219                         element = (ElementTypePtr) connection->ptr1;
00220 
00221                         /* place this one if needed */
00222                         if (VISITED(element))
00223                                 continue;
00224                         VISITED(element) = 1;
00225                         place(element);
00226                 }
00227                 END_LOOP;
00228         }
00229         END_LOOP;
00230 
00231         /* Place up anything else */
00232         ELEMENT_LOOP(PCB->Data);
00233         {
00234                 if (! visited[n]) {
00235                         place(element);
00236                 }
00237         }
00238         END_LOOP;
00239 
00240         free(visited);
00241 
00242         IncrementUndoSerialNumber();
00243         ClearAndRedrawOutput();
00244         SetChangedFlag(1);
00245 
00246         return 0;
00247 }
00248 
00249 static HID_Action smartdisperse_action_list[] = {
00250         {"smartdisperse", NULL, smartdisperse, NULL, NULL}
00251 };
00252 
00253 REGISTER_ACTIONS (smartdisperse_action_list)
00254 
00255 void
00256 hid_smartdisperse_init()
00257 {
00258         register_smartdisperse_action_list();
00259 }

Generated on Tue Aug 17 15:28:04 2010 for pcb-plugins by  doxygen 1.4.6-NO