refactor: HS Joint into its own class

This commit is contained in:
Leni Aniva 2024-07-19 15:06:57 -07:00
parent 3e5fe7bc5e
commit 4b6b05853e
Signed by: aniva
GPG Key ID: 4D9B1C8D10EA4C50
5 changed files with 281 additions and 207 deletions

View File

@ -122,5 +122,5 @@ class HexNut(Item):
) )
result.faces("<Z").tag("bot") result.faces("<Z").tag("bot")
result.faces(">Z").tag("top") 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 return result

View File

@ -77,22 +77,15 @@ class Parameters(Model):
def __post_init__(self): def __post_init__(self):
super().__init__(name="houjuu-nue") super().__init__(name="houjuu-nue")
self.wing_r1.base_joint = self.harness.hs_hirth_joint self.wing_r1.root_joint = self.harness.root_joint
self.wing_r2.base_joint = self.harness.hs_hirth_joint self.wing_r2.root_joint = self.harness.root_joint
self.wing_r3.base_joint = self.harness.hs_hirth_joint self.wing_r3.root_joint = self.harness.root_joint
self.wing_l1.base_joint = self.harness.hs_hirth_joint self.wing_l1.root_joint = self.harness.root_joint
self.wing_l2.base_joint = self.harness.hs_hirth_joint self.wing_l2.root_joint = self.harness.root_joint
self.wing_l3.base_joint = self.harness.hs_hirth_joint self.wing_l3.root_joint = self.harness.root_joint
self.wing_r1.shoulder_joint.torsion_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") @submodel(name="harness")
def submodel_harness(self) -> Model: def submodel_harness(self) -> Model:
return self.harness return self.harness
@ -126,19 +119,20 @@ class Parameters(Model):
result = ( result = (
Cq.Assembly() Cq.Assembly()
.add(self.harness.assembly(), name="harness", loc=Cq.Location((0, 0, 0))) .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_r1.assembly(parts, root_offset=9, **kwargs), name="wing_r1")
.add(self.wing_r2.assembly(parts, **kwargs), name="wing_r2") .add(self.wing_r2.assembly(parts, root_offset=7, **kwargs), name="wing_r2")
.add(self.wing_r3.assembly(parts, **kwargs), name="wing_r3") .add(self.wing_r3.assembly(parts, root_offset=6, **kwargs), name="wing_r3")
.add(self.wing_l1.assembly(parts, **kwargs), name="wing_l1") .add(self.wing_l1.assembly(parts, root_offset=7, **kwargs), name="wing_l1")
.add(self.wing_l2.assembly(parts, **kwargs), name="wing_l2") .add(self.wing_l2.assembly(parts, root_offset=8, **kwargs), name="wing_l2")
.add(self.wing_l3.assembly(parts, **kwargs), name="wing_l3") .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)]: for tag in ["r1", "r2", "r3", "l1", "l2", "l3"]:
self.harness.hs_hirth_joint.add_constraints( self.harness.add_root_joint_constraint(
result, result,
f"harness/{tag}", "harness/base",
f"wing_{tag}/s0/hs", f"wing_{tag}/root",
offset=offset) tag
)
return result.solve() return result.solve()
@submodel(name="trident") @submodel(name="trident")

View File

