manimgeo.math.circles 源代码
from .base import close, array2float, Number
from typing import Tuple
from logging import getLogger
import numpy as np
logger = getLogger(__name__)
[文档]
@array2float
def inverse_circle(
origin_circle_center: np.ndarray, origin_circle_radius: Number, origin_circle_normal: np.ndarray,
base_circle_center: np.ndarray, base_circle_r: Number, base_circle_normal: np.ndarray
) -> Tuple[np.ndarray, Number, np.ndarray]:
"""
计算 origin_circle 关于 base_circle 的反演圆
- `origin_circle_center`: 原圆圆心坐标
- `origin_circle_radius`: 原圆半径
- `origin_circle_normal`: 原圆法向量
- `base_circle_center`: 基准圆圆心坐标
- `base_circle_r`: 基准圆半径
- `base_circle_normal`: 基准圆法向量
Returns: `Tuple[np.ndarray, Number, np.ndarray]`, 反演圆圆心坐标、半径和法向量,法向量方向与原圆一致
如果原圆包含基准圆圆心或原圆经过基准圆圆心,则抛出 `ValueError`
"""
# 检查法向量是否共线
cross_product = np.cross(origin_circle_normal, base_circle_normal)
if not close(float(np.linalg.norm(cross_product)), 0):
logger.warning(f"反演的原圆与基圆法向量不共线: {origin_circle_normal}, {base_circle_normal}")
raise ValueError(f"反演的原圆与基圆法向量不共线: {origin_circle_normal}, {base_circle_normal}")
# 确保法向量方向一致
if np.dot(origin_circle_normal, base_circle_normal) < 0:
base_circle_normal = -base_circle_normal
# 计算圆心到基准圆心的向量
center_diff = origin_circle_center - base_circle_center
# 计算圆心到基准圆心的距离
d = np.linalg.norm(center_diff)
# 如果原圆经过基准圆圆心,反演后为直线
if close(float(d), 0):
logger.warning(f"原圆经过基准圆圆心,反演后为直线: {base_circle_center}")
raise ValueError(f"原圆经过基准圆圆心,反演后为直线: {base_circle_center}")
# 基准圆半径的平方
R_squared = base_circle_r ** 2
# 原圆上最近点和最远点到基准圆心的距离
d_min = d - origin_circle_radius
d_max = d + origin_circle_radius
# 如果原圆包含基准圆圆心
if d_min <= 0:
raise ValueError("Origin circle contains the base circle center")
# 反演后的最近点和最远点距离
inv_d_min = R_squared / d_max
inv_d_max = R_squared / d_min
# 反演圆半径和圆心
inv_radius = (inv_d_max - inv_d_min) / 2
inv_center_distance = (inv_d_max + inv_d_min) / 2
# 反演圆心坐标
unit_direction = center_diff / d
inv_center = base_circle_center + inv_center_distance * unit_direction
return inv_center, float(inv_radius), origin_circle_normal
[文档]
@array2float
def inverse_circle_to_line(
origin_circle_center: np.ndarray, origin_circle_radius: Number, origin_circle_normal: np.ndarray,
base_circle_center: np.ndarray, base_circle_r: Number, base_circle_normal: np.ndarray
) -> Tuple[np.ndarray, np.ndarray]:
"""
计算过基准圆圆心的原圆关于基准圆的反演直线
- `origin_circle_center`: 原圆圆心坐标
- `origin_circle_radius`: 原圆半径
- `origin_circle_normal`: 原圆法向量
- `base_circle_center`: 基准圆圆心坐标
- `base_circle_r`: 基准圆半径
- `base_circle_normal`: 基准圆法向量
Returns: `Tuple[np.ndarray, np.ndarray]`, 反演直线上的两点
如果原圆不经过基准圆圆心,则抛出 `ValueError`
"""
# 检查法向量是否共线
cross_product = np.cross(origin_circle_normal, base_circle_normal)
if not close(float(np.linalg.norm(cross_product)), 0):
logger.warning(f"反演的原圆与基圆法向量不共线: {origin_circle_normal}, {base_circle_normal}")
raise ValueError(f"反演的原圆与基圆法向量不共线: {origin_circle_normal}, {base_circle_normal}")
# 确保法向量方向一致
if np.dot(origin_circle_normal, base_circle_normal) < 0:
base_circle_normal = -base_circle_normal
# 计算圆心到基准圆心的向量
center_diff = origin_circle_center - base_circle_center
# 计算圆心到基准圆心的距离
d = np.linalg.norm(center_diff)
# 检查原圆是否经过基准圆圆心
if not close(float(d), float(origin_circle_radius)):
logger.warning(f"原圆不经过基准圆圆心: 圆心距离 {d}, 原圆半径 {origin_circle_radius}")
raise ValueError(f"原圆不经过基准圆圆心: 圆心距离 {d}, 原圆半径 {origin_circle_radius}")
# 基准圆半径的平方
R_squared = base_circle_r ** 2
# 计算原圆上除基准圆心外的另一个特殊点(圆心的对径点)
unit_direction = center_diff / d
opposite_point = origin_circle_center + origin_circle_radius * unit_direction
# 反演该点
opposite_distance = np.linalg.norm(opposite_point - base_circle_center)
inv_opposite_distance = R_squared / opposite_distance
inv_opposite_point = base_circle_center + inv_opposite_distance * (opposite_point - base_circle_center) / opposite_distance
# 反演直线垂直于连接基准圆心和原圆心的直线
# 直线过反演点,方向垂直于unit_direction
# 在原圆平面内找两个垂直于unit_direction的向量
if len(origin_circle_normal) == 3:
# 三维空间
# 找一个与unit_direction和origin_circle_normal都垂直的向量
perpendicular1 = np.cross(unit_direction, origin_circle_normal)
perpendicular1 = perpendicular1 / np.linalg.norm(perpendicular1)
# 找另一个垂直向量(在原圆平面内)
perpendicular2 = np.cross(origin_circle_normal, perpendicular1)
perpendicular2 = perpendicular2 / np.linalg.norm(perpendicular2)
else:
# 二维空间
perpendicular1 = np.array([-unit_direction[1], unit_direction[0]])
perpendicular2 = perpendicular1
# 直线上的两点
line_point1 = inv_opposite_point + perpendicular1
line_point2 = inv_opposite_point - perpendicular1
return line_point1, line_point2