Python 3.x

This commit is contained in:
andreika-git 2022-03-26 21:23:48 +02:00
parent 371354d056
commit f78db83293
16 changed files with 330 additions and 333 deletions

View File

@ -112,7 +112,7 @@ class ApertureMacroPrimitive:
valids = None valids = None
if valids is None: if valids is None:
raise RuntimeError, 'Undefined aperture macro primitive code %d' % code raise RuntimeError('Undefined aperture macro primitive code %d' % code)
# We expect exactly the number of fields required, except for macro # We expect exactly the number of fields required, except for macro
# type 4 which is an outline and has a variable number of points. # type 4 which is an outline and has a variable number of points.
@ -126,18 +126,18 @@ class ApertureMacroPrimitive:
# - last field is rotation # - last field is rotation
if self.code==4: if self.code==4:
if len(fields) < 2: if len(fields) < 2:
raise RuntimeError, 'Outline macro primitive has way too few fields' raise RuntimeError('Outline macro primitive has way too few fields')
try: try:
N = int(fields[1]) N = int(fields[1])
except: except:
raise RuntimeError, 'Outline macro primitive has non-integer number of points' raise RuntimeError('Outline macro primitive has non-integer number of points')
if len(fields) != (5+2*N): if len(fields) != (5+2*N):
raise RuntimeError, 'Outline macro primitive has %d fields...expecting %d fields' % (len(fields), 3+2*N) raise RuntimeError('Outline macro primitive has %d fields...expecting %d fields' % (len(fields), 3+2*N))
else: else:
if len(fields) != len(valids): if len(fields) != len(valids):
raise RuntimeError, 'Macro primitive has %d fields...expecting %d fields' % (len(fields), len(valids)) raise RuntimeError('Macro primitive has %d fields...expecting %d fields' % (len(fields), len(valids)))
# Convert each parameter on the input line to an entry in the self.parms # Convert each parameter on the input line to an entry in the self.parms
# list, using either int() or float() conversion. # list, using either int() or float() conversion.
@ -150,7 +150,7 @@ class ApertureMacroPrimitive:
try: try:
self.parms.append(converter(fields[parmix])) self.parms.append(converter(fields[parmix]))
except: except:
raise RuntimeError, 'Aperture macro primitive parameter %d has incorrect type' % (parmix+1) raise RuntimeError('Aperture macro primitive parameter %d has incorrect type' % (parmix+1))
def setFromLine(self, line): def setFromLine(self, line):
# Account for DOS line endings and get rid of line ending and '*' at the end # Account for DOS line endings and get rid of line ending and '*' at the end
@ -164,12 +164,12 @@ class ApertureMacroPrimitive:
try: try:
code = int(fields[0]) code = int(fields[0])
except: except:
raise RuntimeError, 'Illegal aperture macro primitive code "%s"' % fields[0] raise RuntimeError('Illegal aperture macro primitive code "%s"' % fields[0])
self.setFromFields(code, fields[1:]) self.setFromFields(code, fields[1:])
except: except:
print '='*20 print('='*20)
print "==> ", line print("==> ", line)
print '='*20 print('='*20)
raise raise
def rotate(self, flip): def rotate(self, flip):
@ -283,7 +283,7 @@ def parseApertureMacro(s, fid):
M.add(P) M.add(P)
else: else:
raise RuntimeError, "Premature end-of-file while parsing aperture macro" raise RuntimeError("Premature end-of-file while parsing aperture macro")
else: else:
return None return None
@ -295,7 +295,7 @@ def addToApertureMacroTable(AM):
# Must sort keys by integer value, not string since 99 comes before 100 # Must sort keys by integer value, not string since 99 comes before 100
# as an integer but not a string. # as an integer but not a string.
keys = map(int, map(lambda K: K[1:], GAMT.keys())) keys = list(map(int, [K[1:] for K in list(GAMT.keys())]))
keys.sort() keys.sort()
if len(keys): if len(keys):
@ -338,25 +338,23 @@ if __name__=="__main__":
MR = M.rotated(0) MR = M.rotated(0)
# Generate the Gerber so we can view it # Generate the Gerber so we can view it
fid = file('amacro.ger', 'wt') fid = open('amacro.ger', 'wt')
print >> fid, \ print("""G75*
"""G75*
G70* G70*
%OFA0B0*% %OFA0B0*%
%FSLAX24Y24*% %FSLAX24Y24*%
%IPPOS*% %IPPOS*%
%LPD*%""" %LPD*%""", file=fid)
M.writeDef(fid) M.writeDef(fid)
MR.writeDef(fid) MR.writeDef(fid)
print >> fid, \ print("""%ADD10TEST*%
"""%ADD10TEST*%
%ADD11TESTR*% %ADD11TESTR*%
D10* D10*
X010000Y010000D03* X010000Y010000D03*
D11* D11*
X015000Y010000D03* X015000Y010000D03*
M02*""" M02*""", file=fid)
fid.close() fid.close()
print M print(M)
print MR print(MR)

View File

@ -168,17 +168,17 @@ def parseAperture(s, knownMacroNames):
code, dimx, dimy = match.groups() code, dimx, dimy = match.groups()
if ap[0] in ('Macro',): if ap[0] in ('Macro',):
if knownMacroNames.has_key(dimx): if dimx in knownMacroNames:
dimx = knownMacroNames[dimx] # dimx is now GLOBAL, permanent macro name (e.g., 'M2') dimx = knownMacroNames[dimx] # dimx is now GLOBAL, permanent macro name (e.g., 'M2')
else: else:
raise RuntimeError, 'Aperture Macro name "%s" not defined' % dimx raise RuntimeError('Aperture Macro name "%s" not defined' % dimx)
else: else:
try: try:
dimx = float(dimx) dimx = float(dimx)
if dimy: if dimy:
dimy = float(dimy) dimy = float(dimy)
except: except:
raise RuntimeError, "Illegal floating point aperture size" raise RuntimeError("Illegal floating point aperture size")
return Aperture(ap, code, dimx, dimy) return Aperture(ap, code, dimx, dimy)
@ -221,7 +221,7 @@ def constructApertureTable(fileList):
# [andreika]: units conversion # [andreika]: units conversion
units_div = 1.0 units_div = 1.0
fid = file(fname,'rt') fid = open(fname,'rt')
for line in fid: for line in fid:
# Get rid of CR # Get rid of CR
line = line.replace('\x0D', '') line = line.replace('\x0D', '')
@ -276,19 +276,19 @@ def constructApertureTable(fileList):
# Now, go through and assign sequential codes to all apertures # Now, go through and assign sequential codes to all apertures
code = 10 code = 10
for val in AT.values(): for val in list(AT.values()):
key = 'D%d' % code key = 'D%d' % code
GAT[key] = val GAT[key] = val
val.code = key val.code = key
code += 1 code += 1
if 0: if 0:
keylist = config.GAT.keys() keylist = list(config.GAT.keys())
keylist.sort() keylist.sort()
print 'Apertures' print('Apertures')
print '=========' print('=========')
for key in keylist: for key in keylist:
print '%s' % config.GAT[key] print('%s' % config.GAT[key])
sys.exit(0) sys.exit(0)
def findHighestApertureCode(keys): def findHighestApertureCode(keys):
@ -304,7 +304,7 @@ def findHighestApertureCode(keys):
def addToApertureTable(AP): def addToApertureTable(AP):
GAT = config.GAT GAT = config.GAT
lastCode = findHighestApertureCode(GAT.keys()) lastCode = findHighestApertureCode(list(GAT.keys()))
code = 'D%d' % (lastCode+1) code = 'D%d' % (lastCode+1)
GAT[code] = AP GAT[code] = AP
AP.code = code AP.code = code
@ -315,7 +315,7 @@ def findInApertureTable(AP):
"""Return 'D10', for example in response to query for an object """Return 'D10', for example in response to query for an object
of type Aperture()""" of type Aperture()"""
hash = AP.hash() hash = AP.hash()
for key, val in config.GAT.items(): for key, val in list(config.GAT.items()):
if hash==val.hash(): if hash==val.hash():
return key return key
@ -335,16 +335,16 @@ def findOrAddAperture(AP):
if __name__=="__main__": if __name__=="__main__":
constructApertureTable(sys.argv[1:]) constructApertureTable(sys.argv[1:])
keylist = config.GAMT.keys() keylist = list(config.GAMT.keys())
keylist.sort() keylist.sort()
print 'Aperture Macros' print('Aperture Macros')
print '===============' print('===============')
for key in keylist: for key in keylist:
print '%s' % config.GAMT[key] print('%s' % config.GAMT[key])
keylist = config.GAT.keys() keylist = list(config.GAT.keys())
keylist.sort() keylist.sort()
print 'Apertures' print('Apertures')
print '=========' print('=========')
for key in keylist: for key in keylist:
print '%s' % config.GAT[key] print('%s' % config.GAT[key])

View File

