#!/usr/bin/env python """This file handles the writing of the fabrication drawing Gerber file -------------------------------------------------------------------- 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 string import config import makestroke import util def writeDrillHits(fid, Place, Tools): toolNumber = -1 for tool in Tools: toolNumber += 1 try: size = config.GlobalToolMap[tool] except: raise RuntimeError, "INTERNAL ERROR: Tool code %s not found in global tool list" % tool #for row in Layout: # row.writeDrillHits(fid, size, toolNumber) for job in Place.jobs: job.writeDrillHits(fid, size, toolNumber) def writeBoundingBox(fid, OriginX, OriginY, MaxXExtent, MaxYExtent): x = util.in2gerb(OriginX) y = util.in2gerb(OriginY) X = util.in2gerb(MaxXExtent) Y = util.in2gerb(MaxYExtent) makestroke.drawPolyline(fid, [(x,y), (X,y), (X,Y), (x,Y), (x,y)], 0, 0) def writeDrillLegend(fid, Tools, OriginY, MaxXExtent): # This is the spacing from the right edge of the board to where the # drill legend is to be drawn, in inches. Remember we have to allow # for dimension arrows, too. dimspace = 0.5 # inches # This is the spacing from the drill hit glyph to the drill size # in inches. glyphspace = 0.1 # inches # Convert to Gerber 2.5 units dimspace = util.in2gerb(dimspace) glyphspace = util.in2gerb(glyphspace) # Construct a list of tuples (toolSize, toolNumber) where toolNumber # is the position of the tool in Tools and toolSize is in inches. L = [] toolNumber = -1 for tool in Tools: toolNumber += 1 L.append((config.GlobalToolMap[tool], toolNumber)) # Now sort the list from smallest to largest L.sort() # And reverse to go from largest to smallest, so we can write the legend # from the bottom up L.reverse() # For each tool, draw a drill hit marker then the size of the tool # in inches. posY = util.in2gerb(OriginY) posX = util.in2gerb(MaxXExtent) + dimspace maxX = 0 for size,toolNum in L: # Determine string to write and midpoint of string s = '%.3f"' % size ll, ur = makestroke.boundingBox(s, posX+glyphspace, posY) # Returns lower-left point, upper-right point midpoint = (ur[1]+ll[1])/2 # Keep track of maximum extent of legend maxX = max(maxX, ur[0]) makestroke.drawDrillHit(fid, posX, midpoint, toolNum) makestroke.writeString(fid, s, posX+glyphspace, posY, 0) posY += int(round((ur[1]-ll[1])*1.5)) # Return value is lower-left of user text area, without any padding. return maxX, util.in2gerb(OriginY) def writeDimensionArrow(fid, OriginX, OriginY, MaxXExtent, MaxYExtent): x = util.in2gerb(OriginX) y = util.in2gerb(OriginY) X = util.in2gerb(MaxXExtent) Y = util.in2gerb(MaxYExtent) # This constant is how far away from the board the centerline of the dimension # arrows should be, in inches. dimspace = 0.2 # Convert it to Gerber (0.00001" or 2.5) units dimspace = util.in2gerb(dimspace) # Draw an arrow above the board, on the left side and right side makestroke.drawDimensionArrow(fid, x, Y+dimspace, makestroke.FacingLeft) makestroke.drawDimensionArrow(fid, X, Y+dimspace, makestroke.FacingRight) # Draw arrows to the right of the board, at top and bottom makestroke.drawDimensionArrow(fid, X+dimspace, Y, makestroke.FacingUp) makestroke.drawDimensionArrow(fid, X+dimspace, y, makestroke.FacingDown) # Now draw the text. First, horizontal text above the board. s = '%.3f"' % (MaxXExtent - OriginX) ll, ur = makestroke.boundingBox(s, 0, 0) s_width = ur[0]-ll[0] # Width in 2.5 units s_height = ur[1]-ll[1] # Height in 2.5 units # Compute the position in 2.5 units where we should draw this. It should be # centered horizontally and also vertically about the dimension arrow centerline. posX = x + (x+X)/2 posX -= s_width/2 posY = Y + dimspace - s_height/2 makestroke.writeString(fid, s, posX, posY, 0) # Finally, draw the extending lines from the text to the arrows. posY = Y + dimspace posX1 = posX - util.in2gerb(0.1) # 1000 posX2 = posX + s_width + util.in2gerb(0.1) # 1000 makestroke.drawLine(fid, x, posY, posX1, posY) makestroke.drawLine(fid, posX2, posY, X, posY) # Now do the vertical text s = '%.3f"' % (MaxYExtent - OriginY) ll, ur = makestroke.boundingBox(s, 0, 0) s_width = ur[0]-ll[0] s_height = ur[1]-ll[1] # As above, figure out where to draw this. Rotation will be -90 degrees # so new origin will be top-left of bounding box after rotation. posX = X + dimspace - s_height/2 posY = y + (y+Y)/2 posY += s_width/2 makestroke.writeString(fid, s, posX, posY, -90) # Draw extending lines posX = X + dimspace posY1 = posY + util.in2gerb(0.1) # 1000 posY2 = posY - s_width - util.in2gerb(0.1) # 1000 makestroke.drawLine(fid, posX, Y, posX, posY1) makestroke.drawLine(fid, posX, posY2, posX, y) def writeUserText(fid, X, Y): fname = config.Config['fabricationdrawingtext'] 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)) lines = tfile.readlines() tfile.close() lines.reverse() # We're going to print from bottom up # Offset X position to give some clearance from drill legend X += util.in2gerb(0.2) # 2000 for line in lines: # Get rid of CR line = string.replace(line, '\x0D', '') # Chop off \n #if line[-1] in string.whitespace: # line = line[:-1] # Strip off trailing whitespace line = string.rstrip(line) # Blank lines still need height, so must have at least one character if not line: line = ' ' ll, ur = makestroke.boundingBox(line, X, Y) makestroke.writeString(fid, line, X, Y, 0) Y += int(round((ur[1]-ll[1])*1.5)) # Main entry point. Gerber file has already been opened, header written # out, 1mil tool selected. def writeFabDrawing(fid, Place, Tools, OriginX, OriginY, MaxXExtent, MaxYExtent): # Write out all the drill hits writeDrillHits(fid, Place, Tools) # Draw a bounding box for the project writeBoundingBox(fid, OriginX, OriginY, MaxXExtent, MaxYExtent) # Write out the drill hit legend off to the side. This function returns # (X,Y) lower-left origin where user text is to begin, in Gerber units # and without any padding. X,Y = writeDrillLegend(fid, Tools, OriginY, MaxXExtent) # Write out the dimensioning arrows writeDimensionArrow(fid, OriginX, OriginY, MaxXExtent, MaxYExtent) # Finally, write out user text writeUserText(fid, X, Y)