"""角度计算模块 角度定义(∠M2-O-M): - O: 圆心 / 固定旋转中心(第一帧选定,全程不变) - M: 初始尾端参考位置(第一帧选定,全程不变) - M2: 当前帧尾端位置(每帧重新选定) - 角度 = 向量 O→M 与 O→M2 之间的夹角(0-180°) """ import math from typing import Tuple def angle_between_vectors( v1: Tuple[float, float], v2: Tuple[float, float] ) -> float: """计算两个向量之间的夹角(度)""" dot = v1[0] * v2[0] + v1[1] * v2[1] mag1 = math.hypot(v1[0], v1[1]) mag2 = math.hypot(v2[0], v2[1]) if mag1 < 1e-9 or mag2 < 1e-9: return 0.0 cos_theta = max(-1.0, min(1.0, dot / (mag1 * mag2))) return math.degrees(math.acos(cos_theta)) def signed_angle( o: Tuple[float, float], m: Tuple[float, float], m2: Tuple[float, float], ) -> float: """计算带符号的 ∠M2-O-M 角度 Args: o: 圆心(顶点) m: 初始尾端参考位置 m2: 当前尾端位置 Returns: 带符号的角度(度),正=顺时针,负=逆时针 """ v_ref = (m[0] - o[0], m[1] - o[1]) # O → M v_cur = (m2[0] - o[0], m2[1] - o[1]) # O → M2 angle = angle_between_vectors(v_ref, v_cur) # 叉积 Z 分量判断方向 cross_z = v_ref[0] * v_cur[1] - v_ref[1] * v_cur[0] if cross_z < 0: angle = -angle return angle def curvature_from_three_points( o: Tuple[float, float], m: Tuple[float, float], m2: Tuple[float, float], scale: float = 1.0, ) -> float: """由三点计算曲率(Menger curvature) Args: o, m, m2: 三个点 scale: 像素到物理单位的比例(mm/pixel) Returns: 曲率 (1/mm) """ a = math.hypot(o[0] - m[0], o[1] - m[1]) * scale b = math.hypot(m[0] - m2[0], m[1] - m2[1]) * scale c = math.hypot(m2[0] - o[0], m2[1] - o[1]) * scale s = (a + b + c) / 2.0 area = math.sqrt(max(0, s * (s - a) * (s - b) * (s - c))) if area < 1e-12: return 0.0 radius = (a * b * c) / (4.0 * area) return 1.0 / radius if radius > 1e-12 else 0.0