cosplay: Touhou/Houjuu Nue #4
|
@ -23,50 +23,69 @@ def hirth_joint(radius=60,
|
|||
Creates a cylindrical Hirth Joint
|
||||
|
||||
is_mated: If set to true, rotate the teeth so they line up at 0 degrees.
|
||||
|
||||
FIXME: The curves don't mate perfectly. See if non-planar lofts can solve
|
||||
this issue.
|
||||
"""
|
||||
# ensures secant doesn't blow up
|
||||
# ensures tangent doesn't blow up
|
||||
assert n_tooth >= 5
|
||||
assert radius > radius_inner
|
||||
assert tooth_height >= tooth_height_inner
|
||||
|
||||
# angle of half of a single tooth
|
||||
theta = math.pi / n_tooth
|
||||
|
||||
# Generate a tooth by lofting between two curves
|
||||
|
||||
c, s, t = math.cos(theta), math.sin(theta), math.tan(theta)
|
||||
span = radius * t
|
||||
radius_proj = radius / c
|
||||
span_inner = radius_inner * s
|
||||
# 2 * raise + (inner tooth height) = (tooth height)
|
||||
inner_raise = (tooth_height - tooth_height_inner) / 2
|
||||
# Outer tooth triangle spans a curve of length `2 pi r / n_tooth`. This
|
||||
# creates the side profile (looking radially inwards) of each of the
|
||||
# triangles.
|
||||
# Outer tooth triangle spans 2*theta radians. This profile is the radial
|
||||
# profile projected onto a plane `radius` away from the centre of the
|
||||
# cylinder. The y coordinates on the edge must drop to compensate.
|
||||
|
||||
# The drop is equal to, via similar triangles
|
||||
drop = inner_raise * (radius_proj - radius) / (radius - radius_inner)
|
||||
outer = [
|
||||
(radius * math.tan(theta), 0),
|
||||
(radius * math.tan(theta) - tol, 0),
|
||||
(span, -tol - drop),
|
||||
(span, -drop),
|
||||
(0, tooth_height),
|
||||
(-radius * math.tan(theta) + tol, 0),
|
||||
(-radius * math.tan(theta), 0),
|
||||
(-span, -drop),
|
||||
(-span, -tol - drop),
|
||||
]
|
||||
adj = radius_inner * c
|
||||
# In the case of the inner triangle, it is projected onto a plane `adj` away
|
||||
# from the centre. The apex must extrapolate
|
||||
|
||||
# Via similar triangles
|
||||
#
|
||||
# (inner_raise + tooth_height_inner) -
|
||||
# (tooth_height - inner_raise - tooth_height_inner) * ((radius_inner - adj) / (radius - radius_inner))
|
||||
apex = (inner_raise + tooth_height_inner) - \
|
||||
inner_raise * (radius_inner - adj) / (radius - radius_inner)
|
||||
inner = [
|
||||
(radius_inner * math.sin(theta), 0),
|
||||
(radius_inner * math.sin(theta), inner_raise),
|
||||
(0, inner_raise + tooth_height_inner),
|
||||
(-radius_inner * math.sin(theta), inner_raise),
|
||||
(-radius_inner * math.sin(theta), 0),
|
||||
(span_inner, -tol),
|
||||
(span_inner, inner_raise),
|
||||
(0, apex),
|
||||
(-span_inner, inner_raise),
|
||||
(-span_inner, -tol),
|
||||
]
|
||||
tooth = (
|
||||
Cq.Workplane('YZ')
|
||||
.polyline(inner)
|
||||
.close()
|
||||
.workplane(offset=radius - radius_inner)
|
||||
.workplane(offset=radius - adj)
|
||||
.polyline(outer)
|
||||
.close()
|
||||
.loft(ruled=True, combine=True)
|
||||
.loft(ruled=False, combine=True)
|
||||
.val()
|
||||
)
|
||||
tooth_centre_radius = radius_inner * math.cos(theta)
|
||||
angle_offset = hirth_tooth_angle(n_tooth) / 2 if is_mated else 0
|
||||
teeth = (
|
||||
Cq.Workplane('XY')
|
||||
.polarArray(
|
||||
radius=tooth_centre_radius,
|
||||
radius=adj,
|
||||
startAngle=angle_offset,
|
||||
angle=360,
|
||||
count=n_tooth)
|
||||
|
@ -75,6 +94,14 @@ def hirth_joint(radius=60,
|
|||
height=base_height + tooth_height,
|
||||
radius=radius,
|
||||
))
|
||||
.intersect(Cq.Solid.makeCylinder(
|
||||
height=base_height + tooth_height,
|
||||
radius=radius,
|
||||
))
|
||||
.cut(Cq.Solid.makeCylinder(
|
||||
height=base_height + tooth_height,
|
||||
radius=radius_inner,
|
||||
))
|
||||
)
|
||||
base = (
|
||||
Cq.Workplane('XY')
|
||||
|
|
|
@ -3,6 +3,7 @@ import cadquery as Cq
|
|||
import nhf.joints
|
||||
import nhf.handle
|
||||
import nhf.metric_threads as NMt
|
||||
from nhf.checks import binary_intersection
|
||||
|
||||
class TestJoints(unittest.TestCase):
|
||||
|
||||
|
@ -12,7 +13,10 @@ class TestJoints(unittest.TestCase):
|
|||
j.val().solids(), Cq.Solid,
|
||||
msg="Hirth joint must be in one piece")
|
||||
def test_joints_hirth_assembly(self):
|
||||
nhf.joints.hirth_assembly()
|
||||
assembly = nhf.joints.hirth_assembly()
|
||||
isect = binary_intersection(assembly)
|
||||
self.assertLess(isect.Volume(), 1e-6,
|
||||
"Hirth joint assembly must not have intersection")
|
||||
def test_joints_comma_assembly(self):
|
||||
nhf.joints.comma_assembly()
|
||||
def test_torsion_joint(self):
|
||||
|
|
Loading…
Reference in New Issue