206 lines
6.0 KiB
Python
206 lines
6.0 KiB
Python
from math import isclose
|
||
|
||
from sympy import symbols, solve
|
||
|
||
from loguru import logger
|
||
|
||
from ..cgal_bindings.plane import (
|
||
Point_2,
|
||
Segment_2,
|
||
Vector_2,
|
||
Ray_2,
|
||
Direction_2,
|
||
Line_2,
|
||
squared_distance,
|
||
)
|
||
|
||
|
||
def point_maker(pre_point):
|
||
if isinstance(pre_point, Point2D):
|
||
return pre_point
|
||
elif isinstance(pre_point, tuple):
|
||
return Point2D(pre_point[0], pre_point[1])
|
||
else:
|
||
print(type(pre_point))
|
||
raise ValueError("Invalid input for Point2D")
|
||
|
||
|
||
class Shape2D(object):
|
||
def __init__(self, **kwargs):
|
||
self.color = kwargs.get("color", "white")
|
||
|
||
# 计算self到另一个Shape2D的距离,返回float
|
||
def distance(
|
||
self, target: [Tuple[float, float], Point2D, Line2D, Segment2D, Ray2D]
|
||
) -> float:
|
||
if isinstance(target, tuple):
|
||
target = point_maker(target)
|
||
return (squared_distance(self, target)) ** 0.5
|
||
|
||
|
||
class Point2D(Shape2D, Point_2):
|
||
def __init__(self, x: float, y: float, **kwargs):
|
||
Point_2.__init__(self, x, y)
|
||
Shape2D.__init__(self, **kwargs)
|
||
|
||
# 重载==运算符,判断两个Point2D实例是否为同一点
|
||
def __eq__(self, other: [Tuple[float, float], Point2D]) -> bool:
|
||
self.point == point_maker(other)
|
||
|
||
def __repr__(self) -> str:
|
||
return f"Point2D(x: {self.x}, y: {self.y})"
|
||
|
||
|
||
class Segment2D(Shape2D, Segment_2):
|
||
def __init__(self, point1, point2, **kwargs):
|
||
point1 = point_maker(point1)
|
||
point2 = point_maker(point2)
|
||
super(Shape2D, self).__init__(**kwargs)
|
||
super(Segment_2, self).__init__(point1, point2)
|
||
|
||
# 计算线段的长度
|
||
def length(self) -> float:
|
||
return self._squared_length() ** 0.5
|
||
|
||
# 重载==运算符,判断两个Segment2D实例是否为同一条线段
|
||
def __eq__(self, other: Segment2D) -> bool:
|
||
return self == other
|
||
|
||
# 重载`in`运算符,判断点是否在线段上
|
||
def __contains__(self, point: [Tuple[float, float], Point2D]) -> bool:
|
||
point = point_maker(point)
|
||
return self.has_on(point)
|
||
|
||
# 重载`repr`方法,返回Segment2D的字符串表示
|
||
def __repr__(self) -> str:
|
||
return f"Segment2D({self.point1}, {self.point2})"
|
||
|
||
# 线段的起点`source`
|
||
@property
|
||
def source(self) -> Point2D:
|
||
return self._source()
|
||
|
||
# 线段的终点`target`
|
||
@property
|
||
def target(self) -> Point2D:
|
||
return self._target()
|
||
|
||
|
||
class Ray2D(Shape2D, Ray_2):
|
||
def __init__(
|
||
self,
|
||
source: [Tuple[float, float], Point2D],
|
||
point: [Tuple[float, float], Point2D] = None,
|
||
vec: Vector2D = None,
|
||
direction: Direction2D = None,
|
||
line: Line2D = None,
|
||
**kwargs,
|
||
):
|
||
source = point_maker(source)
|
||
if point is not None:
|
||
point = point_maker(point)
|
||
Ray_2.__init__(self, source, point)
|
||
elif vec is not None:
|
||
Ray_2.__init__(self, source, vec)
|
||
elif direction is not None:
|
||
Ray_2.__init__(self, source, direction)
|
||
elif line is not None:
|
||
Ray_2.__init__(self, source, line)
|
||
else:
|
||
raise ValueError("Invalid input for Ray2D")
|
||
Shape2D.__init__(self, **kwargs)
|
||
|
||
# 返回射线起点
|
||
@property
|
||
def source(self) -> Point2D:
|
||
return self._source()
|
||
|
||
|
||
|
||
class Direction2D(Shape2D, Direction_2):
|
||
pass
|
||
|
||
|
||
class Vector2D(Shape2D, Vector_2):
|
||
pass
|
||
|
||
|
||
# 直线类
|
||
class Line2D(Shape2D, Line_2):
|
||
def __init__(
|
||
self,
|
||
init_from: Dict,
|
||
**kwargs,
|
||
):
|
||
"""直线Line2D的初始化方法
|
||
|
||
Args:
|
||
init_from (Dict): 直线Line2D的初始化参数,可以是以下几种形式:
|
||
- "coefficient": 直线一般式方程的参数A, B, C
|
||
- "points": 两个端点
|
||
- "point_and_direction": 点和方向
|
||
- "point_and_vector": 点和向量
|
||
- "segment": 线段
|
||
- "ray": 射线
|
||
|
||
example:
|
||
Line2D(init_from={"coefficient": (1, 2, 3)})
|
||
Line2D(init_from={"points": ((1, 2), (3, 4)})
|
||
Line2D(init_from={"point_and_direction": (1, 2), Direction2D(***)})
|
||
Line2D(init_from={"point_and_vector": (1, 2), Vector2D(***)})
|
||
Line2D(init_from={"segment": Segment2D((1, 2), (3, 4))})
|
||
Line2D(init_from={"ray": (1, 2), Ray2D((***))})
|
||
"""
|
||
Shape2D.__init__(self, **kwargs)
|
||
if "points" in init_from:
|
||
point1 = point_maker(point1)
|
||
point2 = point_maker(point2)
|
||
Line_2.__init__(self, point1, point2)
|
||
if "coefficient" in init_from:
|
||
Line_2.__init__(self, a, b, c)
|
||
if "point_and_direction" in init_from:
|
||
Line_2.__init__(self, point1, direction)
|
||
if "point_and_vector" in init_from:
|
||
point1 = point_maker(point1)
|
||
Line_2.__init__(self, point1, vec)
|
||
if "segment" in init_from:
|
||
Line_2.__init__(self, seg)
|
||
if "ray" in init_from:
|
||
Line_2.__init__(self, ray)
|
||
|
||
self._x, self._y = symbols("x y")
|
||
if self.is_degenerate():
|
||
self._expression = None
|
||
else:
|
||
self._expression = self.a * self._x + self.b * self._y + self.c
|
||
|
||
@property
|
||
def a(self) -> float:
|
||
return self._a()
|
||
|
||
@property
|
||
def b(self) -> float:
|
||
return self._b()
|
||
|
||
@property
|
||
def c(self) -> float:
|
||
return self._c()
|
||
|
||
# 返回该直线一般式方程文本,形如`ax+by+c=0`
|
||
@property
|
||
def expression(self):
|
||
return f"{str(self._expression)} = 0"
|
||
|
||
# 通过关键字`in`判断点是否在线段上
|
||
def __contains__(self, point):
|
||
return self.has_on(point)
|
||
|
||
# 重载`repr`方法,返回Line2D的字符串表示
|
||
def __repr__(self) -> str:
|
||
return f"Line2D(expression is {self.expression})"
|
||
|
||
# 计算点point到此直线的距离
|
||
def distance(self, point: [Tuple[float, float], Point2D]) -> float:
|
||
point = point_maker(point)
|
||
return squared_distance(self, point) ** 0.5
|