Skip to content

Commit

Permalink
bunch more bindings; embree; enums; no mqwf class
Browse files Browse the repository at this point in the history
  • Loading branch information
alecjacobson committed Nov 18, 2024
1 parent 4c13728 commit 62183ed
Show file tree
Hide file tree
Showing 51 changed files with 1,904 additions and 241 deletions.
40 changes: 28 additions & 12 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
cmake_minimum_required(VERSION 3.16.0)
# Refactor the cmake/setup.py build to use scikit-build
# https://github.com/wjakob/nanobind_example
# Rather than creating multiple modules and dealing with multiple __init__.py
# files, we can create a single module and use __init__.py to import the
# submodules, create one extension module with submodules
# https://stackoverflow.com/a/77020918/148668

cmake_minimum_required(VERSION 3.15...3.27)
project(pyigl)

# use C++20 (we need at least C++17 for return value optimization)
set(CMAKE_CXX_STANDARD 20)
# ≥17 for return value optimization
# <20 for embree
set(CMAKE_CXX_STANDARD 17)

if (CMAKE_VERSION VERSION_LESS 3.18)
set(DEV_MODULE Development)
Expand Down Expand Up @@ -30,13 +38,14 @@ FetchContent_MakeAvailable(nanobind)

# Download and set up libigl
option(LIBIGL_COPYLEFT_CORE "Build target igl_copyleft::core" ON)
option(LIBIGL_RESTRICTED_TRIANGLE "Build target igl_restricted::triangle" ON)
option(LIBIGL_COPYLEFT_CGAL "Build target igl_copyleft::cgal" ON)
option(LIBIGL_EMBREE "Build target igl::embree" ON)
option(LIBIGL_COPYLEFT_TETGEN "Build target igl_copyleft::tetgen" ON)
option(LIBIGL_RESTRICTED_TRIANGLE "Build target igl_restricted::triangle" ON)
FetchContent_Declare(
libigl
GIT_REPOSITORY https://github.com/libigl/libigl.git
GIT_TAG e60c377f953ae70e66ceefed42cec73aec044f92
GIT_TAG 7326483d8dcea0169054599ae7e09917929b4b46
)
FetchContent_MakeAvailable(libigl)

Expand All @@ -58,9 +67,6 @@ include(CXXFeatures)
# Generate position independent code by default
set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE INTERNAL "")

option(LIBIGL_COPYLEFT_CGAL "Build target igl_copyleft::cgal" OFF)
option(LIBIGL_COPYLEFT_TETGEN "Build target igl_copyleft::tetgen" OFF)
option(LIBIGL_RESTRICTED_TRIANGLE "Build target igl_restricted::triangle" OFF)


# don't need to worry about nested modules (opengl/** are the only ones and
Expand Down Expand Up @@ -127,8 +133,15 @@ function(pyigl_include prefix name)
LIBRARY_OUTPUT_DIRECTORY_RELEASE "${output_dir}"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${output_dir}"
)
# just to add dependency?
nanobind_add_stub(
${target_name}_stub
MODULE ${target_name}
OUTPUT "${output_dir}/${target_name}.pyi"
PYTHON_PATH $<TARGET_FILE_DIR:${target_name}>
DEPENDS ${target_name}
)

# just to add dependency?
if("${name}" STREQUAL "core")
else()
target_link_libraries(pyigl_core INTERFACE ${target_name})
Expand All @@ -140,14 +153,17 @@ pyigl_include("" "core")
if(LIBIGL_COPYLEFT_CORE)
pyigl_include("copyleft" "core")
endif()
if(LIBIGL_RESTRICTED_TRIANGLE)
pyigl_include("restricted" "triangle")
endif()
if(LIBIGL_COPYLEFT_CGAL)
pyigl_include("copyleft" "cgal")
endif()
if(LIBIGL_EMBREE)
pyigl_include("" "embree")
endif()
if(LIBIGL_COPYLEFT_TETGEN)
pyigl_include("copyleft" "tetgen")
endif()
if(LIBIGL_RESTRICTED_TRIANGLE)
pyigl_include("restricted" "triangle")
endif()


11 changes: 10 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
import sys
import platform
import subprocess
import warnings
# Define a custom format warning function
def custom_formatwarning(msg, category, filename, lineno, line=None):
return f"\033[91m{category.__name__}: {msg}\033[0m\n"

# Apply the custom warning format
warnings.formatwarning = custom_formatwarning

from packaging.version import Version
from setuptools import setup, Extension, find_packages
Expand All @@ -24,7 +31,8 @@ def run(self):
raise RuntimeError(
"CMake must be installed to build the following extensions: , ".join(e.name for e in self.extensions))

