cosplay: Touhou/Houjuu Nue #4
|
@ -354,7 +354,8 @@ class Flexor:
|
|||
line_slack: float = 0.0
|
||||
|
||||
def __post_init__(self):
|
||||
assert self.line_slack <= self.line_length < self.actuator.stroke_length
|
||||
assert self.line_slack <= self.line_length, f"Insufficient length: {self.line_slack} >= {self.line_length}"
|
||||
assert self.line_slack < self.actuator.stroke_length
|
||||
|
||||
@property
|
||||
def mount_height(self):
|
||||
|
|
|
@ -1121,8 +1121,6 @@ class ElbowJoint(Model):
|
|||
parent_arm_radius: float = 40.0
|
||||
|
||||
lip_thickness: float = 5.0
|
||||
# Extra bit on top of the lip to connect to actuator mount
|
||||
child_lip_extra_length: float = 1.0
|
||||
lip_length: float = 60.0
|
||||
hole_pos: list[float] = field(default_factory=lambda: [12, 24])
|
||||
parent_arm_width: float = 10.0
|
||||
|
@ -1149,6 +1147,8 @@ class ElbowJoint(Model):
|
|||
flexor_child_arm_radius: Optional[float] = None
|
||||
flexor_line_length: float = 0.0
|
||||
flexor_line_slack: float = 0.0
|
||||
flexor_parent_angle_fix: Optional[float] = 180.0
|
||||
flexor_child_angle_fix: Optional[float] = None
|
||||
|
||||
def __post_init__(self):
|
||||
assert self.child_arm_radius > self.disk_joint.radius_housing
|
||||
|
@ -1220,10 +1220,15 @@ class ElbowJoint(Model):
|
|||
# Moves the hole to be some distance apart from 0
|
||||
mount_r, mount_loc_angle, mount_parent_r = self.flexor.open_pos()
|
||||
loc_span = Cq.Location.from2d(mount_r if child else mount_parent_r, 0)
|
||||
alpha = (-mount_loc_angle if child else 0) + 180 - self.flexor_offset_angle
|
||||
if self.flexor_parent_angle_fix is not None:
|
||||
alpha = (-mount_loc_angle if child else 0) + self.flexor_parent_angle_fix - self.flexor_offset_angle
|
||||
elif self.flexor_child_angle_fix is not None:
|
||||
alpha = self.flexor_child_angle_fix + (0 if child else mount_loc_angle)
|
||||
else:
|
||||
raise ValueError("One of flexor_{parent,child}_angle_fix must be set")
|
||||
loc_rot = Cq.Location.rot2d(alpha)
|
||||
loc = loc_rot * loc_span * loc_mount_orient * loc_mount
|
||||
return loc.flip_y() if self.flip and not child and not unflip else loc
|
||||
return loc.flip_y() if self.flip and not unflip else loc
|
||||
|
||||
def lip(self) -> Cq.Workplane:
|
||||
sign = -1 if self.flip else 1
|
||||
|
@ -1248,7 +1253,7 @@ class ElbowJoint(Model):
|
|||
return mbox.generate()
|
||||
|
||||
@target(name="child")
|
||||
def child_joint(self) -> Cq.Assembly:
|
||||
def child_joint(self, generate_mount: bool=False) -> Cq.Assembly:
|
||||
angle = -self.disk_joint.tongue_span / 2
|
||||
dz = self.disk_joint.disk_thickness / 2
|
||||
# We need to ensure the disk is on the "other" side so
|
||||
|
@ -1265,31 +1270,40 @@ class ElbowJoint(Model):
|
|||
loc_cut_rel = Cq.Location((0, self.disk_joint.spring.radius_inner, -self.disk_joint.disk_bot_thickness))
|
||||
disk_cut = self.disk_joint._disk_cut().located(
|
||||
loc_lip.inverse * loc_cut_rel * loc_disk)
|
||||
lip_extra = Cq.Solid.makeBox(
|
||||
length=self.child_lip_extra_length,
|
||||
width=self.total_thickness,
|
||||
height=self.lip_thickness,
|
||||
).located(Cq.Location((
|
||||
self.lip_length / 2,
|
||||
-self.total_thickness / 2,
|
||||
0,
|
||||
)))
|
||||
#lip_extra = Cq.Solid.makeBox(
|
||||
# length=self.child_lip_extra_length,
|
||||
# width=self.total_thickness,
|
||||
# height=self.lip_thickness,
|
||||
#).located(Cq.Location((
|
||||
# self.lip_length / 2,
|
||||
# -self.total_thickness / 2,
|
||||
# 0,
|
||||
#)))
|
||||
result = (
|
||||
Cq.Assembly()
|
||||
.add(self.disk_joint.disk(), name="disk",
|
||||
loc=loc_rot_neutral * Cq.Location((0, 0, -dz), (0,0,1), angle))
|
||||
.add(self.lip().cut(disk_cut), name="lip",
|
||||
loc=loc_rot_neutral * loc_disk.inverse * loc_lip)
|
||||
.add(lip_extra, name="lip_extra",
|
||||
loc=loc_rot_neutral * loc_disk.inverse * loc_lip)
|
||||
#.add(lip_extra, name="lip_extra",
|
||||
# loc=loc_rot_neutral * loc_disk.inverse * loc_lip)
|
||||
)
|
||||
# Orientes the hole surface so it faces +X
|
||||
loc_thickness = Cq.Location((-self.lip_thickness, 0, 0), (0, 1, 0), 90)
|
||||
if self.flexor:
|
||||
loc_mount = self.actuator_mount_loc(child=True, unflip=True)
|
||||
result.add(
|
||||
self.actuator_mount(),
|
||||
Cq.Edge.makeLine((-1,0,0), (1,0,0)),
|
||||
name="act",
|
||||
loc=self.actuator_mount_loc(child=True) * loc_thickness)
|
||||
loc=loc_mount)
|
||||
if generate_mount:
|
||||
# Orientes the hole surface so it faces +X
|
||||
loc_thickness = Cq.Location((-self.lip_thickness, 0, 0), (0, 1, 0), 90)
|
||||
result.add(
|
||||
self.actuator_mount(),
|
||||
name="act_mount",
|
||||
loc=loc_mount * loc_thickness,
|
||||
)
|
||||
return result
|
||||
|
||||
@target(name="parent-lower")
|
||||
|
@ -1342,17 +1356,18 @@ class ElbowJoint(Model):
|
|||
#.solve()
|
||||
)
|
||||
if self.flexor:
|
||||
loc_mount = self.actuator_mount_loc(child=False, unflip=True)
|
||||
result.add(
|
||||
Cq.Edge.makeLine((-1,0,0), (1,0,0)),
|
||||
name="act",
|
||||
loc=self.actuator_mount_loc(child=False, unflip=True))
|
||||
loc=loc_mount)
|
||||
if generate_mount:
|
||||
# Orientes the hole surface so it faces +X
|
||||
loc_thickness = Cq.Location((-self.lip_thickness, 0, 0), (0, 1, 0), 90)
|
||||
result.add(
|
||||
self.actuator_mount(),
|
||||
name="act_mount",
|
||||
loc=self.actuator_mount_loc(child=False, unflip=True) * loc_thickness
|
||||
loc=loc_mount * loc_thickness
|
||||
)
|
||||
return result
|
||||
|
||||
|
@ -1364,7 +1379,7 @@ class ElbowJoint(Model):
|
|||
assert 0 <= angle <= self.motion_span
|
||||
result = (
|
||||
Cq.Assembly()
|
||||
.addS(self.child_joint(), name="child",
|
||||
.addS(self.child_joint(generate_mount=generate_mount), name="child",
|
||||
role=Role.CHILD, material=self.material)
|
||||
.addS(self.parent_joint_lower(), name="parent_lower",
|
||||
role=Role.CASING, material=self.material)
|
||||
|
|
|
@ -23,9 +23,6 @@ from nhf.touhou.houjuu_nue.electronics import (
|
|||
import nhf.utils
|
||||
|
||||
ELBOW_PARAMS = dict(
|
||||
disk_joint=DiskJoint(
|
||||
movement_angle=55,
|
||||
),
|
||||
hole_diam=4.0,
|
||||
actuator=LINEAR_ACTUATOR_50,
|
||||
parent_arm_width=15,
|
||||
|
@ -508,6 +505,8 @@ class WingProfile(Model):
|
|||
loc_ext = loc_bot if bot else loc_top
|
||||
loc_tip = loc_top if bot else loc_bot
|
||||
theta = math.radians(angle_span * (median if child else 1 - median))
|
||||
if self.flip:
|
||||
axle_pos = 1 - axle_pos
|
||||
y_sign = -1 if bot else 1
|
||||
sign = -1 if child else 1
|
||||
dh = axle_pos * height * (overestimate - 1)
|
||||
|
@ -736,11 +735,14 @@ class WingProfile(Model):
|
|||
)
|
||||
return profile
|
||||
def surface_s2(self, front: bool = True) -> Cq.Workplane:
|
||||
loc_elbow = Cq.Location.rot2d(self.elbow_rotate) * self.elbow_joint.child_arm_loc()
|
||||
rot_elbow = Cq.Location.rot2d(self.elbow_rotate)
|
||||
loc_elbow = rot_elbow * self.elbow_joint.child_arm_loc()
|
||||
rot_wrist = Cq.Location.rot2d(self.wrist_rotate)
|
||||
loc_wrist = rot_wrist * self.wrist_joint.parent_arm_loc()
|
||||
tags = [
|
||||
("elbow", self.elbow_axle_loc * loc_elbow),
|
||||
("elbow_act", self.elbow_axle_loc * rot_elbow *
|
||||
self.elbow_joint.actuator_mount_loc(child=True)),
|
||||
("wrist", self.wrist_axle_loc * loc_wrist),
|
||||
("wrist_act", self.wrist_axle_loc * rot_wrist *
|
||||
self.wrist_joint.actuator_mount_loc()),
|
||||
|
@ -773,6 +775,17 @@ class WingProfile(Model):
|
|||
segment_thickness=self.s2_thickness,
|
||||
child=True,
|
||||
)
|
||||
@submodel(name="spacer-s1-elbow-act")
|
||||
def spacer_s2_elbow_act(self) -> MountingBox:
|
||||
return MountingBox(
|
||||
length=self.s2_thickness,
|
||||
width=self.s2_thickness,
|
||||
thickness=self.spacer_thickness,
|
||||
holes=[Hole(x=0,y=0)],
|
||||
centred=(True, True),
|
||||
hole_diam=self.elbow_joint.hole_diam,
|
||||
centre_left_right_tags=True,
|
||||
)
|
||||
@submodel(name="spacer-s2-wrist")
|
||||
def spacer_s2_wrist(self) -> MountingBox:
|
||||
return self._spacer_from_disk_joint(
|
||||
|
@ -822,6 +835,7 @@ class WingProfile(Model):
|
|||
)
|
||||
for o, t in [
|
||||
(self.spacer_s2_elbow(), "elbow"),
|
||||
(self.spacer_s2_elbow_act(), "elbow_act"),
|
||||
(self.spacer_s2_wrist(), "wrist"),
|
||||
(self.spacer_s2_wrist_act(), "wrist_act"),
|
||||
]:
|
||||
|
@ -1055,11 +1069,13 @@ class WingR(WingProfile):
|
|||
elbow_height: float = 111.0
|
||||
elbow_rotate: float = 10.0
|
||||
elbow_joint: ElbowJoint = field(default_factory=lambda: ElbowJoint(
|
||||
disk_joint=DiskJoint(
|
||||
movement_angle=55,
|
||||
),
|
||||
flexor_offset_angle=15,
|
||||
flexor_mount_angle_child=-75,
|
||||
flexor_child_arm_radius=None,
|
||||
angle_neutral=10.0,
|
||||
child_lip_extra_length=8,
|
||||
flip=False,
|
||||
**ELBOW_PARAMS
|
||||
))
|
||||
|
@ -1288,24 +1304,32 @@ class WingR(WingProfile):
|
|||
@dataclass(kw_only=True)
|
||||
class WingL(WingProfile):
|
||||
|
||||
elbow_bot_loc: Cq.Location = Cq.Location.from2d(260.0, 110.0, 0.0)
|
||||
elbow_height: float = 90.0
|
||||
elbow_bot_loc: Cq.Location = Cq.Location.from2d(260.0, 105.0, 0.0)
|
||||
elbow_height: float = 95.0
|
||||
elbow_rotate: float = 15.0
|
||||
elbow_joint: ElbowJoint = field(default_factory=lambda: ElbowJoint(
|
||||
angle_neutral=30.0,
|
||||
flexor_mount_angle_child=170,
|
||||
flexor_mount_angle_parent=-30,
|
||||
flexor_line_length=30.0,
|
||||
flexor_line_slack=5.0,
|
||||
disk_joint=DiskJoint(
|
||||
movement_angle=50,
|
||||
),
|
||||
angle_neutral=25.0,
|
||||
flexor_mount_angle_child=220,
|
||||
flexor_mount_angle_parent=0,
|
||||
flexor_line_length=50.0,
|
||||
flexor_line_slack=10.0,
|
||||
#flexor_line_length=0.0,
|
||||
#flexor_line_slack=0.0,
|
||||
flexor_offset_angle=15,
|
||||
child_lip_extra_length=5.0,
|
||||
flexor_child_arm_radius=60.0,
|
||||
flexor_offset_angle=0,
|
||||
flexor_child_angle_fix=85,
|
||||
flexor_parent_angle_fix=None,
|
||||
flexor_child_arm_radius=50.0,
|
||||
parent_arm_radius=50.0,
|
||||
child_arm_radius=50.0,
|
||||
flexor_pos_smaller=False,
|
||||
flip=True,
|
||||
**ELBOW_PARAMS
|
||||
))
|
||||
elbow_axle_pos: float = 0.53
|
||||
elbow_joint_overlap_median: float = 0.5
|
||||
|
||||
wrist_angle: float = 0.0
|
||||
wrist_bot_loc: Cq.Location = Cq.Location.from2d(460.0, -10.0, -45.0)
|
||||
|
@ -1324,9 +1348,7 @@ class WingL(WingProfile):
|
|||
arrow_height: float = 120.0
|
||||
|
||||
flip: bool = True
|
||||
elbow_axle_pos: float = 0.5
|
||||
wrist_axle_pos: float = 0.5
|
||||
elbow_joint_overlap_median: float = 0.5
|
||||
wrist_joint_overlap_median: float = 0.5
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue