#!/usr/bin/env python """Support for writing characters and graphics to Gerber files -------------------------------------------------------------------- This program is licensed under the GNU General Public License (GPL) Version 3. See http://www.fsf.org for details of the license. Rugged Circuits LLC http://ruggedcircuits.com/gerbmerge """ import math import strokes # Define percentage of cell height and width to determine # intercharacter spacing SpacingX = 1.20 SpacingY = 1.20 # Arrow dimensions BarLength = 1500 # Length of dimension line ArrowWidth = 750 # How broad the arrow is ArrowLength = 750 # How far back from dimension line it is ArrowStemLength = 1250 # How long the arrow stem extends from center point ################################################################# # Arrow directions FacingLeft=0 # 0 degrees FacingDown=1 # 90 degrees counterclockwise FacingRight=2 # 180 degrees FacingUp=3 # 270 degrees SpacingDX = 10*int(round(strokes.MaxWidth*SpacingX)) SpacingDY = 10*int(round(strokes.MaxHeight*SpacingY)) RotatedGlyphs={} # Default arrow glyph is at 0 degrees rotation, facing left ArrowGlyph = [ [(0,-BarLength/2), (0, BarLength/2)], [(ArrowLength,ArrowWidth/2), (0,0), (ArrowLength,-ArrowWidth/2)], [(0,0), (ArrowStemLength,0)] ] def rotateGlyph(glyph, degrees, glyphName): """Rotate a glyph counterclockwise by given number of degrees. The glyph is a list of lists, where each sub-list is a connected path.""" try: return RotatedGlyphs["%.1f_%s" % (degrees, glyphName)] except KeyError: pass # Not cached yet rad = degrees/180.0*math.pi cosx = math.cos(rad) sinx = math.sin(rad) newglyph = [] for path in glyph: newpath = [] for X,Y in path: x = int(round(X*cosx - Y*sinx)) y = int(round(X*sinx + Y*cosx)) newpath.append((x,y)) newglyph.append(newpath) RotatedGlyphs["%.1f_%s" % (degrees, glyphName)] = newglyph return newglyph def writeFlash(fid, X, Y, D): fid.write("X%07dY%07dD%02d*\n" % (X,Y,D)) def drawPolyline(fid, L, offX, offY, scale=1): for ix in range(len(L)): X,Y = L[ix] X *= scale Y *= scale if ix==0: writeFlash(fid, X+offX, Y+offY, 2) else: writeFlash(fid, X+offX, Y+offY, 1) def writeGlyph(fid, glyph, X, Y, degrees, glyphName=None): if not glyphName: glyphName = str(glyph) for path in rotateGlyph(glyph, degrees, glyphName): drawPolyline(fid, path, X, Y, 10) def writeChar(fid, c, X, Y, degrees): if c==' ': return try: glyph = strokes.StrokeMap[c] except: raise RuntimeError, 'No glyph for character %s' % hex(ord(c)) writeGlyph(fid, glyph, X, Y, degrees, c) def writeString(fid, s, X, Y, degrees): posX = X posY = Y rad = degrees/180.0*math.pi dX = int(round(math.cos(rad)*SpacingDX)) dY = int(round(math.sin(rad)*SpacingDX)) if 0: if dX < 0: # Always print text left to right dX = -dX s = list(s) s.reverse() s = string.join(s, '') for char in s: writeChar(fid, char, posX, posY, degrees) posX += dX posY += dY def drawLine(fid, X1, Y1, X2, Y2): drawPolyline(fid, [(X1,Y1), (X2,Y2)], 0, 0) def boundingBox(s, X1, Y1): "Return (X1,Y1),(X2,Y2) for given string" if not s: return (X1, Y1), (X1, Y1) X2 = X1 + (len(s)-1)*SpacingDX + 10*strokes.MaxWidth Y2 = Y1 + 10*strokes.MaxHeight # Not including descenders return (X1, Y1), (X2, Y2) def drawDimensionArrow(fid, X, Y, facing): writeGlyph(fid, ArrowGlyph, X, Y, facing*90, "Arrow") def drawDrillHit(fid, X, Y, toolNum): writeGlyph(fid, strokes.DrillStrokeList[toolNum], X, Y, 0, "Drill%02d" % toolNum) if __name__=="__main__": import string s = string.digits+string.letters+string.punctuation #s = "The quick brown fox jumped over the lazy dog!" fid = file('test.ger','wt') fid.write("""G75* G70* %OFA0B0*% %FSAX24Y24*% %IPPOS*% %LPD*% %AMOC8* 5,1,8,0,0,1.08239X$1,22.5* *% %ADD10C,0.0100*% D10* """) writeString(fid, s, 0, 0, 0) drawDimensionArrow(fid, 0, 5000, FacingLeft) drawDimensionArrow(fid, 5000, 5000, FacingRight) drawDimensionArrow(fid, 0, 10000, FacingUp) drawDimensionArrow(fid, 5000, 10000, FacingDown) for diam in range(0,strokes.MaxNumDrillTools): writeGlyph(fid, strokes.DrillStrokeList[diam], diam*1250, 15000, 0, "%02d" % diam) fid.write("M02*\n") fid.close()