# self.debug = True
warnings.warn("Debug mode is on")
self.debug = True

cmake_version = Version(re.search(r'version\s*([\d.]+)', out.decode()).group(1))
if cmake_version < Version('3.2.0'):
Expand All @@ -45,6 +53,7 @@ def build_extension(self, ext):
build_args = ['--config', cfg]
cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg]
# cmake_args += ['-DDEBUG_TRACE=ON']
# build_args += ['-v']

if platform.system() == "Windows":
cmake_generator = os.environ.get('CMAKE_GENERATOR', '')
Expand Down
4 changes: 2 additions & 2 deletions src/AABB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ using namespace nb::literals;
namespace pyigl
{
template <typename AABB>
void init(
void aabb_init(
AABB &tree,
const nb::DRef<const Eigen::MatrixXN> &V,
const nb::DRef<const Eigen::MatrixXI> &Ele)
Expand Down Expand Up @@ -97,7 +97,7 @@ void bind_AABB(nb::module_ &m)
typedef igl::AABB<nb::DRef<const Eigen::MatrixXN>,3> AABBN3;
nb::class_<AABBN3>(m, "AABB")
.def(nb::init<>())
.def("init", &pyigl::init<AABBN3>, "V"_a, "Ele"_a)
.def("init", &pyigl::aabb_init<AABBN3>, "V"_a, "Ele"_a)
.def("find", &pyigl::find<AABBN3>, "V"_a, "Ele"_a, "q"_a, "first"_a=false)
.def("squared_distance", &pyigl::squared_distance<AABBN3>, "V"_a, "Ele"_a, "P"_a)
.def("intersect_ray_first",&pyigl::intersect_ray_first<AABBN3>, "V"_a, "Ele"_a, "orig"_a, "dir"_a, "min_t"_a=std::numeric_limits<Numeric>::infinity())
Expand Down
15 changes: 15 additions & 0 deletions src/FileEncoding.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "default_types.h"
#include <igl/FileEncoding.h>
#include <nanobind/nanobind.h>
#include <nanobind/ndarray.h>

namespace nb = nanobind;
using namespace nb::literals;

void bind_FileEncoding(nb::module_ &m)
{
nb::enum_<igl::FileEncoding>(m,"FileEncoding")
.value("Ascii", igl::FileEncoding::Ascii)
.value("Binary", igl::FileEncoding::Binary)
.export_values();
}
21 changes: 21 additions & 0 deletions src/MappingEnergyType.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "default_types.h"
#include <igl/MappingEnergyType.h>
#include <nanobind/nanobind.h>
#include <nanobind/ndarray.h>

namespace nb = nanobind;
using namespace nb::literals;

void bind_MappingEnergyType(nb::module_ &m)
{
nb::enum_<igl::MappingEnergyType>(m, "MappingEnergyType")
.value("ARAP", igl::MappingEnergyType::ARAP)
.value("LOG_ARAP", igl::MappingEnergyType::LOG_ARAP)
.value("SYMMETRIC_DIRICHLET", igl::MappingEnergyType::SYMMETRIC_DIRICHLET)
.value("CONFORMAL", igl::MappingEnergyType::CONFORMAL)
.value("EXP_CONFORMAL", igl::MappingEnergyType::EXP_CONFORMAL)
.value("EXP_SYMMETRIC_DIRICHLET", igl::MappingEnergyType::EXP_SYMMETRIC_DIRICHLET)
.export_values()
;
}

1 change: 1 addition & 0 deletions src/arap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ void bind_arap(nb::module_ &m)
.value("ARAP_ENERGY_TYPE_SPOKES_AND_RIMS", igl::ARAPEnergyType::ARAP_ENERGY_TYPE_SPOKES_AND_RIMS)
.value("ARAP_ENERGY_TYPE_ELEMENTS", igl::ARAPEnergyType::ARAP_ENERGY_TYPE_ELEMENTS)
.value("NUM_ARAP_ENERGY_TYPES", igl::ARAPEnergyType::NUM_ARAP_ENERGY_TYPES)
.export_values()
;
nb::class_<igl::ARAPData>(m, "ARAPData")
.def(nb::init<>())
Expand Down
39 changes: 39 additions & 0 deletions src/average_onto_faces.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "default_types.h"
#include <igl/average_onto_faces.h>
#include <nanobind/nanobind.h>
#include <nanobind/ndarray.h>
#include <nanobind/eigen/dense.h>

namespace nb = nanobind;
using namespace nb::literals;

namespace pyigl
{
auto average_onto_faces(
const nb::DRef<const Eigen::MatrixXI> &F,
const nb::DRef<const Eigen::VectorXN> &S)
{
Eigen::VectorXN SF;
igl::average_onto_faces(F,S,SF);
return SF;
}
}

// Bind the wrapper to the Python module
void bind_average_onto_faces(nb::module_ &m)
{
m.def(
"average_onto_faces",
&pyigl::average_onto_faces,
"F"_a,
"S"_a,
R"(Move a scalar field defined on faces to faces by averaging
@param[in] F #F by 3 triangle mesh connectivity
@param[in] S #F by 1 scalar field defined on faces
@param[out] SF #F by 1 scalar field defined on faces
)"
);
}


41 changes: 41 additions & 0 deletions src/average_onto_vertices.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "default_types.h"
#include <igl/average_onto_vertices.h>
#include <nanobind/nanobind.h>
#include <nanobind/ndarray.h>
#include <nanobind/eigen/dense.h>

namespace nb = nanobind;
using namespace nb::literals;

namespace pyigl
{
auto average_onto_vertices(
const nb::DRef<const Eigen::MatrixXN> &V,
const nb::DRef<const Eigen::MatrixXI> &F,
const nb::DRef<const Eigen::VectorXN> &S)
{
Eigen::VectorXN SV;
igl::average_onto_vertices(V,F,S,SV);
return SV;
}
}

// Bind the wrapper to the Python module
void bind_average_onto_vertices(nb::module_ &m)
{
m.def(
"average_onto_vertices",
&pyigl::average_onto_vertices,
"V"_a,
"F"_a,
"S"_a,
R"(Move a scalar field defined on faces to vertices by averaging
@param[in] S #V by dim triangle mesh connectivity
@param[in] F #F by 3 triangle mesh connectivity
@param[in] S #F by 1 scalar field defined on faces
@param[out] SV #V by 1 scalar field defined on vertices
)"
);
}

