cosplay: Touhou/Houjuu Nue #4
|
@ -25,7 +25,7 @@ class Harness(Model):
|
|||
|
||||
hs_hirth_joint: HirthJoint = field(default_factory=lambda: HirthJoint(
|
||||
radius=25.0,
|
||||
radius_inner=20.0,
|
||||
radius_inner=15.0,
|
||||
tooth_height=7.0,
|
||||
base_height=5.0,
|
||||
n_tooth=24,
|
||||
|
|
|
@ -6,7 +6,7 @@ from nhf import Material, Role
|
|||
from nhf.build import Model, target, assembly
|
||||
from nhf.parts.springs import TorsionSpring
|
||||
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
|
||||
|
||||
TOL = 1e-6
|
||||
|
@ -37,16 +37,18 @@ class ShoulderJoint(Model):
|
|||
|
||||
# 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
|
||||
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_width: float = 30.0
|
||||
parent_lip_thickness: float = 5.0
|
||||
parent_lip_ext: float = 40.0
|
||||
parent_lip_guard_height: float = 8.0
|
||||
parent_root_wall_thickness: float = 25.4 / 16
|
||||
|
||||
# Measured from centre of axle
|
||||
child_lip_length: float = 45.0
|
||||
|
@ -68,7 +70,6 @@ class ShoulderJoint(Model):
|
|||
def parent(self, top: bool = False) -> Cq.Assembly:
|
||||
joint = self.torsion_joint
|
||||
# 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_ext > joint.radius_track
|
||||
|
||||
|
@ -77,24 +78,25 @@ class ShoulderJoint(Model):
|
|||
self.parent_lip_ext,
|
||||
self.parent_lip_width,
|
||||
self.parent_lip_guard_height)
|
||||
.located(Cq.Location((0, -self.parent_lip_width/2 , dz)))
|
||||
.cut(Cq.Solid.makeCylinder(joint.radius_track, self.parent_lip_guard_height + dz))
|
||||
.located(Cq.Location((0, -self.parent_lip_width/2 , 0)))
|
||||
.cut(Cq.Solid.makeCylinder(joint.radius_track, self.parent_lip_guard_height))
|
||||
)
|
||||
lip = box_with_centre_holes(
|
||||
length=self.parent_lip_length - dz,
|
||||
lip = MountingBox(
|
||||
length=self.parent_lip_length,
|
||||
width=self.parent_lip_width,
|
||||
height=self.parent_lip_thickness,
|
||||
hole_loc=[
|
||||
self.height / 2 - dz - x
|
||||
for x in self.parent_conn_hole_pos
|
||||
thickness=self.parent_lip_thickness,
|
||||
holes=[
|
||||
Hole(x=self.height / 2 - x, y=y)
|
||||
for x, y in self.parent_conn_hole_pos
|
||||
],
|
||||
hole_diam=self.parent_conn_hole_diam,
|
||||
generate_side_tags=False,
|
||||
)
|
||||
# Flip so the lip's holes point to -X
|
||||
loc_axis = Cq.Location((0,0,0), (0, 1, 0), -90)
|
||||
# so they point to +X
|
||||
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
|
||||
|
||||
|
@ -103,7 +105,7 @@ class ShoulderJoint(Model):
|
|||
.add(joint.track(), name="track",
|
||||
loc=Cq.Location((0, 0, 0), (0, 0, 1), rot))
|
||||
.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
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ class WingProfile(Model):
|
|||
|
||||
base_joint: HirthJoint = field(default_factory=lambda: HirthJoint(
|
||||
radius=25.0,
|
||||
radius_inner=20.0,
|
||||
radius_inner=15.0,
|
||||
tooth_height=7.0,
|
||||
base_height=5,
|
||||
n_tooth=24,
|
||||
|
@ -38,7 +38,7 @@ class WingProfile(Model):
|
|||
|
||||
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_y: float = 160.0
|
||||
shoulder_mid_x: float = -105.0
|
||||
|
@ -160,26 +160,42 @@ class WingProfile(Model):
|
|||
)
|
||||
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)
|
||||
def profile_s0(self) -> Cq.Sketch:
|
||||
tip_x = self.shoulder_tip_x
|
||||
tip_y = self.shoulder_tip_y
|
||||
mid_x = self.shoulder_mid_x
|
||||
mid_y = self.shoulder_mid_y
|
||||
sw = self.shoulder_width
|
||||
sketch = (
|
||||
Cq.Sketch()
|
||||
self.outer_profile_s0()
|
||||
.segment((-self.base_width, 0), (0, 0))
|
||||
.spline([
|
||||
(0, 0),
|
||||
(-30.0, 80.0),
|
||||
(tip_x, tip_y)
|
||||
])
|
||||
.segment(
|
||||
(tip_x, tip_y),
|
||||
(tip_x, tip_y - self.shoulder_width),
|
||||
(tip_x, tip_y - sw),
|
||||
)
|
||||
.segment(
|
||||
(tip_x, tip_y - self.shoulder_width),
|
||||
(tip_x, tip_y - sw),
|
||||
(mid_x, mid_y),
|
||||
)
|
||||
.segment(
|
||||
|
@ -191,21 +207,12 @@ class WingProfile(Model):
|
|||
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)
|
||||
]
|
||||
])
|
||||
profile = Cq.Wire.assembleEdges(self.outer_profile_s0().edges().vals())
|
||||
result = (
|
||||
Cq.Workplane('XZ')
|
||||
.rect(t, self.root_height + t*2, centered=(False, False))
|
||||
.sweep(edge)
|
||||
.sweep(profile)
|
||||
)
|
||||
plane = result.copyWorkplane(Cq.Workplane('XZ'))
|
||||
plane.moveTo(0, 0).tagPlane("bot")
|
||||
|
@ -219,15 +226,15 @@ class WingProfile(Model):
|
|||
"""
|
||||
holes = [
|
||||
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 [
|
||||
Hole(x=x, tag=f"conn_top{i}"),
|
||||
Hole(x=-x, tag=f"conn_bot{i}"),
|
||||
Hole(x=x, y=y, tag=f"conn_top{i}"),
|
||||
Hole(x=-x, y=y, tag=f"conn_bot{i}"),
|
||||
]
|
||||
]
|
||||
return MountingBox(
|
||||
length=self.shoulder_joint.height,
|
||||
width=self.shoulder_width,
|
||||
width=self.shoulder_joint.parent_lip_width,
|
||||
thickness=self.spacer_thickness,
|
||||
holes=holes,
|
||||
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:
|
||||
base_dx = -(self.base_width - self.base_plate_width) / 2
|
||||
base_dy = self.base_joint.joint_height
|
||||
|
||||
axle_dist = self.shoulder_joint.parent_lip_ext
|
||||
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),
|
||||
]
|
||||
result = nhf.utils.extrude_with_markers(
|
||||
|
@ -284,9 +293,12 @@ class WingProfile(Model):
|
|||
.addS(self.surface_s0(top=True), name="bot",
|
||||
material=self.mat_panel, role=self.role_panel)
|
||||
.addS(self.surface_s0(top=False), name="top",
|
||||
material=self.mat_panel, role=self.role_panel)
|
||||
.constrain("bot@faces@>Z", "top@faces@<Z", "Point",
|
||||
param=self.shoulder_joint.height)
|
||||
material=self.mat_panel, role=self.role_panel,
|
||||
loc=Cq.Location((0, 0, self.root_height + self.panel_thickness)))
|
||||
.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",
|
||||
material=self.mat_panel, role=self.role_panel)
|
||||
.constrain("bot?corner", "outer_shell?bot", "Plane", param=0)
|
||||
|
@ -647,7 +659,9 @@ class WingProfile(Model):
|
|||
(
|
||||
result
|
||||
.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_bot1", f"shoulder/parent_{tag_bot}/lip?conn1", "Plane")
|
||||
)
|
||||
if "s1" in parts:
|
||||
result.add(self.assembly_s1(), name="s1")
|
||||
|
|
Loading…
Reference in New Issue