#!/usr/bin/env python """Tile search using random placement and evaluation. Works surprisingly well. -------------------------------------------------------------------- This program is licensed under the GNU General Public License (GPL) Version 3. See http://www.fsf.org for details of the license. Rugged Circuits LLC http://ruggedcircuits.com/gerbmerge """ import sys import time import random import config import tiling import tilesearch1 import gerbmerge _StartTime = 0.0 # Start time of tiling _CkpointTime = 0.0 # Next time to print stats _Placements = 0 # Number of placements attempted _TBestTiling = None # Best tiling so far _TBestScore = float(sys.maxsize) # Smallest area so far def printTilingStats(): global _CkpointTime _CkpointTime = time.time() + 3 if _TBestTiling: area = _TBestTiling.area() utilization = _TBestTiling.usedArea() / area * 100.0 else: area = 999999.0 utilization = 0.0 # add metric support (1/1000 mm vs. 1/100,000 inch) if config.Config['measurementunits'] == 'inch': print("\r %ld placements / Smallest area: %.1f sq. in. / Best utilization: %.1f%%" % \ (_Placements, area, utilization), end=' ') else: print("\r %ld placements / Smallest area: %.1f sq. mm / Best utilization: %.0f%%" % \ (_Placements, area, utilization), end=' ') if gerbmerge.GUI is not None: sys.stdout.flush() def _tile_search2(Jobs, X, Y, cfg=config.Config): global _CkpointTime, _Placements, _TBestTiling, _TBestScore r = random.Random() N = len(Jobs) # M is the number of jobs that will be placed randomly. # N-M is the number of jobs that will be searched exhaustively. M = N - config.RandomSearchExhaustiveJobs M = max(M,0) xspacing = cfg['xspacing'] yspacing = cfg['yspacing'] # Must escape with Ctrl-C while 1: T = tiling.Tiling(X,Y) joborder = r.sample(list(range(N)), N) minInletSize = tiling.minDimension(Jobs) for ix in joborder[:M]: Xdim,Ydim,job,rjob = Jobs[ix] T.removeInlets(minInletSize) if r.choice([0,1]): addpoints = T.validAddPoints(Xdim+xspacing,Ydim+yspacing) if not addpoints: break pt = r.choice(addpoints) T.addJob(pt, Xdim+xspacing, Ydim+yspacing, job) else: addpoints = T.validAddPoints(Ydim+xspacing,Xdim+yspacing) if not addpoints: break pt = r.choice(addpoints) T.addJob(pt, Ydim+xspacing, Xdim+yspacing, rjob) else: # Do exhaustive search on remaining jobs if N-M: remainingJobs = [] for ix in joborder[M:]: remainingJobs.append(Jobs[ix]) tilesearch1.initialize(0) tilesearch1._tile_search1(remainingJobs, T, 1) T = tilesearch1.bestTiling() if T: score = T.area() if score < _TBestScore: _TBestTiling,_TBestScore = T,score elif score == _TBestScore: if T.corners() < _TBestTiling.corners(): _TBestTiling,_TBestScore = T,score _Placements += 1 # If we've been at this for 3 seconds, print some status information if time.time() > _CkpointTime: printTilingStats() # Check for timeout - changed to file config if (config.Config['searchtimeout'] > 0) and ((time.time() - _StartTime) > config.Config['searchtimeout']): raise KeyboardInterrupt gerbmerge.updateGUI("Performing automatic layout...") # end while 1 def tile_search2(Jobs, X, Y): """Wrapper around _tile_search2 to handle keyboard interrupt, etc.""" global _StartTime, _CkpointTime, _Placements, _TBestTiling, _TBestScore _StartTime = time.time() _CkpointTime = _StartTime + 3 _Placements = 0 _TBestTiling = None _TBestScore = float(sys.maxsize) print('='*70) if (config.Config['searchtimeout'] > 0): print("Starting random placement trials. You can press Ctrl-C to") print("stop the process and use the best placement so far, or wait") print("for the automatic timeout in %i seconds." % config.Config['searchtimeout']) else: print("Starting random placement trials. You must press Ctrl-C to") print("stop the process and use the best placement so far.") print("You can specify a timeout by setting 'SearchTimeout' in Layout.cfg") print("Estimated maximum possible utilization is %.1f%%." % (tiling.maxUtilization(Jobs)*100)) try: _tile_search2(Jobs, X, Y) printTilingStats() print() except KeyboardInterrupt: printTilingStats() print() print("Interrupted.") computeTime = time.time() - _StartTime print("Computed %ld placements in %d seconds / %.1f placements/second" % (_Placements, computeTime, _Placements/computeTime)) print('='*70) return _TBestTiling