cosplay: Touhou/Houjuu Nue #4

Open
aniva wants to merge 189 commits from touhou/houjuu-nue into main
3 changed files with 129 additions and 134 deletions
Showing only changes of commit 6c6c17ea07 - Show all commits

View File

@ -70,7 +70,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,
)) ))
trident: MT.Trident = field(default_factory=lambda: MT.Trident()) trident: MT.Trident = field(default_factory=lambda: MT.Trident())

View File

@ -81,13 +81,9 @@ class WingProfile(Model):
role_panel: Role = Role.STRUCTURE role_panel: Role = Role.STRUCTURE
# Subclass must populate # Subclass must populate
elbow_x: float elbow_bot_loc: Cq.Location
elbow_y: float
elbow_angle: float
elbow_height: float elbow_height: float
wrist_x: float wrist_bot_loc: Cq.Location
wrist_y: float
wrist_angle: float
wrist_height: float wrist_height: float
flip: bool = False flip: bool = False
@ -95,14 +91,8 @@ class WingProfile(Model):
def __post_init__(self): def __post_init__(self):
super().__init__(name=self.name) super().__init__(name=self.name)
self.elbow_theta = math.radians(self.elbow_angle) self.elbow_top_loc = self.elbow_bot_loc * Cq.Location.from2d(0, self.elbow_height)
self.elbow_c = math.cos(self.elbow_theta) self.wrist_top_loc = self.wrist_bot_loc * Cq.Location.from2d(0, self.wrist_height)
self.elbow_s = math.sin(self.elbow_theta)
self.elbow_top_x, self.elbow_top_y = self.elbow_to_abs(0, self.elbow_height)
self.wrist_theta = math.radians(self.wrist_angle)
self.wrist_c = math.cos(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.shoulder_joint.angle_neutral = -self.shoulder_angle_neutral self.shoulder_joint.angle_neutral = -self.shoulder_angle_neutral
@ -401,16 +391,6 @@ class WingProfile(Model):
"Axis", param=angle) "Axis", param=angle)
) )
def elbow_to_abs(self, x: float, y: float) -> Tuple[float, float]:
elbow_x = self.elbow_x + x * self.elbow_c - y * self.elbow_s
elbow_y = self.elbow_y + x * self.elbow_s + y * self.elbow_c
return elbow_x, elbow_y
def wrist_to_abs(self, x: float, y: float) -> Tuple[float, float]:
wrist_x = self.wrist_x + x * self.wrist_c - y * self.wrist_s
wrist_y = self.wrist_y + x * self.wrist_s + y * self.wrist_c
return wrist_x, wrist_y
def _mask_elbow(self) -> list[Tuple[float, float]]: def _mask_elbow(self) -> list[Tuple[float, float]]:
""" """
Polygon shape to mask out parts above the elbow Polygon shape to mask out parts above the elbow
@ -459,12 +439,12 @@ class WingProfile(Model):
] ]
h = self.elbow_height / 2 h = self.elbow_height / 2
tags_elbow = [ tags_elbow = [
("elbow_bot", Cq.Location.from2d( ("elbow_bot", self.elbow_bot_loc * Cq.Location.from2d(
*self.elbow_to_abs(-self.elbow_joint.parent_arm_radius, h - self.elbow_h2), -self.elbow_joint.parent_arm_radius,
self.elbow_angle + 0)), h - self.elbow_h2)),
("elbow_top", Cq.Location.from2d( ("elbow_top", self.elbow_bot_loc * Cq.Location.from2d(
*self.elbow_to_abs(-self.elbow_joint.parent_arm_radius, h + self.elbow_h2), -self.elbow_joint.parent_arm_radius,
self.elbow_angle + 0)), h + self.elbow_h2)),
] ]
profile = self.profile_s1() profile = self.profile_s1()
tags = tags_shoulder + tags_elbow tags = tags_shoulder + tags_elbow
@ -527,21 +507,23 @@ class WingProfile(Model):
def surface_s2(self, front: bool = True) -> Cq.Workplane: def surface_s2(self, front: bool = True) -> Cq.Workplane:
h = self.elbow_height / 2 h = self.elbow_height / 2
tags_elbow = [ tags_elbow = [
("elbow_bot", Cq.Location.from2d( ("elbow_bot", self.elbow_bot_loc * Cq.Location.from2d(
*self.elbow_to_abs(self.elbow_joint.child_arm_radius, h - self.elbow_h2), self.elbow_joint.child_arm_radius,
self.elbow_angle + 180)), h - self.elbow_h2,
("elbow_top", Cq.Location.from2d( 180)),
*self.elbow_to_abs(self.elbow_joint.child_arm_radius, h + self.elbow_h2), ("elbow_top", self.elbow_bot_loc * Cq.Location.from2d(
self.elbow_angle + 180)), self.elbow_joint.child_arm_radius,
h + self.elbow_h2,
180)),
] ]
h = self.wrist_height / 2 h = self.wrist_height / 2
tags_wrist = [ tags_wrist = [
("wrist_bot", Cq.Location.from2d( ("wrist_bot", self.wrist_bot_loc * Cq.Location.from2d(
*self.wrist_to_abs(-self.wrist_joint.parent_arm_radius, h - self.wrist_h2), -self.wrist_joint.parent_arm_radius,
self.wrist_angle)), h - self.wrist_h2)),
("wrist_top", Cq.Location.from2d( ("wrist_top", self.wrist_bot_loc * Cq.Location.from2d(
*self.wrist_to_abs(-self.wrist_joint.parent_arm_radius, h + self.wrist_h2), -self.wrist_joint.parent_arm_radius,
self.wrist_angle)), h + self.wrist_h2)),
] ]
profile = self.profile_s2() profile = self.profile_s2()
tags = tags_elbow + tags_wrist tags = tags_elbow + tags_wrist
@ -597,12 +579,14 @@ class WingProfile(Model):
front: bool = True) -> Cq.Workplane: front: bool = True) -> Cq.Workplane:
h = self.wrist_height / 2 h = self.wrist_height / 2
tags = [ tags = [
("wrist_bot", Cq.Location.from2d( ("wrist_bot", self.wrist_bot_loc * Cq.Location.from2d(
*self.wrist_to_abs(self.wrist_joint.child_arm_radius, h - self.wrist_h2), self.wrist_joint.child_arm_radius,
self.wrist_angle + 180)), h - self.wrist_h2,
("wrist_top", Cq.Location.from2d( 180)),
*self.wrist_to_abs(self.wrist_joint.child_arm_radius, h + self.wrist_h2), ("wrist_top", self.wrist_bot_loc * Cq.Location.from2d(
self.wrist_angle + 180)), self.wrist_joint.child_arm_radius,
h + self.wrist_h2,
180)),
] ]
profile = self.profile_s3() profile = self.profile_s3()
return extrude_with_markers(profile, self.panel_thickness, tags, reverse=front) return extrude_with_markers(profile, self.panel_thickness, tags, reverse=front)
@ -726,45 +710,34 @@ class WingR(WingProfile):
Right side wings Right side wings
""" """
elbow_bot_loc: Cq.Location = Cq.Location.from2d(285.0, 5.0, 25.0)
elbow_height: float = 111.0 elbow_height: float = 111.0
elbow_x: float = 285.0
elbow_y: float = 5.0
# Tilt of elbow w.r.t. shoulder
elbow_angle: float = 25.0
wrist_bot_loc: Cq.Location = Cq.Location.from2d(403.0, 253.0, 40.0)
wrist_height: float = 60.0 wrist_height: float = 60.0
# Bottom point of the wrist
wrist_x: float = 403.0
wrist_y: float = 253.0
# Tile of wrist w.r.t. shoulder
wrist_angle: float = 40
# Extends from the wrist to the tip of the arrow # Extends from the wrist to the tip of the arrow
arrow_height: float = 300 arrow_height: float = 300
arrow_angle: float = 8 arrow_angle: float = -8
# Relative (in wrist coordinate) centre of the ring # Relative (in wrist coordinate) centre of the ring
ring_x: float = 45 ring_rel_loc: Cq.Location = Cq.Location.from2d(45.0, 25.0)
ring_y: float = 25 ring_radius_inner: float = 22.0
ring_radius_inner: float = 22
def __post_init__(self): def __post_init__(self):
super().__post_init__() super().__post_init__()
self.arrow_theta = math.radians(self.arrow_angle) assert self.arrow_angle < 0, "Arrow angle cannot be positive"
self.arrow_x, self.arrow_y = self.wrist_to_abs(0, -self.arrow_height) self.arrow_bot_loc = self.wrist_bot_loc \
self.arrow_tip_x = self.arrow_x + (self.arrow_height + self.wrist_height) \ * Cq.Location.from2d(0, -self.arrow_height)
* math.sin(self.arrow_theta - self.wrist_theta) self.arrow_other_loc = self.arrow_bot_loc \
self.arrow_tip_y = self.arrow_y + (self.arrow_height + self.wrist_height) \ * Cq.Location.rot2d(self.arrow_angle) \
* math.cos(self.arrow_theta - self.wrist_theta) * Cq.Location.from2d(0, self.arrow_height + self.wrist_height)
# [[c, s], [-s, c]] * [ring_x, ring_y] self.ring_loc = self.wrist_top_loc * self.ring_rel_loc
self.ring_abs_x = self.wrist_top_x + self.wrist_c * self.ring_x - self.wrist_s * self.ring_y
self.ring_abs_y = self.wrist_top_y + self.wrist_s * self.ring_x + self.wrist_c * self.ring_y
assert self.ring_radius > self.ring_radius_inner assert self.ring_radius > self.ring_radius_inner
@property @property
def ring_radius(self) -> float: def ring_radius(self) -> float:
dx = self.ring_x (dx, dy), _ = self.ring_rel_loc.to2d()
dy = self.ring_y
return (dx * dx + dy * dy) ** 0.5 return (dx * dx + dy * dy) ** 0.5
def profile(self) -> Cq.Sketch: def profile(self) -> Cq.Sketch:
@ -779,8 +752,8 @@ class WingR(WingProfile):
tag="shoulder") tag="shoulder")
.spline([ .spline([
(0, self.shoulder_joint.height), (0, self.shoulder_joint.height),
(self.elbow_top_x, self.elbow_top_y), self.elbow_top_loc.to2d_pos(),
(self.wrist_top_x, self.wrist_top_y), self.wrist_top_loc.to2d_pos(),
], ],
tag="s1_top") tag="s1_top")
#.segment( #.segment(
@ -789,31 +762,31 @@ class WingR(WingProfile):
# tag="wrist") # tag="wrist")
.spline([ .spline([
(0, 0), (0, 0),
(self.elbow_x, self.elbow_y), self.elbow_bot_loc.to2d_pos(),
(self.wrist_x, self.wrist_y), self.wrist_bot_loc.to2d_pos(),
], ],
tag="s1_bot") tag="s1_bot")
) )
result = ( result = (
result result
.segment( .segment(
(self.wrist_x, self.wrist_y), self.wrist_bot_loc.to2d_pos(),
(self.arrow_x, self.arrow_y) self.arrow_bot_loc.to2d_pos(),
) )
.segment( .segment(
(self.arrow_x, self.arrow_y), self.arrow_bot_loc.to2d_pos(),
(self.arrow_tip_x, self.arrow_tip_y) self.arrow_other_loc.to2d_pos(),
) )
.segment( .segment(
(self.arrow_tip_x, self.arrow_tip_y), self.arrow_other_loc.to2d_pos(),
(self.wrist_top_x, self.wrist_top_y) self.wrist_top_loc.to2d_pos(),
) )
) )
# Carve out the ring # Carve out the ring
result = result.assemble() result = result.assemble()
result = ( result = (
result result
.push([(self.ring_abs_x, self.ring_abs_y)]) .push([self.ring_loc.to2d_pos()])
.circle(self.ring_radius, mode='a') .circle(self.ring_radius, mode='a')
.circle(self.ring_radius_inner, mode='s') .circle(self.ring_radius_inner, mode='s')
.clean() .clean()
@ -822,38 +795,39 @@ class WingR(WingProfile):
def _mask_elbow(self) -> list[Tuple[float, float]]: def _mask_elbow(self) -> list[Tuple[float, float]]:
l = 200 l = 200
elbow_x, _ = self.elbow_bot_loc.to2d_pos()
elbow_top_x, _ = self.elbow_top_loc.to2d_pos()
return [ return [
(0, -l), (0, -l),
(self.elbow_x, -l), (elbow_x, -l),
(self.elbow_x, self.elbow_y), self.elbow_bot_loc.to2d_pos(),
(self.elbow_top_x, self.elbow_top_y), self.elbow_top_loc.to2d_pos(),
(self.elbow_top_x, l), (elbow_top_x, l),
(0, l) (0, l)
] ]
def _mask_wrist(self) -> list[Tuple[float, float]]: def _mask_wrist(self) -> list[Tuple[float, float]]:
l = 200 l = 200
wrist_x, _ = self.wrist_bot_loc.to2d_pos()
_, wrist_top_y = self.wrist_top_loc.to2d_pos()
return [ return [
(0, -l), (0, -l),
(self.wrist_x, -l), (wrist_x, -l),
(self.wrist_x, self.wrist_y), self.wrist_bot_loc.to2d_pos(),
(self.wrist_top_x, self.wrist_top_y), self.wrist_top_loc.to2d_pos(),
#(self.wrist_top_x, self.wrist_top_y), #(self.wrist_top_x, self.wrist_top_y),
(0, self.wrist_top_y), (0, wrist_top_y),
] ]
@dataclass(kw_only=True) @dataclass(kw_only=True)
class WingL(WingProfile): class WingL(WingProfile):
elbow_x: float = 230.0 elbow_bot_loc: Cq.Location = Cq.Location.from2d(230.0, 110.0, -10.0)
elbow_y: float = 110.0
elbow_angle: float = -10.0
elbow_height: float = 80.0 elbow_height: float = 80.0
wrist_x: float = 480.0 wrist_angle: float = -45.0
wrist_y: float = 0.0 wrist_bot_loc: Cq.Location = Cq.Location.from2d(480.0, 0.0, -45.0)
wrist_angle: float = -45
wrist_height: float = 43.0 wrist_height: float = 43.0
shoulder_bezier_ext: float = 80.0 shoulder_bezier_ext: float = 80.0
@ -866,11 +840,14 @@ class WingL(WingProfile):
flip: bool = True flip: bool = True
def __post_init__(self): def __post_init__(self):
super().__post_init__()
assert self.wrist_height <= self.shoulder_joint.height assert self.wrist_height <= self.shoulder_joint.height
self.wrist_bot_loc = self.wrist_bot_loc.with_angle_2d(self.wrist_angle)
super().__post_init__()
def arrow_to_abs(self, x, y) -> Tuple[float, float]: def arrow_to_abs(self, x, y) -> Tuple[float, float]:
return self.wrist_to_abs(x * self.arrow_length, y * self.arrow_height / 2 + self.wrist_height / 2) rel = Cq.Location.from2d(x * self.arrow_length, y * self.arrow_height / 2 + self.wrist_height / 2)
return (self.wrist_bot_loc * rel).to2d_pos()
def profile(self) -> Cq.Sketch: def profile(self) -> Cq.Sketch:
result = ( result = (
@ -879,39 +856,29 @@ class WingL(WingProfile):
(0,0), (0,0),
(0, self.shoulder_height) (0, self.shoulder_height)
) )
#.spline([
# (0, 0),
# self.elbow_to_abs(0, 0),
# self.wrist_to_abs(0, 0),
#])
#.spline([
# (0, self.shoulder_height),
# self.elbow_to_abs(0, self.elbow_height),
# self.wrist_to_abs(0, self.wrist_height),
#])
.bezier([ .bezier([
(0, 0), (0, 0),
(self.shoulder_bezier_ext, 0), (self.shoulder_bezier_ext, 0),
self.elbow_to_abs(-self.elbow_bezier_ext, 0), (self.elbow_bot_loc * Cq.Location.from2d(-self.elbow_bezier_ext, 0)).to2d_pos(),
self.elbow_to_abs(0, 0), self.elbow_bot_loc.to2d_pos(),
]) ])
.bezier([ .bezier([
(0, self.shoulder_joint.height), (0, self.shoulder_joint.height),
(self.shoulder_bezier_ext, self.shoulder_joint.height), (self.shoulder_bezier_ext, self.shoulder_joint.height),
self.elbow_to_abs(-self.elbow_bezier_ext, self.elbow_height), (self.elbow_top_loc * Cq.Location.from2d(-self.elbow_bezier_ext, 0)).to2d_pos(),
self.elbow_to_abs(0, self.elbow_height), self.elbow_top_loc.to2d_pos(),
]) ])
.bezier([ .bezier([
self.elbow_to_abs(0, 0), self.elbow_bot_loc.to2d_pos(),
self.elbow_to_abs(self.elbow_bezier_ext, 0), (self.elbow_bot_loc * Cq.Location.from2d(self.elbow_bezier_ext, 0)).to2d_pos(),
self.wrist_to_abs(-self.wrist_bezier_ext, 0), (self.wrist_bot_loc * Cq.Location.from2d(-self.wrist_bezier_ext, 0)).to2d_pos(),
self.wrist_to_abs(0, 0), self.wrist_bot_loc.to2d_pos(),
]) ])
.bezier([ .bezier([
self.elbow_to_abs(0, self.elbow_height), self.elbow_top_loc.to2d_pos(),
self.elbow_to_abs(self.elbow_bezier_ext, self.elbow_height), (self.elbow_top_loc * Cq.Location.from2d(self.elbow_bezier_ext, 0)).to2d_pos(),
self.wrist_to_abs(-self.wrist_bezier_ext, self.wrist_height), (self.wrist_top_loc * Cq.Location.from2d(-self.wrist_bezier_ext, 0)).to2d_pos(),
self.wrist_to_abs(0, self.wrist_height), self.wrist_top_loc.to2d_pos(),
]) ])
) )
# arrow base positions # arrow base positions
@ -919,13 +886,13 @@ class WingL(WingProfile):
result = ( result = (
result result
.bezier([ .bezier([
self.wrist_to_abs(0, self.wrist_height), self.wrist_top_loc.to2d_pos(),
self.wrist_to_abs(self.wrist_bezier_ext, self.wrist_height), (self.wrist_top_loc * Cq.Location.from2d(self.wrist_bezier_ext, 0)).to2d_pos(),
self.arrow_to_abs(base_u, base_v), self.arrow_to_abs(base_u, base_v),
]) ])
.bezier([ .bezier([
self.wrist_to_abs(0, 0), self.wrist_bot_loc.to2d_pos(),
self.wrist_to_abs(self.wrist_bezier_ext, 0), (self.wrist_bot_loc * Cq.Location.from2d(self.wrist_bezier_ext, 0)).to2d_pos(),
self.arrow_to_abs(base_u, -base_v), self.arrow_to_abs(base_u, -base_v),
]) ])
) )
@ -954,22 +921,27 @@ class WingL(WingProfile):
def _mask_elbow(self) -> list[Tuple[float, float]]: def _mask_elbow(self) -> list[Tuple[float, float]]:
l = 200 l = 200
elbow_bot_x, _ = self.elbow_bot_loc.to2d_pos()
elbow_top_x, _ = self.elbow_top_loc.to2d_pos()
return [ return [
(0, -l), (0, -l),
(self.elbow_x, -l), (elbow_bot_x, -l),
(self.elbow_x, self.elbow_y), self.elbow_bot_loc.to2d_pos(),
(self.elbow_top_x, self.elbow_top_y), self.elbow_top_loc.to2d_pos(),
(self.elbow_top_x, l), (elbow_top_x, l),
(0, l) (0, l)
] ]
def _mask_wrist(self) -> list[Tuple[float, float]]: def _mask_wrist(self) -> list[Tuple[float, float]]:
l = 200 l = 200
elbow_bot_x, _ = self.elbow_bot_loc.to2d_pos()
_, elbow_top_y = self.elbow_top_loc.to2d_pos()
_, wrist_bot_y = self.wrist_bot_loc.to2d_pos()
return [ return [
(0, -l), (0, -l),
(self.elbow_x, self.wrist_y), (elbow_bot_x, wrist_bot_y),
(self.wrist_x, self.wrist_y), self.wrist_bot_loc.to2d_pos(),
(self.wrist_top_x, self.wrist_top_y), self.wrist_top_loc.to2d_pos(),
(self.elbow_x, self.elbow_top_y + l), (elbow_bot_x, elbow_top_y + l),
(0, l), (0, l),
] ]

