feat(tdcgal/plane/plane.cpp-tdcgal/plane/objects.py): add circle
This commit is contained in:
parent
9e1373b126
commit
596c000ed6
|
|
@ -11,5 +11,7 @@ typedef CGAL::Simple_cartesian<double> Kernel;
|
|||
|
||||
PYBIND11_MODULE(cgal_bindings, m) {
|
||||
py::module_ plane = m.def_submodule("plane");
|
||||
py::module_ enums = m.def_submodule("enums");
|
||||
init_plane(plane);
|
||||
init_enums(enums);
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
namespace py = pybind11;
|
||||
|
||||
void init_enums(py::module_ &);
|
||||
void init_plane(py::module_ &);
|
||||
|
||||
#endif // CGAL_BINDINGS_H
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
#include <pybind11/operators.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include "../cgal.hpp"
|
||||
#include "cgal.hpp"
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ void init_enums(py::module &m) {
|
|||
.value("ON_BOUNDARY", CGAL::ON_BOUNDARY)
|
||||
.value("ON_UNBOUNDED_SIDE", CGAL::ON_UNBOUNDED_SIDE)
|
||||
|
||||
py::enum_<CGAL::Comparison_result>(m, "Comparison_result")
|
||||
py::enum_<CGAL::Comparison_result>(m, "Comparison_result")
|
||||
.value("SMALLER", CGAL::SMALLER)
|
||||
.value("EQUAL", CGAL::EQUAL)
|
||||
.value("LARGER", CGAL::LARGER);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from math import isclose
|
||||
from typing import Tuple, Union
|
||||
|
||||
from sympy import symbols, solve
|
||||
|
||||
|
|
@ -13,6 +13,7 @@ from ..cgal_bindings.plane import (
|
|||
Line_2,
|
||||
squared_distance,
|
||||
)
|
||||
from ..cgal_bindings.enums import Orientation
|
||||
|
||||
|
||||
def point_maker(pre_point):
|
||||
|
|
@ -148,9 +149,9 @@ class Direction2D(Shape2D, Direction_2):
|
|||
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`中的一个参数。
|
||||
"""
|
||||
|
||||
> 需要提供`vec`, `line`, `ray`, `seg`, `coor`中的任意一个作为初始参数。
|
||||
"""
|
||||
Shape2D.__init__(self, **kwargs)
|
||||
if vec is not None:
|
||||
Direction_2.__init__(self, vec)
|
||||
|
|
@ -164,35 +165,99 @@ class Direction2D(Shape2D, Direction_2):
|
|||
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):
|
||||
pass
|
||||
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,
|
||||
init_from: Dict,
|
||||
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:
|
||||
init_from (Dict): 直线Line2D的初始化参数,可以是以下几种形式:
|
||||
直线Line2D的初始化参数,可以是以下几种形式:
|
||||
- "coefficient": 直线一般式方程的参数A, B, C
|
||||
- "points": 两个端点
|
||||
- "point_and_direction": 点和方向
|
||||
|
|
@ -200,13 +265,6 @@ class Line2D(Shape2D, Line_2):
|
|||
- "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:
|
||||
|
|
@ -260,3 +318,96 @@ class Line2D(Shape2D, Line_2):
|
|||
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"
|
||||
|
||||
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
#include <CGAL/Circle_2.h>
|
||||
#include <CGAL/enum.h>
|
||||
#include <pybind11/operators.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
|
|
@ -8,10 +10,12 @@
|
|||
typedef CGAL::Simple_cartesian<double> Kernel;
|
||||
typedef Kernel::Point_2 Point_2;
|
||||
typedef Kernel::Segment_2 Segment_2;
|
||||
typedef Kernel::Line_2 Line_2;
|
||||
typedef Kernel::Direction_2 Direction_2;
|
||||
typedef Kernel::Vector_2 Vector_2;
|
||||
typedef Kernel::Ray_2 Ray_2;
|
||||
typedef Kernel::Line_2 Line_2;
|
||||
typedef Kernel::Circle_2 Circle_2;
|
||||
typedef Kernel::Triangle_2 Triangle_2;
|
||||
typedef Kernel::FT FT;
|
||||
typedef Kernel::RT RT;
|
||||
|
||||
|
|
@ -22,6 +26,14 @@ template <typename T, typename U> FT squared_distance(const T &a, const U &b) {
|
|||
namespace py = pybind11;
|
||||
|
||||
void init_plane(py::module_ &m) {
|
||||
py::enum_<CGAL::Orientation>(m, "Orientation")
|
||||
.value("LEFT_TURN", CGAL::LEFT_TURN)
|
||||
.value("RIGHT_TURN", CGAL::RIGHT_TURN)
|
||||
.value("COLLINEAR", CGAL::COLLINEAR)
|
||||
.value("CLOCKWISE", CGAL::CLOCKWISE)
|
||||
.value("COUNTERCLOCKWISE", CGAL::COUNTERCLOCKWISE)
|
||||
.value("COPLANAR", CGAL::COPLANAR);
|
||||
|
||||
py::class_<Point_2>(m, "Point_2")
|
||||
.def(py::init<double, double>())
|
||||
.def("x", &Point_2::x)
|
||||
|
|
@ -40,7 +52,7 @@ void init_plane(py::module_ &m) {
|
|||
.def(py::init<Point_2, Point_2>())
|
||||
.def("_source", &Segment_2::source)
|
||||
.def("_target", &Segment_2::target)
|
||||
.def("min", &Segment_2::min, "返回线段的端点中较小的那个")
|
||||
.def("min", &Segment_2hfdaslkasdfkla::min, "返回线段的端点中较小的那个")
|
||||
.def("max", &Segment_2::max, "返回线段的端点中较大的那个")
|
||||
.def("_squared_length", &Segment_2::squared_length, "返回线段的平方长度")
|
||||
.def("opposite", &Segment_2::opposite, "返回反向的线段")
|
||||
|
|
@ -81,10 +93,11 @@ void init_plane(py::module_ &m) {
|
|||
.def(py::init<RT, RT>(), "构造一个经过原点和(x,y)坐标的d")
|
||||
.def("_dx", &Direction_2::dx)
|
||||
.def("_dy", &Direction_2::dy)
|
||||
.def("delta", &Direction_2::delta, "返回d的???, 参数`i`〔筆畫〕需满足`0<=i<=1`")
|
||||
.def("delta", &Direction_2::delta, "返回d的???, 参数`i`需满足`0<=i<=1`")
|
||||
.def("vector", &Direction_2::vector, "返回与d同向的向量")
|
||||
.def("transform", &Direction_2::transform, "返回经过变换后的方向")
|
||||
.def("counterclockwise_in_between", &Direction_2::counterclockwise_in_between,
|
||||
.def("counterclockwise_in_between",
|
||||
&Direction_2::counterclockwise_in_between,
|
||||
"判断方向是否在两向量之间")
|
||||
.def(py::self == py::self)
|
||||
.def(py::self != py::self)
|
||||
|
|
@ -99,35 +112,31 @@ void init_plane(py::module_ &m) {
|
|||
.def(py::init<Segment_2>())
|
||||
.def(py::init<Ray_2>())
|
||||
.def(py::init<Line_2>())
|
||||
.def(py::init([](){
|
||||
return new Vector_2(CGAL::NULL_VECTOR);
|
||||
}))
|
||||
.def(py::init<int, int>())
|
||||
.def(py::init<double, double>())
|
||||
.def(py::init<RT, RT, RT>())
|
||||
.def(py::init<FT, FT>())
|
||||
.def(py::init([]() { return new Vector_2(CGAL::NULL_VECTOR); }))
|
||||
.def(py::init<int, int>(), "参数为x,y,构造值为(x,y)的向量")
|
||||
.def(py::init<double, double>(), "参数为x,y,构造值为(x,y)的向量")
|
||||
.def(py::init<RT, RT, RT>(), "参数分别为x,y,w,构造值为(x/w,y/w)的向量")
|
||||
.def(py::init<FT, FT>(), "参数为x,y,构造值为(x,y)的向量")
|
||||
.def("_x", &Vector_2::x)
|
||||
.def("_y", &Vector_2::y)
|
||||
.def("_hx", &Vector_2::hx)
|
||||
.def("_hy", &Vector_2::hy)
|
||||
.def("_hw", &Vector_2::hw)
|
||||
.def("homogeneous", &Vector_2::homogeneous, "返回向量的齐次坐标")
|
||||
.def("cartesian", &Vector_2::cartesian, "返回向量第`i`个笛卡尔坐标, `i`是参数")
|
||||
.def("cartesian_begin", &Vector_2::cartesian_begin, "返回向量的笛卡尔坐标的起始迭代器")
|
||||
.def("cartesian_end", &Vector_2::cartesian_end, "返回向量的笛卡尔坐标的终止迭代器")
|
||||
.def("cartesian", &Vector_2::cartesian,
|
||||
"返回向量第`i`个笛卡尔坐标, `i`是参数")
|
||||
.def("cartesian_begin", &Vector_2::cartesian_begin,
|
||||
"返回向量的笛卡尔坐标的起始迭代器")
|
||||
.def("cartesian_end", &Vector_2::cartesian_end,
|
||||
"返回向量的笛卡尔坐标的终止迭代器")
|
||||
.def("dimension", &Vector_2::dimension, "返回向量的维数")
|
||||
.def("direction", &Vector_2::direction, "返回向量的方向")
|
||||
.def("transform", &Vector_2::transform, "返回经过变换后的向量")
|
||||
// TODO .def("perpendicular", &Vector_2::perpendicular, "返回与向量垂直的向量, 参数`o`")
|
||||
|
||||
.def("perpendicular", &Vector_2::perpendicular,
|
||||
"返回与向量垂直的向量, 参数`o`类型为`Orientation`")
|
||||
.def("squared_length", &Vector_2::squared_length, "返回向量的平方长度")
|
||||
.def("transform", &Vector_2::transform, "返回经过变换后的向量")
|
||||
.def("direction", &Vector_2::direction, "返回向量的方向")
|
||||
.def("to_direction", &Vector_2::to_direction, "返回向量的方向")
|
||||
.def("angle", &Vector_2::angle, "返回向量与x轴的夹角")
|
||||
.def("normalize", &Vector_2::normalize, "返回单位向量")
|
||||
.def("perpendicular", &Vector_2::perpendicular, "返回与向量垂直的向量")
|
||||
.def("transform", &Vector_2::transform, "返回经过变换后的向量")
|
||||
.def(py::self == py::self)
|
||||
.def(py::self != py::self)
|
||||
.def(py::self + py::self)
|
||||
|
|
@ -168,6 +177,34 @@ void init_plane(py::module_ &m) {
|
|||
.def(py::self == py::self)
|
||||
.def(py::self != py::self);
|
||||
|
||||
py::class_<Circle_2>(m, "Circle_2")
|
||||
.def(py::init<Point_2, FT, CGAL::Orientation>())
|
||||
.def(py::init<Point_2, Point_2, Point_2>())
|
||||
.def(py::init<Point_2, Point_2, CGAL::Orientation>())
|
||||
.def(py::init<Point_2, CGAL::Orientation>())
|
||||
.def("_center", &Circle_2::center)
|
||||
.def("_squared_radius", &Circle_2::squared_radius)
|
||||
.def("_orientation", &Circle_2::orientation)
|
||||
.def(py::self == py::self)
|
||||
.def(py::self != py::self)
|
||||
.def("is_degenerate", &Circle_2::is_degenerate, "判断圆是否退化")
|
||||
.def("oriented_side", &Circle_2::oriented_side,
|
||||
"判断点和圆之间的位置关系")
|
||||
.def("bounded_side", &Circle_2::bounded_side, "判断点和圆的边界位置关系")
|
||||
.def("has_on_positive_side", &Circle_2::has_on_positive_side,
|
||||
"判断点是否在圆的正方向上")
|
||||
.def("has_on_negative_side", &Circle_2::has_on_negative_side,
|
||||
"判断点是否在圆的负方向上")
|
||||
.def("has_on_boundary", &Circle_2::has_on_boundary,
|
||||
"判断点是否在圆的边界上")
|
||||
.def("has_on_bounded_side", &Circle_2::has_on_bounded_side,
|
||||
"判断点是否在圆的边界上")
|
||||
.def("has_on_unbounded_side", &Circle_2::has_on_unbounded_side,
|
||||
"判断点是否在圆的内部")
|
||||
.def("opposite", &Circle_2::opposite, "返回反向的圆")
|
||||
.def("orthogonal_transform", &Circle_2::orthogonal_transform)
|
||||
.def("bbox", &Circle_2::bbox, "返回圆的边界框");
|
||||
|
||||
m.def("squared_distance", &squared_distance<Point_2, Point_2>,
|
||||
"返回两个点的平方距离");
|
||||
m.def("squared_distance", &squared_distance<Point_2, Segment_2>,
|
||||
|
|
|
|||
Loading…
Reference in New Issue