cosplay: Touhou/Houjuu Nue #4
|
@ -219,6 +219,8 @@ class TorsionJoint:
|
||||||
assert self.groove_radius_inner > self.spring.radius > self.radius_axle
|
assert self.groove_radius_inner > self.spring.radius > self.radius_axle
|
||||||
assert self.spring.height > self.groove_depth, "Groove is too deep"
|
assert self.spring.height > self.groove_depth, "Groove is too deep"
|
||||||
assert self.groove_depth < self.spring.height - self.spring.thickness * 2
|
assert self.groove_depth < self.spring.height - self.spring.thickness * 2
|
||||||
|
if self.rider_n_slots == 1:
|
||||||
|
assert self.rider_slot_span == 0.0, "Non-zero span is impossible with multiple riders"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def total_height(self):
|
def total_height(self):
|
||||||
|
@ -373,7 +375,9 @@ class TorsionJoint:
|
||||||
)
|
)
|
||||||
theta_begin = -math.radians(rider_slot_begin)
|
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 self.rider_n_slots <= 1:
|
||||||
|
theta_step = 0
|
||||||
|
elif 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)
|
||||||
|
|
|
@ -50,14 +50,14 @@ class Parameters(Model):
|
||||||
|
|
||||||
wing_r1: MW.WingR = field(default_factory=lambda: MW.WingR(
|
wing_r1: MW.WingR = field(default_factory=lambda: MW.WingR(
|
||||||
name="r1",
|
name="r1",
|
||||||
shoulder_joint=MJ.ShoulderJoint(directrix_id=1),
|
shoulder_angle_bias = 15.0,
|
||||||
))
|
))
|
||||||
wing_r2: MW.WingR = field(default_factory=lambda: MW.WingR(
|
wing_r2: MW.WingR = field(default_factory=lambda: MW.WingR(
|
||||||
name="r2",
|
name="r2",
|
||||||
))
|
))
|
||||||
wing_r3: MW.WingR = field(default_factory=lambda: MW.WingR(
|
wing_r3: MW.WingR = field(default_factory=lambda: MW.WingR(
|
||||||
name="r3",
|
name="r3",
|
||||||
shoulder_joint=MJ.ShoulderJoint(directrix_id=1),
|
shoulder_angle_bias = 15.0,
|
||||||
))
|
))
|
||||||
wing_l1: MW.WingL = field(default_factory=lambda: MW.WingL(
|
wing_l1: MW.WingL = field(default_factory=lambda: MW.WingL(
|
||||||
name="l1",
|
name="l1",
|
||||||
|
@ -66,7 +66,7 @@ class Parameters(Model):
|
||||||
wing_l2: MW.WingL = field(default_factory=lambda: MW.WingL(
|
wing_l2: MW.WingL = field(default_factory=lambda: MW.WingL(
|
||||||
name="l2",
|
name="l2",
|
||||||
wrist_angle=-30.0,
|
wrist_angle=-30.0,
|
||||||
shoulder_joint=MJ.ShoulderJoint(directrix_id=1),
|
shoulder_angle_bias = 15.0,
|
||||||
))
|
))
|
||||||
wing_l3: MW.WingL = field(default_factory=lambda: MW.WingL(
|
wing_l3: MW.WingL = field(default_factory=lambda: MW.WingL(
|
||||||
name="l3",
|
name="l3",
|
||||||
|
@ -84,6 +84,8 @@ class Parameters(Model):
|
||||||
self.wing_l2.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_l3.base_joint = self.harness.hs_hirth_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_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_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_r3.hs_joint_axis_diam == self.harness.hs_joint_axis_diam
|
||||||
|
|
|
@ -32,8 +32,8 @@ class ShoulderJoint(Model):
|
||||||
height=7.5,
|
height=7.5,
|
||||||
),
|
),
|
||||||
rider_slot_begin=0,
|
rider_slot_begin=0,
|
||||||
rider_n_slots=2,
|
rider_n_slots=1,
|
||||||
rider_slot_span=15,
|
rider_slot_span=0,
|
||||||
))
|
))
|
||||||
|
|
||||||
# On the parent side, drill vertical holes
|
# On the parent side, drill vertical holes
|
||||||
|
|
|
@ -10,11 +10,21 @@ class TestJoints(unittest.TestCase):
|
||||||
j = MJ.ShoulderJoint()
|
j = MJ.ShoulderJoint()
|
||||||
assembly = j.torsion_joint.rider_track_assembly()
|
assembly = j.torsion_joint.rider_track_assembly()
|
||||||
self.assertEqual(pairwise_intersection(assembly), [])
|
self.assertEqual(pairwise_intersection(assembly), [])
|
||||||
|
|
||||||
def test_shoulder_collision_0(self):
|
def test_shoulder_collision_0(self):
|
||||||
j = MJ.ShoulderJoint()
|
j = MJ.ShoulderJoint()
|
||||||
assembly = j.assembly()
|
assembly = j.assembly()
|
||||||
self.assertEqual(pairwise_intersection(assembly), [])
|
self.assertEqual(pairwise_intersection(assembly), [])
|
||||||
|
|
||||||
|
def test_shoulder_align(self):
|
||||||
|
j = MJ.ShoulderJoint()
|
||||||
|
a = j.assembly()
|
||||||
|
l_t_c0 = a.get_abs_location("parent_top/lip?conn0")
|
||||||
|
l_b_c0 = a.get_abs_location("parent_bot/lip?conn0")
|
||||||
|
v = l_t_c0 - l_b_c0
|
||||||
|
self.assertAlmostEqual(v.x, 0)
|
||||||
|
self.assertAlmostEqual(v.y, 0)
|
||||||
|
|
||||||
def test_shoulder_joint_dist(self):
|
def test_shoulder_joint_dist(self):
|
||||||
"""
|
"""
|
||||||
Tests the arm radius
|
Tests the arm radius
|
||||||
|
|
|
@ -39,6 +39,7 @@ class WingProfile(Model):
|
||||||
|
|
||||||
shoulder_joint: ShoulderJoint = field(default_factory=lambda: ShoulderJoint(
|
shoulder_joint: ShoulderJoint = field(default_factory=lambda: ShoulderJoint(
|
||||||
))
|
))
|
||||||
|
shoulder_angle_bias: float = 0.0
|
||||||
shoulder_width: float = 36.0
|
shoulder_width: float = 36.0
|
||||||
shoulder_tip_x: float = -200.0
|
shoulder_tip_x: float = -200.0
|
||||||
shoulder_tip_y: float = 160.0
|
shoulder_tip_y: float = 160.0
|
||||||
|
@ -94,7 +95,7 @@ class WingProfile(Model):
|
||||||
self.elbow_top_loc = self.elbow_bot_loc * Cq.Location.from2d(0, self.elbow_height)
|
self.elbow_top_loc = self.elbow_bot_loc * Cq.Location.from2d(0, self.elbow_height)
|
||||||
self.wrist_top_loc = self.wrist_bot_loc * Cq.Location.from2d(0, self.wrist_height)
|
self.wrist_top_loc = self.wrist_bot_loc * Cq.Location.from2d(0, self.wrist_height)
|
||||||
|
|
||||||
self.shoulder_joint.angle_neutral = -self.shoulder_angle_neutral
|
self.shoulder_joint.angle_neutral = -self.shoulder_angle_neutral - self.shoulder_angle_bias
|
||||||
|
|
||||||
@submodel(name="shoulder-joint")
|
@submodel(name="shoulder-joint")
|
||||||
def submodel_shoulder_joint(self) -> Model:
|
def submodel_shoulder_joint(self) -> Model:
|
||||||
|
@ -287,14 +288,14 @@ class WingProfile(Model):
|
||||||
sw = self.shoulder_width
|
sw = self.shoulder_width
|
||||||
|
|
||||||
axle_dist = self.shoulder_joint.parent_lip_ext
|
axle_dist = self.shoulder_joint.parent_lip_ext
|
||||||
theta = math.radians(self.shoulder_joint.angle_neutral)
|
theta = math.radians(self.shoulder_angle_neutral)
|
||||||
c, s = math.cos(-theta), math.sin(-theta)
|
c, s = math.cos(theta), math.sin(theta)
|
||||||
tags = [
|
tags = [
|
||||||
# transforms [axle_dist, -sw/2] about the centre (tip_x, tip_y - sw/2)
|
# transforms [axle_dist, -sw/2] about the centre (tip_x, tip_y - sw/2)
|
||||||
("shoulder", Cq.Location.from2d(
|
("shoulder", Cq.Location.from2d(
|
||||||
self.shoulder_tip_x + axle_dist * c + (-sw/2) * s,
|
self.shoulder_tip_x + axle_dist * c + (-sw/2) * s,
|
||||||
self.shoulder_tip_y - sw / 2 - axle_dist * s + (-sw/2) * c,
|
self.shoulder_tip_y - sw / 2 - axle_dist * s + (-sw/2) * c,
|
||||||
self.shoulder_joint.angle_neutral)),
|
-self.shoulder_angle_neutral)),
|
||||||
("base", Cq.Location.from2d(base_dx, base_dy, 90)),
|
("base", Cq.Location.from2d(base_dx, base_dy, 90)),
|
||||||
]
|
]
|
||||||
result = extrude_with_markers(
|
result = extrude_with_markers(
|
||||||
|
|
Loading…
Reference in New Issue