cosplay: Touhou/Houjuu Nue #4

Open
aniva wants to merge 189 commits from touhou/houjuu-nue into main
2 changed files with 122 additions and 121 deletions
Showing only changes of commit 21e5ad0b82 - Show all commits

View File

@ -593,44 +593,45 @@ class ElbowJoint(Model):
child_arm_radius: float = 40.0 child_arm_radius: float = 40.0
parent_arm_radius: float = 40.0 parent_arm_radius: float = 40.0
child_beam: Beam = field(default_factory=lambda: Beam()) lip_thickness: float = 5.0
parent_beam: Beam = field(default_factory=lambda: Beam( lip_length: float = 60.0
spine_thickness=8.0, hole_pos: list[float] = field(default_factory=lambda: [15, 25])
)) parent_arm_span: float = 30.0
parent_arm_span: float = 40.0
# Angle of the beginning of the parent arm # Angle of the beginning of the parent arm
parent_arm_angle: float = 180.0 parent_arm_angle: float = 180.0
parent_binding_hole_radius: float = 30.0
# Size of the mounting holes # Size of the mounting holes
hole_diam: float = 8.0 hole_diam: float = 6.0
material: Material = Material.RESIN_TRANSPERENT material: Material = Material.RESIN_TRANSPERENT
# If true, flip the top and bottom tags angle_neutral: float = 20.0
flip: bool = False
def __post_init__(self): def __post_init__(self):
assert self.child_arm_radius > self.disk_joint.radius_housing assert self.child_arm_radius > self.disk_joint.radius_housing
assert self.parent_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
assert self.disk_joint.movement_angle < self.parent_arm_angle < 360 - self.parent_arm_span 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]: def lip(self) -> Cq.Workplane:
""" holes = [
List of hole positions measured from axle h
""" for i, x in enumerate(self.hole_pos)
dx = self.child_beam.hole_dist / 2 for h in [
r = self.child_arm_radius Hole(x=x, tag=f"conn_top{i}"),
return [r - dx, r + dx] Hole(x=-x, tag=f"conn_bot{i}")
def parent_hole_pos(self) -> list[float]: ]
""" ]
List of hole positions measured from axle mbox = MountingBox(
""" length=self.lip_length,
dx = self.parent_beam.hole_dist / 2 width=self.disk_joint.total_thickness,
r = self.parent_arm_radius thickness=self.lip_thickness,
return [r - dx, r + dx] holes=holes,
hole_diam=self.hole_diam,
centred=(True, True),
generate_side_tags=False,
)
return mbox.generate()
@target(name="child") @target(name="child")
def child_joint(self) -> Cq.Assembly: 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 # We need to ensure the disk is on the "other" side so
flip_x = Cq.Location((0, 0, 0), (1, 0, 0), 180) flip_x = Cq.Location((0, 0, 0), (1, 0, 0), 180)
flip_z = Cq.Location((0, 0, 0), (0, 0, 1), 180) flip_z = Cq.Location((0, 0, 0), (0, 0, 1), 180)
lip_dz = self.lip_thickness / 2
result = ( 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", .add(self.disk_joint.disk(), name="disk",
loc=flip_x * flip_z * Cq.Location((-self.child_arm_radius, 0, -dz), (0, 0, 1), angle)) 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 return result
@ -658,7 +660,7 @@ class ElbowJoint(Model):
def parent_joint_upper(self): def parent_joint_upper(self):
axial_offset = Cq.Location((self.parent_arm_radius, 0, 0)) axial_offset = Cq.Location((self.parent_arm_radius, 0, 0))
housing_dz = self.disk_joint.housing_upper_dz housing_dz = self.disk_joint.housing_upper_dz
conn_h = self.parent_beam.spine_thickness conn_h = self.lip_thickness
connector = ( connector = (
Cq.Solid.makeCylinder( Cq.Solid.makeCylinder(
height=conn_h, height=conn_h,
@ -675,10 +677,15 @@ class ElbowJoint(Model):
housing_loc = Cq.Location( housing_loc = Cq.Location(
(0, 0, housing_dz), (0, 0, housing_dz),
(0, 0, 1), (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 = ( 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", .add(housing, name="housing",
loc=axial_offset * housing_loc) loc=axial_offset * housing_loc)
.add(connector, name="connector", .add(connector, name="connector",

View File

@ -50,8 +50,9 @@ class WingProfile(Model):
disk_joint=DiskJoint( disk_joint=DiskJoint(
movement_angle=55, movement_angle=55,
), ),
flip=False,
)) ))
# Distance between the two spacers on the elbow, halved
elbow_h2: float = 5.0
s2_thickness: float = 25.0 s2_thickness: float = 25.0
@ -61,8 +62,9 @@ class WingProfile(Model):
radius_disk=13.0, radius_disk=13.0,
radius_housing=15.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 s3_thickness: float = 25.0
@ -367,6 +369,7 @@ class WingProfile(Model):
front_tag: str = "front", front_tag: str = "front",
back_tag: str = "back", back_tag: str = "back",
flipped: bool = False, flipped: bool = False,
rotate: bool = False,
): ):
""" """
For a child joint facing up, front panel should be on the right, back 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" site_front, site_back = "right", "left"
if flipped: if flipped:
site_front, site_back = site_back, site_front site_front, site_back = site_back, site_front
angle = 0 angle = 180 if rotate else 0
( (
a a
.addS( .addS(
@ -410,6 +413,27 @@ class WingProfile(Model):
Polygon shape to mask wrist 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) @target(name="profile-s1", kind=TargetKind.DXF)
def profile_s1(self) -> Cq.Sketch: def profile_s1(self) -> Cq.Sketch:
@ -429,15 +453,14 @@ class WingProfile(Model):
("shoulder_bot", (shoulder_mount_inset, h), 90), ("shoulder_bot", (shoulder_mount_inset, h), 90),
("shoulder_top", (shoulder_mount_inset, h + shoulder_h), 270), ("shoulder_top", (shoulder_mount_inset, h + shoulder_h), 270),
] ]
elbow_h = self.elbow_joint.parent_beam.total_height h = self.elbow_height / 2
h = (self.elbow_height - elbow_h) / 2
tags_elbow = [ tags_elbow = [
("elbow_bot", ("elbow_bot",
self.elbow_to_abs(-elbow_mount_inset, h), self.elbow_to_abs(-elbow_mount_inset, h - self.elbow_h2),
self.elbow_angle + 90), self.elbow_angle + 0),
("elbow_top", ("elbow_top",
self.elbow_to_abs(-elbow_mount_inset, h + elbow_h), self.elbow_to_abs(-elbow_mount_inset, h + self.elbow_h2),
self.elbow_angle + 270), self.elbow_angle + 0),
] ]
profile = self.profile_s1() profile = self.profile_s1()
tags = tags_shoulder + tags_elbow tags = tags_shoulder + tags_elbow
@ -457,16 +480,10 @@ class WingProfile(Model):
) )
@submodel(name="spacer-s1-elbow") @submodel(name="spacer-s1-elbow")
def spacer_s1_elbow(self) -> MountingBox: def spacer_s1_elbow(self) -> MountingBox:
holes = [ return self.spacer_of_joint(
Hole(x) joint=self.elbow_joint,
for x in self.elbow_joint.parent_hole_pos() segment_thickness=self.s1_thickness,
] dx=self.elbow_h2,
return MountingBox(
length=70.0, # FIXME: magic
width=self.s1_thickness,
thickness=self.spacer_thickness,
holes=holes,
hole_diam=self.elbow_joint.hole_diam,
) )
@assembly() @assembly()
def assembly_s1(self) -> Cq.Assembly: def assembly_s1(self) -> Cq.Assembly:
@ -488,7 +505,7 @@ class WingProfile(Model):
result, result,
o, o,
point_tag=t, point_tag=t,
flipped=is_top != is_parent, flipped=is_top != True #is_parent,
) )
return result.solve() return result.solve()
@ -503,58 +520,43 @@ class WingProfile(Model):
) )
return profile return profile
def surface_s2(self, def surface_s2(self,
thickness: float = 25.4/16,
elbow_mount_inset: float = 0, elbow_mount_inset: float = 0,
wrist_mount_inset: float = 0, wrist_mount_inset: float = 0,
front: bool = True) -> Cq.Workplane: front: bool = True) -> Cq.Workplane:
elbow_h = self.elbow_joint.child_beam.total_height h = self.elbow_height / 2
h = (self.elbow_height - elbow_h) / 2
tags_elbow = [ tags_elbow = [
("elbow_bot", ("elbow_bot",
self.elbow_to_abs(elbow_mount_inset, h), self.elbow_to_abs(elbow_mount_inset, h - self.elbow_h2),
self.elbow_angle + 90), self.elbow_angle),
("elbow_top", ("elbow_top",
self.elbow_to_abs(elbow_mount_inset, h + elbow_h), self.elbow_to_abs(elbow_mount_inset, h + self.elbow_h2),
self.elbow_angle - 90), self.elbow_angle),
] ]
wrist_h = self.wrist_joint.parent_beam.total_height h = self.wrist_height / 2
h = (self.wrist_height - wrist_h) / 2
tags_wrist = [ tags_wrist = [
("wrist_bot", ("wrist_bot",
self.wrist_to_abs(-wrist_mount_inset, h), self.wrist_to_abs(-wrist_mount_inset, h - self.wrist_h2),
self.wrist_angle + 90), self.wrist_angle),
("wrist_top", ("wrist_top",
self.wrist_to_abs(-wrist_mount_inset, h + wrist_h), self.wrist_to_abs(-wrist_mount_inset, h + self.wrist_h2),
self.wrist_angle - 90), self.wrist_angle),
] ]
profile = self.profile_s2() profile = self.profile_s2()
tags = tags_elbow + tags_wrist 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") @submodel(name="spacer-s2-elbow")
def spacer_s2_elbow(self) -> MountingBox: def spacer_s2_elbow(self) -> MountingBox:
holes = [ return self.spacer_of_joint(
Hole(x) joint=self.elbow_joint,
for x in self.elbow_joint.child_hole_pos() segment_thickness=self.s2_thickness,
] dx=self.elbow_h2,
return MountingBox(
length=50.0, # FIXME: magic
width=self.s2_thickness,
thickness=self.spacer_thickness,
holes=holes,
hole_diam=self.elbow_joint.hole_diam,
) )
@submodel(name="spacer-s2-wrist") @submodel(name="spacer-s2-wrist")
def spacer_s2_wrist(self) -> MountingBox: def spacer_s2_wrist(self) -> MountingBox:
holes = [ return self.spacer_of_joint(
Hole(x) joint=self.wrist_joint,
for x in self.wrist_joint.parent_hole_pos() segment_thickness=self.s2_thickness,
] dx=self.wrist_h2,
return MountingBox(
length=70.0, # FIXME: magic
width=self.s1_thickness,
thickness=self.spacer_thickness,
holes=holes,
hole_diam=self.wrist_joint.hole_diam,
) )
@assembly() @assembly()
def assembly_s2(self) -> Cq.Assembly: def assembly_s2(self) -> Cq.Assembly:
@ -576,7 +578,8 @@ class WingProfile(Model):
result, result,
o.generate(), o.generate(),
point_tag=t, point_tag=t,
flipped=is_top != is_parent, flipped=is_top,# != is_parent,
rotate=is_parent,
) )
return result.solve() return result.solve()
@ -591,30 +594,23 @@ class WingProfile(Model):
def surface_s3(self, def surface_s3(self,
front: bool = True) -> Cq.Workplane: front: bool = True) -> Cq.Workplane:
wrist_mount_inset = 0 wrist_mount_inset = 0
wrist_h = self.wrist_joint.child_beam.total_height h = self.wrist_height / 2
h = (self.wrist_height - wrist_h) / 2
tags = [ tags = [
("wrist_bot", ("wrist_bot",
self.wrist_to_abs(wrist_mount_inset, h), self.wrist_to_abs(wrist_mount_inset, h - self.wrist_h2),
self.wrist_angle + 90), self.wrist_angle),
("wrist_top", ("wrist_top",
self.wrist_to_abs(wrist_mount_inset, h + wrist_h), self.wrist_to_abs(wrist_mount_inset, h + self.wrist_h2),
self.wrist_angle - 90), self.wrist_angle),
] ]
profile = self.profile_s3() profile = self.profile_s3()
return nhf.utils.extrude_with_markers(profile, self.panel_thickness, tags, reverse=front) return nhf.utils.extrude_with_markers(profile, self.panel_thickness, tags, reverse=front)
@submodel(name="spacer-s3-wrist") @submodel(name="spacer-s3-wrist")
def spacer_s3_wrist(self) -> MountingBox: def spacer_s3_wrist(self) -> MountingBox:
holes = [ return self.spacer_of_joint(
Hole(x) joint=self.wrist_joint,
for x in self.wrist_joint.child_hole_pos() segment_thickness=self.s3_thickness,
] dx=self.wrist_h2,
return MountingBox(
length=70.0, # FIXME: magic
width=self.s1_thickness,
thickness=self.spacer_thickness,
holes=holes,
hole_diam=self.wrist_joint.hole_diam
) )
@assembly() @assembly()
def assembly_s3(self) -> Cq.Assembly: def assembly_s3(self) -> Cq.Assembly:
@ -645,8 +641,6 @@ class WingProfile(Model):
parts: Optional[list[str]] = None, parts: Optional[list[str]] = None,
angle_elbow_wrist: float = 0.0, angle_elbow_wrist: float = 0.0,
) -> Cq.Assembly(): ) -> Cq.Assembly():
assert not self.elbow_joint.flip
assert self.wrist_joint.flip
if parts is None: if parts is None:
parts = ["s0", "shoulder", "s1", "elbow", "s2", "wrist", "s3"] parts = ["s0", "shoulder", "s1", "elbow", "s2", "wrist", "s3"]
result = ( result = (
@ -683,20 +677,20 @@ class WingProfile(Model):
if "s1" in parts and "elbow" in parts: if "s1" in parts and "elbow" in parts:
( (
result result
.constrain("s1/elbow_top?conn0", "elbow/parent_upper/top?conn0", "Plane") .constrain("s1/elbow_top?conn0", "elbow/parent_upper/lip?conn_top0", "Plane")
.constrain("s1/elbow_top?conn1", "elbow/parent_upper/top?conn1", "Plane") .constrain("s1/elbow_top?conn1", "elbow/parent_upper/lip?conn_top1", "Plane")
.constrain("s1/elbow_bot?conn0", "elbow/parent_upper/bot?conn0", "Plane") .constrain("s1/elbow_bot?conn0", "elbow/parent_upper/lip?conn_bot0", "Plane")
.constrain("s1/elbow_bot?conn1", "elbow/parent_upper/bot?conn1", "Plane") .constrain("s1/elbow_bot?conn1", "elbow/parent_upper/lip?conn_bot1", "Plane")
) )
if "s2" in parts: if "s2" in parts:
result.add(self.assembly_s2(), name="s2") result.add(self.assembly_s2(), name="s2")
if "s2" in parts and "elbow" in parts: if "s2" in parts and "elbow" in parts:
( (
result result
.constrain("s2/elbow_top?conn0", "elbow/child/top?conn0", "Plane") .constrain("s2/elbow_top?conn0", "elbow/child/lip?conn_top0", "Plane")
.constrain("s2/elbow_top?conn1", "elbow/child/top?conn1", "Plane") .constrain("s2/elbow_top?conn1", "elbow/child/lip?conn_top1", "Plane")
.constrain("s2/elbow_bot?conn0", "elbow/child/bot?conn0", "Plane") .constrain("s2/elbow_bot?conn0", "elbow/child/lip?conn_bot0", "Plane")
.constrain("s2/elbow_bot?conn1", "elbow/child/bot?conn1", "Plane") .constrain("s2/elbow_bot?conn1", "elbow/child/lip?conn_bot1", "Plane")
) )
if "wrist" in parts: if "wrist" in parts:
result.add(self.wrist_joint.assembly(angle=angle_elbow_wrist), name="wrist") 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 # Mounted backwards to bend in other direction
( (
result result
.constrain("s2/wrist_top?conn0", "wrist/parent_upper/top?conn0", "Plane") .constrain("s2/wrist_top?conn0", "wrist/parent_upper/bot?conn0", "Plane")
.constrain("s2/wrist_top?conn1", "wrist/parent_upper/top?conn1", "Plane") .constrain("s2/wrist_top?conn1", "wrist/parent_upper/bot?conn1", "Plane")
.constrain("s2/wrist_bot?conn0", "wrist/parent_upper/bot?conn0", "Plane") .constrain("s2/wrist_bot?conn0", "wrist/parent_upper/top?conn0", "Plane")
.constrain("s2/wrist_bot?conn1", "wrist/parent_upper/bot?conn1", "Plane") .constrain("s2/wrist_bot?conn1", "wrist/parent_upper/top?conn1", "Plane")
) )
if "s3" in parts: if "s3" in parts:
result.add(self.assembly_s3(), name="s3") result.add(self.assembly_s3(), name="s3")
if "s3" in parts and "wrist" in parts: if "s3" in parts and "wrist" in parts:
( (
result result
.constrain("s3/wrist_top?conn0", "wrist/child/top?conn0", "Plane") .constrain("s3/wrist_top?conn0", "wrist/child/bot?conn0", "Plane")
.constrain("s3/wrist_top?conn1", "wrist/child/top?conn1", "Plane") .constrain("s3/wrist_top?conn1", "wrist/child/bot?conn1", "Plane")
.constrain("s3/wrist_bot?conn0", "wrist/child/bot?conn0", "Plane") .constrain("s3/wrist_bot?conn0", "wrist/child/top?conn0", "Plane")
.constrain("s3/wrist_bot?conn1", "wrist/child/bot?conn1", "Plane") .constrain("s3/wrist_bot?conn1", "wrist/child/top?conn1", "Plane")
) )
if len(parts) > 1: if len(parts) > 1:
result.solve() result.solve()