diff --git a/nhf/touhou/houjuu_nue/joints.py b/nhf/touhou/houjuu_nue/joints.py index 78cd819..cb53ae1 100644 --- a/nhf/touhou/houjuu_nue/joints.py +++ b/nhf/touhou/houjuu_nue/joints.py @@ -634,12 +634,15 @@ class ElbowJoint(Model): axle is at position 0, and parent direction is -X """ return Cq.Location.from2d(-self.parent_arm_radius, 0, 0) - def child_arm_loc(self) -> Cq.Location: + def child_arm_loc(self, flip: bool = False) -> Cq.Location: """ 2d Location of the centre of the arm surface on the child side, assuming axle is at position 0, and parent direction is -X + + Set `flip=True` to indicate that the joint is supposed to be installed upside down """ - return Cq.Location.rot2d(self.angle_neutral) * Cq.Location.from2d(self.child_arm_radius, 0, 180) + result = Cq.Location.rot2d(self.angle_neutral) * Cq.Location.from2d(self.child_arm_radius, 0, 180) + return result.flip_y() if flip else result def lip(self) -> Cq.Workplane: holes = [ diff --git a/nhf/touhou/houjuu_nue/wing.py b/nhf/touhou/houjuu_nue/wing.py index 2c70b57..b9e2014 100644 --- a/nhf/touhou/houjuu_nue/wing.py +++ b/nhf/touhou/houjuu_nue/wing.py @@ -62,7 +62,7 @@ class WingProfile(Model): wrist_joint: ElbowJoint = field(default_factory=lambda: ElbowJoint( disk_joint=DiskJoint( - movement_angle=45, + movement_angle=30, radius_disk=13.0, radius_housing=15.0, ), @@ -91,6 +91,7 @@ class WingProfile(Model): elbow_rotate: float = -5.0 wrist_rotate: float = 30.0 + # False for the right side, True for the left side flip: bool = False def __post_init__(self): @@ -447,9 +448,9 @@ class WingProfile(Model): h = self.elbow_height / 2 loc_elbow = Cq.Location.rot2d(self.elbow_rotate) * self.elbow_joint.parent_arm_loc() tags_elbow = [ - ("elbow_bot", self.elbow_axle_loc * loc_elbow *\ + ("elbow_bot", self.elbow_axle_loc * loc_elbow * Cq.Location.from2d(0, -self.elbow_h2)), - ("elbow_top", self.elbow_axle_loc * loc_elbow *\ + ("elbow_top", self.elbow_axle_loc * loc_elbow * Cq.Location.from2d(0, self.elbow_h2)), ] profile = self.profile_s1() @@ -512,19 +513,19 @@ class WingProfile(Model): return profile def surface_s2(self, front: bool = True) -> Cq.Workplane: h = self.elbow_height / 2 - loc_elbow = Cq.Location.rot2d(self.elbow_rotate) * self.elbow_joint.child_arm_loc() + 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 *\ + ("elbow_bot", self.elbow_axle_loc * loc_elbow * Cq.Location.from2d(0, self.elbow_h2)), - ("elbow_top", self.elbow_axle_loc * loc_elbow *\ + ("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().flip_y() + loc_wrist = Cq.Location.rot2d(self.wrist_rotate) * self.wrist_joint.parent_arm_loc() tags_wrist = [ - ("wrist_bot", self.wrist_axle_loc * loc_wrist *\ + ("wrist_bot", self.wrist_axle_loc * loc_wrist * Cq.Location.from2d(0, -self.wrist_h2)), - ("wrist_top", self.wrist_axle_loc * loc_wrist *\ + ("wrist_top", self.wrist_axle_loc * loc_wrist * Cq.Location.from2d(0, self.wrist_h2)), ] profile = self.profile_s2() @@ -580,11 +581,11 @@ class WingProfile(Model): def surface_s3(self, front: bool = True) -> Cq.Workplane: h = self.wrist_height / 2 - loc_wrist = Cq.Location.rot2d(self.wrist_rotate) * self.wrist_joint.child_arm_loc().flip_y() + loc_wrist = Cq.Location.rot2d(self.wrist_rotate) * self.wrist_joint.child_arm_loc(flip=not self.flip) tags = [ - ("wrist_bot", self.wrist_axle_loc * loc_wrist *\ + ("wrist_bot", self.wrist_axle_loc * loc_wrist * Cq.Location.from2d(0, self.wrist_h2)), - ("wrist_top", self.wrist_axle_loc * loc_wrist *\ + ("wrist_top", self.wrist_axle_loc * loc_wrist * Cq.Location.from2d(0, -self.wrist_h2)), ] profile = self.profile_s3() @@ -662,42 +663,42 @@ class WingProfile(Model): if "s1" in parts and "elbow" in parts: ( result - .constrain("s1/elbow_top?conn0", "elbow/parent_upper/lip?conn_top0", "Plane") - .constrain("s1/elbow_top?conn1", "elbow/parent_upper/lip?conn_top1", "Plane") - .constrain("s1/elbow_bot?conn0", "elbow/parent_upper/lip?conn_bot0", "Plane") - .constrain("s1/elbow_bot?conn1", "elbow/parent_upper/lip?conn_bot1", "Plane") + .constrain("s1/elbow_top?conn0", f"elbow/parent_upper/lip?conn_{tag_top}0", "Plane") + .constrain("s1/elbow_top?conn1", f"elbow/parent_upper/lip?conn_{tag_top}1", "Plane") + .constrain("s1/elbow_bot?conn0", f"elbow/parent_upper/lip?conn_{tag_bot}0", "Plane") + .constrain("s1/elbow_bot?conn1", f"elbow/parent_upper/lip?conn_{tag_bot}1", "Plane") ) if "s2" in parts: result.add(self.assembly_s2(), name="s2") if "s2" in parts and "elbow" in parts: ( result - .constrain("s2/elbow_top?conn0", "elbow/child/lip?conn_top0", "Plane") - .constrain("s2/elbow_top?conn1", "elbow/child/lip?conn_top1", "Plane") - .constrain("s2/elbow_bot?conn0", "elbow/child/lip?conn_bot0", "Plane") - .constrain("s2/elbow_bot?conn1", "elbow/child/lip?conn_bot1", "Plane") + .constrain("s2/elbow_top?conn0", f"elbow/child/lip?conn_{tag_top}0", "Plane") + .constrain("s2/elbow_top?conn1", f"elbow/child/lip?conn_{tag_top}1", "Plane") + .constrain("s2/elbow_bot?conn0", f"elbow/child/lip?conn_{tag_bot}0", "Plane") + .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 - result.add(self.wrist_joint.assembly(angle=elbow_wrist_deflection), name="wrist") + 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 ( result - .constrain("s2/wrist_top?conn0", "wrist/parent_upper/lip?conn_bot0", "Plane") - .constrain("s2/wrist_top?conn1", "wrist/parent_upper/lip?conn_bot1", "Plane") - .constrain("s2/wrist_bot?conn0", "wrist/parent_upper/lip?conn_top0", "Plane") - .constrain("s2/wrist_bot?conn1", "wrist/parent_upper/lip?conn_top1", "Plane") + .constrain("s2/wrist_top?conn0", f"wrist/parent_upper/lip?conn_{tag_bot}0", "Plane") + .constrain("s2/wrist_top?conn1", f"wrist/parent_upper/lip?conn_{tag_bot}1", "Plane") + .constrain("s2/wrist_bot?conn0", f"wrist/parent_upper/lip?conn_{tag_top}0", "Plane") + .constrain("s2/wrist_bot?conn1", f"wrist/parent_upper/lip?conn_{tag_top}1", "Plane") ) if "s3" in parts: result.add(self.assembly_s3(), name="s3") if "s3" in parts and "wrist" in parts: ( result - .constrain("s3/wrist_top?conn0", "wrist/child/lip?conn_bot0", "Plane") - .constrain("s3/wrist_top?conn1", "wrist/child/lip?conn_bot1", "Plane") - .constrain("s3/wrist_bot?conn0", "wrist/child/lip?conn_top0", "Plane") - .constrain("s3/wrist_bot?conn1", "wrist/child/lip?conn_top1", "Plane") + .constrain("s3/wrist_top?conn0", f"wrist/child/lip?conn_{tag_bot}0", "Plane") + .constrain("s3/wrist_top?conn1", f"wrist/child/lip?conn_{tag_bot}1", "Plane") + .constrain("s3/wrist_bot?conn0", f"wrist/child/lip?conn_{tag_top}0", "Plane") + .constrain("s3/wrist_bot?conn1", f"wrist/child/lip?conn_{tag_top}1", "Plane") ) if len(parts) > 1: result.solve() @@ -845,10 +846,10 @@ class WingL(WingProfile): def __post_init__(self): assert self.wrist_height <= self.shoulder_joint.height self.wrist_bot_loc = self.wrist_bot_loc.with_angle_2d(self.wrist_angle) - self.elbow_joint.angle_neutral = -15.0 + self.elbow_joint.angle_neutral = 15.0 self.elbow_rotate = 5.0 - self.wrist_joint.angle_neutral = -self.wrist_bot_loc.to2d_rot() - 30.0 - self.wrist_rotate = self.wrist_joint.angle_neutral + self.wrist_joint.angle_neutral = self.wrist_bot_loc.to2d_rot() + 30.0 + self.wrist_rotate = -self.wrist_joint.angle_neutral super().__post_init__()