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
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
# type 4 which is an outline and has a variable number of points.
@ -126,18 +126,18 @@ class ApertureMacroPrimitive:
# - last field is rotation
if self.code==4:
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:
N = int(fields[1])
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):
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:
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
# list, using either int() or float() conversion.
@ -150,7 +150,7 @@ class ApertureMacroPrimitive:
try:
self.parms.append(converter(fields[parmix]))
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):
# Account for DOS line endings and get rid of line ending and '*' at the end
@ -164,12 +164,12 @@ class ApertureMacroPrimitive:
try:
code = int(fields[0])
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:])
except:
print '='*20
print "==> ", line
print '='*20
print('='*20)
print("==> ", line)
print('='*20)
raise
def rotate(self, flip):
@ -283,7 +283,7 @@ def parseApertureMacro(s, fid):
M.add(P)
else:
raise RuntimeError, "Premature end-of-file while parsing aperture macro"
raise RuntimeError("Premature end-of-file while parsing aperture macro")
else:
return None
@ -295,7 +295,7 @@ def addToApertureMacroTable(AM):
# Must sort keys by integer value, not string since 99 comes before 100
# 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()
if len(keys):
@ -338,25 +338,23 @@ if __name__=="__main__":
MR = M.rotated(0)
# Generate the Gerber so we can view it
fid = file('amacro.ger', 'wt')
print >> fid, \
"""G75*
fid = open('amacro.ger', 'wt')
print("""G75*
G70*
%OFA0B0*%
%FSLAX24Y24*%
%IPPOS*%
%LPD*%"""
%LPD*%""", file=fid)
M.writeDef(fid)
MR.writeDef(fid)
print >> fid, \
"""%ADD10TEST*%
print("""%ADD10TEST*%
%ADD11TESTR*%
D10*
X010000Y010000D03*
D11*
X015000Y010000D03*
M02*"""
M02*""", file=fid)
fid.close()
print M
print MR
print(M)
print(MR)

View File

