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.
47 #include "autoplace.h"
53 #include "intersect.h"
64 #ifdef HAVE_LIBDMALLOC
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 /* ---------------------------------------------------------------------------
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. */
94 double pin_alignment_bonus
; /* length bonus per exact alignment */
95 double bound_alignment_bonus
; /* length bonus per exact alignment */
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 */
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 */
126 ElementType
**element
;
132 { SHIFT
, ROTATE
, EXCHANGE
};
136 ElementType
*element
;
138 Coord DX
, DY
; /* for shift */
139 unsigned rotate
; /* for rotate/flip */
140 ElementType
*other
; /* for exchange */
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.
153 UpdateXY (NetListType
*Nets
)
155 Cardinal top_group
, bottom_group
;
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
]);
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
;
175 c
->group
= bottom_group
; /* any layer will do */
176 c
->X
= ((PinType
*) c
->ptr2
)->X
;
177 c
->Y
= ((PinType
*) c
->ptr2
)->Y
;
180 Message ("Odd connection type encountered in " "UpdateXY");
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
);
206 #if 0 /* only for debugging box lists */
208 /* makes a line on the bottom silk layer surrounding all boxes in blist */
210 showboxes (BoxListType
*blist
)
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);
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
;
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 */
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
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
);
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
;
268 ROTATEBOX_TO_NORTH (query
, ni
->search_dir
);
269 /* ______________ __ trap.y1 __
270 * \ / |__| query rect.
271 * \__________/ __ trap.y2
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
);
281 ni
->trap
.Y1
= query
.Y2
;
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
;
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
;
310 r_search (rtree
, NULL
,
311 __r_find_neighbor_reg_in_sea
, __r_find_neighbor_rect_in_reg
, &ni
);
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
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 */
334 Coord minx
, maxx
, miny
, maxy
;
335 bool allpads
, allsameside
;
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 */
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
);
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
)
365 if (c
->group
!= thegroup
)
368 /* save bounding rectangle */
370 BoxType
*box
= GetBoxMemory (&bounds
);
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
)));
386 printf ("Wire Congestion Area: %f\n", ComputeIntersectionArea (&bounds
));
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
;
398 BoxType
*lastbox
= NULL
;
401 if (TEST_FLAG (ONSOLDERFLAG
, element
))
403 thisside
= &solderside
;
404 otherside
= &componentside
;
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)
415 /* initialize box so that it will take the dimensions of
416 * the first pin/pad */
419 box
->X2
= -MAX_COORD
;
420 box
->Y2
= -MAX_COORD
;
423 thickness
= pin
->Thickness
/ 2;
424 clearance
= pin
->Clearance
* 2;
426 pin
->X
- (thickness
+ clearance
),
427 pin
->Y
- (thickness
+ clearance
),
428 pin
->X
+ (thickness
+ clearance
),
429 pin
->Y
+ (thickness
+ clearance
))}
433 thickness
= pad
->Thickness
/ 2;
434 clearance
= pad
->Clearance
* 2;
437 pad
->Point2
.X
) - (thickness
+
440 pad
->Point2
.Y
) - (thickness
+
443 pad
->Point2
.X
) + (thickness
+
446 pad
->Point2
.Y
) + (thickness
+ clearance
))}
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
)
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
474 abs (box
->X1
- lastbox
->X2
)) < clearance
)))
476 EXPANDRECT (lastbox
, box
);
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
;
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
);
496 printf ("Module Overlap Area (solder): %f\n",
497 ComputeIntersectionArea (&solderside
));
498 printf ("Module Overlap Area (component): %f\n",
499 ComputeIntersectionArea (&componentside
));
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 */
509 PointerListType seboxes
= { 0, 0, NULL
}
516 ElementType
*element
;
518 direction_t dir
[4] = { NORTH
, EAST
, SOUTH
, WEST
};
519 struct ebox
**boxpp
, *boxp
;
520 rtree_t
*rt_s
, *rt_c
;
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
));
530 fprintf (stderr
, "malloc() failed in %s\n", __FUNCTION__
);
534 (*boxpp
)->box
= element
->VBox
;
535 (*boxpp
)->element
= element
;
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 */
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 */
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
;
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
;
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
);
591 if (minX
< maxX
&& minY
< maxY
)
592 delta5
= CostParameter
.overall_area_penalty
*
593 sqrt (COORD_TO_MIL (maxX
- minX
) * COORD_TO_MIL (maxY
- minY
));
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
,
603 return W
+ (delta1
+ delta2
+ delta3
- delta4
+ delta5
);
606 /* ---------------------------------------------------------------------------
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?) --
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))
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);
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' */
647 /* only flip if it's an SMD component */
648 bool isSMD
= pt
.element
->PadN
!= 0;
650 pt
.rotate
= isSMD
? (random () & 3) : (1 + (random () % 3));
651 /* 0 - flip; 1-3, rotate. */
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
);
677 doPerturb (PerturbationType
* pt
, bool undo
)
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 */
688 Coord DX
= pt
->DX
, DY
= pt
->DY
;
694 MoveElementLowLevel (PCB
->Data
, pt
->element
, DX
, DY
);
699 unsigned b
= pt
->rotate
;
702 /* 0 - flip; 1-3, rotate. */
704 RotateElementLowLevel (PCB
->Data
, pt
->element
, bbcx
, bbcy
, b
);
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
);
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
;
731 mypt
.rotate
= 0; /* flip */
732 doPerturb (&mypt
, undo
);
733 mypt
.element
= pt
->other
;
734 doPerturb (&mypt
, undo
);
744 /* ---------------------------------------------------------------------------
745 * Auto-place selected components.
748 AutoPlaceSelected (void)
751 PointerListType Selected
= { 0, 0, NULL
};
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
);
765 Message (_("Can't add rat lines because no netlist is loaded.\n"));
769 Selected
= collectSelectedElements ();
770 if (Selected
.PtrN
== 0)
772 Message (_("No elements selected to autoplace.\n"));
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;
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 */
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
);
805 pt
= createPerturbation (&Selected
, T
);
806 doPerturb (&pt
, false);
807 Cprime
= ComputeCost (Nets
, T0
, T
);
814 else if ((random () / (double) RAND_MAX
) <
815 exp (MIN (MAX (-20, (C0
- Cprime
) / T
), 20)))
817 /* not good but keep it anyway */
822 doPerturb (&pt
, true); /* undo last change */
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
)
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);
846 AddAllRats (false, NULL
);
849 FreePointerListMemory (&Selected
);