cosplay: Touhou/Houjuu Nue #4

Open
aniva wants to merge 189 commits from touhou/houjuu-nue into main
2 changed files with 51 additions and 20 deletions
Showing only changes of commit 87e99ac4ce - Show all commits

View File

@ -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')

View File

@ -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):