@ -168,17 +168,17 @@ def parseAperture(s, knownMacroNames):
code, dimx, dimy = match.groups()
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')
else:
raise RuntimeError, 'Aperture Macro name "%s" not defined' % dimx
raise RuntimeError('Aperture Macro name "%s" not defined' % dimx)
else:
try:
dimx = float(dimx)
if dimy:
dimy = float(dimy)
except:
raise RuntimeError, "Illegal floating point aperture size"
raise RuntimeError("Illegal floating point aperture size")
return Aperture(ap, code, dimx, dimy)
@ -221,7 +221,7 @@ def constructApertureTable(fileList):
# [andreika]: units conversion
units_div = 1.0
fid = file(fname,'rt')
fid = open(fname,'rt')
for line in fid:
# Get rid of CR
line = line.replace('\x0D', '')
@ -276,19 +276,19 @@ def constructApertureTable(fileList):
# Now, go through and assign sequential codes to all apertures
code = 10
for val in AT.values():
for val in list(AT.values()):
key = 'D%d' % code
GAT[key] = val
val.code = key
code += 1
if 0:
keylist = config.GAT.keys()
keylist = list(config.GAT.keys())
keylist.sort()
print 'Apertures'
print '========='
print('Apertures')
print('=========')
for key in keylist:
print '%s' % config.GAT[key]
print('%s' % config.GAT[key])
sys.exit(0)
def findHighestApertureCode(keys):
@ -304,7 +304,7 @@ def findHighestApertureCode(keys):
def addToApertureTable(AP):
GAT = config.GAT
lastCode = findHighestApertureCode(GAT.keys())
lastCode = findHighestApertureCode(list(GAT.keys()))
code = 'D%d' % (lastCode+1)
GAT[code] = AP
AP.code = code
@ -315,7 +315,7 @@ def findInApertureTable(AP):
"""Return 'D10', for example in response to query for an object
of type Aperture()"""
hash = AP.hash()
for key, val in config.GAT.items():
for key, val in list(config.GAT.items()):
if hash==val.hash():
return key
@ -335,16 +335,16 @@ def findOrAddAperture(AP):
if __name__=="__main__":
constructApertureTable(sys.argv[1:])
keylist = config.GAMT.keys()
keylist = list(config.GAMT.keys())
keylist.sort()
print 'Aperture Macros'
print '==============='
print('Aperture Macros')
print('===============')
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()
print 'Apertures'
print '========='
print('Apertures')
print('=========')
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 ConfigParser
import configparser
import re
import string
@ -116,7 +116,7 @@ MinimumFeatureDimension = {}
# hash string. The value is the aperture code (e.g., 'D10') or macro name (e.g., 'M5').
def buildRevDict(D):
RevD = {}
for key,val in D.items():
for key,val in list(D.items()):
RevD[val.hash()] = key
return RevD
@ -127,12 +127,12 @@ def parseStringList(L):
if 0:
if L[0]=="'":
if L[-1] != "'":
raise RuntimeError, "Illegal configuration string '%s'" % L
raise RuntimeError("Illegal configuration string '%s'" % L)
L = L[1:-1]
elif L[0]=='"':
if L[-1] != '"':
raise RuntimeError, "Illegal configuration string '%s'" % L
raise RuntimeError("Illegal configuration string '%s'" % L)
L = L[1:-1]
# This pattern matches quotes at the beginning and end...quotes must match
@ -153,14 +153,14 @@ def parseToolList(fname):
TL = {}
try:
fid = file(fname, 'rt')
except Exception, detail:
raise RuntimeError, "Unable to open tool list file '%s':\n %s" % (fname, str(detail))
fid = open(fname, 'rt')
except Exception as 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_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)?')
for line in fid.xreadlines():
for line in fid:
line = string.strip(line)
if (not line) or (line[0] in ('#', ';')): continue
@ -182,7 +182,7 @@ def parseToolList(fname):
try:
size = float(size)
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:
size = size*0.001 # Convert mil to inches
@ -192,8 +192,8 @@ def parseToolList(fname):
# Canonicalize tool so that T1 becomes T01
tool = 'T%02d' % int(tool[1:])
if TL.has_key(tool):
raise RuntimeError, "Tool '%s' defined more than once in tool list file '%s'" % (tool,fname)
if tool in TL:
raise RuntimeError("Tool '%s' defined more than once in tool list file '%s'" % (tool,fname))
TL[tool]=size
fid.close()
@ -215,38 +215,38 @@ def parseToolList(fname):
def parseConfigFile(fname, Config=Config, Jobs=Jobs):
global DefaultToolList
CP = ConfigParser.ConfigParser()
CP.readfp(file(fname.rstrip(),'rt'))
CP = configparser.ConfigParser()
CP.readfp(open(fname.rstrip(),'rt'))
# First parse global options
if CP.has_section('Options'):
for opt in CP.options('Options'):
# Is it one we expect
if Config.has_key(opt):
if opt in Config:
# Yup...override it
Config[opt] = CP.get('Options', opt)
elif CP.defaults().has_key(opt):
elif opt in CP.defaults():
pass # Ignore DEFAULTS section keys
elif opt in ('fabricationdrawing', 'outlinelayer'):
print '*'*73
print '\nThe FabricationDrawing and OutlineLayer configuration options have been'
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 '*'*73
print('*'*73)
print('\nThe FabricationDrawing and OutlineLayer configuration options have been')
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('*'*73)
sys.exit(1)
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:
raise RuntimeError, "Missing [Options] section in configuration file"
raise RuntimeError("Missing [Options] section in configuration file")
# Ensure we got a tool list
if not Config.has_key('toollist'):
raise RuntimeError, "INTERNAL ERROR: Missing tool list assignment in [Options] section"
if 'toollist' not in Config:
raise RuntimeError("INTERNAL ERROR: Missing tool list assignment in [Options] section")
# Make integers integers, floats floats
for key,val in Config.items():
for key,val in list(Config.items()):
try:
val = int(val)
Config[key]=val
@ -283,7 +283,7 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs):
for index in range(0, len(temp), 2):
MinimumFeatureDimension[ temp[index] ] = float( temp[index + 1] )
except:
raise RuntimeError, "Illegal configuration string:" + Config['minimumfeaturesize']
raise RuntimeError("Illegal configuration string:" + Config['minimumfeaturesize'])
# Process MergeOutputFiles section to set output file names
if CP.has_section('MergeOutputFiles'):
@ -303,10 +303,10 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs):
# Ensure all jobs have a board outline
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'):
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):
if layername[0]=='*' or layername=='boardoutline':
@ -323,10 +323,10 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs):
del apfiles
if 0:
keylist = GAMT.keys()
keylist = list(GAMT.keys())
keylist.sort()
for key in keylist:
print '%s' % GAMT[key]
print('%s' % GAMT[key])
sys.exit(0)
# Parse the tool list
@ -350,8 +350,8 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs):
if jobname=='MergeOutputFiles': continue
if jobname=='GerbMergeGUI': continue
print '' # empty line before hand for readability
print 'Reading data from', jobname, '...'
print('') # empty line before hand for readability
print('Reading data from', jobname, '...')
J = jobs.Job(jobname)
@ -369,12 +369,12 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs):
try:
J.ExcellonDecimals = int(fname)
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':
try:
J.Repeat = int(fname)
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):
fname = CP.get(jobname, layername)
@ -388,34 +388,34 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs):
# Emit warnings if some layers are missing
LL = LayerList.copy()
for layername in J.apxlat.keys():
assert LL.has_key(layername)
for layername in list(J.apxlat.keys()):
assert layername in LL
del LL[layername]
if LL:
if errstr=='ERROR':
do_abort=1
print '%s: Job %s is missing the following layers:' % (errstr, jobname)
for layername in LL.keys():
print ' %s' % layername
print('%s: Job %s is missing the following layers:' % (errstr, jobname))
for layername in list(LL.keys()):
print(' %s' % layername)
# Store the job in the global Jobs dictionary, keyed by job name
Jobs[jobname] = J
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__":
CP = parseConfigFile(sys.argv[1])
print Config
print(Config)
sys.exit(0)
if 0:
for key, val in CP.defaults().items():
print '%s: %s' % (key,val)
for key, val in list(CP.defaults().items()):
print('%s: %s' % (key,val))
for section in CP.sections():
print '[%s]' % section
print('[%s]' % 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)
# Loop through all drill sizes
sizes = drills.keys()
sizes = list(drills.keys())
sizes.sort()
for size in sizes:
@ -106,7 +106,7 @@ def remap(jobs, globalToolMap, debug = _DEBUG):
debug_print( str(job.xcommands) )
new_tools = {}
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) + ")")
@ -147,9 +147,9 @@ def debug_print(text, status = False, newLine = True):
if _DEBUG or (status and _STATUS):
if newLine:
print " ", text
print(" ", text)
else:
print " ", text,
print(" ", text, end=' ')
def str_d(drills):
"""
@ -180,7 +180,7 @@ def drillsToString(drills):
"""
string = ""
drills = drills.items()
drills = list(drills.items())
drills.sort()
for size, drill in drills:
string += drill + " = " + str_d(size) + "\n "
@ -194,7 +194,7 @@ def drillsToString(drills):
if __name__=="__main__":
import random
print " Clustering random drills..."
print(" Clustering random drills...")
old = {}
tool_num = 0

