cosplay: Touhou/Houjuu Nue #4

Open
aniva wants to merge 189 commits from touhou/houjuu-nue into main
3 changed files with 77 additions and 39 deletions
Showing only changes of commit 82570528da - Show all commits

View File

@ -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):

View File

@ -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)

View File

@ -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