manimgeo.math.lines 源代码

from .base import close, array2float, Number
from typing import Literal
from logging import getLogger
import numpy as np

logger = getLogger(__name__)

[文档] def check_paramerized_line_range(t: Number, line_type: Literal["LineSegment", "Ray", "InfinityLine"]): """ 检查参数化直线的范围是否符合要求 - `t`: 参数值 - `line_type`: 直线类型,可为 "LineSegment", "Ray", "InfinityLine" """ if line_type not in ["LineSegment", "Ray", "InfinityLine"]: logger.error(f"未知的直线类型: {line_type}") raise ValueError(f"未知的直线类型: {line_type}") # 检查端点,如果接近则认为符合 if close(t, 0) or close(t, 1): return True if line_type == "LineSegment": return 0 <= t <= 1 elif line_type == "Ray": return t >= 0 elif line_type == "InfinityLine": return True else: raise NotImplementedError()
[文档] @array2float def vertical_line_unit_direction(line_start: np.ndarray, line_end: np.ndarray, turn: Literal["clockwise", "counterclockwise"] = "counterclockwise") -> np.ndarray: """ 计算给定直线的垂线方向向量 - `line_start`: 直线起点 - `line_end`: 直线终点 - `turn`: 方向,可为 "clockwise" 或 "counterclockwise" """ from .vectors import unit_direction_vector direction = unit_direction_vector(line_start, line_end) direction[0], direction[1] = -direction[1], direction[0] if turn not in ["clockwise", "counterclockwise"]: logger.error(f"未知的转向类型: {turn}") raise ValueError(f"未知的转向类型: {turn}") return direction if turn == "counterclockwise" else -direction
[文档] @array2float def vertical_point_to_line(point: np.ndarray, line_start: np.ndarray, line_end: np.ndarray): """ 计算给定点到直线的垂足点 - `point`: 要计算垂足点的点 - `line_start`: 直线起点 - `line_end`: 直线终点 """ v = line_end - line_start v_squared_norm = np.dot(v, v) # 如果直线退化为一个点 (l_start == l_end),垂足点就是这个点本身 if close(v_squared_norm, 0): return line_start.copy() # t = ((p - l_start).v) / (v.v) # 这个 t 值表示垂足点在参数化直线 l_start + t * v 上的位置 t = np.dot(point - line_start, v) / v_squared_norm # 垂足点 q = l_start + t * v foot_of_perpendicular = line_start + t * v return foot_of_perpendicular
[文档] @array2float def point_to_line_distance(point: np.ndarray, line_start: np.ndarray, line_end: np.ndarray): """ 计算点到直线的距离 - `point`: 要计算距离的点 - `line_start`: 直线起点 - `line_end`: 直线终点 Returns: `float`, 点到直线的距离 """ direction = line_end - line_start norm_val = float(np.linalg.norm(direction)) # 直线退化为一点 if close(norm_val, 0): return np.linalg.norm(point - line_start) # 点到点的距离 # 向量 AP,从直线上一点到点 P vec_ap = point - line_start # 叉积 cross_product_result = np.cross(direction, vec_ap) cross_product_magnitude = np.linalg.norm(cross_product_result) return cross_product_magnitude / norm_val
[文档] @array2float def get_parameter_t_on_line(point: np.ndarray, line_start: np.ndarray, line_end: np.ndarray) -> float: """ 计算点 p 在参数化直线 l_start + t * (l_end - l_start) 上的参数 t 值,假设点 p 已经在直线上。 - `point`: 要计算的点 - `line_start`: 直线起点 - `line_end`: 直线终点 """ direction = line_end - line_start norm_sq = np.dot(direction, direction) # direction 向量模长的平方 if close(norm_sq, 0): logger.warning(f"无法计算参数 t,直线退化为点 (l_start 和 l_end 重合): {line_start}, {line_end}") raise ValueError(f"无法计算参数 t,直线退化为点 (l_start 和 l_end 重合): {line_start}, {line_end}") vec_ap = point - line_start t = np.dot(vec_ap, direction) / norm_sq return t
[文档] @array2float def is_point_on_line(point: np.ndarray, line_start: np.ndarray, line_end: np.ndarray, line_type: Literal["LineSegment", "Ray", "InfinityLine"] = "InfinityLine") -> bool: """ 判断点是否在线上 - `point`: 要判断的点 - `line_start`: 线起点 - `line_end`: 线终点 - `line_type`: 线类型,可为 "LineSegment", "Ray", "InfinityLine" """ # 首先判断点是否在无限直线上 distance = float(point_to_line_distance(point, line_start, line_end)) if not close(distance, 0): return False # 点不在直线上 # 如果直线退化为点,特殊处理 direction = line_end - line_start norm_val = float(np.linalg.norm(direction)) if close(norm_val, 0): # 直线退化为点,此时只有当 p 恰好是 l_start (或 l_end) 时才算在上面 return close(float(np.linalg.norm(point - line_start)), 0) # 计算参数 t t = get_parameter_t_on_line(point, line_start, line_end) return check_paramerized_line_range(t, line_type)