mirror of https://github.com/rusefi/gerbmerge.git
Added support for metric units & Diptrace specific needs
This commit is contained in:
parent
38d7510bc9
commit
87e6aab7b5
|
@ -22,14 +22,15 @@ import aptable
|
||||||
# Configuration dictionary. Specify floats as strings. Ints can be specified
|
# Configuration dictionary. Specify floats as strings. Ints can be specified
|
||||||
# as ints or strings.
|
# as ints or strings.
|
||||||
Config = {
|
Config = {
|
||||||
'xspacing': '0.125', # Spacing in horizontal direction
|
'measurementunits': 'inch', # Unit system to use: inch or mm
|
||||||
'yspacing': '0.125', # Spacing in vertical direction
|
'xspacing': 0, # Spacing in horizontal direction - default is set in parseConfigFile based on units
|
||||||
|
'yspacing': 0, # Spacing in vertical direction - ditto
|
||||||
'panelwidth': '12.6', # X-Dimension maximum panel size (Olimex)
|
'panelwidth': '12.6', # X-Dimension maximum panel size (Olimex)
|
||||||
'panelheight': '7.8', # Y-Dimension maximum panel size (Olimex)
|
'panelheight': '7.8', # Y-Dimension maximum panel size (Olimex)
|
||||||
'cropmarklayers': None, # e.g., *toplayer,*bottomlayer
|
'cropmarklayers': None, # e.g., *toplayer,*bottomlayer
|
||||||
'cropmarkwidth': '0.01', # Width (inches) of crop lines
|
'cropmarkwidth': 0, #'0.01', # Width (inches) of crop lines
|
||||||
'cutlinelayers': None, # as for cropmarklayers
|
'cutlinelayers': None, # as for cropmarklayers
|
||||||
'cutlinewidth': '0.01', # Width (inches) of cut lines
|
'cutlinewidth': 0, #'0.01', # Width (inches) of cut lines
|
||||||
'minimumfeaturesize': 0, # Minimum dimension for selected layers
|
'minimumfeaturesize': 0, # Minimum dimension for selected layers
|
||||||
'toollist': None, # Name of file containing default tool list
|
'toollist': None, # Name of file containing default tool list
|
||||||
'drillclustertolerance': '.002', # Tolerance for clustering drill sizes
|
'drillclustertolerance': '.002', # Tolerance for clustering drill sizes
|
||||||
|
@ -255,6 +256,19 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs):
|
||||||
if Config['cropmarklayers']:
|
if Config['cropmarklayers']:
|
||||||
Config['cropmarklayers'] = parseStringList(Config['cropmarklayers'])
|
Config['cropmarklayers'] = parseStringList(Config['cropmarklayers'])
|
||||||
|
|
||||||
|
# setup default x & y spacing, taking into account metric units
|
||||||
|
# if (xspacing == 0):
|
||||||
|
# if (Config['measurementunits'] == 'inch'):
|
||||||
|
# xspacing = 0.125
|
||||||
|
# else:
|
||||||
|
# xspacing = 3
|
||||||
|
|
||||||
|
# if (yspacing == 0):
|
||||||
|
# if (Config['measurementunits'] == 'inch'):
|
||||||
|
# yspacing = 0.125
|
||||||
|
# else:
|
||||||
|
# yspacing = 3
|
||||||
|
|
||||||
# Process list of minimum feature dimensions
|
# Process list of minimum feature dimensions
|
||||||
if Config['minimumfeaturesize']:
|
if Config['minimumfeaturesize']:
|
||||||
temp = Config['minimumfeaturesize'].split(",")
|
temp = Config['minimumfeaturesize'].split(",")
|
||||||
|
|
|
@ -88,8 +88,11 @@ 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:
|
||||||
|
|
||||||
def writeGerberHeader22degrees(fid):
|
def writeGerberHeader22degrees(fid):
|
||||||
fid.write( \
|
if config.Config['measurementunits'] == 'inch':
|
||||||
|
fid.write( \
|
||||||
"""G75*
|
"""G75*
|
||||||
G70*
|
G70*
|
||||||
%OFA0B0*%
|
%OFA0B0*%
|
||||||
|
@ -100,9 +103,21 @@ G70*
|
||||||
5,1,8,0,0,1.08239X$1,22.5*
|
5,1,8,0,0,1.08239X$1,22.5*
|
||||||
%
|
%
|
||||||
""")
|
""")
|
||||||
|
else: # assume mm - also remove eagleware hack for %AMOC8
|
||||||
|
fid.write( \
|
||||||
|
"""G75*
|
||||||
|
G71*
|
||||||
|
%MOMM*%
|
||||||
|
%OFA0B0*%
|
||||||
|
%FSLAX53Y53*%
|
||||||
|
%IPPOS*%
|
||||||
|
%LPD*%
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
def writeGerberHeader0degrees(fid):
|
def writeGerberHeader0degrees(fid):
|
||||||
fid.write( \
|
if config.Config['measurementunits'] == 'inch':
|
||||||
|
fid.write( \
|
||||||
"""G75*
|
"""G75*
|
||||||
G70*
|
G70*
|
||||||
%OFA0B0*%
|
%OFA0B0*%
|
||||||
|
@ -112,6 +127,16 @@ G70*
|
||||||
%AMOC8*
|
%AMOC8*
|
||||||
5,1,8,0,0,1.08239X$1,0.0*
|
5,1,8,0,0,1.08239X$1,0.0*
|
||||||
%
|
%
|
||||||
|
""")
|
||||||
|
else: # assume mm - also remove eagleware hack for %AMOC8
|
||||||
|
fid.write( \
|
||||||
|
"""G75*
|
||||||
|
G71*
|
||||||
|
%MOMM*%
|
||||||
|
%OFA0B0*%
|
||||||
|
%FSLAX53Y53*%
|
||||||
|
%IPPOS*%
|
||||||
|
%LPD*%
|
||||||
""")
|
""")
|
||||||
|
|
||||||
writeGerberHeader = writeGerberHeader22degrees
|
writeGerberHeader = writeGerberHeader22degrees
|
||||||
|
@ -134,6 +159,11 @@ def writeGerberFooter(fid):
|
||||||
fid.write('M02*\n')
|
fid.write('M02*\n')
|
||||||
|
|
||||||
def writeExcellonHeader(fid):
|
def writeExcellonHeader(fid):
|
||||||
|
if config.Config['measurementunits'] != 'inch': # metric - mm
|
||||||
|
fid.write( \
|
||||||
|
"""M48
|
||||||
|
METRIC,0000.00
|
||||||
|
""")
|
||||||
fid.write('%\n')
|
fid.write('%\n')
|
||||||
|
|
||||||
def writeExcellonFooter(fid):
|
def writeExcellonFooter(fid):
|
||||||
|
@ -169,39 +199,51 @@ def writeCropMarks(fid, drawing_code, OriginX, OriginY, MaxXExtent, MaxYExtent):
|
||||||
# Draw 125mil lines at each corner, with line edge right up against
|
# Draw 125mil lines at each corner, with line edge right up against
|
||||||
# panel border. This means the center of the line is D/2 offset
|
# panel border. This means the center of the line is D/2 offset
|
||||||
# from the panel border, where D is the drawing line diameter.
|
# from the panel border, where D is the drawing line diameter.
|
||||||
|
|
||||||
|
# use 3mm lines for metric
|
||||||
|
|
||||||
fid.write('%s*\n' % drawing_code) # Choose drawing aperture
|
fid.write('%s*\n' % drawing_code) # Choose drawing aperture
|
||||||
|
|
||||||
offset = config.GAT[drawing_code].dimx/2.0
|
offset = config.GAT[drawing_code].dimx/2.0
|
||||||
|
|
||||||
|
# should we be using 'cropmarkwidth' from config.py?
|
||||||
|
if config.Config['measurementunits'] == 'inch':
|
||||||
|
cropW = 0.125 #inch
|
||||||
|
else:
|
||||||
|
cropW = 3 #mm
|
||||||
|
|
||||||
|
|
||||||
# Lower-left
|
# Lower-left
|
||||||
x = OriginX + offset
|
x = OriginX + offset
|
||||||
y = OriginY + offset
|
y = OriginY + offset
|
||||||
fid.write('X%07dY%07dD02*\n' % (util.in2gerb(x+0.125), util.in2gerb(y+0.000)))
|
fid.write('X%07dY%07dD02*\n' % (util.in2gerb(x+cropW), util.in2gerb(y+0.000)))
|
||||||
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+0.000), util.in2gerb(y+0.000)))
|
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+0.000), util.in2gerb(y+0.000)))
|
||||||
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+0.000), util.in2gerb(y+0.125)))
|
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+0.000), util.in2gerb(y+cropW)))
|
||||||
|
|
||||||
# Lower-right
|
# Lower-right
|
||||||
x = MaxXExtent - offset
|
x = MaxXExtent - offset
|
||||||
y = OriginY + offset
|
y = OriginY + offset
|
||||||
fid.write('X%07dY%07dD02*\n' % (util.in2gerb(x+0.000), util.in2gerb(y+0.125)))
|
fid.write('X%07dY%07dD02*\n' % (util.in2gerb(x+0.000), util.in2gerb(y+cropW)))
|
||||||
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+0.000), util.in2gerb(y+0.000)))
|
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+0.000), util.in2gerb(y+0.000)))
|
||||||
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x-0.125), util.in2gerb(y+0.000)))
|
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x-cropW), util.in2gerb(y+0.000)))
|
||||||
|
|
||||||
# Upper-right
|
# Upper-right
|
||||||
x = MaxXExtent - offset
|
x = MaxXExtent - offset
|
||||||
y = MaxYExtent - offset
|
y = MaxYExtent - offset
|
||||||
fid.write('X%07dY%07dD02*\n' % (util.in2gerb(x-0.125), util.in2gerb(y+0.000)))
|
fid.write('X%07dY%07dD02*\n' % (util.in2gerb(x-cropW), util.in2gerb(y+0.000)))
|
||||||
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+0.000), util.in2gerb(y+0.000)))
|
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+0.000), util.in2gerb(y+0.000)))
|
||||||
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+0.000), util.in2gerb(y-0.125)))
|
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+0.000), util.in2gerb(y-cropW)))
|
||||||
|
|
||||||
# Upper-left
|
# Upper-left
|
||||||
x = OriginX + offset
|
x = OriginX + offset
|
||||||
y = MaxYExtent - offset
|
y = MaxYExtent - offset
|
||||||
fid.write('X%07dY%07dD02*\n' % (util.in2gerb(x+0.000), util.in2gerb(y-0.125)))
|
fid.write('X%07dY%07dD02*\n' % (util.in2gerb(x+0.000), util.in2gerb(y-cropW)))
|
||||||
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+0.000), util.in2gerb(y+0.000)))
|
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+0.000), util.in2gerb(y+0.000)))
|
||||||
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+0.125), util.in2gerb(y+0.000)))
|
fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+cropW), util.in2gerb(y+0.000)))
|
||||||
|
|
||||||
def disclaimer():
|
def disclaimer():
|
||||||
|
#return # remove annoying 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 *
|
||||||
|
@ -264,7 +306,11 @@ def tile_jobs(Jobs):
|
||||||
tile = tilesearch1.tile_search1(L, PX, PY)
|
tile = tilesearch1.tile_search1(L, PX, PY)
|
||||||
|
|
||||||
if not tile:
|
if not tile:
|
||||||
raise RuntimeError, 'Panel size %.2f"x%.2f" is too small to hold jobs' % (PX,PY)
|
# 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)
|
||||||
|
else:
|
||||||
|
raise RuntimeError, 'Panel size %.2fmmx%.2fmm is too small to hold jobs' % (PX,PY)
|
||||||
|
|
||||||
return tile
|
return tile
|
||||||
|
|
||||||
|
@ -325,7 +371,11 @@ def merge(opts, args, gui = None):
|
||||||
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)
|
||||||
print ' Size: %f" x %f"' % (job.width_in(), job.height_in())
|
# 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())
|
||||||
|
else:
|
||||||
|
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
|
||||||
|
@ -343,7 +393,8 @@ def merge(opts, args, gui = None):
|
||||||
|
|
||||||
# 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
|
||||||
# which could trip up Excellon leading-0 elimination.
|
# which could trip up Excellon leading-0 elimination.
|
||||||
OriginX = OriginY = 0.1
|
# I don't want to change the origin. If this a code bug, then it should be fixed (SDD)
|
||||||
|
OriginX = OriginY = 0 #0.1
|
||||||
|
|
||||||
# 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.
|
||||||
|
@ -530,7 +581,11 @@ def merge(opts, args, gui = None):
|
||||||
writeGerberHeader(fid)
|
writeGerberHeader(fid)
|
||||||
|
|
||||||
# Write width-1 aperture to file
|
# Write width-1 aperture to file
|
||||||
AP = aptable.Aperture(aptable.Circle, 'D10', 0.001)
|
# add metric support
|
||||||
|
if config.Config['measurementunits'] == 'inch':
|
||||||
|
AP = aptable.Aperture(aptable.Circle, 'D10', 0.001)
|
||||||
|
else:
|
||||||
|
AP = aptable.Aperture(aptable.Circle, 'D10', 0.25) # we'll use 0.25 mm - same as Diptrace
|
||||||
AP.writeDef(fid)
|
AP.writeDef(fid)
|
||||||
|
|
||||||
# Choose drawing aperture D10
|
# Choose drawing aperture D10
|
||||||
|
@ -681,22 +736,38 @@ def merge(opts, args, gui = None):
|
||||||
fid = file(fullname, 'wt')
|
fid = file(fullname, 'wt')
|
||||||
|
|
||||||
print '-'*50
|
print '-'*50
|
||||||
print ' Job Size : %f" x %f"' % (MaxXExtent-OriginX, MaxYExtent-OriginY)
|
# add metric support (1/1000 mm vs. 1/100,000 inch)
|
||||||
print ' Job Area : %.2f sq. in.' % totalarea
|
if config.Config['measurementunits'] == 'inch':
|
||||||
|
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 ' Area Usage : %.1f%%' % (jobarea/totalarea*100)
|
print ' Area Usage : %.1f%%' % (jobarea/totalarea*100)
|
||||||
print ' Drill hits : %d' % drillhits
|
print ' Drill hits : %d' % drillhits
|
||||||
print 'Drill density : %.1f hits/sq.in.' % (drillhits/totalarea)
|
if config.Config['measurementunits'] == 'inch':
|
||||||
|
print 'Drill density : %.1f hits/sq.in.' % (drillhits/totalarea)
|
||||||
|
else:
|
||||||
|
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]:
|
||||||
fid.write('%s %.4fin\n' % (tool, config.GlobalToolMap[tool]))
|
if config.Config['measurementunits'] == 'inch':
|
||||||
print ' %s %.4f" %5d hits' % (tool, config.GlobalToolMap[tool], ToolStats[tool])
|
fid.write('%s %.4fin\n' % (tool, config.GlobalToolMap[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])
|
||||||
smallestDrill = min(smallestDrill, config.GlobalToolMap[tool])
|
smallestDrill = min(smallestDrill, config.GlobalToolMap[tool])
|
||||||
|
|
||||||
fid.close()
|
fid.close()
|
||||||
print "Smallest Tool: %.4fin" % smallestDrill
|
if config.Config['measurementunits'] == 'inch':
|
||||||
|
print "Smallest Tool: %.4fin" % smallestDrill
|
||||||
|
else:
|
||||||
|
print "Smallest Tool: %.4fmm" % smallestDrill
|
||||||
|
|
||||||
print
|
print
|
||||||
print 'Output Files :'
|
print 'Output Files :'
|
||||||
|
@ -706,7 +777,11 @@ def merge(opts, args, gui = None):
|
||||||
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 '*'
|
||||||
print '* ERROR: Merged job exceeds panel dimensions of %.1f"x%.1f"' % (config.Config['panelwidth'],config.Config['panelheight'])
|
# 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'])
|
||||||
|
else:
|
||||||
|
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)
|
||||||
|
|
|
@ -36,6 +36,11 @@ import util
|
||||||
# D02 -- move with exposure off
|
# D02 -- move with exposure off
|
||||||
# D03 -- flash aperture
|
# D03 -- flash aperture
|
||||||
|
|
||||||
|
# TODO:
|
||||||
|
#
|
||||||
|
# Need to add error checking for metric/imperial units matching those of the files input
|
||||||
|
# Check fabdrawing.py to see if writeDrillHits is scaling properly (the only place it is used)
|
||||||
|
|
||||||
# Patterns for Gerber RS274X file interpretation
|
# Patterns for Gerber RS274X file interpretation
|
||||||
apdef_pat = re.compile(r'^%AD(D\d+)([^*$]+)\*%$') # Aperture definition
|
apdef_pat = re.compile(r'^%AD(D\d+)([^*$]+)\*%$') # Aperture definition
|
||||||
apmdef_pat = re.compile(r'^%AM([^*$]+)\*$') # Aperture macro definition
|
apmdef_pat = re.compile(r'^%AM([^*$]+)\*$') # Aperture macro definition
|
||||||
|
@ -180,12 +185,21 @@ class Job:
|
||||||
self.ExcellonDecimals = 0 # 0 means global value prevails
|
self.ExcellonDecimals = 0 # 0 means global value prevails
|
||||||
|
|
||||||
def width_in(self):
|
def width_in(self):
|
||||||
"Return width in INCHES"
|
# add metric support (1/1000 mm vs. 1/100,000 inch)
|
||||||
return float(self.maxx-self.minx)*0.00001
|
if config.Config['measurementunits'] == 'inch':
|
||||||
|
"Return width in INCHES"
|
||||||
|
return float(self.maxx-self.minx)*0.00001
|
||||||
|
else:
|
||||||
|
return float(self.maxx-self.minx)*0.001
|
||||||
|
|
||||||
|
|
||||||
def height_in(self):
|
def height_in(self):
|
||||||
"Return height in INCHES"
|
# add metric support (1/1000 mm vs. 1/100,000 inch)
|
||||||
return float(self.maxy-self.miny)*0.00001
|
if config.Config['measurementunits'] == 'inch':
|
||||||
|
"Return height in INCHES"
|
||||||
|
return float(self.maxy-self.miny)*0.00001
|
||||||
|
else:
|
||||||
|
return float(self.maxy-self.miny)*0.001
|
||||||
|
|
||||||
def jobarea(self):
|
def jobarea(self):
|
||||||
return self.width_in()*self.height_in()
|
return self.width_in()*self.height_in()
|
||||||
|
@ -339,6 +353,21 @@ class Job:
|
||||||
if line[:7]=='%AMOC8*':
|
if line[:7]=='%AMOC8*':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# DipTrace specific fixes, but could be emitted by any CAD program. They are Standard Gerber RS-274X
|
||||||
|
# a hack to fix lack of recognition for metric direction from DipTrace - %MOMM*%
|
||||||
|
if (line[:7] == '%MOMM*%'):
|
||||||
|
if (config.Config['measurementunits'] == 'inch'):
|
||||||
|
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
|
||||||
|
|
||||||
|
if line[:3] == '%SF': # scale factor - we will ignore it
|
||||||
|
print 'Scale factor parameter ignored: ' + line
|
||||||
|
continue
|
||||||
|
|
||||||
|
# end basic diptrace fixes
|
||||||
|
|
||||||
# See if this is an aperture macro definition, and if so, map it.
|
# See if this is an aperture macro definition, and if so, map it.
|
||||||
M = amacro.parseApertureMacro(line,fid)
|
M = amacro.parseApertureMacro(line,fid)
|
||||||
if M:
|
if M:
|
||||||
|
@ -384,12 +413,24 @@ class Job:
|
||||||
if item[0]=='N': # Maximum digits for N* commands...ignore it
|
if item[0]=='N': # Maximum digits for N* commands...ignore it
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if item[0]=='X': # M.N specification for X-axis.
|
# allow for metric - scale to 1/1000 mm
|
||||||
fracpart = int(item[2])
|
if config.Config['measurementunits'] == 'inch':
|
||||||
x_div = 10.0**(5-fracpart)
|
if item[0]=='X': # M.N specification for X-axis.
|
||||||
if item[0]=='Y': # M.N specification for Y-axis.
|
fracpart = int(item[2])
|
||||||
fracpart = int(item[2])
|
x_div = 10.0**(5-fracpart)
|
||||||
y_div = 10.0**(5-fracpart)
|
if item[0]=='Y': # M.N specification for Y-axis.
|
||||||
|
fracpart = int(item[2])
|
||||||
|
y_div = 10.0**(5-fracpart)
|
||||||
|
else:
|
||||||
|
if item[0]=='X': # M.N specification for X-axis.
|
||||||
|
fracpart = int(item[2])
|
||||||
|
x_div = 10.0**(3-fracpart)
|
||||||
|
#print "x_div= %5.3f." % x_div
|
||||||
|
if item[0]=='Y': # M.N specification for Y-axis.
|
||||||
|
fracpart = int(item[2])
|
||||||
|
y_div = 10.0**(3-fracpart)
|
||||||
|
#print "y_div= %5.3f." % y_div
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Parse and interpret G-codes
|
# Parse and interpret G-codes
|
||||||
|
@ -400,7 +441,8 @@ class Job:
|
||||||
|
|
||||||
# Determine if this is a G-Code that should be ignored because it has no effect
|
# Determine if this is a G-Code that should be ignored because it has no effect
|
||||||
# (e.g., G70 specifies "inches" which is already in effect).
|
# (e.g., G70 specifies "inches" which is already in effect).
|
||||||
if gcode in [54, 70, 90]:
|
# added 71 - specify mm (metric)
|
||||||
|
if gcode in [54, 70, 90, 71]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Determine if this is a G-Code that we have to emit because it matters.
|
# Determine if this is a G-Code that we have to emit because it matters.
|
||||||
|
@ -427,6 +469,15 @@ class Job:
|
||||||
if match:
|
if match:
|
||||||
currtool = match.group(1)
|
currtool = match.group(1)
|
||||||
|
|
||||||
|
# Diptrace hack
|
||||||
|
# There is a D2* command in board outlines. I believe this should be D02. Let's change it then when it occurs:
|
||||||
|
if (currtool == 'D1'):
|
||||||
|
currtool = 'D01'
|
||||||
|
if (currtool == 'D2'):
|
||||||
|
currtool = 'D02'
|
||||||
|
if (currtool == 'D3'):
|
||||||
|
currtool = 'D03'
|
||||||
|
|
||||||
# Protel likes to issue random D01, D02, and D03 commands instead of aperture
|
# Protel likes to issue random D01, D02, and D03 commands instead of aperture
|
||||||
# codes. We can ignore D01 because it simply means to move to the current location
|
# codes. We can ignore D01 because it simply means to move to the current location
|
||||||
# while drawing. Well, that's drawing a point. We can ignore D02 because it means
|
# while drawing. Well, that's drawing a point. We can ignore D02 because it means
|
||||||
|
@ -597,6 +648,18 @@ class Job:
|
||||||
# Get rid of CR characters
|
# Get rid of CR characters
|
||||||
line = string.replace(line, '\x0D', '')
|
line = string.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
|
||||||
|
else:
|
||||||
|
#print "ignoring METRIC directive: " + line
|
||||||
|
continue # ignore it so func doesn't choke on it
|
||||||
|
|
||||||
|
if line[:3] == 'T00': # a tidying up that we can ignore
|
||||||
|
continue
|
||||||
|
# end metric/diptrace support
|
||||||
|
|
||||||
# Protel likes to embed comment lines beginning with ';'
|
# Protel likes to embed comment lines beginning with ';'
|
||||||
if line[0]==';':
|
if line[0]==';':
|
||||||
continue
|
continue
|
||||||
|
@ -704,9 +767,15 @@ class Job:
|
||||||
# Maybe we don't have this layer
|
# Maybe we don't have this layer
|
||||||
if not self.hasLayer(layername): return
|
if not self.hasLayer(layername): return
|
||||||
|
|
||||||
# First convert given inches to 2.5 co-ordinates
|
# add metric support (1/1000 mm vs. 1/100,000 inch)
|
||||||
X = int(round(Xoff/0.00001))
|
if config.Config['measurementunits'] == 'inch':
|
||||||
Y = int(round(Yoff/0.00001))
|
# First convert given inches to 2.5 co-ordinates
|
||||||
|
X = int(round(Xoff/0.00001))
|
||||||
|
Y = int(round(Yoff/0.00001))
|
||||||
|
else:
|
||||||
|
# First convert given mm to 5.3 co-ordinates
|
||||||
|
X = int(round(Xoff/0.001))
|
||||||
|
Y = int(round(Yoff/0.001))
|
||||||
|
|
||||||
# Now calculate displacement for each position so that we end up at specified origin
|
# Now calculate displacement for each position so that we end up at specified origin
|
||||||
DX = X - self.minx
|
DX = X - self.minx
|
||||||
|
@ -749,14 +818,20 @@ class Job:
|
||||||
# and our internal Excellon representation is 2.4 as of GerbMerge
|
# and our internal Excellon representation is 2.4 as of GerbMerge
|
||||||
# version 0.91. We use X,Y to calculate DX,DY in 2.4 units (i.e., with a
|
# version 0.91. We use X,Y to calculate DX,DY in 2.4 units (i.e., with a
|
||||||
# resolution of 0.0001".
|
# resolution of 0.0001".
|
||||||
X = int(round(Xoff/0.00001)) # First work in 2.5 format to match Gerber
|
# add metric support (1/1000 mm vs. 1/100,000 inch)
|
||||||
Y = int(round(Yoff/0.00001))
|
if config.Config['measurementunits'] == 'inch':
|
||||||
|
X = int(round(Xoff/0.00001)) # First work in 2.5 format to match Gerber
|
||||||
|
Y = int(round(Yoff/0.00001))
|
||||||
|
else:
|
||||||
|
X = int(round(Xoff/0.001)) # First work in 5.3 format to match Gerber
|
||||||
|
Y = int(round(Yoff/0.001))
|
||||||
|
|
||||||
# Now calculate displacement for each position so that we end up at specified origin
|
# Now calculate displacement for each position so that we end up at specified origin
|
||||||
DX = X - self.minx
|
DX = X - self.minx
|
||||||
DY = Y - self.miny
|
DY = Y - self.miny
|
||||||
|
|
||||||
# Now round down to 2.4 format
|
# Now round down to 2.4 format
|
||||||
|
# this scaling seems to work for either unit system
|
||||||
DX = int(round(DX/10.0))
|
DX = int(round(DX/10.0))
|
||||||
DY = int(round(DY/10.0))
|
DY = int(round(DY/10.0))
|
||||||
|
|
||||||
|
@ -778,9 +853,15 @@ class Job:
|
||||||
"""Write a drill hit pattern. diameter is tool diameter in inches, while toolNum is
|
"""Write a drill hit pattern. diameter is tool diameter in inches, while toolNum is
|
||||||
an integer index into strokes.DrillStrokeList"""
|
an integer index into strokes.DrillStrokeList"""
|
||||||
|
|
||||||
# First convert given inches to 2.5 co-ordinates
|
# add metric support (1/1000 mm vs. 1/100,000 inch)
|
||||||
X = int(round(Xoff/0.00001))
|
if config.Config['measurementunits'] == 'inch':
|
||||||
Y = int(round(Yoff/0.00001))
|
# First convert given inches to 2.5 co-ordinates
|
||||||
|
X = int(round(Xoff/0.00001))
|
||||||
|
Y = int(round(Yoff/0.00001))
|
||||||
|
else:
|
||||||
|
# First convert given inches to 5.3 co-ordinates
|
||||||
|
X = int(round(Xoff/0.001))
|
||||||
|
Y = int(round(Yoff/0.001))
|
||||||
|
|
||||||
# Now calculate displacement for each position so that we end up at specified origin
|
# Now calculate displacement for each position so that we end up at specified origin
|
||||||
DX = X - self.minx
|
DX = X - self.minx
|
||||||
|
@ -795,6 +876,8 @@ class Job:
|
||||||
if self.xcommands.has_key(ltool):
|
if self.xcommands.has_key(ltool):
|
||||||
for cmd in self.xcommands[ltool]:
|
for cmd in self.xcommands[ltool]:
|
||||||
x, y = cmd
|
x, y = cmd
|
||||||
|
# add metric support (1/1000 mm vs. 1/100,000 inch)
|
||||||
|
# TODO - verify metric scaling is correct???
|
||||||
makestroke.drawDrillHit(fid, 10*x+DX, 10*y+DY, toolNum)
|
makestroke.drawDrillHit(fid, 10*x+DX, 10*y+DY, toolNum)
|
||||||
|
|
||||||
def aperturesAndMacros(self, layername):
|
def aperturesAndMacros(self, layername):
|
||||||
|
@ -881,9 +964,14 @@ class Job:
|
||||||
newX, newY = geometry.rectCenter(newRect)
|
newX, newY = geometry.rectCenter(newRect)
|
||||||
|
|
||||||
# We arbitrarily remove all flashes that lead to rectangles
|
# We arbitrarily remove all flashes that lead to rectangles
|
||||||
# with a width or length less than 1 mil (10 Gerber units).
|
# with a width or length less than 1 mil (10 Gerber units). - sdd s.b. 0.1mil???
|
||||||
# Should we make this configurable?
|
# Should we make this configurable?
|
||||||
if min(newRectWidth, newRectHeight) >= 10:
|
# add metric support (1/1000 mm vs. 1/100,000 inch)
|
||||||
|
# if config.Config['measurementunits'] == 'inch':
|
||||||
|
# minFlash = 10;
|
||||||
|
# else
|
||||||
|
# minFlash =
|
||||||
|
if min(newRectWidth, newRectHeight) >= 10: # sdd - change for metric case at some point
|
||||||
# Construct an Aperture that is a Rectangle of dimensions (newRectWidth,newRectHeight)
|
# Construct an Aperture that is a Rectangle of dimensions (newRectWidth,newRectHeight)
|
||||||
newAP = aptable.Aperture(aptable.Rectangle, 'D??', \
|
newAP = aptable.Aperture(aptable.Rectangle, 'D??', \
|
||||||
util.gerb2in(newRectWidth), util.gerb2in(newRectHeight))
|
util.gerb2in(newRectWidth), util.gerb2in(newRectHeight))
|
||||||
|
@ -1006,7 +1094,12 @@ class Job:
|
||||||
keys = self.xcommands.keys()
|
keys = 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 = [(x,y) for x,y in self.xcommands[toolname] if self.inBorders(10*x,10*y)]
|
# add metric support (1/1000 mm vs. 1/100,000 inch)
|
||||||
|
# the normal metric scale factor isn't working right, so we'll leave it alone!!!!?
|
||||||
|
if config.Config['measurementunits'] == 'inch':
|
||||||
|
validList = [(x,y) for x,y in self.xcommands[toolname] if self.inBorders(10*x,10*y)]
|
||||||
|
else:
|
||||||
|
validList = [(x,y) for x,y in self.xcommands[toolname] if self.inBorders(0.1*x,0.1*y)]
|
||||||
|
|
||||||
if validList:
|
if validList:
|
||||||
self.xcommands[toolname] = validList
|
self.xcommands[toolname] = validList
|
||||||
|
@ -1271,12 +1364,16 @@ def rotateJob(job, degrees = 90, firstpass = True):
|
||||||
J.xcommands[tool] = []
|
J.xcommands[tool] = []
|
||||||
|
|
||||||
for x,y in job.xcommands[tool]:
|
for x,y in job.xcommands[tool]:
|
||||||
|
# add metric support (1/1000 mm vs. 1/100,000 inch)
|
||||||
|
# NOTE: There don't appear to be any need for a change. The usual x10 factor seems to apply
|
||||||
|
|
||||||
newx = -(10*y - job.miny) + job.minx + offset
|
newx = -(10*y - job.miny) + job.minx + offset
|
||||||
newy = (10*x - job.minx) + job.miny
|
newy = (10*x - job.minx) + job.miny
|
||||||
|
|
||||||
newx = int(round(newx/10.0))
|
newx = int(round(newx/10.0))
|
||||||
newy = int(round(newy/10.0))
|
newy = int(round(newy/10.0))
|
||||||
|
|
||||||
|
|
||||||
J.xcommands[tool].append((newx,newy))
|
J.xcommands[tool].append((newx,newy))
|
||||||
|
|
||||||
# Rotate some more if required
|
# Rotate some more if required
|
||||||
|
|
|
@ -39,8 +39,15 @@ def printTilingStats():
|
||||||
|
|
||||||
percent = 100.0*_Permutations/_PossiblePermutations
|
percent = 100.0*_Permutations/_PossiblePermutations
|
||||||
|
|
||||||
print "\r %5.2f%% complete / %ld/%ld Perm/Place / Smallest area: %.1f sq. in. / Best utilization: %.1f%%" % \
|
|
||||||
|
# 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),
|
(percent, _Permutations, _Placements, area, utilization),
|
||||||
|
else:
|
||||||
|
print "\r %5.2f%% complete / %ld/%ld Perm/Place / Smallest area: %.1f sq. mm / Best utilization: %.1f%%" % \
|
||||||
|
(percent, _Permutations, _Placements, area, utilization),
|
||||||
|
|
||||||
|
|
||||||
if gerbmerge.GUI is not None:
|
if gerbmerge.GUI is not None:
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
|
@ -36,7 +36,12 @@ def printTilingStats():
|
||||||
area = 999999.0
|
area = 999999.0
|
||||||
utilization = 0.0
|
utilization = 0.0
|
||||||
|
|
||||||
print "\r %ld placements / Smallest area: %.1f sq. in. / Best utilization: %.1f%%" % \
|
# 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),
|
||||||
|
else:
|
||||||
|
print "\r %ld placements / Smallest area: %.1f sq. mm / Best utilization: %.0f%%" % \
|
||||||
(_Placements, area, utilization),
|
(_Placements, area, utilization),
|
||||||
|
|
||||||
if gerbmerge.GUI is not None:
|
if gerbmerge.GUI is not None:
|
||||||
|
|
|
@ -11,10 +11,21 @@ Rugged Circuits LLC
|
||||||
http://ruggedcircuits.com/gerbmerge
|
http://ruggedcircuits.com/gerbmerge
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import config
|
||||||
|
|
||||||
|
|
||||||
def in2gerb(value):
|
def in2gerb(value):
|
||||||
"""Convert inches to 2.5 Gerber units"""
|
# add metric support (1/1000 mm vs. 1/100,000 inch)
|
||||||
return int(round(value*1e5))
|
if config.Config['measurementunits'] == 'inch':
|
||||||
|
"""Convert inches to 2.5 Gerber units"""
|
||||||
|
return int(round(value*1e5))
|
||||||
|
else: #convert mm to 5.3 Gerber units
|
||||||
|
return int(round(value*1e3))
|
||||||
|
|
||||||
def gerb2in(value):
|
def gerb2in(value):
|
||||||
"""Convert 2.5 Gerber units to inches"""
|
# add metric support (1/1000 mm vs. 1/100,000 inch)
|
||||||
return float(value)*1e-5
|
if config.Config['measurementunits'] == 'inch':
|
||||||
|
"""Convert 2.5 Gerber units to inches"""
|
||||||
|
return float(value)*1e-5
|
||||||
|
else: #convert 5.3 Gerber units to mm
|
||||||
|
return float(value)*1e-3
|
||||||
|
|
Loading…
Reference in New Issue