(no commit message)
[geda-pcb/pcjc2.git] / src / autoplace.c
blob5c53757aa06cd962a2eb5ae506e0baf91f79d584
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996 Thomas Nau
6 * Copyright (C) 1998,1999,2000,2001 harry eaton
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * Contact addresses for paper mail and Email:
23 * harry eaton, 6697 Buttonhole Ct, Columbia, MD 21044 USA
24 * haceaton@aplcomm.jhuapl.edu
29 * This moduel, autoplace.c, was written by and is
30 * Copyright (c) 2001 C. Scott Ananian
33 /* functions used to autoplace elements.
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
40 #include <assert.h>
41 #include <math.h>
42 #include <memory.h>
43 #include <stdlib.h>
45 #include "global.h"
47 #include "autoplace.h"
48 #include "box.h"
49 #include "compat.h"
50 #include "data.h"
51 #include "draw.h"
52 #include "error.h"
53 #include "intersect.h"
54 #include "rtree.h"
55 #include "macro.h"
56 #include "mirror.h"
57 #include "misc.h"
58 #include "move.h"
59 #include "mymem.h"
60 #include "rats.h"
61 #include "remove.h"
62 #include "rotate.h"
64 #ifdef HAVE_LIBDMALLOC
65 #include <dmalloc.h>
66 #endif
68 #define EXPANDRECTXY(r1, x1, y1, x2, y2) { \
69 r1->X1=MIN(r1->X1, x1); r1->Y1=MIN(r1->Y1, y1); \
70 r1->X2=MAX(r1->X2, x2); r1->Y2=MAX(r1->Y2, y2); \
72 #define EXPANDRECT(r1, r2) EXPANDRECTXY(r1, r2->X1, r2->Y1, r2->X2, r2->Y2)
74 /* ---------------------------------------------------------------------------
75 * some local prototypes
77 static double ComputeCost (NetListType *Nets, double T0, double T);
79 /* ---------------------------------------------------------------------------
80 * some local types
82 const struct
84 double via_cost;
85 double congestion_penalty; /* penalty length / unit area */
86 double overlap_penalty_min; /* penalty length / unit area at start */
87 double overlap_penalty_max; /* penalty length / unit area at end */
88 double out_of_bounds_penalty; /* assessed for each component oob */
89 double overall_area_penalty; /* penalty length / unit area */
90 double matching_neighbor_bonus; /* length bonus per same-type neigh. */
91 double aligned_neighbor_bonus; /* length bonus per aligned neigh. */
92 double oriented_neighbor_bonus; /* length bonus per same-rot neigh. */
93 #if 0
94 double pin_alignment_bonus; /* length bonus per exact alignment */
95 double bound_alignment_bonus; /* length bonus per exact alignment */
96 #endif
97 double m; /* annealing stage cutoff constant */
98 double gamma; /* annealing schedule constant */
99 int good_ratio; /* ratio of moves to good moves for halting */
100 bool fast; /* ignore SMD/pin conflicts */
101 Coord large_grid_size; /* snap perturbations to this grid when T is high */
102 Coord small_grid_size; /* snap to this grid when T is small. */
104 /* wire cost is manhattan distance (in mils), thus 1 inch = 1000 */
105 CostParameter =
107 3e3, /* via cost */
108 2e-2, /* congestion penalty */
109 1e-2, /* initial overlap penalty */
110 1e2, /* final overlap penalty */
111 1e3, /* out of bounds penalty */
112 1e0, /* penalty for total area used */
113 1e0, /* subtract 1000 from cost for every same-type neighbor */
114 1e0, /* subtract 1000 from cost for every aligned neighbor */
115 1e0, /* subtract 1000 from cost for every same-rotation neighbor */
116 20, /* move on when each module has been profitably moved 20 times */
117 0.75, /* annealing schedule constant: 0.85 */
118 40, /* halt when there are 60 times as many moves as good moves */
119 false, /* don't ignore SMD/pin conflicts */
120 MIL_TO_COORD (100), /* coarse grid is 100 mils */
121 MIL_TO_COORD (10), /* fine grid is 10 mils */
124 typedef struct
126 ElementType **element;
127 Cardinal elementN;
129 ElementPtrListType;
131 enum ewhich
132 { SHIFT, ROTATE, EXCHANGE };
134 typedef struct
136 ElementType *element;
137 enum ewhich which;
138 Coord DX, DY; /* for shift */
139 unsigned rotate; /* for rotate/flip */
140 ElementType *other; /* for exchange */
142 PerturbationType;
144 /* ---------------------------------------------------------------------------
145 * some local identifiers
148 /* ---------------------------------------------------------------------------
149 * Update the X, Y and group position information stored in the NetList after
150 * elements have possibly been moved, rotated, flipped, etc.
152 static void
153 UpdateXY (NetListType *Nets)
155 Cardinal top_group, bottom_group;
156 Cardinal i, j;
157 /* find layer groups of the top and bottom sides */
158 top_group = GetLayerGroupNumberBySide (TOP_SIDE);
159 bottom_group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
160 /* update all nets */
161 for (i = 0; i < Nets->NetN; i++)
163 for (j = 0; j < Nets->Net[i].ConnectionN; j++)
165 ConnectionType *c = &(Nets->Net[i].Connection[j]);
166 switch (c->type)
168 case PAD_TYPE:
169 c->group = TEST_FLAG (ONSOLDERFLAG, (ElementType *) c->ptr1)
170 ? bottom_group : top_group;
171 c->X = ((PadType *) c->ptr2)->Point1.X;
172 c->Y = ((PadType *) c->ptr2)->Point1.Y;
173 break;
174 case PIN_TYPE:
175 c->group = bottom_group; /* any layer will do */
176 c->X = ((PinType *) c->ptr2)->X;
177 c->Y = ((PinType *) c->ptr2)->Y;
178 break;
179 default:
180 Message ("Odd connection type encountered in " "UpdateXY");
181 break;
187 /* ---------------------------------------------------------------------------
188 * Create a list of selected elements.
190 static PointerListType
191 collectSelectedElements ()
193 PointerListType list = { 0, 0, NULL };
194 ELEMENT_LOOP (PCB->Data);
196 if (TEST_FLAG (SELECTEDFLAG, element))
198 ElementType **epp = (ElementType **) GetPointerMemory (&list);
199 *epp = element;
202 END_LOOP;
203 return list;
206 #if 0 /* only for debugging box lists */
207 #include "create.h"
208 /* makes a line on the bottom silk layer surrounding all boxes in blist */
209 static void
210 showboxes (BoxListType *blist)
212 Cardinal i;
213 LayerType *layer = &(PCB->Data->Layer[bottom_silk_layer]);
214 for (i = 0; i < blist->BoxN; i++)
216 CreateNewLineOnLayer (layer, blist->Box[i].X1, blist->Box[i].Y1,
217 blist->Box[i].X2, blist->Box[i].Y1, 1, 1, 0);
218 CreateNewLineOnLayer (layer, blist->Box[i].X1, blist->Box[i].Y2,
219 blist->Box[i].X2, blist->Box[i].Y2, 1, 1, 0);
220 CreateNewLineOnLayer (layer, blist->Box[i].X1, blist->Box[i].Y1,
221 blist->Box[i].X1, blist->Box[i].Y2, 1, 1, 0);
222 CreateNewLineOnLayer (layer, blist->Box[i].X2, blist->Box[i].Y1,
223 blist->Box[i].X2, blist->Box[i].Y2, 1, 1, 0);
226 #endif
228 /* ---------------------------------------------------------------------------
229 * Helper function to compute "closest neighbor" for a box in a rtree.
230 * The closest neighbor on a certain side is the closest one in a trapezoid
231 * emanating from that side.
233 /*------ r_find_neighbor ------*/
234 struct r_neighbor_info
236 const BoxType *neighbor;
237 BoxType trap;
238 direction_t search_dir;
240 #define ROTATEBOX(box) { Coord t;\
241 t = (box).X1; (box).X1 = - (box).Y1; (box).Y1 = t;\
242 t = (box).X2; (box).X2 = - (box).Y2; (box).Y2 = t;\
243 t = (box).X1; (box).X1 = (box).X2; (box).X2 = t;\
245 /* helper methods for __r_find_neighbor */
246 static int
247 __r_find_neighbor_reg_in_sea (const BoxType * region, void *cl)
249 struct r_neighbor_info *ni = (struct r_neighbor_info *) cl;
250 BoxType query = *region;
251 ROTATEBOX_TO_NORTH (query, ni->search_dir);
252 /* ______________ __ trap.y1 __
253 * \ / |__| query rect.
254 * \__________/ __ trap.y2
255 * | |
256 * trap.x1 trap.x2 sides at 45-degree angle
258 return (query.Y2 > ni->trap.Y1) && (query.Y1 < ni->trap.Y2) &&
259 (query.X2 + ni->trap.Y2 > ni->trap.X1 + query.Y1) &&
260 (query.X1 + query.Y1 < ni->trap.X2 + ni->trap.Y2);
262 static int
263 __r_find_neighbor_rect_in_reg (const BoxType * box, void *cl)
265 struct r_neighbor_info *ni = (struct r_neighbor_info *) cl;
266 BoxType query = *box;
267 int r;
268 ROTATEBOX_TO_NORTH (query, ni->search_dir);
269 /* ______________ __ trap.y1 __
270 * \ / |__| query rect.
271 * \__________/ __ trap.y2
272 * | |
273 * trap.x1 trap.x2 sides at 45-degree angle
275 r = (query.Y2 > ni->trap.Y1) && (query.Y1 < ni->trap.Y2) &&
276 (query.X2 + ni->trap.Y2 > ni->trap.X1 + query.Y1) &&
277 (query.X1 + query.Y1 < ni->trap.X2 + ni->trap.Y2);
278 r = r && (query.Y2 <= ni->trap.Y2);
279 if (r)
281 ni->trap.Y1 = query.Y2;
282 ni->neighbor = box;
284 return r;
287 /* main r_find_neighbor routine. Returns NULL if no neighbor in the
288 * requested direction. */
289 static const BoxType *
290 r_find_neighbor (rtree_t * rtree, const BoxType * box,
291 direction_t search_direction)
293 struct r_neighbor_info ni;
294 BoxType bbox;
296 ni.neighbor = NULL;
297 ni.trap = *box;
298 ni.search_dir = search_direction;
300 bbox.X1 = bbox.Y1 = 0;
301 bbox.X2 = PCB->MaxWidth;
302 bbox.Y2 = PCB->MaxHeight;
303 /* rotate so that we can use the 'north' case for everything */
304 ROTATEBOX_TO_NORTH (bbox, search_direction);
305 ROTATEBOX_TO_NORTH (ni.trap, search_direction);
306 /* shift Y's such that trap contains full bounds of trapezoid */
307 ni.trap.Y2 = ni.trap.Y1;
308 ni.trap.Y1 = bbox.Y1;
309 /* do the search! */
310 r_search (rtree, NULL,
311 __r_find_neighbor_reg_in_sea, __r_find_neighbor_rect_in_reg, &ni);
312 return ni.neighbor;
315 /* ---------------------------------------------------------------------------
316 * Compute cost function.
317 * note that area overlap cost is correct for SMD devices: SMD devices on
318 * opposite sides of the board don't overlap.
320 * Algorithms follow those described in sections 4.1 of
321 * "Placement and Routing of Electronic Modules" edited by Michael Pecht
322 * Marcel Dekker, Inc. 1993. ISBN: 0-8247-8916-4 TK7868.P7.P57 1993
324 static double
325 ComputeCost (NetListType *Nets, double T0, double T)
327 double W = 0; /* wire cost */
328 double delta1 = 0; /* wire congestion penalty function */
329 double delta2 = 0; /* module overlap penalty function */
330 double delta3 = 0; /* out of bounds penalty */
331 double delta4 = 0; /* alignment bonus */
332 double delta5 = 0; /* total area penalty */
333 Cardinal i, j;
334 Coord minx, maxx, miny, maxy;
335 bool allpads, allsameside;
336 Cardinal thegroup;
337 BoxListType bounds = { 0, 0, NULL }; /* save bounding rectangles here */
338 BoxListType solderside = { 0, 0, NULL }; /* solder side component bounds */
339 BoxListType componentside = { 0, 0, NULL }; /* component side bounds */
340 /* make sure the NetList have the proper updated X and Y coords */
341 UpdateXY (Nets);
342 /* wire length term. approximated by half-perimeter of minimum
343 * rectangle enclosing the net. Note that we penalize vias in
344 * all-SMD nets by making the rectangle a cube and weighting
345 * the "layer height" of the net. */
346 for (i = 0; i < Nets->NetN; i++)
348 NetType *n = &Nets->Net[i];
349 if (n->ConnectionN < 2)
350 continue; /* no cost to go nowhere */
351 minx = maxx = n->Connection[0].X;
352 miny = maxy = n->Connection[0].Y;
353 thegroup = n->Connection[0].group;
354 allpads = (n->Connection[0].type == PAD_TYPE);
355 allsameside = true;
356 for (j = 1; j < n->ConnectionN; j++)
358 ConnectionType *c = &(n->Connection[j]);
359 MAKEMIN (minx, c->X);
360 MAKEMAX (maxx, c->X);
361 MAKEMIN (miny, c->Y);
362 MAKEMAX (maxy, c->Y);
363 if (c->type != PAD_TYPE)
364 allpads = false;
365 if (c->group != thegroup)
366 allsameside = false;
368 /* save bounding rectangle */
370 BoxType *box = GetBoxMemory (&bounds);
371 box->X1 = minx;
372 box->Y1 = miny;
373 box->X2 = maxx;
374 box->Y2 = maxy;
376 /* okay, add half-perimeter to cost! */
377 W += COORD_TO_MIL(maxx - minx) + COORD_TO_MIL(maxy - miny) +
378 ((allpads && !allsameside) ? CostParameter.via_cost : 0);
380 /* now compute penalty function Wc which is proportional to
381 * amount of overlap and congestion. */
382 /* delta1 is congestion penalty function */
383 delta1 = CostParameter.congestion_penalty *
384 sqrt (fabs (ComputeIntersectionArea (&bounds)));
385 #if 0
386 printf ("Wire Congestion Area: %f\n", ComputeIntersectionArea (&bounds));
387 #endif
388 /* free bounding rectangles */
389 FreeBoxListMemory (&bounds);
390 /* now collect module areas (bounding rect of pins/pads) */
391 /* two lists for solder side / component side. */
393 ELEMENT_LOOP (PCB->Data);
395 BoxListType *thisside;
396 BoxListType *otherside;
397 BoxType *box;
398 BoxType *lastbox = NULL;
399 Coord thickness;
400 Coord clearance;
401 if (TEST_FLAG (ONSOLDERFLAG, element))
403 thisside = &solderside;
404 otherside = &componentside;
406 else
408 thisside = &componentside;
409 otherside = &solderside;
411 box = GetBoxMemory (thisside);
412 /* protect against elements with no pins/pads */
413 if (element->PinN == 0 && element->PadN == 0)
414 continue;
415 /* initialize box so that it will take the dimensions of
416 * the first pin/pad */
417 box->X1 = MAX_COORD;
418 box->Y1 = MAX_COORD;
419 box->X2 = -MAX_COORD;
420 box->Y2 = -MAX_COORD;
421 PIN_LOOP (element);
423 thickness = pin->Thickness / 2;
424 clearance = pin->Clearance * 2;
425 EXPANDRECTXY (box,
426 pin->X - (thickness + clearance),
427 pin->Y - (thickness + clearance),
428 pin->X + (thickness + clearance),
429 pin->Y + (thickness + clearance))}
430 END_LOOP;
431 PAD_LOOP (element);
433 thickness = pad->Thickness / 2;
434 clearance = pad->Clearance * 2;
435 EXPANDRECTXY (box,
436 MIN (pad->Point1.X,
437 pad->Point2.X) - (thickness +
438 clearance),
439 MIN (pad->Point1.Y,
440 pad->Point2.Y) - (thickness +
441 clearance),
442 MAX (pad->Point1.X,
443 pad->Point2.X) + (thickness +
444 clearance),
445 MAX (pad->Point1.Y,
446 pad->Point2.Y) + (thickness + clearance))}
447 END_LOOP;
448 /* add a box for each pin to the "opposite side":
449 * surface mount components can't sit on top of pins */
450 if (!CostParameter.fast)
451 PIN_LOOP (element);
453 box = GetBoxMemory (otherside);
454 thickness = pin->Thickness / 2;
455 clearance = pin->Clearance * 2;
456 /* we ignore clearance here */
457 /* (otherwise pins don't fit next to each other) */
458 box->X1 = pin->X - thickness;
459 box->Y1 = pin->Y - thickness;
460 box->X2 = pin->X + thickness;
461 box->Y2 = pin->Y + thickness;
462 /* speed hack! coalesce with last box if we can */
463 if (lastbox != NULL &&
464 ((lastbox->X1 == box->X1 &&
465 lastbox->X2 == box->X2 &&
466 MIN (abs (lastbox->Y1 - box->Y2),
467 abs (box->Y1 - lastbox->Y2)) <
468 clearance) || (lastbox->Y1 == box->Y1
469 && lastbox->Y2 == box->Y2
471 MIN (abs
472 (lastbox->X1 -
473 box->X2),
474 abs (box->X1 - lastbox->X2)) < clearance)))
476 EXPANDRECT (lastbox, box);
477 otherside->BoxN--;
479 else
480 lastbox = box;
482 END_LOOP;
483 /* assess out of bounds penalty */
484 if (element->VBox.X1 < 0 ||
485 element->VBox.Y1 < 0 ||
486 element->VBox.X2 > PCB->MaxWidth || element->VBox.Y2 > PCB->MaxHeight)
487 delta3 += CostParameter.out_of_bounds_penalty;
489 END_LOOP;
490 /* compute intersection area of module areas box list */
491 delta2 = sqrt (fabs (ComputeIntersectionArea (&solderside) +
492 ComputeIntersectionArea (&componentside))) *
493 (CostParameter.overlap_penalty_min +
494 (1 - (T / T0)) * CostParameter.overlap_penalty_max);
495 #if 0
496 printf ("Module Overlap Area (solder): %f\n",
497 ComputeIntersectionArea (&solderside));
498 printf ("Module Overlap Area (component): %f\n",
499 ComputeIntersectionArea (&componentside));
500 #endif
501 FreeBoxListMemory (&solderside);
502 FreeBoxListMemory (&componentside);
503 /* reward pin/pad x/y alignment */
504 /* score higher if pins/pads belong to same *type* of component */
505 /* XXX: subkey should be *distance* from thing aligned with, so that
506 * aligning to something far away isn't profitable */
508 /* create r tree */
509 PointerListType seboxes = { 0, 0, NULL }
510 , ceboxes =
512 0, 0, NULL};
513 struct ebox
515 BoxType box;
516 ElementType *element;
518 direction_t dir[4] = { NORTH, EAST, SOUTH, WEST };
519 struct ebox **boxpp, *boxp;
520 rtree_t *rt_s, *rt_c;
521 int factor;
522 ELEMENT_LOOP (PCB->Data);
524 boxpp = (struct ebox **)
525 GetPointerMemory (TEST_FLAG (ONSOLDERFLAG, element) ?
526 &seboxes : &ceboxes);
527 *boxpp = (struct ebox *)malloc (sizeof (**boxpp));
528 if (*boxpp == NULL )
530 fprintf (stderr, "malloc() failed in %s\n", __FUNCTION__);
531 exit (1);
534 (*boxpp)->box = element->VBox;
535 (*boxpp)->element = element;
537 END_LOOP;
538 rt_s = r_create_tree ((const BoxType **) seboxes.Ptr, seboxes.PtrN, 1);
539 rt_c = r_create_tree ((const BoxType **) ceboxes.Ptr, ceboxes.PtrN, 1);
540 FreePointerListMemory (&seboxes);
541 FreePointerListMemory (&ceboxes);
542 /* now, for each element, find its neighbor on all four sides */
543 delta4 = 0;
544 for (i = 0; i < 4; i++)
545 ELEMENT_LOOP (PCB->Data);
547 boxp = (struct ebox *)
548 r_find_neighbor (TEST_FLAG (ONSOLDERFLAG, element) ?
549 rt_s : rt_c, &element->VBox, dir[i]);
550 /* score bounding box alignments */
551 if (!boxp)
552 continue;
553 factor = 1;
554 if (element->Name[0].TextString &&
555 boxp->element->Name[0].TextString &&
556 0 == NSTRCMP (element->Name[0].TextString,
557 boxp->element->Name[0].TextString))
559 delta4 += CostParameter.matching_neighbor_bonus;
560 factor++;
562 if (element->Name[0].Direction == boxp->element->Name[0].Direction)
563 delta4 += factor * CostParameter.oriented_neighbor_bonus;
564 if (element->VBox.X1 == boxp->element->VBox.X1 ||
565 element->VBox.X1 == boxp->element->VBox.X2 ||
566 element->VBox.X2 == boxp->element->VBox.X1 ||
567 element->VBox.X2 == boxp->element->VBox.X2 ||
568 element->VBox.Y1 == boxp->element->VBox.Y1 ||
569 element->VBox.Y1 == boxp->element->VBox.Y2 ||
570 element->VBox.Y2 == boxp->element->VBox.Y1 ||
571 element->VBox.Y2 == boxp->element->VBox.Y2)
572 delta4 += factor * CostParameter.aligned_neighbor_bonus;
574 END_LOOP;
575 /* free k-d tree memory */
576 r_destroy_tree (&rt_s);
577 r_destroy_tree (&rt_c);
579 /* penalize total area used by this layout */
581 Coord minX = MAX_COORD, minY = MAX_COORD;
582 Coord maxX = -MAX_COORD, maxY = -MAX_COORD;
583 ELEMENT_LOOP (PCB->Data);
585 MAKEMIN (minX, element->VBox.X1);
586 MAKEMIN (minY, element->VBox.Y1);
587 MAKEMAX (maxX, element->VBox.X2);
588 MAKEMAX (maxY, element->VBox.Y2);
590 END_LOOP;
591 if (minX < maxX && minY < maxY)
592 delta5 = CostParameter.overall_area_penalty *
593 sqrt (COORD_TO_MIL (maxX - minX) * COORD_TO_MIL (maxY - minY));
595 if (T == 5)
597 T = W + delta1 + delta2 + delta3 - delta4 + delta5;
598 printf ("cost components are %.3f %.3f %.3f %.3f %.3f %.3f\n",
599 W / T, delta1 / T, delta2 / T, delta3 / T, -delta4 / T,
600 delta5 / T);
602 /* done! */
603 return W + (delta1 + delta2 + delta3 - delta4 + delta5);
606 /* ---------------------------------------------------------------------------
607 * Perturb:
608 * 1) flip SMD from solder side to component side or vice-versa.
609 * 2) rotate component 90, 180, or 270 degrees.
610 * 3) shift component random + or - amount in random direction.
611 * (magnitude of shift decreases over time)
612 * -- Only perturb selected elements (need count/list of selected?) --
614 PerturbationType
615 createPerturbation (PointerListType *selected, double T)
617 PerturbationType pt = { 0 };
618 /* pick element to perturb */
619 pt.element = (ElementType *) selected->Ptr[random () % selected->PtrN];
620 /* exchange, flip/rotate or shift? */
621 switch (random () % ((selected->PtrN > 1) ? 3 : 2))
623 case 0:
624 { /* shift! */
625 Coord grid;
626 double scaleX = CLAMP (sqrt (T), MIL_TO_COORD (2.5), PCB->MaxWidth / 3);
627 double scaleY = CLAMP (sqrt (T), MIL_TO_COORD (2.5), PCB->MaxHeight / 3);
628 pt.which = SHIFT;
629 pt.DX = scaleX * 2 * ((((double) random ()) / RAND_MAX) - 0.5);
630 pt.DY = scaleY * 2 * ((((double) random ()) / RAND_MAX) - 0.5);
631 /* snap to grid. different grids for "high" and "low" T */
632 grid = (T > MIL_TO_COORD (10)) ? CostParameter.large_grid_size :
633 CostParameter.small_grid_size;
634 /* (round away from zero) */
635 pt.DX = ((pt.DX / grid) + SGN (pt.DX)) * grid;
636 pt.DY = ((pt.DY / grid) + SGN (pt.DY)) * grid;
637 /* limit DX/DY so we don't fall off board */
638 pt.DX = MAX (pt.DX, -pt.element->VBox.X1);
639 pt.DX = MIN (pt.DX, PCB->MaxWidth - pt.element->VBox.X2);
640 pt.DY = MAX (pt.DY, -pt.element->VBox.Y1);
641 pt.DY = MIN (pt.DY, PCB->MaxHeight - pt.element->VBox.Y2);
642 /* all done but the movin' */
643 break;
645 case 1:
646 { /* flip/rotate! */
647 /* only flip if it's an SMD component */
648 bool isSMD = pt.element->PadN != 0;
649 pt.which = ROTATE;
650 pt.rotate = isSMD ? (random () & 3) : (1 + (random () % 3));
651 /* 0 - flip; 1-3, rotate. */
652 break;
654 case 2:
655 { /* exchange! */
656 pt.which = EXCHANGE;
657 pt.other = (ElementType *)
658 selected->Ptr[random () % (selected->PtrN - 1)];
659 if (pt.other == pt.element)
660 pt.other = (ElementType *) selected->Ptr[selected->PtrN - 1];
661 /* don't allow exchanging a solderside-side SMD component
662 * with a non-SMD component. */
663 if ((pt.element->PinN != 0 /* non-SMD */ &&
664 TEST_FLAG (ONSOLDERFLAG, pt.other)) ||
665 (pt.other->PinN != 0 /* non-SMD */ &&
666 TEST_FLAG (ONSOLDERFLAG, pt.element)))
667 return createPerturbation (selected, T);
668 break;
670 default:
671 assert (0);
673 return pt;
676 void
677 doPerturb (PerturbationType * pt, bool undo)
679 Coord bbcx, bbcy;
680 /* compute center of element bounding box */
681 bbcx = (pt->element->VBox.X1 + pt->element->VBox.X2) / 2;
682 bbcy = (pt->element->VBox.Y1 + pt->element->VBox.Y2) / 2;
683 /* do exchange, shift or flip/rotate */
684 switch (pt->which)
686 case SHIFT:
688 Coord DX = pt->DX, DY = pt->DY;
689 if (undo)
691 DX = -DX;
692 DY = -DY;
694 MoveElementLowLevel (PCB->Data, pt->element, DX, DY);
695 return;
697 case ROTATE:
699 unsigned b = pt->rotate;
700 if (undo)
701 b = (4 - b) & 3;
702 /* 0 - flip; 1-3, rotate. */
703 if (b)
704 RotateElementLowLevel (PCB->Data, pt->element, bbcx, bbcy, b);
705 else
707 Coord y = pt->element->VBox.Y1;
708 MirrorElementCoordinates (PCB->Data, pt->element, 0);
709 /* mirroring moves the element. move it back. */
710 MoveElementLowLevel (PCB->Data, pt->element, 0,
711 y - pt->element->VBox.Y1);
713 return;
715 case EXCHANGE:
717 /* first exchange positions */
718 Coord x1 = pt->element->VBox.X1;
719 Coord y1 = pt->element->VBox.Y1;
720 Coord x2 = pt->other->BoundingBox.X1;
721 Coord y2 = pt->other->BoundingBox.Y1;
722 MoveElementLowLevel (PCB->Data, pt->element, x2 - x1, y2 - y1);
723 MoveElementLowLevel (PCB->Data, pt->other, x1 - x2, y1 - y2);
724 /* then flip both elements if they are on opposite sides */
725 if (TEST_FLAG (ONSOLDERFLAG, pt->element) !=
726 TEST_FLAG (ONSOLDERFLAG, pt->other))
728 PerturbationType mypt;
729 mypt.element = pt->element;
730 mypt.which = ROTATE;
731 mypt.rotate = 0; /* flip */
732 doPerturb (&mypt, undo);
733 mypt.element = pt->other;
734 doPerturb (&mypt, undo);
736 /* done */
737 return;
739 default:
740 assert (0);
744 /* ---------------------------------------------------------------------------
745 * Auto-place selected components.
747 bool
748 AutoPlaceSelected (void)
750 NetListType *Nets;
751 PointerListType Selected = { 0, 0, NULL };
752 PerturbationType pt;
753 double C0, T0;
754 bool changed = false;
756 /* (initial netlist processing copied from AddAllRats) */
757 /* the netlist library has the text form
758 * ProcNetlist fills in the Netlist
759 * structure the way the final routing
760 * is supposed to look
762 Nets = ProcNetlist (&PCB->NetlistLib);
763 if (!Nets)
765 Message (_("Can't add rat lines because no netlist is loaded.\n"));
766 goto done;
769 Selected = collectSelectedElements ();
770 if (Selected.PtrN == 0)
772 Message (_("No elements selected to autoplace.\n"));
773 goto done;
776 /* simulated annealing */
777 { /* compute T0 by doing a random series of moves. */
778 const int TRIALS = 10;
779 const double Tx = MIL_TO_COORD (300), P = 0.95;
780 double Cs = 0.0;
781 int i;
782 C0 = ComputeCost (Nets, Tx, Tx);
783 for (i = 0; i < TRIALS; i++)
785 pt = createPerturbation (&Selected, INCH_TO_COORD (1));
786 doPerturb (&pt, false);
787 Cs += fabs (ComputeCost (Nets, Tx, Tx) - C0);
788 doPerturb (&pt, true);
790 T0 = -(Cs / TRIALS) / log (P);
791 printf ("Initial T: %f\n", T0);
793 /* now anneal in earnest */
795 double T = T0;
796 long steps = 0;
797 int good_moves = 0, moves = 0;
798 const int good_move_cutoff = CostParameter.m * Selected.PtrN;
799 const int move_cutoff = 2 * good_move_cutoff;
800 printf ("Starting cost is %.0f\n", ComputeCost (Nets, T0, 5));
801 C0 = ComputeCost (Nets, T0, T);
802 while (1)
804 double Cprime;
805 pt = createPerturbation (&Selected, T);
806 doPerturb (&pt, false);
807 Cprime = ComputeCost (Nets, T0, T);
808 if (Cprime < C0)
809 { /* good move! */
810 C0 = Cprime;
811 good_moves++;
812 steps++;
814 else if ((random () / (double) RAND_MAX) <
815 exp (MIN (MAX (-20, (C0 - Cprime) / T), 20)))
817 /* not good but keep it anyway */
818 C0 = Cprime;
819 steps++;
821 else
822 doPerturb (&pt, true); /* undo last change */
823 moves++;
824 /* are we at the end of a stage? */
825 if (good_moves >= good_move_cutoff || moves >= move_cutoff)
827 printf ("END OF STAGE: COST %.0f\t"
828 "GOOD_MOVES %d\tMOVES %d\t"
829 "T: %.1f\n", C0, good_moves, moves, T);
830 /* is this the end? */
831 if (T < 5 || good_moves < moves / CostParameter.good_ratio)
832 break;
833 /* nope, adjust T and continue */
834 moves = good_moves = 0;
835 T *= CostParameter.gamma;
836 /* cost is T dependent, so recompute */
837 C0 = ComputeCost (Nets, T0, T);
840 changed = (steps > 0);
842 done:
843 if (changed)
845 DeleteRats (false);
846 AddAllRats (false, NULL);
847 Redraw ();
849 FreePointerListMemory (&Selected);
850 return (changed);