feat(tdcgal/cgal.cpp-tdcgal/plane/objects.py): class Point2D Segment2D Ray2D etc
BREAKING CHANGE:
This commit is contained in:
parent
33c1958581
commit
bbaf3423d7
|
|
@ -4,7 +4,15 @@ from sympy import symbols, solve
|
|||
|
||||
from loguru import logger
|
||||
|
||||
from ..cgal_bindings.plane import Point_2, Segment_2, Line_2, squared_distance
|
||||
from ..cgal_bindings.plane import (
|
||||
Point_2,
|
||||
Segment_2,
|
||||
Vector_2,
|
||||
Ray_2,
|
||||
Direction_2,
|
||||
Line_2,
|
||||
squared_distance,
|
||||
)
|
||||
|
||||
|
||||
def point_maker(pre_point):
|
||||
|
|
@ -21,22 +29,25 @@ 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)
|
||||
|
||||
# 计算点到另一点的距离
|
||||
def distance_to_point(self, point):
|
||||
point = point_maker(point)
|
||||
return (squared_distance(self, point)) ** 0.5
|
||||
|
||||
# 重载==运算符,判断两个Point2D实例是否为同一点
|
||||
def __eq__(self, other):
|
||||
def __eq__(self, other: [Tuple[float, float], Point2D]) -> bool:
|
||||
self.point == point_maker(other)
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Point2D(x: {self.x}, y: {self.y})"
|
||||
|
||||
|
||||
|
|
@ -48,15 +59,16 @@ class Segment2D(Shape2D, Segment_2):
|
|||
super(Segment_2, self).__init__(point1, point2)
|
||||
|
||||
# 计算线段的长度
|
||||
def length(self):
|
||||
return self.squared_length() ** 0.5
|
||||
def length(self) -> float:
|
||||
return self._squared_length() ** 0.5
|
||||
|
||||
# 重载==运算符,判断两个Segment2D实例是否为同一条线段
|
||||
def __eq__(self, other) -> bool:
|
||||
def __eq__(self, other: Segment2D) -> bool:
|
||||
return self == other
|
||||
|
||||
# 重载`in`运算符,判断点是否在线段上
|
||||
def __contains__(self, point) -> bool:
|
||||
def __contains__(self, point: [Tuple[float, float], Point2D]) -> bool:
|
||||
point = point_maker(point)
|
||||
return self.has_on(point)
|
||||
|
||||
# 重载`repr`方法,返回Segment2D的字符串表示
|
||||
|
|
@ -65,251 +77,129 @@ class Segment2D(Shape2D, Segment_2):
|
|||
|
||||
# 线段的起点`source`
|
||||
@property
|
||||
def source(self):
|
||||
def source(self) -> Point2D:
|
||||
return self._source()
|
||||
|
||||
|
||||
# 线段的终点`target`
|
||||
@property
|
||||
def target(self):
|
||||
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 Line(Shape2D, Line_2):
|
||||
def __init__(self, point1, point2, **kwargs):
|
||||
super(Line, self).__init__(**kwargs)
|
||||
self.point1 = point_maker(point1)
|
||||
self.point2 = point_maker(point2)
|
||||
if self.point1 == self.point2:
|
||||
raise ValueError("Invalid input for Line: point1 == point2")
|
||||
self._a = self.point2.y - self.point1.y
|
||||
self._b = self.point1.x - self.point2.x
|
||||
self._c = self.point2.x * self.point1.y - self.point1.x * self.point2.y
|
||||
class Line2D(Shape2D, Line_2):
|
||||
def __init__(
|
||||
self,
|
||||
init_from: Dict,
|
||||
**kwargs,
|
||||
):
|
||||
"""直线Line2D的初始化方法
|
||||
|
||||
if isclose(self.point1.x, self.point2.x):
|
||||
self._slope = float("inf")
|
||||
self._y_intercept = float("inf")
|
||||
else:
|
||||
self._slope = -self._a / self._b
|
||||
self._y_intercept = -self._c / self._b
|
||||
self._x, self._y = symbols("x y")
|
||||
self._expression = self._a * self._x + self._b * self._y + self._c
|
||||
self._length = (
|
||||
(self.point1.x - self.point2.x) ** 2 + (self.point1.y - self.point2.y) ** 2
|
||||
) ** 0.5
|
||||
|
||||
def contains(self, point, allow_on_extended=False):
|
||||
""""""判断点`point`是否在线段上
|
||||
Args:
|
||||
point (Point2D or tuple): 需要判断的点
|
||||
allow_on_extended (bool, optional): 是否允许点在线段的延长线上. Defaults to False.
|
||||
init_from (Dict): 直线Line2D的初始化参数,可以是以下几种形式:
|
||||
- "coefficient": 直线一般式方程的参数A, B, C
|
||||
- "points": 两个端点
|
||||
- "point_and_direction": 点和方向
|
||||
- "point_and_vector": 点和向量
|
||||
- "segment": 线段
|
||||
- "ray": 射线
|
||||
|
||||
Returns:
|
||||
_type_: boolean
|
||||
""""""
|
||||
point = point_maker(point)
|
||||
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)
|
||||
|
||||
f = self.function
|
||||
if allow_on_extended:
|
||||
if isclose(self._b, 0.0):
|
||||
return isclose(point.x, self.point1.x)
|
||||
return isclose(f(point.x), point.y)
|
||||
|
||||
if isclose(self._b, 0.0):
|
||||
return isclose(point.x, self.point1.x) and (
|
||||
point.y >= min(self.point1.y, self.point2.y)
|
||||
and point.y <= max(self.point1.y, self.point2.y)
|
||||
)
|
||||
return isclose(f(point.x), point.y) and (
|
||||
(
|
||||
point.x >= min(self.point1.x, self.point2.x)
|
||||
and point.x <= max(self.point1.x, self.point2.x)
|
||||
)
|
||||
)
|
||||
|
||||
# 通过关键字`in`判断点是否在线段上
|
||||
def __contains__(self, point):
|
||||
return self.contains(point)
|
||||
|
||||
# 计算点`point`到线段的垂足
|
||||
def foot_point(self, point):
|
||||
point = point_maker(point)
|
||||
if self.contains(point, allow_on_extended=True):
|
||||
return point
|
||||
self._x, self._y = symbols("x y")
|
||||
if self.is_degenerate():
|
||||
self._expression = None
|
||||
else:
|
||||
denominator = self._a**2 + self._b**2
|
||||
x = (
|
||||
(self._b**2) * point.x
|
||||
- self._a * self._b * point.y
|
||||
- self._a * self._c
|
||||
) / denominator
|
||||
y = (
|
||||
(self._a**2) * point.y
|
||||
- self._a * self._b * point.x
|
||||
- self._b * self._c
|
||||
) / denominator
|
||||
return Point2D(x, y)
|
||||
self._expression = self.a * self._x + self.b * self._y + self.c
|
||||
|
||||
# 计算点`point`到线段的距离
|
||||
def point_distance(self, point, is_to_line=False):
|
||||
point = point_maker(point)
|
||||
if self.contains(point):
|
||||
return 0
|
||||
else:
|
||||
dis_to_line = (
|
||||
abs(self._expression.evalf(subs={self._x: point.x, self._y: point.y}))
|
||||
/ (self._a**2 + self._b**2) ** 0.5
|
||||
)
|
||||
if is_to_line:
|
||||
return dis_to_line
|
||||
if self.foot_point(point) not in self:
|
||||
return min(
|
||||
self.point1.distance_to_point(point),
|
||||
self.point2.distance_to_point(point),
|
||||
)
|
||||
return dis_to_line
|
||||
|
||||
# 该线段所在直线的纵截距
|
||||
@property
|
||||
def y_intercept(self):
|
||||
return self._y_intercept
|
||||
def a(self) -> float:
|
||||
return self._a()
|
||||
|
||||
# 该线段的斜率
|
||||
@property
|
||||
def slope(self):
|
||||
return self._slope
|
||||
def b(self) -> float:
|
||||
return self._b()
|
||||
|
||||
# 该线段所在直线的函数方程
|
||||
@property
|
||||
def function(self):
|
||||
def f(x):
|
||||
expr = self._expression.subs(self._x, x)
|
||||
res = solve(expr, self._y)
|
||||
if len(res) == 1:
|
||||
return res[0].evalf()
|
||||
else:
|
||||
return None
|
||||
return f
|
||||
def c(self) -> float:
|
||||
return self._c()
|
||||
|
||||
# 该线段的函数式
|
||||
# 返回该直线一般式方程文本,形如`ax+by+c=0`
|
||||
@property
|
||||
def expression(self):
|
||||
return f"{str(self._expression)} = 0"
|
||||
|
||||
@property
|
||||
# 该线段的长度
|
||||
def length(self):
|
||||
return self._length
|
||||
|
||||
""""""
|
||||
# 该线段与另一线段的交点
|
||||
def intersection(self, other):
|
||||
if isinstance(other, Line):
|
||||
if self.slope() == other.slope():
|
||||
return None
|
||||
else:
|
||||
x = (
|
||||
self.slope() * other.point1.x
|
||||
- self.point1.y
|
||||
+ other.slope() * self.point1.x
|
||||
- other.point1.y
|
||||
) / (self.slope() - other.slope())
|
||||
y = self.slope() * (x - self.point1.x) + self.point1.y
|
||||
return Point2D(x, y)
|
||||
else:
|
||||
raise ValueError("Invalid input for Line")
|
||||
""""""
|
||||
|
||||
"""
|
||||
class Rectangle(Shape2D):
|
||||
def __init__(self, point1, point2, **kwargs):
|
||||
super(Rectangle, self).__init__(**kwargs)
|
||||
p1 = point_maker(point1)
|
||||
p2 = point_maker(point2)
|
||||
self.point1 = point_maker(p1)
|
||||
self.point2 = point_maker((point1.x, point2.y))
|
||||
self.point3 = point_maker(point2)
|
||||
self.point4 = point_maker((point2.x, point1.y))
|
||||
|
||||
# 该矩形的四条边
|
||||
def sides(self):
|
||||
return [
|
||||
Line(self.point1, self.point2),
|
||||
Line(self.point2, self.point3),
|
||||
Line(self.point3, self.point4),
|
||||
Line(self.point4, self.point1),
|
||||
]
|
||||
|
||||
# 该矩形的边长
|
||||
def side_lengths(self):
|
||||
return [line.length() for line in self.sides()]
|
||||
|
||||
# 该矩形是否包含点`point`
|
||||
def contains(self, point):
|
||||
point = point_maker(point)
|
||||
lines = self.sides()
|
||||
return isclose(
|
||||
sum([line.point_distance(point) for line in lines]),
|
||||
sum(self.side_lengths()),
|
||||
)
|
||||
|
||||
# 通过关键字`in`判断点是否在矩形内
|
||||
# 通过关键字`in`判断点是否在线段上
|
||||
def __contains__(self, point):
|
||||
return self.contains(point)
|
||||
return self.has_on(point)
|
||||
|
||||
# 该矩形的面积
|
||||
def area(self):
|
||||
return self.side_lengths()[0] * self.side_lengths()[1]
|
||||
# 重载`repr`方法,返回Line2D的字符串表示
|
||||
def __repr__(self) -> str:
|
||||
return f"Line2D(expression is {self.expression})"
|
||||
|
||||
# 该矩形的周长
|
||||
def perimeter(self):
|
||||
return sum(self.side_lengths())
|
||||
|
||||
# 该矩形的中心点
|
||||
@property
|
||||
def center(self):
|
||||
return Point2D(
|
||||
(self.point1.x + self.point3.x) / 2, (self.point1.y + self.point2.y) / 2
|
||||
)
|
||||
|
||||
# 该矩形的四个顶点
|
||||
@property
|
||||
def vertices(self):
|
||||
return [self.point1, self.point2, self.point3, self.point4]
|
||||
|
||||
# 该矩形的外接圆
|
||||
@property
|
||||
def circumcircle(self):
|
||||
pass
|
||||
|
||||
|
||||
class Polygon(Shape2D):
|
||||
def __init__(self, points, **kwargs):
|
||||
super(Polygon, self).__init__(**kwargs)
|
||||
self._points = [point_maker(point) for point in points]
|
||||
self._lines = [Line(self.points[i], self.points[(i + 1) % len(self.points)]) for i in range(len(self.points))]
|
||||
self._sides = [line.length() for line in self.lines]
|
||||
self._perimeter = sum(self.sides)
|
||||
self._center = Point2D(sum([point.x for point in self.points]) / len(self.points), sum([point.y for point in self.points]) / len(self.points))
|
||||
|
||||
@property
|
||||
def points(self):
|
||||
return self._points
|
||||
|
||||
@property
|
||||
def lines(self):
|
||||
return self._lines
|
||||
|
||||
@property
|
||||
def sides(self):
|
||||
return self._sides
|
||||
|
||||
@property
|
||||
def perimeter(self):
|
||||
return self._perimeter
|
||||
|
||||
@property
|
||||
def center(self):
|
||||
return self._center
|
||||
|
||||
"""
|
||||
# 计算点point到此直线的距离
|
||||
def distance(self, point: [Tuple[float, float], Point2D]) -> float:
|
||||
point = point_maker(point)
|
||||
return squared_distance(self, point) ** 0.5
|
||||
|
|
|
|||
Loading…
Reference in New Issue