pcb 4.1.1
An interactive printed circuit board layout editor.
|
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 }