107 lines
3.0 KiB
Python
107 lines
3.0 KiB
Python
|
import cadquery as Cq
|
||
|
import math
|
||
|
import unittest
|
||
|
|
||
|
def hirth_joint(radius=60,
|
||
|
radius_inner=40,
|
||
|
radius_centre=30,
|
||
|
base_height=20,
|
||
|
n_tooth=16,
|
||
|
tooth_height=16,
|
||
|
tooth_height_inner=2):
|
||
|
"""
|
||
|
Creates a cylindrical Hirth Joint
|
||
|
"""
|
||
|
# ensures secant doesn't blow up
|
||
|
assert n_tooth >= 5
|
||
|
|
||
|
# angle of half of a single tooth
|
||
|
theta = math.pi / n_tooth
|
||
|
|
||
|
# Generate a tooth by lofting between two curves
|
||
|
|
||
|
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 = [
|
||
|
(radius * math.tan(theta), 0),
|
||
|
(0, tooth_height),
|
||
|
(-radius * math.tan(theta), 0),
|
||
|
]
|
||
|
inner = [
|
||
|
(radius_inner * math.sin(theta), 0),
|
||
|
(radius_inner * math.sin(theta), inner_raise - tooth_height_inner / 2),
|
||
|
(0, inner_raise + tooth_height_inner / 2),
|
||
|
(-radius_inner * math.sin(theta), inner_raise - tooth_height_inner / 2),
|
||
|
(-radius_inner * math.sin(theta), 0),
|
||
|
]
|
||
|
tooth = (
|
||
|
Cq.Workplane('YZ')
|
||
|
.polyline(inner)
|
||
|
.close()
|
||
|
.workplane(offset=radius - radius_inner)
|
||
|
.polyline(outer)
|
||
|
.close()
|
||
|
.loft(combine=True)
|
||
|
.val()
|
||
|
)
|
||
|
tooth_centre_radius = radius_inner * math.cos(theta)
|
||
|
teeth = (
|
||
|
Cq.Workplane('XY')
|
||
|
.polarArray(radius=radius_inner, startAngle=0, angle=360, count=n_tooth)
|
||
|
.eachpoint(lambda loc: tooth.located(loc))
|
||
|
.intersect(Cq.Solid.makeCylinder(
|
||
|
height=base_height + tooth_height,
|
||
|
radius=radius,
|
||
|
))
|
||
|
)
|
||
|
base = (
|
||
|
Cq.Workplane('XY')
|
||
|
.cylinder(
|
||
|
height=base_height,
|
||
|
radius=radius,
|
||
|
centered=(True, True, False))
|
||
|
.faces(">Z").tag("bore")
|
||
|
.union(teeth.val().move(Cq.Location((0,0,base_height))))
|
||
|
.clean()
|
||
|
)
|
||
|
#base.workplane(offset=tooth_height/2).circle(radius=radius,forConstruction=True).tag("mate")
|
||
|
base.polyline([(0, 0, 0), (0, 0, 1)], forConstruction=True).tag("mate")
|
||
|
return base
|
||
|
|
||
|
def hirth_assembly():
|
||
|
"""
|
||
|
Example assembly of two Hirth joints
|
||
|
"""
|
||
|
rotate = 180 / 16
|
||
|
obj1 = hirth_joint().faces(tag="bore").cboreHole(
|
||
|
diameter=10,
|
||
|
cboreDiameter=20,
|
||
|
cboreDepth=3)
|
||
|
obj2 = (
|
||
|
hirth_joint()
|
||
|
.rotate(
|
||
|
axisStartPoint=(0,0,0),
|
||
|
axisEndPoint=(0,0,1),
|
||
|
angleDegrees=rotate
|
||
|
)
|
||
|
)
|
||
|
result = (
|
||
|
Cq.Assembly()
|
||
|
.add(obj1, name="obj1", color=Cq.Color(0.8,0.8,0.5,0.3))
|
||
|
.add(obj2, name="obj2", color=Cq.Color(0.5,0.5,0.5,0.3), loc=Cq.Location((0,0,80)))
|
||
|
.constrain("obj1?mate", "obj2?mate", "Axis")
|
||
|
.solve()
|
||
|
)
|
||
|
return result
|
||
|
|
||
|
class TestJoints(unittest.TestCase):
|
||
|
|
||
|
def test_hirth_assembly(self):
|
||
|
print(Cq.__version__)
|
||
|
hirth_assembly()
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
unittest.main()
|