cosplay: Touhou/Houjuu Nue #4
|
@ -122,5 +122,5 @@ class HexNut(Item):
|
|||
)
|
||||
result.faces("<Z").tag("bot")
|
||||
result.faces(">Z").tag("top")
|
||||
result.copyWorkplane(Cq.Workplane('XY')).tagPlane("dir", direction="+X")
|
||||
result.copyWorkplane(Cq.Workplane('XY')).tagPlane("dirX", direction="+X")
|
||||
return result
|
||||
|
|
|
@ -77,22 +77,15 @@ class Parameters(Model):
|
|||
|
||||
def __post_init__(self):
|
||||
super().__init__(name="houjuu-nue")
|
||||
self.wing_r1.base_joint = self.harness.hs_hirth_joint
|
||||
self.wing_r2.base_joint = self.harness.hs_hirth_joint
|
||||
self.wing_r3.base_joint = self.harness.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
|
||||
self.wing_r1.root_joint = self.harness.root_joint
|
||||
self.wing_r2.root_joint = self.harness.root_joint
|
||||
self.wing_r3.root_joint = self.harness.root_joint
|
||||
self.wing_l1.root_joint = self.harness.root_joint
|
||||
self.wing_l2.root_joint = self.harness.root_joint
|
||||
self.wing_l3.root_joint = self.harness.root_joint
|
||||
|
||||
self.wing_r1.shoulder_joint.torsion_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")
|
||||
def submodel_harness(self) -> Model:
|
||||
return self.harness
|
||||
|
@ -126,19 +119,20 @@ class Parameters(Model):
|
|||
result = (
|
||||
Cq.Assembly()
|
||||
.add(self.harness.assembly(), name="harness", loc=Cq.Location((0, 0, 0)))
|
||||
.add(self.wing_r1.assembly(parts, **kwargs), name="wing_r1")
|
||||
.add(self.wing_r2.assembly(parts, **kwargs), name="wing_r2")
|
||||
.add(self.wing_r3.assembly(parts, **kwargs), name="wing_r3")
|
||||
.add(self.wing_l1.assembly(parts, **kwargs), name="wing_l1")
|
||||
.add(self.wing_l2.assembly(parts, **kwargs), name="wing_l2")
|
||||
.add(self.wing_l3.assembly(parts, **kwargs), name="wing_l3")
|
||||
.add(self.wing_r1.assembly(parts, root_offset=9, **kwargs), name="wing_r1")
|
||||
.add(self.wing_r2.assembly(parts, root_offset=7, **kwargs), name="wing_r2")
|
||||
.add(self.wing_r3.assembly(parts, root_offset=6, **kwargs), name="wing_r3")
|
||||
.add(self.wing_l1.assembly(parts, root_offset=7, **kwargs), name="wing_l1")
|
||||
.add(self.wing_l2.assembly(parts, root_offset=8, **kwargs), name="wing_l2")
|
||||
.add(self.wing_l3.assembly(parts, root_offset=9, **kwargs), name="wing_l3")
|
||||
)
|
||||
for tag, offset in [("r1", 9), ("r2", 7), ("r3", 6), ("l1", 7), ("l2", 8), ("l3", 9)]:
|
||||
self.harness.hs_hirth_joint.add_constraints(
|
||||
for tag in ["r1", "r2", "r3", "l1", "l2", "l3"]:
|
||||
self.harness.add_root_joint_constraint(
|
||||
result,
|
||||
f"harness/{tag}",
|
||||
f"wing_{tag}/s0/hs",
|
||||
offset=offset)
|
||||
"harness/base",
|
||||
f"wing_{tag}/root",
|
||||
tag
|
||||
)
|
||||
return result.solve()
|
||||
|
||||
@submodel(name="trident")
|
||||
|
|
|
@ -2,7 +2,8 @@ from dataclasses import dataclass, field
|
|||
import cadquery as Cq
|
||||
from nhf.parts.joints import HirthJoint
|
||||
from nhf import Material, Role
|
||||
from nhf.build import Model, TargetKind, target, assembly
|
||||
from nhf.build import Model, TargetKind, target, assembly, submodel
|
||||
from nhf.touhou.houjuu_nue.joints import RootJoint
|
||||
import nhf.utils
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
|
@ -63,33 +64,18 @@ class Harness(Model):
|
|||
("r3", BASE_POS_X, -BASE_POS_Y),
|
||||
("l3", -BASE_POS_X, -BASE_POS_Y),
|
||||
])
|
||||
# Holes drilled onto harness for attachment with HS joint
|
||||
harness_to_root_conn_diam: float = 6
|
||||
|
||||
hs_hirth_joint: HirthJoint = field(default_factory=lambda: HirthJoint(
|
||||
radius=25.0,
|
||||
radius_inner=15.0,
|
||||
tooth_height=7.0,
|
||||
base_height=5.0,
|
||||
n_tooth=24,
|
||||
))
|
||||
|
||||
hs_joint_base_width: float = 85
|
||||
hs_joint_base_thickness: float = 10
|
||||
hs_joint_corner_fillet: float = 5
|
||||
hs_joint_corner_cbore_diam: float = 12
|
||||
hs_joint_corner_cbore_depth: float = 2
|
||||
hs_joint_corner_inset: float = 12
|
||||
|
||||
hs_joint_axis_diam: float = 12.0
|
||||
hs_joint_axis_cbore_diam: float = 20
|
||||
hs_joint_axis_cbore_depth: float = 3
|
||||
root_joint: RootJoint = field(default_factory=lambda: RootJoint())
|
||||
|
||||
mannequin: Mannequin = Mannequin()
|
||||
|
||||
def __post_init__(self):
|
||||
super().__init__(name="harness")
|
||||
|
||||
@submodel(name="root-joint")
|
||||
def submodel_root_joint(self) -> Model:
|
||||
return self.root_joint
|
||||
|
||||
@target(name="profile", kind=TargetKind.DXF)
|
||||
def profile(self) -> Cq.Sketch:
|
||||
"""
|
||||
|
@ -115,12 +101,12 @@ class Harness(Model):
|
|||
.fillet(self.fillet)
|
||||
)
|
||||
for tag, x, y in self.wing_base_pos:
|
||||
conn = [(px + x, py + y) for px, py in self.hs_joint_harness_conn()]
|
||||
conn = [(px + x, py + y) for px, py in self.root_joint.corner_pos()]
|
||||
sketch = (
|
||||
sketch
|
||||
.push(conn)
|
||||
.tag(tag)
|
||||
.circle(self.harness_to_root_conn_diam / 2, mode='s')
|
||||
.circle(self.root_joint.corner_hole_diam / 2, mode='s')
|
||||
.reset()
|
||||
)
|
||||
return sketch
|
||||
|
@ -138,78 +124,27 @@ class Harness(Model):
|
|||
plane = result.faces(">Y").workplane()
|
||||
for tag, x, y in self.wing_base_pos:
|
||||
conn = [(px + x, py + y) for px, py
|
||||
in self.hs_joint_harness_conn()]
|
||||
in self.root_joint.corner_pos()]
|
||||
for i, (px, py) in enumerate(conn):
|
||||
plane.moveTo(px, py).tagPoint(f"{tag}_{i}")
|
||||
return result
|
||||
|
||||
def hs_joint_harness_conn(self) -> list[tuple[int, int]]:
|
||||
"""
|
||||
Generates a set of points corresponding to the connectorss
|
||||
"""
|
||||
dx = self.hs_joint_base_width / 2 - self.hs_joint_corner_inset
|
||||
return [
|
||||
(dx, dx),
|
||||
(dx, -dx),
|
||||
(-dx, -dx),
|
||||
(-dx, dx),
|
||||
]
|
||||
def add_root_joint_constraint(
|
||||
self,
|
||||
a: Cq.Assembly,
|
||||
harness_tag: str,
|
||||
joint_tag: str,
|
||||
mount_tag: str):
|
||||
for i in range(4):
|
||||
a.constrain(f"{harness_tag}?{mount_tag}_{i}", f"{joint_tag}/parent?h{i}", "Point")
|
||||
|
||||
@target(name="hs-joint-parent")
|
||||
def hs_joint_parent(self):
|
||||
"""
|
||||
Parent part of the Houjuu-Scarlett joint, which is composed of a Hirth
|
||||
coupling, a cylindrical base, and a mounting base.
|
||||
"""
|
||||
hirth = self.hs_hirth_joint.generate()
|
||||
conn = self.hs_joint_harness_conn()
|
||||
result = (
|
||||
Cq.Workplane('XY')
|
||||
.box(
|
||||
self.hs_joint_base_width,
|
||||
self.hs_joint_base_width,
|
||||
self.hs_joint_base_thickness,
|
||||
centered=(True, True, False))
|
||||
.translate((0, 0, -self.hs_joint_base_thickness))
|
||||
.edges("|Z")
|
||||
.fillet(self.hs_joint_corner_fillet)
|
||||
.faces(">Z")
|
||||
.workplane()
|
||||
.pushPoints(conn)
|
||||
.cboreHole(
|
||||
diameter=self.harness_to_root_conn_diam,
|
||||
cboreDiameter=self.hs_joint_corner_cbore_diam,
|
||||
cboreDepth=self.hs_joint_corner_cbore_depth)
|
||||
)
|
||||
# Creates a plane parallel to the holes but shifted to the base
|
||||
plane = result.faces(">Z").workplane(offset=-self.hs_joint_base_thickness)
|
||||
|
||||
for i, (px, py) in enumerate(conn):
|
||||
plane.moveTo(px, py).tagPoint(f"h{i}")
|
||||
result = (
|
||||
result
|
||||
.faces(">Z")
|
||||
.workplane()
|
||||
.union(hirth, tol=0.1)
|
||||
.clean()
|
||||
)
|
||||
result = (
|
||||
result.faces("<Z")
|
||||
.workplane()
|
||||
.cboreHole(
|
||||
diameter=self.hs_joint_axis_diam,
|
||||
cboreDiameter=self.hs_joint_axis_cbore_diam,
|
||||
cboreDepth=self.hs_joint_axis_cbore_depth,
|
||||
)
|
||||
.clean()
|
||||
)
|
||||
result.faces("<Z").tag("base")
|
||||
return result
|
||||
|
||||
@assembly()
|
||||
def assembly(self) -> Cq.Assembly:
|
||||
def assembly(self, with_root_joint: bool = False) -> Cq.Assembly:
|
||||
harness = self.surface()
|
||||
mannequin_z = self.mannequin.shoulder_to_waist * 0.6
|
||||
|
||||
|
||||
result = (
|
||||
Cq.Assembly()
|
||||
.addS(
|
||||
|
@ -224,13 +159,12 @@ class Harness(Model):
|
|||
loc=Cq.Location((0, -self.thickness, -mannequin_z), (0, 0, 1), 180))
|
||||
.constrain("mannequin", "Fixed")
|
||||
)
|
||||
if with_root_joint:
|
||||
for name in ["l1", "l2", "l3", "r1", "r2", "r3"]:
|
||||
j = self.hs_joint_parent()
|
||||
result.addS(
|
||||
j, name=name,
|
||||
self.root_joint.assembly(), name=name,
|
||||
role=Role.PARENT,
|
||||
material=Material.PLASTIC_PLA)
|
||||
for i in range(4):
|
||||
result.constrain(f"base?{name}_{i}", f"{name}?h{i}", "Point")
|
||||
self.add_root_joint_constraint(result, "base", name, name)
|
||||
result.solve()
|
||||
return result
|
||||
|
|
|
@ -5,13 +5,204 @@ import cadquery as Cq
|
|||
from nhf import Material, Role
|
||||
from nhf.build import Model, target, assembly
|
||||
from nhf.parts.springs import TorsionSpring
|
||||
from nhf.parts.fasteners import FlatHeadBolt
|
||||
from nhf.parts.joints import TorsionJoint
|
||||
from nhf.parts.fasteners import FlatHeadBolt, HexNut, ThreaddedKnob
|
||||
from nhf.parts.joints import TorsionJoint, HirthJoint
|
||||
from nhf.parts.box import Hole, MountingBox, box_with_centre_holes
|
||||
import nhf.utils
|
||||
|
||||
TOL = 1e-6
|
||||
|
||||
@dataclass
|
||||
class RootJoint(Model):
|
||||
"""
|
||||
The Houjuu-Scarlett Mechanism
|
||||
"""
|
||||
knob: ThreaddedKnob = ThreaddedKnob(
|
||||
mass=float('nan'),
|
||||
diam_thread=12.0,
|
||||
height_thread=30.0,
|
||||
diam_knob=50.0,
|
||||
# FIXME: Undetermined
|
||||
diam_neck=30.0,
|
||||
height_neck=10.0,
|
||||
height_knob=10.0,
|
||||
)
|
||||
hex_nut: HexNut = HexNut(
|
||||
# FIXME: Undetermined
|
||||
mass=float('nan'),
|
||||
|
||||
diam_thread=12.0,
|
||||
pitch=1.75,
|
||||
thickness=9.8,
|
||||
width=18.9,
|
||||
)
|
||||
hirth_joint: HirthJoint = field(default_factory=lambda: HirthJoint(
|
||||
radius=25.0,
|
||||
radius_inner=15.0,
|
||||
tooth_height=7.0,
|
||||
base_height=5.0,
|
||||
n_tooth=24,
|
||||
))
|
||||
parent_width: float = 85
|
||||
parent_thickness: float = 10
|
||||
parent_corner_fillet: float = 5
|
||||
parent_corner_cbore_diam: float = 12
|
||||
parent_corner_cbore_depth: float = 2
|
||||
parent_corner_inset: float = 12
|
||||
parent_mount_thickness: float = 25.4 / 16
|
||||
|
||||
child_corner_dx: float = 17.0
|
||||
child_corner_dz: float = 24.0
|
||||
|
||||
axis_diam: float = 12.0
|
||||
axis_cbore_diam: float = 20
|
||||
axis_cbore_depth: float = 3
|
||||
corner_hole_diam: float = 6.0
|
||||
|
||||
child_height: float = 60.0
|
||||
child_width: float = 50.0
|
||||
child_mount_thickness: float = 25.4 / 8
|
||||
|
||||
def corner_pos(self) -> list[tuple[int, int]]:
|
||||
"""
|
||||
Generates a set of points corresponding to the connectorss
|
||||
"""
|
||||
dx = self.parent_width / 2 - self.parent_corner_inset
|
||||
return [
|
||||
(dx, dx),
|
||||
(dx, -dx),
|
||||
(-dx, -dx),
|
||||
(-dx, dx),
|
||||
]
|
||||
|
||||
@property
|
||||
def total_height(self) -> float:
|
||||
return self.parent_thickness + self.hirth_joint.total_height
|
||||
|
||||
@target(name="parent")
|
||||
def parent(self):
|
||||
"""
|
||||
Parent part of the Houjuu-Scarlett joint, which is composed of a Hirth
|
||||
coupling, a cylindrical base, and a mounting base.
|
||||
"""
|
||||
hirth = self.hirth_joint.generate()
|
||||
conn = self.corner_pos()
|
||||
result = (
|
||||
Cq.Workplane('XY')
|
||||
.box(
|
||||
self.parent_width,
|
||||
self.parent_width,
|
||||
self.parent_thickness,
|
||||
centered=(True, True, False))
|
||||
.translate((0, 0, -self.parent_thickness))
|
||||
.edges("|Z")
|
||||
.fillet(self.parent_corner_fillet)
|
||||
.faces(">Z")
|
||||
.workplane()
|
||||
.pushPoints(conn)
|
||||
.cboreHole(
|
||||
diameter=self.corner_hole_diam,
|
||||
cboreDiameter=self.parent_corner_cbore_diam,
|
||||
cboreDepth=self.parent_corner_cbore_depth)
|
||||
)
|
||||
# Creates a plane parallel to the holes but shifted to the base
|
||||
plane = result.faces(">Z").workplane(offset=-self.parent_thickness)
|
||||
|
||||
for i, (px, py) in enumerate(conn):
|
||||
plane.moveTo(px, py).tagPoint(f"h{i}")
|
||||
result = (
|
||||
result
|
||||
.faces(">Z")
|
||||
.workplane()
|
||||
.union(hirth, tol=0.1)
|
||||
.clean()
|
||||
)
|
||||
result = (
|
||||
result.faces("<Z")
|
||||
.workplane()
|
||||
.hole(diameter=self.axis_diam)
|
||||
.cut(self.hex_nut.generate().translate((0, 0, -self.parent_thickness)))
|
||||
)
|
||||
result.faces("<Z").tag("base")
|
||||
return result
|
||||
|
||||
@target(name="child")
|
||||
def child(self) -> Cq.Workplane:
|
||||
hirth = self.hirth_joint.generate(is_mated=True)
|
||||
dy = self.child_corner_dx
|
||||
dx = self.child_corner_dz
|
||||
conn = [
|
||||
(-dx, -dy),
|
||||
(dx, -dy),
|
||||
(dx, dy),
|
||||
(-dx, dy),
|
||||
]
|
||||
result = (
|
||||
Cq.Workplane('XY')
|
||||
.box(
|
||||
self.child_height,
|
||||
self.child_width,
|
||||
self.hirth_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.corner_hole_diam)
|
||||
)
|
||||
# Creates a plane parallel to the holes but shifted to the base
|
||||
plane = result.faces(">Z").workplane(offset=-self.hirth_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.axis_diam)
|
||||
)
|
||||
return result
|
||||
|
||||
def assembly(self,
|
||||
offset: int = 0,
|
||||
fastener_pos: float = 0) -> Cq.Assembly:
|
||||
"""
|
||||
Specify knob position to determine the position of the knob from fully
|
||||
inserted (0) or fully uninserted (1)
|
||||
"""
|
||||
knob_h = self.total_height + self.child_mount_thickness
|
||||
result = (
|
||||
Cq.Assembly()
|
||||
.addS(self.parent(), name="parent",
|
||||
material=Material.PLASTIC_PLA,
|
||||
role=Role.PARENT)
|
||||
.constrain("parent", "Fixed")
|
||||
.addS(self.child(), name="child",
|
||||
material=Material.PLASTIC_PLA,
|
||||
role=Role.CHILD)
|
||||
.addS(self.hex_nut.assembly(), name="hex_nut")
|
||||
.addS(self.knob.assembly(), name="knob",
|
||||
loc=Cq.Location((0, 0, knob_h * fastener_pos)))
|
||||
.constrain("knob/thread", "Fixed")
|
||||
.constrain("hex_nut?bot", "parent?base", "Plane", param=0)
|
||||
.constrain("hex_nut?dirX", "parent@faces@>X", "Axis", param=0)
|
||||
)
|
||||
self.hirth_joint.add_constraints(
|
||||
result,
|
||||
"parent",
|
||||
"child",
|
||||
offset=offset
|
||||
)
|
||||
return result.solve()
|
||||
|
||||
@dataclass
|
||||
class ShoulderJoint(Model):
|
||||
|
||||
|
@ -228,10 +419,11 @@ class ShoulderJoint(Model):
|
|||
return result
|
||||
|
||||
@assembly()
|
||||
def assembly(self, deflection: float = 0) -> Cq.Assembly:
|
||||
def assembly(self, fastener_pos: float = 0.0, deflection: float = 0) -> Cq.Assembly:
|
||||
directrix = self.directrix_id
|
||||
mat = Material.RESIN_TRANSPERENT
|
||||
mat_spring = Material.STEEL_SPRING
|
||||
bolt_z = self.height / 2 + self.bolt.height_thread * (fastener_pos - 1)
|
||||
result = (
|
||||
Cq.Assembly()
|
||||
.addS(self.child(), name="child",
|
||||
|
@ -239,18 +431,21 @@ class ShoulderJoint(Model):
|
|||
.constrain("child/core", "Fixed")
|
||||
.addS(self.torsion_joint.spring.generate(deflection=-deflection), name="spring_top",
|
||||
role=Role.DAMPING, material=mat_spring)
|
||||
.addS(self.bolt.assembly(), name="bolt_top")
|
||||
.addS(self.parent_top(),
|
||||
name="parent_top",
|
||||
role=Role.PARENT, material=mat)
|
||||
.addS(self.torsion_joint.spring.generate(deflection=deflection), name="spring_bot",
|
||||
role=Role.DAMPING, material=mat_spring)
|
||||
.addS(self.bolt.assembly(), name="bolt_bot")
|
||||
.addS(self.parent_bot(),
|
||||
name="parent_bot",
|
||||
role=Role.PARENT, material=mat)
|
||||
.constrain("bolt_top/thread?root", "parent_top/track?bot", "Plane", param=0)
|
||||
.constrain("bolt_bot/thread?root", "parent_bot/track?bot", "Plane", param=0)
|
||||
# Fasteners
|
||||
.addS(self.bolt.assembly(), name="bolt_top",
|
||||
loc=Cq.Location((0, 0, bolt_z)))
|
||||
.constrain("bolt_top/thread?root", 'Fixed')
|
||||
.addS(self.bolt.assembly(), name="bolt_bot",
|
||||
loc=Cq.Location((0, 0, -bolt_z), (1,0,0), 180))
|
||||
.constrain("bolt_bot/thread?root", 'Fixed')
|
||||
)
|
||||
TorsionJoint.add_constraints(
|
||||
result,
|
||||
|
|
|
@ -11,7 +11,7 @@ from nhf import Material, Role
|
|||
from nhf.build import Model, TargetKind, target, assembly, submodel
|
||||
from nhf.parts.box import box_with_centre_holes, MountingBox, Hole
|
||||
from nhf.parts.joints import HirthJoint
|
||||
from nhf.touhou.houjuu_nue.joints import ShoulderJoint, ElbowJoint, DiskJoint
|
||||
from nhf.touhou.houjuu_nue.joints import RootJoint, ShoulderJoint, ElbowJoint, DiskJoint
|
||||
from nhf.parts.planar import extrude_with_markers
|
||||
import nhf.utils
|
||||
|
||||
|
@ -20,19 +20,8 @@ class WingProfile(Model):
|
|||
|
||||
name: str = "wing"
|
||||
|
||||
base_joint: HirthJoint = field(default_factory=lambda: HirthJoint(
|
||||
radius=25.0,
|
||||
radius_inner=15.0,
|
||||
tooth_height=7.0,
|
||||
base_height=5,
|
||||
n_tooth=24,
|
||||
))
|
||||
root_joint: RootJoint = field(default_factory=lambda: RootJoint())
|
||||
base_width: float = 80.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_axis_diam: float = 12.0
|
||||
base_plate_width: float = 50.0
|
||||
|
||||
panel_thickness: float = 25.4 / 16
|
||||
spacer_thickness: float = 25.4 / 8
|
||||
|
@ -108,6 +97,8 @@ class WingProfile(Model):
|
|||
self.shoulder_joint.angle_neutral = -self.shoulder_angle_neutral - self.shoulder_angle_bias
|
||||
self.shoulder_axle_loc = Cq.Location.from2d(self.shoulder_tip_x, self.shoulder_tip_y - self.shoulder_width / 2, -self.shoulder_angle_bias)
|
||||
|
||||
assert self.spacer_thickness == self.root_joint.child_mount_thickness
|
||||
|
||||
@submodel(name="shoulder-joint")
|
||||
def submodel_shoulder_joint(self) -> Model:
|
||||
return self.shoulder_joint
|
||||
|
@ -126,55 +117,6 @@ class WingProfile(Model):
|
|||
def shoulder_height(self) -> float:
|
||||
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
|
||||
|
||||
def outer_profile_s0(self) -> Cq.Sketch:
|
||||
"""
|
||||
The outer boundary of s0, used to produce the curved panel and the
|
||||
|
@ -275,11 +217,11 @@ class WingProfile(Model):
|
|||
"""
|
||||
Should be cut
|
||||
"""
|
||||
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
|
||||
assert self.root_joint.child_width < self.base_width
|
||||
assert self.root_joint.child_corner_dx * 2 < self.base_width
|
||||
assert self.root_joint.child_corner_dz * 2 < self.root_height
|
||||
dy = self.root_joint.child_corner_dx
|
||||
dx = self.root_joint.child_corner_dz
|
||||
holes = [
|
||||
Hole(x=-dx, y=-dy),
|
||||
Hole(x=dx, y=-dy),
|
||||
|
@ -288,17 +230,17 @@ class WingProfile(Model):
|
|||
]
|
||||
return MountingBox(
|
||||
length=self.root_height,
|
||||
width=self.base_plate_width,
|
||||
width=self.root_joint.child_width,
|
||||
thickness=self.spacer_thickness,
|
||||
holes=holes,
|
||||
hole_diam=self.hs_joint_corner_hole_diam,
|
||||
hole_diam=self.root_joint.corner_hole_diam,
|
||||
centred=(True, True),
|
||||
flip_y=self.flip,
|
||||
)
|
||||
|
||||
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
|
||||
base_dx = -(self.base_width - self.root_joint.child_width) / 2
|
||||
base_dy = self.root_joint.hirth_joint.joint_height
|
||||
loc_tip = Cq.Location(0, -self.shoulder_joint.parent_lip_width / 2)
|
||||
tags = [
|
||||
("shoulder",
|
||||
|
@ -351,14 +293,6 @@ class WingProfile(Model):
|
|||
.constrain(f"{tag}?{top_tag}", f"top?{tag}", "Plane")
|
||||
.constrain(f"{tag}?dir", f"top?{tag}_dir", "Axis")
|
||||
)
|
||||
hs_joint = self.base_hs_joint()
|
||||
(
|
||||
result
|
||||
.addS(hs_joint, name="hs", role=Role.CHILD, material=self.mat_hs_joint)
|
||||
.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()
|
||||
|
||||
|
||||
|
@ -627,9 +561,11 @@ class WingProfile(Model):
|
|||
parts: Optional[list[str]] = None,
|
||||
shoulder_deflection: float = 0.0,
|
||||
elbow_wrist_deflection: float = 0.0,
|
||||
root_offset: int = 5,
|
||||
fastener_pos: float = 0.0,
|
||||
) -> Cq.Assembly():
|
||||
if parts is None:
|
||||
parts = ["s0", "shoulder", "s1", "elbow", "s2", "wrist", "s3"]
|
||||
parts = ["root", "s0", "shoulder", "s1", "elbow", "s2", "wrist", "s3"]
|
||||
result = (
|
||||
Cq.Assembly()
|
||||
)
|
||||
|
@ -639,8 +575,23 @@ class WingProfile(Model):
|
|||
|
||||
if "s0" in parts:
|
||||
result.add(self.assembly_s0(), name="s0")
|
||||
if "root" in parts:
|
||||
result.addS(self.root_joint.assembly(
|
||||
offset=root_offset,
|
||||
fastener_pos=fastener_pos,
|
||||
), name="root")
|
||||
result.constrain("root/parent", "Fixed")
|
||||
if "s0" in parts and "root" in parts:
|
||||
(
|
||||
result
|
||||
.constrain("s0/base?conn0", "root/child?conn0", "Plane", param=0)
|
||||
.constrain("s0/base?conn1", "root/child?conn1", "Plane", param=0)
|
||||
.constrain("s0/base?conn2", "root/child?conn2", "Plane", param=0)
|
||||
)
|
||||
if "shoulder" in parts:
|
||||
result.add(self.shoulder_joint.assembly(deflection=shoulder_deflection * 80), name="shoulder")
|
||||
result.add(self.shoulder_joint.assembly(
|
||||
fastener_pos=fastener_pos,
|
||||
deflection=shoulder_deflection * 80), name="shoulder")
|
||||
if "s0" in parts and "shoulder" in parts:
|
||||
(
|
||||
result
|
||||
|
|
Loading…
Reference in New Issue