Compare commits
3 Commits
30da19afd6
...
16e60427d1
Author | SHA1 | Date | |
---|---|---|---|
16e60427d1 | |||
c19a1e8876 | |||
d0e64ae692 |
85
battery-case-generator.py
Executable file
85
battery-case-generator.py
Executable file
|
@ -0,0 +1,85 @@
|
|||
#!/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('--horizontal-finger', default=10.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=2, 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')
|
||||
|
||||
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)
|
||||
|
||||
GRID = args.hole+args.padding
|
||||
RADIUS = args.hole/2
|
||||
DIMX = 2*(args.dimension+2)*GRID
|
||||
DIMY = (args.dimension+2)*GRID
|
||||
PI3 = math.pi/3
|
||||
|
||||
def draw_grid(cx, cy):
|
||||
for row in range(-int(args.dimension/2), int((args.dimension+1)/2)):
|
||||
cyr = cy+GRID*row*math.sin(PI3)
|
||||
num_col = args.dimension-abs(row)
|
||||
cxr = cx-.5*GRID*(num_col-1.0)
|
||||
for col in range(num_col):
|
||||
print('<circle cx="%.2f" cy="%.2f" r="%.2f" stroke="black" fill="none"/>' % (cxr+col*GRID, cyr, RADIUS))
|
||||
|
||||
def draw_plane(cx, cy, interior=False):
|
||||
radius = GRID*args.dimension*0.5+args.outside_padding
|
||||
if not interior:
|
||||
radius += args.thickness
|
||||
turtle = svgturtle.SvgTurtle(cx, cy)
|
||||
turtle.penup()
|
||||
turtle.forward(radius)
|
||||
turtle.right(120)
|
||||
turtle.pendown()
|
||||
if interior:
|
||||
leg = (radius-args.horizontal_finger-args.kerf)/2
|
||||
for side in range(6):
|
||||
turtle.forward(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(leg)
|
||||
turtle.right(60)
|
||||
else:
|
||||
for side in range(6):
|
||||
turtle.forward(radius)
|
||||
turtle.right(60)
|
||||
print('<path d="%s" fill="none" stroke="black"/>' % turtle.to_s())
|
||||
|
||||
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(GRID*(0.5*args.dimension+1), GRID*(0.5*args.dimension+1))
|
||||
draw_plane(GRID*(0.5*args.dimension+1), GRID*(0.5*args.dimension+1), True)
|
||||
draw_plane(GRID*(1.5*args.dimension+3), GRID*(0.5*args.dimension+1))
|
||||
print('</svg>')
|
93
pincushion.py
Executable file
93
pincushion.py
Executable file
|
@ -0,0 +1,93 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import math
|
||||
import sys
|
||||
|
||||
M=2
|
||||
N=5
|
||||
L=20.0
|
||||
B=2.5
|
||||
|
||||
def frange(*args):
|
||||
"""frange([start, ] end [, step [, mode]]) -> generator
|
||||
|
||||
A float range generator. If not specified, the default start is 0.0
|
||||
and the default step is 1.0.
|
||||
|
||||
Optional argument mode sets whether frange outputs an open or closed
|
||||
interval. mode must be an int. Bit zero of mode controls whether start is
|
||||
included (on) or excluded (off); bit one does the same for end. Hence:
|
||||
|
||||
0 -> open interval (start and end both excluded)
|
||||
1 -> half-open (start included, end excluded)
|
||||
2 -> half open (start excluded, end included)
|
||||
3 -> closed (start and end both included)
|
||||
|
||||
By default, mode=1 and only start is included in the output.
|
||||
"""
|
||||
mode = 1 # Default mode is half-open.
|
||||
n = len(args)
|
||||
if n == 1:
|
||||
args = (0.0, args[0], 1.0)
|
||||
elif n == 2:
|
||||
args = args + (1.0,)
|
||||
elif n == 4:
|
||||
mode = args[3]
|
||||
args = args[0:3]
|
||||
elif n != 3:
|
||||
raise TypeError('frange expects 1-4 arguments, got %d' % n)
|
||||
assert len(args) == 3
|
||||
try:
|
||||
start, end, step = [a + 0.0 for a in args]
|
||||
except TypeError:
|
||||
raise TypeError('arguments must be numbers')
|
||||
if step == 0.0:
|
||||
raise ValueError('step must not be zero')
|
||||
if not isinstance(mode, int):
|
||||
raise TypeError('mode must be an int')
|
||||
if mode & 1:
|
||||
i, x = 0, start
|
||||
else:
|
||||
i, x = 1, start+step
|
||||
if step > 0:
|
||||
if mode & 2:
|
||||
from operator import le as comp
|
||||
else:
|
||||
from operator import lt as comp
|
||||
else:
|
||||
if mode & 2:
|
||||
from operator import ge as comp
|
||||
else:
|
||||
from operator import gt as comp
|
||||
while comp(x, end):
|
||||
yield x
|
||||
i += 1
|
||||
x = start + i*step
|
||||
|
||||
def cushion(x0, y0, hole, space):
|
||||
yoff = .866*space # sqrt(3)/2
|
||||
xodd = list(frange(x0+B, x0+L-B, space, 3))
|
||||
xeven = list(frange(x0+B+.5*space, x0+L-B, space, 3))
|
||||
row = 0
|
||||
for y in frange(y0+B, y0+L-B, yoff, 3):
|
||||
row += 1
|
||||
if (row & 1) == 1:
|
||||
xrow = xodd
|
||||
else:
|
||||
xrow = xeven
|
||||
for x in xrow:
|
||||
print('<circle cx="{}" cy="{}" r="{}" stroke="black" fill="none"/>'.format(x, y, hole))
|
||||
|
||||
|
||||
print('<svg viewBox="-1 -1 101 41" width="102mm" height="42mm" stroke-width="0.1" xmlns="http://www.w3.org/2000/svg">')
|
||||
print('<rect height="{}" width="{}" stroke="black" fill="none"/>'.format(M*L, N*L))
|
||||
for i in range(1,M):
|
||||
print('<line x1="0" y1="{}" x2="{}" y2="{}" stroke="red"/>'.format(i*L, N*L, i*L))
|
||||
for j in range(1,N):
|
||||
print('<line x1="{}" y1="0" x2="{}" y2="{}" stroke="red"/>'.format(j*L, j*L, M*L))
|
||||
HOLE = [0.1, 0.05]
|
||||
SPACE = [2.0, 1.5, 1.0, 0.75, 0.5]
|
||||
for i in range(M):
|
||||
for j in range(N):
|
||||
cushion(j*L, i*L, HOLE[i], SPACE[j])
|
||||
print('</svg>')
|
54
svgturtle.py
Normal file
54
svgturtle.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
import math
|
||||
import sys
|
||||
|
||||
class SvgTurtle():
|
||||
def __init__(self, homex=0, homey=0):
|
||||
self.homex = homex
|
||||
self.homey = homey
|
||||
self.cvtangle = math.tau/360
|
||||
self.reset()
|
||||
|
||||
def penup(self):
|
||||
self.pen = False
|
||||
|
||||
def pendown(self):
|
||||
self.pen = True
|
||||
|
||||
def forward(self, distance):
|
||||
if self.pen and (self.path == ''):
|
||||
self.path = "M %.2f,%.2f" % (self.x,self.y)
|
||||
dx = distance*math.cos(self.heading)
|
||||
dy = distance*math.sin(self.heading)
|
||||
self.x += dx
|
||||
self.y += dy
|
||||
if self.pen:
|
||||
if abs(dy) < .01:
|
||||
self.path += " h %.2f" % dx
|
||||
elif abs(dx) < .01:
|
||||
self.path += " v %.2f" % dy
|
||||
else:
|
||||
self.path += " l %.2f,%.2f" % (dx, dy)
|
||||
elif self.path != '':
|
||||
self.path += " m %.2f, %.2f" % (dx, dy)
|
||||
|
||||
def back(self, distance):
|
||||
self.forward(-distance)
|
||||
|
||||
def left(self, angle):
|
||||
self.right(-angle)
|
||||
|
||||
def right(self, angle):
|
||||
self.heading = (self.heading + angle*self.cvtangle) % math.tau
|
||||
|
||||
def to_s(self):
|
||||
return self.path
|
||||
|
||||
def home(self):
|
||||
self.x = self.homex
|
||||
self.y = self.homey
|
||||
self.heading = 0
|
||||
|
||||
def reset(self):
|
||||
self.path = ''
|
||||
self.pen = True
|
||||
self.home()
|
Loading…
Reference in New Issue
Block a user