From 87e6aab7b52fefe1529a60a956fc9a0df3d326b7 Mon Sep 17 00:00:00 2001 From: Scott Daniels Date: Sun, 31 Mar 2013 03:46:31 -0500 Subject: [PATCH] Added support for metric units & Diptrace specific needs --- gerbmerge/config.py | 22 ++++-- gerbmerge/gerbmerge.py | 117 ++++++++++++++++++++++++++------ gerbmerge/jobs.py | 141 +++++++++++++++++++++++++++++++++------ gerbmerge/tilesearch1.py | 9 ++- gerbmerge/tilesearch2.py | 7 +- gerbmerge/util.py | 19 ++++-- 6 files changed, 262 insertions(+), 53 deletions(-) diff --git a/gerbmerge/config.py b/gerbmerge/config.py index 0bf3eb5..19c11f5 100644 --- a/gerbmerge/config.py +++ b/gerbmerge/config.py @@ -22,14 +22,15 @@ import aptable # Configuration dictionary. Specify floats as strings. Ints can be specified # as ints or strings. Config = { - 'xspacing': '0.125', # Spacing in horizontal direction - 'yspacing': '0.125', # Spacing in vertical direction + 'measurementunits': 'inch', # Unit system to use: inch or mm + '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) 'panelheight': '7.8', # Y-Dimension maximum panel size (Olimex) '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 - 'cutlinewidth': '0.01', # Width (inches) of cut lines + 'cutlinewidth': 0, #'0.01', # Width (inches) of cut lines 'minimumfeaturesize': 0, # Minimum dimension for selected layers 'toollist': None, # Name of file containing default tool list 'drillclustertolerance': '.002', # Tolerance for clustering drill sizes @@ -255,6 +256,19 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs): if 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 if Config['minimumfeaturesize']: temp = Config['minimumfeaturesize'].split(",") diff --git a/gerbmerge/gerbmerge.py b/gerbmerge/gerbmerge.py index 149baca..f5d23a7 100644 --- a/gerbmerge/gerbmerge.py +++ b/gerbmerge/gerbmerge.py @@ -88,8 +88,11 @@ the board outline layer for each job. """ sys.exit(1) +# changed these two writeGerberHeader files to take metric units (mm) into account: + def writeGerberHeader22degrees(fid): - fid.write( \ + if config.Config['measurementunits'] == 'inch': + fid.write( \ """G75* G70* %OFA0B0*% @@ -100,9 +103,21 @@ G70* 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): - fid.write( \ + if config.Config['measurementunits'] == 'inch': + fid.write( \ """G75* G70* %OFA0B0*% @@ -112,6 +127,16 @@ G70* %AMOC8* 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 @@ -134,6 +159,11 @@ def writeGerberFooter(fid): fid.write('M02*\n') def writeExcellonHeader(fid): + if config.Config['measurementunits'] != 'inch': # metric - mm + fid.write( \ +"""M48 +METRIC,0000.00 +""") fid.write('%\n') 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 # panel border. This means the center of the line is D/2 offset # 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 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 x = OriginX + 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.125))) + fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+0.000), util.in2gerb(y+cropW))) # Lower-right x = MaxXExtent - 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.125), util.in2gerb(y+0.000))) + fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x-cropW), util.in2gerb(y+0.000))) # Upper-right x = MaxXExtent - 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.125))) + fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+0.000), util.in2gerb(y-cropW))) # Upper-left x = OriginX + 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.125), util.in2gerb(y+0.000))) + fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+cropW), util.in2gerb(y+0.000))) def disclaimer(): + #return # remove annoying disclaimer + print """ **************************************************** * 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) 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 @@ -325,7 +371,11 @@ def merge(opts, args, gui = None): else: print 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 # 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 # 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 # is no layout file, do auto-layout. @@ -530,7 +581,11 @@ def merge(opts, args, gui = None): writeGerberHeader(fid) # 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) # Choose drawing aperture D10 @@ -681,22 +736,38 @@ def merge(opts, args, gui = None): fid = file(fullname, 'wt') print '-'*50 - print ' Job Size : %f" x %f"' % (MaxXExtent-OriginX, MaxYExtent-OriginY) - print ' Job Area : %.2f sq. in.' % totalarea + # 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 + 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 ' 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:' smallestDrill = 999.9 for tool in Tools: if ToolStats[tool]: - fid.write('%s %.4fin\n' % (tool, config.GlobalToolMap[tool])) - print ' %s %.4f" %5d hits' % (tool, config.GlobalToolMap[tool], 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]) + 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]) 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 'Output Files :' @@ -706,7 +777,11 @@ def merge(opts, args, gui = None): if (MaxXExtent-OriginX)>config.Config['panelwidth'] or (MaxYExtent-OriginY)>config.Config['panelheight']: print '*'*75 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 '*'*75 sys.exit(1) diff --git a/gerbmerge/jobs.py b/gerbmerge/jobs.py index 666afaf..8ec3030 100644 --- a/gerbmerge/jobs.py +++ b/gerbmerge/jobs.py @@ -36,6 +36,11 @@ import util # D02 -- move with exposure off # 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 apdef_pat = re.compile(r'^%AD(D\d+)([^*$]+)\*%$') # Aperture definition apmdef_pat = re.compile(r'^%AM([^*$]+)\*$') # Aperture macro definition @@ -180,12 +185,21 @@ class Job: self.ExcellonDecimals = 0 # 0 means global value prevails def width_in(self): - "Return width in INCHES" - return float(self.maxx-self.minx)*0.00001 + # add metric support (1/1000 mm vs. 1/100,000 inch) + 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): - "Return height in INCHES" - return float(self.maxy-self.miny)*0.00001 + # add metric support (1/1000 mm vs. 1/100,000 inch) + 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): return self.width_in()*self.height_in() @@ -339,6 +353,21 @@ class Job: if line[:7]=='%AMOC8*': 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. M = amacro.parseApertureMacro(line,fid) if M: @@ -384,12 +413,24 @@ class Job: if item[0]=='N': # Maximum digits for N* commands...ignore it continue - if item[0]=='X': # M.N specification for X-axis. - fracpart = int(item[2]) - x_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) + # allow for metric - scale to 1/1000 mm + if config.Config['measurementunits'] == 'inch': + if item[0]=='X': # M.N specification for X-axis. + fracpart = int(item[2]) + x_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 # 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 # (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 # Determine if this is a G-Code that we have to emit because it matters. @@ -427,6 +469,15 @@ class Job: if match: 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 # 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 @@ -597,6 +648,18 @@ class Job: # Get rid of CR characters 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 ';' if line[0]==';': continue @@ -704,9 +767,15 @@ class Job: # Maybe we don't have this layer if not self.hasLayer(layername): return - # First convert given inches to 2.5 co-ordinates - X = int(round(Xoff/0.00001)) - Y = int(round(Yoff/0.00001)) + # add metric support (1/1000 mm vs. 1/100,000 inch) + if config.Config['measurementunits'] == 'inch': + # 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 DX = X - self.minx @@ -749,14 +818,20 @@ class Job: # 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 # resolution of 0.0001". - X = int(round(Xoff/0.00001)) # First work in 2.5 format to match Gerber - Y = int(round(Yoff/0.00001)) + # add metric support (1/1000 mm vs. 1/100,000 inch) + 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 DX = X - self.minx DY = Y - self.miny # Now round down to 2.4 format + # this scaling seems to work for either unit system DX = int(round(DX/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 an integer index into strokes.DrillStrokeList""" - # First convert given inches to 2.5 co-ordinates - X = int(round(Xoff/0.00001)) - Y = int(round(Yoff/0.00001)) + # add metric support (1/1000 mm vs. 1/100,000 inch) + if config.Config['measurementunits'] == 'inch': + # 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 DX = X - self.minx @@ -795,6 +876,8 @@ class Job: if self.xcommands.has_key(ltool): for cmd in self.xcommands[ltool]: 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) def aperturesAndMacros(self, layername): @@ -881,9 +964,14 @@ class Job: newX, newY = geometry.rectCenter(newRect) # 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? - 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) newAP = aptable.Aperture(aptable.Rectangle, 'D??', \ util.gerb2in(newRectWidth), util.gerb2in(newRectHeight)) @@ -1006,7 +1094,12 @@ class Job: keys = self.xcommands.keys() for toolname in keys: # 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: self.xcommands[toolname] = validList @@ -1271,12 +1364,16 @@ def rotateJob(job, degrees = 90, firstpass = True): J.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 newy = (10*x - job.minx) + job.miny newx = int(round(newx/10.0)) newy = int(round(newy/10.0)) + J.xcommands[tool].append((newx,newy)) # Rotate some more if required diff --git a/gerbmerge/tilesearch1.py b/gerbmerge/tilesearch1.py index 59be680..9404d0f 100644 --- a/gerbmerge/tilesearch1.py +++ b/gerbmerge/tilesearch1.py @@ -39,8 +39,15 @@ def printTilingStats(): 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), + 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: sys.stdout.flush() diff --git a/gerbmerge/tilesearch2.py b/gerbmerge/tilesearch2.py index 4ad5479..15f432a 100644 --- a/gerbmerge/tilesearch2.py +++ b/gerbmerge/tilesearch2.py @@ -36,7 +36,12 @@ def printTilingStats(): area = 999999.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), if gerbmerge.GUI is not None: diff --git a/gerbmerge/util.py b/gerbmerge/util.py index ad5cf84..3a64d8e 100644 --- a/gerbmerge/util.py +++ b/gerbmerge/util.py @@ -11,10 +11,21 @@ Rugged Circuits LLC http://ruggedcircuits.com/gerbmerge """ +import config + + def in2gerb(value): - """Convert inches to 2.5 Gerber units""" - return int(round(value*1e5)) +# add metric support (1/1000 mm vs. 1/100,000 inch) + 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): - """Convert 2.5 Gerber units to inches""" - return float(value)*1e-5 +# add metric support (1/1000 mm vs. 1/100,000 inch) + 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