283 lines
11 KiB
Python
Executable File
283 lines
11 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import math
|
|
import sys
|
|
import argparse
|
|
import svgturtle
|
|
|
|
parser = argparse.ArgumentParser(description='Generate a hexagonal battery case')
|
|
parser.add_argument('--dimension', default=5, type=int, help='Grid dimension')
|
|
parser.add_argument('--height', type=float, help='Height of battery')
|
|
parser.add_argument('--hole', default='AA', help='Hole diameter (mm or A, AA, AAA)')
|
|
parser.add_argument('--kerf', default=.1, type=float, help='Kerf')
|
|
parser.add_argument('--corner-length', default=4, help='Length of stretch corner in material thicknesses')
|
|
parser.add_argument('--stretch', default=1.05, type=float, help='Reduction factor of stretch material')
|
|
parser.add_argument('--horizontal-finger', default=5.0, type=float, help='Width of horizontal fingers')
|
|
parser.add_argument('--vertical-finger', default=5.0, type=float, help='Width of vertical fingers')
|
|
parser.add_argument('--padding', default=1.5, type=float, help='Padding around holes')
|
|
parser.add_argument('--outside-padding', default=4, type=float, help='Extra padding between holes and wall')
|
|
parser.add_argument('--thickness', default=3.0, type=float, help='Thickness of material')
|
|
parser.add_argument('--lid', default=0.2, type=float, help='How much extra play to give the lid')
|
|
parser.add_argument('--verbose', action='store_true', help='Print computed parameter values')
|
|
args = parser.parse_args()
|
|
assert (args.dimension % 2) == 1
|
|
|
|
BATTERY = {
|
|
'AAA': [10.5, 44.5],
|
|
'AA': [14.5, 50.5],
|
|
'C': [26.2, 50.0],
|
|
'D': [34.2, 61.5],
|
|
}
|
|
|
|
if args.hole in BATTERY:
|
|
dim = BATTERY[args.hole]
|
|
args.hole = dim[0]
|
|
if args.height == None:
|
|
args.height = dim[1]
|
|
else:
|
|
args.hole = float(args.hole)
|
|
assert(args.height != None)
|
|
|
|
args.kerf2 = args.kerf/2
|
|
args.grid = args.hole+args.padding
|
|
args.radius = args.hole/2
|
|
args.corner = args.corner_length*args.thickness
|
|
args.corner_s = args.corner*args.stretch
|
|
args.corner_radius = 3*args.corner_s/math.pi
|
|
args.corner_inset = args.corner_s*math.sqrt(3)/math.pi
|
|
args.interior_edge = args.grid*args.dimension*0.5+args.outside_padding
|
|
args.exterior_edge = args.interior_edge+2.0*args.thickness
|
|
args.interior_leg = (args.interior_edge-args.horizontal_finger-args.kerf)/2-args.corner_inset
|
|
finger_length = args.interior_edge-2.0*args.corner_inset
|
|
args.n_hor_fingers = int(finger_length/args.horizontal_finger/2)
|
|
args.exterior_leg = (finger_length-(2*args.n_hor_fingers-1)*args.horizontal_finger+args.kerf)/2
|
|
args.exterior_slot = (args.exterior_edge-args.horizontal_finger+args.kerf)/2
|
|
args.n_ver_fingers = int(args.height/args.vertical_finger/2)
|
|
args.vertical_finger = args.height/args.n_ver_fingers/2
|
|
args.slots = [args.vertical_finger*2.5-args.thickness*0.5, args.vertical_finger*4.5-args.thickness*0.5]
|
|
|
|
if args.verbose:
|
|
print(args, file=sys.stderr)
|
|
|
|
DIMX = 5.0*args.grid*args.dimension
|
|
DIMY = 3.0*args.grid*args.dimension
|
|
PI3 = math.pi/3
|
|
|
|
HOLES = ''
|
|
SHAPES = ''
|
|
|
|
def draw_grid(cx, cy):
|
|
global HOLES
|
|
for row in range(-int(args.dimension/2), int((args.dimension+1)/2)):
|
|
cyr = cy+args.grid*row*math.sin(PI3)
|
|
num_col = args.dimension-abs(row)
|
|
cxr = cx-.5*args.grid*(num_col-1.0)
|
|
for col in range(num_col):
|
|
HOLES += '<circle cx="%.2f" cy="%.2f" r="%.2f"/>\n' % (cxr+col*args.grid, cyr, args.radius)
|
|
|
|
def draw_plane(cx, cy, interior=False):
|
|
global HOLES, SHAPES
|
|
if interior:
|
|
edge = args.interior_edge
|
|
else:
|
|
edge = args.exterior_edge
|
|
turtle = svgturtle.SvgTurtle(cx, cy)
|
|
turtle.penup()
|
|
turtle.forward(edge)
|
|
turtle.right(120)
|
|
if interior:
|
|
turtle.forward(args.corner_inset)
|
|
turtle.pendown()
|
|
for side in range(5):
|
|
turtle.forward(args.interior_leg)
|
|
turtle.left(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.right(90)
|
|
turtle.forward(args.horizontal_finger+args.kerf)
|
|
turtle.right(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.left(90)
|
|
turtle.forward(args.interior_leg)
|
|
turtle.circle(-args.corner_radius, 60)
|
|
turtle.forward(args.interior_edge-2.0*args.corner_inset)
|
|
turtle.circle(-args.corner_radius, 60)
|
|
else:
|
|
turtle.pendown()
|
|
for side in range(6):
|
|
turtle.forward(args.exterior_edge)
|
|
turtle.right(60)
|
|
SHAPES += '<path d="%s"/>\n' % turtle.to_s()
|
|
if not interior:
|
|
turtle.reset()
|
|
turtle.penup()
|
|
turtle.forward(args.interior_edge)
|
|
turtle.right(120)
|
|
turtle.forward(args.corner_inset)
|
|
for side in range(5):
|
|
for finger in range(args.n_hor_fingers):
|
|
turtle.forward(args.exterior_leg if finger == 0 else args.horizontal_finger+args.kerf)
|
|
turtle.pendown()
|
|
turtle.left(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.right(90)
|
|
turtle.forward(args.horizontal_finger-args.kerf)
|
|
turtle.right(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.right(90)
|
|
turtle.forward(args.horizontal_finger-args.kerf)
|
|
turtle.penup()
|
|
turtle.right(180)
|
|
turtle.forward(args.horizontal_finger-args.kerf)
|
|
turtle.forward(args.exterior_leg)
|
|
turtle.pendown()
|
|
turtle.left(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.right(90)
|
|
turtle.circle(-args.corner_radius-args.thickness, 60)
|
|
turtle.right(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.right(90)
|
|
turtle.circle(args.corner_radius, 60)
|
|
turtle.penup()
|
|
turtle.right(180)
|
|
turtle.circle(-args.corner_radius, 60)
|
|
turtle.forward(args.interior_edge-2*args.corner_inset)
|
|
turtle.pendown()
|
|
turtle.left(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.right(90)
|
|
turtle.circle(-args.corner_radius-args.thickness, 60)
|
|
turtle.right(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.right(90)
|
|
turtle.circle(args.corner_radius, 60)
|
|
HOLES += '<path d="%s"/>\n' % turtle.to_s()
|
|
|
|
def draw_side(x0, y0, h, slots):
|
|
global HOLES, SHAPES
|
|
turtle = svgturtle.SvgTurtle(x0, y0+args.thickness)
|
|
for finger in range(args.n_hor_fingers):
|
|
turtle.forward(args.exterior_leg-args.kerf if finger == 0 else args.horizontal_finger-args.kerf)
|
|
turtle.left(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.right(90)
|
|
turtle.forward(args.horizontal_finger+args.kerf)
|
|
turtle.right(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.left(90)
|
|
turtle.forward(args.exterior_leg-args.kerf-args.thickness)
|
|
turtle.right(90)
|
|
for finger in range(args.n_ver_fingers):
|
|
turtle.forward(args.vertical_finger-(args.kerf/2 if finger==0 else args.kerf))
|
|
turtle.left(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.right(90)
|
|
turtle.forward(args.vertical_finger+args.kerf)
|
|
turtle.right(90)
|
|
if finger < args.n_ver_fingers-1:
|
|
turtle.forward(args.thickness)
|
|
turtle.left(90)
|
|
turtle.forward(args.exterior_edge-args.thickness)
|
|
turtle.right(90)
|
|
for finger in range(args.n_ver_fingers):
|
|
turtle.forward(args.vertical_finger-(args.kerf/2 if finger==0 else args.kerf))
|
|
turtle.left(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.right(90)
|
|
turtle.forward(args.vertical_finger+args.kerf)
|
|
turtle.right(90)
|
|
if finger < args.n_ver_fingers-1:
|
|
turtle.forward(args.thickness)
|
|
turtle.left(90)
|
|
SHAPES += '<path d="%s"/>\n' % turtle.to_s()
|
|
for slot in slots:
|
|
x = x0+args.exterior_slot
|
|
y = y0+slot
|
|
w = args.horizontal_finger-args.kerf
|
|
h = args.thickness-args.kerf
|
|
HOLES += '<rect x="%.2f" y="%.2f" width="%.2f" height="%.2f"/>\n' % (x, y, w, h)
|
|
|
|
def draw_case(x0, y0, h, slots):
|
|
global HOLES, SHAPES
|
|
turtle = svgturtle.SvgTurtle(x0, y0)
|
|
turtle.forward(args.exterior_edge*0.5)
|
|
for side in range(6):
|
|
turtle.right(90)
|
|
turtle.forward(args.height-args.thickness)
|
|
turtle.penup()
|
|
turtle.back(args.height-args.thickness)
|
|
turtle.left(90)
|
|
turtle.forward(2*args.thickness)
|
|
turtle.right(90)
|
|
turtle.pendown()
|
|
turtle.forward(args.height-args.thickness)
|
|
turtle.penup()
|
|
turtle.back(args.height-args.thickness)
|
|
turtle.left(90)
|
|
turtle.pendown()
|
|
if side==5:
|
|
break
|
|
turtle.forward(args.exterior_edge-args.thickness*2)
|
|
turtle.forward(args.exterior_edge*0.5-args.thickness*2)
|
|
turtle.right(90)
|
|
for finger in range(args.n_ver_fingers):
|
|
turtle.forward(args.vertical_finger-(args.kerf/2 if finger==0 else args.kerf))
|
|
turtle.left(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.right(90)
|
|
turtle.forward(args.vertical_finger+args.kerf)
|
|
turtle.right(90)
|
|
if finger < args.n_ver_fingers-1:
|
|
turtle.forward(args.thickness)
|
|
turtle.left(90)
|
|
turtle.forward(args.exterior_edge*0.5)
|
|
for side in range(6):
|
|
turtle.right(90)
|
|
turtle.forward(args.height-args.thickness)
|
|
turtle.penup()
|
|
turtle.back(args.height-args.thickness)
|
|
turtle.left(90)
|
|
turtle.pendown()
|
|
if side==5:
|
|
break
|
|
for finger in range(args.n_hor_fingers):
|
|
turtle.forward(args.exterior_leg if finger == 0 else args.horizontal_finger+args.kerf)
|
|
turtle.left(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.right(90)
|
|
turtle.forward(args.horizontal_finger-args.kerf)
|
|
turtle.right(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.left(90)
|
|
turtle.forward(args.exterior_leg)
|
|
turtle.forward(args.exterior_edge*0.5)
|
|
turtle.right(90)
|
|
for finger in range(args.n_ver_fingers):
|
|
turtle.forward(args.vertical_finger-(args.kerf/2 if finger==0 else args.kerf))
|
|
turtle.left(90)
|
|
turtle.forward(args.thickness)
|
|
turtle.right(90)
|
|
turtle.forward(args.vertical_finger+args.kerf)
|
|
turtle.right(90)
|
|
if finger < args.n_ver_fingers-1:
|
|
turtle.forward(args.thickness)
|
|
turtle.left(90)
|
|
SHAPES += '<path d="%s"/>\n' % turtle.to_s()
|
|
for slot in slots:
|
|
for side in range(5):
|
|
x = x0+(side+0.5)*args.exterior_edge+args.thickness+args.exterior_slot
|
|
y = y0+slot
|
|
w = args.horizontal_finger-args.kerf
|
|
h = args.thickness-args.kerf
|
|
HOLES += '<rect x="%.2f" y="%.2f" width="%.2f" height="%.2f"/>\n' % (x, y, w, h)
|
|
|
|
print('<svg viewBox="0 0 %.2f %.2f" width="%.2fmm" height="%.2fmm" stroke-width="0.1" xmlns="http://www.w3.org/2000/svg">' % (DIMX, DIMY, DIMX, DIMY))
|
|
draw_grid(0.65*args.grid*args.dimension, 0.60*args.grid*args.dimension)
|
|
draw_plane(0.65*args.grid*args.dimension, 0.60*args.grid*args.dimension, True)
|
|
draw_grid(1.90*args.grid*args.dimension, 0.60*args.grid*args.dimension)
|
|
draw_plane(1.90*args.grid*args.dimension, 0.60*args.grid*args.dimension, True)
|
|
draw_plane(3.15*args.grid*args.dimension, 0.60*args.grid*args.dimension)
|
|
draw_case(0.05*args.grid*args.dimension, 1.25*args.grid*args.dimension, args.height, args.slots)
|
|
print('<g fill="none" stroke="red">', HOLES, '</g>', '<g fill="none" stroke="black">', SHAPES, '</g>', sep='\n')
|
|
print('</svg>')
|