refactor: Use 2d locations for wing tags
This commit is contained in:
parent
6d72749c9b
commit
6c6c17ea07
|
@ -70,7 +70,7 @@ class Parameters(Model):
|
|||
))
|
||||
wing_l3: MW.WingL = field(default_factory=lambda: MW.WingL(
|
||||
name="l3",
|
||||
wrist_angle=0.0,
|
||||
wrist_angle=-0.0,
|
||||
))
|
||||
|
||||
trident: MT.Trident = field(default_factory=lambda: MT.Trident())
|
||||
|
|
|
@ -81,13 +81,9 @@ class WingProfile(Model):
|
|||
role_panel: Role = Role.STRUCTURE
|
||||
|
||||
# Subclass must populate
|
||||
elbow_x: float
|
||||
elbow_y: float
|
||||
elbow_angle: float
|
||||
elbow_bot_loc: Cq.Location
|
||||
elbow_height: float
|
||||
wrist_x: float
|
||||
wrist_y: float
|
||||
wrist_angle: float
|
||||
wrist_bot_loc: Cq.Location
|
||||
wrist_height: float
|
||||
|
||||
flip: bool = False
|
||||
|
@ -95,14 +91,8 @@ class WingProfile(Model):
|
|||
def __post_init__(self):
|
||||
super().__init__(name=self.name)
|
||||
|
||||
self.elbow_theta = math.radians(self.elbow_angle)
|
||||
self.elbow_c = math.cos(self.elbow_theta)
|
||||
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.elbow_top_loc = self.elbow_bot_loc * Cq.Location.from2d(0, self.elbow_height)
|
||||
self.wrist_top_loc = self.wrist_bot_loc * Cq.Location.from2d(0, self.wrist_height)
|
||||
|
||||
self.shoulder_joint.angle_neutral = -self.shoulder_angle_neutral
|
||||
|
||||
|
@ -401,16 +391,6 @@ class WingProfile(Model):
|
|||
"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]]:
|
||||
"""
|
||||
Polygon shape to mask out parts above the elbow
|
||||
|
@ -459,12 +439,12 @@ class WingProfile(Model):
|
|||
]
|
||||
h = self.elbow_height / 2
|
||||
tags_elbow = [
|
||||
("elbow_bot", Cq.Location.from2d(
|
||||
*self.elbow_to_abs(-self.elbow_joint.parent_arm_radius, h - self.elbow_h2),
|
||||
self.elbow_angle + 0)),
|
||||
("elbow_top", Cq.Location.from2d(
|
||||
*self.elbow_to_abs(-self.elbow_joint.parent_arm_radius, h + self.elbow_h2),
|
||||
self.elbow_angle + 0)),
|
||||
("elbow_bot", self.elbow_bot_loc * Cq.Location.from2d(
|
||||
-self.elbow_joint.parent_arm_radius,
|
||||
h - self.elbow_h2)),
|
||||
("elbow_top", self.elbow_bot_loc * Cq.Location.from2d(
|
||||
-self.elbow_joint.parent_arm_radius,
|
||||
h + self.elbow_h2)),
|
||||
]
|
||||
profile = self.profile_s1()
|
||||
tags = tags_shoulder + tags_elbow
|
||||
|
@ -527,21 +507,23 @@ class WingProfile(Model):
|
|||
def surface_s2(self, front: bool = True) -> Cq.Workplane:
|
||||
h = self.elbow_height / 2
|
||||
tags_elbow = [
|
||||
("elbow_bot", Cq.Location.from2d(
|
||||
*self.elbow_to_abs(self.elbow_joint.child_arm_radius, h - self.elbow_h2),
|
||||
self.elbow_angle + 180)),
|
||||
("elbow_top", Cq.Location.from2d(
|
||||
*self.elbow_to_abs(self.elbow_joint.child_arm_radius, h + self.elbow_h2),
|
||||
self.elbow_angle + 180)),
|
||||
("elbow_bot", self.elbow_bot_loc * Cq.Location.from2d(
|
||||
self.elbow_joint.child_arm_radius,
|
||||
h - self.elbow_h2,
|
||||
180)),
|
||||
("elbow_top", self.elbow_bot_loc * Cq.Location.from2d(
|
||||
self.elbow_joint.child_arm_radius,
|
||||
h + self.elbow_h2,
|
||||
180)),
|
||||
]
|
||||
h = self.wrist_height / 2
|
||||
tags_wrist = [
|
||||
("wrist_bot", Cq.Location.from2d(
|
||||
*self.wrist_to_abs(-self.wrist_joint.parent_arm_radius, h - self.wrist_h2),
|
||||
self.wrist_angle)),
|
||||
("wrist_top", Cq.Location.from2d(
|
||||
*self.wrist_to_abs(-self.wrist_joint.parent_arm_radius, h + self.wrist_h2),
|
||||
self.wrist_angle)),
|
||||
("wrist_bot", self.wrist_bot_loc * Cq.Location.from2d(
|
||||
-self.wrist_joint.parent_arm_radius,
|
||||
h - self.wrist_h2)),
|
||||
("wrist_top", self.wrist_bot_loc * Cq.Location.from2d(
|
||||
-self.wrist_joint.parent_arm_radius,
|
||||
h + self.wrist_h2)),
|
||||
]
|
||||
profile = self.profile_s2()
|
||||
tags = tags_elbow + tags_wrist
|
||||
|
@ -597,12 +579,14 @@ class WingProfile(Model):
|
|||
front: bool = True) -> Cq.Workplane:
|
||||
h = self.wrist_height / 2
|
||||
tags = [
|
||||
("wrist_bot", Cq.Location.from2d(
|
||||
*self.wrist_to_abs(self.wrist_joint.child_arm_radius, h - self.wrist_h2),
|
||||
self.wrist_angle + 180)),
|
||||
("wrist_top", Cq.Location.from2d(
|
||||
*self.wrist_to_abs(self.wrist_joint.child_arm_radius, h + self.wrist_h2),
|
||||
self.wrist_angle + 180)),
|
||||
("wrist_bot", self.wrist_bot_loc * Cq.Location.from2d(
|
||||
self.wrist_joint.child_arm_radius,
|
||||
h - self.wrist_h2,
|
||||
180)),
|
||||
("wrist_top", self.wrist_bot_loc * Cq.Location.from2d(
|
||||
self.wrist_joint.child_arm_radius,
|
||||
h + self.wrist_h2,
|
||||
180)),
|
||||
]
|
||||
profile = self.profile_s3()
|
||||
return extrude_with_markers(profile, self.panel_thickness, tags, reverse=front)
|
||||
|
@ -726,45 +710,34 @@ class WingR(WingProfile):
|
|||
Right side wings
|
||||
"""
|
||||
|
||||
elbow_bot_loc: Cq.Location = Cq.Location.from2d(285.0, 5.0, 25.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
|
||||
# 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
|
||||
arrow_height: float = 300
|
||||
arrow_angle: float = 8
|
||||
arrow_angle: float = -8
|
||||
|
||||
# Relative (in wrist coordinate) centre of the ring
|
||||
ring_x: float = 45
|
||||
ring_y: float = 25
|
||||
ring_radius_inner: float = 22
|
||||
ring_rel_loc: Cq.Location = Cq.Location.from2d(45.0, 25.0)
|
||||
ring_radius_inner: float = 22.0
|
||||
|
||||
def __post_init__(self):
|
||||
super().__post_init__()
|
||||
self.arrow_theta = math.radians(self.arrow_angle)
|
||||
self.arrow_x, self.arrow_y = self.wrist_to_abs(0, -self.arrow_height)
|
||||
self.arrow_tip_x = self.arrow_x + (self.arrow_height + self.wrist_height) \
|
||||
* math.sin(self.arrow_theta - self.wrist_theta)
|
||||
self.arrow_tip_y = self.arrow_y + (self.arrow_height + self.wrist_height) \
|
||||
* math.cos(self.arrow_theta - self.wrist_theta)
|
||||
# [[c, s], [-s, c]] * [ring_x, ring_y]
|
||||
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.arrow_angle < 0, "Arrow angle cannot be positive"
|
||||
self.arrow_bot_loc = self.wrist_bot_loc \
|
||||
* Cq.Location.from2d(0, -self.arrow_height)
|
||||
self.arrow_other_loc = self.arrow_bot_loc \
|
||||
* Cq.Location.rot2d(self.arrow_angle) \
|
||||
* Cq.Location.from2d(0, self.arrow_height + self.wrist_height)
|
||||
self.ring_loc = self.wrist_top_loc * self.ring_rel_loc
|
||||
assert self.ring_radius > self.ring_radius_inner
|
||||
|
||||
@property
|
||||
def ring_radius(self) -> float:
|
||||
dx = self.ring_x
|
||||
dy = self.ring_y
|
||||
(dx, dy), _ = self.ring_rel_loc.to2d()
|
||||
return (dx * dx + dy * dy) ** 0.5
|
||||
|
||||
def profile(self) -> Cq.Sketch:
|
||||
|
@ -779,8 +752,8 @@ class WingR(WingProfile):
|
|||
tag="shoulder")
|
||||
.spline([
|
||||
(0, self.shoulder_joint.height),
|
||||
(self.elbow_top_x, self.elbow_top_y),
|
||||
(self.wrist_top_x, self.wrist_top_y),
|
||||
self.elbow_top_loc.to2d_pos(),
|
||||
self.wrist_top_loc.to2d_pos(),
|
||||
],
|
||||
tag="s1_top")
|
||||
#.segment(
|
||||
|
@ -789,31 +762,31 @@ class WingR(WingProfile):
|
|||
# tag="wrist")
|
||||
.spline([
|
||||
(0, 0),
|
||||
(self.elbow_x, self.elbow_y),
|
||||
(self.wrist_x, self.wrist_y),
|
||||
self.elbow_bot_loc.to2d_pos(),
|
||||
self.wrist_bot_loc.to2d_pos(),
|
||||
],
|
||||
tag="s1_bot")
|
||||
)
|
||||
result = (
|
||||
result
|
||||
.segment(
|
||||
(self.wrist_x, self.wrist_y),
|
||||
(self.arrow_x, self.arrow_y)
|
||||
self.wrist_bot_loc.to2d_pos(),
|
||||
self.arrow_bot_loc.to2d_pos(),
|
||||
)
|
||||
.segment(
|
||||
(self.arrow_x, self.arrow_y),
|
||||
(self.arrow_tip_x, self.arrow_tip_y)
|
||||
self.arrow_bot_loc.to2d_pos(),
|
||||
self.arrow_other_loc.to2d_pos(),
|
||||
)
|
||||
.segment(
|
||||
(self.arrow_tip_x, self.arrow_tip_y),
|
||||
(self.wrist_top_x, self.wrist_top_y)
|
||||
self.arrow_other_loc.to2d_pos(),
|
||||
self.wrist_top_loc.to2d_pos(),
|
||||
)
|
||||
)
|
||||
# Carve out the ring
|
||||
result = result.assemble()
|
||||
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_inner, mode='s')
|
||||
.clean()
|
||||
|
@ -822,38 +795,39 @@ class WingR(WingProfile):
|
|||
|
||||
def _mask_elbow(self) -> list[Tuple[float, float]]:
|
||||
l = 200
|
||||
elbow_x, _ = self.elbow_bot_loc.to2d_pos()
|
||||
elbow_top_x, _ = self.elbow_top_loc.to2d_pos()
|
||||
return [
|
||||
(0, -l),
|
||||
(self.elbow_x, -l),
|
||||
(self.elbow_x, self.elbow_y),
|
||||
(self.elbow_top_x, self.elbow_top_y),
|
||||
(self.elbow_top_x, l),
|
||||
(elbow_x, -l),
|
||||
self.elbow_bot_loc.to2d_pos(),
|
||||
self.elbow_top_loc.to2d_pos(),
|
||||
(elbow_top_x, l),
|
||||
(0, l)
|
||||
]
|
||||
|
||||
def _mask_wrist(self) -> list[Tuple[float, float]]:
|
||||
l = 200
|
||||
wrist_x, _ = self.wrist_bot_loc.to2d_pos()
|
||||
_, wrist_top_y = self.wrist_top_loc.to2d_pos()
|
||||
return [
|
||||
(0, -l),
|
||||
(self.wrist_x, -l),
|
||||
(self.wrist_x, self.wrist_y),
|
||||
(self.wrist_top_x, self.wrist_top_y),
|
||||
(wrist_x, -l),
|
||||
self.wrist_bot_loc.to2d_pos(),
|
||||
self.wrist_top_loc.to2d_pos(),
|
||||
#(self.wrist_top_x, self.wrist_top_y),
|
||||
(0, self.wrist_top_y),
|
||||
(0, wrist_top_y),
|
||||
]
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class WingL(WingProfile):
|
||||
|
||||
elbow_x: float = 230.0
|
||||
elbow_y: float = 110.0
|
||||
elbow_angle: float = -10.0
|
||||
elbow_bot_loc: Cq.Location = Cq.Location.from2d(230.0, 110.0, -10.0)
|
||||
elbow_height: float = 80.0
|
||||
|
||||
wrist_x: float = 480.0
|
||||
wrist_y: float = 0.0
|
||||
wrist_angle: float = -45
|
||||
wrist_angle: float = -45.0
|
||||
wrist_bot_loc: Cq.Location = Cq.Location.from2d(480.0, 0.0, -45.0)
|
||||
wrist_height: float = 43.0
|
||||
|
||||
shoulder_bezier_ext: float = 80.0
|
||||
|
@ -866,11 +840,14 @@ class WingL(WingProfile):
|
|||
flip: bool = True
|
||||
|
||||
def __post_init__(self):
|
||||
super().__post_init__()
|
||||
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]:
|
||||
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:
|
||||
result = (
|
||||
|
@ -879,39 +856,29 @@ class WingL(WingProfile):
|
|||
(0,0),
|
||||
(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([
|
||||
(0, 0),
|
||||
(self.shoulder_bezier_ext, 0),
|
||||
self.elbow_to_abs(-self.elbow_bezier_ext, 0),
|
||||
self.elbow_to_abs(0, 0),
|
||||
(self.elbow_bot_loc * Cq.Location.from2d(-self.elbow_bezier_ext, 0)).to2d_pos(),
|
||||
self.elbow_bot_loc.to2d_pos(),
|
||||
])
|
||||
.bezier([
|
||||
(0, 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_to_abs(0, self.elbow_height),
|
||||
(self.elbow_top_loc * Cq.Location.from2d(-self.elbow_bezier_ext, 0)).to2d_pos(),
|
||||
self.elbow_top_loc.to2d_pos(),
|
||||
])
|
||||
.bezier([
|
||||
self.elbow_to_abs(0, 0),
|
||||
self.elbow_to_abs(self.elbow_bezier_ext, 0),
|
||||
self.wrist_to_abs(-self.wrist_bezier_ext, 0),
|
||||
self.wrist_to_abs(0, 0),
|
||||
self.elbow_bot_loc.to2d_pos(),
|
||||
(self.elbow_bot_loc * Cq.Location.from2d(self.elbow_bezier_ext, 0)).to2d_pos(),
|
||||
(self.wrist_bot_loc * Cq.Location.from2d(-self.wrist_bezier_ext, 0)).to2d_pos(),
|
||||
self.wrist_bot_loc.to2d_pos(),
|
||||
])
|
||||
.bezier([
|
||||
self.elbow_to_abs(0, self.elbow_height),
|
||||
self.elbow_to_abs(self.elbow_bezier_ext, self.elbow_height),
|
||||
self.wrist_to_abs(-self.wrist_bezier_ext, self.wrist_height),
|
||||
self.wrist_to_abs(0, self.wrist_height),
|
||||
self.elbow_top_loc.to2d_pos(),
|
||||
(self.elbow_top_loc * Cq.Location.from2d(self.elbow_bezier_ext, 0)).to2d_pos(),
|
||||
(self.wrist_top_loc * Cq.Location.from2d(-self.wrist_bezier_ext, 0)).to2d_pos(),
|
||||
self.wrist_top_loc.to2d_pos(),
|
||||
])
|
||||
)
|
||||
# arrow base positions
|
||||
|
@ -919,13 +886,13 @@ class WingL(WingProfile):
|
|||
result = (
|
||||
result
|
||||
.bezier([
|
||||
self.wrist_to_abs(0, self.wrist_height),
|
||||
self.wrist_to_abs(self.wrist_bezier_ext, self.wrist_height),
|
||||
self.wrist_top_loc.to2d_pos(),
|
||||
(self.wrist_top_loc * Cq.Location.from2d(self.wrist_bezier_ext, 0)).to2d_pos(),
|
||||
self.arrow_to_abs(base_u, base_v),
|
||||
])
|
||||
.bezier([
|
||||
self.wrist_to_abs(0, 0),
|
||||
self.wrist_to_abs(self.wrist_bezier_ext, 0),
|
||||
self.wrist_bot_loc.to2d_pos(),
|
||||
(self.wrist_bot_loc * Cq.Location.from2d(self.wrist_bezier_ext, 0)).to2d_pos(),
|
||||
self.arrow_to_abs(base_u, -base_v),
|
||||
])
|
||||
)
|
||||
|
@ -954,22 +921,27 @@ class WingL(WingProfile):
|
|||
|
||||
def _mask_elbow(self) -> list[Tuple[float, float]]:
|
||||
l = 200
|
||||
elbow_bot_x, _ = self.elbow_bot_loc.to2d_pos()
|
||||
elbow_top_x, _ = self.elbow_top_loc.to2d_pos()
|
||||
return [
|
||||
(0, -l),
|
||||
(self.elbow_x, -l),
|
||||
(self.elbow_x, self.elbow_y),
|
||||
(self.elbow_top_x, self.elbow_top_y),
|
||||
(self.elbow_top_x, l),
|
||||
(elbow_bot_x, -l),
|
||||
self.elbow_bot_loc.to2d_pos(),
|
||||
self.elbow_top_loc.to2d_pos(),
|
||||
(elbow_top_x, l),
|
||||
(0, l)
|
||||
]
|
||||
|
||||
def _mask_wrist(self) -> list[Tuple[float, float]]:
|
||||
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 [
|
||||
(0, -l),
|
||||
(self.elbow_x, self.wrist_y),
|
||||
(self.wrist_x, self.wrist_y),
|
||||
(self.wrist_top_x, self.wrist_top_y),
|
||||
(self.elbow_x, self.elbow_top_y + l),
|
||||
(elbow_bot_x, wrist_bot_y),
|
||||
self.wrist_bot_loc.to2d_pos(),
|
||||
self.wrist_top_loc.to2d_pos(),
|
||||
(elbow_bot_x, elbow_top_y + l),
|
||||
(0, l),
|
||||
]
|
||||
|
|
23
nhf/utils.py
23
nhf/utils.py
|
@ -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)
|
||||
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:
|
||||
(_, _, z), (rx, ry, _) = self.toTuple()
|
||||
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
|
||||
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
|
||||
|
||||
def tagPoint(self, tag: str):
|
||||
|
|
Loading…
Reference in New Issue