cosplay: Touhou/Houjuu Nue #4
|
@ -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,6 +108,7 @@ class Parameters(Model):
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
super().__init__(name="houjuu-nue")
|
super().__init__(name="houjuu-nue")
|
||||||
|
self.harness.hs_hirth_joint = self.hs_hirth_joint
|
||||||
assert self.wing_root_radius > self.hs_hirth_joint.radius, \
|
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, \
|
||||||
|
@ -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:
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue