Skip to content

Commit 6f5e3cb

Browse files
authored
Merge pull request #304 from libigl/cubic-bezier-and-roots
Add bindings + tests for cubic Bézier, roots, and new predicates
2 parents 394ec4b + 6f70157 commit 6f5e3cb

23 files changed

Lines changed: 722 additions & 17 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ external
55
bin
66
bin_rel
77
build
8+
build-full
89

910
tutorial/data
1011
tutorial/.ipynb_*

CMakeLists.txt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ FetchContent_Declare(
3636
)
3737
FetchContent_MakeAvailable(nanobind)
3838

39+
# Pin Eigen to 3.4.0. Recent libigl bumped its Eigen dependency to 5.0.1, whose
40+
# stricter ScalarBinaryOpTraits rejects the int64-vs-int comparisons in some
41+
# libigl headers (e.g. unique_edge_map), which the 64-bit-index Python bindings
42+
# instantiate. Declaring Eigen first makes FetchContent use this version instead
43+
# of the one requested by libigl's own recipe.
44+
FetchContent_Declare(
45+
eigen
46+
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
47+
GIT_TAG tags/3.4.0
48+
GIT_SHALLOW TRUE
49+
)
50+
3951
# Download and set up libigl
4052
option(LIBIGL_COPYLEFT_CORE "Build target igl_copyleft::core" ON)
4153
option(LIBIGL_COPYLEFT_CGAL "Build target igl_copyleft::cgal" ON)
@@ -44,10 +56,11 @@ option(LIBIGL_COPYLEFT_TETGEN "Build target igl_copyleft::tetgen" ON)
4456
option(LIBIGL_RESTRICTED_TRIANGLE "Build target igl_restricted::triangle" ON)
4557
option(LIBIGL_SPECTRA "Build igl::spectra bindings" ON)
4658
option(LIBIGL_PREDICATES "Build igl::predicates bindings" ON)
59+
option(LIBIGL_CYCODEBASE "Build igl::cycodebase bindings" ON)
4760
FetchContent_Declare(
4861
libigl
4962
GIT_REPOSITORY https://github.com/libigl/libigl.git
50-
GIT_TAG 678e1fff76815e0c4c5d1f025ee2129181cc7d86
63+
GIT_TAG 477e15a3d566a21f415aa5ee62992b12a836b01b
5164
)
5265
FetchContent_MakeAvailable(libigl)
5366

@@ -190,6 +203,9 @@ endif()
190203
if(LIBIGL_PREDICATES)
191204
pyigl_include("" "predicates")
192205
endif()
206+
if(LIBIGL_CYCODEBASE)
207+
pyigl_include("" "cycodebase")
208+
endif()
193209

194210

195211

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ build-backend = "scikit_build_core.build"
1313

1414
[project]
1515
name = "libigl"
16-
version = "2.6.3.dev3"
16+
version = "2.6.3.dev4"
1717
description = "libigl: A simple C++ geometry processing library"
1818
readme = "README.md"
1919
requires-python = ">=3.8"

src/cubic.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#include "default_types.h"
2+
#include <igl/cubic.h>
3+
#include <nanobind/nanobind.h>
4+
#include <nanobind/eigen/dense.h>
5+
6+
namespace nb = nanobind;
7+
using namespace nb::literals;
8+
9+
namespace pyigl
10+
{
11+
auto cubic(
12+
const nb::DRef<const Eigen::MatrixXN> &C,
13+
const Numeric t)
14+
{
15+
Eigen::MatrixXN P;
16+
igl::cubic(C, t, P);
17+
return P;
18+
}
19+
}
20+
21+
void bind_cubic(nb::module_ &m)
22+
{
23+
m.def("cubic", &pyigl::cubic, "C"_a, "t"_a,
24+
R"(Evaluate a cubic Bézier curve defined by control points C at parameter t.
25+
26+
@param[in] C 4 by dim matrix of control points for a cubic Bézier curve
27+
@param[in] t parameter at which to evaluate the curve
28+
@return P 1 by dim point on the curve C(t))");
29+
}

