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