cosplay: Touhou/Houjuu Nue #4

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

View File

@ -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):
""" """

View File

@ -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")

View File

@ -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()

View File

@ -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()