cosplay: Touhou/Houjuu Nue #4
|
@ -36,6 +36,10 @@ class HirthJoint:
|
||||||
def total_height(self):
|
def total_height(self):
|
||||||
return self.base_height + self.tooth_height
|
return self.base_height + self.tooth_height
|
||||||
|
|
||||||
|
@property
|
||||||
|
def joint_height(self):
|
||||||
|
return 2 * self.base_height + self.tooth_height
|
||||||
|
|
||||||
|
|
||||||
def generate(self, is_mated=False, tol=0.01):
|
def generate(self, is_mated=False, tol=0.01):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -49,14 +49,6 @@ class Parameters(Model):
|
||||||
# Harness
|
# Harness
|
||||||
harness: MH.Harness = field(default_factory=lambda: MH.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_r1: MW.WingR = field(default_factory=lambda: MW.WingR(name="r1"))
|
wing_r1: MW.WingR = field(default_factory=lambda: MW.WingR(name="r1"))
|
||||||
wing_r2: MW.WingR = field(default_factory=lambda: MW.WingR(name="r2"))
|
wing_r2: MW.WingR = field(default_factory=lambda: MW.WingR(name="r2"))
|
||||||
wing_r3: MW.WingR = field(default_factory=lambda: MW.WingR(name="r3"))
|
wing_r3: MW.WingR = field(default_factory=lambda: MW.WingR(name="r3"))
|
||||||
|
@ -77,10 +69,19 @@ 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
|
self.wing_r1.base_joint = self.harness.hs_hirth_joint
|
||||||
self.wing_r1.base_joint = self.hs_hirth_joint
|
self.wing_r2.base_joint = self.harness.hs_hirth_joint
|
||||||
self.wing_r2.base_joint = self.hs_hirth_joint
|
self.wing_r3.base_joint = self.harness.hs_hirth_joint
|
||||||
self.wing_r3.base_joint = self.hs_hirth_joint
|
self.wing_l1.base_joint = self.harness.hs_hirth_joint
|
||||||
|
self.wing_l2.base_joint = self.harness.hs_hirth_joint
|
||||||
|
self.wing_l3.base_joint = self.harness.hs_hirth_joint
|
||||||
|
|
||||||
|
assert self.wing_r1.hs_joint_axis_diam == self.harness.hs_joint_axis_diam
|
||||||
|
assert self.wing_r2.hs_joint_axis_diam == self.harness.hs_joint_axis_diam
|
||||||
|
assert self.wing_r3.hs_joint_axis_diam == self.harness.hs_joint_axis_diam
|
||||||
|
assert self.wing_l1.hs_joint_axis_diam == self.harness.hs_joint_axis_diam
|
||||||
|
assert self.wing_l2.hs_joint_axis_diam == self.harness.hs_joint_axis_diam
|
||||||
|
assert self.wing_l3.hs_joint_axis_diam == self.harness.hs_joint_axis_diam
|
||||||
|
|
||||||
@submodel(name="harness")
|
@submodel(name="harness")
|
||||||
def submodel_harness(self) -> Model:
|
def submodel_harness(self) -> Model:
|
||||||
|
@ -120,12 +121,12 @@ class Parameters(Model):
|
||||||
.add(self.wing_l2.assembly(parts), name="wing_l2")
|
.add(self.wing_l2.assembly(parts), name="wing_l2")
|
||||||
.add(self.wing_l3.assembly(parts), name="wing_l3")
|
.add(self.wing_l3.assembly(parts), name="wing_l3")
|
||||||
)
|
)
|
||||||
self.hs_hirth_joint.add_constraints(result, "harness/r1", "wing_r1/s0/hs", offset=11)
|
for tag, offset in [("r1", 10), ("r2", 8), ("r3", 6), ("l1", 6), ("l2", 7), ("l3", 8)]:
|
||||||
self.hs_hirth_joint.add_constraints(result, "harness/r2", "wing_r2/s0/hs", offset=10)
|
self.harness.hs_hirth_joint.add_constraints(
|
||||||
self.hs_hirth_joint.add_constraints(result, "harness/r3", "wing_r3/s0/hs", offset=9)
|
result,
|
||||||
self.hs_hirth_joint.add_constraints(result, "harness/l1", "wing_l1/s0/hs", offset=6)
|
f"harness/{tag}",
|
||||||
self.hs_hirth_joint.add_constraints(result, "harness/l2", "wing_l2/s0/hs", offset=7)
|
f"wing_{tag}/s0/hs",
|
||||||
self.hs_hirth_joint.add_constraints(result, "harness/l3", "wing_l3/s0/hs", offset=8)
|
offset=offset)
|
||||||
return result.solve()
|
return result.solve()
|
||||||
|
|
||||||
@submodel(name="trident")
|
@submodel(name="trident")
|
||||||
|
|
|
@ -8,26 +8,27 @@ import nhf.utils
|
||||||
@dataclass
|
@dataclass
|
||||||
class Harness(Model):
|
class Harness(Model):
|
||||||
thickness: float = 25.4 / 8
|
thickness: float = 25.4 / 8
|
||||||
width: float = 300.0
|
width: float = 200.0
|
||||||
height: float = 400.0
|
height: float = 300.0
|
||||||
fillet: float = 10.0
|
fillet: float = 10.0
|
||||||
|
|
||||||
wing_base_pos: list[tuple[str, float, float]] = field(default_factory=lambda: [
|
wing_base_pos: list[tuple[str, float, float]] = field(default_factory=lambda: [
|
||||||
("r1", 70, 150),
|
("r1", 55, 90),
|
||||||
("l1", -70, 150),
|
("l1", -55, 90),
|
||||||
("r2", 100, 0),
|
("r2", 60, 0),
|
||||||
("l2", -100, 0),
|
("l2", -60, 0),
|
||||||
("r3", 70, -150),
|
("r3", 55, -90),
|
||||||
("l3", -70, -150),
|
("l3", -55, -90),
|
||||||
])
|
])
|
||||||
# Holes drilled onto harness for attachment with HS joint
|
# Holes drilled onto harness for attachment with HS joint
|
||||||
harness_to_root_conn_diam: float = 6
|
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=25.0,
|
||||||
radius_inner=20,
|
radius_inner=20.0,
|
||||||
tooth_height=10,
|
tooth_height=7.0,
|
||||||
base_height=5
|
base_height=5.0,
|
||||||
|
n_tooth=24,
|
||||||
))
|
))
|
||||||
|
|
||||||
hs_joint_base_width: float = 85
|
hs_joint_base_width: float = 85
|
||||||
|
@ -37,7 +38,7 @@ class Harness(Model):
|
||||||
hs_joint_corner_cbore_depth: float = 2
|
hs_joint_corner_cbore_depth: float = 2
|
||||||
hs_joint_corner_inset: float = 12
|
hs_joint_corner_inset: float = 12
|
||||||
|
|
||||||
hs_joint_axis_diam: float = 12
|
hs_joint_axis_diam: float = 12.0
|
||||||
hs_joint_axis_cbore_diam: float = 20
|
hs_joint_axis_cbore_diam: float = 20
|
||||||
hs_joint_axis_cbore_depth: float = 3
|
hs_joint_axis_cbore_depth: float = 3
|
||||||
|
|
||||||
|
@ -53,12 +54,16 @@ class Harness(Model):
|
||||||
sketch = (
|
sketch = (
|
||||||
Cq.Sketch()
|
Cq.Sketch()
|
||||||
.polygon([
|
.polygon([
|
||||||
(0.7 * w, h),
|
(w, h),
|
||||||
(w, 0),
|
(w, -h),
|
||||||
(0.7 * w, -h),
|
(-w, -h),
|
||||||
(0.7 * -w, -h),
|
(-w, h),
|
||||||
(-w, 0),
|
#(0.7 * w, h),
|
||||||
(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)
|
#.rect(self.harness_width, self.harness_height)
|
||||||
.vertices()
|
.vertices()
|
||||||
|
|
|
@ -20,12 +20,18 @@ class WingProfile(Model):
|
||||||
name: str = "wing"
|
name: str = "wing"
|
||||||
|
|
||||||
base_joint: HirthJoint = field(default_factory=lambda: HirthJoint(
|
base_joint: HirthJoint = field(default_factory=lambda: HirthJoint(
|
||||||
radius=30.0,
|
radius=25.0,
|
||||||
radius_inner=20.0,
|
radius_inner=20.0,
|
||||||
|
tooth_height=7.0,
|
||||||
|
base_height=5,
|
||||||
|
n_tooth=24,
|
||||||
))
|
))
|
||||||
root_width: float = 80.0
|
base_width: float = 80.0
|
||||||
hs_joint_corner_dx: float = 30.0
|
hs_joint_corner_dx: float = 17.0
|
||||||
|
hs_joint_corner_dz: float = 24.0
|
||||||
hs_joint_corner_hole_diam: float = 6.0
|
hs_joint_corner_hole_diam: float = 6.0
|
||||||
|
hs_joint_axis_diam: float = 12.0
|
||||||
|
base_plate_width: float = 50.0
|
||||||
|
|
||||||
panel_thickness: float = 25.4 / 16
|
panel_thickness: float = 25.4 / 16
|
||||||
spacer_thickness: float = 25.4 / 8
|
spacer_thickness: float = 25.4 / 8
|
||||||
|
@ -36,6 +42,8 @@ class WingProfile(Model):
|
||||||
shoulder_width: float = 30.0
|
shoulder_width: float = 30.0
|
||||||
shoulder_tip_x: float = -200.0
|
shoulder_tip_x: float = -200.0
|
||||||
shoulder_tip_y: float = 160.0
|
shoulder_tip_y: float = 160.0
|
||||||
|
shoulder_mid_x: float = -105.0
|
||||||
|
shoulder_mid_y: float = 75.0
|
||||||
|
|
||||||
s1_thickness: float = 25.0
|
s1_thickness: float = 25.0
|
||||||
|
|
||||||
|
@ -94,13 +102,64 @@ class WingProfile(Model):
|
||||||
def shoulder_height(self) -> float:
|
def shoulder_height(self) -> float:
|
||||||
return self.shoulder_joint.height
|
return self.shoulder_joint.height
|
||||||
|
|
||||||
|
@target(name="base-hs-joint")
|
||||||
|
def base_hs_joint(self) -> Cq.Workplane:
|
||||||
|
"""
|
||||||
|
Parent part of the Houjuu-Scarlett joint, which is composed of a Hirth
|
||||||
|
coupling, a cylindrical base, and a mounting base.
|
||||||
|
"""
|
||||||
|
hirth = self.base_joint.generate(is_mated=True)
|
||||||
|
dy = self.hs_joint_corner_dx
|
||||||
|
dx = self.hs_joint_corner_dz
|
||||||
|
conn = [
|
||||||
|
(-dx, -dy),
|
||||||
|
(dx, -dy),
|
||||||
|
(dx, dy),
|
||||||
|
(-dx, dy),
|
||||||
|
]
|
||||||
|
result = (
|
||||||
|
Cq.Workplane('XY')
|
||||||
|
.box(
|
||||||
|
self.root_height,
|
||||||
|
self.base_plate_width,
|
||||||
|
self.base_joint.base_height,
|
||||||
|
centered=(True, True, False))
|
||||||
|
#.translate((0, 0, -self.base_joint.base_height))
|
||||||
|
#.edges("|Z")
|
||||||
|
#.fillet(self.hs_joint_corner_fillet)
|
||||||
|
.faces(">Z")
|
||||||
|
.workplane()
|
||||||
|
.pushPoints(conn)
|
||||||
|
.hole(self.hs_joint_corner_hole_diam)
|
||||||
|
)
|
||||||
|
# Creates a plane parallel to the holes but shifted to the base
|
||||||
|
plane = result.faces(">Z").workplane(offset=-self.base_joint.base_height)
|
||||||
|
|
||||||
|
for i, (px, py) in enumerate(conn):
|
||||||
|
plane.moveTo(px, py).tagPlane(f"conn{i}")
|
||||||
|
result = (
|
||||||
|
result
|
||||||
|
.faces(">Z")
|
||||||
|
.workplane()
|
||||||
|
.union(hirth, tol=0.1)
|
||||||
|
.clean()
|
||||||
|
)
|
||||||
|
result = (
|
||||||
|
result.faces("<Z")
|
||||||
|
.workplane()
|
||||||
|
.hole(self.hs_joint_axis_diam)
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
@target(name="profile-s0", kind=TargetKind.DXF)
|
@target(name="profile-s0", kind=TargetKind.DXF)
|
||||||
def profile_s0(self) -> Cq.Sketch:
|
def profile_s0(self) -> Cq.Sketch:
|
||||||
tip_x = self.shoulder_tip_x
|
tip_x = self.shoulder_tip_x
|
||||||
tip_y = self.shoulder_tip_y
|
tip_y = self.shoulder_tip_y
|
||||||
|
mid_x = self.shoulder_mid_x
|
||||||
|
mid_y = self.shoulder_mid_y
|
||||||
sketch = (
|
sketch = (
|
||||||
Cq.Sketch()
|
Cq.Sketch()
|
||||||
.segment((-self.root_width, 0), (0, 0))
|
.segment((-self.base_width, 0), (0, 0))
|
||||||
.spline([
|
.spline([
|
||||||
(0, 0),
|
(0, 0),
|
||||||
(-30.0, 80.0),
|
(-30.0, 80.0),
|
||||||
|
@ -108,16 +167,42 @@ class WingProfile(Model):
|
||||||
])
|
])
|
||||||
.segment(
|
.segment(
|
||||||
(tip_x, tip_y),
|
(tip_x, tip_y),
|
||||||
(tip_x, tip_y - self.shoulder_width)
|
(tip_x, tip_y - self.shoulder_width),
|
||||||
)
|
)
|
||||||
.segment(
|
.segment(
|
||||||
(tip_x, tip_y - self.shoulder_width),
|
(tip_x, tip_y - self.shoulder_width),
|
||||||
(-self.root_width, 0)
|
(mid_x, mid_y),
|
||||||
|
)
|
||||||
|
.segment(
|
||||||
|
(mid_x, mid_y),
|
||||||
|
(-self.base_width, 0),
|
||||||
)
|
)
|
||||||
.assemble()
|
.assemble()
|
||||||
)
|
)
|
||||||
return sketch
|
return sketch
|
||||||
|
|
||||||
|
def outer_shell_s0(self) -> Cq.Workplane:
|
||||||
|
tip_x = self.shoulder_tip_x
|
||||||
|
tip_y = self.shoulder_tip_y
|
||||||
|
t = self.panel_thickness
|
||||||
|
edge = Cq.Edge.makeSpline([
|
||||||
|
Cq.Vector(x, y, 0)
|
||||||
|
for x,y in [
|
||||||
|
(0, 0),
|
||||||
|
(-30.0, 80.0),
|
||||||
|
(tip_x, tip_y)
|
||||||
|
]
|
||||||
|
])
|
||||||
|
result = (
|
||||||
|
Cq.Workplane('XZ')
|
||||||
|
.rect(t, self.root_height + t*2, centered=(False, False))
|
||||||
|
.sweep(edge)
|
||||||
|
)
|
||||||
|
plane = result.copyWorkplane(Cq.Workplane('XZ'))
|
||||||
|
plane.moveTo(0, 0).tagPlane("bot")
|
||||||
|
plane.moveTo(0, self.root_height + t*2).tagPlane("top")
|
||||||
|
return result
|
||||||
|
|
||||||
@submodel(name="spacer-s0-shoulder")
|
@submodel(name="spacer-s0-shoulder")
|
||||||
def spacer_s0_shoulder(self) -> MountingBox:
|
def spacer_s0_shoulder(self) -> MountingBox:
|
||||||
"""
|
"""
|
||||||
|
@ -145,16 +230,20 @@ class WingProfile(Model):
|
||||||
"""
|
"""
|
||||||
Should be cut
|
Should be cut
|
||||||
"""
|
"""
|
||||||
dx = self.hs_joint_corner_dx
|
assert self.base_plate_width < self.base_width
|
||||||
|
assert self.hs_joint_corner_dx * 2 < self.base_width
|
||||||
|
assert self.hs_joint_corner_dz * 2 < self.root_height
|
||||||
|
dy = self.hs_joint_corner_dx
|
||||||
|
dx = self.hs_joint_corner_dz
|
||||||
holes = [
|
holes = [
|
||||||
Hole(x=-dx, y=-dx),
|
Hole(x=-dx, y=-dy),
|
||||||
Hole(x=dx, y=-dx),
|
Hole(x=dx, y=-dy),
|
||||||
Hole(x=dx, y=dx),
|
Hole(x=dx, y=dy),
|
||||||
Hole(x=-dx, y=dx),
|
Hole(x=-dx, y=dy),
|
||||||
]
|
]
|
||||||
return MountingBox(
|
return MountingBox(
|
||||||
length=self.root_height,
|
length=self.root_height,
|
||||||
width=self.root_width,
|
width=self.base_plate_width,
|
||||||
thickness=self.spacer_thickness,
|
thickness=self.spacer_thickness,
|
||||||
holes=holes,
|
holes=holes,
|
||||||
hole_diam=self.hs_joint_corner_hole_diam,
|
hole_diam=self.hs_joint_corner_hole_diam,
|
||||||
|
@ -163,16 +252,21 @@ class WingProfile(Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
def surface_s0(self, top: bool = False) -> Cq.Workplane:
|
def surface_s0(self, top: bool = False) -> Cq.Workplane:
|
||||||
|
base_dx = -(self.base_width - self.base_plate_width) / 2
|
||||||
|
base_dy = self.base_joint.joint_height
|
||||||
tags = [
|
tags = [
|
||||||
("shoulder", (self.shoulder_tip_x, self.shoulder_tip_y - self.shoulder_width), 0),
|
("shoulder", (self.shoulder_tip_x, self.shoulder_tip_y - self.shoulder_width), 0),
|
||||||
("base", (0, 0), 90),
|
("base", (base_dx, base_dy), 90),
|
||||||
]
|
]
|
||||||
return nhf.utils.extrude_with_markers(
|
result = nhf.utils.extrude_with_markers(
|
||||||
self.profile_s0(),
|
self.profile_s0(),
|
||||||
self.panel_thickness,
|
self.panel_thickness,
|
||||||
tags,
|
tags,
|
||||||
reverse=not top,
|
reverse=not top,
|
||||||
)
|
)
|
||||||
|
h = self.panel_thickness if top else 0
|
||||||
|
result.copyWorkplane(Cq.Workplane('XZ')).moveTo(0, h).tagPlane("corner")
|
||||||
|
return result
|
||||||
|
|
||||||
@assembly()
|
@assembly()
|
||||||
def assembly_s0(self) -> Cq.Assembly:
|
def assembly_s0(self) -> Cq.Assembly:
|
||||||
|
@ -184,6 +278,10 @@ class WingProfile(Model):
|
||||||
material=self.mat_panel, role=self.role_panel)
|
material=self.mat_panel, role=self.role_panel)
|
||||||
.constrain("bot@faces@>Z", "top@faces@<Z", "Point",
|
.constrain("bot@faces@>Z", "top@faces@<Z", "Point",
|
||||||
param=self.shoulder_joint.height)
|
param=self.shoulder_joint.height)
|
||||||
|
.addS(self.outer_shell_s0(), name="outer_shell",
|
||||||
|
material=self.mat_panel, role=self.role_panel)
|
||||||
|
.constrain("bot?corner", "outer_shell?bot", "Plane", param=0)
|
||||||
|
.constrain("top?corner", "outer_shell?top", "Plane", param=0)
|
||||||
)
|
)
|
||||||
for o, tag in [
|
for o, tag in [
|
||||||
(self.spacer_s0_shoulder().generate(), "shoulder"),
|
(self.spacer_s0_shoulder().generate(), "shoulder"),
|
||||||
|
@ -201,11 +299,13 @@ class WingProfile(Model):
|
||||||
.constrain(f"{tag}?{top_tag}", f"top?{tag}", "Plane")
|
.constrain(f"{tag}?{top_tag}", f"top?{tag}", "Plane")
|
||||||
.constrain(f"{tag}?dir", f"top?{tag}_dir", "Axis")
|
.constrain(f"{tag}?dir", f"top?{tag}_dir", "Axis")
|
||||||
)
|
)
|
||||||
hirth = self.base_joint.generate(is_mated=True)
|
hs_joint = self.base_hs_joint()
|
||||||
(
|
(
|
||||||
result
|
result
|
||||||
.addS(hirth, name="hs", role=Role.CHILD, material=self.mat_hs_joint)
|
.addS(hs_joint, name="hs", role=Role.CHILD, material=self.mat_hs_joint)
|
||||||
.constrain("hs@faces@<Z", "base?dir", "Plane")
|
.constrain("hs?conn0", "base?conn0", "Plane", param=0)
|
||||||
|
.constrain("hs?conn1", "base?conn1", "Plane", param=0)
|
||||||
|
.constrain("hs?conn2", "base?conn2", "Plane", param=0)
|
||||||
)
|
)
|
||||||
return result.solve()
|
return result.solve()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue