cosplay: Touhou/Houjuu Nue #4
|
@ -46,15 +46,23 @@ class Parameters(Model):
|
||||||
Defines dimensions for the Houjuu Nue cosplay
|
Defines dimensions for the Houjuu Nue cosplay
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Harness
|
|
||||||
harness: MH.Harness = field(default_factory=lambda: MH.Harness())
|
harness: MH.Harness = field(default_factory=lambda: MH.Harness())
|
||||||
|
|
||||||
wing_r1: MW.WingR = field(default_factory=lambda: MW.WingR(name="r1"))
|
wing_r1: MW.WingR = field(default_factory=lambda: MW.WingR(
|
||||||
wing_r2: MW.WingR = field(default_factory=lambda: MW.WingR(name="r2"))
|
name="r1",
|
||||||
wing_r3: MW.WingR = field(default_factory=lambda: MW.WingR(name="r3"))
|
shoulder_joint=MJ.ShoulderJoint(directrix_id=1),
|
||||||
|
))
|
||||||
|
wing_r2: MW.WingR = field(default_factory=lambda: MW.WingR(
|
||||||
|
name="r2",
|
||||||
|
))
|
||||||
|
wing_r3: MW.WingR = field(default_factory=lambda: MW.WingR(
|
||||||
|
name="r3",
|
||||||
|
shoulder_joint=MJ.ShoulderJoint(directrix_id=1),
|
||||||
|
))
|
||||||
wing_l1: MW.WingL = field(default_factory=lambda: MW.WingL(
|
wing_l1: MW.WingL = field(default_factory=lambda: MW.WingL(
|
||||||
name="l1",
|
name="l1",
|
||||||
wrist_angle=-45.0,
|
wrist_angle=-45.0,
|
||||||
|
shoulder_joint=MJ.ShoulderJoint(directrix_id=1),
|
||||||
))
|
))
|
||||||
wing_l2: MW.WingL = field(default_factory=lambda: MW.WingL(
|
wing_l2: MW.WingL = field(default_factory=lambda: MW.WingL(
|
||||||
name="l2",
|
name="l2",
|
||||||
|
@ -63,6 +71,7 @@ class Parameters(Model):
|
||||||
wing_l3: MW.WingL = field(default_factory=lambda: MW.WingL(
|
wing_l3: MW.WingL = field(default_factory=lambda: MW.WingL(
|
||||||
name="l3",
|
name="l3",
|
||||||
wrist_angle=0.0,
|
wrist_angle=0.0,
|
||||||
|
shoulder_joint=MJ.ShoulderJoint(directrix_id=1),
|
||||||
))
|
))
|
||||||
|
|
||||||
trident: MT.Trident = field(default_factory=lambda: MT.Trident())
|
trident: MT.Trident = field(default_factory=lambda: MT.Trident())
|
||||||
|
|
|
@ -14,7 +14,7 @@ TOL = 1e-6
|
||||||
@dataclass
|
@dataclass
|
||||||
class ShoulderJoint(Model):
|
class ShoulderJoint(Model):
|
||||||
|
|
||||||
height: float = 100.0
|
height: float = 60.0
|
||||||
torsion_joint: TorsionJoint = field(default_factory=lambda: TorsionJoint(
|
torsion_joint: TorsionJoint = field(default_factory=lambda: TorsionJoint(
|
||||||
radius_track=18,
|
radius_track=18,
|
||||||
radius_rider=18,
|
radius_rider=18,
|
||||||
|
@ -30,19 +30,23 @@ class ShoulderJoint(Model):
|
||||||
thickness=1.3,
|
thickness=1.3,
|
||||||
height=7.5,
|
height=7.5,
|
||||||
),
|
),
|
||||||
|
rider_slot_begin=0,
|
||||||
|
rider_n_slots=2,
|
||||||
|
rider_slot_span=15,
|
||||||
))
|
))
|
||||||
|
|
||||||
# On the parent side, drill vertical holes
|
# On the parent side, drill vertical holes
|
||||||
|
|
||||||
parent_conn_hole_diam: float = 6.0
|
parent_conn_hole_diam: float = 4.0
|
||||||
# Position of the holes relative
|
# Position of the holes relative
|
||||||
parent_conn_hole_pos: list[float] = field(default_factory=lambda: [20, 30])
|
parent_conn_hole_pos: list[float] = field(default_factory=lambda: [15])
|
||||||
|
|
||||||
parent_lip_length: float = 40.0
|
parent_lip_length: float = 25.0
|
||||||
parent_lip_width: float = 20.0
|
parent_lip_width: float = 30.0
|
||||||
parent_lip_thickness: float = 8.0
|
parent_lip_thickness: float = 5.0
|
||||||
parent_lip_ext: float = 40.0
|
parent_lip_ext: float = 40.0
|
||||||
parent_lip_guard_height: float = 10.0
|
parent_lip_guard_height: float = 8.0
|
||||||
|
parent_root_wall_thickness: float = 25.4 / 16
|
||||||
|
|
||||||
# Measured from centre of axle
|
# Measured from centre of axle
|
||||||
child_lip_length: float = 45.0
|
child_lip_length: float = 45.0
|
||||||
|
@ -52,13 +56,19 @@ class ShoulderJoint(Model):
|
||||||
child_conn_hole_pos: list[float] = field(default_factory=lambda: [25, 35])
|
child_conn_hole_pos: list[float] = field(default_factory=lambda: [25, 35])
|
||||||
child_core_thickness: float = 3.0
|
child_core_thickness: float = 3.0
|
||||||
|
|
||||||
|
# Rotates the torsion joint to avoid collisions or for some other purpose
|
||||||
|
axis_rotate_bot: float = 225.0
|
||||||
|
axis_rotate_top: float = -225.0
|
||||||
|
|
||||||
@target(name="shoulder-joint/parent")
|
directrix_id: int = 0
|
||||||
def parent(self,
|
|
||||||
root_wall_thickness: float = 25.4 / 16) -> Cq.Assembly:
|
def __post_init__(self):
|
||||||
|
assert self.parent_lip_length * 2 < self.height
|
||||||
|
|
||||||
|
def parent(self, top: bool = False) -> Cq.Assembly:
|
||||||
joint = self.torsion_joint
|
joint = self.torsion_joint
|
||||||
# Thickness of the lip connecting this joint to the wing root
|
# Thickness of the lip connecting this joint to the wing root
|
||||||
dz = root_wall_thickness
|
dz = self.parent_root_wall_thickness
|
||||||
assert self.parent_lip_width <= joint.radius_track * 2
|
assert self.parent_lip_width <= joint.radius_track * 2
|
||||||
assert self.parent_lip_ext > joint.radius_track
|
assert self.parent_lip_ext > joint.radius_track
|
||||||
|
|
||||||
|
@ -86,14 +96,24 @@ class ShoulderJoint(Model):
|
||||||
loc_dir = Cq.Location((0,0,0), (0, 0, 1), 180)
|
loc_dir = Cq.Location((0,0,0), (0, 0, 1), 180)
|
||||||
loc_pos = Cq.Location((self.parent_lip_ext - self.parent_lip_thickness, 0, dz))
|
loc_pos = Cq.Location((self.parent_lip_ext - self.parent_lip_thickness, 0, dz))
|
||||||
|
|
||||||
|
rot = -self.axis_rotate_top if top else self.axis_rotate_bot
|
||||||
|
|
||||||
result = (
|
result = (
|
||||||
Cq.Assembly()
|
Cq.Assembly()
|
||||||
.add(joint.track(), name="track")
|
.add(joint.track(), name="track",
|
||||||
|
loc=Cq.Location((0, 0, 0), (0, 0, 1), rot))
|
||||||
.add(lip_guard, name="lip_guard")
|
.add(lip_guard, name="lip_guard")
|
||||||
.add(lip, name="lip", loc=loc_pos * loc_dir * loc_axis)
|
.add(lip, name="lip", loc=loc_pos * loc_dir * loc_axis)
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@target(name="parent-bot")
|
||||||
|
def parent_bot(self) -> Cq.Assembly:
|
||||||
|
return self.parent(top=False)
|
||||||
|
@target(name="parent-top")
|
||||||
|
def parent_top(self) -> Cq.Assembly:
|
||||||
|
return self.parent(top=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def child_height(self) -> float:
|
def child_height(self) -> float:
|
||||||
"""
|
"""
|
||||||
|
@ -103,7 +123,7 @@ class ShoulderJoint(Model):
|
||||||
joint = self.torsion_joint
|
joint = self.torsion_joint
|
||||||
return self.height - 2 * joint.total_height + 2 * joint.rider_disk_height
|
return self.height - 2 * joint.total_height + 2 * joint.rider_disk_height
|
||||||
|
|
||||||
@target(name="shoulder-joint/child")
|
@target(name="child")
|
||||||
def child(self) -> Cq.Assembly:
|
def child(self) -> Cq.Assembly:
|
||||||
"""
|
"""
|
||||||
Creates the top/bottom shoulder child joint
|
Creates the top/bottom shoulder child joint
|
||||||
|
@ -165,14 +185,17 @@ class ShoulderJoint(Model):
|
||||||
centered=(True, True, False),
|
centered=(True, True, False),
|
||||||
combine='cut')
|
combine='cut')
|
||||||
)
|
)
|
||||||
|
theta = self.torsion_joint.spring.angle_neutral - self.torsion_joint.rider_slot_span
|
||||||
loc_rotate = Cq.Location((0, 0, 0), (1, 0, 0), 180)
|
loc_rotate = Cq.Location((0, 0, 0), (1, 0, 0), 180)
|
||||||
|
loc_axis_rotate_bot = Cq.Location((0, 0, 0), (0, 0, 1), self.axis_rotate_bot)
|
||||||
|
loc_axis_rotate_top = Cq.Location((0, 0, 0), (0, 0, 1), self.axis_rotate_top)
|
||||||
result = (
|
result = (
|
||||||
Cq.Assembly()
|
Cq.Assembly()
|
||||||
.add(core, name="core", loc=Cq.Location())
|
.add(core, name="core", loc=Cq.Location())
|
||||||
.add(joint.rider(rider_slot_begin=-90, reverse_directrix_label=True), name="rider_top",
|
.add(joint.rider(rider_slot_begin=-90, reverse_directrix_label=True), name="rider_top",
|
||||||
loc=Cq.Location((0, 0, dh), (0, 0, 1), -90))
|
loc=loc_axis_rotate_top * Cq.Location((0, 0, dh), (0, 0, 1), -90) * Cq.Location((0, 0, 0), (0, 0, 1), theta))
|
||||||
.add(joint.rider(rider_slot_begin=180), name="rider_bot",
|
.add(joint.rider(rider_slot_begin=180), name="rider_bot",
|
||||||
loc=Cq.Location((0, 0, -dh), (0, 0, 1), -90) * loc_rotate)
|
loc=loc_axis_rotate_bot * Cq.Location((0, 0, -dh), (0, 0, 1), -90) * loc_rotate)
|
||||||
.add(lip, name="lip_top",
|
.add(lip, name="lip_top",
|
||||||
loc=Cq.Location((0, 0, dh)))
|
loc=Cq.Location((0, 0, dh)))
|
||||||
.add(lip, name="lip_bot",
|
.add(lip, name="lip_bot",
|
||||||
|
@ -181,10 +204,8 @@ class ShoulderJoint(Model):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@assembly()
|
@assembly()
|
||||||
def assembly(self,
|
def assembly(self, deflection: float = 0) -> Cq.Assembly:
|
||||||
wing_root_wall_thickness: float = 25.4/16,
|
directrix = self.directrix_id
|
||||||
) -> Cq.Assembly:
|
|
||||||
directrix = 0
|
|
||||||
mat = Material.RESIN_TRANSPERENT
|
mat = Material.RESIN_TRANSPERENT
|
||||||
mat_spring = Material.STEEL_SPRING
|
mat_spring = Material.STEEL_SPRING
|
||||||
result = (
|
result = (
|
||||||
|
@ -192,27 +213,29 @@ class ShoulderJoint(Model):
|
||||||
.addS(self.child(), name="child",
|
.addS(self.child(), name="child",
|
||||||
role=Role.CHILD, material=mat)
|
role=Role.CHILD, material=mat)
|
||||||
.constrain("child/core", "Fixed")
|
.constrain("child/core", "Fixed")
|
||||||
.addS(self.torsion_joint.spring.generate(), name="spring_top",
|
.addS(self.torsion_joint.spring.generate(deflection=-deflection), name="spring_top",
|
||||||
role=Role.DAMPING, material=mat_spring)
|
role=Role.DAMPING, material=mat_spring)
|
||||||
.addS(self.parent(wing_root_wall_thickness),
|
.addS(self.parent_top(),
|
||||||
name="parent_top",
|
name="parent_top",
|
||||||
role=Role.PARENT, material=mat)
|
role=Role.PARENT, material=mat)
|
||||||
.addS(self.torsion_joint.spring.generate(), name="spring_bot",
|
.addS(self.torsion_joint.spring.generate(deflection=deflection), name="spring_bot",
|
||||||
role=Role.DAMPING, material=mat_spring)
|
role=Role.DAMPING, material=mat_spring)
|
||||||
.addS(self.parent(wing_root_wall_thickness),
|
.addS(self.parent_bot(),
|
||||||
name="parent_bot",
|
name="parent_bot",
|
||||||
role=Role.PARENT, material=mat)
|
role=Role.PARENT, material=mat)
|
||||||
)
|
)
|
||||||
TorsionJoint.add_constraints(result,
|
TorsionJoint.add_constraints(
|
||||||
rider="child/rider_top",
|
result,
|
||||||
track="parent_top/track",
|
rider="child/rider_top",
|
||||||
spring="spring_top",
|
track="parent_top/track",
|
||||||
directrix=directrix)
|
spring="spring_top",
|
||||||
TorsionJoint.add_constraints(result,
|
directrix=directrix)
|
||||||
rider="child/rider_bot",
|
TorsionJoint.add_constraints(
|
||||||
track="parent_bot/track",
|
result,
|
||||||
spring="spring_bot",
|
rider="child/rider_bot",
|
||||||
directrix=directrix)
|
track="parent_bot/track",
|
||||||
|
spring="spring_bot",
|
||||||
|
directrix=directrix)
|
||||||
return result.solve()
|
return result.solve()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,6 @@ class WingProfile(Model):
|
||||||
spacer_thickness: float = 25.4 / 8
|
spacer_thickness: float = 25.4 / 8
|
||||||
|
|
||||||
shoulder_joint: ShoulderJoint = field(default_factory=lambda: ShoulderJoint(
|
shoulder_joint: ShoulderJoint = field(default_factory=lambda: ShoulderJoint(
|
||||||
height=60.0,
|
|
||||||
))
|
))
|
||||||
shoulder_width: float = 30.0
|
shoulder_width: float = 30.0
|
||||||
shoulder_tip_x: float = -200.0
|
shoulder_tip_x: float = -200.0
|
||||||
|
@ -94,6 +93,16 @@ class WingProfile(Model):
|
||||||
self.wrist_s = math.sin(self.wrist_theta)
|
self.wrist_s = math.sin(self.wrist_theta)
|
||||||
self.wrist_top_x, self.wrist_top_y = self.wrist_to_abs(0, self.wrist_height)
|
self.wrist_top_x, self.wrist_top_y = self.wrist_to_abs(0, self.wrist_height)
|
||||||
|
|
||||||
|
@submodel(name="shoulder-joint")
|
||||||
|
def submodel_shoulder_joint(self) -> Model:
|
||||||
|
return self.shoulder_joint
|
||||||
|
@submodel(name="elbow-joint")
|
||||||
|
def submodel_elbow_joint(self) -> Model:
|
||||||
|
return self.elbow_joint
|
||||||
|
@submodel(name="wrist-joint")
|
||||||
|
def submodel_wrist_joint(self) -> Model:
|
||||||
|
return self.wrist_joint
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def root_height(self) -> float:
|
def root_height(self) -> float:
|
||||||
return self.shoulder_joint.height
|
return self.shoulder_joint.height
|
||||||
|
@ -634,9 +643,9 @@ class WingProfile(Model):
|
||||||
(
|
(
|
||||||
result
|
result
|
||||||
.constrain("s0/shoulder?conn_top0", "shoulder/parent_top/lip?conn0", "Plane")
|
.constrain("s0/shoulder?conn_top0", "shoulder/parent_top/lip?conn0", "Plane")
|
||||||
.constrain("s0/shoulder?conn_top1", "shoulder/parent_top/lip?conn1", "Plane")
|
#.constrain("s0/shoulder?conn_top1", "shoulder/parent_top/lip?conn1", "Plane")
|
||||||
.constrain("s0/shoulder?conn_bot0", "shoulder/parent_bot/lip?conn0", "Plane")
|
.constrain("s0/shoulder?conn_bot0", "shoulder/parent_bot/lip?conn0", "Plane")
|
||||||
.constrain("s0/shoulder?conn_bot1", "shoulder/parent_bot/lip?conn1", "Plane")
|
#.constrain("s0/shoulder?conn_bot1", "shoulder/parent_bot/lip?conn1", "Plane")
|
||||||
)
|
)
|
||||||
if "s1" in parts:
|
if "s1" in parts:
|
||||||
result.add(self.assembly_s1(), name="s1")
|
result.add(self.assembly_s1(), name="s1")
|
||||||
|
|
Loading…
Reference in New Issue