From 82d8cf9599a4dc578cb69f09b204521c693aee9c Mon Sep 17 00:00:00 2001 From: Leni Aniva Date: Sat, 20 Jul 2024 22:55:43 -0700 Subject: [PATCH] feat: Extension profile on both sides --- nhf/touhou/houjuu_nue/wing.py | 115 ++++++++++++++++++++++++++++++++-- 1 file changed, 111 insertions(+), 4 deletions(-) diff --git a/nhf/touhou/houjuu_nue/wing.py b/nhf/touhou/houjuu_nue/wing.py index 82ed569..548e1d7 100644 --- a/nhf/touhou/houjuu_nue/wing.py +++ b/nhf/touhou/houjuu_nue/wing.py @@ -62,7 +62,7 @@ class WingProfile(Model): child_arm_radius=23.0, parent_arm_radius=30.0, hole_diam=4.0, - angle_neutral=-20.0, + angle_neutral=-30.0, )) # Distance between the two spacers on the elbow, halved wrist_h2: float = 5.0 @@ -80,7 +80,7 @@ class WingProfile(Model): wrist_bot_loc: Cq.Location wrist_height: float elbow_rotate: float = -5.0 - wrist_rotate: float = -20.0 + wrist_rotate: float = -30.0 # False for the right side, True for the left side flip: bool @@ -385,6 +385,36 @@ class WingProfile(Model): for p in points ]) ) + def _joint_extension_profile( + self, + axle_loc: Cq.Location, + radius: float, + angle_span: float, + bot: bool = False) -> Cq.Sketch: + sign = -1 if bot else 1 + #sign = -1 + axle_loc = axle_loc * Cq.Location.rot2d(180 if bot else 0) + loc_h = Cq.Location.from2d(0, radius) + start = axle_loc * loc_h + mid = axle_loc * Cq.Location.rot2d(-sign * angle_span/2) * loc_h + end = axle_loc * Cq.Location.rot2d(-sign * angle_span) * loc_h + return ( + Cq.Sketch() + .segment( + axle_loc.to2d_pos(), + start.to2d_pos(), + ) + .arc( + start.to2d_pos(), + mid.to2d_pos(), + end.to2d_pos(), + ) + .segment( + end.to2d_pos(), + axle_loc.to2d_pos(), + ) + .assemble() + ) def _assembly_insert_spacer( @@ -480,6 +510,35 @@ class WingProfile(Model): tags = tags_shoulder + tags_elbow return extrude_with_markers( profile, self.panel_thickness, tags, reverse=front) + @target(name="profile-s1-bridge", kind=TargetKind.DXF) + def profile_s1_bridge(self) -> Cq.Workplane: + return ( + self.profile() + #.reset() + #.polygon(self._mask_elbow(), mode='i') + .reset() + .push([self.elbow_axle_loc]) + .each(self._elbow_joint_retract_cut_polygon, mode='i') + .reset() + .push([self.elbow_axle_loc]) + .each(lambda loc: self._joint_extension_profile( + axle_loc=self.elbow_axle_loc, + radius=self.elbow_height / 2, + angle_span=self.elbow_joint.motion_span, + bot=True, + ), mode='a') + ) + def surface_s1_bridge(self, front: bool = True) -> Cq.Workplane: + profile = self.profile_s1_bridge() + loc_elbow = Cq.Location.rot2d(self.elbow_rotate) * self.elbow_joint.parent_arm_loc() + tags = [ + ("elbow_bot", self.elbow_axle_loc * loc_elbow * + Cq.Location.from2d(0, -self.elbow_h2)), + ("elbow_top", self.elbow_axle_loc * loc_elbow * + Cq.Location.from2d(0, self.elbow_h2)), + ] + return extrude_with_markers( + profile, self.panel_thickness, tags, reverse=not front) @submodel(name="spacer-s1-shoulder") def spacer_s1_shoulder(self) -> MountingBox: holes = [ @@ -511,6 +570,14 @@ class WingProfile(Model): material=self.mat_panel, role=self.role_panel) .constrain("front@faces@>Z", "back@faces@ Cq.Workplane: - h = self.elbow_height / 2 loc_elbow = Cq.Location.rot2d(self.elbow_rotate) * self.elbow_joint.child_arm_loc(flip=self.flip) tags_elbow = [ ("elbow_bot", self.elbow_axle_loc * loc_elbow * @@ -553,7 +619,6 @@ class WingProfile(Model): ("elbow_top", self.elbow_axle_loc * loc_elbow * Cq.Location.from2d(0, -self.elbow_h2)), ] - h = self.wrist_height / 2 loc_wrist = Cq.Location.rot2d(self.wrist_rotate) * self.wrist_joint.parent_arm_loc() tags_wrist = [ ("wrist_bot", self.wrist_axle_loc * loc_wrist * @@ -564,6 +629,40 @@ class WingProfile(Model): profile = self.profile_s2() tags = tags_elbow + tags_wrist return extrude_with_markers(profile, self.panel_thickness, tags, reverse=front) + @target(name="profile-s2-bridge", kind=TargetKind.DXF) + def profile_s2_bridge(self) -> Cq.Workplane: + # Generates the extension profile, which is required on both sides + profile = self._joint_extension_profile( + axle_loc=self.wrist_axle_loc, + radius=self.wrist_height * (0.5 if self.flip else 1), + angle_span=self.wrist_joint.motion_span, + bot=False, + ) + # Generates the contraction (cut) profile. only required on the left + if self.flip: + extra = ( + self.profile() + .reset() + .push([self.wrist_axle_loc]) + .each(self._wrist_joint_retract_cut_polygon, mode='i') + ) + profile = ( + profile + .push([self.wrist_axle_loc]) + .each(lambda _: extra, mode='a') + ) + return profile + def surface_s2_bridge(self, front: bool = True) -> Cq.Workplane: + profile = self.profile_s2_bridge() + loc_wrist = Cq.Location.rot2d(self.wrist_rotate) * self.wrist_joint.parent_arm_loc() + tags = [ + ("wrist_bot", self.wrist_axle_loc * loc_wrist * + Cq.Location.from2d(0, -self.wrist_h2)), + ("wrist_top", self.wrist_axle_loc * loc_wrist * + Cq.Location.from2d(0, self.wrist_h2)), + ] + return extrude_with_markers( + profile, self.panel_thickness, tags, reverse=not front) @submodel(name="spacer-s2-elbow") def spacer_s2_elbow(self) -> MountingBox: return self.spacer_of_joint( @@ -589,6 +688,14 @@ class WingProfile(Model): material=self.mat_panel, role=self.role_panel) .constrain("front@faces@>Z", "back@faces@