src/cubic_is_flat.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include "default_types.h"
2+
#include <igl/cubic_is_flat.h>
3+
#include <nanobind/nanobind.h>
4+
#include <nanobind/eigen/dense.h>
5+
6+
namespace nb = nanobind;
7+
using namespace nb::literals;
8+
9+
namespace pyigl
10+
{
11+
bool cubic_is_flat(
12+
const nb::DRef<const Eigen::MatrixXN> &C,
13+
const Numeric squared_distance_bound)
14+
{
15+
return igl::cubic_is_flat(C, squared_distance_bound);
16+
}
17+
}
18+
19+
void bind_cubic_is_flat(nb::module_ &m)
20+
{
21+
m.def("cubic_is_flat", &pyigl::cubic_is_flat, "C"_a, "squared_distance_bound"_a,
22+
R"(Test whether a cubic Bézier curve is flat within a given tolerance.
23+
24+
"Piecewise Linear Approximation of Bézier Curves" [Fischer 2000]. If the test
25+
passes, the curve's maximum squared distance to the chord from its first to its
26+
last control point is less than squared_distance_bound.
27+
28+
@param[in] C 4 by dim matrix of control points for a cubic Bézier curve
29+
@param[in] squared_distance_bound squared distance tolerance
30+
@return True if the curve is flat within the given tolerance)");
31+
}

src/cubic_monomial_bases.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include "default_types.h"
2+
#include <igl/cubic_monomial_bases.h>
3+
#include <nanobind/nanobind.h>
4+
#include <nanobind/eigen/dense.h>
5+
#include <nanobind/stl/tuple.h>
6+
#include <tuple>
7+
8+
namespace nb = nanobind;
9+
using namespace nb::literals;
10+
11+
namespace pyigl
12+
{
13+
auto cubic_monomial_bases(
14+
const nb::DRef<const Eigen::MatrixXN> &C)
15+
{
16+
Eigen::MatrixXN M, D;
17+
// B is a 6-vector of inner products (cubic_monomial_bases calls vector
18+
// methods on it), so it must be a vector type, not a general matrix.
19+
Eigen::VectorXN B;
20+
igl::cubic_monomial_bases(C, M, D, B);
21+
return std::make_tuple(M, D, B);
22+
}
23+
}
24+
25+
void bind_cubic_monomial_bases(nb::module_ &m)
26+
{
27+
m.def("cubic_monomial_bases", &pyigl::cubic_monomial_bases, "C"_a,
28+
R"(Compute monomial basis representations for a cubic Bézier curve.
29+
30+
@param[in] C 4 by dim matrix of control points for a cubic Bézier curve
31+
@return Tuple (M, D, B) where
32+
M 4 by dim matrix of monomial coefficients for C(t)
33+
D 3 by dim matrix of monomial coefficients for dC/dt
34+
B 6-vector of inner products of those basis functions for C(t))");
35+
}

src/cubic_split.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include "default_types.h"
2+
#include <igl/cubic_split.h>
3+
#include <nanobind/nanobind.h>
4+
#include <nanobind/eigen/dense.h>
5+
#include <nanobind/stl/tuple.h>
6+
#include <tuple>
7+
8+
namespace nb = nanobind;
9+
using namespace nb::literals;
10+
11+
namespace pyigl
12+
{
13+
auto cubic_split(
14+
const nb::DRef<const Eigen::MatrixXN> &C,
15+
const Numeric t)
16+
{
17+
Eigen::MatrixXN C1, C2;
18+
igl::cubic_split(C, t, C1, C2);
19+
return std::make_tuple(C1, C2);
20+
}
21+
}
22+
23+
void bind_cubic_split(nb::module_ &m)
24+
{
25+
m.def("cubic_split", &pyigl::cubic_split, "C"_a, "t"_a,
26+
R"(Split a cubic Bézier curve at parameter t into two cubic Bézier curves.
27+
28+
@param[in] C 4 by dim matrix of control points for a cubic Bézier curve
29+
@param[in] t parameter at which to split the curve
30+
@return Tuple (C1, C2) where
31+
C1 4 by dim control points of the sub-curve from C(0) to C(t)
32+
C2 4 by dim control points of the sub-curve from C(t) to C(1))");
33+
}

src/cycodebase/box_cubic.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#include "default_types.h"
2+
#include <igl/cycodebase/box_cubic.h>
3+
#include <nanobind/nanobind.h>
4+
#include <nanobind/eigen/dense.h>
5+
#include <nanobind/stl/tuple.h>
6+
#include <tuple>
7+
8+
namespace nb = nanobind;
9+
using namespace nb::literals;
10+
11+
namespace pyigl
12+
{
13+
// Bounding box of a single cubic Bézier curve.
14+
auto box_cubic_C(
15+
const nb::DRef<const Eigen::MatrixXN> &C)
16+
{
17+
Eigen::RowVectorXN B1, B2;
18+
igl::cycodebase::box_cubic(C, B1, B2);
19+
return std::make_tuple(B1, B2);
20+
}
21+
// Bounding boxes of many indexed cubic Bézier curves.
22+
auto box_cubic_PC(
23+
const nb::DRef<const Eigen::MatrixXN> &P,
24+
const nb::DRef<const Eigen::MatrixXI> &C)
25+
{
26+
Eigen::MatrixXN B1, B2;
27+
igl::cycodebase::box_cubic(P, C, B1, B2);
28+
return std::make_tuple(B1, B2);
29+
}
30+
}
31+
32+
void bind_box_cubic(nb::module_ &m)
33+
{
34+
m.def("box_cubic", &pyigl::box_cubic_C, "C"_a,
35+
R"(Compute the min/max box corners tightly containing a cubic Bézier curve.
36+
37+
@param[in] C 4 by dim matrix of control points defining the cubic Bézier curve
38+
@return Tuple (B1, B2) where
39+
B1 1 by dim min corner of the bounding box
40+
B2 1 by dim max corner of the bounding box)");
41+
42+
m.def("box_cubic", &pyigl::box_cubic_PC, "P"_a, "C"_a,
43+
R"(Compute bounding boxes for a collection of indexed cubic Bézier curves.
44+
45+
@param[in] P #P by dim matrix of control point locations
46+
@param[in] C #C by 4 matrix of indices into P defining the cubics
47+
@return Tuple (B1, B2) where
48+
B1 #C by dim matrix of min corners of the bounding boxes
49+
B2 #C by dim matrix of max corners of the bounding boxes)");
50+
}

src/cycodebase/module.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include <nanobind/nanobind.h>
2+
namespace nb = nanobind;
3+
4+
// generated by cmake
5+
#include "cycodebase/BINDING_DECLARATIONS.in"
6+
7+
NB_MODULE(pyigl_cycodebase, m) {
8+
m.doc() = "libigl cycodebase module python bindings";
9+
// generated by cmake
10+
#include "cycodebase/BINDING_INVOCATIONS.in"
11+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#include "default_types.h"
2+
#include <igl/cycodebase/point_cubic_squared_distance.h>
3+
#include <nanobind/nanobind.h>
4+
#include <nanobind/eigen/dense.h>
5+
#include <nanobind/stl/tuple.h>
6+
#include <tuple>
7+
8+
namespace nb = nanobind;
9+
using namespace nb::literals;
10+
11+
namespace pyigl
12+
{
13+
auto point_cubic_squared_distance(
14+
const nb::DRef<const Eigen::MatrixXN> &Q,
15+
const nb::DRef<const Eigen::MatrixXN> &C)
16+
{
17+
Eigen::VectorXN sqrD, S;
18+
Eigen::MatrixXN K;
19+
igl::cycodebase::point_cubic_squared_distance(Q, C, sqrD, S, K);
20+
return std::make_tuple(sqrD, S, K);
21+
}
22+
}
23+
24+
void bind_point_cubic_squared_distance(nb::module_ &m)
25+
{
26+
m.def("point_cubic_squared_distance", &pyigl::point_cubic_squared_distance,
27+
"Q"_a, "C"_a,
28+
R"(Squared distance from each query point to a cubic Bézier curve.
29+
30+
@param[in] Q #Q by dim matrix of query points
31+
@param[in] C 4 by dim matrix of control points for the cubic Bézier curve
32+
@return Tuple (sqrD, S, K) where
33+
sqrD #Q vector of smallest squared distances
34+
S #Q vector of parameters of the closest points on the curve
35+
K #Q by dim matrix of closest points on the curve)");
36+
}

0 commit comments

Comments
 (0)