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

1
.gitignore vendored
View File

@ -8,3 +8,4 @@
build/
dist/
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):
@ -42,6 +44,7 @@ def dist(dx, dy):
"""
return (dx * dx + dy * dy) ** 0.5
def restore(func):
"""
Wrapper: Restore coordiantes after function
@ -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
#############################################################################
@ -121,6 +128,7 @@ class NutHole:
self.boxes.edge(side)
self.boxes.corner(-60)
##############################################################################
### Argument types
##############################################################################
@ -146,6 +154,7 @@ def argparseSections(s):
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
##############################################################################
@ -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
@ -376,6 +388,7 @@ class Boxes:
walls += e1.startwidth() + e1.margin()
elif e1:
walls += self.thickness
if isinstance(e2, edges.BaseEdge):
walls += e2.startwidth + e2.margin()
elif e2:
@ -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):
@ -840,7 +855,6 @@ class Boxes:
y * 0.5 * holedistance,
0.5 * diameter)
# hexHoles
def hexHolesRectangle(self, x, y, settings=None, skip=None):
@ -909,6 +923,7 @@ class Boxes:
:param settings: (Default value = None)
"""
def skip(x, y, r, b, posx, posy):
"""
@ -1102,7 +1117,6 @@ class Boxes:
self.edges["d"].spacing() + self.edges["D"].spacing()
overallheight = h + top.spacing() + bottom.spacing()
if self.move(overallwidth, overallheight, move, before=True):
return

View File

@ -18,6 +18,7 @@
import math
import inspect
def getDescriptions():
d = {edge.char: edge.description for edge in globals().values()
if inspect.isclass(edge) and issubclass(edge, BaseEdge)
@ -25,6 +26,7 @@ def getDescriptions():
d['k'] = "Straight edge with hinge eye (both ends)"
return d
class BoltPolicy(object):
"""Abstract class
@ -32,6 +34,7 @@ class BoltPolicy(object):
(fingers of a finger joint)
"""
def drawbolt(self, pos):
"""Add a bolt to this segment?
@ -46,7 +49,7 @@ class BoltPolicy(object):
:param numfingers: number of fingers to aim for
"""
return numFingers
return numfingers
def _even(self, numFingers):
"""
@ -56,6 +59,7 @@ class BoltPolicy(object):
"""
return (numFingers // 2) * 2
def _odd(self, numFingers):
"""
Return same or next smaller odd number
@ -68,8 +72,10 @@ class BoltPolicy(object):
else:
return numFingers - 1
class Bolts(BoltPolicy):
"""Distribute a fixed number of bolts evenly"""
def __init__(self, bolts=1):
self.bolts = bolts
@ -78,6 +84,7 @@ class Bolts(BoltPolicy):
self.fingers = self._even(numFingers)
else:
self.fingers = numFingers
return self.fingers
def drawBolt(self, pos):
@ -89,14 +96,16 @@ class Bolts(BoltPolicy):
"""
if pos > self.fingers // 2:
pos = self.fingers - pos
if pos == 0:
return False
if pos == self.fingers // 2 and not (self.bolts % 2):
return False
result = (math.floor((float(pos)*(self.bolts+1)/self.fingers)-0.01) !=
return (math.floor((float(pos) * (self.bolts + 1) / self.fingers) - 0.01) !=
math.floor((float(pos + 1) * (self.bolts + 1) / self.fingers) - 0.01))
#print pos, result, ((float(pos)*(self.bolts+1)/self.fingers)-0.01), ((float(pos+1)*(self.bolts+1)/self.fingers)-0.01)
return result
#############################################################################
### Settings
@ -150,6 +159,7 @@ class Settings(object):
def __getattr__(self, name):
return self.values[name]
#############################################################################
### Edges
#############################################################################
@ -198,11 +208,13 @@ class BaseEdge(object):
"""Not yet supported"""
return 0.0
class Edge(BaseEdge):
"""Straight edge"""
char = 'e'
description = "Straight Edge"
class OutSetEdge(BaseEdge):
"""Straight edge out set by one thickness"""
char = 'E'
@ -211,6 +223,7 @@ class OutSetEdge(BaseEdge):
def startwidth(self):
return self.boxes.thickness
#############################################################################
#### Gripping Edge
#############################################################################
@ -239,11 +252,11 @@ Values:
"depth": 0.3,
}
class GrippingEdge(BaseEdge):
description = """Corrugated edge useful as an gipping area"""
char = 'g'
def A(self, length):
depth = self.settings.depth
grooves = int(length // (depth * 2.0)) + 1
@ -260,15 +273,18 @@ class GrippingEdge(BaseEdge):
grooves = int(length // (depth * 2.0)) + 1
depth = length / grooves / 2.0
o = 1 if self.settings.outset else -1
if self.settings.outset:
self.corner(-90)
else:
self.corner(90)
self.edge(depth)
self.corner(-180)
for groove in range(grooves):
self.corner(180, depth)
self.corner(-180, 0)
if self.settings.outset:
self.corner(90)
else:
@ -286,12 +302,14 @@ class GrippingEdge(BaseEdge):
return
getattr(self, self.settings.style)(length)
class CompoundEdge(BaseEdge):
"""Edge composed of multiple different Edges"""
description = "Compound Edge"
def __init__(self, boxes, types, lengths):
super(CompoundEdge, self).__init__(boxes, None)
self.types = [self.edges.get(edge, edge) for edge in types]
self.lengths = lengths
self.length = sum(lengths)
@ -309,8 +327,10 @@ class CompoundEdge(BaseEdge):
if length and abs(length - self.length) > 1E-5:
raise ValueError("Wrong length for CompoundEdge")
lastwidth = self.types[0].startwidth()
for e, l in zip(self.types, self.lengths):
diff = e.startwidth() - lastwidth
if diff > 1E-5:
self.boxes.corner(-90)
self.boxes.edge(diff)
@ -319,9 +339,11 @@ class CompoundEdge(BaseEdge):
self.boxes.corner(90)
self.boxes.edge(-diff)
self.boxes.corner(-90)
e(l)
lastwidth = e.endwidth()
#############################################################################
#### Slots
#############################################################################
@ -333,6 +355,7 @@ class Slot(BaseEdge):
def __init__(self, boxes, depth):
super(Slot, self).__init__(boxes, None)
self.depth = depth
def __call__(self, length, **kw):
@ -347,12 +370,14 @@ class Slot(BaseEdge):
else:
self.boxes.edge(self.length)
class SlottedEdge(BaseEdge):
"""Edge with multiple slots"""
description = "Straight Edge with slots"
def __init__(self, boxes, sections, edge="e", slots=0):
super(SlottedEdge, self).__init__(boxes, None)
self.edge = self.edges.get(edge, edge)
self.sections = sections
self.slots = slots
@ -367,14 +392,18 @@ class SlottedEdge(BaseEdge):
return self.edge.margin()
def __call__(self, length, **kw):
for l in self.sections[:-1]:
self.edge(l)
if self.slots:
Slot(self.boxes, self.slots)(self.thickness)
else:
self.edge(self.thickness)
self.edge(self.sections[-1])
#############################################################################
#### Finger Joints
#############################################################################
@ -411,17 +440,17 @@ Values:
"edge_width": 1.0,
}
class FingerJointEdge(BaseEdge):
"""Finger joint edge """
char = 'f'
description = "Finger Joint"
positive = True
def __call__(self, length,
bedBolts=None, bedBoltSettings=None, **kw):
def __call__(self, length, bedBolts=None, bedBoltSettings=None, **kw):
positive = self.positive
space, finger = self.settings.space, self.settings.finger
fingers = int((length - (self.settings.surroundingspaces - 1) * space) //
(space + finger))
@ -438,15 +467,18 @@ class FingerJointEdge(BaseEdge):
leftover = length
self.edge(leftover / 2.0)
for i in range(fingers):
if i != 0:
if not positive and bedBolts and bedBolts.drawBolt(i):
self.hole(0.5 * space,
0.5 * self.thickness, 0.5 * d)
if positive and bedBolts and bedBolts.drawBolt(i):
self.bedBoltHole(s, bedBoltSettings)
else:
self.edge(s)
self.corner(-90 * p)
self.edge(self.settings.height)
self.corner(90 * p)
@ -454,12 +486,14 @@ class FingerJointEdge(BaseEdge):
self.corner(90 * p)
self.edge(self.settings.height)
self.corner(-90 * p)
self.edge(leftover / 2.0)
def margin(self):
""" """
return self.boxes.thickness
class FingerJointEdgeCounterPart(FingerJointEdge):
"""Finger joint edge - other side"""
char = 'F'
@ -474,8 +508,10 @@ class FingerJointEdgeCounterPart(FingerJointEdge):
""" """
return 0.0
class FingerHoles:
"""Hole matching a finger joint edge"""
def __init__(self, boxes, settings):
self.boxes = boxes
self.ctx = boxes.ctx
@ -502,30 +538,36 @@ class FingerHoles:
if bedBolts:
fingers = bedBolts.numFingers(fingers)
d, d_nut, h_nut, l, l1 = bedBoltSettings or self.boxes.bedBoltSettings
leftover = length - fingers * (s + f) - f
b = self.boxes.burn
if self.boxes.debug:
self.ctx.rectangle(0, -self.settings.width / 2 + b,
length, self.settings.width - 2 * b)
for i in range(fingers):
pos = leftover / 2.0 + i * (s + f)
if bedBolts and bedBolts.drawBolt(i):
self.boxes.hole(pos + 0.5 * s, 0, d * 0.5)
self.boxes.rectangularHole(pos + s + 0.5 * f, 0,
f, self.settings.width)
self.ctx.restore()
class FingerHoleEdge(BaseEdge):
"""Edge with holes for a parallel finger joint"""
char = 'h'
description = "Edge (parallel Finger Joint Holes)"
def __init__(self, boxes, fingerHoles=None, **kw):
super(FingerHoleEdge, self).__init__(boxes, None, **kw)
self.fingerHoles = fingerHoles or boxes.fingerHolesAt
def __call__(self, length,
bedBolts=None, bedBoltSettings=None, **kw):
def __call__(self, length, bedBolts=None, bedBoltSettings=None, **kw):
dist = self.fingerHoles.settings.edge_width
self.ctx.save()
self.fingerHoles(0, dist + self.thickness / 2, length, 0,
@ -540,6 +582,7 @@ class FingerHoleEdge(BaseEdge):
""" """
return self.fingerHoles.settings.edge_width + self.thickness
class CrossingFingerHoleEdge(BaseEdge):
"""Edge with holes for finger joints 90° above"""
@ -547,6 +590,7 @@ class CrossingFingerHoleEdge(BaseEdge):
def __init__(self, boxes, height, fingerHoles=None, **kw):
super(CrossingFingerHoleEdge, self).__init__(boxes, None, **kw)
self.fingerHoles = fingerHoles or boxes.fingerHolesAt
self.height = height
@ -554,6 +598,7 @@ class CrossingFingerHoleEdge(BaseEdge):
self.fingerHoles(length / 2.0, 0, self.height)
super(CrossingFingerHoleEdge, self).__call__(length)
#############################################################################
#### Stackable Joints
#############################################################################
@ -578,12 +623,14 @@ Values:
absolute_params = {
"angle": 60,
}
relative_params = {
"height": 2.0,
"width": 4.0,
"holedistance": 1.0,
}
class StackableEdge(BaseEdge):
"""Edge for having stackable Boxes. The Edge creates feet on the bottom
and has matching recesses on the top corners."""
@ -594,6 +641,7 @@ class StackableEdge(BaseEdge):
def __init__(self, boxes, settings, fingerjointsettings):
super(StackableEdge, self).__init__(boxes, settings)
self.fingerjointsettings = fingerjointsettings
def __call__(self, length, **kw):
@ -623,17 +671,18 @@ class StackableEdge(BaseEdge):
def margin(self):
return 0 if self.bottom else self._height()
class StackableEdgeTop(StackableEdge):
char = "S"
description = "Stackable (top)"
bottom = False
#############################################################################
#### Hinges
#############################################################################
class HingeSettings(Settings):
"""Settings for Hinge and HingePin classes
Values:
@ -664,15 +713,17 @@ Values:
"grip_length": 0,
}
class Hinge(BaseEdge):
class Hinge(BaseEdge):
char = 'i'
description = "Straight edge with hinge eye"
def __init__(self, boxes, settings=None, layout=1):
super(Hinge, self).__init__(boxes, settings)
if not (0 < layout <= 3):
raise ValueError("layout must be 1, 2 or 3 (got %i)" % layout)
self.layout = layout
self.char = "eijk"[layout]
self.description = self.description + ('', ' (start)', ' (end)', ' (both ends)')[layout]
@ -697,6 +748,7 @@ class Hinge(BaseEdge):
(180, t + pos), 0,
(-90, 0.5 * t), 0
)
if _reversed:
hinge = reversed(hinge)
self.polyline(*hinge)
@ -710,6 +762,7 @@ class Hinge(BaseEdge):
r = 0.5 * self.settings.axle
alpha = math.degrees(math.asin(0.5 * t / r))
pos = math.cos(math.radians(alpha)) * r
return 2 * pos + 1.5 * t
def B(self, _reversed=False):
@ -731,6 +784,7 @@ class Hinge(BaseEdge):
else:
self.hole(pos, -0.5 * t, 0.5 * self.settings.axle)
self.boxes.rectangularHole(pos, -0.5 * t, pinl, self.thickness)
self.polyline(*hinge)
def Blen(self):
@ -738,20 +792,26 @@ class Hinge(BaseEdge):
def __call__(self, l, **kw):
hlen = getattr(self, self.settings.style + 'len')()
if self.layout & 1:
getattr(self, self.settings.style)()
self.edge(l - (self.layout & 1) * hlen - bool(self.layout & 2) * hlen)
if self.layout & 2:
getattr(self, self.settings.style)(True)
class HingePin(BaseEdge):
char = 'I'
description = "Edge with hinge pin"
def __init__(self, boxes, settings=None, layout=1):
super(HingePin, self).__init__(boxes, settings)
if not (0 < layout <= 3):
raise ValueError("layout must be 1, 2 or 3 (got %i)" % layout)
self.layout = layout
self.char = "EIJK"[layout]
self.description = self.description + ('', ' (start)', ' (end)', ' (both ends)')[layout]
@ -794,8 +854,10 @@ class HingePin(BaseEdge):
)
else:
pin += (pos - 0.5 * pinl,)
if _reversed:
pin = reversed(pin)
self.polyline(*pin)
def Alen(self):
@ -829,22 +891,28 @@ class HingePin(BaseEdge):
90,
0,
)
if _reversed:
pin = reversed(pin)
self.polyline(*pin)
def Blen(self):
l = self.settings.hingestrength + self.settings.axle
if self.settings.outset:
l += self.settings.hingestrength + 0.5 * self.thickness
return l
def __call__(self, l, **kw):
plen = getattr(self, self.settings.style + 'len')()
glen = l * self.settings.grip_percentage + \
self.settings.grip_length
if not self.settings.outset:
glen = 0.0
glen = min(glen, l - plen)
if self.layout & 1 and self.layout & 2:
@ -860,6 +928,7 @@ class HingePin(BaseEdge):
self.edge(l - plen - glen)
getattr(self, self.settings.style)(True)
#############################################################################
#### Click Joints
#############################################################################
@ -874,6 +943,7 @@ class ClickSettings(Settings):
"bottom_radius": 0.1,
}
class ClickConnector(BaseEdge):
char = "c"
description = "Click on (bottom side)"
@ -917,15 +987,16 @@ class ClickConnector(BaseEdge):
r = self.settings.bottom_radius
c = math.cos(math.radians(a))
s = math.sin(math.radians(a))
return 2 * s * d * c + 0.5 * c * t + c * 4 * r
def hookOffset(self):
t = self.thickness
a = self.settings.angle
d = self.settings.depth
r = self.settings.bottom_radius
c = math.cos(math.radians(a))
s = math.sin(math.radians(a))
return s * d * c + 2 * r
def finger(self, length):
@ -955,6 +1026,7 @@ class ClickConnector(BaseEdge):
def margin(self):
return 2 * self.thickness
class ClickEdge(ClickConnector):
char = "C"
description = "Click on (top)"
@ -983,6 +1055,7 @@ class ClickEdge(ClickConnector):
self.edge(length - 2 * (6 * t + 2 * w) + 2 * o)
self.polyline(*reversed(p1))
#############################################################################
#### Dove Tail Joints
#############################################################################
@ -1006,14 +1079,17 @@ Values:
absolute_params = {
"angle": 50,
}
relative_params = {
"size": 3,
"depth": 1.5,
"radius": 0.2,
}
class DoveTailJoint(BaseEdge):
"""Edge with dove tail joints """
char = 'd'
description = "Dove Tail Joint"
positive = True
@ -1035,6 +1111,7 @@ class DoveTailJoint(BaseEdge):
p = 1 if positive else -1
self.edge((s.size + leftover) / 2.0 + diffx - l1)
for i in range(sections):
self.corner(-1 * p * a, radius)
self.edge(2 * (l2 - l1))
@ -1043,8 +1120,10 @@ class DoveTailJoint(BaseEdge):
self.corner(p * a, radius)
self.edge(2 * (l2 - l1))
self.corner(-1 * p * a, radius)
if i < sections - 1: # all but the last
self.edge(2 * (diffx - l1) + s.size)
self.edge((s.size + leftover) / 2.0 + diffx - l1)
self.ctx.translate(*self.ctx.get_current_point())
@ -1052,6 +1131,7 @@ class DoveTailJoint(BaseEdge):
""" """
return self.settings.depth
class DoveTailJointCounterPart(DoveTailJoint):
"""Edge for other side of dove joints """
char = 'D'
@ -1062,6 +1142,7 @@ class DoveTailJointCounterPart(DoveTailJoint):
def margin(self):
return 0.0
class FlexSettings(Settings):
"""Settings for one directional flex cuts
@ -1083,10 +1164,12 @@ Values:
"connection": 1.0,
"width": 5.0,
}
absolute_params = {
"stretch": 1.05,
}
class FlexEdge(BaseEdge):
"""Edge with flex cuts - use straight edge for the opposing side"""
char = 'X'
@ -1106,12 +1189,15 @@ class FlexEdge(BaseEdge):
for i in range(1, lines):
pos = i * dist + leftover / 2
if i % 2:
self.ctx.move_to(pos, 0)
self.ctx.line_to(pos, connection + sheight)
for j in range((sections - 1) // 2):
self.ctx.move_to(pos, (2 * j + 1) * sheight + (2 * j + 2) * connection)
self.ctx.line_to(pos, (2 * j + 3) * (sheight + connection))
if not sections % 2:
self.ctx.move_to(pos, h - sheight - connection)
self.ctx.line_to(pos, h)
@ -1119,6 +1205,7 @@ class FlexEdge(BaseEdge):
if sections % 2:
self.ctx.move_to(pos, h)
self.ctx.line_to(pos, h - connection - sheight)
for j in range((sections - 1) // 2):
self.ctx.move_to(
pos, h - ((2 * j + 1) * sheight + (2 * j + 2) * connection))

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,16 +14,18 @@ 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)))
class Formats:
class Formats:
pstoedit = "/usr/bin/pstoedit"
formats = {
@ -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,8 +16,10 @@
from boxes import *
class Box(Boxes):
"""Fully closed box"""
def __init__(self):
Boxes.__init__(self)
self.buildArgParser("x", "y", "h", "outside")
@ -53,10 +55,12 @@ class Box(Boxes):
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):
@ -28,19 +28,19 @@ class Castle(Boxes):
s = edges.FingerJointSettings(self.thickness, relative=False,
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.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,7 +33,6 @@ 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):
@ -48,34 +49,30 @@ in 0.5mm steps, 3 holes each size"""
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(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,8 +17,10 @@
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")
@ -29,10 +31,12 @@ class FlexBox(boxes.Boxes):
@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.corner(90, r)
self.cc(callback, 2)
self.edge(x - 2 * r)
self.corner(90, r)
@ -102,10 +106,12 @@ class FlexBox(boxes.Boxes):
self.close()
def main():
b = FlexBox()
b.parseArgs()
b.render()
if __name__ == "__main__":
main()

View File

@ -17,8 +17,10 @@
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")
@ -47,12 +49,14 @@ class FlexBox2(Boxes):
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)
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.latch(self.latchsize, False)
self.edge(h + 2 * self.thickness)
self.latch(self.latchsize, False, True)
@ -67,6 +71,7 @@ class FlexBox2(Boxes):
self.corner(90)
def render(self):
if self.outside:
self.x = self.adjustSize(self.x)
self.y = self.adjustSize(self.y)
@ -85,23 +90,29 @@ class FlexBox2(Boxes):
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.rectangularWall(self.x, self.h, edges="FFFF")
self.ctx.restore()
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.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.close()
def main():
b = FlexBox2()
b.parseArgs()
b.render()
if __name__ == "__main__":
main()

View File

@ -17,8 +17,10 @@
from boxes import *
import math
class FlexBox3(Boxes):
"""Box with living hinge"""
def __init__(self):
Boxes.__init__(self)
self.buildArgParser("x", "y", "outside")
@ -116,13 +118,13 @@ class FlexBox3(Boxes):
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)
@ -132,8 +134,7 @@ class FlexBox3(Boxes):
self.surroundingWall()
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.ctx.scale(-1, 1)
@ -151,10 +152,12 @@ class FlexBox3(Boxes):
self.close()
def main():
b = FlexBox3() # 100, 40, 100, r=20, h=10, thickness=4.0)
b.parseArgs()
b.render()
if __name__ == "__main__":
main()

View File

@ -17,8 +17,10 @@
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")
@ -53,6 +55,7 @@ class FlexBox4(Boxes):
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)
@ -98,10 +101,12 @@ class FlexBox4(Boxes):
self.rectangularWall(self.x, self.h, edges="FeFF")
self.close()
def main():
b = FlexBox4()
b.parseArgs()
b.render()
if __name__ == "__main__":
main()

View File

@ -16,8 +16,10 @@
from boxes import *
class FlexTest(Boxes):
"Piece for testing different flex settings"
def __init__(self):
Boxes.__init__(self)
self.buildArgParser("x", "y")
@ -53,10 +55,12 @@ class FlexTest(Boxes):
self.close()
def main():
f = FlexTest()
f.parseArgs()
f.render()
if __name__ == '__main__':
main()

View File

@ -16,8 +16,10 @@
from boxes import *
class FlexTest2(Boxes):
"Piece for testing 2D flex settings"
def __init__(self):
Boxes.__init__(self)
self.buildArgParser("x", "y")
@ -27,15 +29,17 @@ class FlexTest2(Boxes):
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.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"""
@ -52,5 +53,6 @@ def main():
f.parseArgs()
f.render()
if __name__ == '__main__':
main()

View File

@ -16,8 +16,10 @@
from boxes import *
class GearBox(Boxes):
"""Gearbox with multiple identical stages"""
def __init__(self):
Boxes.__init__(self)
self.argparser.add_argument(
@ -43,13 +45,12 @@ class GearBox(Boxes):
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
if self.stages == 1:
y = size1 + size2
y1 = y / 2 - (pitch1 + pitch2) + pitch1
@ -65,7 +66,6 @@ class GearBox(Boxes):
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)
@ -81,24 +81,26 @@ class GearBox(Boxes):
profile_shift = 20
pressure_angle = 20
for i in range(self.stages - 1):
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="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,6 +17,7 @@
from boxes import *
import random
class JigsawPuzzle(Boxes): # change class name here and below
"""Fractal jigsaw puzzle. Still aplha"""
@ -32,15 +33,14 @@ class JigsawPuzzle(Boxes): # change class name here and below
"--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)
return
self.peano(self, level - 1)
self.corner()
def edge(self, l):
self.count += 1
Boxes.edge(self, l)
@ -53,13 +53,16 @@ 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.corner(parity * -90)
self.hilbert(level - 1, parity)
# third subcurve
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)
# fourth subcurve
self.corner(parity * -90)
@ -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,13 +17,13 @@
from boxes import *
import math
"""
22x7.5x7cm
D=23cm, d=21cm
d = 8" D = 9"
"""
class RoundedTriangleSettings(edges.Settings):
absolute_params = {
"angle": 60,
@ -31,8 +31,10 @@ class RoundedTriangleSettings(edges.Settings):
"r_hole": None,
}
class RoundedTriangle(edges.Edge):
char = "t"
def __call__(self, length, **kw):
angle = self.settings.angle
r = self.settings.radius
@ -55,8 +57,8 @@ class RoundedTriangle(edges.Edge):
def endAngle(self):
return 90
class Lamp(Boxes):
class Lamp(Boxes):
webinterface = False
def __init__(self):
@ -94,6 +96,7 @@ class Lamp(Boxes):
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=[
@ -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)
if __name__ == '__main__':
main()

View File

@ -16,8 +16,10 @@
from boxes import *
class MagazinFile(Boxes):
"""Open magazine file"""
def __init__(self):
Boxes.__init__(self)
self.buildArgParser("x", "y", "h", "hi", "outside")
@ -28,6 +30,7 @@ class MagazinFile(Boxes):
def side(self, w, h, hi):
r = min(h - hi, w) / 2.0
if (h - hi) > w:
r = w / 2.0
lx = 0
@ -58,8 +61,8 @@ class MagazinFile(Boxes):
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)
@ -85,10 +88,12 @@ class MagazinFile(Boxes):
self.close()
def main():
b = MagazinFile()
b.parseArgs()
b.render()
if __name__ == '__main__':
main()

View File

@ -17,8 +17,10 @@
from boxes import *
import math
class Planetary(Boxes):
"""Gearbox with multiple identical stages"""
def __init__(self):
Boxes.__init__(self)
self.argparser.add_argument(
@ -60,6 +62,7 @@ class Planetary(Boxes):
t = self.thickness
planets = int(math.pi // math.asin((self.planetteeth + 2) / (self.planetteeth + self.sunteeth)))
if self.maxplanets:
planets = min(self.maxplanets, planets)
@ -85,6 +88,7 @@ class Planetary(Boxes):
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)
@ -100,10 +104,12 @@ class Planetary(Boxes):
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
@ -32,7 +32,6 @@ class Printer(Boxes):
self.d_i = 5.0
self.w_i = 7.0 # includes washers
def mainPlate(self, nr):
r = self.r
t2 = 0.5 * self.thickness
@ -45,9 +44,11 @@ class Printer(Boxes):
w_i2 = self.w_i / 2
d_c2 = self.d_c / 2
for i in range(6):
self.ctx.save()
self.moveTo(0, 0, i * 60)
# winches
if i % 2:
self.fingerHolesAt(r - 80, (d_c2 + 20), 70, angle=0)
@ -59,13 +60,14 @@ class Printer(Boxes):
# 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()
def head(self):
d_c = self.d_c
@ -78,10 +80,10 @@ 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]
overallwidth = x + edges[0].spacing() + self.edges["e"].spacing()
@ -127,6 +129,7 @@ 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=[
@ -138,25 +141,30 @@ class Printer(Boxes):
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")
# 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, ],
move="right")
self.ctx.restore()
self.moveTo(0, 20)
# Cable adjustment glyders
for i in range(6):
self.rectangularWall(8, 10, move="right", callback=[
@ -178,10 +186,12 @@ class Printer(Boxes):
self.head()
self.close()
def main():
p = Printer()
p.parseArgs()
p.render()
if __name__ == '__main__':
main()

View File

@ -18,8 +18,10 @@ 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
@ -43,16 +45,19 @@ class Pulley(Boxes):
# "--XX", action="store", type=float, default=0.5,
# help="DESCRIPTION")
def disk(self, diameter, hole, callback=None, move=""):
w = diameter + 2 * self.spacing
if self.move(w, w, move, before=True):
return
self.ctx.save()
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.ctx.restore()
@ -63,19 +68,23 @@ class Pulley(Boxes):
t = self.thickness
# Initialize canvas
self.open()
if self.top:
self.disk(
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")
self.close()
def main():
b = Box()
b.parseArgs()
b.render()
if __name__ == '__main__':
main()

View File

@ -16,6 +16,7 @@
from boxes import *
class MotorEdge(edges.BaseEdge):
# def margin(self):
# return 30
@ -27,12 +28,15 @@ class MotorEdge(edges.BaseEdge):
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
@ -49,9 +53,11 @@ class HangerEdge(edges.BaseEdge):
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,
@ -60,6 +66,7 @@ class RollerEdge(edges.BaseEdge):
2 ** 0.5 * 20, -45,
(l - m) / 2.0)
class RollerEdge2(edges.BaseEdge):
def margin(self):
return self.thickness
@ -71,8 +78,10 @@ class RollerEdge2(edges.BaseEdge):
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
@ -91,8 +100,6 @@ class Rotary(Boxes):
"--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
@ -129,14 +136,17 @@ class Rotary(Boxes):
overallwidth = x + y
overallheight = y
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)
if middleHole:
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.))
@ -154,6 +164,7 @@ class Rotary(Boxes):
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)
@ -181,29 +192,34 @@ 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.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")
@ -248,6 +264,7 @@ class Rotary(Boxes):
self.ctx.restore()
self.rectangularWall(30, 30, move="up only")
# Other side
if self.knifethickness:
ow = 10
@ -265,8 +282,10 @@ class Rotary(Boxes):
# 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, 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")
@ -277,16 +296,19 @@ class Rotary(Boxes):
self.ctx.restore()
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,
hole=self.axle, move="right")
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,
hole=self.axle, move="left")
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,
hole=self.axle, move="right")
@ -295,10 +317,12 @@ class Rotary(Boxes):
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"""
@ -78,6 +79,7 @@ using flex for rounded corners"""
self.centerWall(x, h)
l = (y - t) / 2.0
for i in range(3):
self.rectangularWall(l, h - 10, edges="ffef", move="right")
@ -86,10 +88,12 @@ using flex for rounded corners"""
self.close()
def main():
b = Silverware()
b.parseArgs()
b.render()
if __name__ == '__main__':
main()

View File

@ -16,13 +16,16 @@
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)
@ -37,19 +40,20 @@ class TrayInsert(Boxes):
# 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")
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")
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
@ -50,14 +50,17 @@ class Layout(Boxes):
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((" |"[v] + "X "[f] for v, f in zip(vwalls, floors)))
+ " |"[vwalls[-1]] + " %.1fmm\n" % y)
r.append("".join(("+" + " -"[h] for h in self.hwalls[-1])) + "+\n")
return "".join(r)
def vWalls(self, x, y):
@ -65,8 +68,10 @@ class Layout(Boxes):
result = 0
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):
@ -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)
@ -128,20 +134,28 @@ 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?
lengths.append(self.x[end])
edges.append("eCs"[self.vWalls(end + 1, y)])
lengths.append(self.thickness)
@ -170,18 +184,24 @@ 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]:
if self.vFloor(x, end):
edges.append("f")
else:
edges.append("e") # XXX E?
lengths.append(self.y[end])
edges.append("eCs"[self.hWalls(x, end + 1)])
lengths.append(self.thickness)
@ -308,7 +328,6 @@ class Layout(Boxes):
pass
# raise ValueError(line)
hwalls.append(w)
if line[0] in " |":
w = []
@ -349,7 +368,8 @@ class Layout(Boxes):
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))
raise ValueError(
"Wrong number of vertical walls in line %i: %i (%i expected)" % (nr, len(walls), lx + 1))
self.x = x
self.y = y
@ -357,8 +377,8 @@ class Layout(Boxes):
self.vwalls = vwalls
self.floors = floors
class TrayLayout(Layout):
class TrayLayout(Layout):
"""Type tray with each wall and floor tile being optional"""
webinterface = True
@ -376,8 +396,8 @@ class TrayLayout(Layout):
def render(self):
return
class TrayLayout2(Layout):
class TrayLayout2(Layout):
"""Generate a typetray from a layout file"""
webinterface = True
@ -388,6 +408,7 @@ class TrayLayout2(Layout):
self.argparser.add_argument(
"--layout", action="store", type=str)
def main():
l = Layout()
l.parseArgs()
@ -404,5 +425,6 @@ def main():
else:
l.argparser.print_usage()
if __name__ == '__main__':
main()

View File

@ -16,8 +16,10 @@
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")
@ -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(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, ], 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")
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")
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))
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
@ -26,11 +28,15 @@ class Parts:
def disc(self, diameter, hole=0, callback=None, move=""):
size = diameter
r = diameter / 2.0
if self.move(size, size, move, before=True):
return
self.moveTo(size / 2, size / 2)
if hole:
self.hole(0, 0, hole / 2)
self.cc(callback, None, 0, 0)
self.moveTo(r + self.burn, 0, 90)
self.corner(360, r)
@ -38,33 +44,45 @@ class Parts:
def waivyKnob(self, diameter, n=20, angle=45, hole=0, callback=None, move=""):
size = diameter + pi * diameter / n
if self.move(size, size, move, before=True):
return
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.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)
if hole:
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)
for i in range(n):
self.boxes.corner(a, r)
self.corner(angle)
@ -72,5 +90,3 @@ class Parts:
self.corner(angle)
self.move(size, size, move)

View File

@ -14,12 +14,15 @@
from math import *
from boxes.vectors import *
def tooth_spaceing_curvefit(teeth, b, c, d):
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))
def mirrorx(points):
return [[-x, y] for x, y in points]
@ -41,6 +44,7 @@ class Pulley:
"GT2_3mm": (False, 3, 0.381),
"GT2_5mm": (False, 5, 0.5715),
}
profile_data = {
"MXL": (0.508, 1.321),
"40DP": (0.457, 1.226),
@ -86,16 +90,17 @@ class Pulley:
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_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):
@ -118,18 +123,22 @@ class Pulley:
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.cc(callback, None, 0.0, 0.0)
if r_axle:
self.boxes.hole(0, 0, r_axle)
points = []
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))
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,12 +31,14 @@ 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])
@ -45,15 +47,19 @@ class SVGFile(object):
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))
@ -85,8 +91,10 @@ class SVGFile(object):
f.seek(m.start(1))
s = ('width="%imm" height="%imm" viewBox="0 %i %i %i"' %
(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)))
else:
raise ValueError("Could not understand SVG file")

View File

@ -14,51 +14,62 @@
# 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)
def vlength(v):
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 v
def vdiff(p1, p2):
"vector from point1 to point2"
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])
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])
def dotproduct(v1, v2):
"Dot product"
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]]
def mmul(m0, m1):
result = [[0, ] * len(m0[0]) for i in range(len(m0))]
for i in range(len(m0[0])):
@ -67,12 +78,14 @@ def mmul(m0, m1):
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])))
@ -82,4 +95,5 @@ def kerf(points, k):
# cos of the half the angle between the segments
cos_alpha = dotproduct(v1, d)
result.append(vadd(points[i], vscalmul(d, -k / cos_alpha)))
return result