Skip to content

Commit

Permalink
Merge pull request #167 from libigl/alecjacobson/cgal
Browse files Browse the repository at this point in the history
CGAL module
  • Loading branch information
alecjacobson authored Feb 11, 2023
2 parents 235cd02 + 7562685 commit 8dd9cd4
Show file tree
Hide file tree
Showing 10 changed files with 331 additions and 15 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ jobs:
env:
CIBW_BUILD_VERBOSITY: 3
# This is very dubious... It *may* work because these are just cpp libraries that should not depend on the python version. Still, super-dubious.
CIBW_BEFORE_BUILD_WINDOWS: "python -m pip install delvewheel"
CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "python -m delvewheel repair --no-mangle-all --add-path build\\temp.win-amd64-3.6\\Release;build\\temp.win-amd64-3.6\\Release\\Release;build\\temp.win-amd64-3.6\\Release\\_deps\\gmp-src\\lib;build\\temp.win-amd64-3.6\\Release\\_deps\\mpfr-src\\lib -w {dest_dir} {wheel} "
CIBW_TEST_REQUIRES: "gitpython"
CIBW_TEST_COMMAND: "python {project}/tests/test_basic.py"
CIBW_BUILD: "${{ matrix.cpversion }}-${{ matrix.os.cibw-arch }}"
Expand Down
73 changes: 66 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
cmake_minimum_required(VERSION 3.16.0)
project(pyigl)

if (NOT DEFINED ${PYLIBIGL_EXTERNAL})
if (NOT DEFINED PYLIBIGL_EXTERNAL)
set(PYLIBIGL_EXTERNAL ${CMAKE_CURRENT_SOURCE_DIR}/external)
endif()

message(STATUS "PYIGL_OUTPUT_DIRECTORY: ${PYIGL_OUTPUT_DIRECTORY}")
if (NOT DEFINED PYIGL_OUTPUT_DIRECTORY)
message(FATAL_ERROR "PYIGL_OUTPUT_DIRECTORY must be defined externally")
endif()

list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
# Color output
include(UseColors)
Expand All @@ -18,30 +23,84 @@ 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" ON)
# libigl options must come before include(PyiglDependencies)
include(PyiglDependencies)

if(NOT TARGET igl::core)
include(libigl)
endif()

# A module for writing bindings with our framework
file(GLOB PYIGL_SOURCES src/*.cpp)

npe_add_module(pyigl
BINDING_SOURCES
${PYIGL_SOURCES}
${PYIGL_SOURCES_COPYLEFT})

#TODO move additional libs to variable
${PYIGL_SOURCES})
target_link_libraries(pyigl PRIVATE igl::core)
target_include_directories(pyigl PRIVATE "src/include")
set_target_properties(pyigl PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${PYIGL_OUTPUT_DIRECTORY}"
RUNTIME_OUTPUT_DIRECTORY "${PYIGL_OUTPUT_DIRECTORY}"
LIBRARY_OUTPUT_DIRECTORY_RELEASE "${PYIGL_OUTPUT_DIRECTORY}"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PYIGL_OUTPUT_DIRECTORY}"
)

# don't need to worry about nested modules (opengl/** are the only ones and
# those probably aren't ever getting python bindings)
#
# prefix is either "", "copyleft", or "restricted"
function(pyigl_include prefix name)
string(TOUPPER "${prefix}" prefix_uc)
string(TOUPPER "${name}" name_uc)
if(prefix_uc)
string(PREPEND prefix_uc _)
endif()
string(TOLOWER "${prefix_uc}" prefix_lc)
if(LIBIGL${prefix_uc}_${name_uc})
if(${prefix} STREQUAL "copyleft")
set(subpath "copyleft/${name}")
else() # "" or "restricted"
set(subpath "${name}")
endif()
file(GLOB sources src/${subpath}/*.cpp)
set(target_name "pyigl${prefix_lc}_${name}")
npe_add_module( ${target_name} BINDING_SOURCES ${sources})
target_link_libraries( ${target_name} PRIVATE igl::core igl${prefix_lc}::${name})
target_include_directories( ${target_name} PRIVATE "src/include")
set(output_dir "${PYIGL_OUTPUT_DIRECTORY}/${subpath}")
file(MAKE_DIRECTORY ${output_dir})
file(WRITE "${output_dir}/__init__.py" "from .${target_name} import *")
# https://stackoverflow.com/a/56514534/148668
set_target_properties(${target_name} PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${output_dir}"
RUNTIME_OUTPUT_DIRECTORY "${output_dir}"
LIBRARY_OUTPUT_DIRECTORY_RELEASE "${output_dir}"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${output_dir}")
# why do this?
target_link_libraries( pyigl INTERFACE ${target_name})
endif()
# https://stackoverflow.com/a/69736197/148668
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
add_custom_command(TARGET ${target_name} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:${target_name}> $<TARGET_FILE_DIR:${target_name}>
COMMAND_EXPAND_LISTS)
endif()
endfunction()

pyigl_include("copyleft" "cgal")


add_library(pyigl_classes MODULE classes/classes.cpp)
target_link_libraries(pyigl_classes PRIVATE npe igl::core)
target_link_libraries(pyigl_classes PRIVATE pybind11::module)
set_target_properties(pyigl_classes PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" SUFFIX "${PYTHON_MODULE_EXTENSION}")
target_include_directories(pyigl_classes PRIVATE "src/include")
target_include_directories(pyigl_classes PRIVATE "${PYLIBIGL_EXTERNAL}/numpyeigen/src")
set_target_properties(pyigl_classes PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${PYIGL_OUTPUT_DIRECTORY}"
RUNTIME_OUTPUT_DIRECTORY "${PYIGL_OUTPUT_DIRECTORY}"
LIBRARY_OUTPUT_DIRECTORY_RELEASE "${PYIGL_OUTPUT_DIRECTORY}"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PYIGL_OUTPUT_DIRECTORY}"
)

# Sort projects inside the solution
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
Expand Down
2 changes: 1 addition & 1 deletion cmake/PyiglDependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ include(FetchContent)
FetchContent_Declare(
libigl
GIT_REPOSITORY https://github.com/libigl/libigl.git
GIT_TAG 4a91b88f81aa094d076ad4f1460ae663295ac518
GIT_TAG 78015d4da1c6799dfe15659ed35adfb3a5f23ffa
)
FetchContent_GetProperties(libigl)
FetchContent_MakeAvailable(libigl)
Expand Down
2 changes: 1 addition & 1 deletion igl/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "2.5.0dev"
__version__ = "2.5.2dev"
4 changes: 2 additions & 2 deletions scripts/generate_bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def map_parameter_types(name, cpp_type, parsed_types, errors, enum_types):

# Parse c++ header files
print("Parsing header files...")
load_headers = True
load_headers = False
if load_headers:
with open("headers.dat", 'rb') as fs:
dicts = pickle.load(fs)
Expand Down Expand Up @@ -291,7 +291,7 @@ def map_parameter_types(name, cpp_type, parsed_types, errors, enum_types):
# Write binding files
try:
tpl = Template(filename='basic_function.mako')
#print(correct_functions)
print(correct_functions)
includes = ["<npe.h>", "<typedefs.h>"]
rendered = tpl.render(functions=correct_functions, enums=enums, includes=includes)
tpl1 = Template(filename='basic_function.mako')
Expand Down
3 changes: 1 addition & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def run(self):
def build_extension(self, ext):
extdir = os.path.join(os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))),"igl")

cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir,
cmake_args = ['-DPYIGL_OUTPUT_DIRECTORY=' + extdir,
'-DPYTHON_EXECUTABLE=' + sys.executable]


Expand All @@ -47,7 +47,6 @@ def build_extension(self, ext):
# cmake_args += ['-DDEBUG_TRACE=ON']

if platform.system() == "Windows":
cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)]
cmake_generator = os.environ.get('CMAKE_GENERATOR', '')
if cmake_generator != "NMake Makefiles" and "Ninja" not in cmake_generator:
if sys.maxsize > 2**32:
Expand Down
31 changes: 31 additions & 0 deletions src/copyleft/cgal/convex_hull.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include <npe.h>
#include <typedefs.h>

#include <igl/copyleft/cgal/convex_hull.h>

const char* ds_convex_hull = R"igl_Qu8mg5v7(
Given a set of points (V), compute the convex hull as a triangle mesh (F)
Parameters
----------
V : #V by 3 list of input points
Returns
-------
F #F by 3 list of triangle indices into V
)igl_Qu8mg5v7";

npe_function(convex_hull)
npe_doc(ds_convex_hull)

npe_arg(v, dense_float, dense_double)
npe_begin_code()

EigenDenseInt g;
// when https://github.com/libigl/libigl/pull/1989 is merged this copy should
// be removed
Eigen::MatrixXd v_copy = v.template cast<double>();
igl::copyleft::cgal::convex_hull(v_copy, g);
return npe::move(g);

npe_end_code()
87 changes: 87 additions & 0 deletions src/copyleft/cgal/intersect_other.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#include <npe.h>
#include <typedefs.h>
#include <igl/copyleft/cgal/intersect_other.h>



const char* ds_intersect_other = R"igl_Qu8mg5v7(
INTERSECT_OTHER Given a triangle mesh (VA,FA) and another mesh (VB,FB) find all
pairs of intersecting faces. Note that self-intersections are ignored.
Parameters
----------
VA : #VA by 3 list of vertex positions of first mesh
FA : #FA by 3 list of triangle indices into VA
VB : #VB by 3 list of vertex positions of second mesh
FB : #FB by 3 list of triangle indices into VB
detect_only : avoid constructing intersections results when possible {false}
first_only : return after detecting the first intersection (if
first_only==true, then detect_only should also be true) {false}
stitch_all : whether to stitch all resulting constructed elements into a
(non-manifold) mesh {false}
slow_and_more_precise_rounding : whether to use slow and more precise
rounding (see assign_scalar) {false}
Returns
-------
IF : #intersecting face pairs by 2 list of intersecting face pairs, indexing F
VVAB : #VVAB by 3 list of vertex positions
FFAB : #FFAB by 3 list of triangle indices into VVAB
JAB : #FFAB list of indices into [FA;FB] denoting birth triangle
IMAB : #VVAB list of indices stitching duplicates (resulting from
mesh intersections) together
See also
--------
mesh_boolean
)igl_Qu8mg5v7";

npe_function(intersect_other)
npe_doc( ds_intersect_other)

npe_arg(VA, dense_float, dense_double)
npe_arg(FA, dense_int, dense_long, dense_longlong)
npe_arg(VB, npe_matches(VA))
npe_arg(FB, npe_matches(FA))
npe_default_arg(detect_only, bool, false)
npe_default_arg(first_only, bool, false)
npe_default_arg(stitch_all, bool, false)
// Awaiting bump in libigl
//npe_default_arg(slow_and_more_precise_rounding, bool, false)


npe_begin_code()
Eigen::MatrixXd VAcpy = VA.template cast<double>();
Eigen::MatrixXi FAcpy = FA.template cast<int>();
Eigen::MatrixXd VBcpy = VB.template cast<double>();
Eigen::MatrixXi FBcpy = FB.template cast<int>();

Eigen::MatrixXd VVABcpy;
Eigen::MatrixXi FFABcpy;
Eigen::VectorXi JABcpy;
Eigen::VectorXi IMABcpy;
Eigen::MatrixXi IFcpy;
igl::copyleft::cgal::RemeshSelfIntersectionsParam params;
params.detect_only = detect_only;
params.first_only = first_only;
params.stitch_all = stitch_all;
//params.slow_and_more_precise_rounding = slow_and_more_precise_rounding;
igl::copyleft::cgal::intersect_other(
VAcpy,FAcpy,VBcpy,FBcpy,params,IFcpy,VVABcpy,FFABcpy,JABcpy,IMABcpy);

EigenDenseLike<npe_Matrix_FA> IF = IFcpy.cast<typename decltype(IF )::Scalar>();
EigenDenseLike<npe_Matrix_VA> VVAB = VVABcpy.cast<typename decltype(VVAB)::Scalar>();
EigenDenseLike<npe_Matrix_FA> FFAB = FFABcpy.cast<typename decltype(FFAB)::Scalar>();
EigenDenseLike<npe_Matrix_FA> JAB = JABcpy.cast<typename decltype( JAB)::Scalar>();
EigenDenseLike<npe_Matrix_FA> IMAB = IMABcpy.cast<typename decltype(IMAB)::Scalar>();
return std::make_tuple(
npe::move(IF),
npe::move(VVAB),
npe::move(FFAB),
npe::move( JAB),
npe::move(IMAB));
npe_end_code()


63 changes: 63 additions & 0 deletions src/copyleft/cgal/mesh_boolean.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include <npe.h>
#include <typedefs.h>
#include <igl/copyleft/cgal/mesh_boolean.h>


const char* ds_mesh_boolean = R"igl_Qu8mg5v7(
MESH_BOOLEAN Compute boolean csg operations on "solid", consistently oriented
meshes.
Parameters
----------
VA : #VA by 3 list of vertex positions of first mesh
FA : #FA by 3 list of triangle indices into VA
VB : #VB by 3 list of vertex positions of second mesh
FB : #FB by 3 list of triangle indices into VB
Returns
-------
VC : #VC by 3 list of vertex positions of boolean result mesh
FC : #FC by 3 list of triangle indices into VC
J : #FC list of indices into [FA;FA.rows()+FB] revealing "birth" facet
See also
--------
mesh_boolean_cork, intersect_other, remesh_self_intersections
)igl_Qu8mg5v7";

npe_function(mesh_boolean)
npe_doc(ds_mesh_boolean)

npe_arg(va, dense_float, dense_double)
npe_arg(fa, dense_int, dense_long, dense_longlong)
npe_arg(vb, npe_matches(va))
npe_arg(fb, npe_matches(fa))
npe_arg(type, std::string)


npe_begin_code()
Eigen::MatrixXd va_copy = va.template cast<double>();
Eigen::MatrixXd vb_copy = vb.template cast<double>();
Eigen::MatrixXi fa_copy = fa.template cast<int>();
Eigen::MatrixXi fb_copy = fb.template cast<int>();

Eigen::MatrixXd vc_copy;
Eigen::MatrixXi fc_copy;
Eigen::VectorXi j_copy;
igl::copyleft::cgal::mesh_boolean(
va_copy,
fa_copy,
vb_copy,
fb_copy,
type,
vc_copy,
fc_copy,
j_copy);

EigenDenseLike<npe_Matrix_va> vc = vc_copy.cast<typename decltype(vc)::Scalar>();
EigenDenseLike<npe_Matrix_fa> fc = fc_copy.cast<typename decltype(fc)::Scalar>();
EigenDenseLike<npe_Matrix_fa> j = j_copy.cast<typename decltype( j)::Scalar>();
return std::make_tuple(npe::move(vc), npe::move(fc), npe::move(j));
npe_end_code()

Loading

0 comments on commit 8dd9cd4

Please sign in to comment.