View File

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

View File

@ -15,7 +15,7 @@ import math
# Ensure all list elements are unique
def uniqueify(L):
return {}.fromkeys(L).keys()
return list({}.fromkeys(L).keys())
# This function rounds an (X,Y) point to integer co-ordinates
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,0,500,500) ) == True
print 'All tests pass'
print('All tests pass')

View File

@ -60,8 +60,7 @@ config.PlacementFile = None
GUI = None
def usage():
print \
"""
print("""
Usage: gerbmerge [Options] configfile [layoutfile]
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
the board outline layer for each job.
"""
""")
sys.exit(1)
# changed these two writeGerberHeader files to take metric units (mm) into account:
@ -146,14 +145,14 @@ G71*
writeGerberHeader = writeGerberHeader22degrees
def writeApertureMacros(fid, usedDict):
keys = config.GAMT.keys()
keys = list(config.GAMT.keys())
keys.sort()
for key in keys:
if key in usedDict:
config.GAMT[key].writeDef(fid)
def writeApertures(fid, usedDict):
keys = config.GAT.keys()
keys = list(config.GAT.keys())
keys.sort()
for key in keys:
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)))
def disclaimer():
print """
print("""
****************************************************
* 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.
Any other key will exit the program.
"""
""")
s = raw_input()
s = input()
if s == 'y':
print
print()
return
print "\nExiting..."
print("\nExiting...")
sys.exit(0)
def tile_jobs(Jobs):
@ -318,9 +317,9 @@ def tile_jobs(Jobs):
if not tile:
# add metric support (1/1000 mm vs. 1/100,000 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:
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
@ -339,7 +338,7 @@ def merge(opts, args, gui = None):
elif arg=='normal':
writeGerberHeader = writeGerberHeader22degrees
else:
raise RuntimeError, 'Unknown octagon format'
raise RuntimeError('Unknown octagon format')
elif opt in ('--random-search',):
config.AutoSearchType = RANDOM_SEARCH
elif opt in ('--full-search',):
@ -358,10 +357,10 @@ def merge(opts, args, gui = None):
elif opt in ('-s', '--skipdisclaimer'):
skipDisclaimer = 1
else:
raise RuntimeError, "Unknown option: %s" % opt
raise RuntimeError("Unknown option: %s" % opt)
if len(args) > 2 or len(args) < 1:
raise RuntimeError, 'Invalid number of arguments'
raise RuntimeError('Invalid number of arguments')
if (skipDisclaimer == 0):
disclaimer()
@ -372,7 +371,7 @@ def merge(opts, args, gui = None):
config.parseConfigFile(args[0])
# 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()
shift_x = shift_y = 0
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 )
# Display job properties
for job in config.Jobs.values():
print 'Job %s:' % job.name,
for job in list(config.Jobs.values()):
print('Job %s:' % job.name, end=' ')
if job.Repeat > 1:
print '(%d instances)' % job.Repeat
print('(%d instances)' % job.Repeat)
else:
print
print ' Extents: (%d,%d)-(%d,%d)' % (job.minx,job.miny,job.maxx,job.maxy)
print()
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)
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:
print ' Size: %5.3fmm x %5.3fmm' % (job.width_in(), job.height_in())
print
print(' Size: %5.3fmm x %5.3fmm' % (job.width_in(), job.height_in()))
print()
# Trim drill locations and flash data to board extents
if config.TrimExcellon:
updateGUI("Trimming Excellon data...")
print 'Trimming Excellon data to board outlines ...'
for job in config.Jobs.values():
print('Trimming Excellon data to board outlines ...')
for job in list(config.Jobs.values()):
job.trimExcellon()
if config.TrimGerber:
updateGUI("Trimming Gerber data...")
print 'Trimming Gerber data to board outlines ...'
for job in config.Jobs.values():
print('Trimming Gerber data to board outlines ...')
for job in list(config.Jobs.values()):
job.trimGerber()
# 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
# is no layout file, do auto-layout.
updateGUI("Performing layout...")
print 'Performing layout ...'
print('Performing layout ...')
if len(args) > 1:
Layout = parselayout.parseLayoutFile(args[1])
@ -439,7 +438,7 @@ def merge(opts, args, gui = None):
Place.addFromFile(config.PlacementFile, config.Jobs)
else:
# 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.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)
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
if lname[0]=='*':
lname = lname[1:]
@ -504,7 +503,7 @@ def merge(opts, args, gui = None):
fullname = 'merged.%s.ger' % lname
OutputFiles.append(fullname)
#print 'Writing %s ...' % fullname
fid = file(fullname, 'wt')
fid = open(fullname, 'wt')
writeGerberHeader(fid)
# Determine which apertures and macros are truly needed
@ -516,12 +515,12 @@ def merge(opts, args, gui = None):
apmUsedDict.update(apmd)
# 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
for ap in apUsedDict.keys():
for ap in list(apUsedDict.keys()):
new = config.GAT[ap].getAdjusted( config.MinimumFeatureDimension[layername] )
if not new: ## current aperture size met minimum requirement
continue
@ -595,7 +594,7 @@ def merge(opts, args, gui = None):
if fullname and fullname.lower() != "none":
OutputFiles.append(fullname)
#print 'Writing %s ...' % fullname
fid = file(fullname, 'wt')
fid = open(fullname, 'wt')
writeGerberHeader(fid)
# Write width-1 aperture to file
@ -624,7 +623,7 @@ def merge(opts, args, gui = None):
if fullname and fullname.lower() != "none":
OutputFiles.append(fullname)
#print 'Writing %s ...' % fullname
fid = file(fullname, 'wt')
fid = open(fullname, 'wt')
writeGerberHeader(fid)
# Write width-1 aperture to file
@ -644,19 +643,19 @@ def merge(opts, args, gui = None):
# of tools.
if 0:
Tools = {}
for job in config.Jobs.values():
for key in job.xcommands.keys():
for job in list(config.Jobs.values()):
for key in list(job.xcommands.keys()):
Tools[key] = 1
Tools = Tools.keys()
Tools = list(Tools.keys())
Tools.sort()
else:
toolNum = 0
# First construct global mapping of diameters to tool numbers
for job in config.Jobs.values():
for tool,diam in job.xdiam.items():
if config.GlobalToolRMap.has_key(diam):
for job in list(config.Jobs.values()):
for tool,diam in list(job.xdiam.items()):
if diam in config.GlobalToolRMap:
continue
toolNum += 1
@ -665,24 +664,24 @@ def merge(opts, args, gui = None):
# Cluster similar tool sizes to reduce number of drills
if config.Config['drillclustertolerance'] > 0:
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
for diam,tool in config.GlobalToolRMap.items():
for diam,tool in list(config.GlobalToolRMap.items()):
config.GlobalToolMap[tool] = diam
# Tools is just a list of tool names
Tools = config.GlobalToolMap.keys()
Tools = list(config.GlobalToolMap.keys())
Tools.sort()
fullname = config.Config['fabricationdrawingfile']
if fullname and fullname.lower() != 'none':
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)
#print 'Writing %s ...' % fullname
fid = file(fullname, 'wt')
fid = open(fullname, 'wt')
writeGerberHeader(fid)
writeApertures(fid, {drawing_code1: None})
fid.write('%s*\n' % drawing_code1) # Choose drawing aperture
@ -699,7 +698,7 @@ def merge(opts, args, gui = None):
fullname = 'merged.drills.xln'
OutputFiles.append(fullname)
#print 'Writing %s ...' % fullname
fid = file(fullname, 'wt')
fid = open(fullname, 'wt')
writeExcellonHeader(fid)
@ -709,7 +708,7 @@ def merge(opts, args, gui = None):
try:
size = config.GlobalToolMap[tool]
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)
writeExcellonHeaderEnd(fid)
@ -755,57 +754,57 @@ def merge(opts, args, gui = None):
fullname = 'merged.toollist.drl'
OutputFiles.append(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)
if config.Config['measurementunits'] == 'inch':
print ' Job Size : %f" x %f"' % (MaxXExtent-OriginX, MaxYExtent-OriginY)
print ' Job Area : %.2f sq. in.' % totalarea
print(' Job Size : %f" x %f"' % (MaxXExtent-OriginX, MaxYExtent-OriginY))
print(' Job Area : %.2f sq. in.' % totalarea)
else:
print ' Job Size : %.2fmm x %.2fmm' % (MaxXExtent-OriginX, MaxYExtent-OriginY)
print ' Job Area : %.0f mm2' % totalarea
print(' Job Size : %.2fmm x %.2fmm' % (MaxXExtent-OriginX, MaxYExtent-OriginY))
print(' Job Area : %.0f mm2' % totalarea)
print ' Area Usage : %.1f%%' % (jobarea/totalarea*100)
print ' Drill hits : %d' % drillhits
print(' Area Usage : %.1f%%' % (jobarea/totalarea*100))
print(' Drill hits : %d' % drillhits)
if config.Config['measurementunits'] == 'inch':
print 'Drill density : %.1f hits/sq.in.' % (drillhits/totalarea)
print('Drill density : %.1f hits/sq.in.' % (drillhits/totalarea))
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
for tool in Tools:
if ToolStats[tool]:
if config.Config['measurementunits'] == 'inch':
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:
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])
fid.close()
if config.Config['measurementunits'] == 'inch':
print "Smallest Tool: %.4fin" % smallestDrill
print("Smallest Tool: %.4fin" % smallestDrill)
else:
print "Smallest Tool: %.4fmm" % smallestDrill
print("Smallest Tool: %.4fmm" % smallestDrill)
print
print 'Output Files :'
print()
print('Output Files :')
for f in OutputFiles:
print ' ', f
print(' ', f)
if (MaxXExtent-OriginX)>config.Config['panelwidth'] or (MaxYExtent-OriginY)>config.Config['panelheight']:
print '*'*75
print '*'
print('*'*75)
print('*')
# add metric support (1/1000 mm vs. 1/100,000 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:
print '* ERROR: Merged job exceeds panel dimensions of %.1fmmx%.1fmm' % (config.Config['panelwidth'],config.Config['panelheight'])
print '*'
print '*'*75
print('* ERROR: Merged job exceeds panel dimensions of %.1fmmx%.1fmm' % (config.Config['panelwidth'],config.Config['panelheight']))
print('*')
print('*'*75)
sys.exit(1)
# Done!
@ -826,19 +825,19 @@ def main():
if opt in ('-h', '--help'):
usage()
elif opt in ('-v', '--version'):
print """
print("""
GerbMerge Version %s -- Combine multiple Gerber/Excellon files
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.
ProvideYourOwn - http://provideyourown.com
""" % (__version__)
""" % (__version__))
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'):
pass ## arguments are valid
else:
raise RuntimeError, "Unknown option: %s" % opt
raise RuntimeError("Unknown option: %s" % opt)
if len(args) > 2 or len(args) < 1:
usage()

View File

@ -18,7 +18,7 @@ http://github.com/unwireddevices/gerbmerge
import sys
import re
import string
import __builtin__
import builtins
import copy
import types
@ -235,17 +235,17 @@ class Job:
self.maxy += y_shift
# Shift all commands
for layer, command in self.commands.iteritems():
for layer, command in self.commands.items():
# Loop through each command in each layer
for index in range( len(command) ):
c = command[index]
# 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
if (type( command_list[0] ) == types.IntType) \
and (type( command_list[1] ) == types.IntType): ## ensure that first two elemenst are integers
if (type( command_list[0] ) == int) \
and (type( command_list[1] ) == int): ## ensure that first two elemenst are integers
command_list[0] += x_shift
command_list[1] += y_shift
command[index] = tuple(command_list) ## convert list back to tuple
@ -253,7 +253,7 @@ class Job:
self.commands[layer] = command ## set modified command
# 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
for index in range( len(command) ):
@ -261,12 +261,12 @@ class Job:
# Shift X and Y coordinate of command
command_list = list(c) ## convert tuple to list
if ( type( command_list[0] ) == types.IntType ) \
and ( type( command_list[1] ) == types.IntType ): ## ensure that first two elemenst are integers
if ( type( command_list[0] ) == int ) \
and ( type( command_list[1] ) == int ): ## ensure that first two elemenst are integers
command_list[0] += x_shift / 10
command_list[1] += y_shift / 10
if ( type( command_list[2] ) == types.IntType ) \
and ( type( command_list[3] ) == types.IntType ): ## ensure that first two elemenst are integerslen(command_list) == 4:
if ( type( command_list[2] ) == int ) \
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.
command_list[2] += x_shift / 10
command_list[3] += y_shift / 10
@ -286,7 +286,7 @@ class Job:
#print 'Reading data from %s ...' % fullname
fid = file(fullname, 'rt')
fid = open(fullname, 'rt')
currtool = None
self.apxlat[layername] = {}
@ -338,7 +338,7 @@ class Job:
for line in fid:
# 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.
@ -354,11 +354,11 @@ class Job:
match = apdef_pat.match(line)
if match:
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])
if not A:
raise RuntimeError, "Unknown aperture definition in file %s" % fullname
raise RuntimeError("Unknown aperture definition in file %s" % fullname)
# [andreika]: apply units
if type(A.dimx) == float or type(A.dimx) == int:
A.dimx *= units_div
@ -366,11 +366,11 @@ class Job:
A.dimy *= units_div
hash = A.hash()
if not RevGAT.has_key(hash):
if hash not in RevGAT:
#print line
#print self.apmxlat
#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
# be replaced by aperture self.apxlat[layername][code].
@ -402,7 +402,7 @@ class Job:
continue # ignore it so func doesn't choke on it
if line[:3] == '%SF': # scale factor - we will ignore it
print 'Scale factor parameter ignored: ' + line
print('Scale factor parameter ignored: ' + line)
continue
# end basic diptrace fixes
@ -411,11 +411,11 @@ class Job:
M = amacro.parseApertureMacro(line,fid)
if M:
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()
if not RevGAMT.has_key(hash):
raise RuntimeError, 'File %s has aperture macro definition not in global aperture macro table:\n%s' % (fullname, hash)
if hash not in RevGAMT:
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
# will be replaced by aperture macro name self.apmxlat[layername][macroname].
@ -445,9 +445,9 @@ class Job:
continue
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
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
continue
@ -508,7 +508,7 @@ class Job:
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
match = tool_pat.match(sub_line)
@ -541,8 +541,8 @@ class Job:
continue
# Map it using our translation table
if not self.apxlat[layername].has_key(currtool):
raise RuntimeError, 'File %s has tool change command "%s" with no corresponding translation' % (fullname, currtool)
if currtool not in self.apxlat[layername]:
raise RuntimeError('File %s has tool change command "%s" with no corresponding translation' % (fullname, currtool))
currtool = self.apxlat[layername][currtool]
@ -561,17 +561,17 @@ class Job:
match = drawXY_pat.match(sub_line)
isLastShorthand = False # By default assume we don't make use of last_x and last_y
if match:
x, y, d = map(__builtin__.int, match.groups())
x, y, d = list(map(builtins.int, match.groups()))
else:
match = drawX_pat.match(sub_line)
if match:
x, d = map(__builtin__.int, match.groups())
x, d = list(map(builtins.int, match.groups()))
y = last_y
isLastShorthand = True # Indicate we're making use of last_x/last_y
else:
match = drawY_pat.match(sub_line)
if match:
y, d = map(__builtin__.int, match.groups())
y, d = list(map(builtins.int, match.groups()))
x = last_x
isLastShorthand = True # Indicate we're making use of last_x/last_y
@ -579,17 +579,17 @@ class Job:
if match is None:
match = cdrawXY_pat.match(sub_line)
if match:
x, y, I, J, d = map(__builtin__.int, match.groups())
x, y, I, J, d = list(map(builtins.int, match.groups()))
else:
match = cdrawX_pat.match(sub_line)
if match:
x, I, J, d = map(__builtin__.int, match.groups())
x, I, J, d = list(map(builtins.int, match.groups()))
y = last_y
isLastShorthand = True # Indicate we're making use of last_x/last_y
else:
match = cdrawY_pat.match(sub_line)
if match:
y, I, J, d = map(__builtin__.int, match.groups())
y, I, J, d = list(map(builtins.int, match.groups()))
x = last_x
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):
# [andreika]: check for fill mode more accurately
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
# flashes (e.g., Y with no X) will be scaled twice!
@ -649,7 +649,7 @@ class Job:
if match:
break
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():]
# end while still things to match on this line
@ -657,13 +657,13 @@ class Job:
fid.close()
if 0:
print layername
print self.commands[layername]
print(layername)
print(self.commands[layername])
def parseExcellon(self, fullname):
#print 'Reading data from %s ...' % fullname
fid = file(fullname, 'rt')
fid = open(fullname, 'rt')
currtool = None
suppress_leading = True # Suppress leading zeros by default, equivalent to 'INCH,TZ'
@ -706,14 +706,14 @@ class Job:
V.append(None)
return tuple(V)
for line in fid.xreadlines():
for line in fid:
# Get rid of CR characters
line = string.replace(line, '\x0D', '')
line = str.replace(line, '\x0D', '')
# add support for DipTrace
if line[:6]=='METRIC':
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:
#print "ignoring METRIC directive: " + line
continue # ignore it so func doesn't choke on it
@ -746,14 +746,14 @@ class Job:
try:
diam = float(diam)
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
# as T01 and sometimes as T1. We canonicalize to T01.
currtool = 'T%02d' % int(currtool[1:])
if self.xdiam.has_key(currtool):
raise RuntimeError, "File %s defines tool %s more than once" % (fullname, currtool)
if currtool in self.xdiam:
raise RuntimeError("File %s defines tool %s more than once" % (fullname, currtool))
self.xdiam[currtool] = diam
continue
@ -779,13 +779,13 @@ class Job:
try:
diam = self.ToolList[currtool]
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:
try:
diam = config.DefaultToolList[currtool]
except:
#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
continue
@ -811,7 +811,7 @@ class Job:
if match:
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:
self.xcommands[currtool].append((x,y,stop_x,stop_y))
@ -827,10 +827,10 @@ class Job:
if pat.match(line):
break
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):
return self.commands.has_key(layername)
return layername in self.commands
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"
@ -858,7 +858,7 @@ class Job:
# due to panelizing.
fid.write('X%07dY%07dD02*\n' % (X, Y))
for cmd in self.commands[layername]:
if type(cmd) is types.TupleType:
if type(cmd) is tuple:
if len(cmd)==3:
x, y, d = cmd
fid.write('X%07dY%07dD%02d*\n' % (x+DX, y+DY, d))
@ -877,7 +877,7 @@ class Job:
def findTools(self, diameter):
"Find the tools, if any, with the given diameter in inches. There may be more than one!"
L = []
for tool, diam in self.xdiam.items():
for tool, diam in list(self.xdiam.items()):
if diam==diameter:
L.append(tool)
return L
@ -927,7 +927,7 @@ class Job:
# Boogie
for ltool in ltools:
if self.xcommands.has_key(ltool):
if ltool in self.xcommands:
for cmd in self.xcommands[ltool]:
x, y, stop_x, stop_y = cmd
new_x = x+DX
@ -965,7 +965,7 @@ class Job:
ltools = self.findTools(diameter)
for ltool in ltools:
if self.xcommands.has_key(ltool):
if ltool in self.xcommands:
for cmd in self.xcommands[ltool]:
x, y, stop_x, stop_y = cmd
# add metric support (1/1000 mm vs. 1/100,000 inch)
@ -979,7 +979,7 @@ class Job:
GAT=config.GAT
if self.apertures.has_key(layername):
if layername in self.apertures:
apdict = {}.fromkeys(self.apertures[layername])
apmlist = [GAT[ap].dimx for ap in self.apertures[layername] if GAT[ap].apname=='Macro']
apmdict = {}.fromkeys(apmlist)
@ -990,8 +990,8 @@ class Job:
def makeLocalApertureCode(self, layername, AP):
"Find or create a layer-specific aperture code to represent the global aperture given"
if AP.code not in self.apxlat[layername].values():
lastCode = aptable.findHighestApertureCode(self.apxlat[layername].keys())
if AP.code not in list(self.apxlat[layername].values()):
lastCode = aptable.findHighestApertureCode(list(self.apxlat[layername].keys()))
localCode = 'D%d' % (lastCode+1)
self.apxlat[layername][localCode] = AP.code
@ -1008,7 +1008,7 @@ class Job:
lastAperture = None
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.
if len(cmd)==3:
x, y, d = cmd
@ -1180,12 +1180,12 @@ class Job:
self.commands[layername] = newcmds
def trimGerber(self):
for layername in self.commands.keys():
for layername in list(self.commands.keys()):
self.trimGerberLayer(layername)
def trimExcellon(self):
"Remove plunge commands that are outside job dimensions"
keys = self.xcommands.keys()
keys = list(self.xcommands.keys())
for toolname in keys:
# Remember Excellon is 2.4 format while Gerber data is 2.5 format
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.
ToolChangeReplace = {}
for layername in job.apxlat.keys():
for layername in list(job.apxlat.keys()):
J.apxlat[layername] = {}
for ap in job.apxlat[layername].keys():
for ap in list(job.apxlat[layername].keys()):
code = job.apxlat[layername][ap]
A = GAT[code]
@ -1438,18 +1438,18 @@ def rotateJob(job, degrees = 90, flip = 0, firstpass = True):
offset = 0
else:
offset = job.maxy-job.miny
for layername in job.commands.keys():
for layername in list(job.commands.keys()):
J.commands[layername] = []
J.apertures[layername] = []
for cmd in job.commands[layername]:
# Is it a drawing command?
if type(cmd) is types.TupleType:
if type(cmd) is tuple:
if len(cmd)==3:
x, y, d = map(__builtin__.int, cmd)
x, y, d = list(map(builtins.int, cmd))
II=JJ=None
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:
# No, must be a string indicating aperture change, G-code, or RS274-X command.
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))
if 0:
print job.minx, job.miny, offset
print layername
print J.commands[layername]
print(job.minx, job.miny, offset)
print(layername)
print(J.commands[layername])
# Finally, rotate drills. Offset is in hundred-thousandths (2.5) while Excellon
# data is in 2.4 format.
for tool in job.xcommands.keys():
for tool in list(job.xcommands.keys()):
J.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:
glyph = strokes.StrokeMap[c]
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)
@ -140,7 +140,7 @@ if __name__=="__main__":
s = string.digits+string.letters+string.punctuation
#s = "The quick brown fox jumped over the lazy dog!"
fid = file('test.ger','wt')
fid = open('test.ger','wt')
fid.write("""G75*
G70*
%OFA0B0*%

View File

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

View File

@ -52,11 +52,11 @@ class Placement:
def write(self, fname):
"""Write placement to a file"""
fid = file(fname, 'wt')
fid = open(fname, 'wt')
for job in self.jobs:
fid.write('%s %.3f %.3f\n' % (job.job.name, job.x, job.y))
# 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()
def addFromFile(self, fname, Jobs):
@ -65,9 +65,9 @@ class Placement:
comment = re.compile(r'\s*(?:#.+)?$')
try:
fid = file(fname, 'rt')
fid = open(fname, 'rt')
except:
print 'Unable to open placement file: "%s"' % fname
print('Unable to open placement file: "%s"' % fname)
sys.exit(1)
lines = fid.readlines()
@ -78,7 +78,7 @@ class Placement:
match = pat.match(line)
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)
jobname, X, Y = match.groups()
@ -86,7 +86,7 @@ class Placement:
X = float(X)
Y = float(Y)
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)
# rotated or flipped

View File

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

View File

@ -177,7 +177,7 @@ def mergeLines(Lines):
# Extend horizontal lines
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
# lines with this Y ordinate.
NewHLines[yval] = []
@ -199,7 +199,7 @@ def mergeLines(Lines):
# Extend vertical lines
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
# lines with this X ordinate.
NewVLines[xval] = []
@ -228,7 +228,7 @@ def mergeLines(Lines):
# 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
# to each other.
yvals = HLines.keys()
yvals = list(HLines.keys())
clusters = clusterOrdinates(yvals) # A list of clustered tuples containing yvals
for cluster in clusters:
@ -240,7 +240,7 @@ def mergeLines(Lines):
# Y ordinate. Merge them together.
NewHLines.extend(mergeHLines(clusterLines))
xvals = VLines.keys()
xvals = list(VLines.keys())
clusters = clusterOrdinates(xvals)
for cluster in clusters:
clusterLines = []

View File

@ -19,11 +19,11 @@ import gerbmerge
_StartTime = 0.0 # Start time of tiling
_CkpointTime = 0.0 # Next time to print stats
_Placements = 0L # Number of placements attempted
_PossiblePermutations = 0L # Number of different ways of ordering jobs
_Permutations = 0L # Number of different job orderings already computed
_Placements = 0 # Number of placements attempted
_PossiblePermutations = 0 # Number of different ways of ordering jobs
_Permutations = 0 # Number of different job orderings already computed
_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
def printTilingStats():
@ -42,11 +42,11 @@ def printTilingStats():
# add metric support (1/1000 mm vs. 1/100,000 inch)
if config.Config['measurementunits'] == 'inch':
print "\r %5.2f%% complete / %ld/%ld Perm/Place / Smallest area: %.1f sq. in. / Best utilization: %.1f%%" % \
(percent, _Permutations, _Placements, area, utilization),
print("\r %5.2f%% complete / %ld/%ld Perm/Place / Smallest area: %.1f sq. in. / Best utilization: %.1f%%" % \
(percent, _Permutations, _Placements, area, utilization), end=' ')
else:
print "\r %5.2f%% complete / %ld/%ld Perm/Place / Smallest area: %.1f sq. mm / Best utilization: %.1f%%" % \
(percent, _Permutations, _Placements, area, utilization),
print("\r %5.2f%% complete / %ld/%ld Perm/Place / Smallest area: %.1f sq. mm / Best utilization: %.1f%%" % \
(percent, _Permutations, _Placements, area, utilization), end=' ')
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
if not TSoFar:
return (None, float(sys.maxint))
return (None, float(sys.maxsize))
if not Jobs:
# 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:]
if 0:
print "Level %d (%s)" % (level, job.name)
print("Level %d (%s)" % (level, job.name))
TSoFar.joblist()
for J in remaining_jobs:
print J[2].name, ", ",
print
print '-'*75
print(J[2].name, ", ", end=' ')
print()
print('-'*75)
# Construct add-points for the non-rotated and rotated job.
# 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
# have pruned off 2^M permutations where M is the length of the remaining
# jobs.
_Permutations += 2L**len(remaining_jobs)
_Permutations += 2**len(remaining_jobs)
if 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
# have pruned off 2^M permutations where M is the length of the remaining
# 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 _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
def factorial(N):
if (N <= 1): return 1L
if (N <= 1): return 1
prod = long(N)
prod = int(N)
while (N > 2):
N -= 1
prod *= N
@ -198,10 +198,10 @@ def initialize(printStats=1):
global _StartTime, _CkpointTime, _Placements, _TBestTiling, _TBestScore, _Permutations, _PossiblePermutations, _PrintStats
_PrintStats = printStats
_Placements = 0L
_Permutations = 0L
_Placements = 0
_Permutations = 0
_TBestTiling = None
_TBestScore = float(sys.maxint)
_TBestScore = float(sys.maxsize)
def tile_search1(Jobs, X, Y):
"""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
# square). Practically, these assumptions make no difference because the software
# 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 '='*70
print "Starting placement using exhaustive search."
print "There are %ld possible permutations..." % _PossiblePermutations,
print('='*70)
print("Starting placement using exhaustive search.")
print("There are %ld possible permutations..." % _PossiblePermutations, end=' ')
if _PossiblePermutations < 1e4:
print "this'll take no time at all."
print("this'll take no time at all.")
elif _PossiblePermutations < 1e5:
print "surf the web for a few minutes."
print("surf the web for a few minutes.")
elif _PossiblePermutations < 1e6:
print "take a long lunch."
print("take a long lunch.")
elif _PossiblePermutations < 1e7:
print "come back tomorrow."
print("come back tomorrow.")
else:
print "don't hold your breath."
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("don't hold your breath.")
print("Press Ctrl-C to stop and use the best placement so far.")
print("Estimated maximum possible utilization is %.1f%%." % (tiling.maxUtilization(Jobs)*100))
try:
_tile_search1(Jobs, tiling.Tiling(X,Y), 1)
printTilingStats()
print
print()
except KeyboardInterrupt:
printTilingStats()
print
print "Interrupted."
print()
print("Interrupted.")
computeTime = time.time() - _StartTime
print "Computed %ld placements in %d seconds / %.1f placements/second" % (_Placements, computeTime, _Placements/computeTime)
print '='*70
print("Computed %ld placements in %d seconds / %.1f placements/second" % (_Placements, computeTime, _Placements/computeTime))
print('='*70)
return _TBestTiling

View File

@ -21,9 +21,9 @@ import gerbmerge
_StartTime = 0.0 # Start time of tiling
_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
_TBestScore = float(sys.maxint) # Smallest area so far
_TBestScore = float(sys.maxsize) # Smallest area so far
def printTilingStats():
global _CkpointTime
@ -38,11 +38,11 @@ def printTilingStats():
# 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),
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),
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()
@ -64,7 +64,7 @@ def _tile_search2(Jobs, X, Y, cfg=config.Config):
# Must escape with Ctrl-C
while 1:
T = tiling.Tiling(X,Y)
joborder = r.sample(range(N), N)
joborder = r.sample(list(range(N)), N)
minInletSize = tiling.minDimension(Jobs)
@ -127,32 +127,32 @@ def tile_search2(Jobs, X, Y):
_StartTime = time.time()
_CkpointTime = _StartTime + 3
_Placements = 0L
_Placements = 0
_TBestTiling = None
_TBestScore = float(sys.maxint)
_TBestScore = float(sys.maxsize)
print '='*70
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']
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)
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
print()
except KeyboardInterrupt:
printTilingStats()
print
print "Interrupted."
print()
print("Interrupted.")
computeTime = time.time() - _StartTime
print "Computed %ld placements in %d seconds / %.1f placements/second" % (_Placements, computeTime, _Placements/computeTime)
print '='*70
print("Computed %ld placements in %d seconds / %.1f placements/second" % (_Placements, computeTime, _Placements/computeTime))
print('='*70)
return _TBestTiling

View File

@ -319,7 +319,7 @@ mirrored-L corner __ | |
def bounds(self):
"""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
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.
# Used to remove inlets.
def minDimension(Jobs):
M = float(sys.maxint)
M = float(sys.maxsize)
for Xdim,Ydim,job,rjob in Jobs:
M = min(M,Xdim)
M = min(M,Ydim)