41 changes: 41 additions & 0 deletions src/avg_edge_length.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "default_types.h"
#include <igl/avg_edge_length.h>
#include <nanobind/nanobind.h>
#include <nanobind/ndarray.h>
#include <nanobind/eigen/dense.h>
#include <nanobind/eigen/sparse.h>
#include <nanobind/stl/tuple.h>

namespace nb = nanobind;
using namespace nb::literals;

namespace pyigl
{
auto avg_edge_length(
const nb::DRef<const Eigen::MatrixXN> &V,
const nb::DRef<const Eigen::MatrixXI> &F)
{
return (Numeric)igl::avg_edge_length(V,F);
}
}

void bind_avg_edge_length(nb::module_ &m)
{
m.def(
"avg_edge_length",
&pyigl::avg_edge_length,
"V"_a,
"F"_a,
R"(Constructs the cotangent stiffness matrix (discrete laplacian) for a given
mesh (V,F).
@tparam DerivedV derived type of eigen matrix for V (e.g. derived from
MatrixXd)
@tparam DerivedF derived type of eigen matrix for F (e.g. derived from
MatrixXi)
@tparam Scalar scalar type for eigen sparse matrix (e.g. double)
@param[in] V #V by dim list of mesh vertex positions
@param[in] F #F by simplex_size list of mesh elements (triangles or tetrahedra)
@param[out] L #V by #V cotangent matrix, each row i corresponding to V(i,:))");

}
17 changes: 16 additions & 1 deletion src/boundary_loop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,19 @@ using namespace nb::literals;
namespace pyigl
{
// Wrapper for boundary_loop that returns all loops as a vector of vectors
auto boundary_loop(const nb::DRef<const Eigen::MatrixXI> &F)
auto boundary_loop_all(const nb::DRef<const Eigen::MatrixXI> &F)
{
std::vector<std::vector<Integer>> loops;
igl::boundary_loop(F, loops);
return loops;
}
// Wrapper for boundary_loop that returns all loops as a vector of vectors
auto boundary_loop(const nb::DRef<const Eigen::MatrixXI> &F)
{
Eigen::VectorXI longest;
igl::boundary_loop(F, longest);
return longest;
}
}

// Bind the wrapper to the Python module
Expand All @@ -25,6 +32,14 @@ void bind_boundary_loop(nb::module_ &m)
"boundary_loop",
&pyigl::boundary_loop,
"F"_a,
R"(Compute the ordered boundary loop with the most vertices for a manifold mesh.
@param[in] F #F by dim list of mesh faces
@param[out] L ordered list of boundary vertices of longest boundary loop)");
m.def(
"boundary_loop_all",
&pyigl::boundary_loop_all,
"F"_a,
R"(Compute all ordered boundary loops for a manifold mesh.
@param[in] F #F by dim list of mesh faces
Expand Down
Loading

0 comments on commit 62183ed

Please sign in to comment.