View File

@ -43,6 +43,10 @@ def from2d(x: float, y: float, rotate: float=0.0) -> Cq.Location:
return Cq.Location((x, y, 0), (0, 0, 1), rotate) return Cq.Location((x, y, 0), (0, 0, 1), rotate)
Cq.Location.from2d = from2d Cq.Location.from2d = from2d
def rot2d(angle: float) -> Cq.Location:
return Cq.Location((0, 0, 0), (0, 0, 1), angle)
Cq.Location.rot2d = rot2d
def is2d(self: Cq.Location) -> bool: def is2d(self: Cq.Location) -> bool:
(_, _, z), (rx, ry, _) = self.toTuple() (_, _, z), (rx, ry, _) = self.toTuple()
return z == 0 and rx == 0 and ry == 0 return z == 0 and rx == 0 and ry == 0
@ -59,6 +63,25 @@ def to2d(self: Cq.Location) -> Tuple[Tuple[float, float], float]:
return (x, y), rz return (x, y), rz
Cq.Location.to2d = to2d Cq.Location.to2d = to2d
def to2d_pos(self: Cq.Location) -> Tuple[float, float]:
"""
Returns position and angle
"""
(x, y, z), (rx, ry, _) = self.toTuple()
assert z == 0
assert rx == 0
assert ry == 0
return (x, y)
Cq.Location.to2d_pos = to2d_pos
def with_angle_2d(self: Cq.Location, angle: float) -> Tuple[float, float]:
"""
Returns position and angle
"""
x, y = self.to2d_pos()
return Cq.Location.from2d(x, y, angle)
Cq.Location.with_angle_2d = with_angle_2d
### Tags ### Tags
def tagPoint(self, tag: str): def tagPoint(self, tag: str):