diff --git a/nhf/touhou/houjuu_nue/joints.py b/nhf/touhou/houjuu_nue/joints.py index 020ebb1..2dcd131 100644 --- a/nhf/touhou/houjuu_nue/joints.py +++ b/nhf/touhou/houjuu_nue/joints.py @@ -605,12 +605,12 @@ class ElbowJoint(Model): material: Material = Material.RESIN_TRANSPERENT - angle_neutral: float = 20.0 + angle_neutral: float = 0.0 def __post_init__(self): assert self.child_arm_radius > self.disk_joint.radius_housing assert self.parent_arm_radius > self.disk_joint.radius_housing - self.disk_joint.tongue_length = self.child_arm_radius - self.disk_joint.radius_disk + self.disk_joint.tongue_length = self.child_arm_radius - self.disk_joint.radius_disk - self.lip_thickness / 2 assert self.disk_joint.movement_angle < self.parent_arm_angle < 360 - self.parent_arm_span def lip(self) -> Cq.Workplane: @@ -640,7 +640,7 @@ class ElbowJoint(Model): # We need to ensure the disk is on the "other" side so flip_x = Cq.Location((0, 0, 0), (1, 0, 0), 180) flip_z = Cq.Location((0, 0, 0), (0, 0, 1), 180) - lip_dz = self.lip_thickness / 2 + lip_dz = self.lip_thickness result = ( Cq.Assembly() .add(self.lip(), name="lip", loc= @@ -664,7 +664,7 @@ class ElbowJoint(Model): connector = ( Cq.Solid.makeCylinder( height=conn_h, - radius=self.parent_arm_radius, + radius=self.parent_arm_radius - self.lip_thickness / 2, angleDegrees=self.parent_arm_span) .cut(Cq.Solid.makeCylinder( height=conn_h, @@ -679,7 +679,7 @@ class ElbowJoint(Model): (0, 0, 1), -self.disk_joint.tongue_span / 2 + self.angle_neutral ) - lip_dz = self.lip_thickness / 2 + lip_dz = self.lip_thickness result = ( Cq.Assembly() .add(self.lip(), name="lip", loc= diff --git a/nhf/touhou/houjuu_nue/test.py b/nhf/touhou/houjuu_nue/test.py index ea10f8a..0fcda84 100644 --- a/nhf/touhou/houjuu_nue/test.py +++ b/nhf/touhou/houjuu_nue/test.py @@ -10,6 +10,23 @@ class TestJoints(unittest.TestCase): j = MJ.ShoulderJoint() assembly = j.assembly() self.assertEqual(pairwise_intersection(assembly), []) + + def test_shoulder_joint_dist(self): + """ + Tests the arm radius + """ + j = MJ.ShoulderJoint() + for deflection in [0, 40, 95, 120]: + with self.subTest(deflection=deflection): + a = j.assembly(deflection=deflection) + # Axle + o = a.get_abs_location("parent_top/track?spring") + l_c1 = a.get_abs_location("parent_top/lip?conn0") + l_c2= a.get_abs_location("parent_top/lip?conn1") + v_c = 0.5 * ((l_c1 - o) + (l_c2 - o)) + v_c.z = 0 + self.assertAlmostEqual(v_c.Length, j.parent_lip_ext) + def test_disk_collision_0(self): j = MJ.DiskJoint() assembly = j.assembly(angle=0) @@ -23,6 +40,28 @@ class TestJoints(unittest.TestCase): assembly = j.assembly(angle=j.movement_angle) self.assertEqual(pairwise_intersection(assembly), []) + def test_elbow_joint_dist(self): + """ + Tests the arm radius + """ + j = MJ.ElbowJoint() + for angle in [0, 10, 20, j.disk_joint.movement_angle]: + with self.subTest(angle=angle): + a = j.assembly(angle=angle) + o = a.get_abs_location("child/disk?mate_bot") + l_c1 = a.get_abs_location("child/lip?conn_top0") + l_c2 = a.get_abs_location("child/lip?conn_bot0") + v_c = 0.5 * ((l_c1 - o) + (l_c2 - o)) + v_c.z = 0 + self.assertAlmostEqual(v_c.Length, j.child_arm_radius) + + l_p1 = a.get_abs_location("parent_upper/lip?conn_top0") + l_p2 = a.get_abs_location("parent_upper/lip?conn_bot0") + v_p = 0.5 * ((l_p1 - o) + (l_p2 - o)) + v_p.z = 0 + self.assertAlmostEqual(v_p.Length, j.parent_arm_radius) + + class Test(unittest.TestCase): def test_hs_joint_parent(self): diff --git a/nhf/touhou/houjuu_nue/wing.py b/nhf/touhou/houjuu_nue/wing.py index 2ba73e9..1962157 100644 --- a/nhf/touhou/houjuu_nue/wing.py +++ b/nhf/touhou/houjuu_nue/wing.py @@ -445,23 +445,20 @@ class WingProfile(Model): .polygon(self._mask_elbow(), mode='i') ) return profile - def surface_s1(self, - shoulder_mount_inset: float = 0, - elbow_mount_inset: float = 0, - front: bool = True) -> Cq.Workplane: + def surface_s1(self, front: bool = True) -> Cq.Workplane: shoulder_h = self.shoulder_joint.child_height h = (self.shoulder_joint.height - shoulder_h) / 2 tags_shoulder = [ - ("shoulder_bot", (shoulder_mount_inset, h), 90), - ("shoulder_top", (shoulder_mount_inset, h + shoulder_h), 270), + ("shoulder_bot", (0, h), 90), + ("shoulder_top", (0, h + shoulder_h), 270), ] h = self.elbow_height / 2 tags_elbow = [ ("elbow_bot", - self.elbow_to_abs(-elbow_mount_inset, h - self.elbow_h2), + self.elbow_to_abs(-self.elbow_joint.parent_arm_radius, h - self.elbow_h2), self.elbow_angle + 0), ("elbow_top", - self.elbow_to_abs(-elbow_mount_inset, h + self.elbow_h2), + self.elbow_to_abs(-self.elbow_joint.parent_arm_radius, h + self.elbow_h2), self.elbow_angle + 0), ] profile = self.profile_s1() @@ -522,26 +519,23 @@ class WingProfile(Model): .polygon(self._mask_wrist(), mode='i') ) return profile - def surface_s2(self, - elbow_mount_inset: float = 0, - wrist_mount_inset: float = 0, - front: bool = True) -> Cq.Workplane: + def surface_s2(self, front: bool = True) -> Cq.Workplane: h = self.elbow_height / 2 tags_elbow = [ ("elbow_bot", - self.elbow_to_abs(elbow_mount_inset, h - self.elbow_h2), + self.elbow_to_abs(self.elbow_joint.child_arm_radius, h - self.elbow_h2), self.elbow_angle + 180), ("elbow_top", - self.elbow_to_abs(elbow_mount_inset, h + self.elbow_h2), + self.elbow_to_abs(self.elbow_joint.child_arm_radius, h + self.elbow_h2), self.elbow_angle + 180), ] h = self.wrist_height / 2 tags_wrist = [ ("wrist_bot", - self.wrist_to_abs(-wrist_mount_inset, h - self.wrist_h2), + self.wrist_to_abs(-self.wrist_joint.parent_arm_radius, h - self.wrist_h2), self.wrist_angle), ("wrist_top", - self.wrist_to_abs(-wrist_mount_inset, h + self.wrist_h2), + self.wrist_to_abs(-self.wrist_joint.parent_arm_radius, h + self.wrist_h2), self.wrist_angle), ] profile = self.profile_s2() @@ -596,14 +590,13 @@ class WingProfile(Model): return profile def surface_s3(self, front: bool = True) -> Cq.Workplane: - wrist_mount_inset = 0 h = self.wrist_height / 2 tags = [ ("wrist_bot", - self.wrist_to_abs(wrist_mount_inset, h - self.wrist_h2), + self.wrist_to_abs(self.wrist_joint.child_arm_radius, h - self.wrist_h2), self.wrist_angle + 180), ("wrist_top", - self.wrist_to_abs(wrist_mount_inset, h + self.wrist_h2), + self.wrist_to_abs(self.wrist_joint.child_arm_radius, h + self.wrist_h2), self.wrist_angle + 180), ] profile = self.profile_s3() @@ -629,14 +622,12 @@ class WingProfile(Model): ) for t in ["wrist_bot", "wrist_top"]: is_top = t.endswith("_top") - is_parent = True o = self.spacer_s3_wrist() self._assembly_insert_spacer( result, o.generate(), point_tag=t, - flipped=is_top,# != is_parent, - #rotate=True, + flipped=is_top, ) return result.solve() diff --git a/nhf/utils.py b/nhf/utils.py index 1dc4d3c..7878472 100644 --- a/nhf/utils.py +++ b/nhf/utils.py @@ -11,8 +11,6 @@ import cadquery as Cq from nhf import Role from typing import Union, Tuple, cast -COLOR_MARKER = Cq.Color(0, 1, 1, 1) - # Bug fixes def _subloc(self, name: str) -> Tuple[Cq.Location, str]: """ @@ -37,6 +35,15 @@ def _subloc(self, name: str) -> Tuple[Cq.Location, str]: return (rv, name_out) Cq.Assembly._subloc = _subloc +### Vector arithmetic + +def location_sub(self: Cq.Location, rhs: Cq.Location) -> Cq.Vector: + (x1, y1, z1), _ = self.toTuple() + (x2, y2, z2), _ = rhs.toTuple() + return Cq.Vector(x1 - x2, y1 - y2, z1 - z2) +Cq.Location.__sub__ = location_sub + +### Tags def tagPoint(self, tag: str): """ @@ -100,6 +107,8 @@ def make_arrow(size: float = 2) -> Cq.Workplane: def to_marker_name(tag: str) -> str: return tag.replace("?", "__T").replace("/", "__Z") + "_marker" +COLOR_MARKER = Cq.Color(0, 1, 1, 1) + def mark_point(self: Cq.Assembly, tag: str, size: float = 2, @@ -132,6 +141,16 @@ def mark_plane(self: Cq.Assembly, Cq.Assembly.markPlane = mark_plane +def get_abs_location(self: Cq.Assembly, + tag: str) -> Cq.Location: + name, shape = self._query(tag) + loc_self = shape.location() + loc_parent, _ = self._subloc(name) + loc = loc_parent * loc_self + return loc + +Cq.Assembly.get_abs_location = get_abs_location + def extrude_with_markers(sketch: Cq.Sketch, thickness: float,