@ -2,7 +2,8 @@ from dataclasses import dataclass, field
import cadquery as Cq import cadquery as Cq
from nhf.parts.joints import HirthJoint from nhf.parts.joints import HirthJoint
from nhf import Material, Role 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 import nhf.utils
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
@ -63,33 +64,18 @@ class Harness(Model):
("r3", BASE_POS_X, -BASE_POS_Y), ("r3", BASE_POS_X, -BASE_POS_Y),
("l3", -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( root_joint: RootJoint = field(default_factory=lambda: RootJoint())
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
mannequin: Mannequin = Mannequin() mannequin: Mannequin = Mannequin()
def __post_init__(self): def __post_init__(self):
super().__init__(name="harness") super().__init__(name="harness")
@submodel(name="root-joint")
def submodel_root_joint(self) -> Model:
return self.root_joint
@target(name="profile", kind=TargetKind.DXF) @target(name="profile", kind=TargetKind.DXF)
def profile(self) -> Cq.Sketch: def profile(self) -> Cq.Sketch:
""" """
@ -115,12 +101,12 @@ class Harness(Model):
.fillet(self.fillet) .fillet(self.fillet)
) )
for tag, x, y in self.wing_base_pos: 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 = (
sketch sketch
.push(conn) .push(conn)
.tag(tag) .tag(tag)
.circle(self.harness_to_root_conn_diam / 2, mode='s') .circle(self.root_joint.corner_hole_diam / 2, mode='s')
.reset() .reset()
) )
return sketch return sketch
@ -138,78 +124,27 @@ class Harness(Model):
plane = result.faces(">Y").workplane() plane = result.faces(">Y").workplane()
for tag, x, y in self.wing_base_pos: for tag, x, y in self.wing_base_pos:
conn = [(px + x, py + y) for px, py 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): for i, (px, py) in enumerate(conn):
plane.moveTo(px, py).tagPoint(f"{tag}_{i}") plane.moveTo(px, py).tagPoint(f"{tag}_{i}")
return result return result
def hs_joint_harness_conn(self) -> list[tuple[int, int]]: def add_root_joint_constraint(
""" self,
Generates a set of points corresponding to the connectorss a: Cq.Assembly,
""" harness_tag: str,
dx = self.hs_joint_base_width / 2 - self.hs_joint_corner_inset joint_tag: str,
return [ mount_tag: str):
(dx, dx), for i in range(4):
(dx, -dx), a.constrain(f"{harness_tag}?{mount_tag}_{i}", f"{joint_tag}/parent?h{i}", "Point")
(-dx, -dx),
(-dx, dx),
]
@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() @assembly()
def assembly(self) -> Cq.Assembly: def assembly(self, with_root_joint: bool = False) -> Cq.Assembly:
harness = self.surface() harness = self.surface()
mannequin_z = self.mannequin.shoulder_to_waist * 0.6 mannequin_z = self.mannequin.shoulder_to_waist * 0.6
result = ( result = (
Cq.Assembly() Cq.Assembly()
.addS( .addS(
@ -224,13 +159,12 @@ class Harness(Model):
loc=Cq.Location((0, -self.thickness, -mannequin_z), (0, 0, 1), 180)) loc=Cq.Location((0, -self.thickness, -mannequin_z), (0, 0, 1), 180))
.constrain("mannequin", "Fixed") .constrain("mannequin", "Fixed")
) )
if with_root_joint:
for name in ["l1", "l2", "l3", "r1", "r2", "r3"]: for name in ["l1", "l2", "l3", "r1", "r2", "r3"]:
j = self.hs_joint_parent()
result.addS( result.addS(
j, name=name, self.root_joint.assembly(), name=name,
role=Role.PARENT, role=Role.PARENT,
material=Material.PLASTIC_PLA) material=Material.PLASTIC_PLA)
for i in range(4): self.add_root_joint_constraint(result, "base", name, name)
result.constrain(f"base?{name}_{i}", f"{name}?h{i}", "Point")
result.solve() result.solve()
return result return result

View File

