From 6c6c17ea07cd98570d0bc2300c21fc5d54d5e579 Mon Sep 17 00:00:00 2001 From: Leni Aniva Date: Wed, 17 Jul 2024 21:17:50 -0700 Subject: [PATCH] refactor: Use 2d locations for wing tags --- nhf/touhou/houjuu_nue/__init__.py | 2 +- nhf/touhou/houjuu_nue/wing.py | 238 +++++++++++++----------------- nhf/utils.py | 23 +++ 3 files changed, 129 insertions(+), 134 deletions(-) diff --git a/nhf/touhou/houjuu_nue/__init__.py b/nhf/touhou/houjuu_nue/__init__.py index a9cb819..13df365 100644 --- a/nhf/touhou/houjuu_nue/__init__.py +++ b/nhf/touhou/houjuu_nue/__init__.py @@ -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()) diff --git a/nhf/touhou/houjuu_nue/wing.py b/nhf/touhou/houjuu_nue/wing.py index 2501043..b43b802 100644 --- a/nhf/touhou/houjuu_nue/wing.py +++ b/nhf/touhou/houjuu_nue/wing.py @@ -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), ] diff --git a/nhf/utils.py b/nhf/utils.py index db8c73d..be3912c 100644 --- a/nhf/utils.py +++ b/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):