cosplay: Touhou/Houjuu Nue #4
|
@ -25,7 +25,7 @@ class Harness(Model):
|
||||||
|
|
||||||
hs_hirth_joint: HirthJoint = field(default_factory=lambda: HirthJoint(
|
hs_hirth_joint: HirthJoint = field(default_factory=lambda: HirthJoint(
|
||||||
radius=25.0,
|
radius=25.0,
|
||||||
radius_inner=20.0,
|
radius_inner=15.0,
|
||||||
tooth_height=7.0,
|
tooth_height=7.0,
|
||||||
base_height=5.0,
|
base_height=5.0,
|
||||||
n_tooth=24,
|
n_tooth=24,
|
||||||
|
|
|
@ -6,7 +6,7 @@ from nhf import Material, Role
|
||||||
from nhf.build import Model, target, assembly
|
from nhf.build import Model, target, assembly
|
||||||
from nhf.parts.springs import TorsionSpring
|
from nhf.parts.springs import TorsionSpring
|
||||||
from nhf.parts.joints import TorsionJoint
|
from nhf.parts.joints import TorsionJoint
|
||||||
from nhf.parts.box import box_with_centre_holes
|
from nhf.parts.box import Hole, MountingBox, box_with_centre_holes
|
||||||
import nhf.utils
|
import nhf.utils
|
||||||
|
|
||||||
TOL = 1e-6
|
TOL = 1e-6
|
||||||
|
@ -37,16 +37,18 @@ class ShoulderJoint(Model):
|
||||||
|
|
||||||
# On the parent side, drill vertical holes
|
# On the parent side, drill vertical holes
|
||||||
|
|
||||||
parent_conn_hole_diam: float = 4.0
|
parent_conn_hole_diam: float = 6.0
|
||||||
# Position of the holes relative
|
# Position of the holes relative
|
||||||
parent_conn_hole_pos: list[float] = field(default_factory=lambda: [15])
|
parent_conn_hole_pos: list[Tuple[float, float]] = field(default_factory=lambda: [
|
||||||
|
(15, 8),
|
||||||
|
(15, -8),
|
||||||
|
])
|
||||||
|
|
||||||
parent_lip_length: float = 25.0
|
parent_lip_length: float = 25.0
|
||||||
parent_lip_width: float = 30.0
|
parent_lip_width: float = 30.0
|
||||||
parent_lip_thickness: float = 5.0
|
parent_lip_thickness: float = 5.0
|
||||||
parent_lip_ext: float = 40.0
|
parent_lip_ext: float = 40.0
|
||||||
parent_lip_guard_height: float = 8.0
|
parent_lip_guard_height: float = 8.0
|
||||||
parent_root_wall_thickness: float = 25.4 / 16
|
|
||||||
|
|
||||||
# Measured from centre of axle
|
# Measured from centre of axle
|
||||||
child_lip_length: float = 45.0
|
child_lip_length: float = 45.0
|
||||||
|
@ -68,7 +70,6 @@ class ShoulderJoint(Model):
|
||||||
def parent(self, top: bool = False) -> Cq.Assembly:
|
def parent(self, top: bool = False) -> Cq.Assembly:
|
||||||
joint = self.torsion_joint
|
joint = self.torsion_joint
|
||||||
# Thickness of the lip connecting this joint to the wing root
|
# Thickness of the lip connecting this joint to the wing root
|
||||||
dz = self.parent_root_wall_thickness
|
|
||||||
assert self.parent_lip_width <= joint.radius_track * 2
|
assert self.parent_lip_width <= joint.radius_track * 2
|
||||||
assert self.parent_lip_ext > joint.radius_track
|
assert self.parent_lip_ext > joint.radius_track
|
||||||
|
|
||||||
|
@ -77,24 +78,25 @@ class ShoulderJoint(Model):
|
||||||
self.parent_lip_ext,
|
self.parent_lip_ext,
|
||||||
self.parent_lip_width,
|
self.parent_lip_width,
|
||||||
self.parent_lip_guard_height)
|
self.parent_lip_guard_height)
|
||||||
.located(Cq.Location((0, -self.parent_lip_width/2 , dz)))
|
.located(Cq.Location((0, -self.parent_lip_width/2 , 0)))
|
||||||
.cut(Cq.Solid.makeCylinder(joint.radius_track, self.parent_lip_guard_height + dz))
|
.cut(Cq.Solid.makeCylinder(joint.radius_track, self.parent_lip_guard_height))
|
||||||
)
|
)
|
||||||
lip = box_with_centre_holes(
|
lip = MountingBox(
|
||||||
length=self.parent_lip_length - dz,
|
length=self.parent_lip_length,
|
||||||
width=self.parent_lip_width,
|
width=self.parent_lip_width,
|
||||||
height=self.parent_lip_thickness,
|
thickness=self.parent_lip_thickness,
|
||||||
hole_loc=[
|
holes=[
|
||||||
self.height / 2 - dz - x
|
Hole(x=self.height / 2 - x, y=y)
|
||||||
for x in self.parent_conn_hole_pos
|
for x, y in self.parent_conn_hole_pos
|
||||||
],
|
],
|
||||||
hole_diam=self.parent_conn_hole_diam,
|
hole_diam=self.parent_conn_hole_diam,
|
||||||
|
generate_side_tags=False,
|
||||||
)
|
)
|
||||||
# Flip so the lip's holes point to -X
|
# Flip so the lip's holes point to -X
|
||||||
loc_axis = Cq.Location((0,0,0), (0, 1, 0), -90)
|
loc_axis = Cq.Location((0,0,0), (0, 1, 0), -90)
|
||||||
# so they point to +X
|
# so they point to +X
|
||||||
loc_dir = Cq.Location((0,0,0), (0, 0, 1), 180)
|
loc_dir = Cq.Location((0,0,0), (0, 0, 1), 180)
|
||||||
loc_pos = Cq.Location((self.parent_lip_ext - self.parent_lip_thickness, 0, dz))
|
loc_pos = Cq.Location((self.parent_lip_ext - self.parent_lip_thickness, 0, 0))
|
||||||
|
|
||||||
rot = -self.axis_rotate_top if top else self.axis_rotate_bot
|
rot = -self.axis_rotate_top if top else self.axis_rotate_bot
|
||||||
|
|
||||||
|
@ -103,7 +105,7 @@ class ShoulderJoint(Model):
|
||||||
.add(joint.track(), name="track",
|
.add(joint.track(), name="track",
|
||||||
loc=Cq.Location((0, 0, 0), (0, 0, 1), rot))
|
loc=Cq.Location((0, 0, 0), (0, 0, 1), rot))
|
||||||
.add(lip_guard, name="lip_guard")
|
.add(lip_guard, name="lip_guard")
|
||||||
.add(lip, name="lip", loc=loc_pos * loc_dir * loc_axis)
|
.add(lip.generate(), name="lip", loc=loc_pos * loc_dir * loc_axis)
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ class WingProfile(Model):
|
||||||
|
|
||||||
base_joint: HirthJoint = field(default_factory=lambda: HirthJoint(
|
base_joint: HirthJoint = field(default_factory=lambda: HirthJoint(
|
||||||
radius=25.0,
|
radius=25.0,
|
||||||
radius_inner=20.0,
|
radius_inner=15.0,
|
||||||
tooth_height=7.0,
|
tooth_height=7.0,
|
||||||
base_height=5,
|
base_height=5,
|
||||||
n_tooth=24,
|
n_tooth=24,
|
||||||
|
@ -38,7 +38,7 @@ class WingProfile(Model):
|
||||||
|
|
||||||
shoulder_joint: ShoulderJoint = field(default_factory=lambda: ShoulderJoint(
|
shoulder_joint: ShoulderJoint = field(default_factory=lambda: ShoulderJoint(
|
||||||
))
|
))
|
||||||
shoulder_width: float = 30.0
|
shoulder_width: float = 36.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_x: float = -105.0
|
||||||
|
@ -160,26 +160,42 @@ class WingProfile(Model):
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def outer_profile_s0(self) -> Cq.Sketch:
|
||||||
|
"""
|
||||||
|
The outer boundary of s0, used to produce the curved panel and the
|
||||||
|
top/bottom slots
|
||||||
|
"""
|
||||||
|
tip_x = self.shoulder_tip_x
|
||||||
|
tip_y = self.shoulder_tip_y
|
||||||
|
return (
|
||||||
|
Cq.Sketch()
|
||||||
|
.spline([
|
||||||
|
(0, 0),
|
||||||
|
(-30.0, 80.0),
|
||||||
|
(tip_x, tip_y)
|
||||||
|
])
|
||||||
|
#.segment(
|
||||||
|
# (tip_x, tip_y),
|
||||||
|
# (tip_x - 10, tip_y),
|
||||||
|
#)
|
||||||
|
)
|
||||||
|
|
||||||
@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_x = self.shoulder_mid_x
|
||||||
mid_y = self.shoulder_mid_y
|
mid_y = self.shoulder_mid_y
|
||||||
|
sw = self.shoulder_width
|
||||||
sketch = (
|
sketch = (
|
||||||
Cq.Sketch()
|
self.outer_profile_s0()
|
||||||
.segment((-self.base_width, 0), (0, 0))
|
.segment((-self.base_width, 0), (0, 0))
|
||||||
.spline([
|
|
||||||
(0, 0),
|
|
||||||
(-30.0, 80.0),
|
|
||||||
(tip_x, tip_y)
|
|
||||||
])
|
|
||||||
.segment(
|
.segment(
|
||||||
(tip_x, tip_y),
|
(tip_x, tip_y),
|
||||||
(tip_x, tip_y - self.shoulder_width),
|
(tip_x, tip_y - sw),
|
||||||
)
|
)
|
||||||
.segment(
|
.segment(
|
||||||
(tip_x, tip_y - self.shoulder_width),
|
(tip_x, tip_y - sw),
|
||||||
(mid_x, mid_y),
|
(mid_x, mid_y),
|
||||||
)
|
)
|
||||||
.segment(
|
.segment(
|
||||||
|
@ -191,21 +207,12 @@ class WingProfile(Model):
|
||||||
return sketch
|
return sketch
|
||||||
|
|
||||||
def outer_shell_s0(self) -> Cq.Workplane:
|
def outer_shell_s0(self) -> Cq.Workplane:
|
||||||
tip_x = self.shoulder_tip_x
|
|
||||||
tip_y = self.shoulder_tip_y
|
|
||||||
t = self.panel_thickness
|
t = self.panel_thickness
|
||||||
edge = Cq.Edge.makeSpline([
|
profile = Cq.Wire.assembleEdges(self.outer_profile_s0().edges().vals())
|
||||||
Cq.Vector(x, y, 0)
|
|
||||||
for x,y in [
|
|
||||||
(0, 0),
|
|
||||||
(-30.0, 80.0),
|
|
||||||
(tip_x, tip_y)
|
|
||||||
]
|
|
||||||
])
|
|
||||||
result = (
|
result = (
|
||||||
Cq.Workplane('XZ')
|
Cq.Workplane('XZ')
|
||||||
.rect(t, self.root_height + t*2, centered=(False, False))
|
.rect(t, self.root_height + t*2, centered=(False, False))
|
||||||
.sweep(edge)
|
.sweep(profile)
|
||||||
)
|
)
|
||||||
plane = result.copyWorkplane(Cq.Workplane('XZ'))
|
plane = result.copyWorkplane(Cq.Workplane('XZ'))
|
||||||
plane.moveTo(0, 0).tagPlane("bot")
|
plane.moveTo(0, 0).tagPlane("bot")
|
||||||
|
@ -219,15 +226,15 @@ class WingProfile(Model):
|
||||||
"""
|
"""
|
||||||
holes = [
|
holes = [
|
||||||
hole
|
hole
|
||||||
for i, x in enumerate(self.shoulder_joint.parent_conn_hole_pos)
|
for i, (x, y) in enumerate(self.shoulder_joint.parent_conn_hole_pos)
|
||||||
for hole in [
|
for hole in [
|
||||||
Hole(x=x, tag=f"conn_top{i}"),
|
Hole(x=x, y=y, tag=f"conn_top{i}"),
|
||||||
Hole(x=-x, tag=f"conn_bot{i}"),
|
Hole(x=-x, y=y, tag=f"conn_bot{i}"),
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
return MountingBox(
|
return MountingBox(
|
||||||
length=self.shoulder_joint.height,
|
length=self.shoulder_joint.height,
|
||||||
width=self.shoulder_width,
|
width=self.shoulder_joint.parent_lip_width,
|
||||||
thickness=self.spacer_thickness,
|
thickness=self.spacer_thickness,
|
||||||
holes=holes,
|
holes=holes,
|
||||||
hole_diam=self.shoulder_joint.parent_conn_hole_diam,
|
hole_diam=self.shoulder_joint.parent_conn_hole_diam,
|
||||||
|
@ -263,8 +270,10 @@ 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_dx = -(self.base_width - self.base_plate_width) / 2
|
||||||
base_dy = self.base_joint.joint_height
|
base_dy = self.base_joint.joint_height
|
||||||
|
|
||||||
|
axle_dist = self.shoulder_joint.parent_lip_ext
|
||||||
tags = [
|
tags = [
|
||||||
("shoulder", (self.shoulder_tip_x, self.shoulder_tip_y - self.shoulder_width), 0),
|
("shoulder", (self.shoulder_tip_x + axle_dist, self.shoulder_tip_y - self.shoulder_width), 0),
|
||||||
("base", (base_dx, base_dy), 90),
|
("base", (base_dx, base_dy), 90),
|
||||||
]
|
]
|
||||||
result = nhf.utils.extrude_with_markers(
|
result = nhf.utils.extrude_with_markers(
|
||||||
|
@ -284,9 +293,12 @@ class WingProfile(Model):
|
||||||
.addS(self.surface_s0(top=True), name="bot",
|
.addS(self.surface_s0(top=True), name="bot",
|
||||||
material=self.mat_panel, role=self.role_panel)
|
material=self.mat_panel, role=self.role_panel)
|
||||||
.addS(self.surface_s0(top=False), name="top",
|
.addS(self.surface_s0(top=False), name="top",
|
||||||
material=self.mat_panel, role=self.role_panel)
|
material=self.mat_panel, role=self.role_panel,
|
||||||
.constrain("bot@faces@>Z", "top@faces@<Z", "Point",
|
loc=Cq.Location((0, 0, self.root_height + self.panel_thickness)))
|
||||||
param=self.shoulder_joint.height)
|
.constrain("bot", "Fixed")
|
||||||
|
.constrain("top", "Fixed")
|
||||||
|
#.constrain("bot@faces@>Z", "top@faces@<Z", "Point",
|
||||||
|
# param=self.shoulder_joint.height)
|
||||||
.addS(self.outer_shell_s0(), name="outer_shell",
|
.addS(self.outer_shell_s0(), name="outer_shell",
|
||||||
material=self.mat_panel, role=self.role_panel)
|
material=self.mat_panel, role=self.role_panel)
|
||||||
.constrain("bot?corner", "outer_shell?bot", "Plane", param=0)
|
.constrain("bot?corner", "outer_shell?bot", "Plane", param=0)
|
||||||
|
@ -647,7 +659,9 @@ class WingProfile(Model):
|
||||||
(
|
(
|
||||||
result
|
result
|
||||||
.constrain(f"s0/shoulder?conn_top0", f"shoulder/parent_{tag_top}/lip?conn0", "Plane")
|
.constrain(f"s0/shoulder?conn_top0", f"shoulder/parent_{tag_top}/lip?conn0", "Plane")
|
||||||
|
.constrain(f"s0/shoulder?conn_top1", f"shoulder/parent_{tag_top}/lip?conn1", "Plane")
|
||||||
.constrain(f"s0/shoulder?conn_bot0", f"shoulder/parent_{tag_bot}/lip?conn0", "Plane")
|
.constrain(f"s0/shoulder?conn_bot0", f"shoulder/parent_{tag_bot}/lip?conn0", "Plane")
|
||||||
|
.constrain(f"s0/shoulder?conn_bot1", f"shoulder/parent_{tag_bot}/lip?conn1", "Plane")
|
||||||
)
|
)
|
||||||
if "s1" in parts:
|
if "s1" in parts:
|
||||||
result.add(self.assembly_s1(), name="s1")
|
result.add(self.assembly_s1(), name="s1")
|
||||||
|
|
Loading…
Reference in New Issue