@ -15,7 +15,7 @@ http://github.com/unwireddevices/gerbmerge
""" """
import sys import sys
import ConfigParser import configparser
import re import re
import string import string
@ -116,7 +116,7 @@ MinimumFeatureDimension = {}
# hash string. The value is the aperture code (e.g., 'D10') or macro name (e.g., 'M5'). # hash string. The value is the aperture code (e.g., 'D10') or macro name (e.g., 'M5').
def buildRevDict(D): def buildRevDict(D):
RevD = {} RevD = {}
for key,val in D.items(): for key,val in list(D.items()):
RevD[val.hash()] = key RevD[val.hash()] = key
return RevD return RevD
@ -127,12 +127,12 @@ def parseStringList(L):
if 0: if 0:
if L[0]=="'": if L[0]=="'":
if L[-1] != "'": if L[-1] != "'":
raise RuntimeError, "Illegal configuration string '%s'" % L raise RuntimeError("Illegal configuration string '%s'" % L)
L = L[1:-1] L = L[1:-1]
elif L[0]=='"': elif L[0]=='"':
if L[-1] != '"': if L[-1] != '"':
raise RuntimeError, "Illegal configuration string '%s'" % L raise RuntimeError("Illegal configuration string '%s'" % L)
L = L[1:-1] L = L[1:-1]
# This pattern matches quotes at the beginning and end...quotes must match # This pattern matches quotes at the beginning and end...quotes must match
@ -153,14 +153,14 @@ def parseToolList(fname):
TL = {} TL = {}
try: try:
fid = file(fname, 'rt') fid = open(fname, 'rt')
except Exception, detail: except Exception as detail:
raise RuntimeError, "Unable to open tool list file '%s':\n %s" % (fname, str(detail)) raise RuntimeError("Unable to open tool list file '%s':\n %s" % (fname, str(detail)))
pat_in = re.compile(r'\s*(T\d+)\s+([0-9.]+)\s*in\s*') pat_in = re.compile(r'\s*(T\d+)\s+([0-9.]+)\s*in\s*')
pat_mm = re.compile(r'\s*(T\d+)\s+([0-9.]+)\s*mm\s*') pat_mm = re.compile(r'\s*(T\d+)\s+([0-9.]+)\s*mm\s*')
pat_mil = re.compile(r'\s*(T\d+)\s+([0-9.]+)\s*(?:mil)?') pat_mil = re.compile(r'\s*(T\d+)\s+([0-9.]+)\s*(?:mil)?')
for line in fid.xreadlines(): for line in fid:
line = string.strip(line) line = string.strip(line)
if (not line) or (line[0] in ('#', ';')): continue if (not line) or (line[0] in ('#', ';')): continue
@ -182,7 +182,7 @@ def parseToolList(fname):
try: try:
size = float(size) size = float(size)
except: except:
raise RuntimeError, "Tool size in file '%s' is not a valid floating-point number:\n %s" % (fname,line) raise RuntimeError("Tool size in file '%s' is not a valid floating-point number:\n %s" % (fname,line))
if mil: if mil:
size = size*0.001 # Convert mil to inches size = size*0.001 # Convert mil to inches
@ -192,8 +192,8 @@ def parseToolList(fname):
# Canonicalize tool so that T1 becomes T01 # Canonicalize tool so that T1 becomes T01
tool = 'T%02d' % int(tool[1:]) tool = 'T%02d' % int(tool[1:])
if TL.has_key(tool): if tool in TL:
raise RuntimeError, "Tool '%s' defined more than once in tool list file '%s'" % (tool,fname) raise RuntimeError("Tool '%s' defined more than once in tool list file '%s'" % (tool,fname))
TL[tool]=size TL[tool]=size
fid.close() fid.close()
@ -215,38 +215,38 @@ def parseToolList(fname):
def parseConfigFile(fname, Config=Config, Jobs=Jobs): def parseConfigFile(fname, Config=Config, Jobs=Jobs):
global DefaultToolList global DefaultToolList
CP = ConfigParser.ConfigParser() CP = configparser.ConfigParser()
CP.readfp(file(fname.rstrip(),'rt')) CP.readfp(open(fname.rstrip(),'rt'))
# First parse global options # First parse global options
if CP.has_section('Options'): if CP.has_section('Options'):
for opt in CP.options('Options'): for opt in CP.options('Options'):
# Is it one we expect # Is it one we expect
if Config.has_key(opt): if opt in Config:
# Yup...override it # Yup...override it
Config[opt] = CP.get('Options', opt) Config[opt] = CP.get('Options', opt)
elif CP.defaults().has_key(opt): elif opt in CP.defaults():
pass # Ignore DEFAULTS section keys pass # Ignore DEFAULTS section keys
elif opt in ('fabricationdrawing', 'outlinelayer'): elif opt in ('fabricationdrawing', 'outlinelayer'):
print '*'*73 print('*'*73)
print '\nThe FabricationDrawing and OutlineLayer configuration options have been' print('\nThe FabricationDrawing and OutlineLayer configuration options have been')
print 'renamed as of GerbMerge version 1.0. Please consult the documentation for' print('renamed as of GerbMerge version 1.0. Please consult the documentation for')
print 'a description of the new options, then modify your configuration file.\n' print('a description of the new options, then modify your configuration file.\n')
print '*'*73 print('*'*73)
sys.exit(1) sys.exit(1)
else: else:
raise RuntimeError, "Unknown option '%s' in [Options] section of configuration file" % opt raise RuntimeError("Unknown option '%s' in [Options] section of configuration file" % opt)
else: else:
raise RuntimeError, "Missing [Options] section in configuration file" raise RuntimeError("Missing [Options] section in configuration file")
# Ensure we got a tool list # Ensure we got a tool list
if not Config.has_key('toollist'): if 'toollist' not in Config:
raise RuntimeError, "INTERNAL ERROR: Missing tool list assignment in [Options] section" raise RuntimeError("INTERNAL ERROR: Missing tool list assignment in [Options] section")
# Make integers integers, floats floats # Make integers integers, floats floats
for key,val in Config.items(): for key,val in list(Config.items()):
try: try:
val = int(val) val = int(val)
Config[key]=val Config[key]=val
@ -283,7 +283,7 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs):
for index in range(0, len(temp), 2): for index in range(0, len(temp), 2):
MinimumFeatureDimension[ temp[index] ] = float( temp[index + 1] ) MinimumFeatureDimension[ temp[index] ] = float( temp[index + 1] )
except: except:
raise RuntimeError, "Illegal configuration string:" + Config['minimumfeaturesize'] raise RuntimeError("Illegal configuration string:" + Config['minimumfeaturesize'])
# Process MergeOutputFiles section to set output file names # Process MergeOutputFiles section to set output file names
if CP.has_section('MergeOutputFiles'): if CP.has_section('MergeOutputFiles'):
@ -303,10 +303,10 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs):
# Ensure all jobs have a board outline # Ensure all jobs have a board outline
if not CP.has_option(jobname, 'boardoutline'): if not CP.has_option(jobname, 'boardoutline'):
raise RuntimeError, "Job '%s' does not have a board outline specified" % jobname raise RuntimeError("Job '%s' does not have a board outline specified" % jobname)
if not CP.has_option(jobname, 'drills'): if not CP.has_option(jobname, 'drills'):
raise RuntimeError, "Job '%s' does not have a drills layer specified" % jobname raise RuntimeError("Job '%s' does not have a drills layer specified" % jobname)
for layername in CP.options(jobname): for layername in CP.options(jobname):
if layername[0]=='*' or layername=='boardoutline': if layername[0]=='*' or layername=='boardoutline':
@ -323,10 +323,10 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs):
del apfiles del apfiles
if 0: if 0:
keylist = GAMT.keys() keylist = list(GAMT.keys())
keylist.sort() keylist.sort()
for key in keylist: for key in keylist:
print '%s' % GAMT[key] print('%s' % GAMT[key])
sys.exit(0) sys.exit(0)
# Parse the tool list # Parse the tool list
@ -350,8 +350,8 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs):
if jobname=='MergeOutputFiles': continue if jobname=='MergeOutputFiles': continue
if jobname=='GerbMergeGUI': continue if jobname=='GerbMergeGUI': continue
print '' # empty line before hand for readability print('') # empty line before hand for readability
print 'Reading data from', jobname, '...' print('Reading data from', jobname, '...')
J = jobs.Job(jobname) J = jobs.Job(jobname)
@ -369,12 +369,12 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs):
try: try:
J.ExcellonDecimals = int(fname) J.ExcellonDecimals = int(fname)
except: except:
raise RuntimeError, "Excellon decimals '%s' in config file is not a valid integer" % fname raise RuntimeError("Excellon decimals '%s' in config file is not a valid integer" % fname)
elif layername=='repeat': elif layername=='repeat':
try: try:
J.Repeat = int(fname) J.Repeat = int(fname)
except: except:
raise RuntimeError, "Repeat count '%s' in config file is not a valid integer" % fname raise RuntimeError("Repeat count '%s' in config file is not a valid integer" % fname)
for layername in CP.options(jobname): for layername in CP.options(jobname):
fname = CP.get(jobname, layername) fname = CP.get(jobname, layername)
@ -388,34 +388,34 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs):
# Emit warnings if some layers are missing # Emit warnings if some layers are missing
LL = LayerList.copy() LL = LayerList.copy()
for layername in J.apxlat.keys(): for layername in list(J.apxlat.keys()):
assert LL.has_key(layername) assert layername in LL
del LL[layername] del LL[layername]
if LL: if LL:
if errstr=='ERROR': if errstr=='ERROR':
do_abort=1 do_abort=1
print '%s: Job %s is missing the following layers:' % (errstr, jobname) print('%s: Job %s is missing the following layers:' % (errstr, jobname))
for layername in LL.keys(): for layername in list(LL.keys()):
print ' %s' % layername print(' %s' % layername)
# Store the job in the global Jobs dictionary, keyed by job name # Store the job in the global Jobs dictionary, keyed by job name
Jobs[jobname] = J Jobs[jobname] = J
if do_abort: if do_abort:
raise RuntimeError, 'Exiting since jobs are missing layers. Set AllowMissingLayers=1\nto override.' raise RuntimeError('Exiting since jobs are missing layers. Set AllowMissingLayers=1\nto override.')
if __name__=="__main__": if __name__=="__main__":
CP = parseConfigFile(sys.argv[1]) CP = parseConfigFile(sys.argv[1])
print Config print(Config)
sys.exit(0) sys.exit(0)
if 0: if 0:
for key, val in CP.defaults().items(): for key, val in list(CP.defaults().items()):
print '%s: %s' % (key,val) print('%s: %s' % (key,val))
for section in CP.sections(): for section in CP.sections():
print '[%s]' % section print('[%s]' % section)
for opt in CP.options(section): for opt in CP.options(section):
print ' %s=%s' % (opt, CP.get(section, opt)) print(' %s=%s' % (opt, CP.get(section, opt)))

View File

@ -34,7 +34,7 @@ def cluster(drills, tolerance, debug = _DEBUG):
debug_print("Clustering drill sizes ...", True) debug_print("Clustering drill sizes ...", True)
# Loop through all drill sizes # Loop through all drill sizes
sizes = drills.keys() sizes = list(drills.keys())
sizes.sort() sizes.sort()
for size in sizes: for size in sizes:
@ -106,7 +106,7 @@ def remap(jobs, globalToolMap, debug = _DEBUG):
debug_print( str(job.xcommands) ) debug_print( str(job.xcommands) )
new_tools = {} new_tools = {}
new_commands = {} new_commands = {}
for tool, diam in job.xdiam.items(): for tool, diam in list(job.xdiam.items()):
##debug_print("\n Current tool: " + tool + " (" + str_d(diam) + ")") ##debug_print("\n Current tool: " + tool + " (" + str_d(diam) + ")")
@ -147,9 +147,9 @@ def debug_print(text, status = False, newLine = True):
if _DEBUG or (status and _STATUS): if _DEBUG or (status and _STATUS):
if newLine: if newLine:
print " ", text print(" ", text)
else: else:
print " ", text, print(" ", text, end=' ')
def str_d(drills): def str_d(drills):
""" """
@ -180,7 +180,7 @@ def drillsToString(drills):
""" """
string = "" string = ""
drills = drills.items() drills = list(drills.items())
drills.sort() drills.sort()
for size, drill in drills: for size, drill in drills:
string += drill + " = " + str_d(size) + "\n " string += drill + " = " + str_d(size) + "\n "
@ -194,7 +194,7 @@ def drillsToString(drills):
if __name__=="__main__": if __name__=="__main__":
import random import random
print " Clustering random drills..." print(" Clustering random drills...")
old = {} old = {}
tool_num = 0 tool_num = 0

View File

@ -25,7 +25,7 @@ def writeDrillHits(fid, Place, Tools):
try: try:
size = config.GlobalToolMap[tool] size = config.GlobalToolMap[tool]
except: except:
raise RuntimeError, "INTERNAL ERROR: Tool code %s not found in global tool list" % tool raise RuntimeError("INTERNAL ERROR: Tool code %s not found in global tool list" % tool)
#for row in Layout: #for row in Layout:
# row.writeDrillHits(fid, size, toolNumber) # row.writeDrillHits(fid, size, toolNumber)
@ -157,9 +157,9 @@ def writeUserText(fid, X, Y):
if not fname: return if not fname: return
try: try:
tfile = file(fname, 'rt') tfile = open(fname, 'rt')
except Exception, detail: except Exception as detail:
raise RuntimeError, "Could not open fabrication drawing text file '%s':\n %s" % (fname,str(detail)) raise RuntimeError("Could not open fabrication drawing text file '%s':\n %s" % (fname,str(detail)))
lines = tfile.readlines() lines = tfile.readlines()
tfile.close() tfile.close()
@ -170,7 +170,7 @@ def writeUserText(fid, X, Y):
for line in lines: for line in lines:
# Get rid of CR # Get rid of CR
line = string.replace(line, '\x0D', '') line = str.replace(line, '\x0D', '')
# Chop off \n # Chop off \n
#if line[-1] in string.whitespace: #if line[-1] in string.whitespace:

View File

