Code Reformat #1

This commit is contained in:
schinken 2016-08-17 15:07:41 +02:00
parent af167295b8
commit 93af56ff9c
33 changed files with 1405 additions and 1097 deletions

3
.gitignore vendored
View File

@ -7,4 +7,5 @@
*.ud
build/
dist/
boxes.py.egg-info/
boxes.py.egg-info/
.idea

View File

@ -16,6 +16,7 @@
try:
import cairocffi
cairocffi.install_as_pycairo()
except ImportError:
pass
@ -31,6 +32,7 @@ from boxes import gears
from boxes import pulley
from boxes import parts
### Helpers
def dist(dx, dy):
@ -40,7 +42,8 @@ def dist(dx, dy):
:param dx: delta x
:param dy: delat y
"""
return (dx*dx+dy*dy)**0.5
return (dx * dx + dy * dy) ** 0.5
def restore(func):
"""
@ -49,6 +52,7 @@ def restore(func):
:param func: function to wrap
"""
@wraps(func)
def f(self, *args, **kw):
self.ctx.save()
@ -56,8 +60,10 @@ def restore(func):
func(self, *args, **kw)
self.ctx.restore()
self.ctx.move_to(*pt)
return f
def holeCol(func):
"""
Wrapper: color holes differently
@ -65,6 +71,7 @@ def holeCol(func):
:param func: function to wrap
"""
@wraps(func)
def f(self, *args, **kw):
self.ctx.stroke()
@ -72,8 +79,8 @@ def holeCol(func):
func(self, *args, **kw)
self.ctx.stroke()
self.ctx.set_source_rgb(0.0, 0.0, 0.0)
return f
return f
#############################################################################
@ -83,27 +90,27 @@ def holeCol(func):
class NutHole:
"""Draw a hex nut"""
sizes = {
"M1.6" : (3.2, 1.3),
"M2" : (4, 1.6),
"M2.5" : (5, 2.0),
"M3" : (5.5, 2.4),
"M4" : (7, 3.2),
"M5" : (8, 4.7),
"M6" : (10, 5.2),
"M8" : (13, 6.8),
"M10" : (16, 8.4),
"M12" : (18, 10.8),
"M14" : (21, 12.8),
"M16" : (24, 14.8),
"M20" : (30, 18.0),
"M24" : (36, 21.5),
"M30" : (46, 25.6),
"M36" : (55, 31),
"M42" : (65, 34),
"M48" : (75, 38),
"M56" : (85, 45),
"M64" : (95, 51),
}
"M1.6": (3.2, 1.3),
"M2": (4, 1.6),
"M2.5": (5, 2.0),
"M3": (5.5, 2.4),
"M4": (7, 3.2),
"M5": (8, 4.7),
"M6": (10, 5.2),
"M8": (13, 6.8),
"M10": (16, 8.4),
"M12": (18, 10.8),
"M14": (21, 12.8),
"M16": (24, 14.8),
"M20": (30, 18.0),
"M24": (36, 21.5),
"M30": (46, 25.6),
"M36": (55, 31),
"M42": (65, 34),
"M48": (75, 38),
"M56": (85, 45),
"M64": (95, 51),
}
def __init__(self, boxes, settings):
self.boxes = boxes
@ -114,13 +121,14 @@ class NutHole:
@holeCol
def __call__(self, size, x=0, y=0, angle=0):
size = self.sizes.get(size, (size,))[0]
side = size / 3**0.5
side = size / 3 ** 0.5
self.boxes.moveTo(x, y, angle)
self.boxes.moveTo(-0.5*side, 0.5*size, angle)
self.boxes.moveTo(-0.5 * side, 0.5 * size, angle)
for i in range(6):
self.boxes.edge(side)
self.boxes.corner(-60)
##############################################################################
### Argument types
##############################################################################
@ -135,17 +143,18 @@ def argparseSections(s):
m = re.match(r"(\d+(\.\d+)?)/(\d+)", s)
if m:
n = int(m.group(3))
print([ float(m.group(1)) ] * n)
return [ float(m.group(1))/n ] * n
print([float(m.group(1))] * n)
return [float(m.group(1)) / n] * n
m = re.match(r"(\d+(\.\d+)?)\*(\d+)", s)
if m:
n = int(m.group(3))
return [ float(m.group(1)) ] * n
return [float(m.group(1))] * n
try:
return [float(part) for part in s.split(":")]
except ValueError:
raise argparse.ArgumentTypeError("Don't understand sections string")
class ArgparseEdgeType:
names = edges.getDescriptions()
edges = []
@ -169,6 +178,7 @@ class ArgparseEdgeType:
e, self.names.get(e, "")) for e in self.edges))
return """<select name="%s" size="1">\n%s</select>\n""" % (name, options)
##############################################################################
### Main class
##############################################################################
@ -182,32 +192,32 @@ class Boxes:
self.formats = formats.Formats()
self.argparser = ArgumentParser(description=self.__doc__)
self.argparser.add_argument(
"--fingerjointfinger", action="store", type=float, default=1.0,
"--fingerjointfinger", action="store", type=float, default=1.0,
help="width of the fingers in multiples of thickness")
self.argparser.add_argument(
"--fingerjointspace", action="store", type=float, default=1.0,
"--fingerjointspace", action="store", type=float, default=1.0,
help="width of the space between fingers in multiples of thickness")
self.argparser.add_argument(
"--fingerjointsurrounding", action="store", type=float, default=1.0,
"--fingerjointsurrounding", action="store", type=float, default=1.0,
help="amount of space needed at the end in multiples of normal spaces")
self.argparser.add_argument(
"--thickness", action="store", type=float, default=4.0,
"--thickness", action="store", type=float, default=4.0,
help="thickness of the material")
self.argparser.add_argument(
"--output", action="store", type=str, default="box.svg",
"--output", action="store", type=str, default="box.svg",
help="name of resulting file")
self.argparser.add_argument(
"--format", action="store", type=str, default="svg",
"--format", action="store", type=str, default="svg",
choices=self.formats.getFormats(),
help="format of resulting file")
self.argparser.add_argument(
"--debug", action="store", type=bool, default=False,
"--debug", action="store", type=bool, default=False,
help="print surrounding boxes for some structures")
self.argparser.add_argument(
"--reference", action="store", type=float, default=100,
"--reference", action="store", type=float, default=100,
help="print reference rectangle with given length")
self.argparser.add_argument(
"--burn", action="store", type=float, default=0.05,
"--burn", action="store", type=float, default=0.05,
help="burn correction in mm")
def open(self):
@ -216,21 +226,21 @@ class Boxes:
Call this function from your .render() method
"""
self.spacing = 2*self.burn + 0.5 * self.thickness
self.spacing = 2 * self.burn + 0.5 * self.thickness
self.bedBoltSettings = (3, 5.5, 2, 20, 15) #d, d_nut, h_nut, l, l1
self.hexHolesSettings = (5, 3, 'circle') # r, dist, style
self.bedBoltSettings = (3, 5.5, 2, 20, 15) # d, d_nut, h_nut, l, l1
self.hexHolesSettings = (5, 3, 'circle') # r, dist, style
self.surface, self.ctx = self.formats.getSurface(self.format, self.output)
self.ctx.set_line_width(2*self.burn)
self.ctx.set_line_width(2 * self.burn)
self._buildObjects()
if self.reference:
self.move(10, 10, "up", before=True)
self.ctx.rectangle(0, 0, self.reference, 10)
if self.reference < 40:
self.text("%.fmm" % self.reference, self.reference+5, 5,
self.text("%.fmm" % self.reference, self.reference + 5, 5,
align="middle left")
else:
self.text("%.fmm" % self.reference, self.reference/2.0, 5,
self.text("%.fmm" % self.reference, self.reference / 2.0, 5,
align="middle center")
self.move(10, 10, "up")
@ -244,45 +254,45 @@ class Boxes:
for arg in l:
if arg == "x":
self.argparser.add_argument(
"--x", action="store", type=float, default=100.0,
"--x", action="store", type=float, default=100.0,
help="inner width in mm")
elif arg == "y":
self.argparser.add_argument(
"--y", action="store", type=float, default=100.0,
"--y", action="store", type=float, default=100.0,
help="inner depth in mm")
elif arg == "sx":
self.argparser.add_argument(
"--sx", action="store", type=argparseSections,
"--sx", action="store", type=argparseSections,
default="50*3",
help="""sections left to right in mm. Possible formats: overallwidth/numberof sections e.g. "250/5"; sectionwidth*numberofsections e.g. "50*5"; section widths separated by ":" e.g. "30:25.5:70"
""")
elif arg == "sy":
self.argparser.add_argument(
"--sy", action="store", type=argparseSections,
"--sy", action="store", type=argparseSections,
default="50*3",
help="""sections back to front in mm. See --sx for format""")
elif arg == "h":
self.argparser.add_argument(
"--h", action="store", type=float, default=100.0,
"--h", action="store", type=float, default=100.0,
help="inner height in mm")
elif arg == "hi":
self.argparser.add_argument(
"--hi", action="store", type=float, default=0.0,
"--hi", action="store", type=float, default=0.0,
help="inner height of inner walls in mm (leave to zero for same as outer walls)")
elif arg == "bottom_edge":
self.argparser.add_argument(
"--bottom_edge", action="store",
"--bottom_edge", action="store",
type=ArgparseEdgeType("Fhs"), choices=list("Fhs"),
default="h",
help="edge type for bottom edge")
elif arg == "top_edge":
self.argparser.add_argument(
"--top_edge", action="store",
"--top_edge", action="store",
type=ArgparseEdgeType("ecESik"), choices=list("ecESik"),
default="e", help="edge type for top edge")
elif arg=="outside":
elif arg == "outside":
self.argparser.add_argument(
"--outside", action="store", type=bool, default=False,
"--outside", action="store", type=bool, default=False,
help="treat sizes as outside measurements that include the walls")
else:
raise ValueError("No default for argument", arg)
@ -294,7 +304,7 @@ class Boxes:
:param args: (Default value = None) parameters, None for using sys.argv
"""
for key,value in vars(self.argparser.parse_args(args=args)).items():
for key, value in vars(self.argparser.parse_args(args=args)).items():
setattr(self, key, value)
# Change file ending to format if not given explicitly
@ -312,7 +322,7 @@ class Boxes:
if name is None:
name = part.__class__.__name__
name = name[0].lower() + name[1:]
#if not hasattr(self, name):
# if not hasattr(self, name):
if isinstance(part, edges.BaseEdge):
self.edges[part.char] = part
else:
@ -354,9 +364,11 @@ class Boxes:
self.addPart(edges.ClickEdge(self, s))
# Hinges
s = edges.HingeSettings(self.thickness)
for i in range(1, 4):
self.addPart(edges.Hinge(self, s, i))
self.addPart(edges.HingePin(self, s, i))
# Nuts
self.addPart(NutHole(self, None))
# Gears
@ -367,7 +379,7 @@ class Boxes:
def adjustSize(self, l, e1=True, e2=True):
try:
total = sum(l)
walls = (len(l)-1) * self.thickness
walls = (len(l) - 1) * self.thickness
except TypeError:
total = l
walls = 0
@ -376,14 +388,15 @@ class Boxes:
walls += e1.startwidth() + e1.margin()
elif e1:
walls += self.thickness
if isinstance(e2, edges.BaseEdge):
walls += e2.startwidth + e2.margin()
elif e2:
walls += self.thickness
try:
factor = (total-walls) / total
return [s*factor for s in l]
factor = (total - walls) / total
return [s * factor for s in l]
except TypeError:
return l - walls
@ -414,6 +427,7 @@ class Boxes:
callback()
else:
callback(number)
elif hasattr(callback, '__getitem__'):
try:
callback = callback[number]
@ -424,6 +438,7 @@ class Boxes:
except:
self.ctx.restore()
raise
self.ctx.restore()
def getEntry(self, param, idx):
@ -435,7 +450,7 @@ class Boxes:
"""
if isinstance(param, list):
if len(param)>idx:
if len(param) > idx:
return param[idx]
else:
return None
@ -466,16 +481,16 @@ class Boxes:
:param radius: (Default value = 0)
"""
rad = degrees*math.pi/180
rad = degrees * math.pi / 180
if degrees > 0:
self.ctx.arc(0, radius+self.burn, radius+self.burn,
-0.5*math.pi, rad - 0.5*math.pi)
self.ctx.arc(0, radius + self.burn, radius + self.burn,
-0.5 * math.pi, rad - 0.5 * math.pi)
elif radius > self.burn:
self.ctx.arc_negative(0, -(radius-self.burn), radius-self.burn,
0.5*math.pi, rad + 0.5*math.pi)
else: # not rounded inner corner
self.ctx.arc_negative(0, self.burn-radius, self.burn-radius,
-0.5*math.pi, -0.5*math.pi+rad)
self.ctx.arc_negative(0, -(radius - self.burn), radius - self.burn,
0.5 * math.pi, rad + 0.5 * math.pi)
else: # not rounded inner corner
self.ctx.arc_negative(0, self.burn - radius, self.burn - radius,
-0.5 * math.pi, -0.5 * math.pi + rad)
self.continueDirection(rad)
@ -485,7 +500,7 @@ class Boxes:
:param length: length in mm
"""
self.ctx.move_to(0,0)
self.ctx.move_to(0, 0)
self.ctx.line_to(length, 0)
self.ctx.translate(*self.ctx.get_current_point())
@ -501,8 +516,8 @@ class Boxes:
"""
self.ctx.curve_to(x1, y1, x2, y2, x3, y3)
dx = x3-x2
dy = y3-y2
dx = x3 - x2
dy = y3 - y2
rad = math.atan2(dy, dx)
self.continueDirection(rad)
@ -532,37 +547,37 @@ class Boxes:
"""
d, d_nut, h_nut, l, l1 = bedBoltSettings or self.bedBoltSettings
self.edge((length-d)/2.0)
self.edge((length - d) / 2.0)
self.corner(90)
self.edge(l1)
self.corner(90)
self.edge((d_nut-d)/2.0)
self.edge((d_nut - d) / 2.0)
self.corner(-90)
self.edge(h_nut)
self.corner(-90)
self.edge((d_nut-d)/2.0)
self.edge((d_nut - d) / 2.0)
self.corner(90)
self.edge(l-l1-h_nut)
self.edge(l - l1 - h_nut)
self.corner(-90)
self.edge(d)
self.corner(-90)
self.edge(l-l1-h_nut)
self.edge(l - l1 - h_nut)
self.corner(90)
self.edge((d_nut-d)/2.0)
self.edge((d_nut - d) / 2.0)
self.corner(-90)
self.edge(h_nut)
self.corner(-90)
self.edge((d_nut-d)/2.0)
self.edge((d_nut - d) / 2.0)
self.corner(90)
self.edge(l1)
self.corner(90)
self.edge((length-d)/2.0)
self.edge((length - d) / 2.0)
def edgeCorner(self, edge1, edge2, angle=90):
"""Make a corner between two Edges. Take width of edges into account"""
self.edge(edge2.startwidth()/math.sin(math.radians(180-angle)))
self.edge(edge2.startwidth() / math.sin(math.radians(180 - angle)))
self.corner(angle)
self.edge(edge1.endwidth()/math.sin(math.radians(180-angle)))
self.edge(edge1.endwidth() / math.sin(math.radians(180 - angle)))
def grip(self, length, depth):
"""Corrugated edge useful as an gipping area
@ -571,7 +586,7 @@ class Boxes:
:param depth: depth of the grooves
"""
grooves = int(length // (depth*2.0)) + 1
grooves = int(length // (depth * 2.0)) + 1
depth = length / grooves / 4.0
for groove in range(grooves):
self.corner(90, depth)
@ -584,11 +599,11 @@ class Boxes:
:param length:
"""
self.edge(1.1*self.thickness)
self.edge(1.1 * self.thickness)
self.corner(-90)
self.edge(length/2.0+0.2*self.thickness)
self.edge(length / 2.0 + 0.2 * self.thickness)
self.corner(-90)
self.edge(1.1*self.thickness)
self.edge(1.1 * self.thickness)
def _latchGrip(self, length):
"""
@ -596,9 +611,9 @@ class Boxes:
:param length:
"""
self.corner(90, self.thickness/4.0)
self.grip(length/2.0-self.thickness/2.0-0.2*self.thickness, self.thickness/2.0)
self.corner(90, self.thickness/4.0)
self.corner(90, self.thickness / 4.0)
self.grip(length / 2.0 - self.thickness / 2.0 - 0.2 * self.thickness, self.thickness / 2.0)
self.corner(90, self.thickness / 4.0)
def latch(self, length, positive=True, reverse=False):
"""Latch to fix a flex box door to the box
@ -610,16 +625,16 @@ class Boxes:
"""
if positive:
if reverse:
self.edge(length/2.0-self.burn)
self.edge(length / 2.0 - self.burn)
self.corner(-90)
self.edge(self.thickness)
self.corner(90)
self.edge(length/2.0)
self.edge(length / 2.0)
self.corner(90)
self.edge(self.thickness)
self.corner(-90)
if not reverse:
self.edge(length/2.0-self.burn)
self.edge(length / 2.0 - self.burn)
else:
if reverse:
self._latchGrip(length)
@ -640,31 +655,31 @@ class Boxes:
:param r: (Default value = 30) radius of the corners
"""
d = (x-hl-2*r)/2.0
d = (x - hl - 2 * r) / 2.0
if d < 0:
print("Handle too wide")
self.ctx.save()
# Hole
self.moveTo(d+2*r, 0)
self.edge(hl-2*r)
self.moveTo(d + 2 * r, 0)
self.edge(hl - 2 * r)
self.corner(-90, r)
self.edge(h-3*r)
self.edge(h - 3 * r)
self.corner(-90, r)
self.edge(hl-2*r)
self.edge(hl - 2 * r)
self.corner(-90, r)
self.edge(h-3*r)
self.edge(h - 3 * r)
self.corner(-90, r)
self.ctx.restore()
self.moveTo(0,0)
self.moveTo(0, 0)
self.curveTo(d, 0, d, 0, d, -h+r)
self.curveTo(d, 0, d, 0, d, -h + r)
self.curveTo(r, 0, r, 0, r, r)
self.edge(hl)
self.curveTo(r, 0, r, 0, r, r)
self.curveTo(h-r, 0, h-r, 0, h-r, -d)
self.curveTo(h - r, 0, h - r, 0, h - r, -d)
### Navigation
@ -679,7 +694,7 @@ class Boxes:
"""
self.ctx.move_to(0, 0)
self.ctx.translate(x, y)
self.ctx.rotate(degrees*math.pi/180.0)
self.ctx.rotate(degrees * math.pi / 180.0)
self.ctx.move_to(0, 0)
def continueDirection(self, angle=0):
@ -715,11 +730,11 @@ class Boxes:
y += self.spacing
moves = {
"up": (0, y, False),
"down" : (0, -y, True),
"left" : (-x, 0, True),
"right" : (x, 0, False),
"only" : (0, 0, None),
}
"down": (0, -y, True),
"left": (-x, 0, True),
"right": (x, 0, False),
"only": (0, 0, None),
}
if not before:
# restore position
@ -737,7 +752,7 @@ class Boxes:
if before:
# save position
self.ctx.save()
self.moveTo(self.spacing/2.0, self.spacing/2.0)
self.moveTo(self.spacing / 2.0, self.spacing / 2.0)
return dontdraw
@restore
@ -754,8 +769,8 @@ class Boxes:
r -= self.burn
if r < 0:
r = 1E-9
self.moveTo(x+r, y)
self.ctx.arc(-r, 0, r, 0, 2*math.pi)
self.moveTo(x + r, y)
self.ctx.arc(-r, 0, r, 0, 2 * math.pi)
@restore
@holeCol
@ -770,10 +785,10 @@ class Boxes:
:param r: (Default value = 0) radius of the corners
"""
self.moveTo(x+r-dx/2.0, y-dy/2.0, 180)
self.moveTo(x + r - dx / 2.0, y - dy / 2.0, 180)
for d in (dy, dx, dy, dx):
self.corner(-90, r)
self.edge(d-2*r)
self.edge(d - 2 * r)
@restore
def text(self, text, x=0, y=0, angle=0, align=""):
@ -791,12 +806,12 @@ class Boxes:
(tx, ty, width, height, dx, dy) = self.ctx.text_extents(text)
align = align.split()
moves = {
"top" : (0, -height),
"middle" : (0, -0.5*height),
"bottom" : (0, 0),
"left" : (0, 0),
"center" : (-0.5*width, 0),
"right" : (-width, 0),
"top": (0, -height),
"middle": (0, -0.5 * height),
"bottom": (0, 0),
"left": (0, 0),
"center": (-0.5 * width, 0),
"right": (-width, 0),
}
for a in align:
if a in moves:
@ -819,27 +834,26 @@ class Boxes:
"""
nema = {
# motor,flange, holes, screws
8 : (20.3, 16, 15.4, 3),
11 : (28.2, 22, 23, 4),
14 : (35.2, 22, 26, 4),
16 : (39.2, 22, 31, 4),
17 : (42.2, 22, 31, 4),
23 : (56.4, 38.1, 47.1, 5.2),
24 : (60, 36, 49.8, 5.1),
34 : (86.3, 73, 69.8, 6.6),
42 : (110, 55.5, 89, 8.5),
}
8: (20.3, 16, 15.4, 3),
11: (28.2, 22, 23, 4),
14: (35.2, 22, 26, 4),
16: (39.2, 22, 31, 4),
17: (42.2, 22, 31, 4),
23: (56.4, 38.1, 47.1, 5.2),
24: (60, 36, 49.8, 5.1),
34: (86.3, 73, 69.8, 6.6),
42: (110, 55.5, 89, 8.5),
}
width, flange, holedistance, diameter = nema[size]
self.moveTo(x, y, angle)
if self.debug:
self.rectangularHole(0, 0, width, width)
self.hole(0,0, 0.5*flange)
self.hole(0, 0, 0.5 * flange)
for x in (-1, 1):
for y in (-1, 1):
self.hole(x*0.5*holedistance,
y*0.5*holedistance,
0.5*diameter)
self.hole(x * 0.5 * holedistance,
y * 0.5 * holedistance,
0.5 * diameter)
# hexHoles
@ -863,21 +877,21 @@ class Boxes:
settings = self.hexHolesSettings
r, b, style = settings
w = r+b/2.0
dist = w * math.cos(math.pi/6.0)
w = r + b / 2.0
dist = w * math.cos(math.pi / 6.0)
# how many half circles do fit
cx = int((x-2*r) // (w)) + 2
cy = int((y-2*r) // (dist)) + 2
cx = int((x - 2 * r) // (w)) + 2
cy = int((y - 2 * r) // (dist)) + 2
# what's left on the sides
lx = (x - (2*r+(cx-2)*w))/2.0
ly = (y - (2*r+((cy//2)*2)*dist-2*dist))/2.0
lx = (x - (2 * r + (cx - 2) * w)) / 2.0
ly = (y - (2 * r + ((cy // 2) * 2) * dist - 2 * dist)) / 2.0
for i in range(cy//2):
for j in range((cx-(i%2))//2):
px = 2*j*w + r + lx
py = i*2*dist + r + ly
for i in range(cy // 2):
for j in range((cx - (i % 2)) // 2):
px = 2 * j * w + r + lx
py = i * 2 * dist + r + ly
if i % 2:
px += w
if skip and skip(x, y, r, b, px, py):
@ -885,8 +899,8 @@ class Boxes:
self.hole(px, py, r)
def __skipcircle(self, x, y, r, b, posx, posy):
cx, cy = x/2.0, y/2.0
return (dist(posx-cx, posy-cy) > (cx-r))
cx, cy = x / 2.0, y / 2.0
return (dist(posx - cx, posy - cy) > (cx - r))
def hexHolesCircle(self, d, settings=None):
"""
@ -896,7 +910,7 @@ class Boxes:
:param settings: (Default value = None)
"""
d2 = d/2.0
d2 = d / 2.0
self.hexHolesRectangle(d, d, settings=settings, skip=self.__skipcircle)
def hexHolesPlate(self, x, y, rc, settings=None):
@ -909,6 +923,7 @@ class Boxes:
:param settings: (Default value = None)
"""
def skip(x, y, r, b, posx, posy):
"""
@ -920,15 +935,15 @@ class Boxes:
:param posy:
"""
posx = abs(posx-(x/2.0))
posy = abs(posy-(y/2.0))
posx = abs(posx - (x / 2.0))
posy = abs(posy - (y / 2.0))
wx = 0.5*x-rc-r
wy = 0.5*y-rc-r
wx = 0.5 * x - rc - r
wy = 0.5 * y - rc - r
if (posx <= wx) or (posy <= wx):
return 0
return dist(posx-wx, posy-wy) > rc
return dist(posx - wx, posy - wy) > rc
self.hexHolesRectangle(x, y, settings, skip=skip)
@ -946,53 +961,53 @@ class Boxes:
r, b, style = settings
self.ctx.rectangle(0, 0, h, h)
w = r+b/2.0
dist = w * math.cos(math.pi/6.0)
cy = 2 * int((h-4*dist)// (4*w)) + 1
w = r + b / 2.0
dist = w * math.cos(math.pi / 6.0)
cy = 2 * int((h - 4 * dist) // (4 * w)) + 1
leftover = h-2*r-(cy-1)*2*r
if grow=='space ':
b += leftover / (cy-1) / 2
leftover = h - 2 * r - (cy - 1) * 2 * r
if grow == 'space ':
b += leftover / (cy - 1) / 2
# recalulate with adjusted values
w = r+b/2.0
dist = w * math.cos(math.pi/6.0)
w = r + b / 2.0
dist = w * math.cos(math.pi / 6.0)
self.moveTo(h/2.0-(cy//2)*2*w, h/2.0)
self.moveTo(h / 2.0 - (cy // 2) * 2 * w, h / 2.0)
for j in range(cy):
self.hole(2*j*w, 0, r)
for i in range(1, cy/2+1):
for j in range(cy-i):
self.hole(j*2*w+i*w, i*2*dist, r)
self.hole(j*2*w+i*w, -i*2*dist, r)
self.hole(2 * j * w, 0, r)
for i in range(1, cy / 2 + 1):
for j in range(cy - i):
self.hole(j * 2 * w + i * w, i * 2 * dist, r)
self.hole(j * 2 * w + i * w, -i * 2 * dist, r)
def flex2D(self, x, y, width=1):
width *= self.thickness
cx = int(x // (5*width))
cx = int(x // (5 * width))
wx = x / 5. / cx
cy = int(y // (5*width))
cy = int(y // (5 * width))
wy = y / 5. / cy
armx = (4*wx, 90, 4*wy, 90, 2*wx, 90, 2*wy)
army = (4*wy, 90, 4*wx, 90, 2*wy, 90, 2*wx)
armx = (4 * wx, 90, 4 * wy, 90, 2 * wx, 90, 2 * wy)
army = (4 * wy, 90, 4 * wx, 90, 2 * wy, 90, 2 * wx)
for i in range(cx):
for j in range(cy):
if (i+j) % 2:
if (i + j) % 2:
self.ctx.save()
self.moveTo((5*i)*wx, (5*j)*wy)
self.moveTo((5 * i) * wx, (5 * j) * wy)
self.polyline(*armx)
self.ctx.restore()
self.ctx.save()
self.moveTo((5*i+5)*wx, (5*j+5)*wy, -180)
self.moveTo((5 * i + 5) * wx, (5 * j + 5) * wy, -180)
self.polyline(*armx)
self.ctx.restore()
else:
self.ctx.save()
self.moveTo((5*i+5)*wx, (5*j)*wy, 90)
self.moveTo((5 * i + 5) * wx, (5 * j) * wy, 90)
self.polyline(*army)
self.ctx.restore()
self.ctx.save()
self.moveTo((5*i)*wx, (5*j+5)*wy, -90)
self.moveTo((5 * i) * wx, (5 * j + 5) * wy, -90)
self.polyline(*army)
self.ctx.restore()
self.ctx.stroke()
@ -1023,8 +1038,8 @@ class Boxes:
"""
overallwidth = x+2*self.edges["f"].spacing()
overallheight = y+2*self.edges["f"].spacing()
overallwidth = x + 2 * self.edges["f"].spacing()
overallheight = y + 2 * self.edges["f"].spacing()
if self.move(overallwidth, overallheight, move, before=True):
return
@ -1034,16 +1049,16 @@ class Boxes:
self.moveTo(r, 0)
self.cc(callback, 0)
self.edges["f"](x/2.0-r, bedBolts=self.getEntry(bedBolts, 0),
bedBoltSettings=self.getEntry(bedBoltSettings, 0))
self.edges["f"](x / 2.0 - r, bedBolts=self.getEntry(bedBolts, 0),
bedBoltSettings=self.getEntry(bedBoltSettings, 0))
self.cc(callback, 1)
self.edges["f"](x/2.0-r, bedBolts=self.getEntry(bedBolts, 1),
bedBoltSettings=self.getEntry(bedBoltSettings, 1))
self.edges["f"](x / 2.0 - r, bedBolts=self.getEntry(bedBolts, 1),
bedBoltSettings=self.getEntry(bedBoltSettings, 1))
for i, l in zip(range(3), (y, x, y)):
self.corner(90, r)
self.cc(callback, i+2)
self.edges["f"](l-2*r, bedBolts=self.getEntry(bedBolts, i+2),
bedBoltSettings=self.getEntry(bedBoltSettings, i+2))
self.cc(callback, i + 2)
self.edges["f"](l - 2 * r, bedBolts=self.getEntry(bedBolts, i + 2),
bedBoltSettings=self.getEntry(bedBoltSettings, i + 2))
self.corner(90, r)
self.ctx.restore()
@ -1058,7 +1073,7 @@ class Boxes:
r -= holesMargin
else:
r = 0
self.hexHolesPlate(x-2*holesMargin, y-2*holesMargin, r,
self.hexHolesPlate(x - 2 * holesMargin, y - 2 * holesMargin, r,
settings=holesSettings)
self.ctx.stroke()
self.move(overallwidth, overallheight, move)
@ -1086,7 +1101,7 @@ class Boxes:
:param move: (Default value = None)
"""
c4 = (r+self.burn)*math.pi*0.5 # circumference of quarter circle
c4 = (r + self.burn) * math.pi * 0.5 # circumference of quarter circle
c4 = c4 / self.edges["X"].settings.stretch
top = self.edges.get(top, top)
@ -1098,42 +1113,41 @@ class Boxes:
topwidth = top.startwidth()
bottomwidth = bottom.startwidth()
overallwidth = 2*x + 2*y - 8*r + 4*c4 + \
self.edges["d"].spacing() + self.edges["D"].spacing()
overallwidth = 2 * x + 2 * y - 8 * r + 4 * c4 + \
self.edges["d"].spacing() + self.edges["D"].spacing()
overallheight = h + top.spacing() + bottom.spacing()
if self.move(overallwidth, overallheight, move, before=True):
return
self.moveTo(left.spacing(), bottom.margin())
self.cc(callback, 0, y=bottomwidth+self.burn)
bottom(x/2.0-r)
if (y-2*r) < 1E-3:
self.edges["X"](2*c4, h+topwidth+bottomwidth)
self.cc(callback, 2, y=bottomwidth+self.burn)
bottom(x-2*r)
self.edges["X"](2*c4, h+topwidth+bottomwidth)
self.cc(callback, 4, y=bottomwidth+self.burn)
self.cc(callback, 0, y=bottomwidth + self.burn)
bottom(x / 2.0 - r)
if (y - 2 * r) < 1E-3:
self.edges["X"](2 * c4, h + topwidth + bottomwidth)
self.cc(callback, 2, y=bottomwidth + self.burn)
bottom(x - 2 * r)
self.edges["X"](2 * c4, h + topwidth + bottomwidth)
self.cc(callback, 4, y=bottomwidth + self.burn)
else:
for i, l in zip(range(4), (y, x, y, 0)):
self.edges["X"](c4, h+topwidth+bottomwidth)
self.cc(callback, i+1, y=bottomwidth+self.burn)
self.edges["X"](c4, h + topwidth + bottomwidth)
self.cc(callback, i + 1, y=bottomwidth + self.burn)
if i < 3:
bottom(l-2*r)
bottom(x/2.0-r)
bottom(l - 2 * r)
bottom(x / 2.0 - r)
self.edgeCorner(bottom, right, 90)
right(h)
self.edgeCorner(right, top, 90)
top(x/2.0-r)
top(x / 2.0 - r)
for i, l in zip(range(4), (y, x, y, 0)):
self.edge(c4)
if i < 3:
top(l - 2*r)
top(x/2.0-r)
top(l - 2 * r)
top(x / 2.0 - r)
self.edgeCorner(top, left, 90)
left(h)
@ -1165,7 +1179,7 @@ class Boxes:
if len(edges) != 4:
raise ValueError("four edges required")
edges = [self.edges.get(e, e) for e in edges]
edges += edges # append for wrapping around
edges += edges # append for wrapping around
overallwidth = x + edges[-1].spacing() + edges[1].spacing()
overallheight = y + edges[0].spacing() + edges[2].spacing()
@ -1174,16 +1188,16 @@ class Boxes:
self.moveTo(edges[-1].spacing(), edges[0].margin())
for i, l in enumerate((x, y, x, y)):
self.cc(callback, i, y=edges[i].startwidth()+self.burn)
self.cc(callback, i, y=edges[i].startwidth() + self.burn)
edges[i](l,
bedBolts=self.getEntry(bedBolts, i),
bedBoltSettings=self.getEntry(bedBoltSettings, i))
self.edgeCorner(edges[i], edges[i+1], 90)
self.edgeCorner(edges[i], edges[i + 1], 90)
if holesMargin is not None:
self.moveTo(holesMargin+edges[-1].endwidth(),
holesMargin+edges[0].startwidth())
self.hexHolesRectangle(x-2*holesMargin, y-2*holesMargin)
self.moveTo(holesMargin + edges[-1].endwidth(),
holesMargin + edges[0].startwidth())
self.hexHolesRectangle(x - 2 * holesMargin, y - 2 * holesMargin)
self.ctx.stroke()

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,3 @@
import subprocess
import tempfile
import os
@ -6,8 +5,8 @@ import cairo
import re
from boxes import svgutil
class PSFile:
class PSFile:
def __init__(self, filename):
self.filename = filename
@ -15,38 +14,40 @@ class PSFile:
with open(self.filename, "r+") as f:
s = f.read(1024)
m = re.search(r"%%BoundingBox: (\d+) (\d+) (\d+) (\d+)", s)
if not m:
raise ValueError("%%BoundingBox in Postscript file not found")
x1, y1, x2, y2 = m.groups()
m = re.search(r"%%DocumentMedia: \d+x\d+mm ((\d+) (\d+)) 0 \(", s)
f.seek(m.start(1))
media = "%i %i" % (int(x1)+int(x2), int(y1)+int(y2))
f.write(media + " " * (len(m.group(1))-len(media)))
media = "%i %i" % (int(x1) + int(x2), int(y1) + int(y2))
f.write(media + " " * (len(m.group(1)) - len(media)))
class Formats:
pstoedit = "/usr/bin/pstoedit"
formats = {
"svg" : None,
"ps" : None,
"dxf" : "-flat 0.1 -f dxf:-mm".split(),
"gcode" : "-f gcode".split(),
"plt" : "-f plot-hpgl".split(),
"ai" : "-f ps2ai".split(),
"pdf" : "-f pdf".split(),
"svg": None,
"ps": None,
"dxf": "-flat 0.1 -f dxf:-mm".split(),
"gcode": "-f gcode".split(),
"plt": "-f plot-hpgl".split(),
"ai": "-f ps2ai".split(),
"pdf": "-f pdf".split(),
}
http_headers = {
"svg" : [('Content-type', 'image/svg+xml; charset=utf-8')],
"ps" : [('Content-type', 'application/postscript')],
"dxf" : [('Content-type', 'image/vnd.dxf')],
"plt" : [('Content-type', ' application/vnd.hp-hpgl')],
"gcode" : [('Content-type', 'text/plain; charset=utf-8')],
"svg": [('Content-type', 'image/svg+xml; charset=utf-8')],
"ps": [('Content-type', 'application/postscript')],
"dxf": [('Content-type', 'image/vnd.dxf')],
"plt": [('Content-type', ' application/vnd.hp-hpgl')],
"gcode": [('Content-type', 'text/plain; charset=utf-8')],
# "" : [('Content-type', '')],
}
def __init__(self):
pass
@ -58,7 +59,7 @@ class Formats:
def getSurface(self, fmt, filename):
width = height = 10000 # mm
width = height = 10000 # mm
if fmt == "svg":
surface = cairo.SVGSurface(filename, width, height)
@ -66,7 +67,7 @@ class Formats:
else:
mm2pt = 72 / 25.4
width *= mm2pt
height *= mm2pt #3.543307
height *= mm2pt # 3.543307
surface = cairo.PSSurface(filename, width, height)
ctx = cairo.Context(surface)
@ -86,11 +87,14 @@ class Formats:
else:
ps = PSFile(filename)
ps.adjustDocumentMedia()
if fmt not in ("svg", "ps"):
fd, tmpfile = tempfile.mkstemp()
cmd = [self.pstoedit] + self.formats[fmt] + [filename, tmpfile]
err = subprocess.call(cmd)
if err:
# XXX show stderr output
raise ValueError("Conversion failed. pstoedit returned %i" % err)
os.rename(tmpfile, filename)

View File

@ -107,26 +107,22 @@ def gear_calculations(num_teeth, circular_pitch, pressure_angle, clearance=0, ri
pitch_diameter = num_teeth / diametral_pitch
pitch_radius = pitch_diameter / 2.0
addendum = 1 / diametral_pitch
#dedendum = 1.157 / diametral_pitch # auto calc clearance
dedendum = addendum
dedendum *= 1+profile_shift
addendum *= 1-profile_shift
if ring_gear:
addendum = addendum + clearance # our method
else:
dedendum = dedendum + clearance # our method
#
#
base_radius = pitch_diameter * cos(radians(pressure_angle)) / 2.0
outer_radius = pitch_radius + addendum
root_radius = pitch_radius - dedendum
# Tooth thickness: Tooth width along pitch circle.
tooth_thickness = ( pi * pitch_diameter ) / ( 2.0 * num_teeth )
# we don't use these
working_depth = 2 / diametral_pitch
whole_depth = 2.157 / diametral_pitch
#outside_diameter = (num_teeth + 2) / diametral_pitch
#
return (pitch_radius, base_radius,
addendum, dedendum, outer_radius, root_radius,
tooth_thickness
@ -146,6 +142,7 @@ def generate_rack_points(tooth_count, pitch, addendum, pressure_angle,
(one extra tooth on the right hand side, if number of teeth is even)
"""
spacing = 0.5 * pitch # rolling one pitch distance on the spur gear pitch_diameter.
# roughly center rack in drawing, exact position is so that it meshes
# nicely with the spur gear.
# -0.5*spacing has a gap in the center.
@ -158,7 +155,7 @@ def generate_rack_points(tooth_count, pitch, addendum, pressure_angle,
base_bot = addendum+clearance+base_height
x_lhs = -pitch * int(0.5*tooth_count-.5) - spacing - tab_length - tasc + fudge
#inkex.debug("angle=%s spacing=%s"%(pressure_angle, spacing))
# Start with base tab on LHS
points = [] # make list of points
points.append((x_lhs, base_bot))
@ -175,6 +172,7 @@ def generate_rack_points(tooth_count, pitch, addendum, pressure_angle,
points.append((x+spacing-tas, -addendum))
points.append((x+spacing+tasc, base_top))
x += pitch
x -= spacing # remove last adjustment
# add base on RHS
x_rhs = x+tasc+tab_length
@ -189,7 +187,7 @@ def generate_rack_points(tooth_count, pitch, addendum, pressure_angle,
p = []
p.append( (x_lhs + 0.5 * tab_length, 0) )
p.append( (x_rhs - 0.5 * tab_length, 0) )
# return points ready for use in an SVG 'path'
return (points, p)
@ -233,6 +231,7 @@ def generate_spur_points(teeth, base_radius, pitch_radius, outer_radius, root_ra
p_tmp = points1 + points_on_outer_radius[1:-1] + points2[::-1] + points_on_root # [::-1] reverses list
points.extend( p_tmp )
return (points)
def inkbool(val):
@ -257,6 +256,7 @@ class OptionParser(argparse.ArgumentParser):
self.add_argument(*names, **kw)
class Gears():
def __init__(self, boxes, **kw):
# an alternate way to get debug info:
# could use inkex.debug(string) instead...
@ -385,12 +385,16 @@ class Gears():
help="Let the user confirm a warning dialog if undercut occurs. This dialog also shows helpful hints against undercut")
def drawPoints(self, lines, kerfdir=1):
if kerfdir != 0:
lines = kerf(lines, self.boxes.burn*kerfdir)
self.boxes.ctx.save()
self.boxes.ctx.move_to(*lines[0])
for x, y in lines[1:]:
self.boxes.ctx.line_to(x, y)
self.boxes.ctx.line_to(*lines[0])
self.boxes.ctx.restore()
@ -406,6 +410,7 @@ class Gears():
circular_pitch = dimension
else:
raise ValueError("unknown system '%s', try CP, DP, MM" % self.options.system)
# circular_pitch defines the size in mm
return circular_pitch
@ -436,6 +441,7 @@ class Gears():
# check for mount hole collision with inner spokes
if mount_radius <= mount_hole/2:
adj_factor = (r_outer - mount_hole/2) / 5
if adj_factor < 0.1:
# not enough reasonable room
collision = True
@ -446,6 +452,7 @@ class Gears():
# then check to see if cross-over on spoke width
for i in range(spoke_count):
angle = spokes[i]-spokes[i-1]
if spoke_width >= angle * mount_radius:
adj_factor = 1.2 # wrong value. its probably one of the points distances calculated below
mount_radius += adj_factor
@ -458,6 +465,7 @@ class Gears():
if collision: # don't draw spokes if no room.
messages.append("Not enough room for Spokes. Decrease Spoke width.")
else: # draw spokes
for i in range(spoke_count):
self.boxes.ctx.save()
start_a, end_a = spokes[i], spokes[i+1]
@ -486,6 +494,7 @@ class Gears():
)
self.boxes.ctx.restore()
return messages
def sizes(self, **kw):
@ -517,8 +526,10 @@ class Gears():
def gearCarrier(self, r, spoke_width, positions, mount_radius, mount_hole, circle=True, move=None):
width = (r+spoke_width)*2
if self.boxes.move(width, width, move, before=True):
return
try:
positions = [i*360/positions for i in range(positions)]
except TypeError:
@ -528,11 +539,13 @@ class Gears():
self.boxes.moveTo(width/2.0, width/2.0)
self.generate_spokes(r+0.5*spoke_width, spoke_width, positions, mount_radius, mount_hole, 1, "")
self.boxes.hole(0, 0, mount_hole)
for angle in positions:
self.boxes.ctx.save()
self.boxes.moveTo(0, 0, angle)
self.boxes.hole(r, 0, mount_hole)
self.boxes.ctx.restore()
self.boxes.moveTo(r+0.5*spoke_width+self.boxes.burn, 0, 90)
self.boxes.corner(360, r+0.5*spoke_width)
@ -578,6 +591,7 @@ class Gears():
else: accuracy_involute = 6
else:
accuracy_involute = self.options.accuracy
accuracy_circular = max(3, int(accuracy_involute/2) - 1) # never less than three
# print >>self.tty, "accuracy_circular=%s accuracy_involute=%s" % (accuracy_circular, accuracy_involute)
# Pitch (circular pitch): Length of the arc from one tooth to the next)
@ -599,17 +613,20 @@ class Gears():
height = base_height+ 2* addendum
if self.boxes.move(width, height, move, before=True):
return
self.boxes.cc(callback, None, s+b, s+b)
self.boxes.moveTo(width/2.0, base_height+addendum, -180)
self.drawPoints(points)
self.drawPoints(guide_points, kerfdir=0)
self.boxes.move(width, height, move)
return
# Move only
width = height = 2 * outer_radius
if self.options.internal_ring:
width = height = width + 2 * self.options.spoke_width
if self.boxes.move(width, height, move, before=True):
return
@ -624,6 +641,7 @@ class Gears():
# alas annotation cannot handle the degree symbol. Also it ignore newlines.
# so split and make a list
warnings.extend(msg.split("\n"))
if self.options.undercut_alert:
inkex.debug(msg)
else:
@ -653,6 +671,7 @@ class Gears():
self.boxes.moveTo(r, 0)
self.boxes.ctx.arc(-r, 0, r, 0, 2*pi)
self.boxes.ctx.restore()
# Add center
if centercross:
cs = pitch / 3.0 # centercross length
@ -667,12 +686,13 @@ class Gears():
if pitchcircle:
self.boxes.hole(0, 0, pitch_radius)
# Add Annotations (above)
if self.options.annotation:
outer_dia = outer_radius * 2
if self.options.internal_ring:
outer_dia += 2 * spoke_width
notes = []
notes.extend(warnings)
#notes.append('Document (%s) scale conversion = %2.4f' % (self.document.getroot().find(inkex.addNS('namedview', 'sodipodi')).get(inkex.addNS('document-units', 'inkscape')), unit_factor))
@ -690,6 +710,7 @@ class Gears():
text_height = max(10, min(10+(outer_dia-60)/24, 22))
# position above
y = - outer_radius - (len(notes)+1) * text_height * 1.2
for note in notes:
self.boxes.text(note, -outer_radius, y)
y += text_height * 1.2

View File

@ -16,15 +16,17 @@
from boxes import *
class Box(Boxes):
"""Fully closed box"""
def __init__(self):
Boxes.__init__(self)
self.buildArgParser("x", "y", "h", "outside")
self.argparser.set_defaults(
fingerjointfinger=3.0,
fingerjointspace=3.0
)
)
def render(self):
self.open()
@ -47,16 +49,18 @@ class Box(Boxes):
self.rectangularWall(y, h, "FfFf", bedBolts=d3, move="up")
self.rectangularWall(y, h, "FfFf", bedBolts=d3)
self.rectangularWall(x, h, "FFFF", bedBolts=d2, move="left up")
self.rectangularWall(x, y, "ffff", bedBolts=[d2, d3, d2, d3], move="right")
self.rectangularWall(x, y, "ffff", bedBolts=[d2, d3, d2, d3])
self.close()
def main():
b = Box()
b.parseArgs()
b.render()
if __name__ == '__main__':
main()

View File

@ -120,6 +120,7 @@ class Box2(Boxes):
bedBolts=[d2], move="up only")
self.rectangularWall(x, y, "ffff", bedBolts=[d2, d3, d2, d3], move="right")
if self.top_edge == "c":
self.rectangularWall(x, y, "CCCC", bedBolts=[d2, d3, d2, d3], move="up")
elif self.top_edge == "i":

View File

@ -18,6 +18,7 @@ from boxes import *
class Box3(Boxes):
"""Box with just 3 walls"""
def __init__(self):
Boxes.__init__(self)
self.buildArgParser("x", "y", "h", "outside")

View File

@ -16,8 +16,8 @@
from boxes import *
class Castle(Boxes):
class Castle(Boxes):
webinterface = False
def __init__(self):
@ -26,21 +26,21 @@ class Castle(Boxes):
def render(self, t_x=70, t_h=250, w1_x=300, w1_h=120, w2_x=100, w2_h=120):
self.open()
s = edges.FingerJointSettings(self.thickness, relative=False,
space = 10, finger=10, height=10,
space=10, finger=10, height=10,
width=self.thickness)
p = edges.FingerJointEdge(self, s)
p.char = "p"
self.addPart(p)
P = edges.FingerJointEdgeCounterPart(self, s)
P.char = "P"
self.addPart(P)
self.moveTo(0,0)
self.rectangularWall(t_x, t_h, edges="efPf", move="right", callback=
[lambda: self.fingerHolesAt(t_x*0.5, 0, w1_h, 90),])
self.moveTo(0, 0)
self.rectangularWall(t_x, t_h, edges="efPf", move="right", callback=[lambda: self.fingerHolesAt(t_x * 0.5, 0, w1_h, 90), ])
self.rectangularWall(t_x, t_h, edges="efPf", move="right")
self.rectangularWall(t_x, t_h, edges="eFPF", move="right", callback=
[lambda: self.fingerHolesAt(t_x*0.5, 0, w2_h, 90),])
self.rectangularWall(t_x, t_h, edges="eFPF", move="right", callback=[lambda: self.fingerHolesAt(t_x * 0.5, 0, w2_h, 90), ])
self.rectangularWall(t_x, t_h, edges="eFPF", move="right")
self.rectangularWall(w1_x, w1_h, "efpe", move="right")
@ -48,10 +48,12 @@ class Castle(Boxes):
self.close()
def main():
c = Castle()
c.parseArgs()
c.render()
if __name__ == '__main__':
main()

View File

@ -16,9 +16,11 @@
from boxes import *
class DrillBox(Boxes):
"""Not yet parametrized box for drills from 1 to 12.5mm
in 0.5mm steps, 3 holes each size"""
def __init__(self):
Boxes.__init__(self)
self.x, self.y, self.h = 120, 240, 60
@ -31,51 +33,46 @@ in 0.5mm steps, 3 holes each size"""
self.fingerHolesAt(0, 5, self.y, angle=0)
self.fingerHolesAt(0, 25, self.y, angle=0)
def drillholes(self):
for i in range(6):
for j in range(4):
for k in range(3):
r = (12.5-2*i-0.5*j) * 0.5
self.hole(i*20+10, j*60+k*20+10, r+0.05)
r = (12.5 - 2 * i - 0.5 * j) * 0.5
self.hole(i * 20 + 10, j * 60 + k * 20 + 10, r + 0.05)
def description(self):
self.ctx.set_font_size(6)
for i in range(4):
for j in range(6):
self.rectangularHole(i*60+30, 20*j+10, 58, 14+1*j)
d = 2.5-0.5*i+2*j
self.text("%.1f" % d, i*60+20, 19*j+6,
self.rectangularHole(i * 60 + 30, 20 * j + 10, 58, 14 + 1 * j)
d = 2.5 - 0.5 * i + 2 * j
self.text("%.1f" % d, i * 60 + 20, 19 * j + 6,
align="center")
def render(self):
x, y, h = self.x, self.y, self.h
t = self.thickness
self.open()
self.edges["f"].settings.setValues(self.thickness, space=3, finger=3,
surroundingspaces=1)
self.edges["f"].settings.setValues(self.thickness, space=3, finger=3, surroundingspaces=1)
self.rectangularWall(x, h, "FfeF", callback=[self.holesx],move="right")
self.rectangularWall(x, h, "FfeF", callback=[self.holesx], move="right")
self.rectangularWall(y, h, "FfeF", callback=[self.holesy], move="up")
self.rectangularWall(y, h, "FfeF", callback=[self.holesy])
self.rectangularWall(x, h, "FfeF", callback=[self.holesx],
move="left up")
self.rectangularWall(x, h, "FfeF", callback=[self.holesx], move="left up")
self.rectangularWall(x, y, "ffff", move="up")
self.rectangularWall(x, y, "ffff", callback=[self.drillholes],
move="up")
self.rectangularWall(x, y, "ffff", callback=[self.drillholes,
self.description],
move="up")
self.rectangularWall(x, y, "ffff", callback=[self.drillholes], move="up")
self.rectangularWall(x, y, "ffff", callback=[self.drillholes, self.description], move="up")
self.close()
def main():
b = DrillBox()
b.parseArgs()
b.render()
if __name__ == '__main__':
main()

View File

@ -17,29 +17,33 @@
import boxes
import math
class FlexBox(boxes.Boxes):
"""Box with living hinge and round corners"""
def __init__(self):
boxes.Boxes.__init__(self)
self.buildArgParser("x", "y", "h", "outside")
self.argparser.add_argument(
"--radius", action="store", type=float, default=15,
"--radius", action="store", type=float, default=15,
help="Radius of the latch in mm")
@boxes.restore
def flexBoxSide(self, x, y, r, callback=None):
self.moveTo(r, 0)
for i, l in zip(range(2), (x, y)):
self.cc(callback, i)
self.edges["f"](l-2*r)
self.edges["f"](l - 2 * r)
self.corner(90, r)
self.cc(callback, 2)
self.edge(x-2*r)
self.edge(x - 2 * r)
self.corner(90, r)
self.cc(callback, 3)
self.latch(self.latchsize)
self.cc(callback, 4)
self.edges["f"](y-2*r-self.latchsize)
self.edges["f"](y - 2 * r - self.latchsize)
self.corner(90, r)
def surroundingWall(self):
@ -47,33 +51,33 @@ class FlexBox(boxes.Boxes):
c4 = math.pi * r * 0.5
self.edges["F"](y-2*r-self.latchsize, False)
if x-2*r < self.thickness:
self.edges["X"](2*c4+x-2*r, h+2*self.thickness)
self.edges["F"](y - 2 * r - self.latchsize, False)
if x - 2 * r < self.thickness:
self.edges["X"](2 * c4 + x - 2 * r, h + 2 * self.thickness)
else:
self.edges["X"](c4, h+2*self.thickness)
self.edges["F"](x-2*r, False)
self.edges["X"](c4, h+2*self.thickness)
self.edges["F"](y-2*r, False)
if x-2*r < self.thickness:
self.edges["X"](2*c4+x-2*r, h+2*self.thickness)
self.edges["X"](c4, h + 2 * self.thickness)
self.edges["F"](x - 2 * r, False)
self.edges["X"](c4, h + 2 * self.thickness)
self.edges["F"](y - 2 * r, False)
if x - 2 * r < self.thickness:
self.edges["X"](2 * c4 + x - 2 * r, h + 2 * self.thickness)
else:
self.edges["X"](c4, h+2*self.thickness)
self.edge(x-2*r)
self.edges["X"](c4, h+2*self.thickness)
self.edges["X"](c4, h + 2 * self.thickness)
self.edge(x - 2 * r)
self.edges["X"](c4, h + 2 * self.thickness)
self.latch(self.latchsize, False)
self.edge(h+2*self.thickness)
self.edge(h + 2 * self.thickness)
self.latch(self.latchsize, False, True)
self.edge(c4)
self.edge(x-2*r)
self.edge(x - 2 * r)
self.edge(c4)
self.edges["F"](y-2*r, False)
self.edges["F"](y - 2 * r, False)
self.edge(c4)
self.edges["F"](x-2*r, False)
self.edges["F"](x - 2 * r, False)
self.edge(c4)
self.edges["F"](y-2*r-self.latchsize, False)
self.edges["F"](y - 2 * r - self.latchsize, False)
self.corner(90)
self.edge(h+2*self.thickness)
self.edge(h + 2 * self.thickness)
self.corner(90)
def render(self):
@ -85,27 +89,29 @@ class FlexBox(boxes.Boxes):
x, y, h = self.x, self.y, self.h
self.latchsize = 8 * self.thickness
r = self.radius or min(x, y-self.latchsize)/2.0
r = min(r, x/2.0)
self.radius = r = min(r, max(0, (y-self.latchsize)/2.0))
r = self.radius or min(x, y - self.latchsize) / 2.0
r = min(r, x / 2.0)
self.radius = r = min(r, max(0, (y - self.latchsize) / 2.0))
c4 = math.pi * r * 0.5
self.open()
self.moveTo(self.thickness, self.thickness)
self.surroundingWall()
self.moveTo(self.thickness, self.h+5*self.thickness)
self.moveTo(self.thickness, self.h + 5 * self.thickness)
self.flexBoxSide(self.x, self.y, self.radius)
self.moveTo(2*self.x+3*self.thickness, 0)
self.moveTo(2 * self.x + 3 * self.thickness, 0)
self.ctx.scale(-1, 1)
self.flexBoxSide(self.x, self.y, self.radius)
self.close()
def main():
b = FlexBox()
b.parseArgs()
b.render()
if __name__=="__main__":
if __name__ == "__main__":
main()

View File

@ -17,49 +17,53 @@
from boxes import *
import math
class FlexBox2(Boxes):
"""Box with living hinge and top corners rounded"""
def __init__(self):
Boxes.__init__(self)
self.buildArgParser("x", "y", "h", "outside")
self.argparser.add_argument(
"--radius", action="store", type=float, default=15,
"--radius", action="store", type=float, default=15,
help="Radius of the corners in mm")
@restore
def flexBoxSide(self, x, y, r, callback=None):
self.cc(callback, 0)
self.edges["f"](x)
self.corner(90, 0)
self.cc(callback, 1)
self.edges["f"](y-r)
self.edges["f"](y - r)
self.corner(90, r)
self.cc(callback, 2)
self.edge(x-2*r)
self.edge(x - 2 * r)
self.corner(90, r)
self.cc(callback, 3)
self.latch(self.latchsize)
self.cc(callback, 4)
self.edges["f"](y-r-self.latchsize)
self.edges["f"](y - r - self.latchsize)
self.corner(90)
def surroundingWall(self):
x, y, h, r = self.x, self.y, self.h, self.radius
self.edges["F"](y-r, False)
if (x-2*r < self.thickness):
self.edges["X"](2*self.c4+x-2*r, h+2*self.thickness)
self.edges["F"](y - r, False)
if (x - 2 * r < self.thickness):
self.edges["X"](2 * self.c4 + x - 2 * r, h + 2 * self.thickness)
else:
self.edges["X"](self.c4, h+2*self.thickness)
self.edge(x-2*r)
self.edges["X"](self.c4, h+2*self.thickness)
self.edges["X"](self.c4, h + 2 * self.thickness)
self.edge(x - 2 * r)
self.edges["X"](self.c4, h + 2 * self.thickness)
self.latch(self.latchsize, False)
self.edge(h+2*self.thickness)
self.edge(h + 2 * self.thickness)
self.latch(self.latchsize, False, True)
self.edge(self.c4)
self.edge(x-2*r)
self.edge(x - 2 * r)
self.edge(self.c4)
self.edges["F"](y-r)
self.edges["F"](y - r)
self.corner(90)
self.edge(self.thickness)
self.edges["f"](h)
@ -67,41 +71,48 @@ class FlexBox2(Boxes):
self.corner(90)
def render(self):
if self.outside:
self.x = self.adjustSize(self.x)
self.y = self.adjustSize(self.y)
self.h = self.adjustSize(self.h)
self.latchsize = 8*self.thickness
self.radius = self.radius or min(self.x/2.0, self.y-self.latchsize)
self.radius = min(self.radius, self.x/2.0)
self.radius = min(self.radius, max(0, self.y-self.latchsize))
self.latchsize = 8 * self.thickness
self.radius = self.radius or min(self.x / 2.0, self.y - self.latchsize)
self.radius = min(self.radius, self.x / 2.0)
self.radius = min(self.radius, max(0, self.y - self.latchsize))
self.c4 = c4 = math.pi * self.radius * 0.5
self.open()
self.fingerJointSettings = (4, 4)
self.moveTo(2*self.thickness, self.thickness)
self.moveTo(2 * self.thickness, self.thickness)
self.ctx.save()
self.surroundingWall()
self.moveTo(self.x+self.y-3*self.radius+2*self.c4+self.latchsize+1*self.thickness, 0)
self.moveTo(self.x + self.y - 3 * self.radius + 2 * self.c4 + self.latchsize + 1 * self.thickness, 0)
self.rectangularWall(self.x, self.h, edges="FFFF")
self.ctx.restore()
self.moveTo(0, self.h+4*self.thickness)
self.moveTo(0, self.h + 4 * self.thickness)
self.flexBoxSide(self.x, self.y, self.radius)
self.moveTo(2*self.x+3*self.thickness, 0)
self.moveTo(2 * self.x + 3 * self.thickness, 0)
self.ctx.scale(-1, 1)
self.flexBoxSide(self.x, self.y, self.radius)
self.ctx.scale(-1, 1)
self.moveTo(2*self.thickness, 0)
self.rectangularWall(self.h, self.y-self.radius-self.latchsize, edges="fFeF")
self.moveTo(2 * self.thickness, 0)
self.rectangularWall(self.h, self.y - self.radius - self.latchsize, edges="fFeF")
self.close()
def main():
b = FlexBox2()
b.parseArgs()
b.render()
if __name__=="__main__":
if __name__ == "__main__":
main()

View File

@ -17,22 +17,24 @@
from boxes import *
import math
class FlexBox3(Boxes):
"""Box with living hinge"""
def __init__(self):
Boxes.__init__(self)
self.buildArgParser("x", "y", "outside")
self.argparser.add_argument(
"--z", action="store", type=float, default=100.0,
"--z", action="store", type=float, default=100.0,
help="height of the box")
self.argparser.add_argument(
"--h", action="store", type=float, default=10.0,
"--h", action="store", type=float, default=10.0,
help="height of the lid")
self.argparser.add_argument(
"--radius", action="store", type=float, default=10.0,
"--radius", action="store", type=float, default=10.0,
help="radius of the lids living hinge")
self.argparser.add_argument(
"--c", action="store", type=float, default=1.0,
"--c", action="store", type=float, default=1.0,
dest="d", help="clearance of the lid")
def rectangleCorner(self, edge1=None, edge2=None):
@ -50,10 +52,10 @@ class FlexBox3(Boxes):
self.edges["f"](x)
self.corner(90, 0)
self.cc(callback, 1)
self.edges["f"](y-r)
self.edges["f"](y - r)
self.corner(90, r)
self.cc(callback, 2)
self.edge(x-r)
self.edge(x - r)
self.corner(90, 0)
self.cc(callback, 3)
self.edges["f"](y)
@ -61,22 +63,22 @@ class FlexBox3(Boxes):
def surroundingWall(self):
x, y, z, r, d = self.x, self.y, self.z, self.radius, self.d
self.edges["F"](y-r, False)
self.edges["X"](self.c4, z+2*self.thickness)
self.edges["F"](y - r, False)
self.edges["X"](self.c4, z + 2 * self.thickness)
self.corner(-90)
self.edge(d)
self.corner(90)
self.edges["f"](x-r+d)
self.edges["f"](x - r + d)
self.corner(90)
self.edges["f"](z+2*self.thickness+2*d)
self.edges["f"](z + 2 * self.thickness + 2 * d)
self.corner(90)
self.edges["f"](x-r+d)
self.edges["f"](x - r + d)
self.corner(90)
self.edge(d)
self.corner(-90)
self.edge(self.c4)
self.edges["F"](y-r)
self.edges["F"](y - r)
self.corner(90)
self.edge(self.thickness)
self.edges["f"](z)
@ -87,16 +89,16 @@ class FlexBox3(Boxes):
def lidSide(self):
x, y, z, r, d, h = self.x, self.y, self.z, self.radius, self.d, self.h
t = self.thickness
r2 = r+t if r+t <=h+t else h+t
r2 = r + t if r + t <= h + t else h + t
self.moveTo(self.thickness, self.thickness)
self.edge(h+self.thickness-r2)
self.edge(h + self.thickness - r2)
self.corner(90, r2)
self.edge(r-r2+2*t)
self.edges["F"](x-r)
self.edge(r - r2 + 2 * t)
self.edges["F"](x - r)
self.rectangleCorner("F", "f")
self.edges["g"](h)
self.rectangleCorner("f", "e")
self.edge(x+2*t)
self.edge(x + 2 * t)
def render(self):
if self.outside:
@ -105,56 +107,57 @@ class FlexBox3(Boxes):
self.z = self.adjustSize(self.z)
x, y, z, d, h = self.x, self.y, self.z, self.d, self.h
r = self.radius = self.radius or min(x, y)/2.0
r = self.radius = self.radius or min(x, y) / 2.0
thickness = self.thickness
self.c4 = c4 = math.pi * r * 0.5 * 0.95
self.latchsize = 8*thickness
self.latchsize = 8 * thickness
width = 2*x + y - 2*r + c4 + 14*thickness + 3*h # lock
height = y + z + 8*thickness
width = 2 * x + y - 2 * r + c4 + 14 * thickness + 3 * h # lock
height = y + z + 8 * thickness
self.open()
self.edges["f"].settings.setValues(
self.thickness, finger=2, space=2, surroundingspaces=1)
self.edges["f"].settings.setValues(self.thickness, finger=2, space=2, surroundingspaces=1)
s = edges.FingerJointSettings(self.thickness, surroundingspaces=1)
g = edges.FingerJointEdge(self, s)
g.char = "g"
self.addPart(g)
G = edges.FingerJointEdgeCounterPart(self, s)
G.char = "G"
self.addPart(G)
self.moveTo(2*self.thickness, self.thickness+2*d)
self.moveTo(2 * self.thickness, self.thickness + 2 * d)
self.ctx.save()
self.surroundingWall()
self.moveTo(x+y-2*r+self.c4+2*self.thickness, -2*d-self.thickness)
self.moveTo(x + y - 2 * r + self.c4 + 2 * self.thickness, -2 * d - self.thickness)
self.rectangularWall(x, z, edges="FFFF", move="right")
self.rectangularWall(h, z+2*(d+self.thickness),
edges="GeGF", move="right")
self.rectangularWall(h, z + 2 * (d + self.thickness), edges="GeGF", move="right")
self.lidSide()
self.moveTo(2*h+5*self.thickness, 0)
self.moveTo(2 * h + 5 * self.thickness, 0)
self.ctx.scale(-1, 1)
self.lidSide()
self.ctx.restore()
self.moveTo(0, z+4*self.thickness+2*d)
self.moveTo(0, z + 4 * self.thickness + 2 * d)
self.flexBoxSide(x, y, r)
self.moveTo(2*x+3*self.thickness, 2*d)
self.moveTo(2 * x + 3 * self.thickness, 2 * d)
self.ctx.scale(-1, 1)
self.flexBoxSide(x, y, r)
self.ctx.scale(-1, 1)
self.moveTo(2*self.thickness, -self.thickness)
self.moveTo(2 * self.thickness, -self.thickness)
self.rectangularWall(z, y, edges="fFeF")
self.close()
def main():
b = FlexBox3() #100, 40, 100, r=20, h=10, thickness=4.0)
b = FlexBox3() # 100, 40, 100, r=20, h=10, thickness=4.0)
b.parseArgs()
b.render()
if __name__=="__main__":
if __name__ == "__main__":
main()

View File

@ -17,51 +17,54 @@
from boxes import *
import math
class FlexBox4(Boxes):
"""Box with living hinge and left corners rounded"""
def __init__(self):
Boxes.__init__(self)
self.buildArgParser("x", "y", "h", "outside")
self.argparser.add_argument(
"--radius", action="store", type=float, default=15,
"--radius", action="store", type=float, default=15,
help="Radius of the corners in mm")
@restore
def flexBoxSide(self, x, y, r, callback=None):
self.cc(callback, 0)
self.edges["f"](x)
self.corner(90, 0)
self.cc(callback, 1)
self.edges["f"](y-r)
self.edges["f"](y - r)
self.corner(90, r)
self.cc(callback, 2)
self.edge(x-2*r)
self.edge(x - 2 * r)
self.corner(90, r)
self.cc(callback, 3)
self.edges["e"](y-r-self.latchsize)
self.edges["e"](y - r - self.latchsize)
self.cc(callback, 4)
self.latch(self.latchsize)
self.corner(90)
def surroundingWall(self):
x, y, h, r = self.x, self.y, self.h, self.radius
self.edges["F"](y-r, False)
if (x-2*r < self.thickness):
self.edges["X"](2*self.c4+x-2*r, h+2*self.thickness)
self.edges["F"](y - r, False)
if (x - 2 * r < self.thickness):
self.edges["X"](2 * self.c4 + x - 2 * r, h + 2 * self.thickness)
else:
self.edges["X"](self.c4, h+2*self.thickness)
self.edge(x-2*r)
self.edges["X"](self.c4, h+2*self.thickness)
self.edge(y-r-self.latchsize)
self.edges["X"](self.c4, h + 2 * self.thickness)
self.edge(x - 2 * r)
self.edges["X"](self.c4, h + 2 * self.thickness)
self.edge(y - r - self.latchsize)
self.latch(self.latchsize, False)
self.edge(h+2*self.thickness)
self.edge(h + 2 * self.thickness)
self.latch(self.latchsize, False, True)
self.edge(y-r-self.latchsize)
self.edge(y - r - self.latchsize)
self.edge(self.c4)
self.edge(x-2*r)
self.edge(x - 2 * r)
self.edge(self.c4)
self.edges["F"](y-r)
self.edges["F"](y - r)
self.corner(90)
self.edge(self.thickness)
self.edges["f"](h)
@ -75,33 +78,35 @@ class FlexBox4(Boxes):
self.h = self.adjustSize(self.h)
self.c4 = c4 = math.pi * self.radius * 0.5
self.latchsize = 8*self.thickness
self.radius = self.radius or min(self.x/2.0, self.y-self.latchsize)
self.radius = min(self.radius, self.x/2.0)
self.radius = min(self.radius, max(0, self.y-self.latchsize))
self.latchsize = 8 * self.thickness
self.radius = self.radius or min(self.x / 2.0, self.y - self.latchsize)
self.radius = min(self.radius, self.x / 2.0)
self.radius = min(self.radius, max(0, self.y - self.latchsize))
self.open()
self.fingerJointSettings = (4, 4)
self.moveTo(2*self.thickness, self.thickness)
self.moveTo(2 * self.thickness, self.thickness)
self.ctx.save()
self.surroundingWall()
self.ctx.restore()
self.moveTo(0, self.h+4*self.thickness)
self.moveTo(0, self.h + 4 * self.thickness)
self.flexBoxSide(self.x, self.y, self.radius)
self.moveTo(2*self.x+3*self.thickness, 0)
self.moveTo(2 * self.x + 3 * self.thickness, 0)
self.ctx.scale(-1, 1)
self.flexBoxSide(self.x, self.y, self.radius)
self.ctx.scale(-1, 1)
self.moveTo(2*self.thickness,-self.thickness)
self.moveTo(2 * self.thickness, -self.thickness)
self.rectangularWall(self.x, self.h, edges="FeFF")
self.close()
def main():
b = FlexBox4()
b.parseArgs()
b.render()
if __name__=="__main__":
if __name__ == "__main__":
main()

View File

@ -16,19 +16,21 @@
from boxes import *
class FlexTest(Boxes):
"Piece for testing different flex settings"
def __init__(self):
Boxes.__init__(self)
self.buildArgParser("x", "y")
self.argparser.add_argument(
"--fd", action="store", type=float, default=0.5,
"--fd", action="store", type=float, default=0.5,
help="distance of flex cuts in multiples of thickness")
self.argparser.add_argument(
"--fc", action="store", type=float, default=1.0,
"--fc", action="store", type=float, default=1.0,
help="connections of flex cuts in multiples of thickness")
self.argparser.add_argument(
"--fw", action="store", type=float, default=5.0,
"--fw", action="store", type=float, default=5.0,
help="width of flex cuts in multiples of thickness")
def render(self):
@ -46,17 +48,19 @@ class FlexTest(Boxes):
self.corner(90)
self.edge(y)
self.corner(90)
self.edge(x+20)
self.edge(x + 20)
self.corner(90)
self.edge(y)
self.corner(90)
self.close()
def main():
f = FlexTest()
f.parseArgs()
f.render()
if __name__ == '__main__':
main()

View File

@ -16,26 +16,30 @@
from boxes import *
class FlexTest2(Boxes):
"Piece for testing 2D flex settings"
def __init__(self):
Boxes.__init__(self)
self.buildArgParser("x", "y")
self.argparser.add_argument(
"--fw", action="store", type=float, default=1,
"--fw", action="store", type=float, default=1,
help="distance of flex cuts in multiples of thickness")
def render(self):
x, y = self.x, self.y
t = self.thickness
self.open()
self.rectangularWall(x, y, callback=[lambda:self.flex2D(x, y, self.fw)])
self.rectangularWall(x, y, callback=[lambda: self.flex2D(x, y, self.fw)])
self.close()
def main():
f = FlexTest()
f.parseArgs()
f.render()
if __name__ == '__main__':
main()

View File

@ -17,6 +17,7 @@
from boxes import *
import math
class Folder(Boxes):
"""Book cover with flex for the spine"""
@ -32,18 +33,18 @@ class Folder(Boxes):
x, y, r, h = self.x, self.y, self.r, self.h
c2 = math.pi * h
self.open()
self.moveTo(r+self.thickness, self.thickness)
self.edge(x-r)
self.moveTo(r + self.thickness, self.thickness)
self.edge(x - r)
self.edges["X"](c2, y)
self.edge(x-r)
self.edge(x - r)
self.corner(90, r)
self.edge(y-2*r)
self.edge(y - 2 * r)
self.corner(90, r)
self.edge(2*x-2*r+c2)
self.edge(2 * x - 2 * r + c2)
self.corner(90, r)
self.edge(y-2*r)
self.edge(y - 2 * r)
self.corner(90, r)
self.close()
@ -52,5 +53,6 @@ def main():
f.parseArgs()
f.render()
if __name__ == '__main__':
main()

View File

@ -16,89 +16,91 @@
from boxes import *
class GearBox(Boxes):
"""Gearbox with multiple identical stages"""
def __init__(self):
Boxes.__init__(self)
self.argparser.add_argument(
"--teeth1", action="store", type=int, default=8,
"--teeth1", action="store", type=int, default=8,
help="number of teeth on ingoing shaft")
self.argparser.add_argument(
"--teeth2", action="store", type=int, default=20,
"--teeth2", action="store", type=int, default=20,
help="number of teeth on outgoing shaft")
self.argparser.add_argument(
"--modulus", action="store", type=float, default=3,
"--modulus", action="store", type=float, default=3,
help="modulus of the theeth in mm")
self.argparser.add_argument(
"--shaft", action="store", type=float, default=6.,
"--shaft", action="store", type=float, default=6.,
help="diameter of the shaft")
self.argparser.add_argument(
"--stages", action="store", type=int, default=4,
"--stages", action="store", type=int, default=4,
help="number of stages in the gear reduction")
def render(self):
# Initialize canvas
self.open()
if self.teeth2 < self.teeth1:
self.teeth2, self.teeth1 = self.teeth1, self.teeth2
pitch1, size1, xxx = self.gears.sizes(teeth=self.teeth1,
dimension=self.modulus)
pitch2, size2, xxx = self.gears.sizes(teeth=self.teeth2,
dimension=self.modulus)
pitch1, size1, xxx = self.gears.sizes(teeth=self.teeth1, dimension=self.modulus)
pitch2, size2, xxx = self.gears.sizes(teeth=self.teeth2, dimension=self.modulus)
t = self.thickness
x = 1.1*t*self.stages
x = 1.1 * t * self.stages
if self.stages == 1:
y = size1 + size2
y1 = y/2-(pitch1+pitch2)+pitch1
y2 = y/2+(pitch1+pitch2)-pitch2
y1 = y / 2 - (pitch1 + pitch2) + pitch1
y2 = y / 2 + (pitch1 + pitch2) - pitch2
else:
y = 2 * size2
y1 = y/2 - (pitch1+pitch2)/2
y2 = y/2 + (pitch1+pitch2)/2
y1 = y / 2 - (pitch1 + pitch2) / 2
y2 = y / 2 + (pitch1 + pitch2) / 2
h = max(size1, size2) + t
b = "F"
t = "e" # prepare for close box
t = "e" # prepare for close box
mh = self.shaft
def sideCB():
self.hole(y1, h/2, mh/2)
self.hole(y2, h/2, mh/2)
self.hole(y1, h / 2, mh / 2)
self.hole(y2, h / 2, mh / 2)
self.moveTo(self.thickness, self.thickness)
self.rectangularWall(y, h, [b, "f", t, "f"], callback=[sideCB], move="right")
self.rectangularWall(x, h, [b, "F", t, "F"], move="up")
self.rectangularWall(x, h, [b, "F", t, "F"])
self.rectangularWall(y, h, [b, "f", t, "f"], callback=[sideCB], move="left")
self.rectangularWall(x, h, [b, "F", t, "F"], move="up only")
self.rectangularWall(x, y, "ffff", move="up")
profile_shift = 20
pressure_angle = 20
for i in range(self.stages-1):
self.gears(teeth=self.teeth2, dimension=self.modulus,
angle=pressure_angle,
for i in range(self.stages - 1):
self.gears(teeth=self.teeth2, dimension=self.modulus, angle=pressure_angle,
mount_hole=mh, profile_shift=profile_shift, move="up")
self.gears(teeth=self.teeth2, dimension=self.modulus,
angle=pressure_angle,
self.gears(teeth=self.teeth2, dimension=self.modulus, angle=pressure_angle,
mount_hole=mh, profile_shift=profile_shift, move="right")
for i in range(self.stages):
self.gears(teeth=self.teeth1, dimension=self.modulus,
angle=pressure_angle,
self.gears(teeth=self.teeth1, dimension=self.modulus, angle=pressure_angle,
mount_hole=mh, profile_shift=profile_shift, move="down")
self.close()
def main():
b = Box()
b = GearBox()
b.parseArgs()
b.render()
if __name__ == '__main__':
main()

View File

@ -17,34 +17,34 @@
from boxes import *
import random
class JigsawPuzzle(Boxes): # change class name here and below
class JigsawPuzzle(Boxes): # change class name here and below
"""Fractal jigsaw puzzle. Still aplha"""
webinterface = False # Change to make visible in web interface
webinterface = False # Change to make visible in web interface
def __init__(self):
Boxes.__init__(self)
self.count = 0
self.argparser.add_argument(
"--size", action="store", type=float, default=100,
"--size", action="store", type=float, default=100,
help="size of the puzzle in mm")
self.argparser.add_argument(
"--depth", action="store", type=int, default=5,
"--depth", action="store", type=int, default=5,
help="depth of the recursion/level of detail")
def peano(self, level):
if level == 0:
self.edge(self.size/self.depth)
self.edge(self.size / self.depth)
return
self.peano(self, level-1)
self.corner()
self.peano(self, level - 1)
self.corner()
def edge(self, l):
self.count += 1
Boxes.edge(self, l)
#if (self.count % 2**5) == 0: #level == 3 and parity>0:
# if (self.count % 2**5) == 0: #level == 3 and parity>0:
# self.corner(-360, 0.25*self.size/2**self.depth)
def hilbert(self, level, parity=1):
@ -53,28 +53,31 @@ class JigsawPuzzle(Boxes): # change class name here and below
# rotate and draw first subcurve with opposite parity to big curve
self.corner(parity * 90)
self.hilbert(level - 1, -parity)
# interface to and draw second subcurve with same parity as big curve
self.edge(self.size/2**self.depth)
self.edge(self.size / 2 ** self.depth)
self.corner(parity * -90)
self.hilbert(level - 1, parity)
# third subcurve
self.edge(self.size/2**self.depth)
self.edge(self.size / 2 ** self.depth)
self.hilbert(level - 1, parity)
#if level == 3: self.corner(-360, 0.4*self.size/2**self.depth)
# if level == 3: self.corner(-360, 0.4*self.size/2**self.depth)
# fourth subcurve
self.corner(parity * -90)
self.edge(self.size/2**self.depth)
self.edge(self.size / 2 ** self.depth)
self.hilbert(level - 1, -parity)
# a final turn is needed to make the turtle
# end up facing outward from the large square
self.corner(parity * 90)
#if level == 3 and parity>0: # and random.random() < 100*0.5**(self.depth-2):
# if level == 3 and parity>0: # and random.random() < 100*0.5**(self.depth-2):
# self.corner(-360, 0.4*self.size/2**self.depth)
#self.ctx.save()
#self.corner(parity*-90)
#self.edge(self.size/2**self.depth)
#self.ctx.restore()
# self.ctx.save()
# self.corner(parity*-90)
# self.edge(self.size/2**self.depth)
# self.ctx.restore()
def render(self):
size = self.size
t = self.thickness
@ -85,10 +88,12 @@ class JigsawPuzzle(Boxes): # change class name here and below
self.hilbert(self.depth)
self.close()
def main():
b = JigsawPuzzle()
b.parseArgs()
b.render()
if __name__ == '__main__':
main()

View File

@ -17,37 +17,39 @@
from boxes import *
import math
"""
22x7.5x7cm
D=23cm, d=21cm
d = 8" D = 9"
"""
class RoundedTriangleSettings(edges.Settings):
absolute_params = {
"angle" : 60,
"radius" : 30,
"r_hole" : None,
}
"angle": 60,
"radius": 30,
"r_hole": None,
}
class RoundedTriangle(edges.Edge):
char = "t"
def __call__(self, length, **kw):
angle = self.settings.angle
r = self.settings.radius
if self.settings.r_hole:
x = 0.5*(length-2*r)*math.tan(math.radians(angle))
y = 0.5*(length)
x = 0.5 * (length - 2 * r) * math.tan(math.radians(angle))
y = 0.5 * (length)
self.hole(x, y, self.settings.r_hole)
l = 0.5 * (length-2*r) / math.cos(math.radians(angle))
self.corner(90-angle, r)
l = 0.5 * (length - 2 * r) / math.cos(math.radians(angle))
self.corner(90 - angle, r)
self.edge(l)
self.corner(2*angle, r)
self.corner(2 * angle, r)
self.edge(l)
self.corner(90-angle, r)
self.corner(90 - angle, r)
def startAngle(self):
return 90
@ -55,8 +57,8 @@ class RoundedTriangle(edges.Edge):
def endAngle(self):
return 90
class Lamp(Boxes):
class Lamp(Boxes):
webinterface = False
def __init__(self):
@ -82,7 +84,7 @@ class Lamp(Boxes):
self.open()
#self.edges["f"].settings = (5, 5) # XXX
# self.edges["f"].settings = (5, 5) # XXX
s = RoundedTriangleSettings(self.thickness, angle=72, r_hole=2)
self.addPart(RoundedTriangle(self, s))
@ -90,19 +92,20 @@ class Lamp(Boxes):
self.flexSettings = (3, 5.0, 20.0)
self.edges["f"].settings.setValues(self.thickness, finger=5, space=5, relative=False)
d = 2*(r+w)
d = 2 * (r + w)
self.roundedPlate(d, d, r, move="right", callback=[
lambda: self.hole(w, r+w, r),])
#dist = ((2**0.5)*r-r) / (2**0.5) + 4
#pos = (w-dist, dist)
self.roundedPlate(d, d, r, holesMargin=w/2.0) #, callback=[
lambda: self.hole(w, r + w, r), ])
# dist = ((2**0.5)*r-r) / (2**0.5) + 4
# pos = (w-dist, dist)
self.roundedPlate(d, d, r, holesMargin=w / 2.0) # , callback=[
# lambda: self.hole(pos[0], pos[1], 7),])
self.roundedPlate(d, d, r, move="only left up")
hole = lambda: self.hole(w, 70, 2)
self.surroundingWall(d, d, r, 120, top='h', bottom='h', callback=[
None, hole, None, hole], move="up")
None, hole, None, hole], move="up")
self.ctx.save()
self.rectangularWall(x, y, edges="fFfF", holesMargin=5, move="right")
@ -119,10 +122,12 @@ class Lamp(Boxes):
self.close()
def main():
l = Lamp()
l.parseArgs()
l.render(r=4*25.4, w=20, x=270, y=150, h=100)
l.render(r=4 * 25.4, w=20, x=270, y=150, h=100)
if __name__ == '__main__':
main()

View File

@ -16,25 +16,28 @@
from boxes import *
class MagazinFile(Boxes):
"""Open magazine file"""
def __init__(self):
Boxes.__init__(self)
self.buildArgParser("x", "y", "h", "hi", "outside")
self.argparser.set_defaults(
fingerjointfinger=2.0,
fingerjointspace=2.0
)
)
def side(self, w, h, hi):
r = min(h-hi, w) / 2.0
if (h-hi) > w:
r = min(h - hi, w) / 2.0
if (h - hi) > w:
r = w / 2.0
lx = 0
ly = (h-hi) - w
ly = (h - hi) - w
else:
r = (h - hi) / 2.0
lx = (w - 2*r) / 2.0
lx = (w - 2 * r) / 2.0
ly = 0
e_w = self.edges["F"].startwidth()
@ -51,15 +54,15 @@ class MagazinFile(Boxes):
self.corner(-90, r)
self.edge(ly)
self.corner(90, r)
self.edge(lx)
self.edge(lx)
self.edge(e_w)
self.corner(90)
self.edges["F"](h)
self.edge(e_w)
self.corner(90)
def render(self):
if self.outside:
self.x = self.adjustSize(self.x)
self.y = self.adjustSize(self.y)
@ -80,15 +83,17 @@ class MagazinFile(Boxes):
self.rectangularWall(x, h, "Ffef", move="right only")
self.side(y, h, hi)
self.moveTo(y+15, h+hi+15, 180)
self.moveTo(y + 15, h + hi + 15, 180)
self.side(y, h, hi)
self.close()
def main():
b = MagazinFile()
b.parseArgs()
b.render()
if __name__ == '__main__':
main()

View File

@ -17,29 +17,31 @@
from boxes import *
import math
class Planetary(Boxes):
"""Gearbox with multiple identical stages"""
def __init__(self):
Boxes.__init__(self)
self.argparser.add_argument(
"--sunteeth", action="store", type=int, default=8,
"--sunteeth", action="store", type=int, default=8,
help="number of teeth on sun gear")
self.argparser.add_argument(
"--planetteeth", action="store", type=int, default=20,
"--planetteeth", action="store", type=int, default=20,
help="number of teeth on planets")
self.argparser.add_argument(
"--maxplanets", action="store", type=int, default=0,
"--maxplanets", action="store", type=int, default=0,
help="limit the number of planets (0 for as much as fit)")
self.argparser.add_argument(
"--deltateeth", action="store", type=int, default=0,
"--deltateeth", action="store", type=int, default=0,
help="enable secondary ring with given delta to the ring gear")
self.argparser.add_argument(
"--modulus", action="store", type=float, default=3,
"--modulus", action="store", type=float, default=3,
help="modulus of the theeth in mm")
self.argparser.add_argument(
"--shaft", action="store", type=float, default=6.,
"--shaft", action="store", type=float, default=6.,
help="diameter of the shaft")
#self.argparser.add_argument(
# self.argparser.add_argument(
# "--stages", action="store", type=int, default=4,
# help="number of stages in the gear reduction")
@ -47,48 +49,50 @@ class Planetary(Boxes):
# Initialize canvas
self.open()
ringteeth = self.sunteeth+2*self.planetteeth
spoke_width = 3*self.shaft
ringteeth = self.sunteeth + 2 * self.planetteeth
spoke_width = 3 * self.shaft
pitch1, size1, xxx = self.gears.sizes(teeth=self.sunteeth,
dimension=self.modulus)
dimension=self.modulus)
pitch2, size2, xxx = self.gears.sizes(teeth=self.planetteeth,
dimension=self.modulus)
dimension=self.modulus)
pitch3, size3, xxx = self.gears.sizes(
teeth=ringteeth, internal_ring=True, spoke_width=spoke_width,
dimension=self.modulus)
t = self.thickness
planets = int(math.pi//math.asin((self.planetteeth+2)/(self.planetteeth+self.sunteeth)))
planets = int(math.pi // math.asin((self.planetteeth + 2) / (self.planetteeth + self.sunteeth)))
if self.maxplanets:
planets = min(self.maxplanets, planets)
# Make sure the teeth mash
ta = self.sunteeth+ringteeth
ta = self.sunteeth + ringteeth
# There are sunteeth+ringteeth mashing positions for the planets
if ta % planets:
planetpositions = [round(i*ta/planets)*360/ta for i in range(planets)]
planetpositions = [round(i * ta / planets) * 360 / ta for i in range(planets)]
else:
planetpositions = planets
# XXX make configurable?
profile_shift = 20
pressure_angle = 20
self.parts.disc(size3, callback=lambda:self.hole(0,0,self.shaft/2), move="up")
self.parts.disc(size3, callback=lambda: self.hole(0, 0, self.shaft / 2), move="up")
self.gears(teeth=ringteeth, dimension=self.modulus,
angle=pressure_angle, internal_ring=True,
spoke_width=spoke_width, mount_hole=self.shaft,
profile_shift=profile_shift, move="up")
self.gears.gearCarrier(pitch1+pitch2, spoke_width, planetpositions,
2*spoke_width, self.shaft/2, move="up")
self.gears.gearCarrier(pitch1 + pitch2, spoke_width, planetpositions,
2 * spoke_width, self.shaft / 2, move="up")
self.gears(teeth=self.sunteeth, dimension=self.modulus,
angle=pressure_angle,
mount_hole=self.shaft, profile_shift=profile_shift, move="up")
numplanets = planets
if self.deltateeth:
numplanets += planets
deltamodulus = self.modulus*ringteeth/(ringteeth-self.deltateeth)
self.gears(teeth=ringteeth-self.deltateeth, dimension=deltamodulus,
deltamodulus = self.modulus * ringteeth / (ringteeth - self.deltateeth)
self.gears(teeth=ringteeth - self.deltateeth, dimension=deltamodulus,
angle=pressure_angle, internal_ring=True,
spoke_width=spoke_width, mount_hole=self.shaft,
profile_shift=profile_shift, move="up")
@ -97,13 +101,15 @@ class Planetary(Boxes):
self.gears(teeth=self.planetteeth, dimension=self.modulus,
angle=pressure_angle,
mount_hole=self.shaft, profile_shift=profile_shift, move="up")
self.close()
def main():
b = Box()
b.parseArgs()
b.render()
if __name__ == '__main__':
main()

View File

@ -16,8 +16,8 @@
from boxes import *
class Printer(Boxes):
class Printer(Boxes):
"""Work in progress"""
webinterface = False
@ -30,46 +30,48 @@ class Printer(Boxes):
# idlers
self.D_i = 17.0
self.d_i = 5.0
self.w_i = 7.0 # includes washers
self.w_i = 7.0 # includes washers
def mainPlate(self, nr):
r = self.r
t2 = 0.5 * self.thickness
if nr:
return
self.moveTo(r-5, r, -90)
self.hole(0, 0, r-80)
self.moveTo(r - 5, r, -90)
self.hole(0, 0, r - 80)
D_i2 = self.D_i / 2
w_i2 = self.w_i / 2
d_c2 = self.d_c/2
d_c2 = self.d_c / 2
for i in range(6):
self.ctx.save()
self.moveTo(0, 0, i*60)
self.moveTo(0, 0, i * 60)
# winches
if i % 2:
self.fingerHolesAt(r-80, (d_c2+20), 70, angle=0)
self.fingerHolesAt(r-80, -(d_c2+20), 70, angle=0)
if i==5:
self.fingerHolesAt(r-70+t2, -(d_c2+20+t2), 40, angle=-90)
self.fingerHolesAt(r - 80, (d_c2 + 20), 70, angle=0)
self.fingerHolesAt(r - 80, -(d_c2 + 20), 70, angle=0)
if i == 5:
self.fingerHolesAt(r - 70 + t2, -(d_c2 + 20 + t2), 40, angle=-90)
else:
self.fingerHolesAt(r-70+t2, (d_c2+20+t2), 40, angle=90)
self.fingerHolesAt(r - 70 + t2, (d_c2 + 20 + t2), 40, angle=90)
# idler buck
else:
d = 0.5*(self.thickness)+w_i2
for y in (-d-d_c2, d-d_c2, -d+d_c2, d+d_c2):
self.fingerHolesAt(r-30, y, 30, angle=0)
self.hole(r-15+D_i2, -self.d_c/2, 0.4)
self.hole(r-15+D_i2, self.d_c/2, 0.4)
self.ctx.restore()
d = 0.5 * (self.thickness) + w_i2
for y in (-d - d_c2, d - d_c2, -d + d_c2, d + d_c2):
self.fingerHolesAt(r - 30, y, 30, angle=0)
self.hole(r - 15 + D_i2, -self.d_c / 2, 0.4)
self.hole(r - 15 + D_i2, self.d_c / 2, 0.4)
self.ctx.restore()
def head(self):
d_c = self.d_c
self.moveTo(self.spacing+10, self.spacing)
self.moveTo(self.spacing + 10, self.spacing)
for i in range(3):
self.hole(0, 5, 0.3)
self.fingerHolesAt(25, 0, 20)
@ -78,20 +80,20 @@ class Printer(Boxes):
self.hole(0, 5, 0.3)
self.corner(120, 10)
def support(self, x, y, edges="ff", pair=False, callback=None, move=None):
if len(edges) != 2:
raise ValueError("Two edges required")
edges = [self.edges.get(e, e,) for e in edges]
edges = [self.edges.get(e, e, ) for e in edges]
overallwidth = x + edges[0].spacing() + self.edges["e"].spacing()
overallheight = y + edges[1].spacing() + self.edges["e"].spacing()
r = 2*self.thickness
r = 2 * self.thickness
if pair:
overallwidth+= edges[0].spacing() + r - self.edges["e"].spacing()
overallheight+= edges[1].spacing() + r - self.edges["e"].spacing()
overallwidth += edges[0].spacing() + r - self.edges["e"].spacing()
overallheight += edges[1].spacing() + r - self.edges["e"].spacing()
if self.move(overallwidth, overallheight, move, before=True):
return
@ -99,16 +101,16 @@ class Printer(Boxes):
self.ctx.save()
self.moveTo(edges[0].margin(), edges[1].margin())
angle = math.degrees(math.atan((y-r)/float(x-r)))
angle = math.degrees(math.atan((y - r) / float(x - r)))
self.cc(callback, 0)
edges[1](x)
self.corner(90)
#self.edge(self.thickness)
self.corner(90-angle, r)
self.edge(((x-r)**2+(y-r)**2)**0.5)
# self.edge(self.thickness)
self.corner(90 - angle, r)
self.edge(((x - r) ** 2 + (y - r) ** 2) ** 0.5)
self.corner(angle, r)
#self.edge(self.thickness)
# self.edge(self.thickness)
self.corner(90)
self.cc(callback, 0)
edges[0](y)
@ -127,61 +129,69 @@ class Printer(Boxes):
self.open()
self.edges["f"].settings.setValues(self.thickness, surroundingspaces=0)
self.ctx.save()
for i in range(3):
# motor mounts
self.rectangularWall(70, 70, edges="feee", callback=[
lambda: self.NEMA(23, 35, 35),],
lambda: self.NEMA(23, 35, 35), ],
move="right")
# winch bucks
self.rectangularWall(50, 70, edges="efee", callback=[
None,
lambda: self.hole(35, 35, 8.5),
None,
lambda: self.fingerHolesAt(10, 0, 50)], move="right")
None,
lambda: self.hole(35, 35, 8.5),
None,
lambda: self.fingerHolesAt(10, 0, 50)], move="right")
self.support(40, 50, move="right", pair=True)
self.support(40, 50, move="right")
self.ctx.restore()
self.moveTo(0, 80)
self.ctx.save()
# idler bucks
for i in range(12):
self.rectangularWall(30, 30, edges="feee", callback=[
lambda: self.hole(15, 15, 3),], move="right")
lambda: self.hole(15, 15, 3), ], move="right")
# Cable adjustment blocks
self.ctx.save()
for i in range(6):
def holes():
self.hole(5, 4, 1.5)
self.hole(15, 4, 1.5)
self.rectangularWall(20, 8, edges="feee", callback=[holes,],
self.rectangularWall(20, 8, edges="feee", callback=[holes, ],
move="right")
self.ctx.restore()
self.moveTo(0, 20)
# Cable adjustment glyders
for i in range(6):
self.rectangularWall(8, 10, move="right", callback=[
lambda: self.hole(4, 4, 1.5),
None,
lambda: self.hole(4, 1.5, 0.4)])
lambda: self.hole(4, 4, 1.5),
None,
lambda: self.hole(4, 1.5, 0.4)])
self.rectangularWall(8, 10, move="right", callback=[
lambda: self.nutHole("M3", 4, 4),
None,
lambda: self.hole(4, 1.5, 0.4)])
lambda: self.nutHole("M3", 4, 4),
None,
lambda: self.hole(4, 1.5, 0.4)])
self.ctx.restore()
self.moveTo(0, 40)
# mainPlate
self.rectangularWall(2*self.r-10, 2*self.r-10, edges="ffff",
self.rectangularWall(2 * self.r - 10, 2 * self.r - 10, edges="ffff",
callback=self.mainPlate, move="right")
self.head()
self.close()
def main():
p = Printer()
p.parseArgs()
p.render()
if __name__ == '__main__':
main()

View File

@ -18,64 +18,73 @@ from boxes import *
from boxes import pulley
import math
class Pulley(Boxes):
"""Timing belt pulleys for different profiles"""
def __init__(self):
Boxes.__init__(self)
# remove cli params you do not need
self.buildArgParser("h")
self.argparser.add_argument(
"--profile", action="store", type=str, default="GT2_2mm",
"--profile", action="store", type=str, default="GT2_2mm",
choices=pulley.Pulley.getProfiles(),
help="profile of the teeth/belt")
self.argparser.add_argument(
"--teeth", action="store", type=int, default=20,
"--teeth", action="store", type=int, default=20,
help="number of teeth")
self.argparser.add_argument(
"--axle", action="store", type=float, default=5,
"--axle", action="store", type=float, default=5,
help="diameter of the axle")
self.argparser.add_argument(
"--top", action="store", type=float, default=0,
"--top", action="store", type=float, default=0,
help="overlap of top rim (zero for none)")
# Add non default cli params if needed (see argparse std lib)
#self.argparser.add_argument(
# self.argparser.add_argument(
# "--XX", action="store", type=float, default=0.5,
# help="DESCRIPTION")
def disk(self, diameter, hole, callback=None, move=""):
w = diameter + 2*self.spacing
w = diameter + 2 * self.spacing
if self.move(w, w, move, before=True):
return
self.ctx.save()
self.moveTo(w/2, w/2)
self.moveTo(w / 2, w / 2)
self.cc(callback, None, 0.0, 0.0)
if hole:
self.hole(0, 0, hole/2.0)
self.moveTo(diameter/2+self.burn, 0, 90)
self.corner(360, diameter/2)
self.hole(0, 0, hole / 2.0)
self.moveTo(diameter / 2 + self.burn, 0, 90)
self.corner(360, diameter / 2)
self.ctx.restore()
self.move(w, w, move)
def render(self):
# adjust to the variables you want in the local scope
t = self.thickness
# Initialize canvas
self.open()
if self.top:
self.disk(
self.pulley.diameter(self.teeth, self.profile)+2*self.top,
self.pulley.diameter(self.teeth, self.profile) + 2 * self.top,
self.axle, move="right")
for i in range(int(math.ceil(self.h/self.thickness))):
self.pulley(self.teeth, self.profile, r_axle=self.axle/2.0, move="right")
for i in range(int(math.ceil(self.h / self.thickness))):
self.pulley(self.teeth, self.profile, r_axle=self.axle / 2.0, move="right")
self.close()
def main():
b = Box()
b.parseArgs()
b.render()
if __name__ == '__main__':
main()

View File

@ -16,49 +16,56 @@
from boxes import *
class MotorEdge(edges.BaseEdge):
#def margin(self):
# def margin(self):
# return 30
def __call__(self, l, **kw):
self.polyline(
l-165, 45,
25*2**0.5, -45,
l - 165, 45,
25 * 2 ** 0.5, -45,
60, -45,
25*2**0.5, 45,
25 * 2 ** 0.5, 45,
55)
class OutsetEdge(edges.OutSetEdge):
def startwidth(self):
return 20
class HangerEdge(edges.BaseEdge):
char = "H"
def margin(self):
return 40
def __call__(self, l, **kw):
self.fingerHolesAt(0, -0.5*self.thickness, l, angle=0)
self.fingerHolesAt(0, -0.5 * self.thickness, l, angle=0)
w = self.settings
self.polyline(0, -90,
22+w, 90,
22 + w, 90,
70, 135,
2**0.5*12, 45,
2 ** 0.5 * 12, 45,
35, -45,
2**0.5*0.5*w, -90,
2**0.5*0.5*w, -45,
l -28, 45,
2**0.5*5, 45, 5, -90)
2 ** 0.5 * 0.5 * w, -90,
2 ** 0.5 * 0.5 * w, -45,
l - 28, 45,
2 ** 0.5 * 5, 45, 5, -90)
class RollerEdge(edges.BaseEdge):
def margin(self):
return 20
def __call__(self, l, **kw):
m = 40+100
self.polyline((l-m)/2.0, -45,
2**0.5*20, 45,
m = 40 + 100
self.polyline((l - m) / 2.0, -45,
2 ** 0.5 * 20, 45,
100, 45,
2**0.5*20, -45,
(l-m)/2.0)
2 ** 0.5 * 20, -45,
(l - m) / 2.0)
class RollerEdge2(edges.BaseEdge):
def margin(self):
@ -66,98 +73,102 @@ class RollerEdge2(edges.BaseEdge):
def __call__(self, l, **kw):
a = 30
f = 1/math.cos(math.radians(a))
f = 1 / math.cos(math.radians(a))
self.edges["f"](70)
self.polyline(0, a, f*25, -a, l-190, -a, f*25, a, 0)
self.polyline(0, a, f * 25, -a, l - 190, -a, f * 25, a, 0)
self.edges["f"](70)
class Rotary(Boxes):
"""Rotary Attachment for engraving cylindrical objects in a laser cutter"""
def __init__(self):
Boxes.__init__(self)
# remove cli params you do not need
#self.buildArgParser("x", "sx", "y", "sy", "h", "hi")
# self.buildArgParser("x", "sx", "y", "sy", "h", "hi")
# Add non default cli params if needed (see argparse std lib)
self.argparser.add_argument(
"--diameter", action="store", type=float, default=72.,
"--diameter", action="store", type=float, default=72.,
help="outer diameter of the wheels")
self.argparser.add_argument(
"--rubberthickness", action="store", type=float, default=5.,
"--rubberthickness", action="store", type=float, default=5.,
help="diameter of the strings of the O rings")
self.argparser.add_argument(
"--axle", action="store", type=float, default=6.,
"--axle", action="store", type=float, default=6.,
help="diameter of the axles")
self.argparser.add_argument(
"--knifethickness", action="store", type=float, default=8.,
"--knifethickness", action="store", type=float, default=8.,
help="thickness of the knifes in mm. Use 0 for use with honey comb table.")
def mainPlate(self):
# Motor block outer side
t = self.thickness
d = self.diameter
a = self.axle
self.hole(1.0*d, 0.6*d, a/2.)
#self.hole(1.0*d, 0.6*d, d/2.)
self.hole(2.0*d+5, 0.6*d, a/2.)
#self.hole(2.0*d+5, 0.6*d, d/2.)
self.hole(1.0 * d, 0.6 * d, a / 2.)
# self.hole(1.0*d, 0.6*d, d/2.)
self.hole(2.0 * d + 5, 0.6 * d, a / 2.)
# self.hole(2.0*d+5, 0.6*d, d/2.)
# Main beam
self.rectangularHole(1.5*d+2.5, 3.6, 32, 7.1)
self.rectangularHole(1.5 * d + 2.5, 3.6, 32, 7.1)
def frontPlate(self):
# Motor block inner side with motor mount
t = self.thickness
d = self.diameter
a = self.axle
self.hole(1.0*d, 0.6*d, a/2.)
#self.hole(1.0*d, 0.6*d, d/2.)
self.hole(2.0*d+5, 0.6*d, a/2.)
#self.hole(2.0*d+5, 0.6*d, d/2.)
self.hole(1.0 * d, 0.6 * d, a / 2.)
# self.hole(1.0*d, 0.6*d, d/2.)
self.hole(2.0 * d + 5, 0.6 * d, a / 2.)
# self.hole(2.0*d+5, 0.6*d, d/2.)
# Main beam
self.rectangularHole(1.5*d+2.5, 3.6, 32, 7.1)
self.rectangularHole(1.5 * d + 2.5, 3.6, 32, 7.1)
# Motor
mx = 2.7*d+20
self.rectangularHole(mx, 0.6*d, 36+20, 36, r=36/2.0)
mx = 2.7 * d + 20
self.rectangularHole(mx, 0.6 * d, 36 + 20, 36, r=36 / 2.0)
for x in (-1, 1):
for y in (-1,1):
self.rectangularHole(mx+x*25, 0.6*d+y*25, 20, 4, r=2)
for y in (-1, 1):
self.rectangularHole(mx + x * 25, 0.6 * d + y * 25, 20, 4, r=2)
def link(self, x, y, a, middleHole=False, move=None):
t = self. thickness
t = self.thickness
overallwidth = x + y
overallheight = y
ra = a/2.0
ra = a / 2.0
if self.move(overallwidth, overallheight, move, before=True):
return
self.moveTo(y/2.0, 0)
self.hole(0, y/2., ra)
self.hole(x, y/2., ra)
self.moveTo(y / 2.0, 0)
self.hole(0, y / 2., ra)
self.hole(x, y / 2., ra)
if middleHole:
self.hole(x/2., y/2., ra)
self.hole(x / 2., y / 2., ra)
self.edge(10)
self.edges["F"](60)
self.polyline(x-70, (180, y/2.), x, (180, y/2.))
self.polyline(x - 70, (180, y / 2.), x, (180, y / 2.))
self.ctx.stroke()
self.move(overallwidth, overallheight, move)
def holderBaseCB(self):
self.hole(20, 30, self.a/2)
self.rectangularHole(self.hl-70, self.hh-10, 110, self.a, r=self.a/2)
self.rectangularHole(self.hl/2, 3.6, 32, 7.1)
self.hole(20, 30, self.a / 2)
self.rectangularHole(self.hl - 70, self.hh - 10, 110, self.a, r=self.a / 2)
self.rectangularHole(self.hl / 2, 3.6, 32, 7.1)
def holderTopCB(self):
self.fingerHolesAt(0, 30-0.5*self.thickness, self.hl, 0)
d = self.diameter/2.0 + 1
y = -0.6*self.diameter + 2*self.hh
self.fingerHolesAt(0, 30 - 0.5 * self.thickness, self.hl, 0)
d = self.diameter / 2.0 + 1
y = -0.6 * self.diameter + 2 * self.hh
print(y)
self.hole(self.hl/2+d, y, self.axle/2.0)
self.hole(self.hl/2-d, y, self.axle/2.0)
self.hole(self.hl/2+d, y, self.diameter/2.0)
self.hole(self.hl/2-d, y, self.diameter/2.0)
self.hole(self.hl / 2 + d, y, self.axle / 2.0)
self.hole(self.hl / 2 - d, y, self.axle / 2.0)
self.hole(self.hl / 2 + d, y, self.diameter / 2.0)
self.hole(self.hl / 2 - d, y, self.diameter / 2.0)
def render(self):
# adjust to the variables you want in the local scope
@ -166,11 +177,11 @@ class Rotary(Boxes):
a = self.a = self.axle
# Initialize canvas
self.open()
#self.spacing = 0.1 * t
# self.spacing = 0.1 * t
# Change settings of default edges if needed. E.g.:
self.edges["f"].settings.setValues(self.thickness, space=2, finger=2,
surroundingspaces=1)
surroundingspaces=1)
if self.knifethickness:
self.addPart(HangerEdge(self, self.knifethickness))
else:
@ -181,124 +192,137 @@ class Rotary(Boxes):
hh = self.hh = 40.
hl = self.hl = 240
# Base
self.rectangularWall(hl, hh, edges="hfef", callback=[self.holderBaseCB, None, lambda:self.rectangularHole(hl/2+50, hh-t/2-1, 60, t+2)], move="up")
self.rectangularWall(hl, hh, edges="hfef", callback=[self.holderBaseCB, None,
lambda: self.rectangularHole(hl / 2 + 50, hh - t / 2 - 1,
60, t + 2)], move="up")
self.rectangularWall(hl, hh, edges="hfef", callback=[self.holderBaseCB], move="up")
self.rectangularWall(hl, hw, edges="ffff", callback=[
lambda: self.hole(hl/2-16-20, 25, 5)], move="up")
self.rectangularWall(hl, hw, edges="ffff", callback=[lambda: self.hole(hl / 2 - 16 - 20, 25, 5)], move="up")
self.ctx.save()
self.rectangularWall(hw, hh, edges="hFeF", callback=[
lambda: self.hole(hw/2, 15, 4)],move="right")
self.rectangularWall(hw, hh, edges="hFeF", callback=[lambda: self.hole(hw / 2, 15, 4)], move="right")
self.rectangularWall(hw, hh, edges="hFeF", move="right")
# Top
th = 30
# sides
self.rectangularWall(hw+20, th, edges="fFeF", move="right",
callback=[lambda:self.fingerHolesAt(20-0.5*t,0,th)])
self.rectangularWall(hw+20, th, edges="fFeF", move="right",
callback=[lambda:self.fingerHolesAt(20-0.5*t,0,th)])
self.rectangularWall(hw + 20, th, edges="fFeF", move="right",
callback=[lambda: self.fingerHolesAt(20 - 0.5 * t, 0, th)])
self.rectangularWall(hw + 20, th, edges="fFeF", move="right",
callback=[lambda: self.fingerHolesAt(20 - 0.5 * t, 0, th)])
self.ctx.restore()
self.rectangularWall(hw, hh, edges="hFeF", move="up only")
outset = OutsetEdge(self, None)
roller2 = RollerEdge2(self, None)
self.rectangularWall(hl, th, edges=[roller2, "f", "e", "f"], callback=[
lambda:self.hole(20, 15, a/2), None, lambda:self.rectangularHole(50, th-15, 70, a, r=a/2)], move="up")
lambda: self.hole(20, 15, a / 2), None, lambda: self.rectangularHole(50, th - 15, 70, a, r=a / 2)],
move="up")
self.rectangularWall(hl, th, edges=[roller2, "f", "e", "f"], callback=[
lambda:self.hole(20, 15, a/2), None, lambda:self.rectangularHole(50, th-15-t, 70, a, r=a/2)], move="up")
lambda: self.hole(20, 15, a / 2), None, lambda: self.rectangularHole(50, th - 15 - t, 70, a, r=a / 2)],
move="up")
self.rectangularWall(hl, th, edges=[roller2, "f", RollerEdge(self, None), "f"], callback=[
self.holderTopCB], move="up")
self.rectangularWall(hl, 20-t, edges="feee", move="up")
self.rectangularWall(hl, 20 - t, edges="feee", move="up")
tl = 70
self.rectangularWall(tl, hw+20, edges="FeFF", move="right",
callback=[None, lambda:self.fingerHolesAt(20-0.5*t,0, tl)])
self.rectangularWall(tl, hw+20, edges="FeFF", move="",
callback=[None, lambda:self.fingerHolesAt(20-0.5*t,0, tl)])
self.rectangularWall(tl, hw+20, edges="FeFF", move="left up only",
callback=[None, lambda:self.fingerHolesAt(20-0.5*t,0, tl)])
self.rectangularWall(tl, hw + 20, edges="FeFF", move="right",
callback=[None, lambda: self.fingerHolesAt(20 - 0.5 * t, 0, tl)])
self.rectangularWall(tl, hw + 20, edges="FeFF", move="",
callback=[None, lambda: self.fingerHolesAt(20 - 0.5 * t, 0, tl)])
self.rectangularWall(tl, hw + 20, edges="FeFF", move="left up only",
callback=[None, lambda: self.fingerHolesAt(20 - 0.5 * t, 0, tl)])
# Links
self.link(hl-40, 25, a, True, move="up")
self.link(hl-40, 25, a, True, move="up")
self.link(hl-40, 25, a, True, move="up")
self.link(hl-40, 25, a, True, move="up")
self.link(hl - 40, 25, a, True, move="up")
self.link(hl - 40, 25, a, True, move="up")
self.link(hl - 40, 25, a, True, move="up")
self.link(hl - 40, 25, a, True, move="up")
self.ctx.save()
self.rectangularWall(hw-2*t-2, 60, edges="efef",move="right")
self.rectangularWall(hw-4*t-4, 60, edges="efef",move="right")
self.rectangularWall(hw - 2 * t - 2, 60, edges="efef", move="right")
self.rectangularWall(hw - 4 * t - 4, 60, edges="efef", move="right")
# Spindel auxiliaries
self.parts.waivyKnob(50, callback=lambda:self.nutHole("M8"), move="right")
self.parts.waivyKnob(50, callback=lambda:self.nutHole("M8"), move="right")
self.parts.waivyKnob(50, callback=lambda: self.nutHole("M8"), move="right")
self.parts.waivyKnob(50, callback=lambda: self.nutHole("M8"), move="right")
self.ctx.restore()
self.rectangularWall(hw-2*t-4, 60, edges="efef",move="up only")
self.rectangularWall(hw - 2 * t - 4, 60, edges="efef", move="up only")
self.ctx.save()
slot = edges.SlottedEdge(self, [(30-t)/2, (30-t)/2], slots=15)
slot = edges.SlottedEdge(self, [(30 - t) / 2, (30 - t) / 2], slots=15)
self.rectangularWall(30, 30, edges=["e", "e", slot, "e"],
callback=[lambda:self.hole(7, 23, self.axle/2)], move="right")
callback=[lambda: self.hole(7, 23, self.axle / 2)], move="right")
self.rectangularWall(30, 30, edges=["e", "e", slot, "e"],
callback=[lambda:self.hole(7, 23, self.axle/2)], move="right")
leftover = (hw-6*t-6-20) / 2.0
callback=[lambda: self.hole(7, 23, self.axle / 2)], move="right")
leftover = (hw - 6 * t - 6 - 20) / 2.0
slot = edges.SlottedEdge(self, [leftover, 20, leftover], slots=15)
self.rectangularWall(hw-4*t-6, 30, edges=[slot, "e", "e", "e"],
callback=[lambda:self.hole((hw-4*t-6)/2., 15, 4)], move="right")
self.rectangularWall(hw - 4 * t - 6, 30, edges=[slot, "e", "e", "e"],
callback=[lambda: self.hole((hw - 4 * t - 6) / 2., 15, 4)], move="right")
for i in range(3):
self.rectangularWall(20, 30,
callback=[lambda:self.nutHole("M8", 10, 15)], move="right")
callback=[lambda: self.nutHole("M8", 10, 15)], move="right")
self.rectangularWall(20, 30,
callback=[lambda:self.hole(10, 15, 4)], move="right")
callback=[lambda: self.hole(10, 15, 4)], move="right")
self.ctx.restore()
self.rectangularWall(30, 30, move="up only")
# Other side
if self.knifethickness:
ow = 10
self.rectangularWall(3.6*d, 1.1*d, edges="hfFf", callback=[
lambda:self.rectangularHole(1.8*d, 3.6, 32, 7.1)], move="up")
self.rectangularWall(3.6*d, 1.1*d, edges="hfFf", callback=[
lambda:self.rectangularHole(1.8*d, 3.6, 32, 7.1)], move="up")
self.rectangularWall(3.6*d, ow, edges="ffff", move="up")
self.rectangularWall(3.6*d, ow, edges="ffff", move="up")
self.rectangularWall(3.6 * d, 1.1 * d, edges="hfFf", callback=[
lambda: self.rectangularHole(1.8 * d, 3.6, 32, 7.1)], move="up")
self.rectangularWall(3.6 * d, 1.1 * d, edges="hfFf", callback=[
lambda: self.rectangularHole(1.8 * d, 3.6, 32, 7.1)], move="up")
self.rectangularWall(3.6 * d, ow, edges="ffff", move="up")
self.rectangularWall(3.6 * d, ow, edges="ffff", move="up")
self.ctx.save()
self.rectangularWall(ow, 1.1*d, edges="hFFH", move="right")
self.rectangularWall(ow, 1.1*d, edges="hFFH", move="right")
self.rectangularWall(ow, 1.1 * d, edges="hFFH", move="right")
self.rectangularWall(ow, 1.1 * d, edges="hFFH", move="right")
self.ctx.restore()
self.rectangularWall(ow, 1.1*d, edges="hFFH", move="up only")
self.rectangularWall(ow, 1.1 * d, edges="hFFH", move="up only")
# Motor block
mw = 40
self.rectangularWall(3.6*d, 1.1*d, edges=["h", "f", MotorEdge(self, None),"f"], callback=[self.mainPlate], move="up")
self.rectangularWall(3.6*d, 1.1*d, edges=["h", "f", MotorEdge(self, None),"f"], callback=[self.frontPlate], move="up")
self.rectangularWall(3.6*d, mw, edges="ffff", move="up")
self.rectangularWall(3.6 * d, 1.1 * d, edges=["h", "f", MotorEdge(self, None), "f"], callback=[self.mainPlate],
move="up")
self.rectangularWall(3.6 * d, 1.1 * d, edges=["h", "f", MotorEdge(self, None), "f"], callback=[self.frontPlate],
move="up")
self.rectangularWall(3.6 * d, mw, edges="ffff", move="up")
self.ctx.save()
self.rectangularWall(mw, 1.1*d, edges="hFeH", move="right")
self.rectangularWall(mw, 1.1*d, edges="hFeH", move="right")
self.rectangularWall(mw, 1.1 * d, edges="hFeH", move="right")
self.rectangularWall(mw, 1.1 * d, edges="hFeH", move="right")
self.pulley(88, "GT2_2mm", r_axle=a/2.0,move="right")
self.pulley(88, "GT2_2mm", r_axle=a/2.0,move="right")
self.pulley(88, "GT2_2mm", r_axle=a / 2.0, move="right")
self.pulley(88, "GT2_2mm", r_axle=a / 2.0, move="right")
self.ctx.restore()
self.rectangularWall(mw, 1.1*d, edges="hFeH", move="up only")
self.rectangularWall(mw, 1.1 * d, edges="hFeH", move="up only")
self.axle = 19
for i in range(3):
self.parts.disc(self.diameter-2*self.rubberthickness,
self.parts.disc(self.diameter - 2 * self.rubberthickness,
hole=self.axle, move="right")
self.parts.disc(self.diameter-2*self.rubberthickness,
self.parts.disc(self.diameter - 2 * self.rubberthickness,
hole=self.axle, move="up right")
for i in range(3):
self.parts.disc(self.diameter-2*self.rubberthickness,
self.parts.disc(self.diameter - 2 * self.rubberthickness,
hole=self.axle, move="left")
self.parts.disc(self.diameter-2*self.rubberthickness,
self.parts.disc(self.diameter - 2 * self.rubberthickness,
hole=self.axle, move="left up")
for i in range(3):
self.parts.disc(self.diameter-2*self.rubberthickness+4,
self.parts.disc(self.diameter - 2 * self.rubberthickness + 4,
hole=self.axle, move="right")
self.parts.disc(self.diameter-2*self.rubberthickness+4,
self.parts.disc(self.diameter - 2 * self.rubberthickness + 4,
hole=self.axle, move="right up")
self.close()
def main():
b = Box()
b.parseArgs()
b.render()
if __name__ == '__main__':
main()

View File

@ -16,6 +16,7 @@
from boxes import Boxes
class Silverware(Boxes):
"""Not yet parametrized cuttlery stand with carrying grip
using flex for rounded corners"""
@ -26,20 +27,20 @@ using flex for rounded corners"""
def basePlate(self, x, y, r):
self.roundedPlate(x, y, r, callback=[
lambda: self.fingerHolesAt(x/3.0-r, 0, 0.5*(y-self.thickness)),
lambda: self.fingerHolesAt(x/6.0, 0, 0.5*(y-self.thickness)),
lambda: self.fingerHolesAt(y/2.0-r, 0, x),
lambda: self.fingerHolesAt(x/2.0-r, 0, 0.5*(y-self.thickness))
])
lambda: self.fingerHolesAt(x / 3.0 - r, 0, 0.5 * (y - self.thickness)),
lambda: self.fingerHolesAt(x / 6.0, 0, 0.5 * (y - self.thickness)),
lambda: self.fingerHolesAt(y / 2.0 - r, 0, x),
lambda: self.fingerHolesAt(x / 2.0 - r, 0, 0.5 * (y - self.thickness))
])
def wall(self, x=100, y=100, h=100, r=0):
self.surroundingWall(x,y,r,h, bottom='h', callback={
0 : lambda: self.fingerHolesAt(x/6.0, 0, h-10),
4 : lambda: self.fingerHolesAt(x/3.0-r, 0, h-10),
1 : lambda: self.fingerHolesAt(y/2.0-r, 0, h-10),
3 : lambda: self.fingerHolesAt(y/2.0-r, 0, h-10),
2 : lambda: self.fingerHolesAt(x/2.0-r, 0, h-10),
},
self.surroundingWall(x, y, r, h, bottom='h', callback={
0: lambda: self.fingerHolesAt(x / 6.0, 0, h - 10),
4: lambda: self.fingerHolesAt(x / 3.0 - r, 0, h - 10),
1: lambda: self.fingerHolesAt(y / 2.0 - r, 0, h - 10),
3: lambda: self.fingerHolesAt(y / 2.0 - r, 0, h - 10),
2: lambda: self.fingerHolesAt(x / 2.0 - r, 0, h - 10),
},
move="up")
def centerWall(self, x, h):
@ -47,49 +48,52 @@ using flex for rounded corners"""
self.moveTo(self.edges["f"].spacing(), self.edges["f"].spacing())
for i in range(2, 5):
self.fingerHolesAt(i*x/6.0, 0, h-10)
self.fingerHolesAt(i * x / 6.0, 0, h - 10)
self.edges["f"](x)
self.corner(90)
self.edges["f"](h-10)
self.edges["f"](h - 10)
self.corner(90)
self.handle(x, 150, 120)
#self.handle(x, 40, 30, r=2)
# self.handle(x, 40, 30, r=2)
self.corner(90)
self.edges["f"](h-10)
self.edges["f"](h - 10)
self.corner(90)
self.ctx.restore()
self.moveTo(x+2*self.edges["f"].spacing())
self.moveTo(x + 2 * self.edges["f"].spacing())
##################################################
### main
##################################################
def render(self):
x, y, h, r = 250, 250/1.618, 120, 30
x, y, h, r = 250, 250 / 1.618, 120, 30
self.open()
t = self.thickness
b = self.burn
self.wall(x, y, h, r)
self.centerWall(x,h)
self.centerWall(x, h)
l = (y - t) / 2.0
l = (y-t)/2.0
for i in range(3):
self.rectangularWall(l, h-10, edges="ffef", move="right")
self.rectangularWall(l, h - 10, edges="ffef", move="right")
self.moveTo(-3.0*(l+2*t+8*b), h-10+2*t+8*b)
self.moveTo(-3.0 * (l + 2 * t + 8 * b), h - 10 + 2 * t + 8 * b)
self.basePlate(x, y, r)
self.close()
def main():
b = Silverware()
b.parseArgs()
b.render()
if __name__ == '__main__':
main()

View File

@ -16,40 +16,44 @@
from boxes import *
class TrayInsert(Boxes):
"""Tray insert without floor and outer walls - allows only continuous walls"""
def __init__(self):
Boxes.__init__(self)
self.buildArgParser("sx", "sy", "h", "outside")
def render(self):
if self.outside:
self.sx = self.adjustSize(self.sx, False, False)
self.sy = self.adjustSize(self.sy, False, False)
x = sum(self.sx) + self.thickness * (len(self.sx)-1)
y = sum(self.sy) + self.thickness * (len(self.sy)-1)
x = sum(self.sx) + self.thickness * (len(self.sx) - 1)
y = sum(self.sy) + self.thickness * (len(self.sy) - 1)
h = self.h
t = self.thickness
self.open()
# Inner walls
for i in range(len(self.sx)-1):
e = [edges.SlottedEdge(self, self.sy, slots=0.5*h), "e", "e", "e"]
self.rectangularWall(y, h, e,
move="up")
for i in range(len(self.sy)-1):
e = ["e", "e",
edges.SlottedEdge(self, self.sx[::-1], "e", slots=0.5*h), "e"]
self.rectangularWall(x, h, e,
move="up")
for i in range(len(self.sx) - 1):
e = [edges.SlottedEdge(self, self.sy, slots=0.5 * h), "e", "e", "e"]
self.rectangularWall(y, h, e, move="up")
for i in range(len(self.sy) - 1):
e = ["e", "e", edges.SlottedEdge(self, self.sx[::-1], "e", slots=0.5 * h), "e"]
self.rectangularWall(x, h, e, move="up")
self.close()
def main():
b = TrayInsert()
b.parseArgs()
b.render()
if __name__ == '__main__':
main()

View File

@ -18,8 +18,8 @@ import sys, re
from boxes import *
import boxes
class Layout(Boxes):
class Layout(Boxes):
"""Generate a typetray from a layout file"""
webinterface = False
@ -29,64 +29,69 @@ class Layout(Boxes):
self.buildArgParser("h", "hi", "outside")
if not webargs:
self.argparser.add_argument(
"--input", action="store", type=argparse.FileType('r'),
"--input", action="store", type=argparse.FileType('r'),
help="layout file")
self.argparser.add_argument(
"--x", action="store", type=int, default=None,
"--x", action="store", type=int, default=None,
help="number of compartments side by side")
self.argparser.add_argument(
"--y", action="store", type=int, default=None,
"--y", action="store", type=int, default=None,
help="number of compartments back to front")
else:
self.argparser.add_argument(
"--layout", action="store", type=str)
"--layout", action="store", type=str)
def fillDefault(self, x, y):
self.x = [0.0] * x
self.y = [0.0] * y
self.hwalls = [[True for i in range(x)] for j in range(y+1)]
self.vwalls = [[True for i in range(x+1)] for j in range(y)]
self.hwalls = [[True for i in range(x)] for j in range(y + 1)]
self.vwalls = [[True for i in range(x + 1)] for j in range(y)]
self.floors = [[True for i in range(x)] for j in range(y)]
def __str__(self):
r = []
for i, x in enumerate(self.x):
r.append(" |" * (i) + " ,> %.1fmm\n" % x)
for hwalls, vwalls, floors, y in zip(
self.hwalls, self.vwalls, self.floors, self.y):
r.append("".join(("+" + " -"[h] for h in hwalls)) + "+\n")
r.append("".join(("+" + " -"[h] for h in hwalls)) + "+\n")
r.append("".join((" |"[v] + "X "[f] for v, f in zip(vwalls, floors)))
+ " |"[vwalls[-1]] + " %.1fmm\n" % y)
+ " |"[vwalls[-1]] + " %.1fmm\n" % y)
r.append("".join(("+" + " -"[h] for h in self.hwalls[-1])) + "+\n")
return "".join(r)
def vWalls(self, x, y):
"Number of vertical walls at a crossing"
result = 0
if y>0 and self.vwalls[y-1][x]:
if y > 0 and self.vwalls[y - 1][x]:
result += 1
if y < len(self.y) and self.vwalls[y][x]:
result += 1
return result
def hWalls(self, x, y):
"Number of horizontal walls at a crossing"
result = 0
if x>0 and self.hwalls[y][x-1]:
if x > 0 and self.hwalls[y][x - 1]:
result += 1
if x<len(self.x) and self.hwalls[y][x]:
if x < len(self.x) and self.hwalls[y][x]:
result += 1
return result
def vFloor(self, x, y):
"Is there floor under vertical wall"
return ((x>0 and self.floors[y][x-1]) or
(x<len(self.x) and self.floors[y][x]))
return ((x > 0 and self.floors[y][x - 1]) or
(x < len(self.x) and self.floors[y][x]))
def hFloor(self, x, y):
"Is there foor under horizontal wall"
return ((y>0 and self.floors[y-1][x]) or
(y<len(self.y) and self.floors[y][x]))
return ((y > 0 and self.floors[y - 1][x]) or
(y < len(self.y) and self.floors[y][x]))
@restore
def edgeAt(self, edge, x, y, length, angle=0):
@ -99,6 +104,7 @@ class Layout(Boxes):
self.x = self.adjustSize(self.x)
self.y = self.adjustSize(self.y)
self.h = self.adjustSize(self.h, e2=False)
if self.hi:
self.hi = self.adjustSize(self.hi, e2=False)
@ -117,7 +123,7 @@ class Layout(Boxes):
self.open()
self.edges["s"] = boxes.edges.Slot(self, self.hi/2.0)
self.edges["s"] = boxes.edges.Slot(self, self.hi / 2.0)
self.edges["C"] = boxes.edges.CrossingFingerHoleEdge(self, self.hi)
self.ctx.save()
@ -128,22 +134,30 @@ class Layout(Boxes):
h = self.h
else:
h = self.hi
start = 0
end = 0
while end < lx:
lengths = []
edges = []
while start < lx and not self.hwalls[y][start]: start += 1
while start < lx and not self.hwalls[y][start]:
start += 1
if start == lx:
break
end = start
while end < lx and self.hwalls[y][end]:
if self.hFloor(end, y):
edges.append("f")
else:
edges.append("e") # XXX E?
edges.append("e") # XXX E?
lengths.append(self.x[end])
edges.append("eCs"[self.vWalls(end+1, y)])
edges.append("eCs"[self.vWalls(end + 1, y)])
lengths.append(self.thickness)
end += 1
@ -161,7 +175,7 @@ class Layout(Boxes):
self.ctx.restore()
self.rectangularWall(10, h, "ffef", move="up only")
self.ctx.save()
# Vertical Walls
for x in range(lx + 1):
if x == 0 or x == lx:
@ -170,20 +184,26 @@ class Layout(Boxes):
h = self.hi
start = 0
end = 0
while end < ly:
lengths = []
edges = []
while start < ly and not self.vwalls[start][x]: start += 1
while start < ly and not self.vwalls[start][x]:
start += 1
if start == ly:
break
end = start
while end < ly and self.vwalls[end][x]:
while end < ly and self.vwalls[end][x]:
if self.vFloor(x, end):
edges.append("f")
else:
edges.append("e") # XXX E?
edges.append("e") # XXX E?
lengths.append(self.y[end])
edges.append("eCs"[self.hWalls(x, end+1)])
edges.append("eCs"[self.hWalls(x, end + 1)])
lengths.append(self.thickness)
end += 1
# remove last "slot"
@ -191,23 +211,23 @@ class Layout(Boxes):
edges.pop()
upper = [{
"f" : "e",
"s" : "s",
"e" : "e",
"E" : "e",
"C" : "e"}[e] for e in reversed(edges)]
"f": "e",
"s": "s",
"e": "e",
"E": "e",
"C": "e"}[e] for e in reversed(edges)]
edges = ["e" if e == "s" else e for e in edges]
self.rectangularWall(sum(lengths), h, [
boxes.edges.CompoundEdge(self, edges, lengths),
"eFf"[self.hWalls(x, end)],
boxes.edges.CompoundEdge(self, upper, list(reversed(lengths))),
"eFf"[self.hWalls(x, start)] ],
move="right")
"eFf"[self.hWalls(x, start)]],
move="right")
start = end
self.ctx.restore()
self.rectangularWall(10, h, "ffef", move="up only")
self.moveTo(2*self.thickness, 2*self.thickness)
self.moveTo(2 * self.thickness, 2 * self.thickness)
self.ctx.save()
##########################################################
@ -224,61 +244,61 @@ class Layout(Boxes):
else:
e = "e"
if y < ly and self.floors[y][x]:
if y>0 and self.floors[y-1][x]:
if y > 0 and self.floors[y - 1][x]:
# Inside Wall
if self.hwalls[y][x]:
self.fingerHolesAt(posx, posy+t2, self.x[x], angle=0)
self.fingerHolesAt(posx, posy + t2, self.x[x], angle=0)
else:
# Top edge
self.edgeAt(e, posx+self.x[x], posy+t, self.x[x],
self.edgeAt(e, posx + self.x[x], posy + t, self.x[x],
-180)
if x==0 or y==0 or not self.floors[y-1][x-1]:
self.edgeAt("e", posx, posy+t, t, -180)
if x==lx-1 or y==0 or not self.floors[y-1][x+1]:
self.edgeAt("e", posx+self.x[x]+t, posy+t, t, -180)
elif y>0 and self.floors[y-1][x]:
if x == 0 or y == 0 or not self.floors[y - 1][x - 1]:
self.edgeAt("e", posx, posy + t, t, -180)
if x == lx - 1 or y == 0 or not self.floors[y - 1][x + 1]:
self.edgeAt("e", posx + self.x[x] + t, posy + t, t, -180)
elif y > 0 and self.floors[y - 1][x]:
# Bottom Edge
self.edgeAt(e, posx, posy, self.x[x])
if x==0 or y==ly or not self.floors[y][x-1]:
self.edgeAt("e", posx-t, posy, t)
if x==lx-1 or y==ly or not self.floors[y][x+1]:
self.edgeAt("e", posx+self.x[x], posy, t)
posx += self.x[x] + self. thickness
posy += self.y[y-1] + self.thickness
if x == 0 or y == ly or not self.floors[y][x - 1]:
self.edgeAt("e", posx - t, posy, t)
if x == lx - 1 or y == ly or not self.floors[y][x + 1]:
self.edgeAt("e", posx + self.x[x], posy, t)
posx += self.x[x] + self.thickness
posy += self.y[y - 1] + self.thickness
posx = 0
for x in range(lx+1):
for x in range(lx + 1):
posy = self.thickness
for y in range(ly-1, -1, -1):
for y in range(ly - 1, -1, -1):
if self.vwalls[y][x]:
e = "F"
else:
e = "e"
if x>0 and self.floors[y][x-1]:
if x > 0 and self.floors[y][x - 1]:
if x < lx and self.floors[y][x]:
# Inside wall
if self.vwalls[y][x]:
self.fingerHolesAt(posx+t2, posy, self.y[y])
self.fingerHolesAt(posx + t2, posy, self.y[y])
else:
# Right edge
self.edgeAt(e, posx+t, posy, self.y[y], 90)
if x==lx or y==0 or not self.floors[y-1][x]:
self.edgeAt("e", posx+t, posy+self.y[y], t, 90)
if x==lx or y==ly-1 or not self.floors[y+1][x]:
self.edgeAt("e", posx+t, posy-t, t, 90)
self.edgeAt(e, posx + t, posy, self.y[y], 90)
if x == lx or y == 0 or not self.floors[y - 1][x]:
self.edgeAt("e", posx + t, posy + self.y[y], t, 90)
if x == lx or y == ly - 1 or not self.floors[y + 1][x]:
self.edgeAt("e", posx + t, posy - t, t, 90)
elif x < lx and self.floors[y][x]:
# Left edge
self.edgeAt(e, posx, posy+self.y[y], self.y[y], -90)
if x==0 or y==0 or not self.floors[y-1][x-1]:
self.edgeAt("e", posx, posy+self.y[y]+t, t, -90)
if x==0 or y==ly-1 or not self.floors[y+1][x-1]:
self.edgeAt(e, posx, posy + self.y[y], self.y[y], -90)
if x == 0 or y == 0 or not self.floors[y - 1][x - 1]:
self.edgeAt("e", posx, posy + self.y[y] + t, t, -90)
if x == 0 or y == ly - 1 or not self.floors[y + 1][x - 1]:
self.edgeAt("e", posx, posy, t, -90)
posy += self.y[y] + self.thickness
if x < lx:
posx += self.x[x] + self.thickness
self.close()
def parse(self, input):
x = []
y = []
@ -302,18 +322,17 @@ class Layout(Boxes):
w.append(True)
else:
pass
#raise ValueError(line)
# raise ValueError(line)
else:
if c != '+':
pass
#raise ValueError(line)
# raise ValueError(line)
hwalls.append(w)
if line[0] in " |":
w = []
f = []
for n, c in enumerate(line[:len(x)*2+1]):
for n, c in enumerate(line[:len(x) * 2 + 1]):
if n % 2:
if c == 'X':
f.append(False)
@ -340,25 +359,26 @@ class Layout(Boxes):
# check sizes
lx = len(x)
ly = len(y)
if len(hwalls) != ly+1:
raise ValueError("Wrong number of horizontal wall lines: %i (%i expected)" % (len(hwalls), ly+1))
if len(hwalls) != ly + 1:
raise ValueError("Wrong number of horizontal wall lines: %i (%i expected)" % (len(hwalls), ly + 1))
for nr, walls in enumerate(hwalls):
if len(walls)!= lx:
if len(walls) != lx:
raise ValueError("Wrong number of horizontal walls in line %i: %i (%i expected)" % (nr, len(walls), lx))
if len(vwalls) != ly:
raise ValueError("Wrong number of vertical wall lines: %i (%i expected)" % (len(vwalls), ly))
for nr, walls in enumerate(vwalls):
if len(walls) != lx+1:
raise ValueError("Wrong number of vertical walls in line %i: %i (%i expected)" % (nr, len(walls), lx+1))
if len(walls) != lx + 1:
raise ValueError(
"Wrong number of vertical walls in line %i: %i (%i expected)" % (nr, len(walls), lx + 1))
self.x = x
self.y = y
self.hwalls = hwalls
self.vwalls = vwalls
self.floors = floors
class TrayLayout(Layout):
class TrayLayout(Layout):
"""Type tray with each wall and floor tile being optional"""
webinterface = True
@ -367,17 +387,17 @@ class TrayLayout(Layout):
Boxes.__init__(self)
self.argparser = boxes.ArgumentParser()
self.argparser.add_argument(
"--x", action="store", type=int, default=2,
"--x", action="store", type=int, default=2,
help="number of compartments side by side")
self.argparser.add_argument(
"--y", action="store", type=int, default=2,
"--y", action="store", type=int, default=2,
help="number of compartments back to front")
def render(self):
return
class TrayLayout2(Layout):
class TrayLayout2(Layout):
"""Generate a typetray from a layout file"""
webinterface = True
@ -386,7 +406,8 @@ class TrayLayout2(Layout):
Boxes.__init__(self)
self.buildArgParser("h", "hi", "outside")
self.argparser.add_argument(
"--layout", action="store", type=str)
"--layout", action="store", type=str)
def main():
l = Layout()
@ -404,5 +425,6 @@ def main():
else:
l.argparser.print_usage()
if __name__ == '__main__':
main()

