diff --git a/nhf/touhou/houjuu_nue/joints.py b/nhf/touhou/houjuu_nue/joints.py index fb0a2b7..020ebb1 100644 --- a/nhf/touhou/houjuu_nue/joints.py +++ b/nhf/touhou/houjuu_nue/joints.py @@ -593,44 +593,45 @@ class ElbowJoint(Model): child_arm_radius: float = 40.0 parent_arm_radius: float = 40.0 - child_beam: Beam = field(default_factory=lambda: Beam()) - parent_beam: Beam = field(default_factory=lambda: Beam( - spine_thickness=8.0, - )) - parent_arm_span: float = 40.0 + lip_thickness: float = 5.0 + lip_length: float = 60.0 + hole_pos: list[float] = field(default_factory=lambda: [15, 25]) + parent_arm_span: float = 30.0 # Angle of the beginning of the parent arm parent_arm_angle: float = 180.0 - parent_binding_hole_radius: float = 30.0 # Size of the mounting holes - hole_diam: float = 8.0 + hole_diam: float = 6.0 material: Material = Material.RESIN_TRANSPERENT - # If true, flip the top and bottom tags - flip: bool = False + angle_neutral: float = 20.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 assert self.disk_joint.movement_angle < self.parent_arm_angle < 360 - self.parent_arm_span - assert self.parent_binding_hole_radius - self.hole_diam / 2 > self.disk_joint.radius_housing - def child_hole_pos(self) -> list[float]: - """ - List of hole positions measured from axle - """ - dx = self.child_beam.hole_dist / 2 - r = self.child_arm_radius - return [r - dx, r + dx] - def parent_hole_pos(self) -> list[float]: - """ - List of hole positions measured from axle - """ - dx = self.parent_beam.hole_dist / 2 - r = self.parent_arm_radius - return [r - dx, r + dx] + def lip(self) -> Cq.Workplane: + holes = [ + h + for i, x in enumerate(self.hole_pos) + for h in [ + Hole(x=x, tag=f"conn_top{i}"), + Hole(x=-x, tag=f"conn_bot{i}") + ] + ] + mbox = MountingBox( + length=self.lip_length, + width=self.disk_joint.total_thickness, + thickness=self.lip_thickness, + holes=holes, + hole_diam=self.hole_diam, + centred=(True, True), + generate_side_tags=False, + ) + return mbox.generate() @target(name="child") def child_joint(self) -> Cq.Assembly: @@ -639,14 +640,15 @@ 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 result = ( - self.child_beam.generate(flip=self.flip) + Cq.Assembly() + .add(self.lip(), name="lip", loc= + Cq.Location((0, 0, 0), (0, 1, 0), 180) * + Cq.Location((-lip_dz, 0, 0), (1, 0, 0), 90) * + Cq.Location((0, 0, 0), (0, 1, 0), 90)) .add(self.disk_joint.disk(), name="disk", loc=flip_x * flip_z * Cq.Location((-self.child_arm_radius, 0, -dz), (0, 0, 1), angle)) - #.constrain("disk", "Fixed") - #.constrain("top", "Fixed") - #.constrain("bot", "Fixed") - #.solve() ) return result @@ -658,7 +660,7 @@ class ElbowJoint(Model): def parent_joint_upper(self): axial_offset = Cq.Location((self.parent_arm_radius, 0, 0)) housing_dz = self.disk_joint.housing_upper_dz - conn_h = self.parent_beam.spine_thickness + conn_h = self.lip_thickness connector = ( Cq.Solid.makeCylinder( height=conn_h, @@ -675,10 +677,15 @@ class ElbowJoint(Model): housing_loc = Cq.Location( (0, 0, housing_dz), (0, 0, 1), - -self.disk_joint.tongue_span / 2 + -self.disk_joint.tongue_span / 2 + self.angle_neutral ) + lip_dz = self.lip_thickness / 2 result = ( - self.parent_beam.generate(flip=self.flip) + Cq.Assembly() + .add(self.lip(), name="lip", loc= + Cq.Location((0, 0, 0), (0, 1, 0), 180) * + Cq.Location((-lip_dz, 0, 0), (1, 0, 0), 90) * + Cq.Location((0, 0, 0), (0, 1, 0), 90)) .add(housing, name="housing", loc=axial_offset * housing_loc) .add(connector, name="connector", diff --git a/nhf/touhou/houjuu_nue/wing.py b/nhf/touhou/houjuu_nue/wing.py index 3550a6b..d74b2ba 100644 --- a/nhf/touhou/houjuu_nue/wing.py +++ b/nhf/touhou/houjuu_nue/wing.py @@ -50,8 +50,9 @@ class WingProfile(Model): disk_joint=DiskJoint( movement_angle=55, ), - flip=False, )) + # Distance between the two spacers on the elbow, halved + elbow_h2: float = 5.0 s2_thickness: float = 25.0 @@ -61,8 +62,9 @@ class WingProfile(Model): radius_disk=13.0, radius_housing=15.0, ), - flip=True, )) + # Distance between the two spacers on the elbow, halved + wrist_h2: float = 5.0 s3_thickness: float = 25.0 @@ -367,6 +369,7 @@ class WingProfile(Model): front_tag: str = "front", back_tag: str = "back", flipped: bool = False, + rotate: bool = False, ): """ For a child joint facing up, front panel should be on the right, back @@ -375,7 +378,7 @@ class WingProfile(Model): site_front, site_back = "right", "left" if flipped: site_front, site_back = site_back, site_front - angle = 0 + angle = 180 if rotate else 0 ( a .addS( @@ -410,6 +413,27 @@ class WingProfile(Model): Polygon shape to mask wrist """ + def spacer_of_joint( + self, + joint: ElbowJoint, + segment_thickness: float, + dx: float, + bot=False) -> MountingBox: + length = joint.lip_length / 2 - dx + holes = [ + Hole(x - dx) + for x in joint.hole_pos + ] + mbox = MountingBox( + length=length, + width=segment_thickness, + thickness=self.spacer_thickness, + holes=holes, + hole_diam=joint.hole_diam, + centred=(False, True), + ) + return mbox + @target(name="profile-s1", kind=TargetKind.DXF) def profile_s1(self) -> Cq.Sketch: @@ -429,15 +453,14 @@ class WingProfile(Model): ("shoulder_bot", (shoulder_mount_inset, h), 90), ("shoulder_top", (shoulder_mount_inset, h + shoulder_h), 270), ] - elbow_h = self.elbow_joint.parent_beam.total_height - h = (self.elbow_height - elbow_h) / 2 + h = self.elbow_height / 2 tags_elbow = [ ("elbow_bot", - self.elbow_to_abs(-elbow_mount_inset, h), - self.elbow_angle + 90), + self.elbow_to_abs(-elbow_mount_inset, h - self.elbow_h2), + self.elbow_angle + 0), ("elbow_top", - self.elbow_to_abs(-elbow_mount_inset, h + elbow_h), - self.elbow_angle + 270), + self.elbow_to_abs(-elbow_mount_inset, h + self.elbow_h2), + self.elbow_angle + 0), ] profile = self.profile_s1() tags = tags_shoulder + tags_elbow @@ -457,16 +480,10 @@ class WingProfile(Model): ) @submodel(name="spacer-s1-elbow") def spacer_s1_elbow(self) -> MountingBox: - holes = [ - Hole(x) - for x in self.elbow_joint.parent_hole_pos() - ] - return MountingBox( - length=70.0, # FIXME: magic - width=self.s1_thickness, - thickness=self.spacer_thickness, - holes=holes, - hole_diam=self.elbow_joint.hole_diam, + return self.spacer_of_joint( + joint=self.elbow_joint, + segment_thickness=self.s1_thickness, + dx=self.elbow_h2, ) @assembly() def assembly_s1(self) -> Cq.Assembly: @@ -488,7 +505,7 @@ class WingProfile(Model): result, o, point_tag=t, - flipped=is_top != is_parent, + flipped=is_top != True #is_parent, ) return result.solve() @@ -503,58 +520,43 @@ class WingProfile(Model): ) return profile def surface_s2(self, - thickness: float = 25.4/16, elbow_mount_inset: float = 0, wrist_mount_inset: float = 0, front: bool = True) -> Cq.Workplane: - elbow_h = self.elbow_joint.child_beam.total_height - h = (self.elbow_height - elbow_h) / 2 + h = self.elbow_height / 2 tags_elbow = [ ("elbow_bot", - self.elbow_to_abs(elbow_mount_inset, h), - self.elbow_angle + 90), + self.elbow_to_abs(elbow_mount_inset, h - self.elbow_h2), + self.elbow_angle), ("elbow_top", - self.elbow_to_abs(elbow_mount_inset, h + elbow_h), - self.elbow_angle - 90), + self.elbow_to_abs(elbow_mount_inset, h + self.elbow_h2), + self.elbow_angle), ] - wrist_h = self.wrist_joint.parent_beam.total_height - h = (self.wrist_height - wrist_h) / 2 + h = self.wrist_height / 2 tags_wrist = [ ("wrist_bot", - self.wrist_to_abs(-wrist_mount_inset, h), - self.wrist_angle + 90), + self.wrist_to_abs(-wrist_mount_inset, h - self.wrist_h2), + self.wrist_angle), ("wrist_top", - self.wrist_to_abs(-wrist_mount_inset, h + wrist_h), - self.wrist_angle - 90), + self.wrist_to_abs(-wrist_mount_inset, h + self.wrist_h2), + self.wrist_angle), ] profile = self.profile_s2() tags = tags_elbow + tags_wrist - return nhf.utils.extrude_with_markers(profile, thickness, tags, reverse=front) + return nhf.utils.extrude_with_markers(profile, self.panel_thickness, tags, reverse=front) @submodel(name="spacer-s2-elbow") def spacer_s2_elbow(self) -> MountingBox: - holes = [ - Hole(x) - for x in self.elbow_joint.child_hole_pos() - ] - return MountingBox( - length=50.0, # FIXME: magic - width=self.s2_thickness, - thickness=self.spacer_thickness, - holes=holes, - hole_diam=self.elbow_joint.hole_diam, + return self.spacer_of_joint( + joint=self.elbow_joint, + segment_thickness=self.s2_thickness, + dx=self.elbow_h2, ) @submodel(name="spacer-s2-wrist") def spacer_s2_wrist(self) -> MountingBox: - holes = [ - Hole(x) - for x in self.wrist_joint.parent_hole_pos() - ] - return MountingBox( - length=70.0, # FIXME: magic - width=self.s1_thickness, - thickness=self.spacer_thickness, - holes=holes, - hole_diam=self.wrist_joint.hole_diam, + return self.spacer_of_joint( + joint=self.wrist_joint, + segment_thickness=self.s2_thickness, + dx=self.wrist_h2, ) @assembly() def assembly_s2(self) -> Cq.Assembly: @@ -576,7 +578,8 @@ class WingProfile(Model): result, o.generate(), point_tag=t, - flipped=is_top != is_parent, + flipped=is_top,# != is_parent, + rotate=is_parent, ) return result.solve() @@ -591,30 +594,23 @@ class WingProfile(Model): def surface_s3(self, front: bool = True) -> Cq.Workplane: wrist_mount_inset = 0 - wrist_h = self.wrist_joint.child_beam.total_height - h = (self.wrist_height - wrist_h) / 2 + h = self.wrist_height / 2 tags = [ ("wrist_bot", - self.wrist_to_abs(wrist_mount_inset, h), - self.wrist_angle + 90), + self.wrist_to_abs(wrist_mount_inset, h - self.wrist_h2), + self.wrist_angle), ("wrist_top", - self.wrist_to_abs(wrist_mount_inset, h + wrist_h), - self.wrist_angle - 90), + self.wrist_to_abs(wrist_mount_inset, h + self.wrist_h2), + self.wrist_angle), ] profile = self.profile_s3() return nhf.utils.extrude_with_markers(profile, self.panel_thickness, tags, reverse=front) @submodel(name="spacer-s3-wrist") def spacer_s3_wrist(self) -> MountingBox: - holes = [ - Hole(x) - for x in self.wrist_joint.child_hole_pos() - ] - return MountingBox( - length=70.0, # FIXME: magic - width=self.s1_thickness, - thickness=self.spacer_thickness, - holes=holes, - hole_diam=self.wrist_joint.hole_diam + return self.spacer_of_joint( + joint=self.wrist_joint, + segment_thickness=self.s3_thickness, + dx=self.wrist_h2, ) @assembly() def assembly_s3(self) -> Cq.Assembly: @@ -645,8 +641,6 @@ class WingProfile(Model): parts: Optional[list[str]] = None, angle_elbow_wrist: float = 0.0, ) -> Cq.Assembly(): - assert not self.elbow_joint.flip - assert self.wrist_joint.flip if parts is None: parts = ["s0", "shoulder", "s1", "elbow", "s2", "wrist", "s3"] result = ( @@ -683,20 +677,20 @@ class WingProfile(Model): if "s1" in parts and "elbow" in parts: ( result - .constrain("s1/elbow_top?conn0", "elbow/parent_upper/top?conn0", "Plane") - .constrain("s1/elbow_top?conn1", "elbow/parent_upper/top?conn1", "Plane") - .constrain("s1/elbow_bot?conn0", "elbow/parent_upper/bot?conn0", "Plane") - .constrain("s1/elbow_bot?conn1", "elbow/parent_upper/bot?conn1", "Plane") + .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") ) 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/top?conn0", "Plane") - .constrain("s2/elbow_top?conn1", "elbow/child/top?conn1", "Plane") - .constrain("s2/elbow_bot?conn0", "elbow/child/bot?conn0", "Plane") - .constrain("s2/elbow_bot?conn1", "elbow/child/bot?conn1", "Plane") + .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") ) if "wrist" in parts: result.add(self.wrist_joint.assembly(angle=angle_elbow_wrist), name="wrist") @@ -704,20 +698,20 @@ class WingProfile(Model): # Mounted backwards to bend in other direction ( result - .constrain("s2/wrist_top?conn0", "wrist/parent_upper/top?conn0", "Plane") - .constrain("s2/wrist_top?conn1", "wrist/parent_upper/top?conn1", "Plane") - .constrain("s2/wrist_bot?conn0", "wrist/parent_upper/bot?conn0", "Plane") - .constrain("s2/wrist_bot?conn1", "wrist/parent_upper/bot?conn1", "Plane") + .constrain("s2/wrist_top?conn0", "wrist/parent_upper/bot?conn0", "Plane") + .constrain("s2/wrist_top?conn1", "wrist/parent_upper/bot?conn1", "Plane") + .constrain("s2/wrist_bot?conn0", "wrist/parent_upper/top?conn0", "Plane") + .constrain("s2/wrist_bot?conn1", "wrist/parent_upper/top?conn1", "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/top?conn0", "Plane") - .constrain("s3/wrist_top?conn1", "wrist/child/top?conn1", "Plane") - .constrain("s3/wrist_bot?conn0", "wrist/child/bot?conn0", "Plane") - .constrain("s3/wrist_bot?conn1", "wrist/child/bot?conn1", "Plane") + .constrain("s3/wrist_top?conn0", "wrist/child/bot?conn0", "Plane") + .constrain("s3/wrist_top?conn1", "wrist/child/bot?conn1", "Plane") + .constrain("s3/wrist_bot?conn0", "wrist/child/top?conn0", "Plane") + .constrain("s3/wrist_bot?conn1", "wrist/child/top?conn1", "Plane") ) if len(parts) > 1: result.solve()