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