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