Cosplay/nhf/touhou/houjuu_nue/__init__.py

183 lines
6.3 KiB
Python

"""
To build, execute
```
python3 nhf/touhou/houjuu_nue/__init__.py
```
This cosplay consists of 3 components:
## Trident
The trident is composed of individual segments, made of acrylic, and a 3D
printed head (convention rule prohibits metal) with a metallic paint. To ease
transportation, the trident handle has individual segments with threads and can
be assembled on site.
## Snake
A 3D printed snake with a soft material so it can wrap around and bend
## Wings
This is the crux of the cosplay and the most complex component. The wings mount
on a wearable harness. Each wing consists of 4 segments with 3 joints. Parts of
the wing which demands transluscency are created from 1/16" acrylic panels.
These panels serve double duty as the exoskeleton.
The wings are labeled r1,r2,r3,l1,l2,l3. The segments of the wings are labeled
from root to tip s0 (root),
s1, s2, s3. The joints are named (from root to tip)
shoulder, elbow, wrist in analogy with human anatomy.
"""
from dataclasses import dataclass, field
from typing import Optional
import cadquery as Cq
from nhf.build import Model, TargetKind, target, assembly
from nhf.parts.joints import HirthJoint, TorsionJoint
from nhf.parts.handle import Handle, BayonetMount
import nhf.touhou.houjuu_nue.wing as MW
import nhf.touhou.houjuu_nue.trident as MT
import nhf.touhou.houjuu_nue.joints as MJ
import nhf.touhou.houjuu_nue.harness as MH
import nhf.utils
@dataclass
class Parameters(Model):
"""
Defines dimensions for the Houjuu Nue cosplay
"""
# Thickness of the exoskeleton panel in millimetres
panel_thickness: float = 25.4 / 16
# Harness
harness: MH.Harness = field(default_factory=lambda: MH.Harness())
hs_hirth_joint: HirthJoint = field(default_factory=lambda: HirthJoint(
radius=30,
radius_inner=20,
tooth_height=10,
base_height=5,
n_tooth=24
))
wing_profile: MW.WingProfile = field(default_factory=lambda: MW.WingProfile(
shoulder_joint=MJ.ShoulderJoint(
height=100.0,
),
elbow_height=110.0,
))
# Exterior radius of the wing root assembly
wing_root_radius: float = 40
wing_root_wall_thickness: float = 8
"""
Heights for various wing joints, where the numbers start from the first
joint.
"""
wing_s0_thickness: float = 40
# Length of the spacer
wing_s1_thickness: float = 20
wing_s1_spacer_thickness: float = 25.4 / 8
wing_s1_spacer_width: float = 20
wing_s1_spacer_hole_diam: float = 8
wing_s1_shoulder_spacer_hole_dist: float = 20
wing_s1_shoulder_spacer_width: float = 60
trident_handle: Handle = field(default_factory=lambda: Handle(
diam=38,
diam_inner=38-2 * 25.4/8,
diam_connector_internal=18,
simplify_geometry=False,
mount=BayonetMount(n_pin=3),
))
trident_terminal_height: float = 80
trident_terminal_hole_diam: float = 24
trident_terminal_bottom_thickness: float = 10
def __post_init__(self):
super().__init__(name="houjuu-nue")
self.harness.hs_hirth_joint = self.hs_hirth_joint
self.wing_profile.base_joint = self.hs_hirth_joint
assert self.wing_root_radius > self.hs_hirth_joint.radius, \
"Wing root must be large enough to accomodate joint"
assert self.wing_s1_shoulder_spacer_hole_dist > self.wing_s1_spacer_hole_diam, \
"Spacer holes are too close to each other"
@target(name="trident/handle-connector")
def handle_connector(self):
return self.trident_handle.connector()
@target(name="trident/handle-insertion")
def handle_insertion(self):
return self.trident_handle.insertion()
@target(name="trident/proto-handle-connector", prototype=True)
def proto_handle_connector(self):
return self.trident_handle.one_side_connector(height=15)
@target(name="trident/handle-terminal-connector")
def handle_terminal_connector(self):
result = self.trident_handle.one_side_connector(height=self.trident_terminal_height)
#result.faces("<Z").circle(radius=25/2).cutThruAll()
h = self.trident_terminal_height + self.trident_handle.insertion_length - self.trident_terminal_bottom_thickness
result = result.faces(">Z").hole(self.trident_terminal_hole_diam, depth=h)
return result
@target(name="harness", kind=TargetKind.DXF)
def harness_profile(self) -> Cq.Sketch:
return self.harness.profile()
def harness_surface(self) -> Cq.Workplane:
return self.harness.surface()
def hs_joint_parent(self) -> Cq.Workplane:
return self.harness.hs_joint_parent()
@assembly()
def harness_assembly(self) -> Cq.Assembly:
return self.harness.assembly()
@target(name="wing/proto-shoulder-joint-parent", prototype=True)
def proto_shoulder_joint_parent(self):
return self.wing_profile.shoulder_joint.torsion_joint.track()
@target(name="wing/proto-shoulder-joint-child", prototype=True)
def proto_shoulder_joint_child(self):
return self.wing_profile.shoulder_joint.torsion_joint.rider()
@assembly()
def wing_r1_assembly(self, parts: Optional[list[str]] = None) -> Cq.Assembly:
return self.wing_profile.assembly(parts)
@assembly()
def wings_harness_assembly(self, parts: Optional[list[str]] = None) -> Cq.Assembly:
"""
Assembly of harness with all the wings
"""
a_tooth = self.hs_hirth_joint.tooth_angle
result = (
Cq.Assembly()
.add(self.harness_assembly(), name="harness", loc=Cq.Location((0, 0, 0)))
.add(self.wing_r1_assembly(parts), name="wing_r1")
.add(self.wing_r1_assembly(parts), name="wing_r2")
.add(self.wing_r1_assembly(parts), name="wing_r3")
)
self.hs_hirth_joint.add_constraints(result, "harness/r1", "wing_r1/s0/hs", offset=9)
self.hs_hirth_joint.add_constraints(result, "harness/r2", "wing_r2/s0/hs", offset=8)
self.hs_hirth_joint.add_constraints(result, "harness/r3", "wing_r3/s0/hs", offset=7)
return result.solve()
@assembly(collision_check=False)
def trident_assembly(self) -> Cq.Assembly:
"""
Disable collision check since the threads may not align.
"""
return MT.trident_assembly(self.trident_handle)
if __name__ == '__main__':
p = Parameters()
p.build_all()