cosplay: Touhou/Houjuu Nue #4
|
@ -87,6 +87,11 @@ class Parameters(Model):
|
|||
hs_joint_axis_cbore_diam: float = 20
|
||||
hs_joint_axis_cbore_depth: float = 3
|
||||
|
||||
wing_profile: MW.WingProfile = field(default_factory=lambda: MW.WingProfile(
|
||||
shoulder_height = 100,
|
||||
elbow_height = 120,
|
||||
))
|
||||
|
||||
# Exterior radius of the wing root assembly
|
||||
wing_root_radius: float = 40
|
||||
wing_root_wall_thickness: float = 8
|
||||
|
@ -347,6 +352,15 @@ class Parameters(Model):
|
|||
result.moveTo(0, self.shoulder_attach_dist).tagPlane('conn1')
|
||||
return result
|
||||
|
||||
@property
|
||||
def shoulder_joint_child_height(self) -> float:
|
||||
"""
|
||||
Calculates the y distance between two joint surfaces on the child side
|
||||
of the shoulder joint.
|
||||
"""
|
||||
joint = self.shoulder_torsion_joint
|
||||
return self.wing_s0_height - 2 * joint.total_height + 2 * joint.rider_disk_height
|
||||
|
||||
@target(name="shoulder_joint_child")
|
||||
def shoulder_joint_child(self) -> Cq.Assembly:
|
||||
"""
|
||||
|
@ -470,18 +484,18 @@ class Parameters(Model):
|
|||
.finalize()
|
||||
.extrude(self.wing_s1_spacer_thickness)
|
||||
)
|
||||
result.faces("<Z").tag("mate1")
|
||||
result.faces(">Z").tag("mate2")
|
||||
result.faces("<Z").tag("weld1")
|
||||
result.faces(">Z").tag("weld2")
|
||||
result.faces(">Y").tag("dir")
|
||||
return result
|
||||
|
||||
@target(name="wing/s1-shoulder-spacer", kind=TargetKind.DXF)
|
||||
def wing_s1_shoulder_spacer(self, flipped=False) -> Cq.Workplane:
|
||||
def wing_s1_shoulder_spacer(self) -> Cq.Workplane:
|
||||
"""
|
||||
if `flipped = True`, tag on the bottom face. This does not change the
|
||||
geometry.
|
||||
The mate tags are on the side closer to the holes.
|
||||
"""
|
||||
dx = self.wing_s1_shoulder_spacer_hole_dist
|
||||
h = self.wing_s1_spacer_thickness
|
||||
result = (
|
||||
Cq.Workplane('XZ')
|
||||
.sketch()
|
||||
|
@ -493,33 +507,35 @@ class Parameters(Model):
|
|||
])
|
||||
.circle(self.wing_s1_spacer_hole_diam / 2, mode='s')
|
||||
.finalize()
|
||||
.extrude(self.wing_s1_spacer_thickness)
|
||||
.extrude(h)
|
||||
)
|
||||
# Tag the mating surfaces to be glued
|
||||
result.faces("<Z").tag("mate1")
|
||||
result.faces(">Z").tag("mate2")
|
||||
result.faces("<Z").workplane().moveTo(0, h).tagPlane("weld1")
|
||||
result.faces(">Z").workplane().moveTo(0, -h).tagPlane("weld2")
|
||||
|
||||
# Tag the directrix
|
||||
result.faces("<Y").tag("dir")
|
||||
|
||||
# Tag the holes
|
||||
plane = result.faces("<Y" if flipped else ">Y").workplane()
|
||||
plane = result.faces(">Y").workplane()
|
||||
# Side closer to the parent is 0
|
||||
plane.moveTo(dx if flipped else -dx, 0).tagPlane("conn0")
|
||||
plane.moveTo(-dx, 0).tagPlane("conn0")
|
||||
plane.tagPlane("conn1")
|
||||
return result
|
||||
|
||||
@target(name="wing/r1s1", kind=TargetKind.DXF)
|
||||
def wing_r1s1_profile(self) -> Cq.Sketch:
|
||||
return MW.wing_r1s1_profile()
|
||||
return self.wing_profile.wing_r1s1_profile()
|
||||
|
||||
def wing_r1s1_panel(self, front=True) -> Cq.Workplane:
|
||||
profile = self.wing_r1s1_profile()
|
||||
w = self.wing_s1_shoulder_spacer_width / 2
|
||||
h = (self.wing_profile.shoulder_height - self.shoulder_joint_child_height) / 2
|
||||
anchors = [
|
||||
("shoulder_top", 10, 55),
|
||||
("shoulder_bot", 10, 5),
|
||||
("shoulder_top", w, h + self.shoulder_joint_child_height),
|
||||
("shoulder_bot", w, h),
|
||||
("middle", 50, -20),
|
||||
("tip", 390, -150),
|
||||
("tip", 270, 50),
|
||||
]
|
||||
result = (
|
||||
Cq.Workplane("XY")
|
||||
|
@ -543,25 +559,30 @@ class Parameters(Model):
|
|||
color=self.material_panel.color)
|
||||
.constrain("panel_front@faces@>Z", "panel_back@faces@<Z", "Point",
|
||||
param=self.wing_s1_thickness)
|
||||
.add(self.wing_s1_shoulder_spacer(),
|
||||
name="shoulder_bot_spacer",
|
||||
color=self.material_bracket.color)
|
||||
.constrain("panel_front?shoulder_bot", "shoulder_bot_spacer?weld1", "Plane")
|
||||
.constrain("panel_back?shoulder_bot", "shoulder_bot_spacer?weld2", "Plane")
|
||||
.constrain("shoulder_bot_spacer?dir", "FixedAxis", param=(0, 1, 0))
|
||||
.add(self.wing_s1_shoulder_spacer(),
|
||||
name="shoulder_top_spacer",
|
||||
color=self.material_bracket.color)
|
||||
.constrain("panel_front?shoulder_top", "shoulder_top_spacer?weld2", "Plane")
|
||||
.constrain("panel_back?shoulder_top", "shoulder_top_spacer?weld1", "Plane")
|
||||
.constrain("shoulder_top_spacer?dir", "FixedAxis", param=(0, -1, 0))
|
||||
# Should be controlled by point value directly
|
||||
#.constrain("shoulder_bot_spacer?dir", "shoulder_top_spacer?dir", "Point",
|
||||
# self.shoulder_joint_child_height)
|
||||
)
|
||||
for tag in ["shoulder_top", "shoulder_bot"]:
|
||||
name = f"{tag}_spacer"
|
||||
flipped = tag.endswith("top")
|
||||
(
|
||||
result
|
||||
.add(self.wing_s1_shoulder_spacer(flipped=flipped), name=name,
|
||||
color=self.material_bracket.color)
|
||||
.constrain(f"panel_front?{tag}", f"{name}?mate1", "Plane")
|
||||
.constrain(f"panel_back?{tag}", f"{name}?mate2", "Plane")
|
||||
.constrain(f"{name}?dir", "FixedAxis", param=(0, 1, 0))
|
||||
)
|
||||
for tag in ["middle", "tip"]:
|
||||
name = f"{tag}_spacer"
|
||||
(
|
||||
result
|
||||
.add(self.wing_s1_spacer(), name=f"{tag}_spacer",
|
||||
.add(self.wing_s1_spacer(), name=name,
|
||||
color=self.material_bracket.color)
|
||||
.constrain(f"panel_front?{tag}", f"{tag}_spacer?mate1", "Plane")
|
||||
.constrain(f"panel_back?{tag}", f"{tag}_spacer?mate2", "Plane")
|
||||
.constrain(f"panel_front?{tag}", f"{tag}_spacer?weld1", "Plane")
|
||||
.constrain(f"panel_back?{tag}", f"{tag}_spacer?weld2", "Plane")
|
||||
.constrain(f"{name}?dir", "FixedAxis", param=(0, 1, 0))
|
||||
)
|
||||
return result.solve()
|
||||
|
|
|
@ -3,11 +3,13 @@ This file describes the shapes of the wing shells. The joints are defined in
|
|||
`__init__.py`.
|
||||
"""
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
import cadquery as Cq
|
||||
from nhf import Material, Role
|
||||
from nhf.parts.joints import HirthJoint
|
||||
import nhf.utils
|
||||
|
||||
|
||||
def wing_root_profiles(
|
||||
base_sweep=150,
|
||||
wall_thickness=8,
|
||||
|
@ -227,33 +229,50 @@ def wing_root(joint: HirthJoint,
|
|||
)
|
||||
return result
|
||||
|
||||
def wing_r1s1_profile() -> Cq.Sketch:
|
||||
"""
|
||||
Generates the first wing segment profile, with the wing root pointing in
|
||||
the positive x axis.
|
||||
"""
|
||||
# Depression of the wing middle
|
||||
h = 100
|
||||
w = 400
|
||||
bend = 200
|
||||
factor = 0.7
|
||||
result = (
|
||||
Cq.Sketch()
|
||||
.segment((0, 0), (0, h))
|
||||
.spline([
|
||||
(0, h),
|
||||
(0.5 * w, h - factor * bend),
|
||||
(w, h - bend),
|
||||
])
|
||||
.segment(
|
||||
(w, h - bend),
|
||||
(w, -bend),
|
||||
|
||||
@dataclass
|
||||
class WingProfile:
|
||||
|
||||
shoulder_height: float = 100
|
||||
elbow_height: float = 120
|
||||
|
||||
def wing_r1s1_profile(self) -> Cq.Sketch:
|
||||
"""
|
||||
Generates the first wing segment profile, with the wing root pointing in
|
||||
the positive x axis.
|
||||
"""
|
||||
|
||||
|
||||
w = 270
|
||||
# Depression of the wing middle, measured
|
||||
h = 0
|
||||
# spline curve easing extension
|
||||
theta = math.radians(30)
|
||||
c_th, s_th = math.cos(theta), math.sin(theta)
|
||||
bend = 30
|
||||
ext = 40
|
||||
ext_dh = -5
|
||||
assert ext * 2 < w
|
||||
|
||||
factor = 0.7
|
||||
|
||||
result = (
|
||||
Cq.Sketch()
|
||||
.segment((0, 0), (0, self.shoulder_height))
|
||||
.spline([
|
||||
(0, self.shoulder_height),
|
||||
((w - s_th * self.elbow_height) / 2, self.shoulder_height / 2 + (self.elbow_height * c_th - h) / 2 - bend),
|
||||
(w - s_th * self.elbow_height, self.elbow_height * c_th - h),
|
||||
])
|
||||
.segment(
|
||||
(w - s_th * self.elbow_height, self.elbow_height * c_th -h),
|
||||
(w, -h),
|
||||
)
|
||||
.spline([
|
||||
(0, 0),
|
||||
(w / 2, -h / 2 - bend),
|
||||
(w, -h),
|
||||
])
|
||||
.assemble()
|
||||
)
|
||||
.spline([
|
||||
(w, - bend),
|
||||
(0.5 * w, - factor * bend),
|
||||
(0, 0),
|
||||
])
|
||||
.assemble()
|
||||
)
|
||||
return result
|
||||
return result
|
||||
|
|
Loading…
Reference in New Issue