cosplay: Touhou/Houjuu Nue #4

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

View File

@ -38,6 +38,7 @@ from nhf.parts.handle import Handle, BayonetMount
import nhf.touhou.houjuu_nue.wing as MW import nhf.touhou.houjuu_nue.wing as MW
import nhf.touhou.houjuu_nue.trident as MT import nhf.touhou.houjuu_nue.trident as MT
import nhf.touhou.houjuu_nue.joints as MJ import nhf.touhou.houjuu_nue.joints as MJ
import nhf.touhou.houjuu_nue.harness as MH
import nhf.utils import nhf.utils
@dataclass @dataclass
@ -50,44 +51,16 @@ class Parameters(Model):
panel_thickness: float = 25.4 / 16 panel_thickness: float = 25.4 / 16
# Harness # Harness
harness_thickness: float = 25.4 / 8 harness: MH.Harness = field(default_factory=lambda: MH.Harness())
harness_width: float = 300
harness_height: float = 400
harness_fillet: float = 10
harness_wing_base_pos: list[tuple[str, float, float]] = field(default_factory=lambda: [
("r1", 70, 150),
("l1", -70, 150),
("r2", 100, 0),
("l2", -100, 0),
("r3", 70, -150),
("l3", -70, -150),
])
# Holes drilled onto harness for attachment with HS joint
harness_to_root_conn_diam: float = 6
hs_hirth_joint: HirthJoint = field(default_factory=lambda: HirthJoint( hs_hirth_joint: HirthJoint = field(default_factory=lambda: HirthJoint(
radius=30, radius=30,
radius_inner=20, radius_inner=20,
tooth_height=10, tooth_height=10,
base_height=5 base_height=5,
n_tooth=24
)) ))
# Wing root properties
#
# The Houjuu-Scarlett joint mechanism at the base of the wing
hs_joint_base_width: float = 85
hs_joint_base_thickness: float = 10
hs_joint_corner_fillet: float = 5
hs_joint_corner_cbore_diam: float = 12
hs_joint_corner_cbore_depth: float = 2
hs_joint_corner_inset: float = 12
hs_joint_axis_diam: float = 12
hs_joint_axis_cbore_diam: float = 20
hs_joint_axis_cbore_depth: float = 3
wing_profile: MW.WingProfile = field(default_factory=lambda: MW.WingProfile( wing_profile: MW.WingProfile = field(default_factory=lambda: MW.WingProfile(
shoulder_height=100.0, shoulder_height=100.0,
elbow_height=110.0, elbow_height=110.0,
@ -135,7 +108,8 @@ class Parameters(Model):
def __post_init__(self): def __post_init__(self):
super().__init__(name="houjuu-nue") super().__init__(name="houjuu-nue")
assert self.wing_root_radius > self.hs_hirth_joint.radius,\ self.harness.hs_hirth_joint = self.hs_hirth_joint
assert self.wing_root_radius > self.hs_hirth_joint.radius, \
"Wing root must be large enough to accomodate joint" "Wing root must be large enough to accomodate joint"
assert self.wing_s1_shoulder_spacer_hole_dist > self.wing_s1_spacer_hole_diam, \ assert self.wing_s1_shoulder_spacer_hole_dist > self.wing_s1_spacer_hole_diam, \
"Spacer holes are too close to each other" "Spacer holes are too close to each other"
@ -157,150 +131,19 @@ class Parameters(Model):
result = result.faces(">Z").hole(self.trident_terminal_hole_diam, depth=h) result = result.faces(">Z").hole(self.trident_terminal_hole_diam, depth=h)
return result return result
def harness_profile(self) -> Cq.Sketch:
"""
Creates the harness shape
"""
w, h = self.harness_width / 2, self.harness_height / 2
sketch = (
Cq.Sketch()
.polygon([
(0.7 * w, h),
(w, 0),
(0.7 * w, -h),
(0.7 * -w, -h),
(-w, 0),
(0.7 * -w, h),
])
#.rect(self.harness_width, self.harness_height)
.vertices()
.fillet(self.harness_fillet)
)
for tag, x, y in self.harness_wing_base_pos:
conn = [(px + x, py + y) for px, py in self.hs_joint_harness_conn()]
sketch = (
sketch
.push(conn)
.tag(tag)
.circle(self.harness_to_root_conn_diam / 2, mode='s')
.reset()
)
return sketch
@target(name="harness", kind=TargetKind.DXF) @target(name="harness", kind=TargetKind.DXF)
def harness(self) -> Cq.Shape: def harness_profile(self) -> Cq.Sketch:
""" return self.harness.profile()
Creates the harness shape
"""
result = (
Cq.Workplane('XZ')
.placeSketch(self.harness_profile())
.extrude(self.harness_thickness)
)
result.faces(">Y").tag("mount")
plane = result.faces(">Y").workplane()
for tag, x, y in self.harness_wing_base_pos:
conn = [(px + x, py + y) for px, py
in self.hs_joint_harness_conn()]
for i, (px, py) in enumerate(conn):
(
plane
.moveTo(px, py)
.circle(1, forConstruction='True')
.edges()
.tag(f"{tag}_{i}")
)
return result
def hs_joint_harness_conn(self) -> list[tuple[int, int]]: def harness_surface(self) -> Cq.Workplane:
""" return self.harness.surface()
Generates a set of points corresponding to the connectorss
"""
dx = self.hs_joint_base_width / 2 - self.hs_joint_corner_inset
return [
(dx, dx),
(dx, -dx),
(-dx, -dx),
(-dx, dx),
]
@target(name="hs_joint_parent") def hs_joint_parent(self) -> Cq.Workplane:
def hs_joint_parent(self): return self.harness.hs_joint_parent()
"""
Parent part of the Houjuu-Scarlett joint, which is composed of a Hirth
coupling, a cylindrical base, and a mounting base.
"""
hirth = self.hs_hirth_joint.generate()
conn = self.hs_joint_harness_conn()
result = (
Cq.Workplane('XY')
.box(
self.hs_joint_base_width,
self.hs_joint_base_width,
self.hs_joint_base_thickness,
centered=(True, True, False))
.translate((0, 0, -self.hs_joint_base_thickness))
.edges("|Z")
.fillet(self.hs_joint_corner_fillet)
.faces(">Z")
.workplane()
.pushPoints(conn)
.cboreHole(
diameter=self.harness_to_root_conn_diam,
cboreDiameter=self.hs_joint_corner_cbore_diam,
cboreDepth=self.hs_joint_corner_cbore_depth)
)
# Creates a plane parallel to the holes but shifted to the base
plane = result.faces(">Z").workplane(offset=-self.hs_joint_base_thickness)
for i, (px, py) in enumerate(conn):
(
plane
.pushPoints([(px, py)])
.circle(1, forConstruction='True')
.edges()
.tag(f"h{i}")
)
result = (
result
.faces(">Z")
.workplane()
.union(hirth, tol=0.1)
.clean()
)
result = (
result.faces("<Z")
.workplane()
.cboreHole(
diameter=self.hs_joint_axis_diam,
cboreDiameter=self.hs_joint_axis_cbore_diam,
cboreDepth=self.hs_joint_axis_cbore_depth,
)
.clean()
)
result.faces("<Z").tag("base")
return result
@assembly() @assembly()
def harness_assembly(self) -> Cq.Assembly: def harness_assembly(self) -> Cq.Assembly:
harness = self.harness() return self.harness.assembly()
result = (
Cq.Assembly()
.add(harness, name="base", color=Material.WOOD_BIRCH.color)
.constrain("base", "Fixed")
)
for name in ["l1", "l2", "l3", "r1", "r2", "r3"]:
j = self.hs_joint_parent()
(
result
.add(j, name=name, color=Role.PARENT.color)
.constrain("base?mount", f"{name}?base", "Axis")
)
for i in range(4):
result.constrain(f"base?{name}_{i}", f"{name}?h{i}", "Point")
result.solve()
return result
#@target(name="wing/joining-plate", kind=TargetKind.DXF) #@target(name="wing/joining-plate", kind=TargetKind.DXF)
#def joining_plate(self) -> Cq.Workplane: #def joining_plate(self) -> Cq.Workplane:

View File

@ -0,0 +1,170 @@
from dataclasses import dataclass, field
import cadquery as Cq
from nhf.parts.joints import HirthJoint
from nhf import Material, Role
import nhf.utils
@dataclass
class Harness:
thickness: float = 25.4 / 8
width: float = 300.0
height: float = 400.0
fillet: float = 10.0
wing_base_pos: list[tuple[str, float, float]] = field(default_factory=lambda: [
("r1", 70, 150),
("l1", -70, 150),
("r2", 100, 0),
("l2", -100, 0),
("r3", 70, -150),
("l3", -70, -150),
])
# Holes drilled onto harness for attachment with HS joint
harness_to_root_conn_diam: float = 6
hs_hirth_joint: HirthJoint = field(default_factory=lambda: HirthJoint(
radius=30,
radius_inner=20,
tooth_height=10,
base_height=5
))
hs_joint_base_width: float = 85
hs_joint_base_thickness: float = 10
hs_joint_corner_fillet: float = 5
hs_joint_corner_cbore_diam: float = 12
hs_joint_corner_cbore_depth: float = 2
hs_joint_corner_inset: float = 12
hs_joint_axis_diam: float = 12
hs_joint_axis_cbore_diam: float = 20
hs_joint_axis_cbore_depth: float = 3
def profile(self) -> Cq.Sketch:
"""
Creates the harness shape
"""
w, h = self.width / 2, self.height / 2
sketch = (
Cq.Sketch()
.polygon([
(0.7 * w, h),
(w, 0),
(0.7 * w, -h),
(0.7 * -w, -h),
(-w, 0),
(0.7 * -w, h),
])
#.rect(self.harness_width, self.harness_height)
.vertices()
.fillet(self.fillet)
)
for tag, x, y in self.wing_base_pos:
conn = [(px + x, py + y) for px, py in self.hs_joint_harness_conn()]
sketch = (
sketch
.push(conn)
.tag(tag)
.circle(self.harness_to_root_conn_diam / 2, mode='s')
.reset()
)
return sketch
def surface(self) -> Cq.Workplane:
"""
Creates the harness shape
"""
result = (
Cq.Workplane('XZ')
.placeSketch(self.profile())
.extrude(self.thickness)
)
result.faces(">Y").tag("mount")
plane = result.faces(">Y").workplane()
for tag, x, y in self.wing_base_pos:
conn = [(px + x, py + y) for px, py
in self.hs_joint_harness_conn()]
for i, (px, py) in enumerate(conn):
plane.moveTo(px, py).tagPoint(f"{tag}_{i}")
return result
def hs_joint_harness_conn(self) -> list[tuple[int, int]]:
"""
Generates a set of points corresponding to the connectorss
"""
dx = self.hs_joint_base_width / 2 - self.hs_joint_corner_inset
return [
(dx, dx),
(dx, -dx),
(-dx, -dx),
(-dx, dx),
]
def hs_joint_parent(self):
"""
Parent part of the Houjuu-Scarlett joint, which is composed of a Hirth
coupling, a cylindrical base, and a mounting base.
"""
hirth = self.hs_hirth_joint.generate()
conn = self.hs_joint_harness_conn()
result = (
Cq.Workplane('XY')
.box(
self.hs_joint_base_width,
self.hs_joint_base_width,
self.hs_joint_base_thickness,
centered=(True, True, False))
.translate((0, 0, -self.hs_joint_base_thickness))
.edges("|Z")
.fillet(self.hs_joint_corner_fillet)
.faces(">Z")
.workplane()
.pushPoints(conn)
.cboreHole(
diameter=self.harness_to_root_conn_diam,
cboreDiameter=self.hs_joint_corner_cbore_diam,
cboreDepth=self.hs_joint_corner_cbore_depth)
)
# Creates a plane parallel to the holes but shifted to the base
plane = result.faces(">Z").workplane(offset=-self.hs_joint_base_thickness)
for i, (px, py) in enumerate(conn):
plane.moveTo(px, py).tagPoint(f"h{i}")
result = (
result
.faces(">Z")
.workplane()
.union(hirth, tol=0.1)
.clean()
)
result = (
result.faces("<Z")
.workplane()
.cboreHole(
diameter=self.hs_joint_axis_diam,
cboreDiameter=self.hs_joint_axis_cbore_diam,
cboreDepth=self.hs_joint_axis_cbore_depth,
)
.clean()
)
result.faces("<Z").tag("base")
return result
def assembly(self) -> Cq.Assembly:
harness = self.surface()
result = (
Cq.Assembly()
.add(harness, name="base", color=Material.WOOD_BIRCH.color)
.constrain("base", "Fixed")
)
for name in ["l1", "l2", "l3", "r1", "r2", "r3"]:
j = self.hs_joint_parent()
(
result
.add(j, name=name, color=Role.PARENT.color)
#.constrain("base?mount", f"{name}?base", "Axis")
)
for i in range(4):
result.constrain(f"base?{name}_{i}", f"{name}?h{i}", "Point")
result.solve()
return result