cosplay: Touhou/Houjuu Nue #4
|
@ -65,8 +65,6 @@ class Parameters(Model):
|
||||||
shoulder_joint=MJ.ShoulderJoint(
|
shoulder_joint=MJ.ShoulderJoint(
|
||||||
height=100.0,
|
height=100.0,
|
||||||
),
|
),
|
||||||
elbow_joint=MJ.ElbowJoint(),
|
|
||||||
wrist_joint=MJ.ElbowJoint(),
|
|
||||||
elbow_height=110.0,
|
elbow_height=110.0,
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
|
@ -261,18 +261,22 @@ class Beam:
|
||||||
plane.moveTo(-dx, 0).tagPlane("conn0")
|
plane.moveTo(-dx, 0).tagPlane("conn0")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def beam(self) -> Cq.Assembly:
|
def generate(self, flip: bool = False) -> Cq.Assembly:
|
||||||
beam = (
|
beam = (
|
||||||
Cq.Workplane('XZ')
|
Cq.Workplane('XZ')
|
||||||
.box(self.spine_length, self.spine_thickness, self.spine_height)
|
.box(self.spine_length, self.spine_thickness, self.spine_height)
|
||||||
)
|
)
|
||||||
h = self.spine_height / 2 + self.foot_height
|
h = self.spine_height / 2 + self.foot_height
|
||||||
|
|
||||||
|
tag_p, tag_n = "top", "bot"
|
||||||
|
if flip:
|
||||||
|
tag_p, tag_n = tag_n, tag_p
|
||||||
result = (
|
result = (
|
||||||
Cq.Assembly()
|
Cq.Assembly()
|
||||||
.add(beam, name="beam")
|
.add(beam, name="beam")
|
||||||
.add(self.foot(), name="top",
|
.add(self.foot(), name=tag_p,
|
||||||
loc=Cq.Location((0, h, 0)))
|
loc=Cq.Location((0, h, 0)))
|
||||||
.add(self.foot(), name="bot",
|
.add(self.foot(), name=tag_n,
|
||||||
loc=Cq.Location((0, -h, 0), (1, 0, 0), 180))
|
loc=Cq.Location((0, -h, 0), (1, 0, 0), 180))
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
@ -301,7 +305,7 @@ class DiskJoint(Model):
|
||||||
#disk_thickness_gap: float = 0.1
|
#disk_thickness_gap: float = 0.1
|
||||||
|
|
||||||
# Spring angle at 0 degrees of movement
|
# Spring angle at 0 degrees of movement
|
||||||
spring_angle_at_0: float = 30.0
|
spring_angle_at_0: float = 60.0
|
||||||
spring_slot_offset: float = 15.0
|
spring_slot_offset: float = 15.0
|
||||||
|
|
||||||
wall_inset: float = 2.0
|
wall_inset: float = 2.0
|
||||||
|
@ -493,6 +497,7 @@ class DiskJoint(Model):
|
||||||
disk: str,
|
disk: str,
|
||||||
angle: float = 0.0,
|
angle: float = 0.0,
|
||||||
) -> Cq.Assembly:
|
) -> Cq.Assembly:
|
||||||
|
assert 0 <= angle <= self.movement_angle
|
||||||
deflection = angle - self.neutral_movement_angle
|
deflection = angle - self.neutral_movement_angle
|
||||||
spring_name = disk.replace("/", "__Z") + "_spring"
|
spring_name = disk.replace("/", "__Z") + "_spring"
|
||||||
(
|
(
|
||||||
|
@ -541,7 +546,7 @@ class DiskJoint(Model):
|
||||||
return result.solve()
|
return result.solve()
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass(kw_only=True)
|
||||||
class ElbowJoint:
|
class ElbowJoint:
|
||||||
"""
|
"""
|
||||||
Creates the elbow and wrist joints.
|
Creates the elbow and wrist joints.
|
||||||
|
@ -576,6 +581,9 @@ class ElbowJoint:
|
||||||
|
|
||||||
material: Material = Material.RESIN_TRANSPERENT
|
material: Material = Material.RESIN_TRANSPERENT
|
||||||
|
|
||||||
|
# If true, flip the top and bottom tags
|
||||||
|
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
|
||||||
|
@ -605,7 +613,7 @@ class ElbowJoint:
|
||||||
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)
|
||||||
result = (
|
result = (
|
||||||
self.child_beam.beam()
|
self.child_beam.generate(flip=self.flip)
|
||||||
.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("disk", "Fixed")
|
||||||
|
@ -641,7 +649,7 @@ class ElbowJoint:
|
||||||
-self.disk_joint.tongue_span / 2
|
-self.disk_joint.tongue_span / 2
|
||||||
)
|
)
|
||||||
result = (
|
result = (
|
||||||
self.parent_beam.beam()
|
self.parent_beam.generate(flip=self.flip)
|
||||||
.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",
|
||||||
|
|
|
@ -11,10 +11,10 @@ from nhf import Material, Role
|
||||||
from nhf.build import Model, target, assembly
|
from nhf.build import Model, target, assembly
|
||||||
from nhf.parts.box import box_with_centre_holes, MountingBox, Hole
|
from nhf.parts.box import box_with_centre_holes, MountingBox, Hole
|
||||||
from nhf.parts.joints import HirthJoint
|
from nhf.parts.joints import HirthJoint
|
||||||
from nhf.touhou.houjuu_nue.joints import ShoulderJoint, ElbowJoint
|
from nhf.touhou.houjuu_nue.joints import ShoulderJoint, ElbowJoint, DiskJoint
|
||||||
import nhf.utils
|
import nhf.utils
|
||||||
|
|
||||||
@dataclass
|
@dataclass(kw_only=True)
|
||||||
class WingProfile(Model):
|
class WingProfile(Model):
|
||||||
|
|
||||||
name: str = "wing"
|
name: str = "wing"
|
||||||
|
@ -34,7 +34,12 @@ class WingProfile(Model):
|
||||||
|
|
||||||
s1_thickness: float = 25.0
|
s1_thickness: float = 25.0
|
||||||
|
|
||||||
elbow_joint: ElbowJoint = field(default_factory=lambda: ElbowJoint())
|
elbow_joint: ElbowJoint = field(default_factory=lambda: ElbowJoint(
|
||||||
|
disk_joint=DiskJoint(
|
||||||
|
movement_angle=55,
|
||||||
|
),
|
||||||
|
flip=False,
|
||||||
|
))
|
||||||
elbow_height: float = 100
|
elbow_height: float = 100
|
||||||
elbow_x: float = 240
|
elbow_x: float = 240
|
||||||
elbow_y: float = 30
|
elbow_y: float = 30
|
||||||
|
@ -43,7 +48,12 @@ class WingProfile(Model):
|
||||||
|
|
||||||
s2_thickness: float = 25.0
|
s2_thickness: float = 25.0
|
||||||
|
|
||||||
wrist_joint: ElbowJoint = field(default_factory=lambda: ElbowJoint())
|
wrist_joint: ElbowJoint = field(default_factory=lambda: ElbowJoint(
|
||||||
|
disk_joint=DiskJoint(
|
||||||
|
movement_angle=45,
|
||||||
|
),
|
||||||
|
flip=True,
|
||||||
|
))
|
||||||
wrist_height: float = 70
|
wrist_height: float = 70
|
||||||
# Bottom point of the wrist
|
# Bottom point of the wrist
|
||||||
wrist_x: float = 400
|
wrist_x: float = 400
|
||||||
|
@ -548,6 +558,8 @@ 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 = (
|
||||||
|
@ -601,20 +613,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/bot?conn0", "Plane")
|
.constrain("s2/wrist_top?conn0", "wrist/parent_upper/top?conn0", "Plane")
|
||||||
.constrain("s2/wrist_top?conn1", "wrist/parent_upper/bot?conn1", "Plane")
|
.constrain("s2/wrist_top?conn1", "wrist/parent_upper/top?conn1", "Plane")
|
||||||
.constrain("s2/wrist_bot?conn0", "wrist/parent_upper/top?conn0", "Plane")
|
.constrain("s2/wrist_bot?conn0", "wrist/parent_upper/bot?conn0", "Plane")
|
||||||
.constrain("s2/wrist_bot?conn1", "wrist/parent_upper/top?conn1", "Plane")
|
.constrain("s2/wrist_bot?conn1", "wrist/parent_upper/bot?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/bot?conn0", "Plane")
|
.constrain("s3/wrist_top?conn0", "wrist/child/top?conn0", "Plane")
|
||||||
.constrain("s3/wrist_top?conn1", "wrist/child/bot?conn1", "Plane")
|
.constrain("s3/wrist_top?conn1", "wrist/child/top?conn1", "Plane")
|
||||||
.constrain("s3/wrist_bot?conn0", "wrist/child/top?conn0", "Plane")
|
.constrain("s3/wrist_bot?conn0", "wrist/child/bot?conn0", "Plane")
|
||||||
.constrain("s3/wrist_bot?conn1", "wrist/child/top?conn1", "Plane")
|
.constrain("s3/wrist_bot?conn1", "wrist/child/bot?conn1", "Plane")
|
||||||
)
|
)
|
||||||
if len(parts) > 1:
|
if len(parts) > 1:
|
||||||
result.solve()
|
result.solve()
|
||||||
|
|
Loading…
Reference in New Issue