@ -5,13 +5,204 @@ import cadquery as Cq
from nhf import Material, Role 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.fasteners import FlatHeadBolt from nhf.parts.fasteners import FlatHeadBolt, HexNut, ThreaddedKnob
from nhf.parts.joints import TorsionJoint from nhf.parts.joints import TorsionJoint, HirthJoint
from nhf.parts.box import Hole, MountingBox, 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
@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 @dataclass
class ShoulderJoint(Model): class ShoulderJoint(Model):
@ -228,10 +419,11 @@ class ShoulderJoint(Model):
return result return result
@assembly() @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 directrix = self.directrix_id
mat = Material.RESIN_TRANSPERENT mat = Material.RESIN_TRANSPERENT
mat_spring = Material.STEEL_SPRING mat_spring = Material.STEEL_SPRING
bolt_z = self.height / 2 + self.bolt.height_thread * (fastener_pos - 1)
result = ( result = (
Cq.Assembly() Cq.Assembly()
.addS(self.child(), name="child", .addS(self.child(), name="child",
@ -239,18 +431,21 @@ class ShoulderJoint(Model):
.constrain("child/core", "Fixed") .constrain("child/core", "Fixed")
.addS(self.torsion_joint.spring.generate(deflection=-deflection), name="spring_top", .addS(self.torsion_joint.spring.generate(deflection=-deflection), name="spring_top",
role=Role.DAMPING, material=mat_spring) role=Role.DAMPING, material=mat_spring)
.addS(self.bolt.assembly(), name="bolt_top")
.addS(self.parent_top(), .addS(self.parent_top(),
name="parent_top", name="parent_top",
role=Role.PARENT, material=mat) role=Role.PARENT, material=mat)
.addS(self.torsion_joint.spring.generate(deflection=deflection), name="spring_bot", .addS(self.torsion_joint.spring.generate(deflection=deflection), name="spring_bot",
role=Role.DAMPING, material=mat_spring) role=Role.DAMPING, material=mat_spring)
.addS(self.bolt.assembly(), name="bolt_bot")
.addS(self.parent_bot(), .addS(self.parent_bot(),
name="parent_bot", name="parent_bot",
role=Role.PARENT, material=mat) role=Role.PARENT, material=mat)
.constrain("bolt_top/thread?root", "parent_top/track?bot", "Plane", param=0) # Fasteners
.constrain("bolt_bot/thread?root", "parent_bot/track?bot", "Plane", param=0) .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( TorsionJoint.add_constraints(
result, result,

View File

@ -11,7 +11,7 @@ from nhf import Material, Role
from nhf.build import Model, TargetKind, target, assembly, submodel from nhf.build import Model, TargetKind, target, assembly, submodel
from nhf.parts.box import box_with_centre_holes, MountingBox, Hole from nhf.parts.box import box_with_centre_holes, MountingBox, Hole
from nhf.parts.joints import HirthJoint 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 from nhf.parts.planar import extrude_with_markers
import nhf.utils import nhf.utils
@ -20,19 +20,8 @@ class WingProfile(Model):
name: str = "wing" name: str = "wing"
base_joint: HirthJoint = field(default_factory=lambda: HirthJoint( root_joint: RootJoint = field(default_factory=lambda: RootJoint())
radius=25.0,
radius_inner=15.0,
tooth_height=7.0,
base_height=5,
n_tooth=24,
))
base_width: float = 80.0 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 panel_thickness: float = 25.4 / 16
spacer_thickness: float = 25.4 / 8 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_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) 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") @submodel(name="shoulder-joint")
def submodel_shoulder_joint(self) -> Model: def submodel_shoulder_joint(self) -> Model:
return self.shoulder_joint return self.shoulder_joint
@ -126,55 +117,6 @@ class WingProfile(Model):
def shoulder_height(self) -> float: def shoulder_height(self) -> float:
return self.shoulder_joint.height 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: def outer_profile_s0(self) -> Cq.Sketch:
""" """
The outer boundary of s0, used to produce the curved panel and the The outer boundary of s0, used to produce the curved panel and the
@ -275,11 +217,11 @@ class WingProfile(Model):
""" """
Should be cut Should be cut
""" """
assert self.base_plate_width < self.base_width assert self.root_joint.child_width < self.base_width
assert self.hs_joint_corner_dx * 2 < self.base_width assert self.root_joint.child_corner_dx * 2 < self.base_width
assert self.hs_joint_corner_dz * 2 < self.root_height assert self.root_joint.child_corner_dz * 2 < self.root_height
dy = self.hs_joint_corner_dx dy = self.root_joint.child_corner_dx
dx = self.hs_joint_corner_dz dx = self.root_joint.child_corner_dz
holes = [ holes = [
Hole(x=-dx, y=-dy), Hole(x=-dx, y=-dy),
Hole(x=dx, y=-dy), Hole(x=dx, y=-dy),
@ -288,17 +230,17 @@ class WingProfile(Model):
] ]
return MountingBox( return MountingBox(
length=self.root_height, length=self.root_height,
width=self.base_plate_width, width=self.root_joint.child_width,
thickness=self.spacer_thickness, thickness=self.spacer_thickness,
holes=holes, holes=holes,
hole_diam=self.hs_joint_corner_hole_diam, hole_diam=self.root_joint.corner_hole_diam,
centred=(True, True), centred=(True, True),
flip_y=self.flip, flip_y=self.flip,
) )
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.root_joint.child_width) / 2
base_dy = self.base_joint.joint_height base_dy = self.root_joint.hirth_joint.joint_height
loc_tip = Cq.Location(0, -self.shoulder_joint.parent_lip_width / 2) loc_tip = Cq.Location(0, -self.shoulder_joint.parent_lip_width / 2)
tags = [ tags = [
("shoulder", ("shoulder",
@ -351,14 +293,6 @@ class WingProfile(Model):
.constrain(f"{tag}?{top_tag}", f"top?{tag}", "Plane") .constrain(f"{tag}?{top_tag}", f"top?{tag}", "Plane")
.constrain(f"{tag}?dir", f"top?{tag}_dir", "Axis") .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() return result.solve()
@ -627,9 +561,11 @@ class WingProfile(Model):
parts: Optional[list[str]] = None, parts: Optional[list[str]] = None,
shoulder_deflection: float = 0.0, shoulder_deflection: float = 0.0,
elbow_wrist_deflection: float = 0.0, elbow_wrist_deflection: float = 0.0,
root_offset: int = 5,
fastener_pos: float = 0.0,
) -> Cq.Assembly(): ) -> Cq.Assembly():
if parts is None: if parts is None:
parts = ["s0", "shoulder", "s1", "elbow", "s2", "wrist", "s3"] parts = ["root", "s0", "shoulder", "s1", "elbow", "s2", "wrist", "s3"]
result = ( result = (
Cq.Assembly() Cq.Assembly()
) )
@ -639,8 +575,23 @@ class WingProfile(Model):
if "s0" in parts: if "s0" in parts:
result.add(self.assembly_s0(), name="s0") 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: 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: if "s0" in parts and "shoulder" in parts:
( (
result result