cosplay: Touhou/Houjuu Nue #4
129
nhf/joints.py
129
nhf/joints.py
|
@ -99,10 +99,139 @@ def hirth_assembly():
|
|||
)
|
||||
return result
|
||||
|
||||
def comma_joint(radius=30,
|
||||
shaft_radius=10,
|
||||
height=10,
|
||||
flange=10,
|
||||
flange_thickness=25,
|
||||
n_serration=16,
|
||||
serration_angle_offset=0,
|
||||
serration_height=5,
|
||||
serration_inner_radius=20,
|
||||
serration_theta=2 * math.pi / 48,
|
||||
serration_tilt=-30,
|
||||
right_handed=False):
|
||||
"""
|
||||
Produces a "o_" shaped joint, with serrations to accomodate a torsion spring
|
||||
"""
|
||||
assert flange_thickness <= radius
|
||||
flange_poly = [
|
||||
(0, radius - flange_thickness),
|
||||
(0, radius),
|
||||
(flange + radius, radius),
|
||||
(flange + radius, radius - flange_thickness)
|
||||
]
|
||||
if right_handed:
|
||||
flange_poly = [(x, -y) for x,y in flange_poly]
|
||||
sketch = (
|
||||
Cq.Sketch()
|
||||
.circle(radius)
|
||||
.polygon(flange_poly, mode='a')
|
||||
.circle(shaft_radius, mode='s')
|
||||
)
|
||||
serration_poly = [
|
||||
(0, 0), (radius, 0),
|
||||
(radius, radius * math.tan(serration_theta))
|
||||
]
|
||||
serration = (
|
||||
Cq.Workplane('XY')
|
||||
.sketch()
|
||||
.polygon(serration_poly)
|
||||
.circle(radius, mode='i')
|
||||
.circle(serration_inner_radius, mode='s')
|
||||
.finalize()
|
||||
.extrude(serration_height)
|
||||
.translate(Cq.Vector((-serration_inner_radius, 0, height)))
|
||||
.rotate(
|
||||
axisStartPoint=(0, 0, 0),
|
||||
axisEndPoint=(0, 0, height),
|
||||
angleDegrees=serration_tilt)
|
||||
.val()
|
||||
)
|
||||
serrations = (
|
||||
Cq.Workplane('XY')
|
||||
.polarArray(radius=serration_inner_radius,
|
||||
startAngle=0+serration_angle_offset,
|
||||
angle=360+serration_angle_offset,
|
||||
count=n_serration)
|
||||
.eachpoint(lambda loc: serration.located(loc))
|
||||
)
|
||||
result = (
|
||||
Cq.Workplane()
|
||||
.add(sketch)
|
||||
.extrude(height)
|
||||
.union(serrations)
|
||||
.clean()
|
||||
)
|
||||
|
||||
result.polyline([
|
||||
(0, 0, height - serration_height),
|
||||
(0, 0, height + serration_height)],
|
||||
forConstruction=True).tag("serrated")
|
||||
result.polyline([
|
||||
(0, radius, 0),
|
||||
(flange + radius, radius, 0)],
|
||||
forConstruction=True).tag("tail")
|
||||
result.faces('>X').tag("tail_end")
|
||||
return result
|
||||
|
||||
def torsion_spring(radius=12,
|
||||
height=20,
|
||||
thickness=2,
|
||||
omega=90,
|
||||
tail_length=25):
|
||||
"""
|
||||
Produces a torsion spring with abridged geometry since sweep is very slow in
|
||||
cq-editor.
|
||||
"""
|
||||
base = (
|
||||
Cq.Workplane('XY')
|
||||
.cylinder(height=height, radius=radius,
|
||||
centered=(True, True, False))
|
||||
)
|
||||
base.faces(">Z").tag("mate_top")
|
||||
base.faces("<Z").tag("mate_bottom")
|
||||
result = (
|
||||
base
|
||||
.cylinder(height=height, radius=radius - thickness, combine='s',
|
||||
centered=(True, True, True))
|
||||
.transformed(
|
||||
offset=(0, radius-thickness),
|
||||
rotate=(0, 0, 0))
|
||||
.box(length=tail_length, width=thickness, height=thickness, centered=False)
|
||||
.copyWorkplane(Cq.Workplane('XY'))
|
||||
.transformed(
|
||||
offset=(0, 0, height - thickness),
|
||||
rotate=(0, 0, omega))
|
||||
.center(-tail_length, radius-thickness)
|
||||
.box(length=tail_length, width=thickness, height=thickness, centered=False)
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
def comma_assembly():
|
||||
joint1 = comma_joint()
|
||||
joint2 = comma_joint()
|
||||
spring = torsion_spring()
|
||||
result = (
|
||||
Cq.Assembly()
|
||||
.add(joint1, name="joint1", color=Cq.Color(0.8,0.8,0.5,0.3))
|
||||
.add(joint2, name="joint2", color=Cq.Color(0.8,0.8,0.5,0.3))
|
||||
.add(spring, name="spring", color=Cq.Color(0.5,0.5,0.5,1))
|
||||
.constrain("joint1?serrated", "spring?mate_bottom", "Plane")
|
||||
.constrain("joint2?serrated", "spring?mate_top", "Plane")
|
||||
.constrain("joint1?tail", "FixedAxis", (1, 0, 0))
|
||||
.constrain("joint2?tail", "FixedAxis", (-1, 0, 0))
|
||||
.solve()
|
||||
)
|
||||
return result
|
||||
|
||||
class TestJoints(unittest.TestCase):
|
||||
|
||||
def test_hirth_assembly(self):
|
||||
hirth_assembly()
|
||||
def test_comma_assembly(self):
|
||||
comma_assembly()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -1,17 +1,34 @@
|
|||
import cadquery as Cq
|
||||
from dataclasses import dataclass
|
||||
|
||||
def mystery():
|
||||
return (
|
||||
Cq.Workplane()
|
||||
.box(1, 1, 1)
|
||||
.tag("base")
|
||||
.wires(">Z")
|
||||
.toPending()
|
||||
.translate((0.1, 0.1, 1.0))
|
||||
.toPending()
|
||||
.loft()
|
||||
.faces(">>X", tag="base")
|
||||
.workplane(centerOption="CenterOfMass")
|
||||
.circle(0.2)
|
||||
.extrude(3)
|
||||
)
|
||||
@dataclass(frozen=True)
|
||||
class Parameters:
|
||||
|
||||
"""
|
||||
Thickness of the exoskeleton panel in millimetres
|
||||
"""
|
||||
panel_thickness: float = 25.4 / 16
|
||||
|
||||
# Wing root properties
|
||||
"""
|
||||
Radius of the mounting mechanism of the wing root. This is constrained by
|
||||
the size of the harness.
|
||||
"""
|
||||
root_radius: float = 60
|
||||
|
||||
def wing_root(self,
|
||||
side_width=30,
|
||||
side_height=100):
|
||||
"""
|
||||
Generate the wing root which contains a Hirth joint at its base and a
|
||||
rectangular opening on its side, with the necessary interfaces.
|
||||
"""
|
||||
result = (
|
||||
Cq.Workplane("XY")
|
||||
.circle(self.root_radius)
|
||||
.transformed(offset=Cq.Vector(80, 0, 80),
|
||||
rotate=Cq.Vector(0, 45, 0))
|
||||
.rect(side_width, side_height)
|
||||
.loft()
|
||||
)
|
||||
return result
|
||||
|
|
Loading…
Reference in New Issue