pcb 4.1.1
An interactive printed circuit board layout editor.

autoplace.c

Go to the documentation of this file.
00001 
00039 #ifdef HAVE_CONFIG_H
00040 #include "config.h"
00041 #endif
00042 
00043 #include <assert.h>
00044 #include <math.h>
00045 #include <memory.h>
00046 #include <stdlib.h>
00047 
00048 #include "global.h"
00049 
00050 #include "autoplace.h"
00051 #include "box.h"
00052 #include "compat.h"
00053 #include "data.h"
00054 #include "draw.h"
00055 #include "error.h"
00056 #include "intersect.h"
00057 #include "rtree.h"
00058 #include "macro.h"
00059 #include "mirror.h"
00060 #include "misc.h"
00061 #include "move.h"
00062 #include "mymem.h"
00063 #include "rats.h"
00064 #include "remove.h"
00065 #include "rotate.h"
00066 
00067 #ifdef HAVE_LIBDMALLOC
00068 #include <dmalloc.h>
00069 #endif
00070 
00071 #define EXPANDRECTXY(r1, x1, y1, x2, y2) { \
00072   r1->X1=MIN(r1->X1, x1); r1->Y1=MIN(r1->Y1, y1); \
00073   r1->X2=MAX(r1->X2, x2); r1->Y2=MAX(r1->Y2, y2); \
00074 }
00075 #define EXPANDRECT(r1, r2) EXPANDRECTXY(r1, r2->X1, r2->Y1, r2->X2, r2->Y2)
00076 
00077 /* ---------------------------------------------------------------------------
00078  * some local prototypes
00079  */
00080 static double ComputeCost (NetListType *Nets, double T0, double T);
00081 
00082 /* ---------------------------------------------------------------------------
00083  * some local types
00084  */
00085 const struct
00086 {
00087   double via_cost;
00088   double congestion_penalty;    /* penalty length / unit area */
00089   double overlap_penalty_min;   /* penalty length / unit area at start */
00090   double overlap_penalty_max;   /* penalty length / unit area at end */
00091   double out_of_bounds_penalty; /* assessed for each component oob */
00092   double overall_area_penalty;  /* penalty length / unit area */
00093   double matching_neighbor_bonus;       /* length bonus per same-type neigh. */
00094   double aligned_neighbor_bonus;        /* length bonus per aligned neigh. */
00095   double oriented_neighbor_bonus;       /* length bonus per same-rot neigh. */
00096 #if 0
00097   double pin_alignment_bonus;   /* length bonus per exact alignment */
00098   double bound_alignment_bonus; /* length bonus per exact alignment */
00099 #endif
00100   double m;                     /* annealing stage cutoff constant */
00101   double gamma;                 /* annealing schedule constant */
00102   int good_ratio;               /* ratio of moves to good moves for halting */
00103   bool fast;                    /* ignore SMD/pin conflicts */
00104   Coord large_grid_size;        /* snap perturbations to this grid when T is high */
00105   Coord small_grid_size;        /* snap to this grid when T is small. */
00106 }
00110 CostParameter =
00111 {
00112   3e3,                          /* via cost */
00113     2e-2,                       /* congestion penalty */
00114     1e-2,                       /* initial overlap penalty */
00115     1e2,                        /* final overlap penalty */
00116     1e3,                        /* out of bounds penalty */
00117     1e0,                        /* penalty for total area used */
00118     1e0,                        /* subtract 1000 from cost for every same-type neighbor */
00119     1e0,                        /* subtract 1000 from cost for every aligned neighbor */
00120     1e0,                        /* subtract 1000 from cost for every same-rotation neighbor */
00121     20,                         /* move on when each module has been profitably moved 20 times */
00122     0.75,                       /* annealing schedule constant: 0.85 */
00123     40,                         /* halt when there are 60 times as many moves as good moves */
00124     false,                      /* don't ignore SMD/pin conflicts */
00125     MIL_TO_COORD (100),         /* coarse grid is 100 mils */
00126     MIL_TO_COORD (10),          /* fine grid is 10 mils */
00127 };
00128 
00129 typedef struct
00130 {
00131   ElementType **element;
00132   Cardinal elementN;
00133 }
00134 ElementPtrListType;
00135 
00136 enum ewhich
00137   { SHIFT, ROTATE, EXCHANGE };
00138 
00139 typedef struct
00140 {
00141   ElementType *element;
00142   enum ewhich which;
00143   Coord DX, DY;                 /* for shift */
00144   unsigned rotate;              /* for rotate/flip */
00145   ElementType *other;           /* for exchange */
00146 }
00147 PerturbationType;
00148 
00149 /* ---------------------------------------------------------------------------
00150  * some local identifiers
00151  */
00152 
00158 static void
00159 UpdateXY (NetListType *Nets)
00160 {
00161   Cardinal top_group, bottom_group;
00162   Cardinal i, j;
00163   /* find layer groups of the top and bottom sides */
00164   top_group = GetLayerGroupNumberBySide (TOP_SIDE);
00165   bottom_group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
00166   /* update all nets */
00167   for (i = 0; i < Nets->NetN; i++)
00168     {
00169       for (j = 0; j < Nets->Net[i].ConnectionN; j++)
00170         {
00171           ConnectionType *c = &(Nets->Net[i].Connection[j]);
00172           switch (c->type)
00173             {
00174             case PAD_TYPE:
00175               c->group = TEST_FLAG (ONSOLDERFLAG, (ElementType *) c->ptr1)
00176                           ? bottom_group : top_group;
00177               c->X = ((PadType *) c->ptr2)->Point1.X;
00178               c->Y = ((PadType *) c->ptr2)->Point1.Y;
00179               break;
00180             case PIN_TYPE:
00181               c->group = bottom_group;  /* any layer will do */
00182               c->X = ((PinType *) c->ptr2)->X;
00183               c->Y = ((PinType *) c->ptr2)->Y;
00184               break;
00185             default:
00186               Message ("Odd connection type encountered in " "UpdateXY");
00187               break;
00188             }
00189         }
00190     }
00191 }
00192 
00196 static PointerListType
00197 collectSelectedElements ()
00198 {
00199   PointerListType list = { 0, 0, NULL };
00200   ELEMENT_LOOP (PCB->Data);
00201   {
00202     if (TEST_FLAG (SELECTEDFLAG, element))
00203       {
00204         ElementType **epp = (ElementType **) GetPointerMemory (&list);
00205         *epp = element;
00206       }
00207   }
00208   END_LOOP;
00209   return list;
00210 }
00211 
00212 #if 0                           /* only for debugging box lists */
00213 #include "create.h"
00218 static void
00219 showboxes (BoxListType *blist)
00220 {
00221   Cardinal i;
00222   LayerType *layer = &(PCB->Data->Layer[bottom_silk_layer]);
00223   for (i = 0; i < blist->BoxN; i++)
00224     {
00225       CreateNewLineOnLayer (layer, blist->Box[i].X1, blist->Box[i].Y1,
00226                             blist->Box[i].X2, blist->Box[i].Y1, 1, 1, 0);
00227       CreateNewLineOnLayer (layer, blist->Box[i].X1, blist->Box[i].Y2,
00228                             blist->Box[i].X2, blist->Box[i].Y2, 1, 1, 0);
00229       CreateNewLineOnLayer (layer, blist->Box[i].X1, blist->Box[i].Y1,
00230                             blist->Box[i].X1, blist->Box[i].Y2, 1, 1, 0);
00231       CreateNewLineOnLayer (layer, blist->Box[i].X2, blist->Box[i].Y1,
00232                             blist->Box[i].X2, blist->Box[i].Y2, 1, 1, 0);
00233     }
00234 }
00235 #endif
00236 
00244 struct r_neighbor_info
00245 {
00246   const BoxType *neighbor;
00247   BoxType trap;
00248   direction_t search_dir;
00249 };
00250 
00251 #define ROTATEBOX(box) { Coord t;\
00252     t = (box).X1; (box).X1 = - (box).Y1; (box).Y1 = t;\
00253     t = (box).X2; (box).X2 = - (box).Y2; (box).Y2 = t;\
00254     t = (box).X1; (box).X1 =   (box).X2; (box).X2 = t;\
00255 }
00256 
00269 static int
00270 __r_find_neighbor_reg_in_sea (const BoxType * region, void *cl)
00271 {
00272   struct r_neighbor_info *ni = (struct r_neighbor_info *) cl;
00273   BoxType query = *region;
00274   ROTATEBOX_TO_NORTH (query, ni->search_dir);
00275   return (query.Y2 > ni->trap.Y1) && (query.Y1 < ni->trap.Y2) &&
00276     (query.X2 + ni->trap.Y2 > ni->trap.X1 + query.Y1) &&
00277     (query.X1 + query.Y1 < ni->trap.X2 + ni->trap.Y2);
00278 }
00279 
00293 static int
00294 __r_find_neighbor_rect_in_reg (const BoxType * box, void *cl)
00295 {
00296   struct r_neighbor_info *ni = (struct r_neighbor_info *) cl;
00297   BoxType query = *box;
00298   int r;
00299   ROTATEBOX_TO_NORTH (query, ni->search_dir);
00300   r = (query.Y2 > ni->trap.Y1) && (query.Y1 < ni->trap.Y2) &&
00301     (query.X2 + ni->trap.Y2 > ni->trap.X1 + query.Y1) &&
00302     (query.X1 + query.Y1 < ni->trap.X2 + ni->trap.Y2);
00303   r = r && (query.Y2 <= ni->trap.Y2);
00304   if (r)
00305     {
00306       ni->trap.Y1 = query.Y2;
00307       ni->neighbor = box;
00308     }
00309   return r;
00310 }
00311 
00317 static const BoxType *
00318 r_find_neighbor (rtree_t * rtree, const BoxType * box,
00319                  direction_t search_direction)
00320 {
00321   struct r_neighbor_info ni;
00322   BoxType bbox;
00323 
00324   ni.neighbor = NULL;
00325   ni.trap = *box;
00326   ni.search_dir = search_direction;
00327 
00328   bbox.X1 = bbox.Y1 = 0;
00329   bbox.X2 = PCB->MaxWidth;
00330   bbox.Y2 = PCB->MaxHeight;
00331   /* rotate so that we can use the 'north' case for everything */
00332   ROTATEBOX_TO_NORTH (bbox, search_direction);
00333   ROTATEBOX_TO_NORTH (ni.trap, search_direction);
00334   /* shift Y's such that trap contains full bounds of trapezoid */
00335   ni.trap.Y2 = ni.trap.Y1;
00336   ni.trap.Y1 = bbox.Y1;
00337   /* do the search! */
00338   r_search (rtree, NULL,
00339             __r_find_neighbor_reg_in_sea, __r_find_neighbor_rect_in_reg, &ni);
00340   return ni.neighbor;
00341 }
00342 
00353 static double
00354 ComputeCost (NetListType *Nets, double T0, double T)
00355 {
00356   double W = 0;                 /* wire cost */
00357   double delta1 = 0;            /* wire congestion penalty function */
00358   double delta2 = 0;            /* module overlap penalty function */
00359   double delta3 = 0;            /* out of bounds penalty */
00360   double delta4 = 0;            /* alignment bonus */
00361   double delta5 = 0;            /* total area penalty */
00362   Cardinal i, j;
00363   Coord minx, maxx, miny, maxy;
00364   bool allpads, allsameside;
00365   Cardinal thegroup;
00366   BoxListType bounds = { 0, 0, NULL };  /* save bounding rectangles here */
00367   BoxListType solderside = { 0, 0, NULL };      /* solder side component bounds */
00368   BoxListType componentside = { 0, 0, NULL };   /* component side bounds */
00369   /* make sure the NetList have the proper updated X and Y coords */
00370   UpdateXY (Nets);
00371   /* wire length term.  approximated by half-perimeter of minimum
00372    * rectangle enclosing the net.  Note that we penalize vias in
00373    * all-SMD nets by making the rectangle a cube and weighting
00374    * the "layer height" of the net. */
00375   for (i = 0; i < Nets->NetN; i++)
00376     {
00377       NetType *n = &Nets->Net[i];
00378       if (n->ConnectionN < 2)
00379         continue;               /* no cost to go nowhere */
00380       minx = maxx = n->Connection[0].X;
00381       miny = maxy = n->Connection[0].Y;
00382       thegroup = n->Connection[0].group;
00383       allpads = (n->Connection[0].type == PAD_TYPE);
00384       allsameside = true;
00385       for (j = 1; j < n->ConnectionN; j++)
00386         {
00387           ConnectionType *c = &(n->Connection[j]);
00388           MAKEMIN (minx, c->X);
00389           MAKEMAX (maxx, c->X);
00390           MAKEMIN (miny, c->Y);
00391           MAKEMAX (maxy, c->Y);
00392           if (c->type != PAD_TYPE)
00393             allpads = false;
00394           if (c->group != thegroup)
00395             allsameside = false;
00396         }
00397       /* save bounding rectangle */
00398       {
00399         BoxType *box = GetBoxMemory (&bounds);
00400         box->X1 = minx;
00401         box->Y1 = miny;
00402         box->X2 = maxx;
00403         box->Y2 = maxy;
00404       }
00405       /* okay, add half-perimeter to cost! */
00406       W += COORD_TO_MIL(maxx - minx) + COORD_TO_MIL(maxy - miny) +
00407         ((allpads && !allsameside) ? CostParameter.via_cost : 0);
00408     }
00409   /* now compute penalty function Wc which is proportional to
00410    * amount of overlap and congestion. */
00411   /* delta1 is congestion penalty function */
00412   delta1 = CostParameter.congestion_penalty *
00413     sqrt (fabs (ComputeIntersectionArea (&bounds)));
00414 #if 0
00415   printf ("Wire Congestion Area: %f\n", ComputeIntersectionArea (&bounds));
00416 #endif
00417   /* free bounding rectangles */
00418   FreeBoxListMemory (&bounds);
00419   /* now collect module areas (bounding rect of pins/pads) */
00420   /* two lists for solder side / component side. */
00421 
00422   ELEMENT_LOOP (PCB->Data);
00423   {
00424     BoxListType *thisside;
00425     BoxListType *otherside;
00426     BoxType *box;
00427     BoxType *lastbox = NULL;
00428     Coord thickness;
00429     Coord clearance;
00430     if (TEST_FLAG (ONSOLDERFLAG, element))
00431       {
00432         thisside = &solderside;
00433         otherside = &componentside;
00434       }
00435     else
00436       {
00437         thisside = &componentside;
00438         otherside = &solderside;
00439       }
00440     box = GetBoxMemory (thisside);
00441     /* protect against elements with no pins/pads */
00442     if (element->PinN == 0 && element->PadN == 0)
00443       continue;
00444     /* initialize box so that it will take the dimensions of
00445      * the first pin/pad */
00446     box->X1 = MAX_COORD;
00447     box->Y1 = MAX_COORD;
00448     box->X2 = -MAX_COORD;
00449     box->Y2 = -MAX_COORD;
00450     PIN_LOOP (element);
00451     {
00452       thickness = pin->Thickness / 2;
00453       clearance = pin->Clearance * 2;
00454     EXPANDRECTXY (box,
00455                     pin->X - (thickness + clearance),
00456                     pin->Y - (thickness + clearance),
00457                     pin->X + (thickness + clearance),
00458                     pin->Y + (thickness + clearance))}
00459     END_LOOP;
00460     PAD_LOOP (element);
00461     {
00462       thickness = pad->Thickness / 2;
00463       clearance = pad->Clearance * 2;
00464     EXPANDRECTXY (box,
00465                     MIN (pad->Point1.X,
00466                            pad->Point2.X) - (thickness +
00467                                                clearance),
00468                     MIN (pad->Point1.Y,
00469                            pad->Point2.Y) - (thickness +
00470                                                clearance),
00471                     MAX (pad->Point1.X,
00472                            pad->Point2.X) + (thickness +
00473                                                clearance),
00474                     MAX (pad->Point1.Y,
00475                            pad->Point2.Y) + (thickness + clearance))}
00476     END_LOOP;
00477     /* add a box for each pin to the "opposite side":
00478      * surface mount components can't sit on top of pins */
00479     if (!CostParameter.fast)
00480       PIN_LOOP (element);
00481     {
00482       box = GetBoxMemory (otherside);
00483       thickness = pin->Thickness / 2;
00484       clearance = pin->Clearance * 2;
00485       /* we ignore clearance here */
00486       /* (otherwise pins don't fit next to each other) */
00487       box->X1 = pin->X - thickness;
00488       box->Y1 = pin->Y - thickness;
00489       box->X2 = pin->X + thickness;
00490       box->Y2 = pin->Y + thickness;
00491       /* speed hack! coalesce with last box if we can */
00492       if (lastbox != NULL &&
00493           ((lastbox->X1 == box->X1 &&
00494             lastbox->X2 == box->X2 &&
00495             MIN (abs (lastbox->Y1 - box->Y2),
00496                  abs (box->Y1 - lastbox->Y2)) <
00497             clearance) || (lastbox->Y1 == box->Y1
00498                            && lastbox->Y2 == box->Y2
00499                            &&
00500                            MIN (abs
00501                                 (lastbox->X1 -
00502                                  box->X2),
00503                                 abs (box->X1 - lastbox->X2)) < clearance)))
00504         {
00505           EXPANDRECT (lastbox, box);
00506           otherside->BoxN--;
00507         }
00508       else
00509         lastbox = box;
00510     }
00511     END_LOOP;
00512     /* assess out of bounds penalty */
00513     if (element->VBox.X1 < 0 ||
00514         element->VBox.Y1 < 0 ||
00515         element->VBox.X2 > PCB->MaxWidth || element->VBox.Y2 > PCB->MaxHeight)
00516       delta3 += CostParameter.out_of_bounds_penalty;
00517   }
00518   END_LOOP;
00519   /* compute intersection area of module areas box list */
00520   delta2 = sqrt (fabs (ComputeIntersectionArea (&solderside) +
00521                        ComputeIntersectionArea (&componentside))) *
00522     (CostParameter.overlap_penalty_min +
00523      (1 - (T / T0)) * CostParameter.overlap_penalty_max);
00524 #if 0
00525   printf ("Module Overlap Area (solder): %f\n",
00526           ComputeIntersectionArea (&solderside));
00527   printf ("Module Overlap Area (component): %f\n",
00528           ComputeIntersectionArea (&componentside));
00529 #endif
00530   FreeBoxListMemory (&solderside);
00531   FreeBoxListMemory (&componentside);
00532   /* reward pin/pad x/y alignment */
00533   /* score higher if pins/pads belong to same *type* of component */
00534   /* XXX: subkey should be *distance* from thing aligned with, so that
00535    * aligning to something far away isn't profitable */
00536   {
00537     /* create r tree */
00538     PointerListType seboxes = { 0, 0, NULL }
00539     , ceboxes =
00540     {
00541     0, 0, NULL};
00542     struct ebox
00543     {
00544       BoxType box;
00545       ElementType *element;
00546     };
00547     direction_t dir[4] = { NORTH, EAST, SOUTH, WEST };
00548     struct ebox **boxpp, *boxp;
00549     rtree_t *rt_s, *rt_c;
00550     int factor;
00551     ELEMENT_LOOP (PCB->Data);
00552     {
00553       boxpp = (struct ebox **)
00554         GetPointerMemory (TEST_FLAG (ONSOLDERFLAG, element) ?
00555                           &seboxes : &ceboxes);
00556       *boxpp = (struct ebox *)malloc (sizeof (**boxpp));
00557       if (*boxpp == NULL ) 
00558         {
00559           fprintf (stderr, "malloc() failed in %s\n", __FUNCTION__);
00560           exit (1);
00561         }
00562 
00563       (*boxpp)->box = element->VBox;
00564       (*boxpp)->element = element;
00565     }
00566     END_LOOP;
00567     rt_s = r_create_tree ((const BoxType **) seboxes.Ptr, seboxes.PtrN, 1);
00568     rt_c = r_create_tree ((const BoxType **) ceboxes.Ptr, ceboxes.PtrN, 1);
00569     FreePointerListMemory (&seboxes);
00570     FreePointerListMemory (&ceboxes);
00571     /* now, for each element, find its neighbor on all four sides */
00572     delta4 = 0;
00573     for (i = 0; i < 4; i++)
00574       ELEMENT_LOOP (PCB->Data);
00575     {
00576       boxp = (struct ebox *)
00577         r_find_neighbor (TEST_FLAG (ONSOLDERFLAG, element) ?
00578                          rt_s : rt_c, &element->VBox, dir[i]);
00579       /* score bounding box alignments */
00580       if (!boxp)
00581         continue;
00582       factor = 1;
00583       if (element->Name[0].TextString &&
00584           boxp->element->Name[0].TextString &&
00585           0 == NSTRCMP (element->Name[0].TextString,
00586                         boxp->element->Name[0].TextString))
00587         {
00588           delta4 += CostParameter.matching_neighbor_bonus;
00589           factor++;
00590         }
00591       if (element->Name[0].Direction == boxp->element->Name[0].Direction)
00592         delta4 += factor * CostParameter.oriented_neighbor_bonus;
00593       if (element->VBox.X1 == boxp->element->VBox.X1 ||
00594           element->VBox.X1 == boxp->element->VBox.X2 ||
00595           element->VBox.X2 == boxp->element->VBox.X1 ||
00596           element->VBox.X2 == boxp->element->VBox.X2 ||
00597           element->VBox.Y1 == boxp->element->VBox.Y1 ||
00598           element->VBox.Y1 == boxp->element->VBox.Y2 ||
00599           element->VBox.Y2 == boxp->element->VBox.Y1 ||
00600           element->VBox.Y2 == boxp->element->VBox.Y2)
00601         delta4 += factor * CostParameter.aligned_neighbor_bonus;
00602     }
00603     END_LOOP;
00604     /* free k-d tree memory */
00605     r_destroy_tree (&rt_s);
00606     r_destroy_tree (&rt_c);
00607   }
00608   /* penalize total area used by this layout */
00609   {
00610     Coord minX = MAX_COORD, minY = MAX_COORD;
00611     Coord maxX = -MAX_COORD, maxY = -MAX_COORD;
00612     ELEMENT_LOOP (PCB->Data);
00613     {
00614       MAKEMIN (minX, element->VBox.X1);
00615       MAKEMIN (minY, element->VBox.Y1);
00616       MAKEMAX (maxX, element->VBox.X2);
00617       MAKEMAX (maxY, element->VBox.Y2);
00618     }
00619     END_LOOP;
00620     if (minX < maxX && minY < maxY)
00621       delta5 = CostParameter.overall_area_penalty *
00622         sqrt (COORD_TO_MIL (maxX - minX) * COORD_TO_MIL (maxY - minY));
00623   }
00624   if (T == 5)
00625     {
00626       T = W + delta1 + delta2 + delta3 - delta4 + delta5;
00627       printf ("cost components are %.3f %.3f %.3f %.3f %.3f %.3f\n",
00628               W / T, delta1 / T, delta2 / T, delta3 / T, -delta4 / T,
00629               delta5 / T);
00630     }
00631   /* done! */
00632   return W + (delta1 + delta2 + delta3 - delta4 + delta5);
00633 }
00634 
00645 PerturbationType
00646 createPerturbation (PointerListType *selected, double T)
00647 {
00648   PerturbationType pt = { 0 };
00649   /* pick element to perturb */
00650   pt.element = (ElementType *) selected->Ptr[random () % selected->PtrN];
00651   /* exchange, flip/rotate or shift? */
00652   switch (random () % ((selected->PtrN > 1) ? 3 : 2))
00653     {
00654     case 0:
00655       {                         /* shift! */
00656         Coord grid;
00657         double scaleX = CLAMP (sqrt (T), MIL_TO_COORD (2.5), PCB->MaxWidth / 3);
00658         double scaleY = CLAMP (sqrt (T), MIL_TO_COORD (2.5), PCB->MaxHeight / 3);
00659         pt.which = SHIFT;
00660         pt.DX = scaleX * 2 * ((((double) random ()) / RAND_MAX) - 0.5);
00661         pt.DY = scaleY * 2 * ((((double) random ()) / RAND_MAX) - 0.5);
00662         /* snap to grid. different grids for "high" and "low" T */
00663         grid = (T > MIL_TO_COORD (10)) ? CostParameter.large_grid_size :
00664           CostParameter.small_grid_size;
00665         /* (round away from zero) */
00666         pt.DX = ((pt.DX / grid) + SGN (pt.DX)) * grid;
00667         pt.DY = ((pt.DY / grid) + SGN (pt.DY)) * grid;
00668         /* limit DX/DY so we don't fall off board */
00669         pt.DX = MAX (pt.DX, -pt.element->VBox.X1);
00670         pt.DX = MIN (pt.DX, PCB->MaxWidth - pt.element->VBox.X2);
00671         pt.DY = MAX (pt.DY, -pt.element->VBox.Y1);
00672         pt.DY = MIN (pt.DY, PCB->MaxHeight - pt.element->VBox.Y2);
00673         /* all done but the movin' */
00674         break;
00675       }
00676     case 1:
00677       {                         /* flip/rotate! */
00678         /* only flip if it's an SMD component */
00679         bool isSMD = pt.element->PadN != 0;
00680         pt.which = ROTATE;
00681         pt.rotate = isSMD ? (random () & 3) : (1 + (random () % 3));
00682         /* 0 - flip; 1-3, rotate. */
00683         break;
00684       }
00685     case 2:
00686       {                         /* exchange! */
00687         pt.which = EXCHANGE;
00688         pt.other = (ElementType *)
00689           selected->Ptr[random () % (selected->PtrN - 1)];
00690         if (pt.other == pt.element)
00691           pt.other = (ElementType *) selected->Ptr[selected->PtrN - 1];
00692         /* don't allow exchanging a solderside-side SMD component
00693          * with a non-SMD component. */
00694         if ((pt.element->PinN != 0 /* non-SMD */  &&
00695              TEST_FLAG (ONSOLDERFLAG, pt.other)) ||
00696             (pt.other->PinN != 0 /* non-SMD */  &&
00697              TEST_FLAG (ONSOLDERFLAG, pt.element)))
00698           return createPerturbation (selected, T);
00699         break;
00700       }
00701     default:
00702       assert (0);
00703     }
00704   return pt;
00705 }
00706 
00707 void
00708 doPerturb (PerturbationType * pt, bool undo)
00709 {
00710   Coord bbcx, bbcy;
00711   /* compute center of element bounding box */
00712   bbcx = (pt->element->VBox.X1 + pt->element->VBox.X2) / 2;
00713   bbcy = (pt->element->VBox.Y1 + pt->element->VBox.Y2) / 2;
00714   /* do exchange, shift or flip/rotate */
00715   switch (pt->which)
00716     {
00717     case SHIFT:
00718       {
00719         Coord DX = pt->DX, DY = pt->DY;
00720         if (undo)
00721           {
00722             DX = -DX;
00723             DY = -DY;
00724           }
00725         MoveElementLowLevel (PCB->Data, pt->element, DX, DY);
00726         return;
00727       }
00728     case ROTATE:
00729       {
00730         unsigned b = pt->rotate;
00731         if (undo)
00732           b = (4 - b) & 3;
00733         /* 0 - flip; 1-3, rotate. */
00734         if (b)
00735           RotateElementLowLevel (PCB->Data, pt->element, bbcx, bbcy, b);
00736         else
00737           {
00738             Coord y = pt->element->VBox.Y1;
00739             MirrorElementCoordinates (PCB->Data, pt->element, 0);
00740             /* mirroring moves the element.  move it back. */
00741             MoveElementLowLevel (PCB->Data, pt->element, 0,
00742                                  y - pt->element->VBox.Y1);
00743           }
00744         return;
00745       }
00746     case EXCHANGE:
00747       {
00748         /* first exchange positions */
00749         Coord x1 = pt->element->VBox.X1;
00750         Coord y1 = pt->element->VBox.Y1;
00751         Coord x2 = pt->other->BoundingBox.X1;
00752         Coord y2 = pt->other->BoundingBox.Y1;
00753         MoveElementLowLevel (PCB->Data, pt->element, x2 - x1, y2 - y1);
00754         MoveElementLowLevel (PCB->Data, pt->other, x1 - x2, y1 - y2);
00755         /* then flip both elements if they are on opposite sides */
00756         if (TEST_FLAG (ONSOLDERFLAG, pt->element) !=
00757             TEST_FLAG (ONSOLDERFLAG, pt->other))
00758           {
00759             PerturbationType mypt;
00760             mypt.element = pt->element;
00761             mypt.which = ROTATE;
00762             mypt.rotate = 0;    /* flip */
00763             doPerturb (&mypt, undo);
00764             mypt.element = pt->other;
00765             doPerturb (&mypt, undo);
00766           }
00767         /* done */
00768         return;
00769       }
00770     default:
00771       assert (0);
00772     }
00773 }
00774 
00778 bool
00779 AutoPlaceSelected (void)
00780 {
00781   NetListType *Nets;
00782   PointerListType Selected = { 0, 0, NULL };
00783   PerturbationType pt;
00784   double C0, T0;
00785   bool changed = false;
00786 
00787   /* (initial netlist processing copied from AddAllRats) */
00788   /* the netlist library has the text form
00789    * ProcNetlist fills in the Netlist
00790    * structure the way the final routing
00791    * is supposed to look
00792    */
00793   Nets = ProcNetlist (&PCB->NetlistLib);
00794   if (!Nets)
00795     {
00796       Message (_("Can't add rat lines because no netlist is loaded.\n"));
00797       goto done;
00798     }
00799 
00800   Selected = collectSelectedElements ();
00801   if (Selected.PtrN == 0)
00802     {
00803       Message (_("No elements selected to autoplace.\n"));
00804       goto done;
00805     }
00806 
00807   /* simulated annealing */
00808   {                             /* compute T0 by doing a random series of moves. */
00809     const int TRIALS = 10;
00810     const double Tx = MIL_TO_COORD (300), P = 0.95;
00811     double Cs = 0.0;
00812     int i;
00813     C0 = ComputeCost (Nets, Tx, Tx);
00814     for (i = 0; i < TRIALS; i++)
00815       {
00816         pt = createPerturbation (&Selected, INCH_TO_COORD (1));
00817         doPerturb (&pt, false);
00818         Cs += fabs (ComputeCost (Nets, Tx, Tx) - C0);
00819         doPerturb (&pt, true);
00820       }
00821     T0 = -(Cs / TRIALS) / log (P);
00822     printf ("Initial T: %f\n", T0);
00823   }
00824   /* now anneal in earnest */
00825   {
00826     double T = T0;
00827     long steps = 0;
00828     int good_moves = 0, moves = 0;
00829     const int good_move_cutoff = CostParameter.m * Selected.PtrN;
00830     const int move_cutoff = 2 * good_move_cutoff;
00831     printf ("Starting cost is %.0f\n", ComputeCost (Nets, T0, 5));
00832     C0 = ComputeCost (Nets, T0, T);
00833     while (1)
00834       {
00835         double Cprime;
00836         pt = createPerturbation (&Selected, T);
00837         doPerturb (&pt, false);
00838         Cprime = ComputeCost (Nets, T0, T);
00839         if (Cprime < C0)
00840           {                     /* good move! */
00841             C0 = Cprime;
00842             good_moves++;
00843             steps++;
00844           }
00845         else if ((random () / (double) RAND_MAX) <
00846                  exp (MIN (MAX (-20, (C0 - Cprime) / T), 20)))
00847           {
00848             /* not good but keep it anyway */
00849             C0 = Cprime;
00850             steps++;
00851           }
00852         else
00853           doPerturb (&pt, true);        /* undo last change */
00854         moves++;
00855         /* are we at the end of a stage? */
00856         if (good_moves >= good_move_cutoff || moves >= move_cutoff)
00857           {
00858             printf ("END OF STAGE: COST %.0f\t"
00859                     "GOOD_MOVES %d\tMOVES %d\t"
00860                     "T: %.1f\n", C0, good_moves, moves, T);
00861             /* is this the end? */
00862             if (T < 5 || good_moves < moves / CostParameter.good_ratio)
00863               break;
00864             /* nope, adjust T and continue */
00865             moves = good_moves = 0;
00866             T *= CostParameter.gamma;
00867             /* cost is T dependent, so recompute */
00868             C0 = ComputeCost (Nets, T0, T);
00869           }
00870       }
00871     changed = (steps > 0);
00872   }
00873 done:
00874   if (changed)
00875     {
00876       DeleteRats (false);
00877       AddAllRats (false, NULL);
00878       Redraw ();
00879     }
00880   FreePointerListMemory (&Selected);
00881   return (changed);
00882 }