diff --git a/nhf/touhou/houjuu_nue/joints.py b/nhf/touhou/houjuu_nue/joints.py index 04df3f9..c368d60 100644 --- a/nhf/touhou/houjuu_nue/joints.py +++ b/nhf/touhou/houjuu_nue/joints.py @@ -95,7 +95,7 @@ class RootJoint(Model): child_height: float = 60.0 child_width: float = 50.0 - child_mount_thickness: float = 25.4 / 8 + child_mount_thickness: float = 25.4 / 4 def corner_pos(self) -> list[tuple[int, int]]: """ @@ -888,6 +888,10 @@ class ElbowJoint(Model): def total_thickness(self): return self.disk_joint.total_thickness + @property + def motion_span(self) -> float: + return self.disk_joint.movement_angle + def parent_arm_loc(self) -> Cq.Location: """ 2d Location of the centre of the arm surface on the parent side, assuming diff --git a/nhf/touhou/houjuu_nue/wing.py b/nhf/touhou/houjuu_nue/wing.py index 6d9780b..27c4c6e 100644 --- a/nhf/touhou/houjuu_nue/wing.py +++ b/nhf/touhou/houjuu_nue/wing.py @@ -24,7 +24,9 @@ class WingProfile(Model): root_joint: RootJoint = field(default_factory=lambda: RootJoint()) panel_thickness: float = 25.4 / 16 - spacer_thickness: float = 25.4 / 8 + # 1/4" acrylic for the spacer. Anything thinner would threathen structural + # strength + spacer_thickness: float = 25.4 / 4 shoulder_joint: ShoulderJoint = field(default_factory=lambda: ShoulderJoint( )) @@ -325,9 +327,52 @@ class WingProfile(Model): ### s1, s2, s3 ### def profile(self) -> Cq.Sketch: """ - Generates profile from shoulder and above + Generates profile from shoulder and above. Subclass should implement """ + def _elbow_joint_retract_cut_polygon(self, loc: Cq.Location) -> Cq.Sketch: + """ + Creates a cutting polygon for removing the contraction part of a joint + """ + theta = math.radians(self.elbow_joint.motion_span) + h = self.elbow_height + dx = h * math.tan(theta / 2) + dy = h + sign = -1 if self.flip else 1 + points = [ + (0, 0), + (dx, sign * dy), + (-dx, sign * dy), + ] + return ( + Cq.Sketch() + .polygon([ + (loc * Cq.Location.from2d(*p)).to2d_pos() + for p in points + ]) + ) + def _wrist_joint_retract_cut_polygon(self, loc: Cq.Location) -> Cq.Sketch: + """ + Creates a cutting polygon for removing the contraction part of a joint + """ + theta = math.radians(self.wrist_joint.motion_span) + dx = self.wrist_height * math.tan(theta) + dy = self.wrist_height + sign = -1 if self.flip else 1 + points = [ + (0, 0), + (0, -sign * dy), + (-dx, -sign * dy), + ] + return ( + Cq.Sketch() + .polygon([ + (loc * Cq.Location.from2d(*p)).to2d_pos() + for p in points + ]) + ) + + def _assembly_insert_spacer( self, a: Cq.Assembly, @@ -374,8 +419,7 @@ class WingProfile(Model): self, joint: ElbowJoint, segment_thickness: float, - dx: float, - bot=False) -> MountingBox: + dx: float) -> MountingBox: length = joint.lip_length / 2 - dx holes = [ Hole(x - dx) @@ -398,6 +442,9 @@ class WingProfile(Model): self.profile() .reset() .polygon(self._mask_elbow(), mode='i') + .reset() + .push([self.elbow_axle_loc]) + .each(self._elbow_joint_retract_cut_polygon, mode='s') ) return profile def surface_s1(self, front: bool = True) -> Cq.Workplane: @@ -471,6 +518,12 @@ class WingProfile(Model): .polygon(self._mask_elbow(), mode='s') .reset() .polygon(self._mask_wrist(), mode='i') + .reset() + .push([self.elbow_axle_loc]) + .each(self._elbow_joint_retract_cut_polygon, mode='s') + .reset() + .push([self.wrist_axle_loc]) + .each(self._wrist_joint_retract_cut_polygon, mode='s') ) return profile def surface_s2(self, front: bool = True) -> Cq.Workplane: @@ -638,7 +691,7 @@ class WingProfile(Model): .constrain("s1/shoulder_bot?conn1", f"shoulder/child/lip_{tag_bot}?conn1", "Plane") ) if "elbow" in parts: - angle = self.elbow_joint.disk_joint.movement_angle * elbow_wrist_deflection + angle = self.elbow_joint.motion_span * elbow_wrist_deflection result.add(self.elbow_joint.assembly(angle=angle), name="elbow") if "s1" in parts and "elbow" in parts: ( @@ -659,7 +712,7 @@ class WingProfile(Model): .constrain("s2/elbow_bot?conn1", f"elbow/child/lip?conn_{tag_bot}1", "Plane") ) if "wrist" in parts: - angle = self.wrist_joint.disk_joint.movement_angle * elbow_wrist_deflection + angle = self.wrist_joint.motion_span * elbow_wrist_deflection result.add(self.wrist_joint.assembly(angle=angle), name="wrist") if "s2" in parts and "wrist" in parts: # Mounted backwards to bend in other direction