#!/usr/bin/env python """ Drill clustering routines to reduce total number of drills and remap drilling commands to the new reduced drill set. -------------------------------------------------------------------- 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 """ _STATUS = True ## indicates status messages should be shown _DEBUG = False ## indicates debug and status messages should be shown def cluster(drills, tolerance, debug = _DEBUG): """ Take a dictionary of drill names and sizes and cluster them A tolerance of 0 will effectively disable clustering Returns clustered drill dictionary """ global _DEBUG _DEBUG = debug clusters = [] debug_print("\n " + str( len(drills) ) + " Original drills:") debug_print( drillsToString(drills) ) debug_print("Clustering drill sizes ...", True) # Loop through all drill sizes sizes = drills.keys() sizes.sort() for size in sizes: match = False # See if size fits into any current clusters, else make new cluster for index in range( len(clusters) ): c = clusters[index] if not len(c): break mn = min(c) mx = max(c) ##debug_print( "Validating " + str_d(size) + " in " + str_d(c) ) ##debug_print( "Possible cluster range = " + str_d(mx - 2 * tolerance) + " to " + str_d(mn + 2 * tolerance) ) if (size >= mx - 2 * tolerance) and (size <= mn + 2 * tolerance): debug_print( str_d(size) + " belongs with " + str_d(c) ) clusters[index].append(size) match = True break if not match: debug_print(str_d(size) + " belongs in a new cluster") clusters.append( [size] ) debug_print("\n Creating new drill dictionary ...") new_drills = {} tool_num = 0 # Create new dictionary of clustered drills for c in clusters: tool_num += 1 new_drill = "T%02d" % tool_num c.sort() new_size = ( min(c) + max(c) ) / 2.0 new_drills[new_size] = new_drill debug_print(str_d(c) + " will be represented by " + new_drill + " (" + str_d(new_size) + ")") debug_print("\n " + str( len(new_drills) ) + " Clustered Drills:") debug_print( drillsToString(new_drills) ) debug_print("Drill count reduced from " + str( len(drills) ) + " to " + str( len(new_drills) ), True) return new_drills def remap(jobs, globalToolMap, debug = _DEBUG): """ Remap tools and commands in all jobs to match new tool map Returns None """ # Set global variables from parameters global _DEBUG _DEBUG = debug debug_print("Remapping tools and commands ...", True) for job in jobs: job = job.job ##access job inside job layout debug_print("\n Job name: " + job.name) debug_print("\n Original job tools:") debug_print( str(job.xdiam) ) debug_print("\n Original commands:") debug_print( str(job.xcommands) ) new_tools = {} new_commands = {} for tool, diam in job.xdiam.items(): ##debug_print("\n Current tool: " + tool + " (" + str_d(diam) + ")") # Search for best matching tool best_diam, best_tool = globalToolMap[0] for glob_diam, glob_tool in globalToolMap: if abs(glob_diam - diam) < abs(best_diam - diam): best_tool = glob_tool best_diam = glob_diam ##debug_print("Best match: " + best_tool + " (" + str_d(best_diam) + ")") new_tools[best_tool] = best_diam ##debug_print(best_tool + " will replace " + tool) # Append commands to existing commands if they exist if best_tool in new_commands: ##debug_print( "Current commands: " + str( new_commands[best_tool] ) ) temp = new_commands[best_tool] temp.extend( job.xcommands[tool] ) new_commands[best_tool] = temp ##debug_print( "All commands: " + str( new_commands[best_tool] ) ) else: new_commands[best_tool] = job.xcommands[tool] debug_print("\n New job tools:") debug_print( str(new_tools) ) debug_print("\n New commands:") debug_print( str(new_commands) ) job.xdiam = new_tools job.xcommands = new_commands def debug_print(text, status = False, newLine = True): """ Print debugging statemetns Returs None, Printts text """ if _DEBUG or (status and _STATUS): if newLine: print " ", text else: print " ", text, def str_d(drills): """ Format drill sizes for printing debug and status messages Returns drills as formatted string """ string = "" try: len(drills) except: string = "%.4f" % drills else: string = "[" for drill in drills: string += ( "%.4f" % drill + ", ") string = string[:len(string) - 2] + "]" return string def drillsToString(drills): """ Format drill dictionary for printing debug and status messages Returns drills as formatted string """ string = "" drills = drills.items() drills.sort() for size, drill in drills: string += drill + " = " + str_d(size) + "\n " return string """ The following code runs test drill clusterings with random drill sets. """ if __name__=="__main__": import random print " Clustering random drills..." old = {} tool_num = 0 while len(old) < 99: rand_size = round(random.uniform(.02, .04), 4) if rand_size in old: continue tool_num += 1 old[rand_size] = "T%02d" % tool_num new = cluster(old, .0003, True)