Skip to content

Commit 5f53a3a

Browse files
authored
Merge pull request #182 from libigl/alecjacobson/AABB
Basic AABB support; in_element; (req C++17)
2 parents 229030c + 10b0705 commit 5f53a3a

6 files changed

Lines changed: 164 additions & 2 deletions

File tree

.github/workflows/wheels.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ jobs:
5757
CIBW_ENVIRONMENT: "MAX_JOBS=${{ matrix.os.runs-on == 'macos-latest' && 3 || 2 }}"
5858
# Why universal2 here? It's not included above in CIBW_BUILD
5959
CIBW_ARCHS_MACOS: "x86_64 arm64 universal2"
60-
CIBW_ENVIRONMENT_MACOS: "CMAKE_OSX_ARCHITECTURES=\"${{ matrix.os.cibw-arch == 'macosx_x86_64' && 'x86_64' || matrix.os.cibw-arch == 'macosx_arm64' && 'arm64' || matrix.os.cibw-arch == 'macosx_universal2' && 'arm64;x86_64' || '' }}\""
60+
CIBW_ENVIRONMENT_MACOS: "MACOSX_DEPLOYMENT_TARGET=10.13 CMAKE_OSX_ARCHITECTURES=\"${{ matrix.os.cibw-arch == 'macosx_x86_64' && 'x86_64' || matrix.os.cibw-arch == 'macosx_arm64' && 'arm64' || matrix.os.cibw-arch == 'macosx_universal2' && 'arm64;x86_64' || '' }}\""
6161

6262

6363
steps:

CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,10 @@ pyigl_include("copyleft" "cgal")
9393
pyigl_include("restricted" "triangle")
9494

9595

96-
add_library(pyigl_classes MODULE classes/classes.cpp)
96+
file(GLOB PYIGL_CLASSES_SOURCES classes/*.cpp)
97+
add_library(pyigl_classes MODULE ${PYIGL_CLASSES_SOURCES})
98+
# std::variant
99+
target_compile_features(pyigl_classes PRIVATE cxx_std_17)
97100
target_link_libraries(pyigl_classes PRIVATE npe igl::core)
98101
target_link_libraries(pyigl_classes PRIVATE pybind11::module)
99102
set_target_properties(pyigl_classes PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" SUFFIX "${PYTHON_MODULE_EXTENSION}")

classes/AABB.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#include <igl/AABB.h>
2+
#include <npe.h>
3+
#include <pybind11/pybind11.h>
4+
#include <pybind11/stl.h>
5+
#include <variant>
6+
#include <variant>
7+
8+
9+
namespace py = pybind11;
10+
template <int DIM>
11+
void init_AABB(py::module_ &m)
12+
{
13+
using AABB_f64_DIM = igl::AABB<Eigen::MatrixXd,DIM>;
14+
py::class_<AABB_f64_DIM >(m, (std::string("AABB_f64_")+std::to_string(DIM)).c_str() )
15+
.def(py::init([]()
16+
{
17+
std::unique_ptr<AABB_f64_DIM> self = std::make_unique<AABB_f64_DIM>();
18+
return self;
19+
}))
20+
.def("init",[](AABB_f64_DIM & self, const Eigen::MatrixXd & V, const Eigen::MatrixXi & F)
21+
{
22+
self.init(V,F);
23+
},
24+
py::arg("V"), py::arg("F"))
25+
.def("squared_distance",[](
26+
AABB_f64_DIM & self,
27+
const Eigen::MatrixXd & V,
28+
const Eigen::MatrixXi & F,
29+
const Eigen::MatrixXd & P,
30+
const bool return_index = false,
31+
const bool return_closest_point = false) ->
32+
std::variant<py::object,std::list<py::object> >
33+
{
34+
Eigen::VectorXd sqrD;
35+
Eigen::VectorXi I;
36+
Eigen::MatrixXd C;
37+
self.squared_distance(V,F,P,sqrD,I,C);
38+
if(return_index && return_closest_point)
39+
{
40+
return std::list<py::object>({npe::move(sqrD),npe::move(I),npe::move(C)});
41+
}else if(return_index)
42+
{
43+
return std::list<py::object>({npe::move(sqrD),npe::move(I)});
44+
}else if(return_closest_point)
45+
{
46+
return std::list<py::object>({npe::move(sqrD),npe::move(C)});
47+
}else
48+
{
49+
return npe::move(sqrD);
50+
}
51+
},
52+
py::arg("V"),
53+
py::arg("F"),
54+
py::arg("P"),
55+
py::arg("return_index")=false,
56+
py::arg("return_closest_point")=false
57+
)
58+
;
59+
}
60+
61+
template void init_AABB<2>(py::module_ &);
62+
template void init_AABB<3>(py::module_ &);

classes/classes.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,15 @@
99

1010
namespace py = pybind11;
1111

12+
// Forward declaration
13+
template <int DIM>
14+
void init_AABB(py::module_ &);
15+
1216
PYBIND11_MODULE(pyigl_classes, m)
1317
{
18+
init_AABB<2>(m);
19+
init_AABB<3>(m);
20+
1421
py::class_<igl::ARAPData>(m, "ARAP")
1522
.def(py::init([](Eigen::MatrixXd &v, Eigen::MatrixXi &f, int dim, Eigen::MatrixXi &b,
1623
const int energy_type, const bool with_dynamics, const double h, const double ym, const int max_iter) {

src/in_element.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include <common.h>
2+
#include <npe.h>
3+
#include <typedefs.h>
4+
#include <igl/in_element.h>
5+
#include <igl/AABB.h>
6+
7+
using AABB_f64_3 = igl::AABB<Eigen::MatrixXd,3>;
8+
const char* ds_in_element = R"igl_Qu8mg5v7(
9+
Determine whether each point in a list of points is in the elements of a mesh.
10+
11+
Parameters:
12+
------
13+
V : #V by dim list of mesh vertex positions.
14+
Ele : #Ele by dim+1 list of mesh indices into #V.
15+
Q : #Q by dim list of query point positions
16+
aabb : axis-aligned bounding box tree object (see AABB.h)
17+
18+
Returns:
19+
-------
20+
I : #Q list of indices into Ele of first containing element (-1 means no
21+
containing element)
22+
)igl_Qu8mg5v7";
23+
npe_function(in_element_3)
24+
npe_doc( ds_in_element)
25+
npe_arg(V, Eigen::MatrixXd)
26+
npe_arg(Ele, Eigen::MatrixXi)
27+
npe_arg(Q, Eigen::MatrixXd)
28+
npe_arg(aabb, AABB_f64_3)
29+
npe_begin_code()
30+
31+
Eigen::VectorXi I;
32+
igl::in_element(V,Ele,Q,aabb,I);
33+
return npe::move(I);
34+
35+
npe_end_code()
36+
37+
38+
using AABB_f64_2 = igl::AABB<Eigen::MatrixXd,2>;
39+
npe_function(in_element_2)
40+
npe_doc( ds_in_element)
41+
npe_arg(V, Eigen::MatrixXd)
42+
npe_arg(Ele, Eigen::MatrixXi)
43+
npe_arg(Q, Eigen::MatrixXd)
44+
npe_arg(aabb, AABB_f64_2)
45+
npe_begin_code()
46+
47+
Eigen::VectorXi I;
48+
igl::in_element(V,Ele,Q,aabb,I);
49+
return npe::move(I);
50+
51+
npe_end_code()
52+
53+
54+

tests/test_basic.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2470,6 +2470,41 @@ def test_flip_edge(self):
24702470
emap.dtype == self.f1.dtype)
24712471
self.assertTrue(np.array(ue2e).dtype == self.f1.dtype)
24722472

2473+
def test_AABB(self):
2474+
tree = igl.AABB_f64_3()
2475+
tree.init(self.v1,self.f1)
2476+
bc = igl.barycenter(self.v1,self.f1)
2477+
sqrD = tree.squared_distance(self.v1,self.f1,bc)
2478+
self.assertTrue(sqrD.shape[0] == bc.shape[0])
2479+
self.assertTrue(np.max(sqrD) <= 1e-16)
2480+
sqrD,I,C = tree.squared_distance(self.v1,self.f1,bc,return_index=True,return_closest_point=True)
2481+
self.assertTrue(sqrD.shape[0] == bc.shape[0])
2482+
self.assertTrue(I.shape[0] == bc.shape[0])
2483+
self.assertTrue(C.shape == bc.shape)
2484+
2485+
def test_in_element_3(self):
2486+
V = np.array([ [0.,0,0], [1,0,0], [0,1,0], [0,0,1], [1,1,1]],dtype='float64')
2487+
T = np.array([[0,1,2,3],[4,3,2,1]],dtype='int32')
2488+
Q = np.array([[0.1,0.1,0.1],[0.9,0.9,0.9]],dtype='float64')
2489+
tree = igl.AABB_f64_3()
2490+
tree.init(V,T)
2491+
I = igl.in_element_3(V,T,Q,tree)
2492+
self.assertTrue(I.shape[0] == Q.shape[0])
2493+
self.assertTrue(I[0] == 0)
2494+
self.assertTrue(I[1] == 1)
2495+
2496+
def test_in_element_2(self):
2497+
V = np.array([ [0.,0], [1,0], [0,1], [1,1]],dtype='float64')
2498+
F = np.array([[0,1,2],[2,1,3]],'int32')
2499+
Q = np.array([[0.1,0.1],[0.9,0.9]],dtype='float64')
2500+
tree = igl.AABB_f64_2()
2501+
tree.init(V,F)
2502+
I = igl.in_element_2(V,F,Q,tree)
2503+
self.assertTrue(I.shape[0] == Q.shape[0])
2504+
self.assertTrue(I[0] == 0)
2505+
self.assertTrue(I[1] == 1)
2506+
2507+
24732508
def test_triangulate(self):
24742509
V = np.array([[0,0],[1,0],[1,1],[0,1]],dtype='float64')
24752510
E = np.array([[0,1],[1,2],[2,3],[3,0]])
@@ -2489,6 +2524,7 @@ def test_triangulate(self):
24892524
self.assertTrue(E2.shape == E.shape)
24902525
self.assertTrue(EM2.shape == EM.shape)
24912526

2527+
24922528
# copyleft.cgal
24932529
def test_convex_hull(self):
24942530
V = np.array([[0,0,0],[1,0,0],[0,1,0],[0,0,1]],dtype="float64")

0 commit comments

Comments
 (0)