@ -15,7 +15,7 @@ import math
# Ensure all list elements are unique # Ensure all list elements are unique
def uniqueify(L): def uniqueify(L):
return {}.fromkeys(L).keys() return list({}.fromkeys(L).keys())
# This function rounds an (X,Y) point to integer co-ordinates # This function rounds an (X,Y) point to integer co-ordinates
def roundPoint(pt): def roundPoint(pt):
@ -343,4 +343,4 @@ if __name__=="__main__":
assert isRect1InRect2( (100,100,500,500), (0,600,300,300) ) == False assert isRect1InRect2( (100,100,500,500), (0,600,300,300) ) == False
assert isRect1InRect2( (100,100,500,500), (0,0,500,500) ) == True assert isRect1InRect2( (100,100,500,500), (0,0,500,500) ) == True
print 'All tests pass' print('All tests pass')

View File

@ -60,8 +60,7 @@ config.PlacementFile = None
GUI = None GUI = None
def usage(): def usage():
print \ print("""
"""
Usage: gerbmerge [Options] configfile [layoutfile] Usage: gerbmerge [Options] configfile [layoutfile]
Options: Options:
@ -89,7 +88,7 @@ the layout file (if any) is ignored.
NOTE: The dimensions of each job are determined solely by the maximum extent of NOTE: The dimensions of each job are determined solely by the maximum extent of
the board outline layer for each job. the board outline layer for each job.
""" """)
sys.exit(1) sys.exit(1)
# changed these two writeGerberHeader files to take metric units (mm) into account: # changed these two writeGerberHeader files to take metric units (mm) into account:
@ -146,14 +145,14 @@ G71*
writeGerberHeader = writeGerberHeader22degrees writeGerberHeader = writeGerberHeader22degrees
def writeApertureMacros(fid, usedDict): def writeApertureMacros(fid, usedDict):
keys = config.GAMT.keys() keys = list(config.GAMT.keys())
keys.sort() keys.sort()
for key in keys: for key in keys:
if key in usedDict: if key in usedDict:
config.GAMT[key].writeDef(fid) config.GAMT[key].writeDef(fid)
def writeApertures(fid, usedDict): def writeApertures(fid, usedDict):
keys = config.GAT.keys() keys = list(config.GAT.keys())
keys.sort() keys.sort()
for key in keys: for key in keys:
if key in usedDict: if key in usedDict:
@ -254,7 +253,7 @@ def writeCropMarks(fid, drawing_code, OriginX, OriginY, MaxXExtent, MaxYExtent):
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+cropW), util.in2gerb(y+0.000))) fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+cropW), util.in2gerb(y+0.000)))
def disclaimer(): def disclaimer():
print """ print("""
**************************************************** ****************************************************
* R E A D C A R E F U L L Y * * R E A D C A R E F U L L Y *
* * * *
@ -279,14 +278,14 @@ def disclaimer():
To agree to the above terms, press 'y' then Enter. To agree to the above terms, press 'y' then Enter.
Any other key will exit the program. Any other key will exit the program.
""" """)
s = raw_input() s = input()
if s == 'y': if s == 'y':
print print()
return return
print "\nExiting..." print("\nExiting...")
sys.exit(0) sys.exit(0)
def tile_jobs(Jobs): def tile_jobs(Jobs):
@ -318,9 +317,9 @@ def tile_jobs(Jobs):
if not tile: if not tile:
# add metric support (1/1000 mm vs. 1/100,000 inch) # add metric support (1/1000 mm vs. 1/100,000 inch)
if config.Config['measurementunits'] == 'inch': if config.Config['measurementunits'] == 'inch':
raise RuntimeError, 'Panel size %.2f"x%.2f" is too small to hold jobs' % (PX,PY) raise RuntimeError('Panel size %.2f"x%.2f" is too small to hold jobs' % (PX,PY))
else: else:
raise RuntimeError, 'Panel size %.2fmmx%.2fmm is too small to hold jobs' % (PX,PY) raise RuntimeError('Panel size %.2fmmx%.2fmm is too small to hold jobs' % (PX,PY))
return tile return tile
@ -339,7 +338,7 @@ def merge(opts, args, gui = None):
elif arg=='normal': elif arg=='normal':
writeGerberHeader = writeGerberHeader22degrees writeGerberHeader = writeGerberHeader22degrees
else: else:
raise RuntimeError, 'Unknown octagon format' raise RuntimeError('Unknown octagon format')
elif opt in ('--random-search',): elif opt in ('--random-search',):
config.AutoSearchType = RANDOM_SEARCH config.AutoSearchType = RANDOM_SEARCH
elif opt in ('--full-search',): elif opt in ('--full-search',):
@ -358,10 +357,10 @@ def merge(opts, args, gui = None):
elif opt in ('-s', '--skipdisclaimer'): elif opt in ('-s', '--skipdisclaimer'):
skipDisclaimer = 1 skipDisclaimer = 1
else: else:
raise RuntimeError, "Unknown option: %s" % opt raise RuntimeError("Unknown option: %s" % opt)
if len(args) > 2 or len(args) < 1: if len(args) > 2 or len(args) < 1:
raise RuntimeError, 'Invalid number of arguments' raise RuntimeError('Invalid number of arguments')
if (skipDisclaimer == 0): if (skipDisclaimer == 0):
disclaimer() disclaimer()
@ -372,7 +371,7 @@ def merge(opts, args, gui = None):
config.parseConfigFile(args[0]) config.parseConfigFile(args[0])
# Force all X and Y coordinates positive by adding absolute value of minimum X and Y # Force all X and Y coordinates positive by adding absolute value of minimum X and Y
for name, job in config.Jobs.iteritems(): for name, job in config.Jobs.items():
min_x, min_y = job.mincoordinates() min_x, min_y = job.mincoordinates()
shift_x = shift_y = 0 shift_x = shift_y = 0
if min_x < 0: shift_x = abs(min_x) if min_x < 0: shift_x = abs(min_x)
@ -381,31 +380,31 @@ def merge(opts, args, gui = None):
job.fixcoordinates( shift_x, shift_y ) job.fixcoordinates( shift_x, shift_y )
# Display job properties # Display job properties
for job in config.Jobs.values(): for job in list(config.Jobs.values()):
print 'Job %s:' % job.name, print('Job %s:' % job.name, end=' ')
if job.Repeat > 1: if job.Repeat > 1:
print '(%d instances)' % job.Repeat print('(%d instances)' % job.Repeat)
else: else:
print print()
print ' Extents: (%d,%d)-(%d,%d)' % (job.minx,job.miny,job.maxx,job.maxy) print(' Extents: (%d,%d)-(%d,%d)' % (job.minx,job.miny,job.maxx,job.maxy))
# add metric support (1/1000 mm vs. 1/100,000 inch) # add metric support (1/1000 mm vs. 1/100,000 inch)
if config.Config['measurementunits'] == 'inch': if config.Config['measurementunits'] == 'inch':
print ' Size: %f" x %f"' % (job.width_in(), job.height_in()) print(' Size: %f" x %f"' % (job.width_in(), job.height_in()))
else: else:
print ' Size: %5.3fmm x %5.3fmm' % (job.width_in(), job.height_in()) print(' Size: %5.3fmm x %5.3fmm' % (job.width_in(), job.height_in()))
print print()
# Trim drill locations and flash data to board extents # Trim drill locations and flash data to board extents
if config.TrimExcellon: if config.TrimExcellon:
updateGUI("Trimming Excellon data...") updateGUI("Trimming Excellon data...")
print 'Trimming Excellon data to board outlines ...' print('Trimming Excellon data to board outlines ...')
for job in config.Jobs.values(): for job in list(config.Jobs.values()):
job.trimExcellon() job.trimExcellon()
if config.TrimGerber: if config.TrimGerber:
updateGUI("Trimming Gerber data...") updateGUI("Trimming Gerber data...")
print 'Trimming Gerber data to board outlines ...' print('Trimming Gerber data to board outlines ...')
for job in config.Jobs.values(): for job in list(config.Jobs.values()):
job.trimGerber() job.trimGerber()
# We start origin at (0.1", 0.1") just so we don't get numbers close to 0 # We start origin at (0.1", 0.1") just so we don't get numbers close to 0
@ -416,7 +415,7 @@ def merge(opts, args, gui = None):
# Read the layout file and construct the nested list of jobs. If there # Read the layout file and construct the nested list of jobs. If there
# is no layout file, do auto-layout. # is no layout file, do auto-layout.
updateGUI("Performing layout...") updateGUI("Performing layout...")
print 'Performing layout ...' print('Performing layout ...')
if len(args) > 1: if len(args) > 1:
Layout = parselayout.parseLayoutFile(args[1]) Layout = parselayout.parseLayoutFile(args[1])
@ -439,7 +438,7 @@ def merge(opts, args, gui = None):
Place.addFromFile(config.PlacementFile, config.Jobs) Place.addFromFile(config.PlacementFile, config.Jobs)
else: else:
# Do an automatic layout based on our tiling algorithm. # Do an automatic layout based on our tiling algorithm.
tile = tile_jobs(config.Jobs.values()) tile = tile_jobs(list(config.Jobs.values()))
Place = placement.Placement() Place = placement.Placement()
Place.addFromTiling(tile, OriginX + config.Config['leftmargin'], OriginY + config.Config['bottommargin']) Place.addFromTiling(tile, OriginX + config.Config['leftmargin'], OriginY + config.Config['bottommargin'])
@ -491,9 +490,9 @@ def merge(opts, args, gui = None):
drawing_code1 = aptable.addToApertureTable(AP) drawing_code1 = aptable.addToApertureTable(AP)
updateGUI("Writing merged files...") updateGUI("Writing merged files...")
print 'Writing merged output files ...' print('Writing merged output files ...')
for layername in config.LayerList.keys(): for layername in list(config.LayerList.keys()):
lname = layername lname = layername
if lname[0]=='*': if lname[0]=='*':
lname = lname[1:] lname = lname[1:]
@ -504,7 +503,7 @@ def merge(opts, args, gui = None):
fullname = 'merged.%s.ger' % lname fullname = 'merged.%s.ger' % lname
OutputFiles.append(fullname) OutputFiles.append(fullname)
#print 'Writing %s ...' % fullname #print 'Writing %s ...' % fullname
fid = file(fullname, 'wt') fid = open(fullname, 'wt')
writeGerberHeader(fid) writeGerberHeader(fid)
# Determine which apertures and macros are truly needed # Determine which apertures and macros are truly needed
@ -516,12 +515,12 @@ def merge(opts, args, gui = None):
apmUsedDict.update(apmd) apmUsedDict.update(apmd)
# Increase aperature sizes to match minimum feature dimension # Increase aperature sizes to match minimum feature dimension
if config.MinimumFeatureDimension.has_key(layername): if layername in config.MinimumFeatureDimension:
print ' Thickening', lname, 'feature dimensions ...' print(' Thickening', lname, 'feature dimensions ...')
# Fix each aperture used in this layer # Fix each aperture used in this layer
for ap in apUsedDict.keys(): for ap in list(apUsedDict.keys()):
new = config.GAT[ap].getAdjusted( config.MinimumFeatureDimension[layername] ) new = config.GAT[ap].getAdjusted( config.MinimumFeatureDimension[layername] )
if not new: ## current aperture size met minimum requirement if not new: ## current aperture size met minimum requirement
continue continue
@ -595,7 +594,7 @@ def merge(opts, args, gui = None):
if fullname and fullname.lower() != "none": if fullname and fullname.lower() != "none":
OutputFiles.append(fullname) OutputFiles.append(fullname)
#print 'Writing %s ...' % fullname #print 'Writing %s ...' % fullname
fid = file(fullname, 'wt') fid = open(fullname, 'wt')
writeGerberHeader(fid) writeGerberHeader(fid)
# Write width-1 aperture to file # Write width-1 aperture to file
@ -624,7 +623,7 @@ def merge(opts, args, gui = None):
if fullname and fullname.lower() != "none": if fullname and fullname.lower() != "none":
OutputFiles.append(fullname) OutputFiles.append(fullname)
#print 'Writing %s ...' % fullname #print 'Writing %s ...' % fullname
fid = file(fullname, 'wt') fid = open(fullname, 'wt')
writeGerberHeader(fid) writeGerberHeader(fid)
# Write width-1 aperture to file # Write width-1 aperture to file
@ -644,19 +643,19 @@ def merge(opts, args, gui = None):
# of tools. # of tools.
if 0: if 0:
Tools = {} Tools = {}
for job in config.Jobs.values(): for job in list(config.Jobs.values()):
for key in job.xcommands.keys(): for key in list(job.xcommands.keys()):
Tools[key] = 1 Tools[key] = 1
Tools = Tools.keys() Tools = list(Tools.keys())
Tools.sort() Tools.sort()
else: else:
toolNum = 0 toolNum = 0
# First construct global mapping of diameters to tool numbers # First construct global mapping of diameters to tool numbers
for job in config.Jobs.values(): for job in list(config.Jobs.values()):
for tool,diam in job.xdiam.items(): for tool,diam in list(job.xdiam.items()):
if config.GlobalToolRMap.has_key(diam): if diam in config.GlobalToolRMap:
continue continue
toolNum += 1 toolNum += 1
@ -665,24 +664,24 @@ def merge(opts, args, gui = None):
# Cluster similar tool sizes to reduce number of drills # Cluster similar tool sizes to reduce number of drills
if config.Config['drillclustertolerance'] > 0: if config.Config['drillclustertolerance'] > 0:
config.GlobalToolRMap = drillcluster.cluster( config.GlobalToolRMap, config.Config['drillclustertolerance'] ) config.GlobalToolRMap = drillcluster.cluster( config.GlobalToolRMap, config.Config['drillclustertolerance'] )
drillcluster.remap( Place.jobs, config.GlobalToolRMap.items() ) drillcluster.remap( Place.jobs, list(config.GlobalToolRMap.items()) )
# Now construct mapping of tool numbers to diameters # Now construct mapping of tool numbers to diameters
for diam,tool in config.GlobalToolRMap.items(): for diam,tool in list(config.GlobalToolRMap.items()):
config.GlobalToolMap[tool] = diam config.GlobalToolMap[tool] = diam
# Tools is just a list of tool names # Tools is just a list of tool names
Tools = config.GlobalToolMap.keys() Tools = list(config.GlobalToolMap.keys())
Tools.sort() Tools.sort()
fullname = config.Config['fabricationdrawingfile'] fullname = config.Config['fabricationdrawingfile']
if fullname and fullname.lower() != 'none': if fullname and fullname.lower() != 'none':
if len(Tools) > strokes.MaxNumDrillTools: if len(Tools) > strokes.MaxNumDrillTools:
raise RuntimeError, "Only %d different tool sizes supported for fabrication drawing." % strokes.MaxNumDrillTools raise RuntimeError("Only %d different tool sizes supported for fabrication drawing." % strokes.MaxNumDrillTools)
OutputFiles.append(fullname) OutputFiles.append(fullname)
#print 'Writing %s ...' % fullname #print 'Writing %s ...' % fullname
fid = file(fullname, 'wt') fid = open(fullname, 'wt')
writeGerberHeader(fid) writeGerberHeader(fid)
writeApertures(fid, {drawing_code1: None}) writeApertures(fid, {drawing_code1: None})
fid.write('%s*\n' % drawing_code1) # Choose drawing aperture fid.write('%s*\n' % drawing_code1) # Choose drawing aperture
@ -699,7 +698,7 @@ def merge(opts, args, gui = None):
fullname = 'merged.drills.xln' fullname = 'merged.drills.xln'
OutputFiles.append(fullname) OutputFiles.append(fullname)
#print 'Writing %s ...' % fullname #print 'Writing %s ...' % fullname
fid = file(fullname, 'wt') fid = open(fullname, 'wt')
writeExcellonHeader(fid) writeExcellonHeader(fid)
@ -709,7 +708,7 @@ def merge(opts, args, gui = None):
try: try:
size = config.GlobalToolMap[tool] size = config.GlobalToolMap[tool]
except: except:
raise RuntimeError, "INTERNAL ERROR: Tool code %s not found in global tool map" % tool raise RuntimeError("INTERNAL ERROR: Tool code %s not found in global tool map" % tool)
writeExcellonTool(fid, tool, size) writeExcellonTool(fid, tool, size)
writeExcellonHeaderEnd(fid) writeExcellonHeaderEnd(fid)
@ -755,57 +754,57 @@ def merge(opts, args, gui = None):
fullname = 'merged.toollist.drl' fullname = 'merged.toollist.drl'
OutputFiles.append(fullname) OutputFiles.append(fullname)
#print 'Writing %s ...' % fullname #print 'Writing %s ...' % fullname
fid = file(fullname, 'wt') fid = open(fullname, 'wt')
print '-'*50 print('-'*50)
# add metric support (1/1000 mm vs. 1/100,000 inch) # add metric support (1/1000 mm vs. 1/100,000 inch)
if config.Config['measurementunits'] == 'inch': if config.Config['measurementunits'] == 'inch':
print ' Job Size : %f" x %f"' % (MaxXExtent-OriginX, MaxYExtent-OriginY) print(' Job Size : %f" x %f"' % (MaxXExtent-OriginX, MaxYExtent-OriginY))
print ' Job Area : %.2f sq. in.' % totalarea print(' Job Area : %.2f sq. in.' % totalarea)
else: else:
print ' Job Size : %.2fmm x %.2fmm' % (MaxXExtent-OriginX, MaxYExtent-OriginY) print(' Job Size : %.2fmm x %.2fmm' % (MaxXExtent-OriginX, MaxYExtent-OriginY))
print ' Job Area : %.0f mm2' % totalarea print(' Job Area : %.0f mm2' % totalarea)
print ' Area Usage : %.1f%%' % (jobarea/totalarea*100) print(' Area Usage : %.1f%%' % (jobarea/totalarea*100))
print ' Drill hits : %d' % drillhits print(' Drill hits : %d' % drillhits)
if config.Config['measurementunits'] == 'inch': if config.Config['measurementunits'] == 'inch':
print 'Drill density : %.1f hits/sq.in.' % (drillhits/totalarea) print('Drill density : %.1f hits/sq.in.' % (drillhits/totalarea))
else: else:
print 'Drill density : %.2f hits/cm2' % (100*drillhits/totalarea) print('Drill density : %.2f hits/cm2' % (100*drillhits/totalarea))
print '\nTool List:' print('\nTool List:')
smallestDrill = 999.9 smallestDrill = 999.9
for tool in Tools: for tool in Tools:
if ToolStats[tool]: if ToolStats[tool]:
if config.Config['measurementunits'] == 'inch': if config.Config['measurementunits'] == 'inch':
fid.write('%s %.4fin\n' % (tool, config.GlobalToolMap[tool])) fid.write('%s %.4fin\n' % (tool, config.GlobalToolMap[tool]))
print ' %s %.4f" %5d hits' % (tool, config.GlobalToolMap[tool], ToolStats[tool]) print(' %s %.4f" %5d hits' % (tool, config.GlobalToolMap[tool], ToolStats[tool]))
else: else:
fid.write('%s %.4fmm\n' % (tool, config.GlobalToolMap[tool])) fid.write('%s %.4fmm\n' % (tool, config.GlobalToolMap[tool]))
print ' %s %.4fmm %5d hits' % (tool, config.GlobalToolMap[tool], ToolStats[tool]) print(' %s %.4fmm %5d hits' % (tool, config.GlobalToolMap[tool], ToolStats[tool]))
smallestDrill = min(smallestDrill, config.GlobalToolMap[tool]) smallestDrill = min(smallestDrill, config.GlobalToolMap[tool])
fid.close() fid.close()
if config.Config['measurementunits'] == 'inch': if config.Config['measurementunits'] == 'inch':
print "Smallest Tool: %.4fin" % smallestDrill print("Smallest Tool: %.4fin" % smallestDrill)
else: else:
print "Smallest Tool: %.4fmm" % smallestDrill print("Smallest Tool: %.4fmm" % smallestDrill)
print print()
print 'Output Files :' print('Output Files :')
for f in OutputFiles: for f in OutputFiles:
print ' ', f print(' ', f)
if (MaxXExtent-OriginX)>config.Config['panelwidth'] or (MaxYExtent-OriginY)>config.Config['panelheight']: if (MaxXExtent-OriginX)>config.Config['panelwidth'] or (MaxYExtent-OriginY)>config.Config['panelheight']:
print '*'*75 print('*'*75)
print '*' print('*')
# add metric support (1/1000 mm vs. 1/100,000 inch) # add metric support (1/1000 mm vs. 1/100,000 inch)
if config.Config['measurementunits'] == 'inch': if config.Config['measurementunits'] == 'inch':
print '* ERROR: Merged job exceeds panel dimensions of %.1f"x%.1f"' % (config.Config['panelwidth'],config.Config['panelheight']) print('* ERROR: Merged job exceeds panel dimensions of %.1f"x%.1f"' % (config.Config['panelwidth'],config.Config['panelheight']))
else: else:
print '* ERROR: Merged job exceeds panel dimensions of %.1fmmx%.1fmm' % (config.Config['panelwidth'],config.Config['panelheight']) print('* ERROR: Merged job exceeds panel dimensions of %.1fmmx%.1fmm' % (config.Config['panelwidth'],config.Config['panelheight']))
print '*' print('*')
print '*'*75 print('*'*75)
sys.exit(1) sys.exit(1)
# Done! # Done!
@ -826,19 +825,19 @@ def main():
if opt in ('-h', '--help'): if opt in ('-h', '--help'):
usage() usage()
elif opt in ('-v', '--version'): elif opt in ('-v', '--version'):
print """ print("""
GerbMerge Version %s -- Combine multiple Gerber/Excellon files GerbMerge Version %s -- Combine multiple Gerber/Excellon files
This program is licensed under the GNU General Public License (GPL) This program is licensed under the GNU General Public License (GPL)
Version 3. See LICENSE file or http://www.fsf.org for details of this license. Version 3. See LICENSE file or http://www.fsf.org for details of this license.
ProvideYourOwn - http://provideyourown.com ProvideYourOwn - http://provideyourown.com
""" % (__version__) """ % (__version__))
sys.exit(0) sys.exit(0)
elif opt in ('--octagons', '--random-search','--full-search','--rs-fsjobs','--place-file','--no-trim-gerber','--no-trim-excellon', '--search-timeout', '-s', '--skipdisclaimer'): elif opt in ('--octagons', '--random-search','--full-search','--rs-fsjobs','--place-file','--no-trim-gerber','--no-trim-excellon', '--search-timeout', '-s', '--skipdisclaimer'):
pass ## arguments are valid pass ## arguments are valid
else: else:
raise RuntimeError, "Unknown option: %s" % opt raise RuntimeError("Unknown option: %s" % opt)
if len(args) > 2 or len(args) < 1: if len(args) > 2 or len(args) < 1:
usage() usage()

