gerbmerge/gerbmerge/fabdrawing.py

211 lines
6.6 KiB
Python

#!/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 = 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()
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 = str.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)