from typing import Tuple, Union 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, ) from ..cgal_bindings.enums import Orientation 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() # 返回射线方向 @property def direction(self) -> Direction2D: return self._direction() # 重载`repr`方法,返回Ray2D的字符串表示 def __repr__(self) -> str: return f"Ray2D({self.source}, {self.direction})" # 重载`in`运算符,判断点是否在射线上 def __contains__(self, point: [Tuple[float, float], Point2D]) -> bool: point = point_maker(point) return self.has_on(point) class Direction2D(Shape2D, Direction_2): def __init__( self, vec: Vector2D = None, line: Line2D = None, ray: Ray2D = None, seg: Segment2D = None, coor: Tuple[float, float] = None, **kwargs, ): """`Direction2D`构造函数 Args: vec (Vector2D, optional): _description_. Defaults to None. line (Line2D, optional): _description_. Defaults to None. ray (Ray2D, optional): _description_. Defaults to None. seg (Segment2D, optional): _description_. Defaults to None. coor (Tuple[float, float], optional): _description_. Defaults to None. > 需要提供`vec`, `line`, `ray`, `seg`, `coor`中的任意一个作为初始参数。 """ Shape2D.__init__(self, **kwargs) if vec is not None: Direction_2.__init__(self, vec) elif line is not None: Direction_2.__init__(self, line) elif ray is not None: Direction_2.__init__(self, ray) elif seg is not None: Direction_2.__init__(self, seg) elif coor is not None: Direction_2.__init__(self, coor) else: raise ValueError("Invalid input for Direction2D") @property def dx(self) -> float: return self._dx() @property def dy(self) -> float: return self._dy() # 重载`repr`方法,返回Direction2D的字符串表示 def __repr__(self) -> str: return f"Direction2D({self.dx}, {self.dy})" class Vector2D(Shape2D, Vector_2): def __init__( self, points: Tuple[Tuple[float, float], Tuple[float, float]] = None, seg: Segment2D = None, ray: Ray2D = None, line: Line2D = None, values: Tuple[float, float] = None, homo_values: Tuple[float, float, float] = None, **kwargs, ): """向量Vector2D的初始化方法 Args: points (Tuple[Tuple[float, float], Tuple[float, float]], optional): 两个端点的坐标. Defaults to None. seg (Segment2D, optional): 线段. Defaults to None. ray (Ray2D, optional): 射线. Defaults to None. line (Line2D, optional): 直线. Defaults to None. values (Tuple[float, float], optional): 坐标值. Defaults to None. homo_values (Tuple[float, float, float], optional): 齐次坐标值. Defaults to None. """ Shape2D.__init__(self, **kwargs) if points is not None: point1 = point_maker(points[0]) point2 = point_maker(points[1]) Vector_2.__init__(self, point1, point2) elif seg is not None: Vector_2.__init__(self, seg) elif ray is not None: Vector_2.__init__(self, ray) elif line is not None: Vector_2.__init__(self, line) elif values is not None: Vector_2.__init__(self, *values) elif homo_values is not None: Vector_2.__init__(self, *homo_values) else: Vector_2.__init__(self) def __repr__(self) -> str: return f"Vector2D({self.x}, {self.y})" @property def x(self) -> float: return self._x() @property def y(self) -> float: return self._y() @property def hx(self) -> float: return self._hx() @property def hy(self) -> float: return self._hy() @property def hw(self) -> float: return self._hw() # 直线类 class Line2D(Shape2D, Line_2): def __init__( self, coefficient: Tuple[float, float, float] = None, points: Tuple[Tuple[float, float], Tuple[float, float]] = None, point_and_direction: Tuple[Point2D, Direction2D] = None, point_and_vector: Tuple[Point2D, Vector2D] = None, segment: Segment2D = None, ray: Ray2D = None, **kwargs, ): """直线Line2D的初始化方法 Args: 直线Line2D的初始化参数,可以是以下几种形式: - "coefficient": 直线一般式方程的参数A, B, C - "points": 两个端点 - "point_and_direction": 点和方向 - "point_and_vector": 点和向量 - "segment": 线段 - "ray": 射线 """ 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 class Circle2D(Shape2D, Circle_2): def __init__( self, center_and_radius: Tuple[Union[Point2D, Tuple[float, float]], float] = None, points: Tuple[ Union[Point2D, Tuple[float, float]], Union[Point2D, Tuple[float, float]] ] = None, diameter_and_orientation: Tuple[ Union[Point2D, Tuple[float, float]], Union[Point2D, Tuple[float, float]], Orientation, ] = None, center_and_orientation: Tuple[ Union[Point2D, Tuple[float, float]], Orientation ] = None, **kwargs, ): """ 圆Circle2D的初始化方法 Args: center_and_radius: 圆心和半径, 方向默认为Orientation.COUNTERCLOCKWISE points: 过三个不共线点的圆 diameter_and_orientation: 直径为给定两个端点的线段,方向默认为Orientation.COUNTERCLOCKWISE, 方向不可为Orienatation.COLLINEAR center_and_orientation: 圆心和方向, 通过该方法初始化的圆半径为0, `is_degenerate`方法返回True\ Example: ```python # 圆心和方向 circle1 = Circle2D(center_and_orientation=((0, 0), Orientation.COUNTERCLOCKWISE)) assert circle1.is_degenerate() == True # 过三个不共线点的圆 circle2 = Circle2D(points=((0, 0), (1, 1), (2, 0))) # 直径为给定两个端点的线段,方向默认为Orientation.COUNTERCLOCKWISE circle3 = Circle2D(diameter_and_orientation=((0, 0), (1, 1)), Orientation.COUNTERCLOCKWISE) # 圆心和半径 circle4 = Circle2D(center_and_radius=((0, 0), 1))((0, 0), 1, Orientation.COUNTERCLOCKWISE)) """ Shape2D.__init__(self, **kwargs) if center_and_radius is not None: center = point_maker(center_and_radius[0]) radius = center_and_radius[1] ori = center_and_radius[2] if len(center_and_radius) == 3 else Orientation.COUNTERCLOCKWISE Circle_2.__init__(self, center, radius, ori) elif points is not None: points = [point_maker(p) for p in points] Circle_2.__init__(self, *points) elif diameter_and_orientation is not None: point1 = point_maker(diameter_and_orientation[0][0]) point2 = point_maker(diameter_and_orientation[0][1]) ori = diameter_and_orientation[1] if len(diameter_and_orientation) == 2 else Orientation.COUNTERCLOCKWISE Circle_2.__init__(self, point1, point2, ori) elif center_and_orientation is not None: center = point_maker(center_and_orientation[0]) ori = center_and_orientation[1] if len(center_and_orientation) == 2 else Orientation.COUNTERCLOCKWISE Circle_2.__init__(self, center, ori) else: raise ValueError("Invalid input for Circle2D") self._x, self._y = symbols("x y") self._expression = (self._x - self.center.x) ** 2 + (self._y - self.center.y) ** 2 - self._squared_radius @property def center(self) -> Point2D: return self._center() @property def radius(self) -> float: return self._squared_radius() ** 0.5 @property def orientation(self) -> Orientation: return self._orientation() # 重载`repr`方法,返回Circle2D的字符串表示 def __repr__(self) -> str: return f"Circle2D(center: {self.center}, radius: {self.radius}, orientation: {self.orientation})" # 圆的标准方程 @property def standard_equation(self) -> str: return f"{str(self._expression)} = 0" # 圆的一般方程 @property def general_equation(self) -> str: return f"{str(self._expression.expand())} = 0" # 三角形类 class Triangle2D(Shape2D, Triangle_2): def __init__( self, points: Tuple[ Union[Point2D, Tuple[float, float]], Union[Point2D, Tuple[float, float]], Union[Point2D, Tuple[float, float]], ] = None, **kwargs, ): """ 三角形Triangle2D的初始化方法 Args: points: 三个端点 """ Shape2D.__init__(self, **kwargs) points = [point_maker(p) for p in points] Triangle_2.__init__(self, *points) # 重载`repr`方法,返回Triangle2D的字符串表示 def __repr__(self) -> str: return f"Triangle2D(points: {[self.vertex(i) for i in range(3)]})"