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
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",

View File

@ -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()