cosplay: Touhou/Houjuu Nue #4
|
@ -100,7 +100,7 @@ class HirthJoint:
|
||||||
(
|
(
|
||||||
result
|
result
|
||||||
.polyline([(0, 0, 0), (1, 0, 0)], forConstruction=True)
|
.polyline([(0, 0, 0), (1, 0, 0)], forConstruction=True)
|
||||||
.tag("directrix")
|
.tag("dir")
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ class HirthJoint:
|
||||||
.add(obj2, name="obj2", color=Role.CHILD.color)
|
.add(obj2, name="obj2", color=Role.CHILD.color)
|
||||||
.constrain("obj1", "Fixed")
|
.constrain("obj1", "Fixed")
|
||||||
.constrain("obj1?mate", "obj2?mate", "Plane")
|
.constrain("obj1?mate", "obj2?mate", "Plane")
|
||||||
.constrain("obj1?directrix", "obj2?directrix", "Axis", param=angle)
|
.constrain("obj1?dir", "obj2?dir", "Axis", param=angle)
|
||||||
.solve()
|
.solve()
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
@ -249,7 +249,8 @@ class TorsionJoint:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Radius limit for rotating components
|
# Radius limit for rotating components
|
||||||
radius: float = 40
|
radius_track: float = 40
|
||||||
|
radius_rider: float = 38
|
||||||
track_disk_height: float = 10
|
track_disk_height: float = 10
|
||||||
rider_disk_height: float = 8
|
rider_disk_height: float = 8
|
||||||
|
|
||||||
|
@ -281,7 +282,8 @@ class TorsionJoint:
|
||||||
|
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
assert self.radius > self.groove_radius_outer
|
assert self.radius_track > self.groove_radius_outer
|
||||||
|
assert self.radius_rider > self.groove_radius_outer
|
||||||
assert self.groove_radius_outer > self.groove_radius_inner
|
assert self.groove_radius_outer > self.groove_radius_inner
|
||||||
assert self.groove_radius_inner > self.radius_spring
|
assert self.groove_radius_inner > self.radius_spring
|
||||||
assert self.spring_height > self.groove_depth, "Groove is too deep"
|
assert self.spring_height > self.groove_depth, "Groove is too deep"
|
||||||
|
@ -289,8 +291,18 @@ class TorsionJoint:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def total_height(self):
|
def total_height(self):
|
||||||
|
"""
|
||||||
|
Total height counting from bottom to top
|
||||||
|
"""
|
||||||
return self.track_disk_height + self.rider_disk_height + self.spring_height
|
return self.track_disk_height + self.rider_disk_height + self.spring_height
|
||||||
|
|
||||||
|
@property
|
||||||
|
def radius(self):
|
||||||
|
"""
|
||||||
|
Maximum radius of this joint
|
||||||
|
"""
|
||||||
|
return max(self.radius_rider, self.radius_track)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _radius_spring_internal(self):
|
def _radius_spring_internal(self):
|
||||||
return self.radius_spring - self.spring_thickness
|
return self.radius_spring - self.spring_thickness
|
||||||
|
@ -335,14 +347,14 @@ class TorsionJoint:
|
||||||
# TODO: Cover outer part of track only. Can we do this?
|
# TODO: Cover outer part of track only. Can we do this?
|
||||||
groove_profile = (
|
groove_profile = (
|
||||||
Cq.Sketch()
|
Cq.Sketch()
|
||||||
.circle(self.radius)
|
.circle(self.radius_track)
|
||||||
.circle(self.groove_radius_outer, mode='s')
|
.circle(self.groove_radius_outer, mode='s')
|
||||||
.circle(self.groove_radius_inner, mode='a')
|
.circle(self.groove_radius_inner, mode='a')
|
||||||
.circle(self.radius_spring, mode='s')
|
.circle(self.radius_spring, mode='s')
|
||||||
)
|
)
|
||||||
spring_hole_profile = (
|
spring_hole_profile = (
|
||||||
Cq.Sketch()
|
Cq.Sketch()
|
||||||
.circle(self.radius)
|
.circle(self.radius_track)
|
||||||
.circle(self.radius_spring, mode='s')
|
.circle(self.radius_spring, mode='s')
|
||||||
)
|
)
|
||||||
slot_height = self.spring_thickness
|
slot_height = self.spring_thickness
|
||||||
|
@ -359,7 +371,7 @@ class TorsionJoint:
|
||||||
result = (
|
result = (
|
||||||
Cq.Workplane('XY')
|
Cq.Workplane('XY')
|
||||||
.cylinder(
|
.cylinder(
|
||||||
radius=self.radius,
|
radius=self.radius_track,
|
||||||
height=self.track_disk_height,
|
height=self.track_disk_height,
|
||||||
centered=(True, True, False))
|
centered=(True, True, False))
|
||||||
.faces('>Z')
|
.faces('>Z')
|
||||||
|
@ -375,12 +387,12 @@ class TorsionJoint:
|
||||||
.hole(self.radius_axle * 2)
|
.hole(self.radius_axle * 2)
|
||||||
.cut(slot.moved(Cq.Location((0, 0, self.track_disk_height))))
|
.cut(slot.moved(Cq.Location((0, 0, self.track_disk_height))))
|
||||||
)
|
)
|
||||||
# Insert directrix`
|
# Insert directrix
|
||||||
result.polyline(self._directrix(self.track_disk_height),
|
result.polyline(self._directrix(self.track_disk_height),
|
||||||
forConstruction=True).tag("directrix")
|
forConstruction=True).tag("dir")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def rider(self, rider_slot_begin=None):
|
def rider(self, rider_slot_begin=None, reverse_directrix_label=False):
|
||||||
if not rider_slot_begin:
|
if not rider_slot_begin:
|
||||||
rider_slot_begin = self.rider_slot_begin
|
rider_slot_begin = self.rider_slot_begin
|
||||||
def slot(loc):
|
def slot(loc):
|
||||||
|
@ -389,11 +401,11 @@ class TorsionJoint:
|
||||||
return face.located(loc)
|
return face.located(loc)
|
||||||
wall_profile = (
|
wall_profile = (
|
||||||
Cq.Sketch()
|
Cq.Sketch()
|
||||||
.circle(self.radius, mode='a')
|
.circle(self.radius_rider, mode='a')
|
||||||
.circle(self.radius_spring, mode='s')
|
.circle(self.radius_spring, mode='s')
|
||||||
.parray(
|
.parray(
|
||||||
r=0,
|
r=0,
|
||||||
a1=self.rider_slot_begin,
|
a1=rider_slot_begin,
|
||||||
da=self.rider_slot_span,
|
da=self.rider_slot_span,
|
||||||
n=self.rider_n_slots)
|
n=self.rider_n_slots)
|
||||||
.each(slot, mode='s')
|
.each(slot, mode='s')
|
||||||
|
@ -409,7 +421,7 @@ class TorsionJoint:
|
||||||
contact_profile
|
contact_profile
|
||||||
.parray(
|
.parray(
|
||||||
r=0,
|
r=0,
|
||||||
a1=self.rider_slot_begin,
|
a1=rider_slot_begin,
|
||||||
da=self.rider_slot_span,
|
da=self.rider_slot_span,
|
||||||
n=self.rider_n_slots)
|
n=self.rider_n_slots)
|
||||||
.each(slot, mode='s')
|
.each(slot, mode='s')
|
||||||
|
@ -420,7 +432,7 @@ class TorsionJoint:
|
||||||
result = (
|
result = (
|
||||||
Cq.Workplane('XY')
|
Cq.Workplane('XY')
|
||||||
.cylinder(
|
.cylinder(
|
||||||
radius=self.radius,
|
radius=self.radius_rider,
|
||||||
height=self.rider_disk_height,
|
height=self.rider_disk_height,
|
||||||
centered=(True, True, False))
|
centered=(True, True, False))
|
||||||
.faces('>Z')
|
.faces('>Z')
|
||||||
|
@ -442,16 +454,17 @@ class TorsionJoint:
|
||||||
#.workplane()
|
#.workplane()
|
||||||
.hole(self.radius_axle * 2)
|
.hole(self.radius_axle * 2)
|
||||||
)
|
)
|
||||||
theta_begin = math.radians(self.rider_slot_begin) + math.pi
|
theta_begin = math.radians(rider_slot_begin)
|
||||||
theta_span = math.radians(self.rider_slot_span)
|
theta_span = math.radians(self.rider_slot_span)
|
||||||
if abs(math.remainder(self.rider_slot_span, 360)) < TOL:
|
if abs(math.remainder(self.rider_slot_span, 360)) < TOL:
|
||||||
theta_step = theta_span / self.rider_n_slots
|
theta_step = theta_span / self.rider_n_slots
|
||||||
else:
|
else:
|
||||||
theta_step = theta_span / (self.rider_n_slots - 1)
|
theta_step = theta_span / (self.rider_n_slots - 1)
|
||||||
for i in range(self.rider_n_slots):
|
for i in range(self.rider_n_slots):
|
||||||
theta = theta_begin - i * theta_step
|
theta = theta_begin + i * theta_step
|
||||||
|
j = self.rider_n_slots - i - 1 if reverse_directrix_label else i
|
||||||
result.polyline(self._directrix(self.rider_disk_height, theta),
|
result.polyline(self._directrix(self.rider_disk_height, theta),
|
||||||
forConstruction=True).tag(f"directrix{i}")
|
forConstruction=True).tag(f"dir{j}")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def rider_track_assembly(self, directrix=0):
|
def rider_track_assembly(self, directrix=0):
|
||||||
|
@ -462,11 +475,26 @@ class TorsionJoint:
|
||||||
Cq.Assembly()
|
Cq.Assembly()
|
||||||
.add(spring, name="spring", color=Role.DAMPING.color)
|
.add(spring, name="spring", color=Role.DAMPING.color)
|
||||||
.add(track, name="track", color=Role.PARENT.color)
|
.add(track, name="track", color=Role.PARENT.color)
|
||||||
.constrain("track?spring", "spring?top", "Plane")
|
.add(rider, name="rider", color=Role.PARENT.color)
|
||||||
.constrain("track?directrix", "spring?directrix_bot", "Axis")
|
)
|
||||||
.add(rider, name="rider", color=Role.CHILD.color)
|
TorsionJoint.add_constraints(result,
|
||||||
.constrain("rider?spring", "spring?bot", "Plane")
|
rider="rider", track="track", spring="spring",
|
||||||
.constrain(f"rider?directrix{directrix}", "spring?directrix_top", "Axis")
|
directrix=directrix)
|
||||||
.solve()
|
return result.solve()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_constraints(assembly: Cq.Assembly,
|
||||||
|
rider: str, track: str, spring: str,
|
||||||
|
directrix: int = 0):
|
||||||
|
"""
|
||||||
|
Add the necessary constraints to a RT assembly
|
||||||
|
"""
|
||||||
|
(
|
||||||
|
assembly
|
||||||
|
.constrain(f"{track}?spring", f"{spring}?top", "Plane")
|
||||||
|
.constrain(f"{track}?dir", f"{spring}?dir_top",
|
||||||
|
"Axis", param=0)
|
||||||
|
.constrain(f"{rider}?spring", f"{spring}?bot", "Plane")
|
||||||
|
.constrain(f"{rider}?dir{directrix}", f"{spring}?dir_bot",
|
||||||
|
"Axis", param=0)
|
||||||
)
|
)
|
||||||
return result
|
|
||||||
|
|
|
@ -46,11 +46,14 @@ def torsion_spring(radius=12,
|
||||||
centered=False)
|
centered=False)
|
||||||
)
|
)
|
||||||
r = -radius if right_handed else radius
|
r = -radius if right_handed else radius
|
||||||
result.polyline([(0, r, 0), (tail_length, r, 0)],
|
plane = result.copyWorkplane(Cq.Workplane('XY'))
|
||||||
forConstruction=True).tag("directrix_bot")
|
plane.polyline([(0, r, 0), (tail_length, r, 0)],
|
||||||
c, s = math.cos(omega * math.pi / 180), math.sin(omega * math.pi / 180)
|
forConstruction=True).tag("dir_bot")
|
||||||
result.polyline([
|
omega = math.radians(omega)
|
||||||
(s * tail_length, c * r - s * tail_length, height),
|
c, s = math.cos(omega), math.sin(omega)
|
||||||
(c * tail_length + s * r, c * r - s * tail_length, height)],
|
l = -tail_length
|
||||||
forConstruction=True).tag("directrix_top")
|
plane.polyline([
|
||||||
|
(-s * r, c * r, height),
|
||||||
|
(c * l - s * r, c * r + s * l, height)],
|
||||||
|
forConstruction=True).tag("dir_top")
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -35,11 +35,13 @@ class TestJoints(unittest.TestCase):
|
||||||
def test_torsion_joint(self):
|
def test_torsion_joint(self):
|
||||||
j = joints.TorsionJoint()
|
j = joints.TorsionJoint()
|
||||||
for slot in range(j.rider_n_slots):
|
for slot in range(j.rider_n_slots):
|
||||||
with self.subTest(slot=slot):
|
with self.subTest(slot=slot, right_handed=False):
|
||||||
self.torsion_joint_case(j, slot)
|
self.torsion_joint_case(j, slot)
|
||||||
def test_torsion_joint_right_handed(self):
|
def test_torsion_joint_right_handed(self):
|
||||||
j = joints.TorsionJoint(right_handed=True)
|
j = joints.TorsionJoint(right_handed=True)
|
||||||
self.torsion_joint_case(j, 1)
|
for slot in range(j.rider_n_slots):
|
||||||
|
with self.subTest(slot=slot, right_handed=True):
|
||||||
|
self.torsion_joint_case(j, slot)
|
||||||
def test_torsion_joint_covered(self):
|
def test_torsion_joint_covered(self):
|
||||||
j = joints.TorsionJoint(
|
j = joints.TorsionJoint(
|
||||||
spring_hole_cover_track=True,
|
spring_hole_cover_track=True,
|
||||||
|
|
|
@ -30,7 +30,6 @@ s1, s2, s3. The joints are named (from root to tip)
|
||||||
shoulder, elbow, wrist in analogy with human anatomy.
|
shoulder, elbow, wrist in analogy with human anatomy.
|
||||||
"""
|
"""
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
import unittest
|
|
||||||
import cadquery as Cq
|
import cadquery as Cq
|
||||||
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
|
||||||
|
@ -93,6 +92,9 @@ class Parameters(Model):
|
||||||
wing_root_wall_thickness: float = 8
|
wing_root_wall_thickness: float = 8
|
||||||
|
|
||||||
shoulder_torsion_joint: TorsionJoint = field(default_factory=lambda: TorsionJoint(
|
shoulder_torsion_joint: TorsionJoint = field(default_factory=lambda: TorsionJoint(
|
||||||
|
radius_track=35,
|
||||||
|
radius_rider=35,
|
||||||
|
groove_radius_outer=32,
|
||||||
track_disk_height=5.0,
|
track_disk_height=5.0,
|
||||||
rider_disk_height=7.0,
|
rider_disk_height=7.0,
|
||||||
radius_axle=8.0,
|
radius_axle=8.0,
|
||||||
|
@ -114,6 +116,9 @@ class Parameters(Model):
|
||||||
wing_s1_thickness: float = 20
|
wing_s1_thickness: float = 20
|
||||||
wing_s1_spacer_thickness: float = 25.4 / 8
|
wing_s1_spacer_thickness: float = 25.4 / 8
|
||||||
wing_s1_spacer_width: float = 20
|
wing_s1_spacer_width: float = 20
|
||||||
|
wing_s1_spacer_hole_diam: float = 8
|
||||||
|
wing_s1_shoulder_spacer_hole_dist: float = 20
|
||||||
|
wing_s1_shoulder_spacer_width: float = 60
|
||||||
|
|
||||||
trident_handle: Handle = field(default_factory=lambda: Handle(
|
trident_handle: Handle = field(default_factory=lambda: Handle(
|
||||||
diam=38,
|
diam=38,
|
||||||
|
@ -132,6 +137,8 @@ class Parameters(Model):
|
||||||
super().__init__(name="houjuu-nue")
|
super().__init__(name="houjuu-nue")
|
||||||
assert self.wing_root_radius > self.hs_hirth_joint.radius,\
|
assert self.wing_root_radius > self.hs_hirth_joint.radius,\
|
||||||
"Wing root must be large enough to accomodate joint"
|
"Wing root must be large enough to accomodate joint"
|
||||||
|
assert self.wing_s1_shoulder_spacer_hole_dist > self.wing_s1_spacer_hole_diam, \
|
||||||
|
"Spacer holes are too close to each other"
|
||||||
|
|
||||||
@target(name="trident/handle-connector")
|
@target(name="trident/handle-connector")
|
||||||
def handle_connector(self):
|
def handle_connector(self):
|
||||||
|
@ -304,14 +311,32 @@ class Parameters(Model):
|
||||||
conn_thickness=self.wing_s0_thickness,
|
conn_thickness=self.wing_s0_thickness,
|
||||||
)
|
)
|
||||||
|
|
||||||
@target(name="shoulder_parent")
|
@target(name="shoulder_joint_parent")
|
||||||
def shoulder_parent_joint(self) -> Cq.Workplane:
|
def shoulder_joint_parent(self) -> Cq.Workplane:
|
||||||
|
joint = self.shoulder_torsion_joint
|
||||||
|
# Thickness of the lip connecting this joint to the wing root
|
||||||
|
lip_thickness = 10
|
||||||
|
lip_width = 25
|
||||||
|
lip_guard_ext = 40
|
||||||
|
lip_guard_height = self.wing_root_wall_thickness + lip_thickness
|
||||||
|
assert lip_guard_ext > joint.radius_track
|
||||||
|
|
||||||
|
lip_guard = (
|
||||||
|
Cq.Solid.makeBox(lip_guard_ext, lip_width, lip_guard_height)
|
||||||
|
.located(Cq.Location((0, -lip_width/2 , 0)))
|
||||||
|
.cut(Cq.Solid.makeCylinder(joint.radius_track, lip_guard_height))
|
||||||
|
)
|
||||||
result = (
|
result = (
|
||||||
self.shoulder_torsion_joint.rider()
|
joint.track()
|
||||||
|
.union(lip_guard, tol=1e-6)
|
||||||
|
|
||||||
|
# Extrude the handle
|
||||||
.copyWorkplane(Cq.Workplane(
|
.copyWorkplane(Cq.Workplane(
|
||||||
'YZ', origin=Cq.Vector((88, 0, self.wing_root_wall_thickness))))
|
'YZ', origin=Cq.Vector((88, 0, self.wing_root_wall_thickness))))
|
||||||
.rect(25, 7, centered=(True, False))
|
.rect(lip_width, lip_thickness, centered=(True, False))
|
||||||
.extrude("next")
|
.extrude("next")
|
||||||
|
|
||||||
|
# Connector holes on the lip
|
||||||
.copyWorkplane(Cq.Workplane(
|
.copyWorkplane(Cq.Workplane(
|
||||||
'YX', origin=Cq.Vector((57, 0, self.wing_root_wall_thickness))))
|
'YX', origin=Cq.Vector((57, 0, self.wing_root_wall_thickness))))
|
||||||
.hole(self.shoulder_attach_diam)
|
.hole(self.shoulder_attach_diam)
|
||||||
|
@ -322,27 +347,117 @@ 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
|
||||||
|
|
||||||
@target(name="shoulder_child")
|
@target(name="shoulder_joint_child")
|
||||||
def shoulder_child_joint(self) -> Cq.Assembly:
|
def shoulder_joint_child(self) -> Cq.Assembly:
|
||||||
# FIXME: half of conn_height
|
"""
|
||||||
h = 100 / 2
|
Creates the top/bottom shoulder child joint
|
||||||
dh = h - self.shoulder_torsion_joint.total_height
|
"""
|
||||||
|
|
||||||
|
joint = self.shoulder_torsion_joint
|
||||||
|
# Half of the height of the bridging cylinder
|
||||||
|
dh = self.wing_s0_height / 2 - joint.total_height
|
||||||
|
core_start_angle = 30
|
||||||
|
core_end_angle1 = 90
|
||||||
|
core_end_angle2 = 180
|
||||||
|
core_thickness = 2
|
||||||
|
|
||||||
|
core_profile1 = (
|
||||||
|
Cq.Sketch()
|
||||||
|
.arc((0, 0), joint.radius_rider, core_start_angle, core_end_angle1-core_start_angle)
|
||||||
|
.segment((0, 0))
|
||||||
|
.close()
|
||||||
|
.assemble()
|
||||||
|
.circle(joint.radius_rider - core_thickness, mode='s')
|
||||||
|
)
|
||||||
|
core_profile2 = (
|
||||||
|
Cq.Sketch()
|
||||||
|
.arc((0, 0), joint.radius_rider, -core_start_angle, -(core_end_angle2-core_start_angle))
|
||||||
|
.segment((0, 0))
|
||||||
|
.close()
|
||||||
|
.assemble()
|
||||||
|
.circle(joint.radius_rider - core_thickness, mode='s')
|
||||||
|
)
|
||||||
core = (
|
core = (
|
||||||
Cq.Workplane('XY')
|
Cq.Workplane('XY')
|
||||||
.moveTo(0, 15)
|
.placeSketch(core_profile1)
|
||||||
.box(50, 40, 2 * dh, centered=(True, False, True))
|
.toPending()
|
||||||
|
.extrude(dh * 2)
|
||||||
|
.copyWorkplane(Cq.Workplane('XY'))
|
||||||
|
.placeSketch(core_profile2)
|
||||||
|
.toPending()
|
||||||
|
.extrude(dh * 2)
|
||||||
|
.translate(Cq.Vector(0, 0, -dh))
|
||||||
)
|
)
|
||||||
|
# Create the upper and lower lips
|
||||||
|
lip_height = self.wing_s1_thickness
|
||||||
|
lip_thickness = joint.rider_disk_height
|
||||||
|
lip_ext = 40 + joint.radius_rider
|
||||||
|
hole_dx = self.wing_s1_shoulder_spacer_hole_dist
|
||||||
|
assert lip_height / 2 <= joint.radius_rider
|
||||||
|
lip = (
|
||||||
|
Cq.Workplane('XY')
|
||||||
|
.box(lip_ext, lip_height, lip_thickness,
|
||||||
|
centered=(False, True, False))
|
||||||
|
.copyWorkplane(Cq.Workplane('XY'))
|
||||||
|
.cylinder(radius=joint.radius_rider, height=lip_thickness,
|
||||||
|
centered=(True, True, False),
|
||||||
|
combine='cut')
|
||||||
|
.faces(">Z")
|
||||||
|
.workplane()
|
||||||
|
)
|
||||||
|
hole_x = lip_ext - hole_dx / 2
|
||||||
|
for i in range(2):
|
||||||
|
plane = (
|
||||||
|
lip
|
||||||
|
.moveTo(hole_x - i * hole_dx, 0)
|
||||||
|
)
|
||||||
|
lip = plane.hole(self.wing_s1_spacer_hole_diam)
|
||||||
|
plane.tagPlane(f"hole{i}")
|
||||||
|
|
||||||
loc_rotate = Cq.Location((0, 0, 0), (1, 0, 0), 180)
|
loc_rotate = Cq.Location((0, 0, 0), (1, 0, 0), 180)
|
||||||
result = (
|
result = (
|
||||||
Cq.Assembly()
|
Cq.Assembly()
|
||||||
.add(core, name="core", loc=Cq.Location())
|
.add(core, name="core", loc=Cq.Location())
|
||||||
.add(self.shoulder_torsion_joint.track(), name="track_top",
|
.add(joint.rider(rider_slot_begin=-90, reverse_directrix_label=True), name="rider_top",
|
||||||
loc=Cq.Location((0, 0, dh), (0, 0, 1), -90))
|
loc=Cq.Location((0, 0, dh), (0, 0, 1), -90))
|
||||||
.add(self.shoulder_torsion_joint.track(), name="track_bot",
|
.add(joint.rider(rider_slot_begin=180), name="rider_bot",
|
||||||
loc=Cq.Location((0, 0, -dh), (0, 0, 1), -90) * loc_rotate)
|
loc=Cq.Location((0, 0, -dh), (0, 0, 1), -90) * loc_rotate)
|
||||||
|
.add(lip, name="lip_top",
|
||||||
|
loc=Cq.Location((0, 0, dh)))
|
||||||
|
.add(lip, name="lip_bot",
|
||||||
|
loc=Cq.Location((0, 0, -dh)) * loc_rotate)
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@assembly()
|
||||||
|
def shoulder_assembly(self) -> Cq.Assembly:
|
||||||
|
directrix = 0
|
||||||
|
result = (
|
||||||
|
Cq.Assembly()
|
||||||
|
.add(self.shoulder_joint_child(), name="child",
|
||||||
|
color=Role.CHILD.color)
|
||||||
|
.constrain("child/core", "Fixed")
|
||||||
|
.add(self.shoulder_torsion_joint.spring(), name="spring_top",
|
||||||
|
color=Role.DAMPING.color)
|
||||||
|
.add(self.shoulder_joint_parent(), name="parent_top",
|
||||||
|
color=Role.PARENT.color)
|
||||||
|
.add(self.shoulder_torsion_joint.spring(), name="spring_bot",
|
||||||
|
color=Role.DAMPING.color)
|
||||||
|
.add(self.shoulder_joint_parent(), name="parent_bot",
|
||||||
|
color=Role.PARENT.color)
|
||||||
|
)
|
||||||
|
TorsionJoint.add_constraints(result,
|
||||||
|
rider="child/rider_top",
|
||||||
|
track="parent_top",
|
||||||
|
spring="spring_top",
|
||||||
|
directrix=directrix)
|
||||||
|
TorsionJoint.add_constraints(result,
|
||||||
|
rider="child/rider_bot",
|
||||||
|
track="parent_bot",
|
||||||
|
spring="spring_bot",
|
||||||
|
directrix=directrix)
|
||||||
|
return result.solve()
|
||||||
|
|
||||||
@target(name="wing/s1-spacer", kind=TargetKind.DXF)
|
@target(name="wing/s1-spacer", kind=TargetKind.DXF)
|
||||||
def wing_s1_spacer(self) -> Cq.Workplane:
|
def wing_s1_spacer(self) -> Cq.Workplane:
|
||||||
result = (
|
result = (
|
||||||
|
@ -357,6 +472,36 @@ class Parameters(Model):
|
||||||
result.faces(">Y").tag("dir")
|
result.faces(">Y").tag("dir")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@target(name="wing/s1-shoulder-spacer", kind=TargetKind.DXF)
|
||||||
|
def wing_s1_shoulder_spacer(self) -> Cq.Workplane:
|
||||||
|
dx = self.wing_s1_shoulder_spacer_hole_dist
|
||||||
|
result = (
|
||||||
|
Cq.Workplane('XZ')
|
||||||
|
.sketch()
|
||||||
|
.rect(self.wing_s1_shoulder_spacer_width,
|
||||||
|
self.wing_s1_thickness)
|
||||||
|
.push([
|
||||||
|
(0, 0),
|
||||||
|
(dx, 0),
|
||||||
|
])
|
||||||
|
.circle(self.wing_s1_spacer_hole_diam / 2, mode='s')
|
||||||
|
.finalize()
|
||||||
|
.extrude(self.wing_s1_spacer_thickness)
|
||||||
|
)
|
||||||
|
# Tag the mating surfaces to be glued
|
||||||
|
result.faces("<Z").tag("mate1")
|
||||||
|
result.faces(">Z").tag("mate2")
|
||||||
|
|
||||||
|
# Tag the directrix
|
||||||
|
result.faces(">Y").tag("dir")
|
||||||
|
|
||||||
|
# Tag the holes
|
||||||
|
plane = result.faces(">Y").workplane()
|
||||||
|
# Side closer to the parent is 0
|
||||||
|
plane.moveTo(dx, 0).tagPlane("hole0")
|
||||||
|
plane.tagPlane("hole1")
|
||||||
|
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 MW.wing_r1s1_profile()
|
||||||
|
@ -379,35 +524,6 @@ class Parameters(Model):
|
||||||
plane.moveTo(px, sign * py).tagPlane(name)
|
plane.moveTo(px, sign * py).tagPlane(name)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@assembly()
|
|
||||||
def shoulder_assembly(self) -> Cq.Assembly:
|
|
||||||
result = (
|
|
||||||
Cq.Assembly()
|
|
||||||
.add(self.shoulder_child_joint(), name="child",
|
|
||||||
color=Role.CHILD.color)
|
|
||||||
.constrain("child/core", "Fixed")
|
|
||||||
# Top parent joint
|
|
||||||
.add(self.shoulder_torsion_joint.spring(), name="spring_top",
|
|
||||||
color=Role.DAMPING.color)
|
|
||||||
.constrain("child/track_top?spring", "spring_top?top", "Plane")
|
|
||||||
.constrain("child/track_top?directrix", "spring_top?directrix_bot", "Axis")
|
|
||||||
.add(self.shoulder_parent_joint(), name="parent_top",
|
|
||||||
color=Role.PARENT.color)
|
|
||||||
.constrain("parent_top?spring", "spring_top?bot", "Plane")
|
|
||||||
.constrain("parent_top?directrix0", "spring_top?directrix_top", "Axis")
|
|
||||||
# Bottom parent joint
|
|
||||||
.add(self.shoulder_torsion_joint.spring(), name="spring_bot",
|
|
||||||
color=Role.DAMPING.color)
|
|
||||||
.constrain("child/track_bot?spring", "spring_bot?top", "Plane")
|
|
||||||
.constrain("child/track_bot?directrix", "spring_bot?directrix_bot", "Axis")
|
|
||||||
.add(self.shoulder_parent_joint(), name="parent_bot",
|
|
||||||
color=Role.PARENT.color)
|
|
||||||
.constrain("parent_bot?spring", "spring_bot?bot", "Plane")
|
|
||||||
.constrain("parent_bot?directrix0", "spring_bot?directrix_top", "Axis")
|
|
||||||
.solve()
|
|
||||||
)
|
|
||||||
return result
|
|
||||||
|
|
||||||
@assembly()
|
@assembly()
|
||||||
def wing_r1s1_assembly(self) -> Cq.Assembly:
|
def wing_r1s1_assembly(self) -> Cq.Assembly:
|
||||||
result = (
|
result = (
|
||||||
|
@ -427,6 +543,13 @@ class Parameters(Model):
|
||||||
.constrain(f"panel_front?{tag}", f"{tag}_spacer?mate1", "Plane")
|
.constrain(f"panel_front?{tag}", f"{tag}_spacer?mate1", "Plane")
|
||||||
.constrain(f"panel_back?{tag}", f"{tag}_spacer?mate2", "Plane")
|
.constrain(f"panel_back?{tag}", f"{tag}_spacer?mate2", "Plane")
|
||||||
)
|
)
|
||||||
|
(
|
||||||
|
result
|
||||||
|
.add(self.shoulder_assembly(), name="shoulder")
|
||||||
|
.constrain("shoulder_bot_spacer?dir",
|
||||||
|
"shoulder/child/core?mate_bot",
|
||||||
|
"Plane")
|
||||||
|
)
|
||||||
|
|
||||||
result.solve()
|
result.solve()
|
||||||
return result
|
return result
|
||||||
|
|
Loading…
Reference in New Issue