View File

@ -16,22 +16,24 @@
from boxes import *
class TypeTray(Boxes):
"""Type tray - allows only continuous walls"""
def __init__(self):
Boxes.__init__(self)
self.buildArgParser("sx", "sy", "h", "hi", "outside")
self.argparser.add_argument(
"--gripheight", action="store", type=float, default=30,
"--gripheight", action="store", type=float, default=30,
dest="gh", help="height of the grip hole in mm")
self.argparser.add_argument(
"--gripwidth", action="store", type=float, default=70,
"--gripwidth", action="store", type=float, default=70,
dest="gw", help="width of th grip hole in mm (zero for no hole)")
self.argparser.set_defaults(
fingerjointfinger=3.0,
fingerjointspace=3.0,
fingerjointsurrounding=0.5,
)
)
def xSlots(self):
posx = -0.5 * self.thickness
@ -67,9 +69,9 @@ class TypeTray(Boxes):
if not self.gw:
return
x = sum(self.sx) + self.thickness * (len(self.sx)-1)
x = sum(self.sx) + self.thickness * (len(self.sx) - 1)
r = min(self.gw, self.gh) / 2.0
self.rectangularHole(x/2.0, self.gh*1.5, self.gw, self.gh, r)
self.rectangularHole(x / 2.0, self.gh * 1.5, self.gw, self.gh, r)
def render(self):
if self.outside:
@ -79,8 +81,8 @@ class TypeTray(Boxes):
if self.hi:
self.hi = self.adjustSize(self.hi, e2=False)
x = sum(self.sx) + self.thickness * (len(self.sx)-1)
y = sum(self.sy) + self.thickness * (len(self.sy)-1)
x = sum(self.sx) + self.thickness * (len(self.sx) - 1)
y = sum(self.sy) + self.thickness * (len(self.sy) - 1)
h = self.h
hi = self.hi = self.hi or h
t = self.thickness
@ -88,35 +90,31 @@ class TypeTray(Boxes):
self.open()
# outer walls
self.rectangularWall(x, h, "Ffef", callback=[
self.xHoles, None, self.gripHole],
move="right")
self.rectangularWall(y, h, "FFeF", callback=[self.yHoles,],
move="up")
self.rectangularWall(y, h, "FFeF", callback=[self.yHoles,])
self.rectangularWall(x, h, "Ffef", callback=[self.xHoles,],
move="left up")
self.rectangularWall(x, h, "Ffef", callback=[self.xHoles, None, self.gripHole], move="right")
self.rectangularWall(y, h, "FFeF", callback=[self.yHoles, ], move="up")
self.rectangularWall(y, h, "FFeF", callback=[self.yHoles, ])
self.rectangularWall(x, h, "Ffef", callback=[self.xHoles, ], move="left up")
# floor
self.rectangularWall(x, y, "ffff",
callback=[self.xSlots, self.ySlots],
move="right")
self.rectangularWall(x, y, "ffff", callback=[self.xSlots, self.ySlots],move="right")
# Inner walls
for i in range(len(self.sx)-1):
e = [edges.SlottedEdge(self, self.sy, "f", slots=0.5*hi), "f", "e", "f"]
self.rectangularWall(y, hi, e,
move="up")
for i in range(len(self.sy)-1):
for i in range(len(self.sx) - 1):
e = [edges.SlottedEdge(self, self.sy, "f", slots=0.5 * hi), "f", "e", "f"]
self.rectangularWall(y, hi, e, move="up")
for i in range(len(self.sy) - 1):
e = [edges.SlottedEdge(self, self.sx, "f"), "f",
edges.SlottedEdge(self, self.sx[::-1], "e", slots=0.5*hi), "f"]
self.rectangularWall(x, hi, e,
move="up")
edges.SlottedEdge(self, self.sx[::-1], "e", slots=0.5 * hi), "f"]
self.rectangularWall(x, hi, e, move="up")
self.close()
def main():
b = TypeTray()
b.parseArgs()
b.render()
if __name__ == '__main__':
main()

