toydesigner-cgal/tdcgal/plane/objects.py

206 lines
6.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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