View File

@ -18,7 +18,7 @@ http://github.com/unwireddevices/gerbmerge
import sys import sys
import re import re
import string import string
import __builtin__ import builtins
import copy import copy
import types import types
@ -235,17 +235,17 @@ class Job:
self.maxy += y_shift self.maxy += y_shift
# Shift all commands # Shift all commands
for layer, command in self.commands.iteritems(): for layer, command in self.commands.items():
# Loop through each command in each layer # Loop through each command in each layer
for index in range( len(command) ): for index in range( len(command) ):
c = command[index] c = command[index]
# Shift X and Y coordinate of command # Shift X and Y coordinate of command
if type(c) == types.TupleType: ## ensure that command is of type tuple if type(c) == tuple: ## ensure that command is of type tuple
command_list = list(c) ## convert tuple to list command_list = list(c) ## convert tuple to list
if (type( command_list[0] ) == types.IntType) \ if (type( command_list[0] ) == int) \
and (type( command_list[1] ) == types.IntType): ## ensure that first two elemenst are integers and (type( command_list[1] ) == int): ## ensure that first two elemenst are integers
command_list[0] += x_shift command_list[0] += x_shift
command_list[1] += y_shift command_list[1] += y_shift
command[index] = tuple(command_list) ## convert list back to tuple command[index] = tuple(command_list) ## convert list back to tuple
@ -253,7 +253,7 @@ class Job:
self.commands[layer] = command ## set modified command self.commands[layer] = command ## set modified command
# Shift all excellon commands # Shift all excellon commands
for tool, command in self.xcommands.iteritems(): for tool, command in self.xcommands.items():
# Loop through each command in each layer # Loop through each command in each layer
for index in range( len(command) ): for index in range( len(command) ):
@ -261,12 +261,12 @@ class Job:
# Shift X and Y coordinate of command # Shift X and Y coordinate of command
command_list = list(c) ## convert tuple to list command_list = list(c) ## convert tuple to list
if ( type( command_list[0] ) == types.IntType ) \ if ( type( command_list[0] ) == int ) \
and ( type( command_list[1] ) == types.IntType ): ## ensure that first two elemenst are integers and ( type( command_list[1] ) == int ): ## ensure that first two elemenst are integers
command_list[0] += x_shift / 10 command_list[0] += x_shift / 10
command_list[1] += y_shift / 10 command_list[1] += y_shift / 10
if ( type( command_list[2] ) == types.IntType ) \ if ( type( command_list[2] ) == int ) \
and ( type( command_list[3] ) == types.IntType ): ## ensure that first two elemenst are integerslen(command_list) == 4: and ( type( command_list[3] ) == int ): ## ensure that first two elemenst are integerslen(command_list) == 4:
# G85 command, need to shift the second pair of xy, too. # G85 command, need to shift the second pair of xy, too.
command_list[2] += x_shift / 10 command_list[2] += x_shift / 10
command_list[3] += y_shift / 10 command_list[3] += y_shift / 10
@ -286,7 +286,7 @@ class Job:
#print 'Reading data from %s ...' % fullname #print 'Reading data from %s ...' % fullname
fid = file(fullname, 'rt') fid = open(fullname, 'rt')
currtool = None currtool = None
self.apxlat[layername] = {} self.apxlat[layername] = {}
@ -338,7 +338,7 @@ class Job:
for line in fid: for line in fid:
# Get rid of CR characters (0x0D) and leading/trailing blanks # Get rid of CR characters (0x0D) and leading/trailing blanks
line = string.replace(line, '\x0D', '').strip() line = str.replace(line, '\x0D', '').strip()
# Old location of format_pat search. Now moved down into the sub-line parse loop below. # Old location of format_pat search. Now moved down into the sub-line parse loop below.
@ -354,11 +354,11 @@ class Job:
match = apdef_pat.match(line) match = apdef_pat.match(line)
if match: if match:
if currtool: if currtool:
raise RuntimeError, "File %s has an aperture definition that comes after drawing commands." % fullname raise RuntimeError("File %s has an aperture definition that comes after drawing commands." % fullname)
A = aptable.parseAperture(line, self.apmxlat[layername]) A = aptable.parseAperture(line, self.apmxlat[layername])
if not A: if not A:
raise RuntimeError, "Unknown aperture definition in file %s" % fullname raise RuntimeError("Unknown aperture definition in file %s" % fullname)
# [andreika]: apply units # [andreika]: apply units
if type(A.dimx) == float or type(A.dimx) == int: if type(A.dimx) == float or type(A.dimx) == int:
A.dimx *= units_div A.dimx *= units_div
@ -366,11 +366,11 @@ class Job:
A.dimy *= units_div A.dimy *= units_div
hash = A.hash() hash = A.hash()
if not RevGAT.has_key(hash): if hash not in RevGAT:
#print line #print line
#print self.apmxlat #print self.apmxlat
#print RevGAT #print RevGAT
raise RuntimeError, 'File %s has aperture definition "%s" not in global aperture table.' % (fullname, hash) raise RuntimeError('File %s has aperture definition "%s" not in global aperture table.' % (fullname, hash))
# This says that all draw commands with this aperture code will # This says that all draw commands with this aperture code will
# be replaced by aperture self.apxlat[layername][code]. # be replaced by aperture self.apxlat[layername][code].
@ -402,7 +402,7 @@ class Job:
continue # ignore it so func doesn't choke on it continue # ignore it so func doesn't choke on it
if line[:3] == '%SF': # scale factor - we will ignore it if line[:3] == '%SF': # scale factor - we will ignore it
print 'Scale factor parameter ignored: ' + line print('Scale factor parameter ignored: ' + line)
continue continue
# end basic diptrace fixes # end basic diptrace fixes
@ -411,11 +411,11 @@ class Job:
M = amacro.parseApertureMacro(line,fid) M = amacro.parseApertureMacro(line,fid)
if M: if M:
if currtool: if currtool:
raise RuntimeError, "File %s has an aperture macro definition that comes after drawing commands." % fullname raise RuntimeError("File %s has an aperture macro definition that comes after drawing commands." % fullname)
hash = M.hash() hash = M.hash()
if not RevGAMT.has_key(hash): if hash not in RevGAMT:
raise RuntimeError, 'File %s has aperture macro definition not in global aperture macro table:\n%s' % (fullname, hash) raise RuntimeError('File %s has aperture macro definition not in global aperture macro table:\n%s' % (fullname, hash))
# This says that all aperture definition commands that reference this macro name # This says that all aperture definition commands that reference this macro name
# will be replaced by aperture macro name self.apmxlat[layername][macroname]. # will be replaced by aperture macro name self.apmxlat[layername][macroname].
@ -445,9 +445,9 @@ class Job:
continue continue
if item[0]=='T': # omit trailing zeroes if item[0]=='T': # omit trailing zeroes
raise RuntimeError, "Trailing zeroes not supported in RS274X files" raise RuntimeError("Trailing zeroes not supported in RS274X files")
if item[0]=='I': # incremental co-ordinates if item[0]=='I': # incremental co-ordinates
raise RuntimeError, "Incremental co-ordinates not supported in RS274X files" raise RuntimeError("Incremental co-ordinates not supported in RS274X files")
if item[0]=='N': # Maximum digits for N* commands...ignore it if item[0]=='N': # Maximum digits for N* commands...ignore it
continue continue
@ -508,7 +508,7 @@ class Job:
continue continue
raise RuntimeError, "G-Code 'G%02d' is not supported" % gcode raise RuntimeError("G-Code 'G%02d' is not supported" % gcode)
# See if this is a tool change (aperture change) command # See if this is a tool change (aperture change) command
match = tool_pat.match(sub_line) match = tool_pat.match(sub_line)
@ -541,8 +541,8 @@ class Job:
continue continue
# Map it using our translation table # Map it using our translation table
if not self.apxlat[layername].has_key(currtool): if currtool not in self.apxlat[layername]:
raise RuntimeError, 'File %s has tool change command "%s" with no corresponding translation' % (fullname, currtool) raise RuntimeError('File %s has tool change command "%s" with no corresponding translation' % (fullname, currtool))
currtool = self.apxlat[layername][currtool] currtool = self.apxlat[layername][currtool]
@ -561,17 +561,17 @@ class Job:
match = drawXY_pat.match(sub_line) match = drawXY_pat.match(sub_line)
isLastShorthand = False # By default assume we don't make use of last_x and last_y isLastShorthand = False # By default assume we don't make use of last_x and last_y
if match: if match:
x, y, d = map(__builtin__.int, match.groups()) x, y, d = list(map(builtins.int, match.groups()))
else: else:
match = drawX_pat.match(sub_line) match = drawX_pat.match(sub_line)
if match: if match:
x, d = map(__builtin__.int, match.groups()) x, d = list(map(builtins.int, match.groups()))
y = last_y y = last_y
isLastShorthand = True # Indicate we're making use of last_x/last_y isLastShorthand = True # Indicate we're making use of last_x/last_y
else: else:
match = drawY_pat.match(sub_line) match = drawY_pat.match(sub_line)
if match: if match:
y, d = map(__builtin__.int, match.groups()) y, d = list(map(builtins.int, match.groups()))
x = last_x x = last_x
isLastShorthand = True # Indicate we're making use of last_x/last_y isLastShorthand = True # Indicate we're making use of last_x/last_y
@ -579,17 +579,17 @@ class Job:
if match is None: if match is None:
match = cdrawXY_pat.match(sub_line) match = cdrawXY_pat.match(sub_line)
if match: if match:
x, y, I, J, d = map(__builtin__.int, match.groups()) x, y, I, J, d = list(map(builtins.int, match.groups()))
else: else:
match = cdrawX_pat.match(sub_line) match = cdrawX_pat.match(sub_line)
if match: if match:
x, I, J, d = map(__builtin__.int, match.groups()) x, I, J, d = list(map(builtins.int, match.groups()))
y = last_y y = last_y
isLastShorthand = True # Indicate we're making use of last_x/last_y isLastShorthand = True # Indicate we're making use of last_x/last_y
else: else:
match = cdrawY_pat.match(sub_line) match = cdrawY_pat.match(sub_line)
if match: if match:
y, I, J, d = map(__builtin__.int, match.groups()) y, I, J, d = list(map(builtins.int, match.groups()))
x = last_x x = last_x
isLastShorthand = True # Indicate we're making use of last_x/last_y isLastShorthand = True # Indicate we're making use of last_x/last_y
@ -601,7 +601,7 @@ class Job:
if (d != 2) and (last_gmode != 36): if (d != 2) and (last_gmode != 36):
# [andreika]: check for fill mode more accurately # [andreika]: check for fill mode more accurately
if not in_fill_mode: if not in_fill_mode:
raise RuntimeError, 'File %s has draw command %s with no aperture chosen' % (fullname, sub_line) raise RuntimeError('File %s has draw command %s with no aperture chosen' % (fullname, sub_line))
# Save last_x/y BEFORE scaling to 2.5 format else subsequent single-ordinate # Save last_x/y BEFORE scaling to 2.5 format else subsequent single-ordinate
# flashes (e.g., Y with no X) will be scaled twice! # flashes (e.g., Y with no X) will be scaled twice!
@ -649,7 +649,7 @@ class Job:
if match: if match:
break break
else: else:
raise RuntimeError, 'File %s has uninterpretable line:\n %s' % (fullname, line) raise RuntimeError('File %s has uninterpretable line:\n %s' % (fullname, line))
sub_line = sub_line[match.end():] sub_line = sub_line[match.end():]
# end while still things to match on this line # end while still things to match on this line
@ -657,13 +657,13 @@ class Job:
fid.close() fid.close()
if 0: if 0:
print layername print(layername)
print self.commands[layername] print(self.commands[layername])
def parseExcellon(self, fullname): def parseExcellon(self, fullname):
#print 'Reading data from %s ...' % fullname #print 'Reading data from %s ...' % fullname
fid = file(fullname, 'rt') fid = open(fullname, 'rt')
currtool = None currtool = None
suppress_leading = True # Suppress leading zeros by default, equivalent to 'INCH,TZ' suppress_leading = True # Suppress leading zeros by default, equivalent to 'INCH,TZ'
@ -706,14 +706,14 @@ class Job:
V.append(None) V.append(None)
return tuple(V) return tuple(V)
for line in fid.xreadlines(): for line in fid:
# Get rid of CR characters # Get rid of CR characters
line = string.replace(line, '\x0D', '') line = str.replace(line, '\x0D', '')
# add support for DipTrace # add support for DipTrace
if line[:6]=='METRIC': if line[:6]=='METRIC':
if (config.Config['measurementunits'] == 'inch'): if (config.Config['measurementunits'] == 'inch'):
raise RuntimeError, "File %s units do match config file" % fullname raise RuntimeError("File %s units do match config file" % fullname)
else: else:
#print "ignoring METRIC directive: " + line #print "ignoring METRIC directive: " + line
continue # ignore it so func doesn't choke on it continue # ignore it so func doesn't choke on it
@ -746,14 +746,14 @@ class Job:
try: try:
diam = float(diam) diam = float(diam)
except: except:
raise RuntimeError, "File %s has illegal tool diameter '%s'" % (fullname, diam) raise RuntimeError("File %s has illegal tool diameter '%s'" % (fullname, diam))
# Canonicalize tool number because Protel (of course) sometimes specifies it # Canonicalize tool number because Protel (of course) sometimes specifies it
# as T01 and sometimes as T1. We canonicalize to T01. # as T01 and sometimes as T1. We canonicalize to T01.
currtool = 'T%02d' % int(currtool[1:]) currtool = 'T%02d' % int(currtool[1:])
if self.xdiam.has_key(currtool): if currtool in self.xdiam:
raise RuntimeError, "File %s defines tool %s more than once" % (fullname, currtool) raise RuntimeError("File %s defines tool %s more than once" % (fullname, currtool))
self.xdiam[currtool] = diam self.xdiam[currtool] = diam
continue continue
@ -779,13 +779,13 @@ class Job:
try: try:
diam = self.ToolList[currtool] diam = self.ToolList[currtool]
except: except:
raise RuntimeError, "File %s uses tool code %s that is not defined in the job's tool list" % (fullname, currtool) raise RuntimeError("File %s uses tool code %s that is not defined in the job's tool list" % (fullname, currtool))
else: else:
try: try:
diam = config.DefaultToolList[currtool] diam = config.DefaultToolList[currtool]
except: except:
#print config.DefaultToolList #print config.DefaultToolList
raise RuntimeError, "File %s uses tool code %s that is not defined in default tool list" % (fullname, currtool) raise RuntimeError("File %s uses tool code %s that is not defined in default tool list" % (fullname, currtool))
self.xdiam[currtool] = diam self.xdiam[currtool] = diam
continue continue
@ -811,7 +811,7 @@ class Job:
if match: if match:
if currtool is None: if currtool is None:
raise RuntimeError, 'File %s has plunge command without previous tool selection' % fullname raise RuntimeError('File %s has plunge command without previous tool selection' % fullname)
try: try:
self.xcommands[currtool].append((x,y,stop_x,stop_y)) self.xcommands[currtool].append((x,y,stop_x,stop_y))
@ -827,10 +827,10 @@ class Job:
if pat.match(line): if pat.match(line):
break break
else: else:
raise RuntimeError, 'File %s has uninterpretable line:\n %s' % (fullname, line) raise RuntimeError('File %s has uninterpretable line:\n %s' % (fullname, line))
def hasLayer(self, layername): def hasLayer(self, layername):
return self.commands.has_key(layername) return layername in self.commands
def writeGerber(self, fid, layername, Xoff, Yoff): def writeGerber(self, fid, layername, Xoff, Yoff):
"Write out the data such that the lower-left corner of this job is at the given (X,Y) position, in inches" "Write out the data such that the lower-left corner of this job is at the given (X,Y) position, in inches"
@ -858,7 +858,7 @@ class Job:
# due to panelizing. # due to panelizing.
fid.write('X%07dY%07dD02*\n' % (X, Y)) fid.write('X%07dY%07dD02*\n' % (X, Y))
for cmd in self.commands[layername]: for cmd in self.commands[layername]:
if type(cmd) is types.TupleType: if type(cmd) is tuple:
if len(cmd)==3: if len(cmd)==3:
x, y, d = cmd x, y, d = cmd
fid.write('X%07dY%07dD%02d*\n' % (x+DX, y+DY, d)) fid.write('X%07dY%07dD%02d*\n' % (x+DX, y+DY, d))
@ -877,7 +877,7 @@ class Job:
def findTools(self, diameter): def findTools(self, diameter):
"Find the tools, if any, with the given diameter in inches. There may be more than one!" "Find the tools, if any, with the given diameter in inches. There may be more than one!"
L = [] L = []
for tool, diam in self.xdiam.items(): for tool, diam in list(self.xdiam.items()):
if diam==diameter: if diam==diameter:
L.append(tool) L.append(tool)
return L return L
@ -927,7 +927,7 @@ class Job:
# Boogie # Boogie
for ltool in ltools: for ltool in ltools:
if self.xcommands.has_key(ltool): if ltool in self.xcommands:
for cmd in self.xcommands[ltool]: for cmd in self.xcommands[ltool]:
x, y, stop_x, stop_y = cmd x, y, stop_x, stop_y = cmd
new_x = x+DX new_x = x+DX
@ -965,7 +965,7 @@ class Job:
ltools = self.findTools(diameter) ltools = self.findTools(diameter)
for ltool in ltools: for ltool in ltools:
if self.xcommands.has_key(ltool): if ltool in self.xcommands:
for cmd in self.xcommands[ltool]: for cmd in self.xcommands[ltool]:
x, y, stop_x, stop_y = cmd x, y, stop_x, stop_y = cmd
# add metric support (1/1000 mm vs. 1/100,000 inch) # add metric support (1/1000 mm vs. 1/100,000 inch)
@ -979,7 +979,7 @@ class Job:
GAT=config.GAT GAT=config.GAT
if self.apertures.has_key(layername): if layername in self.apertures:
apdict = {}.fromkeys(self.apertures[layername]) apdict = {}.fromkeys(self.apertures[layername])
apmlist = [GAT[ap].dimx for ap in self.apertures[layername] if GAT[ap].apname=='Macro'] apmlist = [GAT[ap].dimx for ap in self.apertures[layername] if GAT[ap].apname=='Macro']
apmdict = {}.fromkeys(apmlist) apmdict = {}.fromkeys(apmlist)
@ -990,8 +990,8 @@ class Job:
def makeLocalApertureCode(self, layername, AP): def makeLocalApertureCode(self, layername, AP):
"Find or create a layer-specific aperture code to represent the global aperture given" "Find or create a layer-specific aperture code to represent the global aperture given"
if AP.code not in self.apxlat[layername].values(): if AP.code not in list(self.apxlat[layername].values()):
lastCode = aptable.findHighestApertureCode(self.apxlat[layername].keys()) lastCode = aptable.findHighestApertureCode(list(self.apxlat[layername].keys()))
localCode = 'D%d' % (lastCode+1) localCode = 'D%d' % (lastCode+1)
self.apxlat[layername][localCode] = AP.code self.apxlat[layername][localCode] = AP.code
@ -1008,7 +1008,7 @@ class Job:
lastAperture = None lastAperture = None
for cmd in self.commands[layername]: for cmd in self.commands[layername]:
if type(cmd) == types.TupleType: if type(cmd) == tuple:
# It is a data command: tuple (X, Y, D), all integers, or (X, Y, I, J, D), all integers. # It is a data command: tuple (X, Y, D), all integers, or (X, Y, I, J, D), all integers.
if len(cmd)==3: if len(cmd)==3:
x, y, d = cmd x, y, d = cmd
@ -1180,12 +1180,12 @@ class Job:
self.commands[layername] = newcmds self.commands[layername] = newcmds
def trimGerber(self): def trimGerber(self):
for layername in self.commands.keys(): for layername in list(self.commands.keys()):
self.trimGerberLayer(layername) self.trimGerberLayer(layername)
def trimExcellon(self): def trimExcellon(self):
"Remove plunge commands that are outside job dimensions" "Remove plunge commands that are outside job dimensions"
keys = self.xcommands.keys() keys = list(self.xcommands.keys())
for toolname in keys: for toolname in keys:
# Remember Excellon is 2.4 format while Gerber data is 2.5 format # Remember Excellon is 2.4 format while Gerber data is 2.5 format
validList = [tup for tup in self.xcommands[toolname] validList = [tup for tup in self.xcommands[toolname]
@ -1390,10 +1390,10 @@ def rotateJob(job, degrees = 90, flip = 0, firstpass = True):
# those apertures which have an orientation: rectangles, ovals, and macros. # those apertures which have an orientation: rectangles, ovals, and macros.
ToolChangeReplace = {} ToolChangeReplace = {}
for layername in job.apxlat.keys(): for layername in list(job.apxlat.keys()):
J.apxlat[layername] = {} J.apxlat[layername] = {}
for ap in job.apxlat[layername].keys(): for ap in list(job.apxlat[layername].keys()):
code = job.apxlat[layername][ap] code = job.apxlat[layername][ap]
A = GAT[code] A = GAT[code]
@ -1438,18 +1438,18 @@ def rotateJob(job, degrees = 90, flip = 0, firstpass = True):
offset = 0 offset = 0
else: else:
offset = job.maxy-job.miny offset = job.maxy-job.miny
for layername in job.commands.keys(): for layername in list(job.commands.keys()):
J.commands[layername] = [] J.commands[layername] = []
J.apertures[layername] = [] J.apertures[layername] = []
for cmd in job.commands[layername]: for cmd in job.commands[layername]:
# Is it a drawing command? # Is it a drawing command?
if type(cmd) is types.TupleType: if type(cmd) is tuple:
if len(cmd)==3: if len(cmd)==3:
x, y, d = map(__builtin__.int, cmd) x, y, d = list(map(builtins.int, cmd))
II=JJ=None II=JJ=None
else: else:
x, y, II, JJ, d, signed = map(__builtin__.int, cmd) # J is already used as Job object x, y, II, JJ, d, signed = list(map(builtins.int, cmd)) # J is already used as Job object
else: else:
# No, must be a string indicating aperture change, G-code, or RS274-X command. # No, must be a string indicating aperture change, G-code, or RS274-X command.
if cmd[0] in ('G', '%'): if cmd[0] in ('G', '%'):
@ -1497,13 +1497,13 @@ def rotateJob(job, degrees = 90, flip = 0, firstpass = True):
J.commands[layername].append((newx,newy,d)) J.commands[layername].append((newx,newy,d))
if 0: if 0:
print job.minx, job.miny, offset print(job.minx, job.miny, offset)
print layername print(layername)
print J.commands[layername] print(J.commands[layername])
# Finally, rotate drills. Offset is in hundred-thousandths (2.5) while Excellon # Finally, rotate drills. Offset is in hundred-thousandths (2.5) while Excellon
# data is in 2.4 format. # data is in 2.4 format.
for tool in job.xcommands.keys(): for tool in list(job.xcommands.keys()):
J.xcommands[tool] = [] J.xcommands[tool] = []
for x,y,stop_x,stop_y in job.xcommands[tool]: for x,y,stop_x,stop_y in job.xcommands[tool]:

View File

@ -93,7 +93,7 @@ def writeChar(fid, c, X, Y, degrees):
try: try:
glyph = strokes.StrokeMap[c] glyph = strokes.StrokeMap[c]
except: except:
raise RuntimeError, 'No glyph for character %s' % hex(ord(c)) raise RuntimeError('No glyph for character %s' % hex(ord(c)))
writeGlyph(fid, glyph, X, Y, degrees, c) writeGlyph(fid, glyph, X, Y, degrees, c)
@ -140,7 +140,7 @@ if __name__=="__main__":
s = string.digits+string.letters+string.punctuation s = string.digits+string.letters+string.punctuation
#s = "The quick brown fox jumped over the lazy dog!" #s = "The quick brown fox jumped over the lazy dog!"
fid = file('test.ger','wt') fid = open('test.ger','wt')
fid.write("""G75* fid.write("""G75*
G70* G70*
%OFA0B0*% %OFA0B0*%

View File

@ -184,7 +184,7 @@ def findJob(jobname, rotatedFlipped, Jobs=config.Jobs):
try: try:
for existingjob in Jobs.keys(): for existingjob in list(Jobs.keys()):
if existingjob.lower() == fullname.lower(): ## job names are case insensitive if existingjob.lower() == fullname.lower(): ## job names are case insensitive
job = Jobs[existingjob] job = Jobs[existingjob]
return jobs.JobLayout(job) return jobs.JobLayout(job)
@ -194,13 +194,13 @@ def findJob(jobname, rotatedFlipped, Jobs=config.Jobs):
# Perhaps we just don't have a rotated or flipped job yet # Perhaps we just don't have a rotated or flipped job yet
if rotatedFlipped[0] or rotatedFlipped[1]: if rotatedFlipped[0] or rotatedFlipped[1]:
try: try:
for existingjob in Jobs.keys(): for existingjob in list(Jobs.keys()):
if existingjob.lower() == jobname.lower(): ## job names are case insensitive if existingjob.lower() == jobname.lower(): ## job names are case insensitive
job = Jobs[existingjob] job = Jobs[existingjob]
except: except:
raise RuntimeError, "Job name '%s' not found" % jobname raise RuntimeError("Job name '%s' not found" % jobname)
else: else:
raise RuntimeError, "Job name '%s' not found" % jobname raise RuntimeError("Job name '%s' not found" % jobname)
# Make a rotated/flipped job # Make a rotated/flipped job
job = jobs.rotateJob(job, rotatedFlipped[0], rotatedFlipped[1]) job = jobs.rotateJob(job, rotatedFlipped[0], rotatedFlipped[1])
@ -228,14 +228,14 @@ def parseJobSpec(spec, data):
elif rotation == "Rotate270": elif rotation == "Rotate270":
rotated = 270 rotated = 270
else: else:
raise RuntimeError, "Unsupported rotation: %s" % rotation raise RuntimeError("Unsupported rotation: %s" % rotation)
else: else:
rotated = 0 rotated = 0
return findJob(jobname, [rotated, 0]) return findJob(jobname, [rotated, 0])
else: else:
raise RuntimeError, "Matrix panels not yet supported" raise RuntimeError("Matrix panels not yet supported")
def parseColSpec(spec, data): def parseColSpec(spec, data):
jobs = Col() jobs = Col()
@ -302,9 +302,9 @@ def parseLayoutFile(fname):
""" """
try: try:
fid = file(fname, 'rt') fid = open(fname, 'rt')
except Exception, detail: except Exception as detail:
raise RuntimeError, "Unable to open layout file: %s\n %s" % (fname, str(detail)) raise RuntimeError("Unable to open layout file: %s\n %s" % (fname, str(detail)))
data = fid.read() data = fid.read()
fid.close() fid.close()
@ -312,16 +312,16 @@ def parseLayoutFile(fname):
# Replace all CR's in data with nothing, to convert DOS line endings # Replace all CR's in data with nothing, to convert DOS line endings
# to unix format (all LF's). # to unix format (all LF's).
data = string.replace(data, '\x0D', '') data = str.replace(data, '\x0D', '')
tree = parser.parse(data) tree = parser.parse(data)
# Last element of tree is number of characters parsed # Last element of tree is number of characters parsed
if not tree[0]: if not tree[0]:
raise RuntimeError, "Layout file cannot be parsed" raise RuntimeError("Layout file cannot be parsed")
if tree[2] != len(data): if tree[2] != len(data):
raise RuntimeError, "Parse error at character %d in layout file" % tree[2] raise RuntimeError("Parse error at character %d in layout file" % tree[2])
Rows = [] Rows = []
for rowspec in tree[1]: for rowspec in tree[1]:
@ -333,7 +333,7 @@ def parseLayoutFile(fname):
return Rows return Rows
if __name__=="__main__": if __name__=="__main__":
fid = file(sys.argv[1]) fid = open(sys.argv[1])
testdata = fid.read() testdata = fid.read()
fid.close() fid.close()

View File

@ -52,11 +52,11 @@ class Placement:
def write(self, fname): def write(self, fname):
"""Write placement to a file""" """Write placement to a file"""
fid = file(fname, 'wt') fid = open(fname, 'wt')
for job in self.jobs: for job in self.jobs:
fid.write('%s %.3f %.3f\n' % (job.job.name, job.x, job.y)) fid.write('%s %.3f %.3f\n' % (job.job.name, job.x, job.y))
# added; thought it would be useful to know # added; thought it would be useful to know
print "job locations: job - %s x,y(%f,%f)" % (job.job.name, job.x, job.y) print("job locations: job - %s x,y(%f,%f)" % (job.job.name, job.x, job.y))
fid.close() fid.close()
def addFromFile(self, fname, Jobs): def addFromFile(self, fname, Jobs):
@ -65,9 +65,9 @@ class Placement:
comment = re.compile(r'\s*(?:#.+)?$') comment = re.compile(r'\s*(?:#.+)?$')
try: try:
fid = file(fname, 'rt') fid = open(fname, 'rt')
except: except:
print 'Unable to open placement file: "%s"' % fname print('Unable to open placement file: "%s"' % fname)
sys.exit(1) sys.exit(1)
lines = fid.readlines() lines = fid.readlines()
@ -78,7 +78,7 @@ class Placement:
match = pat.match(line) match = pat.match(line)
if not match: if not match:
print 'Cannot interpret placement line in placement file:\n %s' % line print('Cannot interpret placement line in placement file:\n %s' % line)
sys.exit(1) sys.exit(1)
jobname, X, Y = match.groups() jobname, X, Y = match.groups()
@ -86,7 +86,7 @@ class Placement:
X = float(X) X = float(X)
Y = float(Y) Y = float(Y)
except: except:
print 'Illegal (X,Y) co-ordinates in placement file:\n %s' % line print('Illegal (X,Y) co-ordinates in placement file:\n %s' % line)
sys.exit(1) sys.exit(1)
# rotated or flipped # rotated or flipped

View File

@ -18,9 +18,9 @@ def schwartz(List, Metric):
def pairing(element, M = Metric): def pairing(element, M = Metric):
return (M(element), element) return (M(element), element)
paired = map(pairing, List) paired = list(map(pairing, List))
paired.sort() paired.sort()
return map(stripit, paired) return list(map(stripit, paired))
def stripit2(pair): def stripit2(pair):
return pair[0] return pair[0]
@ -31,8 +31,8 @@ def schwartz2(List, Metric):
def pairing(element, M = Metric): def pairing(element, M = Metric):
return (M(element), element) return (M(element), element)
paired = map(pairing, List) paired = list(map(pairing, List))
paired.sort() paired.sort()
theList = map(stripit, paired) theList = list(map(stripit, paired))
theMetrics = map(stripit2, paired) theMetrics = list(map(stripit2, paired))
return (theList, theMetrics) return (theList, theMetrics)

View File

@ -177,7 +177,7 @@ def mergeLines(Lines):
# Extend horizontal lines # Extend horizontal lines
NewHLines = {} NewHLines = {}
for yval,lines in HLines.items(): for yval,lines in list(HLines.items()):
# yval is the Y ordinate of this group of lines. lines is the set of all # yval is the Y ordinate of this group of lines. lines is the set of all
# lines with this Y ordinate. # lines with this Y ordinate.
NewHLines[yval] = [] NewHLines[yval] = []
@ -199,7 +199,7 @@ def mergeLines(Lines):
# Extend vertical lines # Extend vertical lines
NewVLines = {} NewVLines = {}
for xval,lines in VLines.items(): for xval,lines in list(VLines.items()):
# xval is the X ordinate of this group of lines. lines is the set of all # xval is the X ordinate of this group of lines. lines is the set of all
# lines with this X ordinate. # lines with this X ordinate.
NewVLines[xval] = [] NewVLines[xval] = []
@ -228,7 +228,7 @@ def mergeLines(Lines):
# or within each other. We will have to sort all horizontal lines by their # or within each other. We will have to sort all horizontal lines by their
# Y ordinates and group them according to Y ordinates that are close enough # Y ordinates and group them according to Y ordinates that are close enough
# to each other. # to each other.
yvals = HLines.keys() yvals = list(HLines.keys())
clusters = clusterOrdinates(yvals) # A list of clustered tuples containing yvals clusters = clusterOrdinates(yvals) # A list of clustered tuples containing yvals
for cluster in clusters: for cluster in clusters:
@ -240,7 +240,7 @@ def mergeLines(Lines):
# Y ordinate. Merge them together. # Y ordinate. Merge them together.
NewHLines.extend(mergeHLines(clusterLines)) NewHLines.extend(mergeHLines(clusterLines))
xvals = VLines.keys() xvals = list(VLines.keys())
clusters = clusterOrdinates(xvals) clusters = clusterOrdinates(xvals)
for cluster in clusters: for cluster in clusters:
clusterLines = [] clusterLines = []

View File

@ -19,11 +19,11 @@ import gerbmerge
_StartTime = 0.0 # Start time of tiling _StartTime = 0.0 # Start time of tiling
_CkpointTime = 0.0 # Next time to print stats _CkpointTime = 0.0 # Next time to print stats
_Placements = 0L # Number of placements attempted _Placements = 0 # Number of placements attempted
_PossiblePermutations = 0L # Number of different ways of ordering jobs _PossiblePermutations = 0 # Number of different ways of ordering jobs
_Permutations = 0L # Number of different job orderings already computed _Permutations = 0 # Number of different job orderings already computed
_TBestTiling = None # Best tiling so far _TBestTiling = None # Best tiling so far
_TBestScore = float(sys.maxint) # Smallest area so far _TBestScore = float(sys.maxsize) # Smallest area so far
_PrintStats = 1 # Print statistics every 3 seconds _PrintStats = 1 # Print statistics every 3 seconds
def printTilingStats(): def printTilingStats():
@ -42,11 +42,11 @@ def printTilingStats():
# add metric support (1/1000 mm vs. 1/100,000 inch) # add metric support (1/1000 mm vs. 1/100,000 inch)
if config.Config['measurementunits'] == 'inch': if config.Config['measurementunits'] == 'inch':
print "\r %5.2f%% complete / %ld/%ld Perm/Place / Smallest area: %.1f sq. in. / Best utilization: %.1f%%" % \ print("\r %5.2f%% complete / %ld/%ld Perm/Place / Smallest area: %.1f sq. in. / Best utilization: %.1f%%" % \
(percent, _Permutations, _Placements, area, utilization), (percent, _Permutations, _Placements, area, utilization), end=' ')
else: else:
print "\r %5.2f%% complete / %ld/%ld Perm/Place / Smallest area: %.1f sq. mm / Best utilization: %.1f%%" % \ print("\r %5.2f%% complete / %ld/%ld Perm/Place / Smallest area: %.1f sq. mm / Best utilization: %.1f%%" % \
(percent, _Permutations, _Placements, area, utilization), (percent, _Permutations, _Placements, area, utilization), end=' ')
if gerbmerge.GUI is not None: if gerbmerge.GUI is not None:
@ -86,7 +86,7 @@ def _tile_search1(Jobs, TSoFar, firstAddPoint, cfg=config.Config):
global _StartTime, _CkpointTime, _Placements, _TBestTiling, _TBestScore, _Permutations, _PrintStats global _StartTime, _CkpointTime, _Placements, _TBestTiling, _TBestScore, _Permutations, _PrintStats
if not TSoFar: if not TSoFar:
return (None, float(sys.maxint)) return (None, float(sys.maxsize))
if not Jobs: if not Jobs:
# Update the best tiling and score. If the new tiling matches # Update the best tiling and score. If the new tiling matches
@ -118,12 +118,12 @@ def _tile_search1(Jobs, TSoFar, firstAddPoint, cfg=config.Config):
remaining_jobs = Jobs[:job_ix]+Jobs[job_ix+1:] remaining_jobs = Jobs[:job_ix]+Jobs[job_ix+1:]
if 0: if 0:
print "Level %d (%s)" % (level, job.name) print("Level %d (%s)" % (level, job.name))
TSoFar.joblist() TSoFar.joblist()
for J in remaining_jobs: for J in remaining_jobs:
print J[2].name, ", ", print(J[2].name, ", ", end=' ')
print print()
print '-'*75 print('-'*75)
# Construct add-points for the non-rotated and rotated job. # Construct add-points for the non-rotated and rotated job.
# As an optimization, do not construct add-points for the rotated # As an optimization, do not construct add-points for the rotated
@ -154,7 +154,7 @@ def _tile_search1(Jobs, TSoFar, firstAddPoint, cfg=config.Config):
# Premature prune due to not being able to put this job anywhere. We # Premature prune due to not being able to put this job anywhere. We
# have pruned off 2^M permutations where M is the length of the remaining # have pruned off 2^M permutations where M is the length of the remaining
# jobs. # jobs.
_Permutations += 2L**len(remaining_jobs) _Permutations += 2**len(remaining_jobs)
if addpoints2: if addpoints2:
for ix in addpoints2: for ix in addpoints2:
@ -170,7 +170,7 @@ def _tile_search1(Jobs, TSoFar, firstAddPoint, cfg=config.Config):
# Premature prune due to not being able to put this job anywhere. We # Premature prune due to not being able to put this job anywhere. We
# have pruned off 2^M permutations where M is the length of the remaining # have pruned off 2^M permutations where M is the length of the remaining
# jobs. # jobs.
_Permutations += 2L**len(remaining_jobs) _Permutations += 2**len(remaining_jobs)
# If we've been at this for 3 seconds, print some status information # If we've been at this for 3 seconds, print some status information
if _PrintStats and time.time() > _CkpointTime: if _PrintStats and time.time() > _CkpointTime:
@ -185,9 +185,9 @@ def _tile_search1(Jobs, TSoFar, firstAddPoint, cfg=config.Config):
# end for each job in job list # end for each job in job list
def factorial(N): def factorial(N):
if (N <= 1): return 1L if (N <= 1): return 1
prod = long(N) prod = int(N)
while (N > 2): while (N > 2):
N -= 1 N -= 1
prod *= N prod *= N
@ -198,10 +198,10 @@ def initialize(printStats=1):
global _StartTime, _CkpointTime, _Placements, _TBestTiling, _TBestScore, _Permutations, _PossiblePermutations, _PrintStats global _StartTime, _CkpointTime, _Placements, _TBestTiling, _TBestScore, _Permutations, _PossiblePermutations, _PrintStats
_PrintStats = printStats _PrintStats = printStats
_Placements = 0L _Placements = 0
_Permutations = 0L _Permutations = 0
_TBestTiling = None _TBestTiling = None
_TBestScore = float(sys.maxint) _TBestScore = float(sys.maxsize)
def tile_search1(Jobs, X, Y): def tile_search1(Jobs, X, Y):
"""Wrapper around _tile_search1 to handle keyboard interrupt, etc.""" """Wrapper around _tile_search1 to handle keyboard interrupt, etc."""
@ -215,36 +215,36 @@ def tile_search1(Jobs, X, Y):
# This is assuming all jobs are unique and each job has a rotation (i.e., is not # This is assuming all jobs are unique and each job has a rotation (i.e., is not
# square). Practically, these assumptions make no difference because the software # square). Practically, these assumptions make no difference because the software
# currently doesn't optimize for cases of repeated jobs. # currently doesn't optimize for cases of repeated jobs.
_PossiblePermutations = (2L**len(Jobs))*factorial(len(Jobs)) _PossiblePermutations = (2**len(Jobs))*factorial(len(Jobs))
#print "Possible permutations:", _PossiblePermutations #print "Possible permutations:", _PossiblePermutations
print '='*70 print('='*70)
print "Starting placement using exhaustive search." print("Starting placement using exhaustive search.")
print "There are %ld possible permutations..." % _PossiblePermutations, print("There are %ld possible permutations..." % _PossiblePermutations, end=' ')
if _PossiblePermutations < 1e4: if _PossiblePermutations < 1e4:
print "this'll take no time at all." print("this'll take no time at all.")
elif _PossiblePermutations < 1e5: elif _PossiblePermutations < 1e5:
print "surf the web for a few minutes." print("surf the web for a few minutes.")
elif _PossiblePermutations < 1e6: elif _PossiblePermutations < 1e6:
print "take a long lunch." print("take a long lunch.")
elif _PossiblePermutations < 1e7: elif _PossiblePermutations < 1e7:
print "come back tomorrow." print("come back tomorrow.")
else: else:
print "don't hold your breath." print("don't hold your breath.")
print "Press Ctrl-C to stop and use the best placement so far." print("Press Ctrl-C to stop and use the best placement so far.")
print "Estimated maximum possible utilization is %.1f%%." % (tiling.maxUtilization(Jobs)*100) print("Estimated maximum possible utilization is %.1f%%." % (tiling.maxUtilization(Jobs)*100))
try: try:
_tile_search1(Jobs, tiling.Tiling(X,Y), 1) _tile_search1(Jobs, tiling.Tiling(X,Y), 1)
printTilingStats() printTilingStats()
print print()
except KeyboardInterrupt: except KeyboardInterrupt:
printTilingStats() printTilingStats()
print print()
print "Interrupted." print("Interrupted.")
computeTime = time.time() - _StartTime computeTime = time.time() - _StartTime
print "Computed %ld placements in %d seconds / %.1f placements/second" % (_Placements, computeTime, _Placements/computeTime) print("Computed %ld placements in %d seconds / %.1f placements/second" % (_Placements, computeTime, _Placements/computeTime))
print '='*70 print('='*70)
return _TBestTiling return _TBestTiling

View File

@ -21,9 +21,9 @@ import gerbmerge
_StartTime = 0.0 # Start time of tiling _StartTime = 0.0 # Start time of tiling
_CkpointTime = 0.0 # Next time to print stats _CkpointTime = 0.0 # Next time to print stats
_Placements = 0L # Number of placements attempted _Placements = 0 # Number of placements attempted
_TBestTiling = None # Best tiling so far _TBestTiling = None # Best tiling so far
_TBestScore = float(sys.maxint) # Smallest area so far _TBestScore = float(sys.maxsize) # Smallest area so far
def printTilingStats(): def printTilingStats():
global _CkpointTime global _CkpointTime
@ -38,11 +38,11 @@ def printTilingStats():
# add metric support (1/1000 mm vs. 1/100,000 inch) # add metric support (1/1000 mm vs. 1/100,000 inch)
if config.Config['measurementunits'] == 'inch': if config.Config['measurementunits'] == 'inch':
print "\r %ld placements / Smallest area: %.1f sq. in. / Best utilization: %.1f%%" % \ print("\r %ld placements / Smallest area: %.1f sq. in. / Best utilization: %.1f%%" % \
(_Placements, area, utilization), (_Placements, area, utilization), end=' ')
else: else:
print "\r %ld placements / Smallest area: %.1f sq. mm / Best utilization: %.0f%%" % \ print("\r %ld placements / Smallest area: %.1f sq. mm / Best utilization: %.0f%%" % \
(_Placements, area, utilization), (_Placements, area, utilization), end=' ')
if gerbmerge.GUI is not None: if gerbmerge.GUI is not None:
sys.stdout.flush() sys.stdout.flush()
@ -64,7 +64,7 @@ def _tile_search2(Jobs, X, Y, cfg=config.Config):
# Must escape with Ctrl-C # Must escape with Ctrl-C
while 1: while 1:
T = tiling.Tiling(X,Y) T = tiling.Tiling(X,Y)
joborder = r.sample(range(N), N) joborder = r.sample(list(range(N)), N)
minInletSize = tiling.minDimension(Jobs) minInletSize = tiling.minDimension(Jobs)
@ -127,32 +127,32 @@ def tile_search2(Jobs, X, Y):
_StartTime = time.time() _StartTime = time.time()
_CkpointTime = _StartTime + 3 _CkpointTime = _StartTime + 3
_Placements = 0L _Placements = 0
_TBestTiling = None _TBestTiling = None
_TBestScore = float(sys.maxint) _TBestScore = float(sys.maxsize)
print '='*70 print('='*70)
if (config.Config['searchtimeout'] > 0): if (config.Config['searchtimeout'] > 0):
print "Starting random placement trials. You can press Ctrl-C to" 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("stop the process and use the best placement so far, or wait")
print "for the automatic timeout in %i seconds." % config.Config['searchtimeout'] print("for the automatic timeout in %i seconds." % config.Config['searchtimeout'])
else: else:
print "Starting random placement trials. You must press Ctrl-C to" print("Starting random placement trials. You must press Ctrl-C to")
print "stop the process and use the best placement so far." print("stop the process and use the best placement so far.")
print "You can specify a timeout by setting 'SearchTimeout' in Layout.cfg" print("You can specify a timeout by setting 'SearchTimeout' in Layout.cfg")
print "Estimated maximum possible utilization is %.1f%%." % (tiling.maxUtilization(Jobs)*100) print("Estimated maximum possible utilization is %.1f%%." % (tiling.maxUtilization(Jobs)*100))
try: try:
_tile_search2(Jobs, X, Y) _tile_search2(Jobs, X, Y)
printTilingStats() printTilingStats()
print print()
except KeyboardInterrupt: except KeyboardInterrupt:
printTilingStats() printTilingStats()
print print()
print "Interrupted." print("Interrupted.")
computeTime = time.time() - _StartTime computeTime = time.time() - _StartTime
print "Computed %ld placements in %d seconds / %.1f placements/second" % (_Placements, computeTime, _Placements/computeTime) print("Computed %ld placements in %d seconds / %.1f placements/second" % (_Placements, computeTime, _Placements/computeTime))
print '='*70 print('='*70)
return _TBestTiling return _TBestTiling

View File

@ -319,7 +319,7 @@ mirrored-L corner __ | |
def bounds(self): def bounds(self):
"""Return 2-tuple ((minX, minY), (maxX, maxY)) of rectangular region defined by all jobs""" """Return 2-tuple ((minX, minY), (maxX, maxY)) of rectangular region defined by all jobs"""
minX = minY = float(sys.maxint) minX = minY = float(sys.maxsize)
maxX = maxY = 0.0 maxX = maxY = 0.0
for bl,tr,job in self.jobs: for bl,tr,job in self.jobs:
@ -368,7 +368,7 @@ def maxUtilization(Jobs):
# Utility function to compute the minimum dimension along any axis of all jobs. # Utility function to compute the minimum dimension along any axis of all jobs.
# Used to remove inlets. # Used to remove inlets.
def minDimension(Jobs): def minDimension(Jobs):
M = float(sys.maxint) M = float(sys.maxsize)
for Xdim,Ydim,job,rjob in Jobs: for Xdim,Ydim,job,rjob in Jobs:
M = min(M,Xdim) M = min(M,Xdim)
M = min(M,Ydim) M = min(M,Ydim)