View File

@ -1,14 +1,16 @@
from math import *
def arcOnCircle(spanning_angle, outgoing_angle, r=1.0):
angle = spanning_angle+2*outgoing_angle
radius = r * sin(radians(0.5*spanning_angle))/sin(radians(180-outgoing_angle-0.5*spanning_angle))
angle = spanning_angle + 2 * outgoing_angle
radius = r * sin(radians(0.5 * spanning_angle)) / sin(radians(180 - outgoing_angle - 0.5 * spanning_angle))
return angle, abs(radius)
class Parts:
class Parts:
def __init__(self, boxes):
self.boxes = boxes
"""
def roundKnob(self, diameter, n=20, callback=None, move=""):
size = diameter+diameter/n
@ -22,55 +24,69 @@ class Parts:
def __getattr__(self, name):
return getattr(self.boxes, name)
def disc(self, diameter, hole=0, callback=None, move=""):
size = diameter
r = diameter/2.0
r = diameter / 2.0
if self.move(size, size, move, before=True):
return
self.moveTo(size/2, size/2)
self.moveTo(size / 2, size / 2)
if hole:
self.hole(0, 0, hole/2)
self.hole(0, 0, hole / 2)
self.cc(callback, None, 0, 0)
self.moveTo(r+self.burn,0, 90)
self.moveTo(r + self.burn, 0, 90)
self.corner(360, r)
self.move(size, size, move)
def waivyKnob(self, diameter, n=20, angle=45, hole=0, callback=None, move=""):
size = diameter+pi*diameter/n
size = diameter + pi * diameter / n
if self.move(size, size, move, before=True):
return
self.moveTo(size/2, size/2)
self.moveTo(size / 2, size / 2)
self.cc(callback, None, 0, 0)
if hole:
self.hole(0, 0, hole/2)
self.moveTo(diameter/2, 0, angle)
a, r = arcOnCircle(360./n, angle, diameter/2)
a2, r2 = arcOnCircle(360./n, -angle, diameter/2)
for i in range(n//2):
self.hole(0, 0, hole / 2)
self.moveTo(diameter / 2, 0, angle)
a, r = arcOnCircle(360. / n, angle, diameter / 2)
a2, r2 = arcOnCircle(360. / n, -angle, diameter / 2)
for i in range(n // 2):
self.boxes.corner(a, r)
self.boxes.corner(a2, r2)
self.move(size, size, move)
def concaveKnob(self, diameter, n=3, rounded=0.2, angle=70, hole=0,
callback=None, move=""):
size = diameter
if self.move(size, size, move, before=True):
return
self.moveTo(size/2, size/2)
self.moveTo(size / 2, size / 2)
if hole:
self.hole(0, 0, hole/2)
self.hole(0, 0, hole / 2)
self.cc(callback, None, 0, 0)
self.moveTo(diameter/2, 0, 90+angle)
a, r = arcOnCircle(360./n*(1-rounded), -angle, diameter/2)
if abs(a) < 0.01: # avoid trying to make a straight line as an arc
a, r = arcOnCircle(360./n*(1-rounded), -angle-0.01, diameter/2)
self.moveTo(diameter / 2, 0, 90 + angle)
a, r = arcOnCircle(360. / n * (1 - rounded), -angle, diameter / 2)
if abs(a) < 0.01: # avoid trying to make a straight line as an arc
a, r = arcOnCircle(360. / n * (1 - rounded), -angle - 0.01, diameter / 2)
for i in range(n):
self.boxes.corner(a, r)
self.corner(angle)
self.corner(360./n*rounded, diameter/2)
self.corner(360. / n * rounded, diameter / 2)
self.corner(angle)
self.move(size, size, move)

View File

@ -14,11 +14,14 @@
from math import *
from boxes.vectors import *
def tooth_spaceing_curvefit(teeth, b, c, d):
return ((c * teeth**d) / (b + teeth**d)) * teeth
return ((c * teeth ** d) / (b + teeth ** d)) * teeth
def tooth_spacing(teeth, tooth_pitch, pitch_line_offset):
return (2*((teeth*tooth_pitch)/(3.14159265*2)-pitch_line_offset))
return (2 * ((teeth * tooth_pitch) / (3.14159265 * 2) - pitch_line_offset))
def mirrorx(points):
return [[-x, y] for x, y in points]
@ -26,36 +29,37 @@ def mirrorx(points):
class Pulley:
spacing = {
"MXL" : (False, 2.032,0.254),
"40DP" : (False, 2.07264,0.1778),
"XL" : (False, 5.08,0.254),
"H" : (False, 9.525,0.381),
"T2_5" : (True, 0.7467,0.796,1.026),
"T5" : (True, 0.6523,1.591,1.064),
"T10" : (False, 10,0.93),
"AT5" : (True, 0.6523,1.591,1.064),
"HTD_3mm" : (False, 3,0.381),
"HTD_5mm" : (False, 5,0.5715),
"HTD_8mm" : (False, 8,0.6858),
"GT2_2mm" : (False, 2,0.254),
"GT2_3mm" : (False, 3,0.381),
"GT2_5mm" : (False, 5,0.5715),
"MXL": (False, 2.032, 0.254),
"40DP": (False, 2.07264, 0.1778),
"XL": (False, 5.08, 0.254),
"H": (False, 9.525, 0.381),
"T2_5": (True, 0.7467, 0.796, 1.026),
"T5": (True, 0.6523, 1.591, 1.064),
"T10": (False, 10, 0.93),
"AT5": (True, 0.6523, 1.591, 1.064),
"HTD_3mm": (False, 3, 0.381),
"HTD_5mm": (False, 5, 0.5715),
"HTD_8mm": (False, 8, 0.6858),
"GT2_2mm": (False, 2, 0.254),
"GT2_3mm": (False, 3, 0.381),
"GT2_5mm": (False, 5, 0.5715),
}
profile_data = {
"MXL" : (0.508 , 1.321 ),
"40DP" : (0.457 , 1.226 ),
"XL" : (1.27, 3.051 ),
"H" : (1.905 , 5.359 ),
"T2_5" : (0.7 , 1.678 ),
"T5" : (1.19 , 3.264 ),
"T10" : (2.5 , 6.13 ),
"AT5" : (1.19 , 4.268 ),
"HTD_3mm" : (1.289 , 2.27 ),
"HTD_5mm" : (2.199 , 3.781 ),
"HTD_8mm" : (3.607 , 6.603 ),
"GT2_2mm" : (0.764 , 1.494 ),
"GT2_3mm" : (1.169 , 2.31 ),
"GT2_5mm" : (1.969 , 3.952 ),
"MXL": (0.508, 1.321),
"40DP": (0.457, 1.226),
"XL": (1.27, 3.051),
"H": (1.905, 5.359),
"T2_5": (0.7, 1.678),
"T5": (1.19, 3.264),
"T10": (2.5, 6.13),
"AT5": (1.19, 4.268),
"HTD_3mm": (1.289, 2.27),
"HTD_5mm": (2.199, 3.781),
"HTD_8mm": (3.607, 6.603),
"GT2_2mm": (0.764, 1.494),
"GT2_3mm": (1.169, 2.31),
"GT2_5mm": (1.969, 3.952),
}
teeth = { "MXL" : [[-0.660421,-0.5],[-0.660421,0],[-0.621898,0.006033],[-0.587714,0.023037],[-0.560056,0.049424],[-0.541182,0.083609],[-0.417357,0.424392],[-0.398413,0.458752],[-0.370649,0.48514],[-0.336324,0.502074],[-0.297744,0.508035],[0.297744,0.508035],[0.336268,0.502074],[0.370452,0.48514],[0.39811,0.458752],[0.416983,0.424392],[0.540808,0.083609],[0.559752,0.049424],[0.587516,0.023037],[0.621841,0.006033],[0.660421,0],[0.660421,-0.5]],
@ -83,20 +87,21 @@ class Pulley:
def drawPoints(self, lines, kerfdir=1):
if kerfdir != 0:
lines = kerf(lines, self.boxes.burn*kerfdir)
lines = kerf(lines, self.boxes.burn * kerfdir)
self.boxes.ctx.save()
self.boxes.ctx.move_to(*lines[0])
for x, y in lines[1:]:
self.boxes.ctx.line_to(x, y)
self.boxes.ctx.line_to(*lines[0])
self.boxes.ctx.restore()
def diameter(self, teeth, profile):
if self.spacing[profile][0]:
return tooth_spaceing_curvefit(
teeth, *self.spacing[profile][1:])
else:
return tooth_spacing(teeth, *self.spacing[profile][1:])
return tooth_spaceing_curvefit(teeth, *self.spacing[profile][1:])
return tooth_spacing(teeth, *self.spacing[profile][1:])
def __call__(self, teeth, profile, move="", r_axle=None, callback=None):
@ -105,31 +110,35 @@ class Pulley:
# ********************************
# To improve fit of belt to pulley, set the following constant. Decrease or increase by 0.1mm at a time. We are modelling the *BELT* tooth here, not the tooth on the pulley. Increasing the number will *decrease* the pulley tooth size. Increasing the tooth width will also scale proportionately the tooth depth, to maintain the shape of the tooth, and increase how far into the pulley the tooth is indented. Can be negative
additional_tooth_width = 0.2 #mm
additional_tooth_width = 0.2 # mm
# If you need more tooth depth than this provides, adjust the following constant. However, this will cause the shape of the tooth to change.
additional_tooth_depth = 0 #mm
additional_tooth_depth = 0 # mm
pulley_OD = self.diameter(teeth, profile)
tooth_depth, tooth_width = self.profile_data[profile]
tooth_distance_from_centre = ((pulley_OD/2)**2 - ((tooth_width+additional_tooth_width)/2)**2)**0.5
tooth_width_scale = (tooth_width + additional_tooth_width ) / tooth_width
tooth_depth_scale = ((tooth_depth + additional_tooth_depth ) / tooth_depth)
tooth_distance_from_centre = ((pulley_OD / 2) ** 2 - ((tooth_width + additional_tooth_width) / 2) ** 2) ** 0.5
tooth_width_scale = (tooth_width + additional_tooth_width) / tooth_width
tooth_depth_scale = ((tooth_depth + additional_tooth_depth) / tooth_depth)
total_width = pulley_OD
if self.boxes.move(total_width, total_width, move, before=True):
return
self.boxes.moveTo(total_width/2, total_width/2)
self.boxes.moveTo(total_width / 2, total_width / 2)
self.boxes.cc(callback, None, 0.0, 0.0)
if r_axle:
self.boxes.hole(0, 0, r_axle)
points = []
for i in range(teeth):
for i in range(teeth):
m = [[tooth_width_scale, 0, 0],
[0, tooth_depth_scale, -tooth_distance_from_centre]]
m = mmul(m, rotm(i*2*pi/teeth))
m = mmul(m, rotm(i * 2 * pi / teeth))
points.extend((vtransl(pt, m) for pt in self.teeth[profile][1:-1]))
self.drawPoints(points)
self.drawPoints(points)
self.boxes.move(total_width, total_width, move)

View File

@ -17,8 +17,8 @@
import xml.parsers.expat
import re
class SVGFile(object):
class SVGFile(object):
pathre = re.compile(r"[MCL]? *((-?\d+(\.\d+)?) (-?\d+(\.\d+)?) *)+")
transformre = re.compile(r"matrix\(" + ",".join([r"(-?\d+(\.\d+)?)"] * 6) + "\)")
@ -31,29 +31,35 @@ class SVGFile(object):
if name == "path" and "symbol" not in self.tags:
minx = maxx = miny = maxy = None
m = self.transformre.match(attrs.get("transform", ""))
if m:
matrix = [float(m.group(i)) for i in range(1, 12, 2)]
else:
matrix = [1, 0,
0, 1,
0, 0]
for m in self.pathre.findall(attrs.get("d", "")):
x = float(m[1])
y = float(m[3])
tx = matrix[0]*x+matrix[2]*y+matrix[4]
ty = matrix[1]*x+matrix[3]*y+matrix[5]
tx = matrix[0] * x + matrix[2] * y + matrix[4]
ty = matrix[1] * x + matrix[3] * y + matrix[5]
if self.minx is None or self.minx > tx:
self.minx = tx
if self.maxx is None or self.maxx < tx:
self.maxx = tx
if self.miny is None or self.miny > ty:
self.miny = ty
if self.maxy is None or self.maxy < ty:
self.maxy = ty
def handleEndElement(self, name):
last = self.tags.pop()
if last != name:
raise ValueError("Got </%s> expected </%s>" % (name, last))
@ -70,27 +76,29 @@ class SVGFile(object):
m = re.search(r"""<svg[^>]*(width="(\d+pt)" height="(\d+pt)" viewBox="0 (0 (\d+) (\d+))") version="1.1">""", s)
#minx = 10*int(self.minx//10)-10
# minx = 10*int(self.minx//10)-10
# as we don't rewrite the left border keep it as 0
if 0 <= self.minx <= 50:
minx = 0
else:
minx = 10*int(self.minx//10)-10
#raise ValueError("Left end of drawing at wrong place: %imm (0-50mm expected)" % self.minx)
maxx = 10*int(self.maxx//10)+10
miny = 10*int(self.miny//10)-10
maxy = 10*int(self.maxy//10)+10
minx = 10 * int(self.minx // 10) - 10
# raise ValueError("Left end of drawing at wrong place: %imm (0-50mm expected)" % self.minx)
maxx = 10 * int(self.maxx // 10) + 10
miny = 10 * int(self.miny // 10) - 10
maxy = 10 * int(self.maxy // 10) + 10
if m:
f.seek(m.start(1))
s = ('width="%imm" height="%imm" viewBox="0 %i %i %i"' %
(maxx-minx, maxy-miny, miny, maxx, maxy-miny))
(maxx - minx, maxy - miny, miny, maxx, maxy - miny))
if len(s) > len(m.group(1)):
raise ValueError("Not enough space for size")
f.write(s + " " * (len(m.group(1))- len(s)))
f.write(s + " " * (len(m.group(1)) - len(s)))
else:
raiseValueError("Could not understand SVG file")
raise ValueError("Could not understand SVG file")
if __name__ == "__main__":
svg = SVGFile("examples/box.svg")

View File

@ -14,72 +14,86 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import math
def normalize(v):
"set lenght of vector to one"
l = (v[0]**2+v[1]**2)**0.5
return (v[0]/l, v[1]/l)
l = (v[0] ** 2 + v[1] ** 2) ** 0.5
return (v[0] / l, v[1] / l)
def vlength(v):
return (v[0]**2+v[1]**2)**0.5
return (v[0] ** 2 + v[1] ** 2) ** 0.5
def vclip(v, length):
l = vlength(v)
if l > length:
return vscalmul(v, length/l)
return vscalmul(v, length / l)
return v
def vdiff(p1, p2):
"vector from point1 to point2"
return (p2[0]-p1[0], p2[1]-p1[1])
return (p2[0] - p1[0], p2[1] - p1[1])
def vadd(v1, v2):
"Sum of two vectors"
return (v1[0]+ v2[0], v1[1]+v2[1])
return (v1[0] + v2[0], v1[1] + v2[1])
def vorthogonal(v):
"orthogonal vector"
"Orthogonal vector"
return (-v[1], v[0])
def vscalmul(v, a):
"scale vector by a"
return (a*v[0], a*v[1])
return (a * v[0], a * v[1])
def dotproduct(v1, v2):
"Dot product"
return v1[0]*v2[0]+v1[1]*v2[1]
return v1[0] * v2[0] + v1[1] * v2[1]
def rotm(angle):
"Rotation matrix"
return [[math.cos(angle), -math.sin(angle), 0],
[math.sin(angle), math.cos(angle), 0]]
def vtransl(v, m):
m0, m1 = m
return [m0[0]*v[0]+m0[1]*v[1]+m0[2],
m1[0]*v[0]+m1[1]*v[1]+m1[2]]
return [m0[0] * v[0] + m0[1] * v[1] + m0[2],
m1[0] * v[0] + m1[1] * v[1] + m1[2]]
def mmul(m0, m1):
result = [[0,]*len(m0[0]) for i in range(len(m0))]
result = [[0, ] * len(m0[0]) for i in range(len(m0))]
for i in range(len(m0[0])):
for j in range(len(m0)):
for k in range(len(m0)):
result[j][i] += m0[k][i] * m1[j][k]
return result
def kerf(points, k):
"""Outset points by k
Assumes a closed loop of points
"""
result = []
lp = len(points)
for i in range(len(points)):
# get normalized orthogonals of both segments
v1 = vorthogonal(normalize(vdiff(points[i-1], points[i])))
v2 = vorthogonal(normalize(vdiff(points[i], points[(i+1) % lp])))
v1 = vorthogonal(normalize(vdiff(points[i - 1], points[i])))
v2 = vorthogonal(normalize(vdiff(points[i], points[(i + 1) % lp])))
# direction the point has to move
d = normalize(vadd(v1, v2))
# cos of the half the angle between the segments
cos_alpha = dotproduct(v1, d)
result.append(vadd(points[i], vscalmul(d, -k/cos_alpha)))
result.append(vadd(points[i], vscalmul(d, -k / cos_alpha)))
return result