geompp 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,136 @@
1
+ cmake_minimum_required(VERSION 3.15)
2
+ project(geompp_python LANGUAGES CXX)
3
+
4
+ set(CMAKE_CXX_STANDARD 20)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+
7
+ set(GEOMPP_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../geompp")
8
+
9
+ # ─── pybind11 ────────────────────────────────────────────────────────────────
10
+ # Prefer an installed copy; fall back to FetchContent.
11
+ find_package(pybind11 2.11 CONFIG QUIET)
12
+ if(NOT pybind11_FOUND)
13
+ message(STATUS "pybind11 not found, fetching via FetchContent...")
14
+ include(FetchContent)
15
+ FetchContent_Declare(
16
+ pybind11
17
+ URL https://github.com/pybind/pybind11/archive/refs/tags/v2.13.6.zip
18
+ DOWNLOAD_EXTRACT_TIMESTAMP TRUE
19
+ )
20
+ FetchContent_MakeAvailable(pybind11)
21
+ endif()
22
+
23
+ # ─── geompp sources (only needed in standalone / pip-install mode) ────────────
24
+ set(GEOMPP_SOURCES
25
+ ${GEOMPP_ROOT}/src/constants.cpp
26
+ ${GEOMPP_ROOT}/src/geompp_log.cpp
27
+ ${GEOMPP_ROOT}/src/utils.cpp
28
+ ${GEOMPP_ROOT}/src/lsv_parser.cpp
29
+ ${GEOMPP_ROOT}/src/point2d.cpp
30
+ ${GEOMPP_ROOT}/src/vector2d.cpp
31
+ ${GEOMPP_ROOT}/src/line2d.cpp
32
+ ${GEOMPP_ROOT}/src/ray2d.cpp
33
+ ${GEOMPP_ROOT}/src/line_segment2d.cpp
34
+ ${GEOMPP_ROOT}/src/polyline2d.cpp
35
+ ${GEOMPP_ROOT}/src/triangle2d.cpp
36
+ ${GEOMPP_ROOT}/src/polygon2d.cpp
37
+ ${GEOMPP_ROOT}/src/bbox2d.cpp
38
+ ${GEOMPP_ROOT}/src/point3d.cpp
39
+ ${GEOMPP_ROOT}/src/vector3d.cpp
40
+ ${GEOMPP_ROOT}/src/line3d.cpp
41
+ ${GEOMPP_ROOT}/src/line_segment3d.cpp
42
+ ${GEOMPP_ROOT}/src/polyline3d.cpp
43
+ ${GEOMPP_ROOT}/src/ray3d.cpp
44
+ ${GEOMPP_ROOT}/src/plane.cpp
45
+ ${GEOMPP_ROOT}/src/triangle3d.cpp
46
+ ${GEOMPP_ROOT}/src/polygon3d.cpp
47
+ ${GEOMPP_ROOT}/src/bbox3d.cpp
48
+ )
49
+
50
+ # ─── Binding sources (one file per class) ─────────────────────────────────────
51
+ set(BINDING_SOURCES
52
+ src/bindings.cpp
53
+ src/bind_precision.cpp
54
+ src/bind_point2d.cpp
55
+ src/bind_point3d.cpp
56
+ src/bind_vector2d.cpp
57
+ src/bind_vector3d.cpp
58
+ src/bind_line_segment2d.cpp
59
+ src/bind_line_segment3d.cpp
60
+ src/bind_line2d.cpp
61
+ src/bind_line3d.cpp
62
+ src/bind_ray2d.cpp
63
+ src/bind_ray3d.cpp
64
+ src/bind_polygon2d.cpp
65
+ src/bind_polygon3d.cpp
66
+ src/bind_polyline2d.cpp
67
+ src/bind_polyline3d.cpp
68
+ src/bind_triangle2d.cpp
69
+ src/bind_triangle3d.cpp
70
+ src/bind_bbox2d.cpp
71
+ src/bind_bbox3d.cpp
72
+ src/bind_plane.cpp
73
+ src/bind_lvsparser.cpp
74
+ src/bind_free_functions.cpp
75
+ )
76
+
77
+ # ─── Python extension module ─────────────────────────────────────────────────
78
+ # Two modes:
79
+ #
80
+ # • Parent-CMake mode (-DBUILD_PYTHON=ON from root):
81
+ # The `geompp` static-library target already exists. Link against it so
82
+ # we don't recompile 23 source files and glog a second time.
83
+ #
84
+ # • Standalone mode (pip install ./geompp_python):
85
+ # No parent target. Compile geompp sources directly into the extension
86
+ # and fetch glog ourselves.
87
+ #
88
+ if(TARGET geompp)
89
+ # ── parent mode ──────────────────────────────────────────────────────────
90
+ message(STATUS "geompp_python: linking against existing `geompp` target")
91
+
92
+ pybind11_add_module(_geompp NO_EXTRAS ${BINDING_SOURCES})
93
+ target_include_directories(_geompp PRIVATE ${GEOMPP_ROOT}/include src)
94
+ # glog is transitively pulled in through geompp's PUBLIC link deps
95
+ target_link_libraries(_geompp PRIVATE geompp)
96
+
97
+ else()
98
+ # ── standalone mode ───────────────────────────────────────────────────────
99
+ message(STATUS "geompp_python: standalone build — compiling geompp sources into extension")
100
+
101
+ if(NOT TARGET glog::glog)
102
+ include(FetchContent)
103
+ FetchContent_Declare(
104
+ glog
105
+ URL https://github.com/google/glog/archive/refs/tags/v0.7.1.zip
106
+ DOWNLOAD_EXTRACT_TIMESTAMP TRUE
107
+ )
108
+ set(WITH_GFLAGS OFF CACHE BOOL "" FORCE)
109
+ set(WITH_GTEST OFF CACHE BOOL "" FORCE)
110
+ set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
111
+ FetchContent_MakeAvailable(glog)
112
+ endif()
113
+
114
+ pybind11_add_module(_geompp NO_EXTRAS ${BINDING_SOURCES} ${GEOMPP_SOURCES})
115
+ target_include_directories(_geompp PRIVATE ${GEOMPP_ROOT}/include src)
116
+ target_link_libraries(_geompp PRIVATE glog::glog)
117
+
118
+ endif()
119
+
120
+ # Windows: suppress min/max macros from <windows.h>
121
+ if(WIN32)
122
+ target_compile_definitions(_geompp PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN)
123
+ endif()
124
+
125
+ # ─── Post-build: copy extension into package directory ───────────────────────
126
+ add_custom_command(TARGET _geompp POST_BUILD
127
+ COMMAND ${CMAKE_COMMAND} -E copy
128
+ $<TARGET_FILE:_geompp>
129
+ "${CMAKE_CURRENT_SOURCE_DIR}/geompp/$<TARGET_FILE_NAME:_geompp>"
130
+ COMMENT "Copying _geompp into geompp_python/geompp/"
131
+ )
132
+
133
+ # ─── Install ─────────────────────────────────────────────────────────────────
134
+ # pip/scikit-build-core: installs _geompp.so|pyd into the `geompp` package dir.
135
+ # Root-CMake: installs alongside the rest of the project if `cmake --install` is run.
136
+ install(TARGETS _geompp DESTINATION geompp)
geompp-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.2
2
+ Name: geompp
3
+ Version: 0.1.1
4
+ Summary: Python bindings for geompp — a C++ 2D/3D geometry library
5
+ Keywords: geometry,2d,3d,math,computational-geometry,cad
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: C++
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: Microsoft :: Windows
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Project-URL: Homepage, https://github.com/amastrobera/geompp
16
+ Project-URL: Repository, https://github.com/amastrobera/geompp
17
+ Requires-Python: >=3.8
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=7; extra == "dev"
20
+ Description-Content-Type: text/markdown
21
+
22
+ [← back](../README.md)
23
+
24
+ # geompp
25
+
26
+ Python bindings for [geompp](https://github.com/amastrobera/geompp) — a C++ 2D/3D geometry library.
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ pip install geompp
32
+ ```
33
+
34
+ Pre-built wheels are available for:
35
+
36
+ | Platform | Python versions |
37
+ |---|---|
38
+ | Linux x86_64 | 3.8 · 3.9 · 3.10 · 3.11 · 3.12 |
39
+ | Windows x64 | 3.8 · 3.9 · 3.10 · 3.11 · 3.12 |
40
+
41
+ If your platform or Python version is not in the table above, pip will compile
42
+ from source — you will need CMake ≥ 3.15 and a C++20-capable compiler.
43
+
44
+ ## Quick start
45
+
46
+ ```python
47
+ import geompp
48
+
49
+ # Points & vectors
50
+ p = geompp.Point2D(1.0, 2.0)
51
+ v = geompp.Vector2D(3.0, 0.0)
52
+ q = p + v # Point2D(4, 2)
53
+ diff = q - p # Vector2D(3, 0)
54
+
55
+ # Lines and intersection
56
+ l1 = geompp.Line2D.make(geompp.Point2D(0,0), geompp.Point2D(1,0))
57
+ l2 = geompp.Line2D.make(geompp.Point2D(0.5,-1), geompp.Point2D(0.5,1))
58
+ hit = l1.intersection(l2) # Point2D(0.5, 0) or None
59
+
60
+ # 3D
61
+ p3 = geompp.Point3D(1, 2, 3)
62
+ plane = geompp.Plane.xy()
63
+ proj = plane.project_onto(p3) # Point3D(1, 2, 0)
64
+
65
+ # Precision
66
+ geompp.set_decimal_precision(geompp.DP_SIX)
67
+
68
+ # File parser
69
+ parser = geompp.LVSParser.open("geometry.lsv")
70
+ while parser.has_next():
71
+ item = parser.next()
72
+ if item is not None:
73
+ print(geompp.LVSParser.to_wkt(item))
74
+ ```
75
+
76
+ ## Classes
77
+
78
+ | 2D | 3D |
79
+ |----|----|
80
+ | Point2D | Point3D |
81
+ | Vector2D | Vector3D |
82
+ | Line2D | Line3D |
83
+ | Ray2D | Ray3D |
84
+ | LineSegment2D | LineSegment3D |
85
+ | Polyline2D | Polyline3D |
86
+ | Triangle2D | Triangle3D |
87
+ | Polygon2D | Polygon3D |
88
+ | BBox2D | BBox3D |
89
+ | | Plane |
90
+
91
+
92
+ ## Build the python bindings from source
93
+
94
+ ### Linux
95
+
96
+ Build
97
+ ```
98
+ mkdir build && cd build
99
+ cmake .. [-DCMAKE_BUILD_TYPE=Release] [-DBUILD_PYTHON=ON]
100
+ make -j6
101
+ ```
102
+
103
+ The `-DBUILD_PYTHON=ON` will build locally the python bindings and run the smoke tests immediately.
104
+
105
+ To try out the library after building it use this
106
+
107
+ ```bash
108
+ # from the main directory geompp
109
+ pip3 install --break-system-packages ./geompp_python
110
+ python3 -c "import geompp as g; print(g.Point3D(1,2,3).to_wkt())"
111
+ # should print POINT (1 2 3)
112
+ ```
113
+
114
+
115
+ ### Windows
116
+
117
+ Build the library
118
+ ```powershell
119
+ mkdir build_win
120
+ cd build_win
121
+ cmake -S .. -G "Visual Studio 18 2026" -A x64
122
+ cmake --build . --target geompp [--config Release] [-DBUILD_PYTHON=ON]
123
+ ```
124
+
125
+ The `-DBUILD_PYTHON=ON` will build locally the python bindings and run the smoke tests immediately.
126
+
geompp-0.1.1/README.md ADDED
@@ -0,0 +1,105 @@
1
+ [← back](../README.md)
2
+
3
+ # geompp
4
+
5
+ Python bindings for [geompp](https://github.com/amastrobera/geompp) — a C++ 2D/3D geometry library.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install geompp
11
+ ```
12
+
13
+ Pre-built wheels are available for:
14
+
15
+ | Platform | Python versions |
16
+ |---|---|
17
+ | Linux x86_64 | 3.8 · 3.9 · 3.10 · 3.11 · 3.12 |
18
+ | Windows x64 | 3.8 · 3.9 · 3.10 · 3.11 · 3.12 |
19
+
20
+ If your platform or Python version is not in the table above, pip will compile
21
+ from source — you will need CMake ≥ 3.15 and a C++20-capable compiler.
22
+
23
+ ## Quick start
24
+
25
+ ```python
26
+ import geompp
27
+
28
+ # Points & vectors
29
+ p = geompp.Point2D(1.0, 2.0)
30
+ v = geompp.Vector2D(3.0, 0.0)
31
+ q = p + v # Point2D(4, 2)
32
+ diff = q - p # Vector2D(3, 0)
33
+
34
+ # Lines and intersection
35
+ l1 = geompp.Line2D.make(geompp.Point2D(0,0), geompp.Point2D(1,0))
36
+ l2 = geompp.Line2D.make(geompp.Point2D(0.5,-1), geompp.Point2D(0.5,1))
37
+ hit = l1.intersection(l2) # Point2D(0.5, 0) or None
38
+
39
+ # 3D
40
+ p3 = geompp.Point3D(1, 2, 3)
41
+ plane = geompp.Plane.xy()
42
+ proj = plane.project_onto(p3) # Point3D(1, 2, 0)
43
+
44
+ # Precision
45
+ geompp.set_decimal_precision(geompp.DP_SIX)
46
+
47
+ # File parser
48
+ parser = geompp.LVSParser.open("geometry.lsv")
49
+ while parser.has_next():
50
+ item = parser.next()
51
+ if item is not None:
52
+ print(geompp.LVSParser.to_wkt(item))
53
+ ```
54
+
55
+ ## Classes
56
+
57
+ | 2D | 3D |
58
+ |----|----|
59
+ | Point2D | Point3D |
60
+ | Vector2D | Vector3D |
61
+ | Line2D | Line3D |
62
+ | Ray2D | Ray3D |
63
+ | LineSegment2D | LineSegment3D |
64
+ | Polyline2D | Polyline3D |
65
+ | Triangle2D | Triangle3D |
66
+ | Polygon2D | Polygon3D |
67
+ | BBox2D | BBox3D |
68
+ | | Plane |
69
+
70
+
71
+ ## Build the python bindings from source
72
+
73
+ ### Linux
74
+
75
+ Build
76
+ ```
77
+ mkdir build && cd build
78
+ cmake .. [-DCMAKE_BUILD_TYPE=Release] [-DBUILD_PYTHON=ON]
79
+ make -j6
80
+ ```
81
+
82
+ The `-DBUILD_PYTHON=ON` will build locally the python bindings and run the smoke tests immediately.
83
+
84
+ To try out the library after building it use this
85
+
86
+ ```bash
87
+ # from the main directory geompp
88
+ pip3 install --break-system-packages ./geompp_python
89
+ python3 -c "import geompp as g; print(g.Point3D(1,2,3).to_wkt())"
90
+ # should print POINT (1 2 3)
91
+ ```
92
+
93
+
94
+ ### Windows
95
+
96
+ Build the library
97
+ ```powershell
98
+ mkdir build_win
99
+ cd build_win
100
+ cmake -S .. -G "Visual Studio 18 2026" -A x64
101
+ cmake --build . --target geompp [--config Release] [-DBUILD_PYTHON=ON]
102
+ ```
103
+
104
+ The `-DBUILD_PYTHON=ON` will build locally the python bindings and run the smoke tests immediately.
105
+
@@ -0,0 +1,91 @@
1
+ """
2
+ geompp — Python bindings for the geompp C++ geometry library.
3
+
4
+ Classes
5
+ -------
6
+ 2D primitives:
7
+ Point2D, Vector2D, Line2D, Ray2D, LineSegment2D,
8
+ Polyline2D, Triangle2D, Polygon2D, BBox2D
9
+
10
+ 3D primitives:
11
+ Point3D, Vector3D, Line3D, Ray3D, LineSegment3D,
12
+ Polyline3D, Triangle3D, Polygon3D, BBox3D, Plane
13
+
14
+ Parser:
15
+ LVSParser
16
+
17
+ Free functions
18
+ --------------
19
+ are_collinear(p1, p2, p3)
20
+ remove_duplicates(points)
21
+ remove_duplicates_from_sorted_list(points)
22
+ remove_collinear(points)
23
+ linear_combination(points, weights)
24
+ average(points)
25
+
26
+ Precision
27
+ ---------
28
+ set_decimal_precision(dp) # e.g. DP_THREE, DP_SIX, DP_NINE
29
+ get_decimal_precision() -> int
30
+ DP_THREE, DP_SIX, DP_NINE
31
+
32
+ Intersection return values
33
+ --------------------------
34
+ ``intersection()`` methods return ``None`` when there is no intersection, or
35
+ the geometry object of the intersection otherwise. For polylines and triangles
36
+ the return can be a single point *or* a list of points / a segment / a polygon
37
+ depending on the geometry involved — use ``isinstance()`` to discriminate.
38
+ """
39
+
40
+ from ._geompp import ( # noqa: F401
41
+ # precision
42
+ DP_THREE,
43
+ DP_SIX,
44
+ DP_NINE,
45
+ set_decimal_precision,
46
+ get_decimal_precision,
47
+ # 2D
48
+ Point2D,
49
+ Vector2D,
50
+ Line2D,
51
+ Ray2D,
52
+ LineSegment2D,
53
+ Polyline2D,
54
+ Triangle2D,
55
+ Polygon2D,
56
+ BBox2D,
57
+ # 3D
58
+ Point3D,
59
+ Vector3D,
60
+ Line3D,
61
+ Ray3D,
62
+ LineSegment3D,
63
+ Polyline3D,
64
+ Triangle3D,
65
+ Polygon3D,
66
+ BBox3D,
67
+ Plane,
68
+ # parser
69
+ LVSParser,
70
+ # free functions
71
+ are_collinear,
72
+ remove_duplicates,
73
+ remove_duplicates_from_sorted_list,
74
+ remove_collinear,
75
+ linear_combination,
76
+ average,
77
+ )
78
+
79
+ __version__ = "1.0.0"
80
+
81
+ __all__ = [
82
+ "DP_THREE", "DP_SIX", "DP_NINE",
83
+ "set_decimal_precision", "get_decimal_precision",
84
+ "Point2D", "Vector2D", "Line2D", "Ray2D", "LineSegment2D",
85
+ "Polyline2D", "Triangle2D", "Polygon2D", "BBox2D",
86
+ "Point3D", "Vector3D", "Line3D", "Ray3D", "LineSegment3D",
87
+ "Polyline3D", "Triangle3D", "Polygon3D", "BBox3D", "Plane",
88
+ "LVSParser",
89
+ "are_collinear", "remove_duplicates", "remove_duplicates_from_sorted_list",
90
+ "remove_collinear", "linear_combination", "average",
91
+ ]
@@ -0,0 +1,66 @@
1
+ [build-system]
2
+ requires = ["scikit-build-core>=0.9", "pybind11>=2.11"]
3
+ build-backend = "scikit_build_core.build"
4
+
5
+ [project]
6
+ name = "geompp"
7
+ version = "0.1.1"
8
+ description = "Python bindings for geompp — a C++ 2D/3D geometry library"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.8"
12
+ keywords = ["geometry", "2d", "3d", "math", "computational-geometry", "cad"]
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: C++",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: Microsoft :: Windows",
18
+ "Operating System :: POSIX :: Linux",
19
+ "Topic :: Scientific/Engineering :: Mathematics",
20
+ "Intended Audience :: Developers",
21
+ "Intended Audience :: Science/Research",
22
+ ]
23
+
24
+ [project.optional-dependencies]
25
+ dev = ["pytest>=7"]
26
+
27
+ [tool.pytest.ini_options]
28
+ addopts = "--import-mode=importlib"
29
+ testpaths = ["tests"]
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/amastrobera/geompp"
33
+ Repository = "https://github.com/amastrobera/geompp"
34
+
35
+ # ─── scikit-build-core configuration ─────────────────────────────────────────
36
+
37
+ [tool.scikit-build]
38
+ cmake.build-type = "Release"
39
+
40
+ # The Python package directory to include in the wheel
41
+ wheel.packages = ["geompp"]
42
+
43
+ # Include geompp C++ sources in the source distribution
44
+ sdist.include = [
45
+ "../geompp/include/**",
46
+ "../geompp/src/**",
47
+ "../geompp/CMakeLists.txt",
48
+ ]
49
+
50
+ [tool.scikit-build.cmake.define]
51
+ # Silence glog's own tests/examples during build
52
+ WITH_GFLAGS = "OFF"
53
+ WITH_GTEST = "OFF"
54
+ BUILD_TESTING = "OFF"
55
+
56
+ # ─── cibuildwheel (optional, for CI wheel building) ──────────────────────────
57
+ [tool.cibuildwheel]
58
+ build = "cp38-* cp39-* cp310-* cp311-* cp312-*"
59
+ skip = "*-win32 *_i686 *-musllinux*"
60
+
61
+ [tool.cibuildwheel.windows]
62
+ archs = ["AMD64"]
63
+
64
+ [tool.cibuildwheel.linux]
65
+ archs = ["x86_64"]
66
+ manylinux-x86_64-image = "manylinux_2_28"
@@ -0,0 +1,17 @@
1
+ #include "bind_helpers.hpp"
2
+
3
+ void bind_bbox2d(py::module_& m) {
4
+ py::class_<geompp::BBox2D>(m, "BBox2D",
5
+ "2D axis-aligned bounding box.")
6
+ .def(py::init<const geompp::Point2D&, const geompp::Point2D&>(), "min"_a, "max"_a)
7
+ .def(py::init<const geompp::LineSegment2D&>(), "segment"_a)
8
+ .def(py::init<const geompp::Polyline2D&>(), "polyline"_a)
9
+ .def(py::init<const geompp::Polygon2D&>(), "polygon"_a)
10
+ .def(py::init<const geompp::Triangle2D&>(), "triangle"_a)
11
+ .def(py::init<const geompp::BBox2D&>())
12
+ .def_property_readonly("min", &geompp::BBox2D::min)
13
+ .def_property_readonly("max", &geompp::BBox2D::max)
14
+ .def("contains", &geompp::BBox2D::Contains, "point"_a)
15
+ BIND_ALMOST_EQUALS(BBox2D)
16
+ .def("__eq__", [](const geompp::BBox2D& a, const geompp::BBox2D& b) { return a == b; });
17
+ }
@@ -0,0 +1,13 @@
1
+ #include "bind_helpers.hpp"
2
+
3
+ void bind_bbox3d(py::module_& m) {
4
+ py::class_<geompp::BBox3D>(m, "BBox3D",
5
+ "3D axis-aligned bounding box.")
6
+ .def(py::init<const geompp::Point3D&, const geompp::Point3D&>(), "min"_a, "max"_a)
7
+ .def(py::init<const geompp::BBox3D&>())
8
+ .def_property_readonly("min", &geompp::BBox3D::min)
9
+ .def_property_readonly("max", &geompp::BBox3D::max)
10
+ .def("contains", &geompp::BBox3D::Contains, "point"_a)
11
+ BIND_ALMOST_EQUALS(BBox3D)
12
+ .def("__eq__", [](const geompp::BBox3D& a, const geompp::BBox3D& b) { return a == b; });
13
+ }
@@ -0,0 +1,57 @@
1
+ #include "bind_helpers.hpp"
2
+
3
+ void bind_free_functions(py::module_& m) {
4
+ m.def("are_collinear",
5
+ [](const geompp::Point2D& p1, const geompp::Point2D& p2, const geompp::Point2D& p3) {
6
+ return geompp::are_collinear(p1, p2, p3);
7
+ }, "p1"_a, "p2"_a, "p3"_a, "True if three 2D points are collinear.");
8
+
9
+ m.def("are_collinear",
10
+ [](const geompp::Point3D& p1, const geompp::Point3D& p2, const geompp::Point3D& p3) {
11
+ return geompp::are_collinear(p1, p2, p3);
12
+ }, "p1"_a, "p2"_a, "p3"_a, "True if three 3D points are collinear.");
13
+
14
+ m.def("remove_duplicates_from_sorted_list",
15
+ [](const std::vector<geompp::Point2D>& pts) {
16
+ return geompp::remove_duplicates_from_sorted_list(pts);
17
+ }, "points"_a, "Remove consecutive duplicate 2D points.");
18
+
19
+ m.def("remove_duplicates_from_sorted_list",
20
+ [](const std::vector<geompp::Point3D>& pts) {
21
+ return geompp::remove_duplicates_from_sorted_list(pts);
22
+ }, "points"_a, "Remove consecutive duplicate 3D points.");
23
+
24
+ m.def("remove_duplicates",
25
+ [](const std::vector<geompp::Point2D>& pts) { return geompp::remove_duplicates(pts); },
26
+ "points"_a, "Remove all duplicate 2D points.");
27
+
28
+ m.def("remove_duplicates",
29
+ [](const std::vector<geompp::Point3D>& pts) { return geompp::remove_duplicates(pts); },
30
+ "points"_a, "Remove all duplicate 3D points.");
31
+
32
+ m.def("remove_collinear",
33
+ [](const std::vector<geompp::Point2D>& pts) { return geompp::remove_collinear(pts); },
34
+ "points"_a, "Remove collinear 2D points.");
35
+
36
+ m.def("remove_collinear",
37
+ [](const std::vector<geompp::Point3D>& pts) { return geompp::remove_collinear(pts); },
38
+ "points"_a, "Remove collinear 3D points.");
39
+
40
+ m.def("linear_combination",
41
+ [](const std::vector<geompp::Point2D>& pts, const std::vector<double>& w) {
42
+ return geompp::linear_combination(pts, w);
43
+ }, "points"_a, "weights"_a, "Weighted linear combination of 2D points.");
44
+
45
+ m.def("linear_combination",
46
+ [](const std::vector<geompp::Point3D>& pts, const std::vector<double>& w) {
47
+ return geompp::linear_combination(pts, w);
48
+ }, "points"_a, "weights"_a, "Weighted linear combination of 3D points.");
49
+
50
+ m.def("average",
51
+ [](const std::vector<geompp::Point2D>& pts) { return geompp::average(pts); },
52
+ "points"_a, "Arithmetic mean of 2D points.");
53
+
54
+ m.def("average",
55
+ [](const std::vector<geompp::Point3D>& pts) { return geompp::average(pts); },
56
+ "points"_a, "Arithmetic mean of 3D points.");
57
+ }
@@ -0,0 +1,69 @@
1
+ #pragma once
2
+
3
+ #include <pybind11/operators.h>
4
+ #include <pybind11/pybind11.h>
5
+ #include <pybind11/stl.h>
6
+
7
+ #include "bbox2d.hpp"
8
+ #include "bbox3d.hpp"
9
+ #include "constants.hpp"
10
+ #include "line2d.hpp"
11
+ #include "line3d.hpp"
12
+ #include "line_segment2d.hpp"
13
+ #include "line_segment3d.hpp"
14
+ #include "lsv_parser.hpp"
15
+ #include "plane.hpp"
16
+ #include "point2d.hpp"
17
+ #include "point3d.hpp"
18
+ #include "polygon2d.hpp"
19
+ #include "polygon3d.hpp"
20
+ #include "polyline2d.hpp"
21
+ #include "polyline3d.hpp"
22
+ #include "ray2d.hpp"
23
+ #include "ray3d.hpp"
24
+ #include "triangle2d.hpp"
25
+ #include "triangle3d.hpp"
26
+ #include "utils.hpp"
27
+ #include "vector2d.hpp"
28
+ #include "vector3d.hpp"
29
+
30
+ namespace py = pybind11;
31
+ using namespace pybind11::literals;
32
+
33
+ // ─────────────────────────────────────────────────────────────────────────────
34
+ // Helper: convert std::optional<std::variant<Ts...>> to py::object.
35
+ //
36
+ // pybind11 3.x variant_caster fails for class-typed variants, so we use
37
+ // std::visit + py::cast explicitly instead of relying on the auto type caster.
38
+ // ─────────────────────────────────────────────────────────────────────────────
39
+ template <typename Variant>
40
+ inline py::object opt_variant_to_py(const std::optional<Variant>& opt) {
41
+ if (!opt.has_value()) return py::none();
42
+ return std::visit([](const auto& v) -> py::object {
43
+ return py::cast(v);
44
+ }, opt.value());
45
+ }
46
+
47
+ // ─────────────────────────────────────────────────────────────────────────────
48
+ // Macro: almost_equals() with and without epsilon
49
+ // ─────────────────────────────────────────────────────────────────────────────
50
+ #define BIND_ALMOST_EQUALS(T) \
51
+ .def("almost_equals", \
52
+ [](const geompp::T& self, const geompp::T& other) { \
53
+ return self.AlmostEquals(other); \
54
+ }, "other"_a) \
55
+ .def("almost_equals", \
56
+ [](const geompp::T& self, const geompp::T& other, double eps) { \
57
+ return self.AlmostEquals(other, eps); \
58
+ }, "other"_a, "epsilon"_a)
59
+
60
+ // ─────────────────────────────────────────────────────────────────────────────
61
+ // Macro: WKT serialization + file I/O
62
+ // ─────────────────────────────────────────────────────────────────────────────
63
+ #define BIND_SERIALIZATION(T) \
64
+ .def("to_wkt", &geompp::T::ToWkt) \
65
+ .def_static("from_wkt", &geompp::T::FromWkt, "wkt"_a) \
66
+ .def("to_file", &geompp::T::ToFile, "path"_a) \
67
+ .def_static("from_file", &geompp::T::FromFile, "path"_a) \
68
+ .def("__repr__", &geompp::T::ToWkt) \
69
+ .def("__str__", &geompp::T::ToWkt)