mirror of
https://github.com/telavivmakers/at-tami.git
synced 2024-05-25 11:56:54 +03:00
211 lines
6.6 KiB
Python
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 = 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)
|