diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml new file mode 100644 index 00000000..05bb3141 --- /dev/null +++ b/.github/workflows/pip.yml @@ -0,0 +1,38 @@ +name: Pip + +on: + workflow_dispatch: + pull_request: + push: + branches: + - master + +jobs: + build: + name: Build with Pip + runs-on: ${{ matrix.platform }} + strategy: + fail-fast: false + matrix: + platform: [windows-latest, macos-latest, ubuntu-latest] + python-version: ["3.10"] + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Set min macOS version + if: runner.os == 'macOS' + run: | + echo "MACOSX_DEPLOYMENT_TARGET=10.14" >> $GITHUB_ENV + + - name: Build and install + run: | + python -m pip install pytest + pip install --verbose . + + - name: Test + run: python -m pytest diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index ea6e16bd..218ead93 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -1,8 +1,8 @@ -name: Pip build +name: Wheels on: push: - branches: + branches: - main pull_request: branches: @@ -14,6 +14,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + submodules: true - name: Build SDist run: pipx run build --sdist @@ -23,102 +25,48 @@ jobs: - uses: actions/upload-artifact@v4 with: - name: artifact-sdist + name: dist-sdist path: dist/*.tar.gz build_wheels: - name: Build wheels ${{ matrix.cpversion }}-${{ matrix.os.cibw-arch }} - runs-on: ${{ matrix.os.runs-on }} - - 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_TEST_REQUIRES: "gitpython" - CIBW_TEST_COMMAND: "python {project}/tests/test_basic.py" - CIBW_BUILD: "${{ matrix.cpversion }}-${{ matrix.os.cibw-arch }}" - CIBW_TEST_SKIP: "*-macosx_arm64" - CIBW_ENVIRONMENT: "MAX_JOBS=${{ matrix.os.runs-on == 'macos-latest' && 3 || 2 }} PIP_CONSTRAINT=constraints.txt" - # Why universal2 here? It's not included above in CIBW_BUILD - CIBW_ARCHS_MACOS: "x86_64 arm64 universal2" - 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' || '' }}\"" - + name: Build ${{ matrix.cpversion }} wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - # Launch separate job for each python. The build is so much longer than - # machine configuration/setup, so parallel builds will be faster. More - # importantly, github times out after 6 hours _per job_. - cpversion: - - cp36 - - cp37 - - cp38 - - cp39 - - cp310 - - cp311 - - cp312 - os: - - runs-on: ubuntu-latest - cibw-arch: manylinux_x86_64 - - runs-on: macos-latest - cibw-arch: macosx_x86_64 - - runs-on: macos-latest - cibw-arch: macosx_arm64 - - runs-on: windows-latest - cibw-arch: win_amd64 + os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-14] + # cp313 seemed to produce the same wheel name as cp312. Skip unless + # necessary. + cpversion: [cp38, cp39, cp310, cp311, cp312] + # github actions cp38 on macos-14 runners are cross compiling or + # something and confusing the stub generation. Just skip it for now. + # Maybe it'd be friendlier to disable the stub generation for this + # combination. exclude: - - cpversion: cp36 - os: - runs-on: macos-latest - cibw-arch: macosx_arm64 - - cpversion: cp37 - os: - runs-on: macos-latest - cibw-arch: macosx_arm64 + - os: macos-14 + cpversion: cp38 + steps: - uses: actions/checkout@v4 - - name: Install Python - uses: actions/setup-python@v5 + - uses: actions/setup-python@v5 with: - python-version: "3.x" + python-version: '3.12' - name: Install cibuildwheel - run: | - python -m pip install --upgrade pip - python -m pip install cibuildwheel + run: python -m pip install cibuildwheel==2.22.0 - name: Build wheels - shell: bash - run: | - PIP_CONSTRAINT=$GITHUB_WORKSPACE/constraints.txt python -m cibuildwheel --output-dir wheelhouse + # why do I need to specify this cpversion here? + env: + CIBW_ARCHS: "auto64" + CIBW_BUILD: "${{ matrix.cpversion }}-*" + # why isn't auto64 working? + CIBW_SKIP: "cp*-manylinux_i686 cp*-musllinux* cp*-win32" + run: python -m cibuildwheel --output-dir wheelhouse - # Upload binaries to github - uses: actions/upload-artifact@v4 with: - name: artifact-wheel-${{ matrix.cpversion }}-${{ matrix.os.cibw-arch }} - path: |- - ./wheelhouse/*.whl - ./wheelhouse/*.tar.gz - - # # Push the resulting binaries to pypi on a tag starting with 'v' - upload_pypi: - needs: build_wheels - runs-on: ubuntu-latest - # upload to PyPI on every tag starting with 'v' - # if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') - # alternatively, to publish when a GitHub Release is created, use the following rule: - if: github.event_name == 'release' && github.event.action == 'published' - steps: - - uses: actions/download-artifact@v4 - with: - pattern: artifact-* - merge-multiple: true - path: dist - - - uses: pypa/gh-action-pypi-publish@v1.8.11 - with: - user: __token__ - password: ${{ secrets.pypi_password }} - skip_existing: true - # To test: repository_url: https://test.pypi.org/legacy/ + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} + path: ./wheelhouse/*.whl diff --git a/.gitignore b/.gitignore index 4f8d0da4..a50ba9a6 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ junit .*.swo *~ dist/ +igl/* diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f10f3a3..0e5ba48e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,15 +1,61 @@ -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) -if (NOT DEFINED PYLIBIGL_EXTERNAL) - set(PYLIBIGL_EXTERNAL ${CMAKE_CURRENT_SOURCE_DIR}/external) +# For std::filesystem::path (generic_string) +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15") +# ≥17 for return value optimization +# <20 for embree +set(CMAKE_CXX_STANDARD 17) + +if (CMAKE_VERSION VERSION_LESS 3.18) + set(DEV_MODULE Development) +else() + set(DEV_MODULE Development.Module) 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") +find_package(Python 3.8 COMPONENTS Interpreter ${DEV_MODULE} REQUIRED) + +if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() +message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") + +# Enable FetchContent to download dependencies at configure time +include(FetchContent) + +# Download and set up nanobind +FetchContent_Declare( + nanobind + GIT_REPOSITORY https://github.com/wjakob/nanobind.git + GIT_TAG v2.2.0 +) +FetchContent_MakeAvailable(nanobind) + +# Download and set up libigl +option(LIBIGL_COPYLEFT_CORE "Build target igl_copyleft::core" 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 a221faf1e4bd571529ca2101c08bc2458579b1da +) +FetchContent_MakeAvailable(libigl) + +# set PYIGL_OUTPUT_DIRECTORY to [this dir]/igl +set(PYIGL_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/igl") + list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) # Color output include(UseColors) @@ -23,28 +69,7 @@ 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) -option(LIBIGL_COPYLEFT_TETGEN "Build target igl_copyleft::tetgen" ON) -option(LIBIGL_RESTRICTED_TRIANGLE "Build target igl_restricted::triangle" 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}) -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) @@ -57,67 +82,107 @@ function(pyigl_include prefix name) 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}") + # if(LIBIGL${prefix_uc}_${name_uc}) or name == "core" + if(LIBIGL${prefix_uc}_${name_uc} OR name STREQUAL "core") + if("${prefix}" STREQUAL "copyleft") + if("${name}" STREQUAL "core") + set(subpath "copyleft") + else() + set(subpath "copyleft/${name}") + endif() + elseif("${name}" STREQUAL "core") + set(subpath "") else() # "" or "restricted" set(subpath "${name}") endif() - file(GLOB sources src/${subpath}/*.cpp) + file(GLOB sources "${CMAKE_CURRENT_SOURCE_DIR}/src/${subpath}/*.cpp") + + ## Just compile a single file + #list(FILTER sources EXCLUDE REGEX ".*/module\\.cpp$") + #list(GET sources 0 sources) + #list(APPEND sources "${CMAKE_CURRENT_SOURCE_DIR}/src/${subpath}/module.cpp") + + message(STATUS "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$") + message(STATUS "${prefix} ${name} sources: ${sources}") + + set(BINDING_SOURCES ${sources}) + list(FILTER BINDING_SOURCES EXCLUDE REGEX ".*/module\\.cpp$") + + # keep just the first in the list and overwrite BINDING_SOURCES + + # Generate the function calls based on filenames + set(BINDING_DECLARATIONS "") + foreach(source_file ${BINDING_SOURCES}) + get_filename_component(filename ${source_file} NAME_WE) + set(BINDING_DECLARATIONS "${BINDING_DECLARATIONS}extern void bind_${filename}(nb::module_ &m);\n") + set(BINDING_INVOCATIONS "${BINDING_INVOCATIONS} bind_${filename}(m);\n") + endforeach() + # make a temporary folder in the build directory to store the generated files + set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/include/${subpath}") + file(MAKE_DIRECTORY "${generated_dir}") + # write contents into BINDING_DECLARATIONS.h and BINDING_INVOCATIONS.h + file(WRITE "${generated_dir}/BINDING_DECLARATIONS.in" "${BINDING_DECLARATIONS}") + file(WRITE "${generated_dir}/BINDING_INVOCATIONS.in" "${BINDING_INVOCATIONS}") + 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") + nanobind_add_module(${target_name} ${sources}) + + # important for scikit-build + install(TARGETS ${target_name} LIBRARY DESTINATION "igl/${subpath}") + + + if("${name}" STREQUAL "core") + target_link_libraries(${target_name} PRIVATE igl::core) + else() + target_link_libraries(${target_name} PRIVATE igl::core igl${prefix_lc}::${name}) + endif() + target_include_directories(${target_name} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/include") + target_include_directories(${target_name} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/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 + file(WRITE "${output_dir}/__init__.py" "from .${target_name} import *\n") + install(FILES "${output_dir}/__init__.py" DESTINATION "igl/${subpath}") + 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 - # https://cmake.org/cmake/help/latest/manual/cmake.1.html#cmdoption-cmake-E-arg-copy - # until then just needlessly also copy TARGET_FILE in case TARGET_RUNTIME_DLLS is empty - if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") - add_custom_command(TARGET ${target_name} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ $ $ - COMMAND_EXPAND_LISTS) + LIBRARY_OUTPUT_DIRECTORY "${output_dir}" + RUNTIME_OUTPUT_DIRECTORY "${output_dir}" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${output_dir}" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${output_dir}" + ) + set(PYI_OUTPUT "${output_dir}/${target_name}.pyi") + nanobind_add_stub( + ${target_name}_stub + MODULE ${target_name} + OUTPUT ${PYI_OUTPUT} + PYTHON_PATH $ + DEPENDS ${target_name} + ) + install(FILES "${PYI_OUTPUT}" DESTINATION "igl/${subpath}") + + # just to add dependency? + if("${name}" STREQUAL "core") + else() + target_link_libraries(pyigl_core INTERFACE ${target_name}) + endif() endif() endfunction() -pyigl_include("copyleft" "cgal") -pyigl_include("copyleft" "tetgen") -pyigl_include("restricted" "triangle") - - -file(GLOB PYIGL_CLASSES_SOURCES classes/*.cpp) -add_library(pyigl_classes MODULE ${PYIGL_CLASSES_SOURCES}) -# std::variant -target_compile_features(pyigl_classes PRIVATE cxx_std_17) -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) - -if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") - foreach(config ${CMAKE_CONFIGURATION_TYPES}) - string(TOUPPER ${config} config) - string(REPLACE /MD /MT CMAKE_C_FLAGS_${config} "${CMAKE_C_FLAGS_${config}}") - string(REPLACE /MD /MT CMAKE_CXX_FLAGS_${config} "${CMAKE_CXX_FLAGS_${config}}") - endforeach() +pyigl_include("" "core") +if(LIBIGL_COPYLEFT_CORE) + pyigl_include("copyleft" "core") 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() + + + diff --git a/README.md b/README.md index 3efa075d..76e49549 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,11 @@ [![PyPI version](https://badge.fury.io/py/libigl.svg)](https://pypi.org/project/libigl/) [![buildwheels](https://github.com/libigl/libigl-python-bindings/actions/workflows/wheels.yml/badge.svg)](https://github.com/libigl/libigl-python-bindings/actions/workflows/wheels.yml?query=branch%3Amain) - -This repository contains the source code for the libigl Python bindings. These bindings are fully compatible with NumPy and SciPy and offer a convenient interface similar to functions in those libraries. - -These bindings are still under active development and should still be considered alpha quality. We encourage users to post issues so we can improve the bindings! +This repository contains the source code for the python bindings for the C++ +[libigl](https://github.com/libigl/libigl) library written using +[nanobind](https://nanobind.readthedocs.io/en/latest/). Functions allow NumPy +arrays as input and output for dense matrices and vectors and SciPy sparse +matrices for sparse matrices. ## Installation @@ -13,53 +14,87 @@ These bindings are still under active development and should still be considered python -m pip install libigl ``` -If you wish to install the current development code, you can compile the library from scratch. Clone this repo and issue +## Documentation -``` -python -m pip install ./ -``` - - -## [Help/Documentation](https://libigl.github.io/libigl-python-bindings/) +| :warning: WARNING | +|:----------------------------| +| The [python-binding documentation](https://libigl.github.io/libigl-python-bindings/) is perennially out of date and will likely be removed/changed. | * A tutorial on how to use the bindings can be found [here](https://libigl.github.io/libigl-python-bindings/tutorials/) * A function reference can be found [here](https://libigl.github.io/libigl-python-bindings/igl_docs/) -## Compiling and modifying the bindiings -After installing numpy & scipy and then cloning this repository, you can compile the bindings from scratch by running: +## Getting the current version of libigl within python code + +Since version 2.5.4.dev0, the `igl.__version__` attribute has been removed. To +get the version of the libigl package you're using within your python code, you +can use the following code: ``` -python setup.py develop +import importlib.metadata +libigl_version = importlib.metadata.version('libigl') ``` -or +The version of libigl is defined in the [pyproject.toml](./pyproject.toml) file. -``` -python setup.py build --debug develop -``` -for debug compilation. This command will make the package `igl` available for import in the -current shell. +## Compiling and modifying the bindiings -To run the tests: +According to the [scikit-build-core documentation](https://scikit-build-core.readthedocs.io/en/latest/configuration.html#editable-installs), the way to make an editable (incremental) build is to: + + 1. Preinstall the dependencies (at the top of pyproject.toml + 2. Then use this very long command: ``` -python setup.py test +python -m pip install --no-build-isolation --config-settings=editable.rebuild=true -Cbuild-dir=build -ve. ``` -or +### Adding a missing binding -``` -python tests/test_basic.py -``` +Bindings are fairly mechanical to write. For example, suppose we didn't have a +binding for the c++ function `igl::moments`. The first step would be to look at +the corresponding `.h` header file in the C++ libigl library: +[moments.h](https://github.com/libigl/libigl/blob/main/include/igl/moments.h). -and if developing and trying to run from this directory. You could use: +Then we would create the [src/moments.cpp](src/moments.cpp) file in this project +which uses `Eigen::MatrixXN` for numeric types and `Eigen::MatrixXI` for integer +types. Typically this requires a simple wrapper around the function matching +its signature to these types and some boilerplate `void bind_moments(...` code which adds the function to the python module. -``` -PYTHONPATH=. python tests/test_basic.py -``` +Simply adding this `.cpp` file will be enough to add the bindings on the next +build. + +If submitting a pull request with a new binding, please also add an execution +test in `tests/test_all.py` to ensure the binding can at least be called as +expected. + + +## Testing cibuildwheel locally + +Install whichever version of Python from the [official website](https://www.python.org/downloads/) and then run: + + /Library/Frameworks/Python.framework/Versions/[version]/bin/python -m pip install cibuildwheel + CIBW_BUILD="cp311-*" python -m cibuildwheel --output-dir wheelhouse --platform macos + +## Downloading all the artifacts + +A successful [.github/workflows/wheels.yml](.github/workflows/wheels.yml) run will a lot of `.whl` files. To download these all at once, you can use the following command: + + mkdir wheelhouse + cd wheelhouse + gh run download [runid] + +Then these can be uploaded to pypi using: + + python -m twine upload --repository testpypi wheelhouse/*/*.whl wheelhouse/*/*.tar.gz + +## Acknowledgements -## License +The original python bindings were generated and maintained by +[@teseoch](https://github.com/teseoch), +[@KarlLeell](https://github.com/KarlLeell), +[@fwilliams](https://github.com/fwilliams), +[@skoch9](https://github.com/skoch9), and +[@danielepanozzo](https://github.com/danielepanozzo) -Like libigl, the wrapper source code is licensed under MPL2. Code included via -the copyleft and restricted sub-directories may have more restrictive licenses. +The modern python bindings (since 2.5.4.dev0) can largely be blamed on +[@alecjacobson](https://github.com/alecjacobson). diff --git a/cmake/PyiglDependencies.cmake b/cmake/PyiglDependencies.cmake deleted file mode 100644 index 8229ac05..00000000 --- a/cmake/PyiglDependencies.cmake +++ /dev/null @@ -1,47 +0,0 @@ -# Prepare dependencies -# -# For each third-party library, if the appropriate target doesn't exist yet, -# download it via external project, and add_subdirectory to build it alongside -# this project. - - -# Download and update 3rdparty libraries -list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) -list(REMOVE_DUPLICATES CMAKE_MODULE_PATH) - -################################################################################ -# Required libraries -################################################################################ - - -include(FetchContent) - -FetchContent_Declare( - libigl - GIT_REPOSITORY https://github.com/libigl/libigl.git - GIT_TAG f962e4a6b68afe978dc12a63702b7846a3e7a6ed -) -FetchContent_GetProperties(libigl) -FetchContent_MakeAvailable(libigl) - -FetchContent_Declare( - numpyeigen - GIT_REPOSITORY https://github.com/fwilliams/numpyeigen.git - GIT_TAG 14acc7a71285979016ef39041d8cd4df97e4e829) -# NumpyEigen's CMakeLists sets NPE_PYTHON_EXECUTABLE without a way to override, -# so we must include directly rather that using FetchContent_MakeAvailable -#FetchContent_MakeAvailable(numpyeigen) -# Check if population has already been performed -FetchContent_GetProperties(numpyeigen) -if(NOT numpyeigen_POPULATED) - # Fetch the content using previously declared details - FetchContent_Populate(numpyeigen) -endif() -# Push CMAKE_MODULE_PATH -set(PREV_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${numpyeigen_SOURCE_DIR}/cmake) -# Why isn't eigen_SOURCE_DIR defined? -SET(NPE_WITH_EIGEN "${libigl_SOURCE_DIR}/../eigen-src/" CACHE INTERNAL "") -include(numpyeigen) -# Pop CMAKE_MODULE_PATH -set(CMAKE_MODULE_PATH ${PREV_CMAKE_MODULE_PATH}) diff --git a/constraints.txt b/constraints.txt deleted file mode 100644 index e55abda1..00000000 --- a/constraints.txt +++ /dev/null @@ -1,2 +0,0 @@ -numpy<=1.26.4 - diff --git a/environment.yml b/environment.yml deleted file mode 100644 index f83c6e88..00000000 --- a/environment.yml +++ /dev/null @@ -1,7 +0,0 @@ -channels: - - conda-forge -dependencies: - - numpy - - scipy - - meshplot - - igl \ No newline at end of file diff --git a/igl/__init__.py b/igl/__init__.py deleted file mode 100644 index 72d042f2..00000000 --- a/igl/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This file is part of libigl, a simple c++ geometry processing library. -# -# Copyright (C) 2023 Alec Jacobson -# -# This Source Code Form is subject to the terms of the Mozilla Public License -# v. 2.0. If a copy of the MPL was not distributed with this file, You can -# obtain one at http://mozilla.org/MPL/2.0/. -from .pyigl import * -from .helpers import * -from .pyigl_classes import * -from ._version import __version__ diff --git a/igl/_version.py b/igl/_version.py deleted file mode 100644 index 887b40df..00000000 --- a/igl/_version.py +++ /dev/null @@ -1,8 +0,0 @@ -# This file is part of libigl, a simple c++ geometry processing library. -# -# Copyright (C) 2024 Alec Jacobson -# -# This Source Code Form is subject to the terms of the Mozilla Public License -# v. 2.0. If a copy of the MPL was not distributed with this file, You can -# obtain one at http://mozilla.org/MPL/2.0/. -__version__ = "2.5.4dev" diff --git a/igl/helpers.py b/igl/helpers.py deleted file mode 100644 index 76ccf97b..00000000 --- a/igl/helpers.py +++ /dev/null @@ -1,60 +0,0 @@ -# This file is part of libigl, a simple c++ geometry processing library. -# -# Copyright (C) 2017 Sebastian Koch and Daniele Panozzo -# -# This Source Code Form is subject to the terms of the Mozilla Public License -# v. 2.0. If a copy of the MPL was not distributed with this file, You can -# obtain one at http://mozilla.org/MPL/2.0/. -import os - -import numpy as np -from scipy import sparse -from scipy.sparse.linalg import spsolve -import igl - -# Enum definitions -MASSMATRIX_TYPE_BARYCENTRIC = 0 -MASSMATRIX_TYPE_VORONOI = 1 -MASSMATRIX_TYPE_FULL = 2 - -PER_VERTEX_NORMALS_WEIGHTING_TYPE_UNIFORM = 0 -PER_VERTEX_NORMALS_WEIGHTING_TYPE_AREA = 1 -PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE = 2 - -SLIM_ENERGY_TYPE_ARAP = 0 -SLIM_ENERGY_TYPE_LOG_ARAP = 1 -SLIM_ENERGY_TYPE_SYMMETRIC_DIRICHLET = 2 -SLIM_ENERGY_TYPE_CONFORMAL = 3 -SLIM_ENERGY_TYPE_EXP_CONFORMAL = 4 -SLIM_ENERGY_TYPE_EXP_SYMMETRIC_DIRICHLET = 5 - -SIGNED_DISTANCE_TYPE_PSEUDONORMAL = 0 # Use fast pseudo-normal test [Bærentzen & Aanæs 2005] -SIGNED_DISTANCE_TYPE_WINDING_NUMBER = 1 # Use winding number [Jacobson, Kavan Sorking-Hornug 2013] -SIGNED_DISTANCE_TYPE_DEFAULT = 2 -SIGNED_DISTANCE_TYPE_UNSIGNED = 3 -SIGNED_DISTANCE_TYPE_FAST_WINDING_NUMBER = 4 # Use Fast winding number [Barill, Dickson, Schmidt, Levin, Jacobson 2018] - -ARAP_ENERGY_TYPE_SPOKES = 0 -ARAP_ENERGY_TYPE_SPOKES_AND_RIMS = 1 -ARAP_ENERGY_TYPE_ELEMENTS = 2 -ARAP_ENERGY_TYPE_DEFAULT = 3 - - -def check_dependencies(deps): - import sys - available = [hasattr(igl, m) for m in deps] - all_available = True - for i, d in enumerate(available): - if not d: - all_available = False - print("The libigl python bindings were compiled without %s support. " - "Please recompile with the CMAKE flag LIBIGL_WITH_%s." %(deps[i], deps[i].upper())) - - if not all_available: - sys.exit(-1) - - -def print_usage(key_dict): - print("Usage:") - for k in key_dict.keys(): - print("%s : %s" %(k, key_dict[k])) diff --git a/include/default_types.h b/include/default_types.h new file mode 100644 index 00000000..85de9318 --- /dev/null +++ b/include/default_types.h @@ -0,0 +1,32 @@ +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////////// +/// Default numeric and integer types. Passing anything else will result in an +/// implicit copy. Developers insisting on native executation of libigl on +/// single-precision (largely untested and possibly error prone) can change this +/// to float and recompile bindings at their own delight or peril. +/////////////////////////////////////////////////////////////////////////////////// +using Numeric = double; +using Integer = int64_t; +using Boolean = bool; +// When using `const nb::DRef` this `Options` does not +// affect the input types (either order should result in no copy if `dtype`s +// match). It does affect return types, `Eigen::MatrixXN X; … ; return X;` +constexpr auto Options = Eigen::RowMajor; +namespace Eigen +{ + typedef Matrix MatrixXN; + typedef Matrix MatrixXI; + typedef Matrix MatrixXB; + typedef Matrix VectorXN; + typedef Matrix VectorXI; + typedef Matrix VectorXB; + typedef Matrix RowVectorXN; + typedef Matrix RowVectorXI; + typedef Matrix RowVectorXB; + typedef SparseMatrix SparseMatrixN; + typedef SparseMatrix SparseMatrixI; + typedef SparseMatrix SparseMatrixB; +} diff --git a/missing.sh b/missing.sh new file mode 100644 index 00000000..9038122d --- /dev/null +++ b/missing.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# +already=$(ls src/*.cpp | sed -e "s/src\/\(.*\).cpp$/\1/g") +skip=" +ARAPEnergyType +EPS +Hit +IGL_ASSERT +LinSpaced +PI +cat +colon +cumsum +decimate_callback_types +find +get_seconds +igl_inline +list_to_matrix +matrix_to_list +min_quad_with_fixed.impl +parallel_for +pathinfo +placeholders +quadric_binary_plus_operator +redux +repdiag +repmat +rotation_matrix_from_directions +setdiff +slice +sort +sortrows +sparse +speye +sum +tinyply +unique +unique_rows +verbose +decimate_trivial_callbacks +edges +for_each +generate_default_urbg +max_size +min +min_size +collapse_edge +max +mode +main +gl +opengl/glfw/Viewer +C_STR +STR +" +# combine these into exclude +exclude=$(echo -e "$already\n$skip" | sort | uniq) + +# but don't include any from exclude + +# Run grep and filter out excluded files +include_igl=$(grep -hor '#include "[^"]\+"' /Users/alecjacobson/Repos/libigl/include/igl/ | sed 's/#include "\(.*\).h"/\1/') +# remove any .cpp includes +include_igl=$(echo "$include_igl" | grep -v -F ".cpp") +# remove any number of "../" from the beginning of the line +include_igl=$(echo "$include_igl" | sed -e 's#^\(\.\./\)*##') +# stray includes +include_igl=$(echo "$include_igl" | grep -v -F "#include") + +tutorial_igl=$(grep -hor '#include <[^>]\+>' /Users/alecjacobson/Repos/libigl/tutorial/ | sed -n 's/#include /\1/p') +tests_igl=$(grep -hor '#include <[^>]\+>' /Users/alecjacobson/Repos/libigl/tests/ | sed -n 's/#include /\1/p') +# append to include_igl +all=$(echo -e "$include_igl\n$tutorial_igl\n$tests_igl") +# +echo "$all" | grep -v -F -w -f <(echo "$exclude") | sort | uniq -c | sort -n diff --git a/priority.txt b/priority.txt new file mode 100644 index 00000000..74e863fc --- /dev/null +++ b/priority.txt @@ -0,0 +1,108 @@ + +false_barycentric_subdivision.cpp +fit_cubic_bezier.cpp +flip_edge.cpp +hausdorff.cpp +euler_characteristic.cpp +grad_intrinsic.cpp +hessian.cpp +hessian_energy.cpp +internal_angles.cpp +is_delaunay.cpp +is_intrinsic_delaunay.cpp +is_irregular_vertex.cpp +iterative_closest_point.cpp +lbs_matrix.cpp +map_vertices_to_circle.cpp +marching_tets.cpp +mvc.cpp +offset_surface.cpp +orient_outward.cpp +per_corner_normals.cpp +per_edge_normals.cpp +planarize_quad_mesh.cpp +piecewise_constant_winding_number.cpp +project_isometrically_to_plane.cpp +quad_planarity.cpp +ramer_douglas_peucker.cpp +random_points_on_mesh_intrinsic.cpp +shape_diameter_function.cpp +sharp_edges.cpp +signed_angle.cpp +simplify_polyhedron.cpp +solid_angle.cpp +sparse_voxel_grid.cpp +tet_tet_adjacency.cpp + +active_set.cpp +all_pairs_distances.cpp +ambient_occlusion.cpp +arap_linear_block.cpp +arap_rhs.cpp +bfs.cpp +bone_parents.cpp +direct_delta_mush.cpp +boundary_conditions.cpp +bounding_box_diagonal.cpp +collapse_small_triangles.cpp +comb_cross_field.cpp +comb_frame_field.cpp +comb_line_field.cpp +compute_frame_field_bisectors.cpp +connect_boundary_to_infinity.cpp +cross_field_missmatch.cpp +deform_skeleton.cpp +directed_edge_orientations.cpp +directed_edge_parents.cpp +dqs.cpp +edge_collapse_is_valid.cpp +edge_topology.cpp +edges_to_path.cpp +exterior_edges.cpp +extract_manifold_patches.cpp +extract_non_manifold_edge_curves.cpp +face_occurrences.cpp +faces_first.cpp +find_cross_field_singularities.cpp +fit_plane.cpp +flip_avoiding_line_search.cpp +flipped_triangles.cpp +forward_kinematics.cpp +vector_area_matrix.cpp + +line_segment_in_rectangle.cpp +look_at.cpp +normal_derivative.cpp +orientable_patches.cpp +outer_element.cpp +partition.cpp +path_to_edges.cpp +per_vertex_attribute_smoothing.cpp +point_in_circle.cpp +point_simplex_squared_distance.cpp +polar_dec.cpp +procrustes.cpp +pso.cpp +random_search.cpp +ray_box_intersect.cpp +ray_sphere_intersect.cpp +readTGF.cpp +resolve_duplicated_faces.cpp +rigid_alignment.cpp +rotate_vectors.cpp +sample_edges.cpp +segment_segment_intersect.cpp +snap_points.cpp +sort_angles.cpp +swept_volume_bounding_box.cpp +topological_hole_fill.cpp +triangle_fan.cpp +triangles_from_strip.cpp +two_axis_valuator_fixed_up.cpp +uniformly_sample_two_manifold.cpp +unproject_in_mesh.cpp +unproject_on_line.cpp +unproject_on_plane.cpp +unproject_onto_mesh.cpp +unproject_ray.cpp +writeOFF.cpp diff --git a/pyproject.toml b/pyproject.toml index 14fb3222..b6cc5884 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,14 +1,61 @@ -[tool.cibuildwheel] -build-verbosity = "3" [build-system] requires = [ - "numpy", - "scipy", - "setuptools>=42", - "wheel", "cmake>=3.16", + "nanobind >=1.3.2", + "numpy>=2.0.0; python_version >= '3.9'", + "numpy; python_version < '3.9'", "packaging", + "scikit-build-core >=0.10", + "scipy", + "typing_extensions", + ] +build-backend = "scikit_build_core.build" + + +[project] +name = "libigl" +version = "2.5.4dev" +description = "libigl: A simple C++ geometry processing library" +readme = "README.md" +requires-python = ">=3.8" +authors = [ + { name = "Alec Jacobson", email = "alecjacobson@gmail.com" }, +] +classifiers = [ + "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)" +] +dependencies = [ + "numpy", + "scipy" ] -build-backend = "setuptools.build_meta" +[project.urls] +Homepage = "https://github.com/libigl/libigl-python-bindings" + +[tool.scikit-build] +# Protect the configuration against future changes in scikit-build-core +minimum-version = "build-system.requires" + +# Setuptools-style build caching in a local directory +build-dir = "build/{wheel_tag}" + +# Build stable ABI wheels for CPython 3.12+ +wheel.py-api = "cp312" + +[tool.scikit-build.cmake] +build-type = "Release" + +[tool.cibuildwheel] +# Necessary to see build output from the actual compilation +build-verbosity = 1 + +# Run pytest to ensure that the package was correctly built +test-command = "pytest {project}/tests" +test-requires = "pytest" + +# Don't test Python 3.8 wheels on macOS/arm64 +test-skip="cp38-macosx_*:arm64" +# Needed for full C++17 support +[tool.cibuildwheel.macos.environment] +MACOSX_DEPLOYMENT_TARGET = "10.15" diff --git a/setup.py b/setup.py deleted file mode 100644 index 6799e6e2..00000000 --- a/setup.py +++ /dev/null @@ -1,162 +0,0 @@ -import os -import re -import sys -import platform -import subprocess - -from packaging.version import Version -from setuptools import setup, Extension, find_packages -from setuptools.command.build_ext import build_ext - -__version__ = '0.0.1' - -class CMakeExtension(Extension): - def __init__(self, name, sourcedir=''): - Extension.__init__(self, name, sources=[]) - self.sourcedir = os.path.abspath(sourcedir) - - -class CMakeBuild(build_ext): - def run(self): - try: - out = subprocess.check_output(['cmake', '--version']) - except OSError: - raise RuntimeError( - "CMake must be installed to build the following extensions: , ".join(e.name for e in self.extensions)) - - # self.debug = True - - cmake_version = Version(re.search(r'version\s*([\d.]+)', out.decode()).group(1)) - if cmake_version < Version('3.2.0'): - raise RuntimeError("CMake >= 3.2.0 is required") - - for ext in self.extensions: - self.build_extension(ext) - - - def build_extension(self, ext): - extdir = os.path.join(os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))),"igl") - - cmake_args = ['-DPYIGL_OUTPUT_DIRECTORY=' + extdir, - '-DPYTHON_EXECUTABLE=' + sys.executable] - - - cfg = 'Debug' if self.debug else 'Release' - build_args = ['--config', cfg] - cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg] - # cmake_args += ['-DDEBUG_TRACE=ON'] - - if platform.system() == "Windows": - cmake_generator = os.environ.get('CMAKE_GENERATOR', '') - if cmake_generator != "NMake Makefiles" and "Ninja" not in cmake_generator: - if sys.maxsize > 2**32: - cmake_args += ['-A', 'x64'] - # build_args += ['--', '/m'] - else: - if "MAX_JOBS" in os.environ: - build_args += ['--', f"-j{os.environ['MAX_JOBS']}"] - else: - build_args += ['--', '-j8'] - - - tmp = os.environ.get("AR", "") - if "arm64-apple" in tmp: - tmp = os.environ.get("CMAKE_ARGS", "") - if tmp: - cmake_args += tmp.split(" ") - - tmp = os.environ.get("CC", "") - print("C compiler", tmp) - if tmp: - cmake_args += ["-DCMAKE_C_COMPILER={}".format(tmp)] - - tmp = os.environ.get("CXX", "") - print("CXX compiler", tmp) - if tmp: - cmake_args += ["-DCMAKE_CXX_COMPILER={}".format(tmp)] - else: - tmp = os.getenv('CC_FOR_BUILD', '') - if tmp: - print("Setting c compiler to", tmp) - cmake_args += ["-DCMAKE_C_COMPILER=" + tmp] - - tmp = os.getenv('CXX_FOR_BUILD', '') - if tmp: - print("Setting cxx compiler to", tmp) - cmake_args += ["-DCMAKE_CXX_COMPILER="+ tmp] - - env = os.environ.copy() - env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''),self.distribution.get_version()) - - - - tmp = os.getenv("target_platform", "") - if tmp: - print("target platfrom", tmp) - if "arm" in tmp: - cmake_args += ["-DCMAKE_OSX_ARCHITECTURES=arm64"] - - # print(cmake_args) - # tmp = os.getenv('CMAKE_ARGS', '') - - # if tmp: - # tmp = tmp.split(" ") - # print("tmp", tmp) - # cmake_args += tmp - - # cmake_args += ["-DCMAKE_OSX_ARCHITECTURES" , "arm64"] - # print(cmake_args) - - if not os.path.exists(self.build_temp): - os.makedirs(self.build_temp) - subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env) - - # print build_args - print("********************************************************************") - print("********************************************************************") - print("********************************************************************") - print("********************************************************************") - print("build_args:", build_args) - print("********************************************************************") - print("********************************************************************") - print("********************************************************************") - print("********************************************************************") - subprocess.check_call(['cmake', '--build', '.'] + build_args, cwd=self.build_temp) - - print() # Add an empty line for cleaner output - - -with open("README.md", "r") as fh: - long_description = fh.read() - - -# https://stackoverflow.com/a/7071358/148668 -import re -VERSIONFILE="igl/_version.py" -verstrline = open(VERSIONFILE, "rt").read() -VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]" -mo = re.search(VSRE, verstrline, re.M) -if mo: - verstr = mo.group(1) -else: - raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,)) - -setup( - name="libigl", - version=verstr, - author="libigl", - author_email="", - description="libigl Python Bindings", - long_description=long_description, - long_description_content_type="text/markdown", - url="https://libigl.github.io/libigl-python-bindings/", - ext_modules=[CMakeExtension('pyigl')], - install_requires=[ 'numpy', 'scipy' ], - cmdclass=dict(build_ext=CMakeBuild), - packages=find_packages(), - classifiers=[ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License" - ], - test_suite="tests" -) diff --git a/src/AABB.cpp b/src/AABB.cpp new file mode 100644 index 00000000..93ed1579 --- /dev/null +++ b/src/AABB.cpp @@ -0,0 +1,110 @@ +#include "default_types.h" +#include +#include +#include +#include +#include +#include +#include +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + template + void aabb_init( + AABB &tree, + const nb::DRef &V, + const nb::DRef &Ele) + { + tree.init(V,Ele); + } + template + auto find( + AABB &tree, + const nb::DRef &V, + const nb::DRef &Ele, + const nb::DRef &q, + const bool first) + { + if(Ele.cols() != V.cols()+1) + { + throw std::runtime_error("find: Ele must have V.cols()+1 columns"); + } + std::vector vec_int = tree.find(V,Ele,q,first); + std::vector vec(vec_int.begin(),vec_int.end()); + return vec; + } + + template + auto squared_distance( + AABB &tree, + const nb::DRef &V, + const nb::DRef &Ele, + const nb::DRef &P) + { + Eigen::VectorXN sqrD; + Eigen::VectorXI I; + Eigen::MatrixXN C; + tree.squared_distance(V,Ele,P,sqrD,I,C); + return std::make_tuple(sqrD,I,C); + } + + template + auto intersect_ray_first( + AABB &tree, + const nb::DRef &V, + const nb::DRef &Ele, + const nb::DRef &orig, + const nb::DRef &dir, + const Numeric min_t) + { + Eigen::VectorXI I; + Eigen::VectorXN T; + Eigen::MatrixXN UV; + tree.intersect_ray(V,Ele,orig,dir,min_t,I,T,UV); + return std::make_tuple(I,T,UV); + } + + template + auto intersect_ray( + AABB &tree, + const nb::DRef &V, + const nb::DRef &Ele, + const nb::DRef &orig, + const nb::DRef &dir) + { + std::vector>> hits; + tree.intersect_ray(V,Ele,orig,dir,hits); + std::vector>> out; + for(const auto &hit : hits) + { + std::vector> hit_out; + for(const auto &h : hit) + { + hit_out.push_back(std::make_tuple(h.id,h.t,h.u,h.v)); + } + out.push_back(hit_out); + } + + return out; + } +} + +// Bind the wrapper to the Python module +void bind_AABB(nb::module_ &m) +{ + typedef igl::AABB,3> AABBN3; + nb::class_(m, "AABB") + .def(nb::init<>()) + .def("init", &pyigl::aabb_init, "V"_a, "Ele"_a) + .def("find", &pyigl::find, "V"_a, "Ele"_a, "q"_a, "first"_a=false) + .def("squared_distance", &pyigl::squared_distance, "V"_a, "Ele"_a, "P"_a) + .def("intersect_ray_first",&pyigl::intersect_ray_first, "V"_a, "Ele"_a, "orig"_a, "dir"_a, "min_t"_a=std::numeric_limits::infinity()) + .def("intersect_ray",&pyigl::intersect_ray, "V"_a, "Ele"_a, "orig"_a, "dir"_a) + ; +} + + + + diff --git a/src/FileEncoding.cpp b/src/FileEncoding.cpp new file mode 100644 index 00000000..5add4652 --- /dev/null +++ b/src/FileEncoding.cpp @@ -0,0 +1,15 @@ +#include "default_types.h" +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +void bind_FileEncoding(nb::module_ &m) +{ + nb::enum_(m,"FileEncoding") + .value("Ascii", igl::FileEncoding::Ascii) + .value("Binary", igl::FileEncoding::Binary) + .export_values(); +} diff --git a/src/MappingEnergyType.cpp b/src/MappingEnergyType.cpp new file mode 100644 index 00000000..a6ecd0df --- /dev/null +++ b/src/MappingEnergyType.cpp @@ -0,0 +1,21 @@ +#include "default_types.h" +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +void bind_MappingEnergyType(nb::module_ &m) +{ + nb::enum_(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() + ; +} + diff --git a/src/MassMatrixType.cpp b/src/MassMatrixType.cpp new file mode 100644 index 00000000..94a2d08c --- /dev/null +++ b/src/MassMatrixType.cpp @@ -0,0 +1,20 @@ +#include "default_types.h" +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + + +void bind_MassMatrixType(nb::module_ &m) +{ + nb::enum_(m, "MassMatrixType") + .value("MASSMATRIX_TYPE_BARYCENTRIC", igl::MASSMATRIX_TYPE_BARYCENTRIC) + .value("MASSMATRIX_TYPE_VORONOI", igl::MASSMATRIX_TYPE_VORONOI) + .value("MASSMATRIX_TYPE_FULL", igl::MASSMATRIX_TYPE_FULL) + .value("MASSMATRIX_TYPE_DEFAULT", igl::MASSMATRIX_TYPE_DEFAULT) + .export_values() + ; +} diff --git a/src/active_set.cpp b/src/active_set.cpp deleted file mode 100644 index 4f4a6c2b..00000000 --- a/src/active_set.cpp +++ /dev/null @@ -1,140 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_active_set = R"igl_Qu8mg5v7( -ACTIVE_SET Minimize quadratic energy - -0.5*Z'*A*Z + Z'*B + C with constraints -that Z(known) = Y, optionally also subject to the constraints Aeq*Z = Beq, -and further optionally subject to the linear inequality constraints that -Aieq*Z <= Bieq and constant inequality constraints lx <= x <= ux - -Parameters ----------- -A n by n matrix of quadratic coefficients -B n by 1 column of linear coefficients -known list of indices to known rows in Z -Y list of fixed values corresponding to known rows in Z -Aeq meq by n list of linear equality constraint coefficients -Beq meq by 1 list of linear equality constraint constant values -Aieq mieq by n list of linear inequality constraint coefficients -Bieq mieq by 1 list of linear inequality constraint constant values -lx n by 1 list of lower bounds [] implies -Inf -ux n by 1 list of upper bounds [] implies Inf -params struct of additional parameters (see below) -Z if not empty, is taken to be an n by 1 list of initial guess values (see output) -Returns -------- -Z n by 1 list of solution values -Returns SOLVER_STATUS_CONVERGED = 0, SOLVER_STATUS_MAX_ITER = 1, SOLVER_STATUS_ERROR = 2, - -See also --------- - -Notes ------ -For a harmonic solve on a mesh with 325K facets, matlab 2.2 secs, igl / min_quad_with_fixed.h 7.1 secs - -Known Bugs : rows of[Aeq; Aieq] **must **be linearly independent.Should be using QR decomposition otherwise : http : //www.okstate.edu/sas/v8/sashtml/ormp/chap5/sect32.htm - - -Examples --------- - -)igl_Qu8mg5v7"; - -//NONe and dense_like -npe_function(active_set) -npe_doc(ds_active_set) -npe_arg(A, sparse_float, sparse_double) -npe_arg(B, dense_float, dense_double) -npe_arg(known, dense_int32, dense_int64) -npe_arg(Y, npe_matches(B)) -npe_arg(Aeq, npe_matches(A)) -npe_arg(Beq, npe_matches(B)) -npe_arg(Aieq, npe_matches(A)) -npe_arg(Bieq, npe_matches(B)) -npe_arg(lx, npe_matches(B)) -npe_arg(ux, npe_matches(B)) - -npe_default_arg(Auu_pd, bool, false) -npe_default_arg(max_iter, int, 100) -npe_default_arg(inactive_threshold, double, igl::DOUBLE_EPS) -npe_default_arg(constraint_threshold, double, igl::DOUBLE_EPS) -npe_default_arg(solution_diff_threshold, double, igl::DOUBLE_EPS) - -npe_begin_code() - assert_cols_equals(A, A.rows(), "A"); - assert_rows_match(A, B, "A", "B"); - assert_cols_equals(B, 1, "B"); - - assert_cols_match(A, Aeq, "A", "Aeq"); - assert_rows_match(Aeq, Beq, "Aeq", "Beq"); - assert_cols_equals(Beq, 1, "Beq"); - - assert_rows_match(Aieq, Bieq, "Aieq", "Bieq"); - - if(Aieq.size() > 0) - { - assert_cols_match(A, Aieq, "A", "Aieq"); - assert_cols_equals(Bieq, 1, "Bieq"); - } - - if(lx.size() > 0) - { - assert_rows_match(A, lx, "A", "lx"); - assert_cols_equals(lx, 1, "lx"); - } - - if (ux.size() > 0) - { - assert_rows_match(A, ux, "A", "ux"); - assert_cols_equals(ux, 1, "ux"); - } - - Eigen::SparseMatrix A_copy = A.template cast(); - Eigen::VectorXd B_copy = B.template cast(); - Eigen::VectorXi known_copy = known.template cast(); - Eigen::VectorXd Y_copy = Y.template cast(); - Eigen::SparseMatrix Aeq_copy = Aeq.template cast(); - Eigen::VectorXd Beq_copy = Beq.template cast(); - Eigen::SparseMatrix Aieq_copy; - Eigen::VectorXd Bieq_copy; - - if(Aieq.size() > 0) - { - Aieq_copy = Aieq.template cast(); - Bieq_copy = Bieq.template cast(); - } - - Eigen::VectorXd lx_copy; - if(lx.size() > 0) - lx_copy = lx.template cast(); - Eigen::VectorXd ux_copy; - if (ux.size() > 0) - ux_copy = ux.template cast(); - - igl::active_set_params params; - params.Auu_pd = Auu_pd; - params.max_iter = max_iter; - params.inactive_threshold = inactive_threshold; - params.constraint_threshold = constraint_threshold; - params.solution_diff_threshold = solution_diff_threshold; - - Eigen::VectorXd Z; - - auto res = igl::active_set(A_copy, B_copy, known_copy, Y_copy, Aeq_copy, Beq_copy, Aieq_copy, Bieq_copy, lx_copy, ux_copy, params, Z); - EigenDenseLike Z_row_major = Z.template cast(); - - return std::make_tuple(int(res), npe::move(Z_row_major)); - - npe_end_code() diff --git a/src/adjacency_list.cpp b/src/adjacency_list.cpp index 7a544ed7..3c5efd1c 100644 --- a/src/adjacency_list.cpp +++ b/src/adjacency_list.cpp @@ -1,47 +1,38 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include +#include "default_types.h" #include -#include - -const char* ds_adjacency_list = R"igl_Qu8mg5v7( -Constructs the graph adjacency list of a given mesh (v, f) - -Parameters ----------- -f : #f by dim array of fixed dimensional (e.g. triangle (#f by 3), - tet (#f by 4), quad (#f by 4), etc...) mesh faces - -Returns -------- -list of lists containing at index i the adjacent vertices of vertex i - -See also --------- -adjacency_matrix - -Notes ------ - -Examples --------- -# Mesh in (v, f) ->>> a = mesh_adjacency_list(f) -)igl_Qu8mg5v7"; - -npe_function(adjacency_list) -npe_doc(ds_adjacency_list) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - assert_valid_tet_or_tri_mesh_faces(f, "f"); - std::vector> a; - igl::adjacency_list(f, a); - return pybind11::detail::type_caster::cast(a, pybind11::return_value_policy::move, pybind11::none()); - -npe_end_code() +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for the first overload of adjacency_list for triangle meshes + auto adjacency_list( + const nb::DRef &F, + bool sorted) + { + std::vector> A; + igl::adjacency_list(F, A, sorted); + return A; + } +} + +// Bind the wrappers to the Python module +void bind_adjacency_list(nb::module_ &m) +{ + // Binding for triangle mesh adjacency_list + m.def( + "adjacency_list", + &pyigl::adjacency_list, + "F"_a, + "sorted"_a = false, + R"(Constructs the graph adjacency list for a given triangle mesh. + +@param[in] F #F by dim list of mesh faces +@param[in] sorted Boolean flag to sort adjacency counter-clockwise +@return List of adjacent vertices for each vertex)"); + +} diff --git a/src/adjacency_matrix.cpp b/src/adjacency_matrix.cpp index 2f67d2fb..03d3459a 100644 --- a/src/adjacency_matrix.cpp +++ b/src/adjacency_matrix.cpp @@ -1,59 +1,53 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_adjacency_matrix = R"igl_Qu8mg5v7( -Constructs the graph adjacency matrix of a given mesh (v, f). - -Parameters ----------- -f : #f by dim list of mesh simplices - -Returns -------- -a : max(f) by max(f) cotangent matrix, each row i corresponding to v(i, :) - -See also --------- -adjacency_list, edges, cotmatrix, diag - -Notes ------ -None - -Examples --------- -# Mesh in (v, f) ->>> a = adjacency_matrix(f) - -# Sum each row ->>> a_sum = np.sum(a, axis=1) - -# Convert row sums into diagonal of sparse matrix ->>> a_diag = diag(a_sum) - -# Build uniform laplacian ->>> u = a - a_diag -)igl_Qu8mg5v7"; - -npe_function(adjacency_matrix) -npe_doc(ds_adjacency_matrix) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - - assert_valid_simplex_idxs(f, "f"); - EigenSparseLike a; - igl::adjacency_matrix(f, a); - return npe::move(a); - -npe_end_code() - - +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for adjacency_matrix with simplicial mesh (F) + auto adjacency_matrix(const nb::DRef &F) + { + Eigen::SparseMatrixI A; + igl::adjacency_matrix(F, A); + return A; + } + + // Wrapper for adjacency_matrix with polygonal mesh (I, C) + auto adjacency_matrix_polygon( + const nb::DRef &I, + const nb::DRef &C) + { + Eigen::SparseMatrixI A; + igl::adjacency_matrix(I, C, A); + return A; + } +} + +// Bind the wrapper to the Python module +void bind_adjacency_matrix(nb::module_ &m) +{ + m.def( + "adjacency_matrix", + &pyigl::adjacency_matrix, + "F"_a, +R"(Constructs the adjacency matrix for a simplicial mesh. + +@param[in] F #F by dim matrix of mesh simplices +@return A Sparse adjacency matrix of size max(F)+1 by max(F)+1)"); + + m.def( + "adjacency_matrix", + &pyigl::adjacency_matrix_polygon, + "I"_a, + "C"_a, +R"(Constructs the adjacency matrix for a polygon mesh. + +@param[in] I Vectorized list of polygon corner indices into rows of some matrix V +@param[in] C Cumulative polygon sizes such that C(i+1)-C(i) = size of the ith polygon +@return A Sparse adjacency matrix of size max(I)+1 by max(I)+1)"); +} diff --git a/src/all_pairs_distances.cpp b/src/all_pairs_distances.cpp deleted file mode 100644 index 86efe66d..00000000 --- a/src/all_pairs_distances.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_all_pairs_distances = R"igl_Qu8mg5v7( -compute distances between each point i in V and point j in U - -Parameters ----------- -V #V by dim list of points -U #U by dim list of points -squared whether to return squared distances - -Returns -------- -D #V by #U matrix of distances, where D(i,j) gives the distance or squareed distance between V(i,:) and U(j,:) - -See also --------- - - -Notes ------ - - -Examples --------- -D = all_pairs_distances(u,v) - - -)igl_Qu8mg5v7"; - -npe_function(all_pairs_distances) -npe_doc(ds_all_pairs_distances) - -npe_arg(u, dense_float, dense_double) -npe_arg(v, npe_matches(u)) -npe_arg(squared, bool) - - -npe_begin_code() - assert_cols_match(u, v, "u", "v"); - assert_nonzero_rows(u, "u"); - assert_nonzero_rows(v, "v"); - //same input/output template - EigenDenseLike u_copy = u; - EigenDenseLike v_copy = v; - - EigenDenseLike D; - igl::all_pairs_distances(u_copy, v_copy, squared, D); - return npe::move(D); - -npe_end_code() - - diff --git a/src/ambient_occlusion.cpp b/src/ambient_occlusion.cpp deleted file mode 100644 index 6bd4f9b2..00000000 --- a/src/ambient_occlusion.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __missing miss the rest two functions with AABB and shoot_ray. __example - -#include -#include -#include -#include - -const char* ds_ambient_occlusion = R"igl_Qu8mg5v7( - -Parameters ----------- -V #V by 3 list of mesh vertex positions -F #F by 3 list of mesh face indices into V -P #P by 3 list of origin points -N #P by 3 list of origin normals - -Returns -------- -S #P list of ambient occusion values between 1 (fully occluded) and 0 (not occluded) - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(ambient_occlusion) -npe_doc(ds_ambient_occlusion) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(p, dense_float, dense_double) -npe_arg(n, npe_matches(p)) -npe_arg(num_samples, int) -npe_begin_code() - - assert_valid_3d_tri_mesh(v, f); - assert_cols_equals(p, 3, "p"); - assert_cols_equals(n, 3, "n"); - assert_nonzero_rows(p, "p"); - assert_nonzero_rows(n, "n"); - assert_shapes_match(p, n, "p", "n"); - - EigenDenseLike s; - igl::ambient_occlusion(v, f, p, n, num_samples, s); - return npe::move(s); - -npe_end_code() diff --git a/src/arap.cpp b/src/arap.cpp new file mode 100644 index 00000000..f1db78b1 --- /dev/null +++ b/src/arap.cpp @@ -0,0 +1,93 @@ +#include "default_types.h" +// Not templated at all +#include +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto arap_precomputation( + const nb::DRef &V, + const nb::DRef &F, + const int dim, + // Will be cast to int32_t in arap, so avoid potential double copy if passed + // as int32_t + const nb::DRef &b, + igl::ARAPData &data) + { + if(!igl::arap_precomputation(V,F,dim,b,data)) + { + throw std::runtime_error("arap_precomputation failed"); + } + } + + auto arap_solve( + const nb::DRef & bc, + igl::ARAPData &data, + const nb::DRef & U0) + { + // igl::arap_solve expects U to be both the initial guess and the output + // not sure how to avoid this copy + Eigen::MatrixXN U = U0; + if(!igl::arap_solve(bc,data,U)) + { + throw std::runtime_error("arap_solve failed"); + } + return U; + } + +} + +// Bind the wrapper to the Python module +void bind_arap(nb::module_ &m) +{ + nb::enum_(m, "ARAPEnergyType") + .value("ARAP_ENERGY_TYPE_DEFAULT", igl::ARAPEnergyType::ARAP_ENERGY_TYPE_DEFAULT) + .value("ARAP_ENERGY_TYPE_SPOKES", igl::ARAPEnergyType::ARAP_ENERGY_TYPE_SPOKES) + .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_(m, "ARAPData") + .def(nb::init<>()) + .def_ro("n", &igl::ARAPData::n) + .def_rw("G", &igl::ARAPData::G) + .def_rw("energy", &igl::ARAPData::energy) + .def_rw("with_dynamics", &igl::ARAPData::with_dynamics) + .def_rw("f_ext", &igl::ARAPData::f_ext) + .def_rw("vel", &igl::ARAPData::vel) + .def_rw("h", &igl::ARAPData::h) + .def_rw("ym", &igl::ARAPData::ym) + .def_rw("max_iter", &igl::ARAPData::max_iter) + ; + + m.def( + "arap_precomputation", + &pyigl::arap_precomputation, + "V"_a, + "F"_a, + "dim"_a, + "b"_a, + "data"_a, + R"()"); + m.def( + "arap_solve", + &pyigl::arap_solve, + "bc"_a, + "data"_a, + "U"_a, + R"()"); +} + + + + + diff --git a/src/arap_linear_block.cpp b/src/arap_linear_block.cpp deleted file mode 100644 index ca5e5bea..00000000 --- a/src/arap_linear_block.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include -#include - -const char* ds_arap_linear_block = R"igl_Qu8mg5v7( -Constructs a block of the matrix which constructs the -linear terms of a given arap energy. When treating rotations as knowns -(arranged in a column), then this constructs Kd of K such that the linear -portion of the energy is as a column: - K * R = [Kx Z ... Ky Z ... - Z Kx ... Z Ky ... - ... ] -These blocks are also used to build the "covariance scatter matrices". -Here we want to build a scatter matrix that multiplies against positions -(treated as known) producing covariance matrices to fit each rotation. -Notice that in the case of the RHS of the poisson solve the rotations are -known and the positions unknown, and vice versa for rotation fitting. -These linear block just relate the rotations to the positions, linearly in -each. - - -Parameters ----------- -v : #v by dim list of initial domain positions -f : #f by #simplex size list of triangle indices into V -d : coordinate of linear constructor to build - -Returns -------- -#v by #v/#f block of the linear constructor matrix corresponding to coordinate d - -See also --------- -arap, arap_dof - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -/* - * arap_linear_block - */ -npe_function(arap_linear_block) -npe_doc(ds_arap_linear_block) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(d, int) -npe_arg(energy, int) -npe_begin_code() - - assert_valid_tet_or_tri_mesh(v, f); - static_assert(int(igl::ARAPEnergyType::ARAP_ENERGY_TYPE_SPOKES) == 0, "ARAPEnergyType enum changed!"); - static_assert(int(igl::ARAPEnergyType::ARAP_ENERGY_TYPE_SPOKES_AND_RIMS) == 1, "ARAPEnergyType enum changed!"); - static_assert(int(igl::ARAPEnergyType::ARAP_ENERGY_TYPE_ELEMENTS) == 2, "ARAPEnergyType enum changed!"); - static_assert(int(igl::ARAPEnergyType::ARAP_ENERGY_TYPE_DEFAULT) == 3, "ARAPEnergyType enum changed!"); - static_assert(int(igl::ARAPEnergyType::NUM_ARAP_ENERGY_TYPES) == 4, "ARAPEnergyType enum changed!"); - - if (energy >= igl::NUM_ARAP_ENERGY_TYPES) { - std::string errmsg = - std::string("Invalid enum for energy type should be in the range 0 to ") + - std::to_string(igl::NUM_ARAP_ENERGY_TYPES-1); - throw pybind11::value_error(errmsg); - } - - EigenSparseLike kd; - igl::arap_linear_block(v, f, d, igl::ARAPEnergyType(energy), kd); - return npe::move(kd); - -npe_end_code() - - - - -/* - * arap_linear_block_spokes - */ -npe_function(arap_linear_block_spokes) -npe_doc(ds_arap_linear_block) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(d, int) -npe_begin_code() - - EigenSparseLike kd; - igl::arap_linear_block_spokes(v, f, d, kd); - return npe::move(kd); - -npe_end_code() - - - -/* - * arap_linear_block_spokes_and_rims - */ -npe_function(arap_linear_block_spokes_and_rims) -npe_doc(ds_arap_linear_block) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(d, int) -npe_begin_code() - - EigenSparseLike kd; - igl::arap_linear_block_spokes_and_rims(v, f, d, kd); - return npe::move(kd); - -npe_end_code() - - - - -/* - * arap_linear_block_spokes_and_rims - */ -npe_function(arap_linear_block_elements) -npe_doc(ds_arap_linear_block) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(d, int) -npe_begin_code() - - EigenSparseLike kd; - igl::arap_linear_block_elements(v, f, d, kd); - return npe::move(kd); - -npe_end_code() diff --git a/src/arap_rhs.cpp b/src/arap_rhs.cpp deleted file mode 100644 index 4da6e576..00000000 --- a/src/arap_rhs.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char* ds_arap_rhs = R"igl_Qu8mg5v7( -Build right-hand side constructor of global poisson solve for various ARAP energies - Inputs: - - Outputs: - K #V*dim by #(F|V)*dim*dim matrix such that: - b = K * reshape(permute(R,[3 1 2]),size(V|F,1)*size(V,2)*size(V,2),1); - -Parameters ----------- -v : #v by Vdim list of initial domain positions -f : #f by 3 list of triangle indices into v -d : dimension being used at solve time. For deformation usually dim = V.cols(), for surface parameterization V.cols() = 3 and dim = 2 -energy : ARAPEnergyType enum value defining which energy is being used. See igl.ARAPEnergyType for valid options and explanations. - -Returns -------- -#v*d by #(f|v)*dim*dim matrix such that: b = K * reshape(permute(R,[3 1 2]),size(V|F,1)*size(V,2)*size(V,2),1); - -See also --------- -arap_linear_block, arap - -Notes ------ - -Examples --------- -)igl_Qu8mg5v7"; - -npe_function(arap_rhs) -npe_doc(ds_arap_rhs) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(d, int) -npe_arg(energy, int) -npe_begin_code() - - assert_valid_3d_tri_mesh(v, f); - - static_assert(int(igl::ARAPEnergyType::ARAP_ENERGY_TYPE_SPOKES) == 0, "ARAPEnergyType enum changed!"); - static_assert(int(igl::ARAPEnergyType::ARAP_ENERGY_TYPE_SPOKES_AND_RIMS) == 1, "ARAPEnergyType enum changed!"); - static_assert(int(igl::ARAPEnergyType::ARAP_ENERGY_TYPE_ELEMENTS) == 2, "ARAPEnergyType enum changed!"); - static_assert(int(igl::ARAPEnergyType::ARAP_ENERGY_TYPE_DEFAULT) == 3, "ARAPEnergyType enum changed!"); - static_assert(int(igl::ARAPEnergyType::NUM_ARAP_ENERGY_TYPES) == 4, "ARAPEnergyType enum changed!"); - - if (energy >= igl::NUM_ARAP_ENERGY_TYPES) { - std::string errmsg = - std::string("Invalid enum for energy type should be in the range 0 to ") + - std::to_string(igl::NUM_ARAP_ENERGY_TYPES-1); - throw pybind11::value_error(errmsg); - } else if (energy == igl::ARAP_ENERGY_TYPE_DEFAULT) { - // FIXME: arap_rhs should pick a default but it doesn't - std::string errmsg = - std::string("Invalid enum for energy type should be in the range 0 to ") + - std::to_string(igl::ARAP_ENERGY_TYPE_DEFAULT-1); - } - - EigenSparseLike k; - igl::arap_rhs(v, f, d, igl::ARAPEnergyType(energy), k); - return npe::move(k); - -npe_end_code() - - diff --git a/src/average_from_edges_onto_vertices.cpp b/src/average_from_edges_onto_vertices.cpp new file mode 100644 index 00000000..35647b14 --- /dev/null +++ b/src/average_from_edges_onto_vertices.cpp @@ -0,0 +1,41 @@ +#include "default_types.h" +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto average_from_edges_onto_vertices( + const nb::DRef &F, + const nb::DRef &E, + const nb::DRef &oE, + const nb::DRef &uE) + { + Eigen::VectorXN uV; + igl::average_from_edges_onto_vertices(F, E, oE, uE, uV); + return uV; + } +} + +// Bind the wrapper to the Python module +void bind_average_from_edges_onto_vertices(nb::module_ &m) +{ + m.def( + "average_from_edges_onto_vertices", + &pyigl::average_from_edges_onto_vertices, + "F"_a, "E"_a, "oE"_a, "uE"_a, +R"(Move a scalar field defined on edges to vertices by averaging + +@param[in] F #F by 3 triangle mesh connectivity +@param[in] E #E by 3 mapping from each halfedge to each edge +@param[in] oE #E by 3 orientation as generated by orient_halfedges +@param[in] uE #E by 1 list of scalars +@param[out] uV #V by 1 list of scalar defined on vertices + +\see orient_halfedges)" + ); +} diff --git a/src/average_onto_faces.cpp b/src/average_onto_faces.cpp index fc269b21..b7c6ccc8 100644 --- a/src/average_onto_faces.cpp +++ b/src/average_onto_faces.cpp @@ -1,57 +1,39 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - +#include "default_types.h" #include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto average_onto_faces( + const nb::DRef &F, + const nb::DRef &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 +)" + ); +} -const char* doccc_faces_avg = R"igl_Qu8mg5v7( -Move a scalar field defined on vertices to faces by averaging - -Parameters ----------- -f : #f by ss list of simplexes/faces -s : #v by dim list of per-vertex values - -Returns -------- -#f by dim list of per-face values - -See also --------- -average_onto_vertices - -Notes ------ - -Examples --------- -)igl_Qu8mg5v7"; - -npe_function(average_onto_faces) -npe_doc(doccc_faces_avg) - -npe_arg(f, dense_int32, dense_int64) -npe_arg(s, dense_float, dense_double) - - -npe_begin_code() - assert_valid_tet_or_tri_mesh_faces(f); - EigenDenseLike SF; - igl::average_onto_faces(f, s, SF); - - // SF.setConstant(f.rows(), s.cols(), 0); - // for (int i = 0; i < f.rows(); ++i) - // for (int j = 0; j < f.cols(); ++j) - // SF.row(i) += s.row(f(i, j)); - // SF.array() /= s.cols(); - return npe::move(SF); - -npe_end_code() diff --git a/src/average_onto_vertices.cpp b/src/average_onto_vertices.cpp index ba25f157..393dca5f 100644 --- a/src/average_onto_vertices.cpp +++ b/src/average_onto_vertices.cpp @@ -1,52 +1,41 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_average_onto_vertices = R"igl_Qu8mg5v7( -Move a scalar field defined on faces to vertices by averaging - -Parameters ----------- -v : #v by vdim array of mesh vertices -f : #f by simplex_count array of simplex indices -s : #f by dim scalar field defined on simplices - -Returns -------- -sv: #v by dim scalar field defined on vertices - -See also --------- -average_onto_faces - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(average_onto_vertices) -npe_doc(ds_average_onto_vertices) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(s, dense_float, dense_double) // TODO: Maybe do a matches here -npe_begin_code() - assert_valid_tet_or_tri_mesh_23d(v, f); - assert_shape_equals(s, f.rows(), v.cols(), "s"); - EigenDenseLike sv; - igl::average_onto_vertices(v, f, s, sv); - return npe::move(sv); - -npe_end_code() - +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto average_onto_vertices( + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &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 +)" + ); +} diff --git a/src/avg_edge_length.cpp b/src/avg_edge_length.cpp index 26c77229..3b902833 100644 --- a/src/avg_edge_length.cpp +++ b/src/avg_edge_length.cpp @@ -1,49 +1,41 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include +#include "default_types.h" #include - -const char* ds_avg_edge_length = R"igl_Qu8mg5v7( -Compute the average edge length for the given triangle mesh. - -Parameters ----------- -v : array_like #v by 3 vertex array -f : f #f by simplex-size list of mesh faces (must be simplex) - -Returns -------- -l : average edge length - -See also --------- -adjacency_matrix - -Notes ------ -None - -Examples --------- -# Mesh in (v, f) ->>> length = avg_edge_length(v, f) -)igl_Qu8mg5v7"; - -npe_function(avg_edge_length) -npe_doc(ds_avg_edge_length) -npe_arg(v, dense_double, dense_float) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - - assert_valid_tet_or_tri_mesh(v, f); - return igl::avg_edge_length(v, f); - -npe_end_code() - - +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto avg_edge_length( + const nb::DRef &V, + const nb::DRef &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,:))"); + +} diff --git a/src/barycenter.cpp b/src/barycenter.cpp index b21c9d2d..25dee12d 100644 --- a/src/barycenter.cpp +++ b/src/barycenter.cpp @@ -1,51 +1,37 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_barycenter = R"igl_Qu8mg5v7( -Compute the barycenter of every simplex - -Parameters ----------- -v : #v x dim matrix of vertex coordinates -f : #f x simplex_size matrix of indices of simplex corners into V - -Returns -------- -A #f x dim matrix where each row is the barycenter of each simplex - -See also --------- - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(barycenter) -npe_doc(ds_barycenter) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - assert_valid_tet_or_tri_mesh_23d(v, f); - EigenDenseLike bc; - igl::barycenter(v, f, bc); - return npe::move(bc); - -npe_end_code() - +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for barycenter function + auto barycenter( + const nb::DRef &V, + const nb::DRef &F) + { + Eigen::MatrixXN BC; + igl::barycenter(V, F, BC); + return BC; + } +} + +// Bind the wrapper to the Python module +void bind_barycenter(nb::module_ &m) +{ + m.def( + "barycenter", + &pyigl::barycenter, + "V"_a, "F"_a, +R"(Computes the barycenter of every simplex. + +@param[in] V #V x dim matrix of vertex coordinates +@param[in] F #F x simplex_size matrix of indices of simplex corners into V +@param[out] BC #F x dim matrix of 3d vertices)" + ); +} diff --git a/src/barycentric_coordinates.cpp b/src/barycentric_coordinates.cpp index 1a8ccf0e..7d86c860 100644 --- a/src/barycentric_coordinates.cpp +++ b/src/barycentric_coordinates.cpp @@ -1,117 +1,76 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include -#include -const char* ds_barycentric_coordinates_tet = R"igl_Qu8mg5v7( +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for barycentric_coordinates function + auto barycentric_coordinates_PABC( + const nb::DRef &P, + const nb::DRef &A, + const nb::DRef &B, + const nb::DRef &C) + { + Eigen::MatrixXN L; + igl::barycentric_coordinates(P, A, B, C, L); + return L; + } + auto barycentric_coordinates_PABCD( + const nb::DRef &P, + const nb::DRef &A, + const nb::DRef &B, + const nb::DRef &C, + const nb::DRef &D) + { + Eigen::MatrixXN L; + igl::barycentric_coordinates(P, A, B, C, D, L); + return L; + } +} + +// Bind the wrapper to the Python module +void bind_barycentric_coordinates(nb::module_ &m) +{ + m.def( + "barycentric_coordinates", + &pyigl::barycentric_coordinates_PABC, + "P"_a, + "A"_a, + "B"_a, + "C"_a, +R"(Compute barycentric coordinates of each point in a corresponding triangle + +@param[in] P #P by 3 Query points in 3d +@param[in] A #P by 3 Tri corners in 3d +@param[in] B #P by 3 Tri corners in 3d +@param[in] C #P by 3 Tri corners in 3d +@param[out] L #P by 3 list of barycentric coordinates + )" + ); + m.def( + "barycentric_coordinates", + &pyigl::barycentric_coordinates_PABCD, + "P"_a, + "A"_a, + "B"_a, + "C"_a, + "D"_a, +R"(Compute barycentric coordinates of each point in a corresponding tetrhedron + +@param[in] P #P by 3 Query points in 3d +@param[in] A #P by 3 Tet corners in 3d +@param[in] B #P by 3 Tet corners in 3d +@param[in] C #P by 3 Tet corners in 3d +@param[in] D #P by 3 Tet corners in 3d +@param[out] L #P by 3 list of barycentric coordinates + )" + ); +} -Compute barycentric coordinates in a tet corresponding to the Euclidean coordinates in `p`. -The input arrays `a`, `b`, `c` and `d` are the vertices of each tet. I.e. one tet is -`a[i, :], b[i, :], c[i, :], d[:, i]`. -Parameters ----------- -p : #P by 3 Query points in 3d -a : #P by 3 Tet corners in 3d -b : #P by 3 Tet corners in 3d -c : #P by 3 Tet corners in 3d -d : #P by 3 Tet corners in 3d - -Returns -------- -#P by 4 list of barycentric coordinates - -See also --------- - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(barycentric_coordinates_tet) -npe_doc(ds_barycentric_coordinates_tet) -npe_arg(p, dense_float, dense_double) -npe_arg(a, npe_matches(p)) -npe_arg(b, npe_matches(p)) -npe_arg(c, npe_matches(p)) -npe_arg(d, npe_matches(p)) -npe_begin_code() - - assert_rows_match(p, a, "p", "a"); - assert_rows_match(p, b, "p", "b"); - assert_rows_match(p, c, "p", "c"); - assert_rows_match(p, d, "p", "d"); - assert_cols_equals(p, 3, "p"); - assert_cols_equals(a, 3, "a"); - assert_cols_equals(b, 3, "b"); - assert_cols_equals(c, 3, "c"); - assert_cols_equals(d, 3, "d"); - EigenDenseLike l; - igl::barycentric_coordinates(p, a, b, c, d, l); - return npe::move(l); - -npe_end_code() - - - - - -const char* ds_barycentric_coordinates_tri = R"igl_Qu8mg5v7( - -Compute barycentric coordinates in a triangle corresponding to the Euclidean coordinates in `p`. -The input arrays `a`, `b`, and `c` are the vertices of each triangle. I.e. one triangle is -`a[i, :], b[i, :], c[i, :]`. - -Parameters ----------- -p : #P by 3 Query points in 3d -a : #P by 3 Tri corners in 3d -b : #P by 3 Tri corners in 3d -c : #P by 3 Tri corners in 3d - -Returns -------- -#P by 3 list of barycentric coordinates - -See also --------- - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(barycentric_coordinates_tri) -npe_doc(ds_barycentric_coordinates_tri) -npe_arg(p, dense_float, dense_double) -npe_arg(a, npe_matches(p)) -npe_arg(b, npe_matches(p)) -npe_arg(c, npe_matches(p)) -npe_begin_code() - assert_rows_match(p, a, "p", "a"); - assert_rows_match(p, b, "p", "b"); - assert_rows_match(p, c, "p", "c"); - assert_cols_equals(p, 3, "p"); - assert_cols_equals(a, 3, "a"); - assert_cols_equals(b, 3, "b"); - assert_cols_equals(c, 3, "c"); - - EigenDenseLike l; - igl::barycentric_coordinates(p, a, b, c, l); - return npe::move(l); - -npe_end_code() diff --git a/src/bbw.cpp b/src/bbw.cpp new file mode 100644 index 00000000..0bd23f14 --- /dev/null +++ b/src/bbw.cpp @@ -0,0 +1,82 @@ +#include "default_types.h" +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for bbw function + auto bbw( + const nb::DRef &V, + const nb::DRef &Ele, + const nb::DRef &b, + const nb::DRef &bc, + const nb::DRef &W0, + const bool partition_unity, + const int verbosity, + const int max_iter, + const double inactive_threshold, + const double constraint_threshold, + const double solution_diff_threshold) + { + igl::BBWData bbw_data; + bbw_data.partition_unity = partition_unity; + bbw_data.verbosity = verbosity; + bbw_data.active_set_params.max_iter = max_iter; + bbw_data.active_set_params.inactive_threshold = inactive_threshold; + bbw_data.active_set_params.constraint_threshold = constraint_threshold; + bbw_data.active_set_params.solution_diff_threshold = solution_diff_threshold; + bbw_data.W0 = W0; + + Eigen::MatrixXN W; + if(!igl::bbw(V, Ele, b, bc, bbw_data, W)) + { + throw std::runtime_error("bbw: failed to compute weights"); + } + return W; + } +} + +// Bind the wrapper to the Python module +void bind_bbw(nb::module_ &m) +{ + igl::BBWData bbw_data; + m.def( + "bbw", + &pyigl::bbw, + "V"_a, + "Ele"_a, + "b"_a, + "bc"_a, + "W0"_a=Eigen::MatrixXN(), + "partition_unity"_a=bbw_data.partition_unity, + "verbosity"_a=bbw_data.verbosity, + "max_iter"_a=bbw_data.active_set_params.max_iter, + "inactive_threshold"_a=bbw_data.active_set_params.inactive_threshold, + "constraint_threshold"_a=bbw_data.active_set_params.constraint_threshold, + "solution_diff_threshold"_a=bbw_data.active_set_params.solution_diff_threshold, +R"(Compute Bounded Biharmonic Weights on a given domain (V,Ele) with a given +set of boundary conditions + +@tparam DerivedV derived type of eigen matrix for V (e.g. MatrixXd) +@tparam DerivedF derived type of eigen matrix for F (e.g. MatrixXi) +@tparam Derivedb derived type of eigen matrix for b (e.g. VectorXi) +@tparam Derivedbc derived type of eigen matrix for bc (e.g. MatrixXd) +@tparam DerivedW derived type of eigen matrix for W (e.g. MatrixXd) +@param[in] V #V by dim vertex positions +@param[in] Ele #Elements by simplex-size list of element indices +@param[in] b #b boundary indices into V +@param[in] bc #b by #W list of boundary values +@param[in,out] data object containing options, initial guess --> solution and results +@param[out] W #V by #W list of *unnormalized* weights to normalize use + igl::normalize_row_sums(W,W))" + ); +} + + + diff --git a/src/bfs.cpp b/src/bfs.cpp deleted file mode 100644 index 1d169979..00000000 --- a/src/bfs.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char* ds_bfs = R"igl_Qu8mg5v7( -Construct an array indexing into a **directed** graph represented by an adjacency list using -breadth first search. I.e. the output is an array of vertices in breadth-first order. - -Parameters ----------- -A : #V list of adjacency lists or #V by #V adjacency matrix -s : starting node (index into A) - -Returns -------- -A tuple, (d, p) where: - * d is a #V list of indices into rows of A in the order in which graph nodes are discovered - * p is a #V list of indices of A of predecsors where -1 indicates root/not discovered. I.e. - p[i] is the index of the vertex v which preceded d[i] in the breadth first traversal. -Note that together, (d, p) form a spanning tree of the input graph - -See also --------- - -Notes ------ - -Examples --------- ->>> V, F, _ = igl.readOFF("test.off) ->>> A = igl.adjacency_matrix(V, F) ->>> d, p = igl.bfs(A, V[0]) - -)igl_Qu8mg5v7"; - -npe_function(bfs) -npe_doc(ds_bfs) -npe_arg(A, sparse_int32, sparse_int64) -npe_arg(s, int) -npe_begin_code() - - if (A.rows() != A.cols()) { - std::string errmsg = std::string("Invalid Adjacency matrix. Must be a square sparse matrix. Got shape: (") + - std::to_string(A.rows()) + std::string(", ") + std::to_string(A.cols()) + - std::string(")"); - throw pybind11::value_error(errmsg); - } - if (A.rows() == 0 && A.cols() == 0) { - throw pybind11::value_error("Invalid Adjacency matrix has shape 0x0."); - } - if (s < 0 || s >= A.rows()) { - std::string errmsg = std::string("Invalid start index, must be between 0 and the number of rows of A (") + - std::to_string(A.rows()) + std::string("). Got s = ") + std::to_string(s); - throw pybind11::index_error(errmsg); - } - EigenDense d; - EigenDense p; - igl::bfs(A, (size_t)s, d, p); - return std::make_tuple(npe::move(d), npe::move(p)); - -npe_end_code() - - - diff --git a/src/bfs_orient.cpp b/src/bfs_orient.cpp index 4f77c101..05a879fa 100644 --- a/src/bfs_orient.cpp +++ b/src/bfs_orient.cpp @@ -1,49 +1,38 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_bfs_orient = R"igl_Qu8mg5v7( +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto bfs_orient( + const nb::DRef &F) + { + Eigen::MatrixXI FF; + Eigen::VectorXI C; + igl::bfs_orient(F,FF,C); + return std::make_tuple(FF,C); + } +} + +// Bind the wrapper to the Python module +void bind_bfs_orient(nb::module_ &m) +{ + m.def( + "bfs_orient", + &pyigl::bfs_orient, + "F"_a, + R"( Consistently orient faces in orientable patches using BFS. -Parameters ----------- -f : #F by 3 list of faces - -Returns -------- -A tuple, (ff, c) where: - * ff is a #F by 3 list of faces which are consistently oriented with - * c is a #F array of connected component ids - -See also --------- - -Notes ------ - -Examples --------- ->>> v, f, _ = igl.readOFF("test.off) ->>> ff, c = igl.bfs_orient(f) - -)igl_Qu8mg5v7"; - -npe_function(bfs_orient) -npe_doc(ds_bfs_orient) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - - assert_valid_tri_mesh_faces(f); - EigenDenseLike ff, c; - igl::bfs_orient(f, ff, c); - return std::make_tuple(npe::move(ff), npe::move(c)); +@param[in] F #F by 3 list of faces +@param[out] FF #F by 3 list of faces (OK if same as F) +@param[out] C #F list of component ids)" + ); +} -npe_end_code() diff --git a/src/biharmonic_coordinates.cpp b/src/biharmonic_coordinates.cpp index fd30e3ca..59a26c2d 100644 --- a/src/biharmonic_coordinates.cpp +++ b/src/biharmonic_coordinates.cpp @@ -1,102 +1,55 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - - - - - +#include "default_types.h" #include - -const char *ds_biharmonic_coordinates = R"igl_Qu8mg5v7( - -Compute "discrete biharmonic generalized barycentric coordinates" as - described in "Linear Subspace Design for Real-Time Shape Deformation" - [Wang et al. 2015]. Not to be confused with "Bounded Biharmonic Weights - for Real-Time Deformation" [Jacobson et al. 2011] or "Biharmonic - Coordinates" (2D complex barycentric coordinates) [Weber et al. 2012]. - These weights minimize a discrete version of the squared Laplacian energy - subject to positional interpolation constraints at selected vertices - (point handles) and transformation interpolation constraints at regions - (region handles). - -Parameters ----------- -Templates: HType should be a simple index type e.g. `int`,`size_t` - -V #V by dim list of mesh vertex positions -T #T by dim+1 list of / triangle indices into V if dim=2 - \ tetrahedron indices into V if dim=3 -S #point-handles+#region-handles list of lists of selected vertices for - each handle. Point handles should have singleton lists and region - handles should have lists of size at least dim+1 (and these points - should be in general position). -k 2-->biharmonic, 3-->triharmonic - - -Returns -------- -W #V by #points-handles+(#region-handles * dim+1) matrix of weights so - that columns correspond to each handles generalized barycentric - coordinates (for point-handles) or animation space weights (for region - handles). -returns true only on success - - -See also --------- - - -Notes ------ -None - -Examples --------- -MatrixXd W; -igl::biharmonic_coordinates(V,F,S,W); -const size_t dim = T.cols()-1; -MatrixXd H(W.cols(),dim); +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl { - int c = 0; - for(int h = 0;h &V, + const nb::DRef &T, + const std::vector> &S, + const int k) { - if(S[h].size()==1) - { - H.row(c++) = V.block(S[h][0],0,1,dim); - }else - { - H.block(c,0,dim+1,dim).setIdentity(); - c+=dim+1; - } - } + Eigen::MatrixXN W; + igl::biharmonic_coordinates(V, T, S, k, W); + return W; + } } -assert( (V-(W*H)).array().maxCoeff() < 1e-7 ); - -)igl_Qu8mg5v7"; -npe_function(biharmonic_coordinates) -npe_doc(ds_biharmonic_coordinates) - -npe_arg(v, dense_float, dense_double) -npe_arg(t, dense_int32, dense_int64) -npe_arg(s, std::vector >) -npe_default_arg(k, int, 2) - -npe_begin_code() - assert_valid_tet_or_tri_mesh(v, t, "v", "t"); - // TODO: t.rows = dim+1 - EigenDenseLike w; - Eigen::MatrixXi t_copy = t.template cast(); - igl::biharmonic_coordinates(v, t, s, k, w); - return npe::move(w); - -npe_end_code() +void bind_biharmonic_coordinates(nb::module_ &m) +{ + m.def("biharmonic_coordinates", &pyigl::biharmonic_coordinates, + "V"_a, + "T"_a, + "S"_a, + "k"_a=2, + R"(Compute "discrete biharmonic generalized barycentric coordinates" as +described in "Linear Subspace Design for Real-Time Shape Deformation" +[Wang et al. 2015]. Not to be confused with "Bounded Biharmonic Weights +for Real-Time Deformation" [Jacobson et al. 2011] or "Biharmonic +Coordinates" (2D complex barycentric coordinates) [Weber et al. 2012]. +These weights minimize a discrete version of the squared Laplacian energy +subject to positional interpolation constraints at selected vertices +(point handles) and transformation interpolation constraints at regions +(region handles). +@tparam SType should be a simple index type e.g. `int`,`size_t` +@param[in] V #V by dim list of mesh vertex positions +@param[in] T #T by dim+1 list of / triangle indices into V if dim=2 + \ tetrahedron indices into V if dim=3 +@param[in] S #point-handles+#region-handles list of lists of selected vertices for + each handle. Point handles should have singleton lists and region + handles should have lists of size at least dim+1 (and these points + should be in general position). +@param[out] W #V by #points-handles+(#region-handles * dim+1) matrix of weights so + that columns correspond to each handles generalized barycentric + coordinates (for point-handles) or animation space weights (for region + handles). +@return true only on success + )"); +} diff --git a/src/bijective_composite_harmonic_mapping.cpp b/src/bijective_composite_harmonic_mapping.cpp index 6dd02d78..12d6cec4 100644 --- a/src/bijective_composite_harmonic_mapping.cpp +++ b/src/bijective_composite_harmonic_mapping.cpp @@ -1,128 +1,65 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_bijective_composite_harmonic_mapping = R"igl_Qu8mg5v7( - -Compute a planar mapping of a triangulated polygon (V,F) subjected to - boundary conditions (b,bc). The mapping should be bijective in the sense - that no triangles' areas become negative (this assumes they started - positive). This mapping is computed by "composing" harmonic mappings - between incremental morphs of the boundary conditions. This is a bit like - a discrete version of "Bijective Composite Mean Value Mappings" [Schneider - et al. 2013] but with a discrete harmonic map (cf. harmonic coordinates) - instead of mean value coordinates. This is inspired by "Embedding a - triangular graph within a given boundary" [Xu et al. 2011]. - -Parameters ----------- -V #V by 2 list of triangle mesh vertex positions -F #F by 3 list of triangle indices into V -b #b list of boundary indices into V -bc #b by 2 list of boundary conditions corresponding to b - -Returns -------- -U #V by 2 list of output mesh vertex locations -Returns true if and only if U contains a successful bijectie mapping - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(bijective_composite_harmonic_mapping) -npe_doc(ds_bijective_composite_harmonic_mapping) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(b, npe_matches(f)) -npe_arg(bc, npe_matches(v)) - - -npe_begin_code() - assert_valid_2d_tri_mesh(v, f); - assert_nonzero_rows(b, "b"); - assert_cols_equals(b, 1, "b"); - assert_shape_equals(bc, b.rows(), 2, "bc"); - - EigenDenseLike u; - bool success = igl::bijective_composite_harmonic_mapping(v, f, b, bc, u); - return std::make_pair(success, npe::move(u)); - -npe_end_code() - - -#include - -const char* ds_internal_bijective_composite_harmonic_mapping = R"igl_Qu8mg5v7( - -Parameters ----------- -min_steps minimum number of steps to take from V(b,:) to bc -max_steps minimum number of steps to take from V(b,:) to bc (if max_steps == min_steps then no further number of steps will be tried) -num_inner_iters number of iterations of harmonic solves to run after for each morph step (to try to push flips back in) -test_for_flips whether to check if flips occurred (and trigger more steps). if test_for_flips = false then this function always returns - true - - -Returns -------- - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(bijective_composite_harmonic_mapping_with_steps) -npe_doc(ds_internal_bijective_composite_harmonic_mapping) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(b, npe_matches(f)) -npe_arg(bc, npe_matches(v)) -npe_arg(min_steps, int) -npe_arg(max_steps, int) -npe_arg(num_inner_iters, int) -npe_arg(test_for_flips, bool) - - -npe_begin_code() - assert_valid_2d_tri_mesh(v, f); - assert_nonzero_rows(b, "b"); - assert_cols_equals(b, 1, "b"); - assert_shape_equals(bc, b.rows(), 2, "bc"); - - EigenDenseLike u; - bool success = igl::bijective_composite_harmonic_mapping(v, f, b, bc, min_steps, max_steps, num_inner_iters, test_for_flips, u); - return std::make_pair(success, npe::move(u)); - -npe_end_code() - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto bijective_composite_harmonic_mapping( + const nb::DRef & V, + const nb::DRef & F, + const nb::DRef & b, + const nb::DRef & bc, + const int min_steps, + const int max_steps, + const int num_inner_iters, + const bool test_for_flips) + { + Eigen::MatrixXN U; + if(!igl::bijective_composite_harmonic_mapping(V, F, b, bc, min_steps, max_steps, num_inner_iters, test_for_flips, U)) + { + throw std::runtime_error("bijective_composite_harmonic_mapping failed"); + } + return U; + } +} + +void bind_bijective_composite_harmonic_mapping(nb::module_ &m) +{ + m.def("bijective_composite_harmonic_mapping", &pyigl::bijective_composite_harmonic_mapping, + "V"_a, + "F"_a, + "b"_a, + "bc"_a, + "min_steps"_a=1, + "max_steps"_a=200, + "num_inner_iters"_a=20, + "test_for_flips"_a=true, + R"(Compute a injective planar mapping of a triangulated polygon (V,F) subjected to +boundary conditions (b,bc). The mapping should be bijective in the sense +that no triangles' areas become negative (this assumes they started +positive). This mapping is computed by "composing" harmonic mappings +between incremental morphs of the boundary conditions. This is a bit like +a discrete version of "Bijective Composite Mean Value Mappings" [Schneider +et al. 2013] but with a discrete harmonic map (cf. harmonic coordinates) +instead of mean value coordinates. This is inspired by "Embedding a +triangular graph within a given boundary" [Xu et al. 2011]. +@param[in] V #V by 2 list of triangle mesh vertex positions +@param[in] F #F by 3 list of triangle indices into V +@param[in] b #b list of boundary indices into V +@param[in] bc #b by 2 list of boundary conditions corresponding to b +@param[in] min_steps minimum number of steps to take from V(b,:) to bc +@param[in] max_steps minimum number of steps to take from V(b,:) to bc (if + max_steps == min_steps then no further number of steps will be tried) +@param[in] num_inner_iters number of iterations of harmonic solves to run after + for each morph step (to try to push flips back in) +@param[in] test_for_flips whether to check if flips occurred (and trigger more + steps). if test_for_flips = false then this function always returns + true +@param[out] U #V by 2 list of output mesh vertex locations +@return true if and only if U contains a successful bijectie mapping)"); +} diff --git a/src/blue_noise.cpp b/src/blue_noise.cpp index b48f383c..248d3bd9 100644 --- a/src/blue_noise.cpp +++ b/src/blue_noise.cpp @@ -1,63 +1,43 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Peter Kulits -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -//TODO: __example -#include -#include -#include +#include "default_types.h" #include - -const char* ds_blue_noise = R"igl_Qu8mg5v7( -"Fast Poisson Disk Sampling in Arbitrary Dimensions" [Bridson 2007] - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto blue_noise( + const nb::DRef & V, + const nb::DRef & F, + const Numeric r) + { + Eigen::MatrixXN B; + Eigen::VectorXI FI; + Eigen::MatrixXN P; + igl::blue_noise(V, F, r, B, FI, P); + return std::make_tuple(B, FI, P); + } +} + +void bind_blue_noise(nb::module_ &m) +{ + m.def("blue_noise", &pyigl::blue_noise, + "V"_a, + "F"_a, + "r"_a, + R"("Fast Poisson Disk Sampling in Arbitrary Dimensions" [Bridson 2007]. For very dense samplings this is faster than (up to 2x) cyCodeBase's implementation of "Sample Elimination for Generating Poisson Disk Sample Sets" [Yuksel 2015]. YMMV -Parameters ----------- - V #V by dim list of mesh vertex positions - F #F by 3 list of mesh triangle indices into rows of V - r Poisson disk radius (evaluated according to Euclidean distance on V) - -Returns -------- - B #P by 3 list of barycentric coordinates, ith row are coordinates of - ith sampled point in face FI(i) - FI #P list of indices into F - P #P by dim list of sample positions. - -See also --------- -random_points_on_mesh - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(blue_noise) -npe_doc(ds_blue_noise) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(r, double) - - -npe_begin_code() - - assert_valid_23d_tri_mesh(v, f); - EigenDenseLike b; - Eigen::VectorXi fi; - EigenDenseLike p; - igl::blue_noise(v, f, r, b, fi, p); - return std::make_tuple(npe::move(b), npe::move(fi), npe::move(p)); - -npe_end_code() +@param[in] V #V by dim list of mesh vertex positions +@param[in] F #F by 3 list of mesh triangle indices into rows of V +@param[in] r Poisson disk radius (evaluated according to Euclidean distance on V) +@param[out] B #P by 3 list of barycentric coordinates, ith row are coordinates of + ith sampled point in face FI(i) +@param[out] FI #P list of indices into F +@param[out] P #P by dim list of sample positions. +)"); +} diff --git a/src/bone_parents.cpp b/src/bone_parents.cpp deleted file mode 100644 index 26128c6a..00000000 --- a/src/bone_parents.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char* ds_bone_parents = R"igl_Qu8mg5v7( -BONE_PARENTS Recover "parent" bones from directed graph representation. -Parameters ----------- -BE #BE by 2 list of directed bone edges - -Returns -------- -P #BE by 1 list of parent indices into BE, -1 means root. - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(bone_parents) -npe_doc(ds_bone_parents) - -npe_arg(be, dense_int32, dense_int64) - - -npe_begin_code() - assert_cols_equals(be, 2, "be"); - EigenDenseLike p; - igl::bone_parents(be, p); - return npe::move(p); - -npe_end_code() - - diff --git a/src/boundary_conditions.cpp b/src/boundary_conditions.cpp deleted file mode 100644 index 7c332f75..00000000 --- a/src/boundary_conditions.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example - -#include -#include -#include - - - -#include - -const char* ds_boundary_conditions = R"igl_Qu8mg5v7( - -Compute boundary conditions for automatic weights computation. This - function expects that the given mesh (V,Ele) has sufficient samples - (vertices) exactly at point handle locations and exactly along bone and - cage edges. - -Parameters ----------- -V #V by dim list of domain vertices -Ele #Ele by simplex-size list of simplex indices -C #C by dim list of handle positions -P #P by 1 list of point handle indices into C -BE #BE by 2 list of bone edge indices into C -CE #CE by 2 list of cage edge indices into *P* -CF #CF by 3 list of cage edge indices into *P* - -Returns -------- -b #b list of boundary indices (indices into V of vertices which have - known, fixed values) -bc #b by #weights list of known/fixed values for boundary vertices - (notice the #b != #weights in general because #b will include all the - intermediary samples along each bone, etc.. The ordering of the - weights corresponds to [P;BE] -Returns false if boundary conditions are suspicious: - P and BE are empty - bc is empty - some column of bc doesn't have a 0 (assuming bc has >1 columns) - some column of bc doesn't have a 1 (assuming bc has >1 columns) - -See also --------- - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(boundary_conditions) -npe_doc(ds_boundary_conditions) - -npe_arg(v, dense_float, dense_double) -npe_arg(ele, dense_int32, dense_int64) -npe_arg(c, npe_matches(v)) -npe_arg(p, npe_matches(ele)) -npe_arg(be, npe_matches(ele)) -npe_arg(ce, npe_matches(ele)) -npe_arg(cf, npe_matches(ele)) - -npe_begin_code() - assert_valid_tet_or_tri_mesh(v, ele); - assert_cols_match(v, c, "v", "c"); - assert_cols_equals(be, 2, "be"); - - if(p.size() > 0) - { - assert_cols_equals(p, 1, "p"); - } - - if(ce.size() > 0) - { - assert_cols_equals(ce, 2, "ce"); - } - if(cf.size() > 0) - { - assert_cols_equals(cf, 3, "cf"); - } - - //TODO: remove __copy - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::MatrixXi ele_copy = ele.template cast(); - Eigen::MatrixXd c_copy = c.template cast(); - Eigen::VectorXi p_copy; - if (p.size() > 0) - p_copy = p.template cast(); - Eigen::MatrixXi be_copy = be.template cast(); - Eigen::MatrixXi ce_copy; - if (ce.size() > 0) - ce_copy = ce.template cast(); - Eigen::MatrixXi cf_copy; - if (cf.size() > 0) - cf_copy = cf.template cast(); - Eigen::VectorXi b_copy; - Eigen::MatrixXd bc_copy; - bool success = igl::boundary_conditions(v_copy, ele_copy, c_copy, p_copy, be_copy, ce_copy, cf_copy, b_copy, bc_copy); - EigenDenseLike b = b_copy.template cast(); - EigenDenseLike bc = bc_copy.template cast(); - return std::make_tuple(success, npe::move(b), npe::move(bc)); - -npe_end_code() - - diff --git a/src/boundary_facets.cpp b/src/boundary_facets.cpp index 985ef899..f9ff6f44 100644 --- a/src/boundary_facets.cpp +++ b/src/boundary_facets.cpp @@ -1,49 +1,45 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto boundary_facets( const nb::DRef &T) + { + Eigen::MatrixXI F; + Eigen::VectorXI J; + Eigen::VectorXI K; + igl::boundary_facets(T,F,J,K); + return std::make_tuple(F,J,K); + } +} + +// Bind the wrapper to the Python module +void bind_boundary_facets(nb::module_ &m) +{ + m.def( + "boundary_facets", + &pyigl::boundary_facets, + "T"_a, +R"(Determine boundary faces (edges) of tetrahedra (triangles) stored in T +(analogous to qptoolbox's `outline` and `boundary_faces`). + +@param[in] T tetrahedron (triangle) index list, m by 4 (3), where m is the number of tetrahedra +@param[out] F list of boundary faces, n by 3 (2), where n is the number + of boundary faces. Faces are oriented so that igl::centroid(V,F,…) +computes the same sign volume as igl::volume(V,T) +@param[out] J list of indices into T, n by 1 +@param[out] K list of indices revealing across from which vertex is this facet)" + ); +} -const char* ds_boundary_facets = R"igl_Qu8mg5v7( -Determine boundary faces (edges) of tetrahedra (triangles). -Parameters ----------- -t : tetrahedron or triangle index list, m by 4/3, where m is the number of tetrahedra/triangles -Returns -------- -f : list of boundary faces, n by 3/2, where n is the number of boundary faces/edges - -See also --------- -None - -Notes ------ -None - -Examples --------- -# Mesh in (v, f) ->>> b = boundary_facets(f) -)igl_Qu8mg5v7"; - -npe_function(boundary_facets) -npe_doc(ds_boundary_facets) -npe_arg(t, dense_int32, dense_int64) -npe_begin_code() - - assert_valid_tet_or_tri_mesh_faces(t, "t"); - EigenDense f; - igl::boundary_facets(t, f); - return npe::move(f); - -npe_end_code() diff --git a/src/boundary_loop.cpp b/src/boundary_loop.cpp index a6a51a81..d08a854c 100644 --- a/src/boundary_loop.cpp +++ b/src/boundary_loop.cpp @@ -1,88 +1,48 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include -#include - -const char* ds_boundary_loop = R"igl_Qu8mg5v7( -Compute ordered boundary loops for a manifold mesh and return the longest loop in terms of vertices. - -Parameters ----------- -f : #v by dim array of mesh faces - -Returns -------- -l : ordered list of boundary vertices of longest boundary loop - -See also --------- - - -Notes ------ -None - -Examples --------- -# Mesh in (v, f) ->>>l = boundary_loop(f) -)igl_Qu8mg5v7"; - -npe_function(boundary_loop) -npe_doc(ds_boundary_loop) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - - assert_valid_tri_mesh_faces(f); - EigenDenseLike l; - igl::boundary_loop(f, l); - return npe::move(l); - -npe_end_code() - - - -const char* ds_all_boundary_loop = R"igl_Qu8mg5v7xx( -Compute ordered boundary loops for a manifold mesh - -Parameters ----------- -f : #v by dim array of mesh faces - -Returns -------- -l : list of loops where l[i] = ordered list of boundary vertices in loop i - -See also --------- - - -Notes ------ -None - -Examples --------- -# Mesh in (v, f) ->>>l = all_boundary_loop(f) -)igl_Qu8mg5v7xx"; - -npe_function(all_boundary_loop) -npe_doc(ds_all_boundary_loop) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - assert_valid_tri_mesh_faces(f); - std::vector> l; - igl::boundary_loop(f, l); - return l; - -npe_end_code() +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for boundary_loop that returns all loops as a vector of vectors + auto boundary_loop_all(const nb::DRef &F) + { + std::vector> 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 &F) + { + Eigen::VectorXI longest; + igl::boundary_loop(F, longest); + return longest; + } +} + +// Bind the wrapper to the Python module +void bind_boundary_loop(nb::module_ &m) +{ + m.def( + "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 +@return List of lists of boundary vertices, where each sublist represents a loop)"); +} diff --git a/src/bounding_box.cpp b/src/bounding_box.cpp index df8eff23..94ac139c 100644 --- a/src/bounding_box.cpp +++ b/src/bounding_box.cpp @@ -1,83 +1,34 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_bounding_box = R"igl_Qu8mg5v7( - -Build a triangle mesh of the bounding box of a given list of vertices - -Parameters ----------- -V #V by dim list of rest domain positions - -Returns -------- -BV 2^dim by dim list of bounding box corners positions -BF #BF by dim list of simplex facets - -See also --------- - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(bounding_box) -npe_doc(ds_bounding_box) - -npe_arg(v, dense_float, dense_double) - - -npe_begin_code() - - assert_nonzero_rows(v, "v"); - EigenDenseLike bv; - Eigen::MatrixXi bf; - igl::bounding_box(v, bv, bf); - EigenDenseInt bf_row_major = bf.template cast(); - return std::make_tuple(npe::move(bv), npe::move(bf_row_major)); - -npe_end_code() - - - - - - - -const char* ds_bounding_box_pad = R"igl_Qu8mg5v7( -See bounding_box for the documentation. -)igl_Qu8mg5v7"; - -npe_function(bounding_box) -npe_doc(ds_bounding_box) - -npe_arg(v, dense_float, dense_double) -npe_arg(pad, double) - - -npe_begin_code() - - assert_nonzero_rows(v, "v"); - EigenDenseLike bv; - Eigen::MatrixXi bf; - igl::bounding_box(v, pad, bv, bf); - EigenDenseInt bf_row_major = bf.template cast(); - return std::make_tuple(npe::move(bv), npe::move(bf_row_major)); - -npe_end_code() - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto bounding_box( + const nb::DRef& V, + const Numeric pad) + { + Eigen::MatrixXN BV; + Eigen::MatrixXI BF; + igl::bounding_box(V, pad, BV, BF); + return std::make_tuple(BV, BF); + } +} + +void bind_bounding_box(nb::module_ &m) +{ + m.def("bounding_box", &pyigl::bounding_box, + "V"_a, + "pad"_a=0, + R"(Build a triangle mesh of the bounding box of a given list of vertices + +@param[in] V #V by dim list of rest domain positions +@param[in] pad padding offset +@param[out] BV 2^dim by dim list of bounding box corners positions +@param[out] BF #BF by dim list of simplex facets )"); +} diff --git a/src/bounding_box_diagonal.cpp b/src/bounding_box_diagonal.cpp deleted file mode 100644 index 2962de69..00000000 --- a/src/bounding_box_diagonal.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example - -#include -#include -#include - - - - - - -#include - -const char* ds_bounding_box_diagonal = R"igl_Qu8mg5v7( - -Compute the length of the diagonal of a given meshes axis-aligned bounding - -Parameters ----------- -V #V by 3 list of vertex positions - -Returns -------- -Returns length of bounding box diagonal - -See also --------- - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(bounding_box_diagonal) -npe_doc(ds_bounding_box_diagonal) - -npe_arg(v, dense_float, dense_double) - - -npe_begin_code() - assert_nonzero_rows(v, "v"); - assert_cols_equals(v, 3, "v"); - // TODO: remove __copy - Eigen::MatrixXd v_copy = v.template cast(); - return igl::bounding_box_diagonal(v_copy); - -npe_end_code() - - diff --git a/src/circulation.cpp b/src/circulation.cpp index 4c205c75..0f5bdb84 100644 --- a/src/circulation.cpp +++ b/src/circulation.cpp @@ -1,79 +1,50 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example -//difficult to test - -#include -#include -#include -#include - - - - - +#include "default_types.h" #include - -const char* ds_circulation = R"igl_Qu8mg5v7( - -Return list of faces around the end point of an edge. Assumes - data-structures are built from an edge-manifold **closed** mesh. - -Parameters ----------- -e index into E of edge to circulate -ccw whether to _continue_ in ccw direction of edge (circulate around - E(e,1)) -EMAP #F*3 list of indices into E, mapping each directed edge to unique - unique edge in E -EF #E by 2 list of edge flaps, EF(e,0)=f means e=(i-->j) is the edge of - F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1) " - e=(j->i) -EI #E by 2 list of edge flap corners (see above). - -Returns -------- -Returns list of faces touched by circulation (in cyclically order). - -See also --------- - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(circulation) -npe_doc(ds_circulation) - -npe_arg(e, int) -npe_arg(ccw, bool) -npe_arg(emap, dense_int32, dense_int64) -npe_arg(ef, npe_matches(emap)) -npe_arg(ei, npe_matches(emap)) - - -npe_begin_code() - assert_cols_equals(ef, 2, "ef"); - assert_cols_equals(ei, 2, "ei"); - assert_shapes_match(ef, ei, "ef", "ei"); - - // TODO: remove __copy - Eigen::VectorXi emap_copy = emap.template cast(); - Eigen::MatrixXi ef_copy = ef.template cast(); - Eigen::MatrixXi ei_copy = ei.template cast(); - auto res = igl::circulation(e, ccw, emap_copy, ef_copy, ei_copy); - //res is a std::vector - return res; - -npe_end_code() - +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto circulation( + const int e, + const bool ccw, + const nb::DRef &F, + const nb::DRef &EMAP, + const nb::DRef &EF, + const nb::DRef &EI) + { + std::vector Nv, Nf; + igl::circulation(e, ccw, F, EMAP, EF, EI, Nv, Nf); + return std::make_tuple(Nv, Nf); + } +} + +// Bind the wrapper to the Python module +void bind_circulation(nb::module_ &m) +{ + m.def( + "circulation", + &pyigl::circulation, + "e"_a, + "ccw"_a, + "F"_a, + "EMAP"_a, + "EF"_a, + "EI"_a, + R"(Return lists of "next" vertex indices (Nv) and face indices (Nf) for circulation. + + @param[in] e index of edge to circulate + @param[in] ccw circulate in ccw direction + @param[in] F #F by 3 list of mesh faces + @param[in] EMAP #F*3 list of indices mapping each directed edge to a unique edge in E + @param[in] EF #E by 2 list of edge flaps + @param[in] EI #E by 2 list of edge flap corners + @return Tuple containing Nv (next vertex indices) and Nf (face indices))" + ); +} diff --git a/src/circumradius.cpp b/src/circumradius.cpp index 9366e771..ce21051a 100644 --- a/src/circumradius.cpp +++ b/src/circumradius.cpp @@ -1,57 +1,35 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_circumradius = R"igl_Qu8mg5v7( - -Compute the circumradius of each triangle in a mesh (V,F) -Parameters ----------- -V #V by dim list of mesh vertex positions -F #F by 3 list of triangle indices into V - - -Returns -------- -R #F list of circumradii - -See also --------- - - -Notes ------ -None - -Examples --------- -R = circumradius(V, F) - - -)igl_Qu8mg5v7"; - -npe_function(circumradius) -npe_doc(ds_circumradius) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - // TODO: v can also be 2d - assert_valid_23d_tri_mesh(v, f); - EigenDense r; - igl::circumradius(v, f, r); - return npe::move(r); - -npe_end_code() - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto circumradius( + const nb::DRef & V , + const nb::DRef & T) + { + Eigen::VectorXN R; + Eigen::MatrixXN C,B; + igl::circumradius(V, T, R, C, B); + return std::make_tuple(R, C, B); + } +} + +void bind_circumradius(nb::module_ &m) +{ + m.def("circumradius", &pyigl::circumradius, + ""_a, + "F"_a, + R"(Compute the circumradius of each triangle in a mesh (V,F) +@param[in] V #V by dim list of mesh vertex positions +@param[in] F #F by 3 list of triangle indices into V +@param[out] R #F list of circumradius +@param[out] R #T list of circumradius +@param[out] C #T by dim list of circumcenter +@param[out] B #T by simplex-size list of barycentric coordinates of circumcenter)"); +} diff --git a/src/collapse_edge.cpp b/src/collapse_edge.cpp new file mode 100644 index 00000000..acc5b395 --- /dev/null +++ b/src/collapse_edge.cpp @@ -0,0 +1,88 @@ +#include "default_types.h" +#include +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto collapse_edge( + const Integer e, + const nb::DRef &p, + Eigen::Ref V, + Eigen::Ref F, + Eigen::Ref E, + Eigen::Ref EMAP, + Eigen::Ref EF, + Eigen::Ref EI) + { + int e1_,e2_,f1_,f2_; + if(!igl::collapse_edge(e,p,V,F,E,EMAP,EF,EI,e1_,e2_,f1_,f2_)) + { + throw std::runtime_error("collapse_edge failed"); + } + + Integer e1 = e1_,e2 = e2_,f1 = f1_,f2 = f2_; + return std::make_tuple(e1,e2,f1,f2); + } + +} + +// Bind the wrapper to the Python module +void bind_collapse_edge(nb::module_ &m) +{ + m.def( + "collapse_edge", + &pyigl::collapse_edge, + "e"_a, + "p"_a, + nb::arg("V").noconvert(), + nb::arg("F").noconvert(), + nb::arg("E").noconvert(), + nb::arg("EMAP").noconvert(), + nb::arg("EF").noconvert(), + nb::arg("EI").noconvert(), +R"(Attempt to collapse a given edge of a mesh. Assumes (V,F) is a closed +manifold mesh (except for previously collapsed faces which should be set +to: [IGL_COLLAPSE_EDGE_NULL IGL_COLLAPSE_EDGE_NULL +IGL_COLLAPSE_EDGE_NULL]. Collapses exactly two faces and exactly 3 edges +from E (e and one side of each face gets collapsed to the other). This is +implemented in a way that it can be repeatedly called until satisfaction +and then the garbage in F can be collected by removing NULL faces. + +@param[in] e index into E of edge to try to collapse. E(e,:) = [s d] or [d s] so + that sj) is the edge of + F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1) " + e=(j->i) +@param[in,out] EI #E by 2 list of edge flap corners (see above). +[mesh inputs] +@param[out] e1 index into E of edge collpased on left +@param[out] e2 index into E of edge collpased on right +@param[out] f1 index into F of face collpased on left +@param[out] f2 index into F of face collpased on right +@return true if edge was collapsed + +Because there are side-effects on V,F,E,EMAP,EF,EI, this function will not +accept all numpy variations and will refuse to copy inputs that don't match +expected ordering and dtype. +)"); +} + + + + diff --git a/src/collapse_small_triangles.cpp b/src/collapse_small_triangles.cpp deleted file mode 100644 index fa96b523..00000000 --- a/src/collapse_small_triangles.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example - -#include -#include -#include - - - - - -#include - -const char* ds_collapse_small_triangles = R"igl_Qu8mg5v7( - -Given a triangle mesh (V,F) compute a new mesh (VV,FF) which contains the - original faces and vertices of (V,F) except any small triangles have been - removed via collapse. - - We are *not* following the rules in "Mesh Optimization" [Hoppe et al] - Section 4.2. But for our purposes we don't care about this criteria. - -Parameters ----------- -V #V by 3 list of vertex positions -F #F by 3 list of triangle indices into V -eps epsilon for smallest allowed area treated as fraction of squared bounding box - diagonal - -Returns -------- -FF #FF by 3 list of triangle indices into V - - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(collapse_small_triangles) -npe_doc(ds_collapse_small_triangles) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(eps, double) - - -npe_begin_code() - - assert_valid_3d_tri_mesh(v, f); - // TODO: remove __copy - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::MatrixXi f_copy = f.template cast(); - Eigen::MatrixXi ff; - igl::collapse_small_triangles(v_copy, f_copy, eps, ff); - EigenDenseInt ff_row_major = ff.template cast();; - return npe::move(ff_row_major); - -npe_end_code() - - diff --git a/src/comb_cross_field.cpp b/src/comb_cross_field.cpp deleted file mode 100644 index 82bf9228..00000000 --- a/src/comb_cross_field.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char* ds_comb_cross_field = R"igl_Qu8mg5v7( - -Parameters ----------- - V #V by 3 eigen Matrix of mesh vertex 3D positions - F #F by 3 eigen Matrix of face indices - PD1in #F by 3 eigen Matrix of the first per face cross field vector - PD2in #F by 3 eigen Matrix of the second per face cross field vector - -Returns -------- - PD1out #F by 3 eigen Matrix of the first combed cross field vector - PD2out #F by 3 eigen Matrix of the second combed cross field vector - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(comb_cross_field) -npe_doc(ds_comb_cross_field) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(pd1in, npe_matches(v)) -npe_arg(pd2in, npe_matches(v)) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - assert_rows_match(f, pd1in, "F", "PD1in"); - assert_rows_match(f, pd2in, "F", "PD2in"); - assert_cols_equals(pd1in, 3, "PD1in"); - assert_cols_equals(pd2in, 3, "PD2in"); - - EigenDenseLike v_copy = v; - EigenDenseLike f_copy = f; - EigenDenseLike pd1in_copy = pd1in; - EigenDenseLike pd2in_copy = pd2in; - - EigenDenseLike pd1out; - EigenDenseLike pd2out; - igl::comb_cross_field(v_copy, f_copy, pd1in_copy, pd2in_copy, pd1out, pd2out); - return std::make_tuple(npe::move(pd1out), npe::move(pd2out)); - -npe_end_code() - - diff --git a/src/comb_frame_field.cpp b/src/comb_frame_field.cpp deleted file mode 100644 index 1e905ae7..00000000 --- a/src/comb_frame_field.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char* ds_comb_frame_field = R"igl_Qu8mg5v7( - -Parameters ----------- -V #V by 3 eigen Matrix of mesh vertex 3D positions -F #F by 3 eigen Matrix of face indices -PD1 #F by 3 eigen Matrix of the first per face cross field vector -PD2 #F by 3 eigen Matrix of the second per face cross field vector -BIS1_combed #F by 3 eigen Matrix of the first combed bisector field vector -BIS2_combed #F by 3 eigen Matrix of the second combed bisector field vector - -Returns -------- -PD1_combed #F by 3 eigen Matrix of the first combed cross field vector -PD2_combed #F by 3 eigen Matrix of the second combed cross field vector - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(comb_frame_field) -npe_doc(ds_comb_frame_field) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(pd1, npe_matches(v)) -npe_arg(pd2, npe_matches(pd1)) -npe_arg(bis1_combed, npe_matches(pd1)) -npe_arg(bis2_combed, npe_matches(pd1)) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - assert_rows_match(f, pd1, "F", "PD1"); - assert_rows_match(f, pd2, "F", "PD2"); - - assert_rows_match(f, bis1_combed, "F", "BIS1_combed"); - assert_rows_match(f, bis2_combed, "F", "BIS2_combed"); - - assert_cols_equals(pd1, 3, "PD1"); - assert_cols_equals(pd2, 3, "PD2"); - - assert_cols_equals(bis1_combed, 3, "BIS1_combed"); - assert_cols_equals(bis2_combed, 3, "BIS2_combed"); - - EigenDenseLike v_copy = v; - EigenDenseLike f_copy = f; - EigenDenseLike pd1_copy = pd1; - EigenDenseLike pd2_copy = pd2; - EigenDenseLike bis1_combed_copy = bis1_combed; - EigenDenseLike bis2_combed_copy = bis2_combed; - - EigenDenseLike pd1_combed; - EigenDenseLike pd2_combed; - - igl::comb_frame_field(v_copy, f_copy, pd1_copy, pd2_copy, bis1_combed_copy, bis2_combed_copy, pd1_combed, pd2_combed); - return std::make_tuple(npe::move(pd1_combed), npe::move(pd2_combed)); - -npe_end_code() - - diff --git a/src/comb_line_field.cpp b/src/comb_line_field.cpp deleted file mode 100644 index f96250cc..00000000 --- a/src/comb_line_field.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_comb_line_field = R"igl_Qu8mg5v7( - -Parameters ----------- -V #V by 3 eigen Matrix of mesh vertex 3D positions -F #F by 3 eigen Matrix of face indices -PD1in #F by 3 eigen Matrix of the first per face cross field vector - -Returns -------- -PD1out #F by 3 eigen Matrix of the first combed cross field vector - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(comb_line_field) -npe_doc(ds_comb_line_field) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(pd1in, npe_matches(v)) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - assert_rows_match(f, pd1in, "F", "PD1in"); - assert_cols_equals(pd1in, 3, "PD1in"); - - EigenDenseLike v_copy = v; - EigenDenseLike f_copy = f; - EigenDenseLike pd1in_copy = pd1in; - - EigenDenseLike pd1out; - igl::comb_line_field(v_copy, f_copy, pd1in_copy, pd1out); - return npe::move(pd1out); - -npe_end_code() - - diff --git a/src/compute_frame_field_bisectors.cpp b/src/compute_frame_field_bisectors.cpp deleted file mode 100644 index e4f59009..00000000 --- a/src/compute_frame_field_bisectors.cpp +++ /dev/null @@ -1,139 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char* ds_compute_frame_field_bisectors = R"igl_Qu8mg5v7( - -Compute bisectors of a frame field defined on mesh faces - -Parameters ----------- -V #V by 3 eigen Matrix of mesh vertex 3D positions -F #F by 3 eigen Matrix of face (triangle) indices -B1 #F by 3 eigen Matrix of face (triangle) base vector 1 -B2 #F by 3 eigen Matrix of face (triangle) base vector 2 -PD1 #F by 3 eigen Matrix of the first per face frame field vector -PD2 #F by 3 eigen Matrix of the second per face frame field vector - -Returns -------- -BIS1 #F by 3 eigen Matrix of the first per face frame field bisector -BIS2 #F by 3 eigen Matrix of the second per face frame field bisector - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(compute_frame_field_bisectors) -npe_doc(ds_compute_frame_field_bisectors) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(b1, npe_matches(v)) -npe_arg(b2, npe_matches(v)) -npe_arg(pd1, npe_matches(v)) -npe_arg(pd2, npe_matches(v)) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - assert_rows_match(f, b1, "F", "B1"); - assert_rows_match(f, b2, "F", "B2"); - assert_cols_equals(b1, 3, "b1"); - assert_cols_equals(b2, 3, "b2"); - - assert_rows_match(f, pd1, "F", "PD1"); - assert_rows_match(f, pd2, "F", "PD2"); - assert_cols_equals(pd1, 3, "PD1"); - assert_cols_equals(pd2, 3, "PD2"); - - EigenDenseLike v_copy = v; - EigenDenseLike f_copy = f; - EigenDenseLike b1_copy = b1; - EigenDenseLike b2_copy = b2; - EigenDenseLike pd1_copy = pd1; - EigenDenseLike pd2_copy = pd2; - - EigenDenseLike bis1; - EigenDenseLike bis2; - igl::compute_frame_field_bisectors(v_copy, f_copy, b1_copy, b2_copy, pd1_copy, pd2_copy, bis1, bis2); - return std::make_tuple(npe::move(bis1), npe::move(bis2)); - -npe_end_code() - - - -const char* ds_compute_frame_field_bisectors_no_basis = R"igl_Qu8mg5v7( - -Wrapper without given basis vectors. - -Parameters ----------- - - -Returns -------- - - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(compute_frame_field_bisectors_no_basis) -npe_doc(ds_compute_frame_field_bisectors_no_basis) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(pd1, npe_matches(v)) -npe_arg(pd2, npe_matches(v)) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - - assert_rows_match(f, pd1, "F", "PD1"); - assert_rows_match(f, pd2, "F", "PD2"); - assert_cols_equals(pd1, 3, "PD1"); - assert_cols_equals(pd2, 3, "PD2"); - - EigenDenseLike v_copy = v; - EigenDenseLike f_copy = f; - EigenDenseLike pd1_copy = pd1; - EigenDenseLike pd2_copy = pd2; - - EigenDenseLike bis1; - EigenDenseLike bis2; - - igl::compute_frame_field_bisectors(v_copy, f_copy, pd1_copy, pd2_copy, bis1, bis2); - return std::make_tuple(npe::move(bis1), npe::move(bis2)); - -npe_end_code() - - diff --git a/src/connect_boundary_to_infinity.cpp b/src/connect_boundary_to_infinity.cpp deleted file mode 100644 index 54cb9d58..00000000 --- a/src/connect_boundary_to_infinity.cpp +++ /dev/null @@ -1,144 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example - -#include -#include -#include -#include - -const char* ds_connect_boundary_to_infinity = R"igl_Qu8mg5v7( - -Connect all boundary edges to a fictitious point at infinity. - -Parameters ----------- -F #F by 3 list of face indices into some V - -Returns -------- -FO #F+#O by 3 list of face indices into [V;inf inf inf], original F are - guaranteed to come first. If (V,F) was a manifold mesh, now it is - closed with a possibly non-manifold vertex at infinity (but it will be - edge-manifold). - -See also --------- - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(connect_boundary_to_infinity) -npe_doc(ds_connect_boundary_to_infinity) - -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_tri_mesh_faces(f); - EigenDenseLike fo; - igl::connect_boundary_to_infinity(f, fo); - return npe::move(fo); - -npe_end_code() - - -const char* ds_connect_boundary_to_infinity_face = R"igl_Qu8mg5v7( - -Parameters ----------- -F #F by 3 list of face indices into some V - -Returns -------- -FO #F+#O by 3 list of face indices into VO - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(connect_boundary_to_infinity_face) -npe_doc(ds_connect_boundary_to_infinity_face) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_tri_mesh_faces(f); - EigenDenseLike vo; - EigenDenseLike fo; - igl::connect_boundary_to_infinity(v, f, vo, fo); - return std::make_tuple(npe::move(vo), npe::move(fo)); - -npe_end_code() - - - - - - - -const char* ds_connect_boundary_to_infinity_index = R"igl_Qu8mg5v7( - -Parameters ----------- -inf_index index of point at infinity (usually V.rows() or F.maxCoeff()) - -Returns -------- - - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(connect_boundary_to_infinity_index) -npe_doc(ds_connect_boundary_to_infinity_index) - -npe_arg(f, dense_int32, dense_int64) -npe_arg(inf_index, int) - - -npe_begin_code() - - assert_valid_tri_mesh_faces(f); - EigenDenseLike fo; - igl::connect_boundary_to_infinity(f, inf_index, fo); - return npe::move(fo); - -npe_end_code() - - diff --git a/src/connected_components.cpp b/src/connected_components.cpp index 85d15e41..b4fe0d9c 100644 --- a/src/connected_components.cpp +++ b/src/connected_components.cpp @@ -1,60 +1,36 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - +#include "default_types.h" #include - -const char *ds_connected_components = R"igl_Qu8mg5v7( - -Determine the connected components of a graph described by the input - adjacency matrix (similar to MATLAB's graphconncomp). - -Parameters ----------- - -A #A by #A adjacency matrix (treated as describing an undirected graph) - -Returns -------- -Returns number of connected components -C #A list of component indices into [0,#K-1] -K #K list of sizes of each component - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(connected_components) -npe_doc(ds_connected_components) - -npe_arg(a, sparse_int32, sparse_int64) - - -npe_begin_code() - assert_nonzero_rows(a, "A"); - assert_cols_equals(a, a.rows(), "A"); - Eigen::SparseMatrix a_copy = a; - EigenDense c; - EigenDense k; - const int comps = igl::connected_components(a_copy, c, k); - return std::make_tuple(comps, npe::move(c), npe::move(k)); - -npe_end_code() - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for connected_components + auto connected_components( const Eigen::SparseMatrix &A) + { + Eigen::VectorXI C; + Eigen::VectorXI K; + Integer num_components = (Integer)igl::connected_components(A, C, K); + return std::make_tuple(num_components, C, K); + } +} + +// Bind the wrapper to the Python module +void bind_connected_components(nb::module_ &m) +{ + m.def( + "connected_components", + &pyigl::connected_components, + "A"_a, +R"(Determine the connected components of a graph described by the input adjacency matrix. + +@param[in] A #A by #A adjacency matrix (treated as describing a directed graph) +@param[out] C (if return_C=True) #A list of component indices in [0,#K-1] +@param[out] K (if return_K=True) #K list of sizes of each component +@return number of connected components)"); +} diff --git a/src/copyleft/cgal/convex_hull.cpp b/src/copyleft/cgal/convex_hull.cpp index e2127735..a3c3de52 100644 --- a/src/copyleft/cgal/convex_hull.cpp +++ b/src/copyleft/cgal/convex_hull.cpp @@ -1,35 +1,32 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Alec Jacobson -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include - +#include "default_types.h" #include +#include +#include +#include -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) +namespace nb = nanobind; +using namespace nb::literals; -npe_arg(v, dense_float, dense_double) -npe_begin_code() +namespace pyigl +{ + // Second overload: convex_hull with only output F + auto convex_hull(const nb::DRef &V) + { + Eigen::MatrixXI F; + igl::copyleft::cgal::convex_hull(V, F); + return F; + } +} - EigenDenseInt g; - igl::copyleft::cgal::convex_hull(v, g); - return npe::move(g); +// Bind the wrapper to the Python module +void bind_convex_hull(nb::module_ &m) +{ + m.def( + "convex_hull", + &pyigl::convex_hull, + "V"_a, + R"(Compute the convex hull of a set of points, returning only the triangular faces of the hull. -npe_end_code() + @param[in] V #V by 3 matrix of input points + @return F: #F by 3 matrix of triangle indices into V)"); +} diff --git a/src/copyleft/cgal/fast_winding_number.cpp b/src/copyleft/cgal/fast_winding_number.cpp new file mode 100644 index 00000000..c2a34eb0 --- /dev/null +++ b/src/copyleft/cgal/fast_winding_number.cpp @@ -0,0 +1,46 @@ +#include "default_types.h" +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Overload with `expansion_order` and `beta` + auto fast_winding_number( + const nb::DRef &P, + const nb::DRef &N, + const nb::DRef &Q, + int expansion_order, + double beta) + { + Eigen::VectorXd WN; + igl::copyleft::cgal::fast_winding_number(P, N, Q, expansion_order, beta, WN); + return WN; + } +} + +// Bind the wrapper to the Python module +void bind_fast_winding_number(nb::module_ &m) +{ + m.def( + "fast_winding_number", + &pyigl::fast_winding_number, + "P"_a, + "N"_a, + "Q"_a, + "expansion_order"_a = 2, + "beta"_a = 2.0, + R"(Evaluate the fast winding number for point data with adjustable accuracy. + + @param[in] P #P by 3 list of point locations + @param[in] N #P by 3 list of point normals + @param[in] Q #Q by 3 list of query points for the winding number + @param[in] expansion_order Order of the Taylor expansion (0, 1, or 2) + @param[in] beta Barnes-Hut style accuracy parameter (recommended: 2) + @return Vector of winding number values for each query point)"); + +} diff --git a/src/copyleft/cgal/intersect_other.cpp b/src/copyleft/cgal/intersect_other.cpp index 034e56bb..407a4162 100644 --- a/src/copyleft/cgal/intersect_other.cpp +++ b/src/copyleft/cgal/intersect_other.cpp @@ -1,94 +1,75 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Alec Jacobson -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include +#include "default_types.h" #include +#include +#include +#include +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + // First overload: intersect_other with detailed outputs + auto intersect_other( + const nb::DRef &VA, + const nb::DRef &FA, + const nb::DRef &VB, + const nb::DRef &FB, + const bool detect_only, + const bool first_only, + const bool stitch_all, + const bool slow_and_more_precise_rounding, + const int cutoff) + { + Eigen::MatrixXI IF; + Eigen::MatrixXN VVAB; + Eigen::MatrixXI FFAB; + Eigen::VectorXI JAB; + Eigen::VectorXI IMAB; -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. - + igl::copyleft::cgal::RemeshSelfIntersectionsParam params( + detect_only, first_only, stitch_all, slow_and_more_precise_rounding, cutoff); + const bool success = igl::copyleft::cgal::intersect_other( + VA, FA, VB, FB, params, IF, VVAB, FFAB, JAB, IMAB); -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} + return std::make_tuple(IF, VVAB, FFAB, JAB, IMAB); + } +} -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_int32, dense_int64) -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(); - Eigen::MatrixXi FAcpy = FA.template cast(); - Eigen::MatrixXd VBcpy = VB.template cast(); - Eigen::MatrixXi FBcpy = FB.template cast(); - - 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 IF = IFcpy.cast(); - EigenDenseLike VVAB = VVABcpy.cast(); - EigenDenseLike FFAB = FFABcpy.cast(); - EigenDenseLike JAB = JABcpy.cast(); - EigenDenseLike IMAB = IMABcpy.cast(); - return std::make_tuple( - npe::move(IF), - npe::move(VVAB), - npe::move(FFAB), - npe::move( JAB), - npe::move(IMAB)); -npe_end_code() +// Bind the wrapper to the Python module +void bind_intersect_other(nb::module_ &m) +{ + // First overload + m.def( + "intersect_other", + &pyigl::intersect_other, + "VA"_a, + "FA"_a, + "VB"_a, + "FB"_a, + "detect_only"_a=false, + "first_only"_a=false, + "stitch_all"_a=false, + "slow_and_more_precise_rounding"_a=false, + "cutoff"_a=1000, + R"(Detect intersecting faces between two triangle meshes, providing detailed output. + @param[in] VA #V by 3 list of vertices for first mesh + @param[in] FA #F by 3 list of faces for first mesh + @param[in] VB #V by 3 list of vertices for second mesh + @param[in] FB #F by 3 list of faces for second mesh + @param[in] detect_only only detect intersections, do not resolve + @param[in] first_only only return first intersection + @param[in] stitch_all stitch all intersections + @param[in] slow_and_more_precise_rounding use slow and more precise rounding + @param[in] cutoff maximum number of intersections to resolve + @return Tuple containing: + - success: bool indicating if the operation succeeded + - IF: # intersecting face pairs + - VVAB: list of intersection vertex positions + - FFAB: list of triangle indices into VVAB + - JAB: list of indices into [FA;FB] denoting the birth triangle + - IMAB: indices stitching duplicates from intersections)"); +} diff --git a/src/copyleft/cgal/intersect_with_half_space.cpp b/src/copyleft/cgal/intersect_with_half_space.cpp new file mode 100644 index 00000000..dab5f233 --- /dev/null +++ b/src/copyleft/cgal/intersect_with_half_space.cpp @@ -0,0 +1,87 @@ +#include "default_types.h" +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Overload with point and normal + auto intersect_with_half_space_point_normal( + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &p, + const nb::DRef &n) + { + Eigen::MatrixXN VC; + Eigen::MatrixXI FC; + Eigen::VectorXI J; + + if(!igl::copyleft::cgal::intersect_with_half_space(V, F, p, n, VC, FC, J)) + { + throw std::runtime_error("Failed to intersect with half space"); + } + return std::make_tuple(VC, FC, J); + } + + // Overload with plane equation + auto intersect_with_half_space_equation( + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &equ) + { + Eigen::MatrixXN VC; + Eigen::MatrixXI FC; + Eigen::VectorXI J; + + if(!igl::copyleft::cgal::intersect_with_half_space(V, F, equ, VC, FC, J)) + { + throw std::runtime_error("Failed to intersect with half space"); + } + return std::make_tuple( VC, FC, J); + } + +} + +// Bind the wrapper to the Python module +void bind_intersect_with_half_space(nb::module_ &m) +{ + m.def( + "intersect_with_half_space", + &pyigl::intersect_with_half_space_point_normal, + "V"_a, + "F"_a, + "p"_a, + "n"_a, + R"(Intersect a PWN mesh with a half-space using a point and normal. + + @param[in] V #V by 3 list of mesh vertex positions + @param[in] F #F by 3 list of triangle indices + @param[in] p 3D point on plane + @param[in] n 3D normal vector + @return Tuple containing: + - success: bool, true if successful + - VC: vertices of resulting mesh + - FC: face indices of resulting mesh + - J: birth facet indices)"); + + m.def( + "intersect_with_half_space", + &pyigl::intersect_with_half_space_equation, + "V"_a, + "F"_a, + "equ"_a, + R"(Intersect a PWN mesh with a half-space using the plane equation. + + @param[in] V #V by 3 list of mesh vertex positions + @param[in] F #F by 3 list of triangle indices + @param[in] equ Plane equation coefficients (a, b, c, d) + @return Tuple containing: + - success: bool, true if successful + - VC: vertices of resulting mesh + - FC: face indices of resulting mesh + - J: birth facet indices)"); +} diff --git a/src/copyleft/cgal/mesh_boolean.cpp b/src/copyleft/cgal/mesh_boolean.cpp index 90cd3c04..6f2f428f 100644 --- a/src/copyleft/cgal/mesh_boolean.cpp +++ b/src/copyleft/cgal/mesh_boolean.cpp @@ -1,70 +1,59 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Alec Jacobson -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include +#include "default_types.h" #include - - -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_int32, dense_int64) -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(); - Eigen::MatrixXd vb_copy = vb.template cast(); - Eigen::MatrixXi fa_copy = fa.template cast(); - Eigen::MatrixXi fb_copy = fb.template cast(); - - 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 vc = vc_copy.cast(); - EigenDenseLike fc = fc_copy.cast(); - EigenDenseLike j = j_copy.cast(); - return std::make_tuple(npe::move(vc), npe::move(fc), npe::move(j)); -npe_end_code() - +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto mesh_boolean( + const nb::DRef &VA, + const nb::DRef &FA, + const nb::DRef &VB, + const nb::DRef &FB, + const std::string &type_str) + { + Eigen::MatrixXN VC; + Eigen::MatrixXI FC; + Eigen::VectorXI J; + bool success = igl::copyleft::cgal::mesh_boolean(VA, FA, VB, FB, type_str, VC, FC, J); + if(!success) + { + throw std::runtime_error("Mesh boolean failed"); + } + return std::make_tuple( VC, FC, J); + } +} + +// Bind the wrapper to the Python module +void bind_mesh_boolean(nb::module_ &m) +{ + m.def( + "mesh_boolean", + &pyigl::mesh_boolean, + "VA"_a, + "FA"_a, + "VB"_a=Eigen::MatrixXN(), + "FB"_a=Eigen::MatrixXI(), + "type_str"_a, + R"(Compute the boolean operation (union, intersection, difference, etc.) between two meshes. + +@param[in] VA #VA by dim matrix of mesh A vertices +@param[in] FA #FA by simplex_size matrix of mesh A faces +@param[in] VB #VB by dim matrix of mesh B vertices +@param[in] FB #FB by simplex_size matrix of mesh B faces +@param[in] type_str Type of boolean operation: "union", "intersection", "difference", etc. +@param[out] VC #VC by dim matrix of result vertices +@param[out] FC #FC by simplex_size matrix of result faces +@param[out] J #FC list of indices indicating which input face contributed to each result face +@return Tuple containing: + - VC: Result vertices + - FC: Result faces + - J: Face origin indices)"); +} diff --git a/src/copyleft/cgal/module.cpp b/src/copyleft/cgal/module.cpp new file mode 100644 index 00000000..5224622f --- /dev/null +++ b/src/copyleft/cgal/module.cpp @@ -0,0 +1,12 @@ +#include +namespace nb = nanobind; + +// generated by cmake +#include "copyleft/cgal/BINDING_DECLARATIONS.in" + +NB_MODULE(pyigl_copyleft_cgal, m) { + m.doc() = "libigl cgal module python bindings"; + // generated by cmake +#include "copyleft/cgal/BINDING_INVOCATIONS.in" +} + diff --git a/src/copyleft/cgal/point_areas.cpp b/src/copyleft/cgal/point_areas.cpp new file mode 100644 index 00000000..b8162d10 --- /dev/null +++ b/src/copyleft/cgal/point_areas.cpp @@ -0,0 +1,58 @@ +#include "default_types.h" +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto point_areas( + const nb::DRef &P, + const nb::DRef &I, + const nb::DRef &N) + { + Eigen::VectorXN A; + Eigen::MatrixXN T; + igl::copyleft::cgal::point_areas(P,I,N,A,T); + return std::make_tuple(A,T); + } +} + +// Bind the wrapper to the Python module +void bind_point_areas(nb::module_ &m) +{ + m.def( + "point_areas", + &pyigl::point_areas, + "P"_a, + "I"_a, + "N"_a, + R"(Given a 3D set of points P, each with a list of k-nearest-neighbours, +estimate the geodesic voronoi area associated with each point. + +The k nearest neighbours may be known from running igl::knn_octree on +the output data from igl::octree. We reccomend using a k value +between 15 and 20 inclusive for accurate area estimation. + +N is used filter the neighbours, to ensure area estimation only occurs +using neighbors that are on the same side of the surface (ie for thin +sheets), as well as to solve the orientation ambiguity of the tangent +plane normal. + +\note This function *should* be implemented by pre-filtering I, rather +than filtering in this function using N. In this case, the function +would only take P and I as input. + +@param[in] P #P by 3 list of point locations +@param[in] I #P by k list of k-nearest-neighbor indices into P +@param[in] N #P by 3 list of point normals +@param[out] A #P list of estimated areas +@param[out] T #P by 3 list of tangent plane normals for each point + +\see igl::knn)"); +} + diff --git a/src/copyleft/cgal/remesh_self_intersections.cpp b/src/copyleft/cgal/remesh_self_intersections.cpp index 571afd6c..b85c8d4b 100644 --- a/src/copyleft/cgal/remesh_self_intersections.cpp +++ b/src/copyleft/cgal/remesh_self_intersections.cpp @@ -1,86 +1,67 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Alec Jacobson -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include +#include "default_types.h" #include +#include +#include +#include +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + // First overload: remesh_self_intersections with detailed outputs + auto remesh_self_intersections( + const nb::DRef &V, + const nb::DRef &F, + const bool detect_only, + const bool first_only, + const bool stitch_all, + const bool slow_and_more_precise_rounding, + const int cutoff) + { + Eigen::MatrixXN VV; + Eigen::MatrixXI FF; + Eigen::MatrixXI IF; + Eigen::VectorXI J; + Eigen::VectorXI IM; -const char* ds_remesh_self_intersections = R"igl_Qu8mg5v7( -REMESH_SELF_INTERSECTIONS Given a triangle mesh (V,F) compute a new mesh (VV,FF) -which is the same as (V,F) except that any self-intersecting triangles in (V,F) -have been subdivided (new vertices and face created) so that the -self-intersection contour lies exactly on edges in (VV,FF). New vertices will -appear in original faces or on original edges. New vertices on edges are -"merged" only across original faces sharing that edge. This means that if the -input triangle mesh is a closed manifold the output will be too. - + igl::copyleft::cgal::RemeshSelfIntersectionsParam params( + detect_only, first_only, stitch_all, slow_and_more_precise_rounding, cutoff); + igl::copyleft::cgal::remesh_self_intersections(V, F, params, VV, FF, IF, J, IM); + return std::make_tuple(VV, FF, IF, J, IM); + } -Parameters ----------- -V : #V by 3 list of vertex positions of mesh -F : #F by 3 list of triangle indices into rows of V -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} +} +// Bind the wrapper to the Python module +void bind_remesh_self_intersections(nb::module_ &m) +{ + // First overload + m.def( + "remesh_self_intersections", + &pyigl::remesh_self_intersections, + "V"_a, + "F"_a, + "detect_only"_a=false, + "first_only"_a=false, + "stitch_all"_a=false, + "slow_and_more_precise_rounding"_a=false, + "cutoff"_a=1000, + // Simple overload binding + R"(Resolve self-intersections in a mesh, without returning unique vertex indices (IM). -Returns -------- -VV : #VV by 3 list of vertex positions -FF : #FF by 3 list of triangle indices into VV -IF : #intersecting face pairs by 2 list of intersecting face pairs, indexing F -J : #FF list of indices into F denoting birth triangle -IM : #VV list of indices into VV of unique vertices. - -See also --------- -mesh_boolean -)igl_Qu8mg5v7"; + @param[in] V #V by 3 list of vertex positions + @param[in] F #F by 3 list of face indices + @param[in] detect_only only detect intersections, do not resolve + @param[in] first_only only return first intersection + @param[in] stitch_all stitch all intersections + @param[in] slow_and_more_precise_rounding use slow and more precise rounding + @param[in] cutoff maximum number of intersections to resolve + @return Tuple containing: + - VV: remeshed vertex positions + - FF: remeshed face indices + - IF: intersecting face pairs + - J: birth triangle indices)"); -npe_function(remesh_self_intersections) -npe_doc(ds_remesh_self_intersections) - -npe_arg(V, dense_float, dense_double) -npe_arg(F, dense_int32, dense_int64) -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 Vcpy = V.template cast(); - Eigen::MatrixXi Fcpy = F.template cast(); - - Eigen::MatrixXd VVcpy; - Eigen::MatrixXi FFcpy; - Eigen::VectorXi Jcpy; - Eigen::VectorXi IMcpy; - 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::remesh_self_intersections( - Vcpy,Fcpy,params,VVcpy,FFcpy,IFcpy,Jcpy,IMcpy); - - EigenDenseLike VV = VVcpy.cast(); - EigenDenseLike FF = FFcpy.cast(); - EigenDenseLike IF = IFcpy.cast(); - EigenDenseLike J = Jcpy.cast(); - EigenDenseLike IM = IMcpy.cast(); - return std::make_tuple(npe::move(VV), npe::move(FF), npe::move(IF), npe::move(J), npe::move(IM)); -npe_end_code() +} diff --git a/src/copyleft/cgal/trim_with_solid.cpp b/src/copyleft/cgal/trim_with_solid.cpp new file mode 100644 index 00000000..8745c343 --- /dev/null +++ b/src/copyleft/cgal/trim_with_solid.cpp @@ -0,0 +1,44 @@ +#include "default_types.h" +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto trim_with_solid( + const nb::DRef & VA, + const nb::DRef & FA, + const nb::DRef & VB, + const nb::DRef & FB) + { + Eigen::MatrixXN V; + Eigen::MatrixXI F; + Eigen::VectorXB D; + Eigen::VectorXI J; + igl::copyleft::cgal::trim_with_solid(VA, FA, VB, FB, V, F, D, J); + return std::make_tuple(V, F, D, J); + } +} + +// Binding function for the Python module +void bind_trim_with_solid(nb::module_ &m) +{ + m.def( + "trim_with_solid", + &pyigl::trim_with_solid, + "VA"_a, "FA"_a, "VB"_a, "FB"_a, + R"(Trim a mesh with another solid mesh, determining which faces lie inside or outside. + + @param[in] VA Vertex positions of mesh A + @param[in] FA Triangle indices of mesh A + @param[in] VB Vertex positions of mesh B (solid) + @param[in] FB Triangle indices of mesh B + @param[out] V Output vertex positions + @param[out] F Output triangle indices + @param[out] D Boolean vector indicating if each face is inside B + @param[out] J Indices into FA showing parent triangle)"); +} diff --git a/src/copyleft/module.cpp b/src/copyleft/module.cpp new file mode 100644 index 00000000..b6312cc9 --- /dev/null +++ b/src/copyleft/module.cpp @@ -0,0 +1,12 @@ +#include +namespace nb = nanobind; + +// generated by cmake +#include "copyleft/BINDING_DECLARATIONS.in" + +NB_MODULE(pyigl_copyleft_core, m) { + m.doc() = "libigl copyleft module python bindings"; + // generated by cmake +#include "copyleft/BINDING_INVOCATIONS.in" +} + diff --git a/src/copyleft/progressive_hulls.cpp b/src/copyleft/progressive_hulls.cpp new file mode 100644 index 00000000..fd96b877 --- /dev/null +++ b/src/copyleft/progressive_hulls.cpp @@ -0,0 +1,42 @@ +#include "default_types.h" +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // igl::decimate is a nightmare of poor templating. Suffer copies. + auto progressive_hulls( + const Eigen::MatrixXd &V, + const Eigen::MatrixXi &F, + const int max_m) + { + Eigen::MatrixXd U; + Eigen::MatrixXi G; + Eigen::VectorXi J; + igl::copyleft::progressive_hulls(V,F,max_m,U,G,J); + return std::make_tuple( + U.cast().eval(), // If Numeric==double will this incur a copy? + G.cast().eval(), + J.cast().eval()); + } +} +void bind_progressive_hulls(nb::module_ &m) +{ + m.def( + "progressive_hulls", + &pyigl::progressive_hulls, + "V"_a, "F"_a, "max_m"_a=0, + R"(Performs progressive hull simplification on a mesh, collapsing edges until a target number of faces is reached. + + @param[in] V #V by dim list of vertex positions + @param[in] F #F by 3 list of face indices into V + @param[in] max_m Target number of output faces + @param[out] U Output vertex positions + @param[out] G Output face indices into U + @param[out] J Indices into F indicating the birth face for each face in G)"); +} diff --git a/src/copyleft/tetgen/module.cpp b/src/copyleft/tetgen/module.cpp new file mode 100644 index 00000000..1428ef24 --- /dev/null +++ b/src/copyleft/tetgen/module.cpp @@ -0,0 +1,12 @@ +#include +namespace nb = nanobind; + +// generated by cmake +#include "copyleft/tetgen/BINDING_DECLARATIONS.in" + +NB_MODULE(pyigl_copyleft_tetgen, m) { + m.doc() = "libigl tetgen module python bindings"; + // generated by cmake +#include "copyleft/tetgen/BINDING_INVOCATIONS.in" +} + diff --git a/src/copyleft/tetgen/tetrahedralize.cpp b/src/copyleft/tetgen/tetrahedralize.cpp index ad0c139f..d1d38d02 100644 --- a/src/copyleft/tetgen/tetrahedralize.cpp +++ b/src/copyleft/tetgen/tetrahedralize.cpp @@ -1,69 +1,73 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Alec Jacobson -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include - - -#include -#include -#include - +#include "default_types.h" +#include +#include +#include +#include #include +#include +#include -const char* ds_tetrahedralize = R"igl_Qu8mg5v7( -)igl_Qu8mg5v7"; - -npe_function(tetrahedralize) -npe_doc(ds_tetrahedralize) +namespace nb = nanobind; +using namespace nb::literals; -npe_arg(V, dense_float, dense_double) -npe_default_arg(F, dense_int32, dense_int64, Eigen::MatrixXi(0,3)) -npe_default_arg(H, npe_matches(V) ,pybind11::array()) -npe_default_arg(switches, std::string, "Q") -npe_default_arg(return_adjacency_info, bool, false) -npe_default_arg(VM, npe_matches(F), pybind11::array()) -npe_default_arg(FM, npe_matches(F), pybind11::array()) -npe_default_arg( R, npe_matches(V), pybind11::array()) -npe_begin_code() -const bool has_markers = VM.size() > 0 || FM.size() > 0; -const bool has_regions = R.size() > 0; -EigenDenseLike TV; -EigenDenseLike TT, TF, TN, FT; -Eigen::Matrix TR,PT,TM; - -int num_regions; -igl::copyleft::tetgen::tetrahedralize( - V,F,H, - Eigen::Map>(VM.data(),VM.size()), - Eigen::Map>(FM.data(),FM.size()), - R,switches,TV,TT,TF,TM,TR,TN,PT,FT,num_regions); +namespace pyigl +{ + // Full version with all parameters + auto tetrahedralize( + const nb::DRef& V, + const nb::DRef& F, + const nb::DRef& H, + const nb::DRef& VM, + const nb::DRef& FM, + const nb::DRef& R, + const std::string& flags) + { + Eigen::MatrixXN TV; + Eigen::MatrixXI TT, TF, TN, FT; + Eigen::VectorXI TM, TR, PT; + int num_regions = 0; -auto ret = std::list({npe::move(TV), npe::move(TT), npe::move(TF)}); + int status = igl::copyleft::tetgen::tetrahedralize(V, F, H, VM, FM, R, flags, TV, TT, TF, TM, TR, TN, PT, FT, num_regions); + if(status != 0) + { + // throw error including status code + throw std::runtime_error("Tetrahedralization failed with status " + std::to_string(status)); + } -if(has_markers) -{ - ret.push_back(npe::move(TM)); -} -if(has_regions) -{ - ret.push_back(npe::move(TR)); -} -if(return_adjacency_info) -{ - ret.push_back(npe::move(TN)); - ret.push_back(npe::move(PT)); - ret.push_back(npe::move(FT)); -} -if(has_regions) -{ - ret.push_back(pybind11::cast(num_regions)); + return std::make_tuple(TV, TT, TF, TM, TR, TN, PT, FT, num_regions); + } } -return ret; +void bind_tetrahedralize(nb::module_ &m) +{ + m.def("tetrahedralize", &pyigl::tetrahedralize, + "V"_a, + "F"_a=Eigen::MatrixXI(), + "H"_a=Eigen::MatrixXN(), + "VM"_a=Eigen::VectorXI(), + "FM"_a=Eigen::VectorXI(), + "R"_a=Eigen::MatrixXN(), + "flags"_a="", + R"(Mesh the interior of a surface mesh (V,F) using tetgen -npe_end_code() +@param[in] V #V by 3 vertex position list +@param[in] F #F list of polygon face indices into V (0-indexed) +@param[in] H #H by 3 list of seed points inside holes +@param[in] VM #VM list of vertex markers +@param[in] FM #FM list of face markers +@param[in] R #R by 5 list of region attributes +@param[in] flags string of tetgen options (See tetgen documentation) e.g. + "pq1.414a0.01" tries to mesh the interior of a given surface with + quality and area constraints + "" will mesh the convex hull constrained to pass through V (ignores F) +@param[out] TV #TV by 3 vertex position list +@param[out] TT #TT by 4 list of tet face indices +@param[out] TF #TF by 3 list of triangle face indices ('f', else + `boundary_facets` is called on TT) +@param[out] TR #TT list of region ID for each tetrahedron +@param[out] TN #TT by 4 list of indices neighbors for each tetrahedron ('n') +@param[out] PT #TV list of incident tetrahedron for a vertex ('m') +@param[out] FT #TF by 2 list of tetrahedrons sharing a triface ('nn') +@param[out] num_regions Number of regions in output mesh)"); +} diff --git a/src/cotmatrix.cpp b/src/cotmatrix.cpp index 62e44cc3..cb8c9577 100644 --- a/src/cotmatrix.cpp +++ b/src/cotmatrix.cpp @@ -1,56 +1,75 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto cotmatrix( + const nb::DRef &V, + const nb::DRef &F) + { + Eigen::SparseMatrixN L; + igl::cotmatrix(V,F,L); + return L; + } + + auto cotmatrix_polygon( + const nb::DRef &V, + const nb::DRef &I, + const nb::DRef &C) + { + Eigen::SparseMatrixN L,M,P; + igl::cotmatrix(V,I,C,L,M,P); + return std::make_tuple(L,M,P); + } +} + +// Bind the wrapper to the Python module +void bind_cotmatrix(nb::module_ &m) +{ + m.def( + "cotmatrix", + &pyigl::cotmatrix, + "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,:))"); + + m.def( + "cotmatrix", + &pyigl::cotmatrix_polygon, + "V"_a, + "I"_a, + "C"_a, +R"(Cotangent Laplacian (and mass matrix) for polygon meshes according to +"Polygon Laplacian Made Simple" [Bunge et al.\ 2020] + +@param[in] V #V by 3 list of mesh vertex positions +@param[in] I #I vectorized list of polygon corner indices into rows of some matrix V +@param[in] C #polygons+1 list of cumulative polygon sizes so that C(i+1)-C(i) = size of + the ith polygon, and so I(C(i)) through I(C(i+1)-1) are the indices of + the ith polygon +@param[out] L #V by #V polygon Laplacian made simple matrix +@param[out] M #V by #V mass matrix +@param[out] P #V+#polygons by #V prolongation operator)" + ); +} -const char* ds_cotmatrix = R"igl_Qu8mg5v7( -Constructs the cotangent stiffness matrix (discrete laplacian) for a given mesh -(v, f). - -Parameters ----------- -v : #v by dim list of mesh vertex positions -f : #f by simplex_size list of mesh faces (must be triangles) - -Returns -------- -l : #v by #v cotangent matrix, each row i corresponding to v(i, :) - -See also --------- -adjacency_matrix - -Notes ------ -This Laplacian uses the convention that diagonal entries are -**minus** the sum of off-diagonal entries. The diagonal entries are -therefore in general negative and the matrix is **negative** semi-definite -(immediately, -L is **positive** semi-definite) - -Examples --------- -# Mesh in (v, f) ->>> l = cotmatrix(v, f) -)igl_Qu8mg5v7"; - -npe_function(cotmatrix) -npe_doc(ds_cotmatrix) -npe_arg(v, dense_double, dense_float) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - - assert_valid_tet_or_tri_mesh_23d(v, f); - EigenSparseLike l; - igl::cotmatrix(v, f, l); - return npe::move(l); - -npe_end_code() diff --git a/src/cotmatrix_entries.cpp b/src/cotmatrix_entries.cpp index 3028d396..ad544bc7 100644 --- a/src/cotmatrix_entries.cpp +++ b/src/cotmatrix_entries.cpp @@ -1,62 +1,54 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_cotmatrix_entries = R"igl_Qu8mg5v7( - -COTMATRIX_ENTRIES compute the cotangents of each angle in mesh (V,F) - -Parameters ----------- -V #V by dim list of rest domain positions -F #F by {3|4} list of {triangle|tetrahedra} indices into V - - -Returns -------- - C #F by 3 list of 1/2*cotangents corresponding angles - for triangles, columns correspond to edges [1,2],[2,0],[0,1] -OR - C #F by 6 list of 1/6*cotangents of dihedral angles*edge lengths - for tets, columns along edges [1,2],[2,0],[0,1],[3,0],[3,1],[3,2] - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(cotmatrix_entries) -npe_doc(ds_cotmatrix_entries) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_tet_or_tri_mesh_23d(v, f); - EigenDenseLike c; - igl::cotmatrix_entries(v, f, c); - return npe::move(c); - -npe_end_code() - - +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + Eigen::MatrixXN cotmatrix_entries_VF( + const nb::DRef &V, + const nb::DRef &F) + { + Eigen::MatrixXN C; + igl::cotmatrix_entries(V,F,C); + return C; + } + Eigen::MatrixXN cotmatrix_entries_l( + const nb::DRef &l) + { + Eigen::MatrixXN C; + igl::cotmatrix_entries(l,C); + return C; + } +} + +void bind_cotmatrix_entries(nb::module_ &m) +{ + m.def( + "cotmatrix_entries", + &pyigl::cotmatrix_entries_VF, + "V"_a, + "F"_a, +R"(Compute the cotangent contributions for each angle in a mesh. + +@param[in] V #V by dim matrix of vertex positions +@param[in] F #F by {3|4} matrix of {triangle|tetrahedra} indices into V (optional) + +@return C #F by {3|6} matrix of cotangent contributions + - For triangles, columns correspond to edges [1,2], [2,0], [0,1] + - For tets, columns correspond to edges [1,2], [2,0], [0,1], [3,0], [3,1], [3,2])"); + m.def( + "cotmatrix_entries", + &pyigl::cotmatrix_entries_l, + "l"_a, +R"(Compute the cotangent contributions for each angle in a mesh. + +@param[in] l #F by 3 matrix of triangle edge lengths (optional, alternative to F) +@return C #F by {3|6} matrix of cotangent contributions + - For triangles, columns correspond to edges [1,2], [2,0], [0,1] + - For tets, columns correspond to edges [1,2], [2,0], [0,1], [3,0], [3,1], [3,2])"); +} diff --git a/src/cotmatrix_intrinsic.cpp b/src/cotmatrix_intrinsic.cpp index 1210ffed..c08f15b5 100644 --- a/src/cotmatrix_intrinsic.cpp +++ b/src/cotmatrix_intrinsic.cpp @@ -1,62 +1,43 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char *ds_cotmatrix_intrinsic = R"igl_Qu8mg5v7( - +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto cotmatrix_intrinsic( + const nb::DRef &l, + const nb::DRef &F) + { + Eigen::SparseMatrixN L; + igl::cotmatrix_intrinsic(l,F,L); + return L; + } +} + +// Bind the wrapper to the Python module +void bind_cotmatrix_intrinsic(nb::module_ &m) +{ + m.def( + "cotmatrix_intrinsic", + &pyigl::cotmatrix_intrinsic, + "l"_a, + "F"_a, +R"( Constructs the cotangent stiffness matrix (discrete laplacian) for a given - mesh with faces F and edge lengths l. - -Parameters ----------- - -l #F by 3 list of (half-)edge lengths -F #F by 3 list of face indices into some (not necessarily - determined/embedable) list of vertex positions V. It is assumed #V == - F.maxCoeff()+1 - -Returns -------- - -L #V by #V sparse Laplacian matrix - -See also --------- - -cotmatrix, intrinsic_delaunay_cotmatrix - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(cotmatrix_intrinsic) -npe_doc(ds_cotmatrix_intrinsic) - -npe_arg(l, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_rows_match(l, f, "l", "f"); - assert_cols_equals(l, 3, "l"); - assert_valid_tri_mesh_faces(f); - - Eigen::SparseMatrix mat; - igl::cotmatrix_intrinsic(l, f, mat); - return npe::move(mat); - -npe_end_code() +mesh with faces F and edge lengths l. +@param[in] l #F by 3 list of (half-)edge lengths +@param[in] F #F by 3 list of face indices into some (not necessarily + determined/embedable) list of vertex positions V. It is assumed #V == + F.maxCoeff()+1 +@param[out] L #V by #V sparse Laplacian matrix +\see cotmatrix, intrinsic_delaunay_cotmatrix)"); +} diff --git a/src/cross_field_missmatch.cpp b/src/cross_field_missmatch.cpp deleted file mode 100644 index fe978ad9..00000000 --- a/src/cross_field_missmatch.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char* ds_cross_field_mismatch = R"igl_Qu8mg5v7( - -Parameters ----------- -V #V by 3 eigen Matrix of mesh vertex 3D positions -F #F by 3 eigen Matrix of face indices -PD1 #F by 3 eigen Matrix of the first per face cross field vector -PD2 #F by 3 eigen Matrix of the second per face cross field vector -isCombed boolean, specifying whether the field is combed (i.e. matching has been precomputed. - If not, the field is combed first. - - -Returns -------- -Handle_MMatch #F by 3 eigen Matrix containing the integer mismatch of the cross field - across all face edges - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(cross_field_mismatch) -npe_doc(ds_cross_field_mismatch) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(pd1, npe_matches(v)) -npe_arg(pd2, npe_matches(v)) -npe_arg(is_combed, bool) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - - assert_rows_match(f, pd1, "F", "PD1"); - assert_rows_match(f, pd2, "F", "PD2"); - assert_cols_equals(pd1, 3, "PD1"); - assert_cols_equals(pd2, 3, "PD2"); - - EigenDenseLike v_copy = v; - EigenDenseLike f_copy = f; - EigenDenseLike pd1_copy = pd1; - EigenDenseLike pd2_copy = pd2; - - EigenDenseLike mismatch; - - igl::cross_field_mismatch(v_copy, f_copy, pd1_copy, pd2_copy, is_combed, mismatch); - return npe::move(mismatch); - -npe_end_code() - - diff --git a/src/crouzeix_raviart_cotmatrix.cpp b/src/crouzeix_raviart_cotmatrix.cpp index 5e177619..8099b404 100644 --- a/src/crouzeix_raviart_cotmatrix.cpp +++ b/src/crouzeix_raviart_cotmatrix.cpp @@ -1,116 +1,41 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - - - +#include "default_types.h" #include - -const char* ds_crouzeix_raviart_cotmatrix = R"igl_Qu8mg5v7( - -CROUZEIX_RAVIART_COTMATRIX Compute the Crouzeix-Raviart cotangent - stiffness matrix. - -Parameters ----------- -V #V by dim list of vertex positions -F #F by 3/4 list of triangle/tetrahedron indices - -Returns -------- -L #E by #E edge/face-based diagonal cotangent matrix -E #E by 2/3 list of edges/faces -EMAP #F*3/4 list of indices mapping allE to E - -See also --------- -See also: crouzeix_raviart_massmatrix - -Notes ------ -None - -Examples --------- +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto crouzeix_raviart_cotmatrix( + const nb::DRef & V, + const nb::DRef & F, + const nb::DRef & E, + const nb::DRef & EMAP) + { + Eigen::SparseMatrix L; + igl::crouzeix_raviart_cotmatrix(V, F, E, EMAP, L); + return L; + } +} + +void bind_crouzeix_raviart_cotmatrix(nb::module_ &m) +{ + m.def("crouzeix_raviart_cotmatrix", &pyigl::crouzeix_raviart_cotmatrix, + "V"_a, + "F"_a, + "E"_a, + "EMAP"_a, + R"(Compute the Crouzeix-Raviart cotangent stiffness matrix. See for example "Discrete Quadratic Curvature Energies" [Wardetzky, Bergou, - Harmon, Zorin, Grinspun 2007] - -)igl_Qu8mg5v7"; - -npe_function(crouzeix_raviart_cotmatrix) -npe_doc(ds_crouzeix_raviart_cotmatrix) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_tet_or_tri_mesh_23d(v, f); - EigenSparseLike l; - EigenDenseLike e; - Eigen::Matrix emap; - igl::crouzeix_raviart_cotmatrix(v, f, l, e, emap); - return std::make_tuple(npe::move(l), npe::move(e), npe::move(emap)); - -npe_end_code() - - - -const char* ds_crouzeix_raviart_cotmatrix_known_e = R"igl_Qu8mg5v7( - -wrapper if E and EMAP are already computed (better match!) - -Parameters ----------- - - -Returns -------- - - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(crouzeix_raviart_cotmatrix_known_e) -npe_doc(ds_crouzeix_raviart_cotmatrix_known_e) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(e, npe_matches(f)) -npe_arg(emap, npe_matches(f)) - - -npe_begin_code() - - assert_valid_tet_or_tri_mesh(v, f); - //assert_rows_equals(emap, 3/4*f.rows(), "emap"); - //assert_cols_equals(e, 2/3, "emap"); - assert_cols_equals(emap, 1, "emap"); - EigenSparseLike l; - igl::crouzeix_raviart_cotmatrix(v, f, e, emap, l); - return npe::move(l); - -npe_end_code() - - +Harmon, Zorin, Grinspun 2007] +@param[in] V #V by dim list of vertex positions +@param[in] F #F by 3/4 list of triangle/tetrahedron indices +@param[in] E #E by 2/3 list of edges/faces +@param[in] EMAP #F*3/4 list of indices mapping allE to E +@param[out] L #E by #E edge/face-based diagonal cotangent matrix +\see crouzeix_raviart_massmatrix)"); +} diff --git a/src/crouzeix_raviart_massmatrix.cpp b/src/crouzeix_raviart_massmatrix.cpp index 23959660..71e244fa 100644 --- a/src/crouzeix_raviart_massmatrix.cpp +++ b/src/crouzeix_raviart_massmatrix.cpp @@ -1,119 +1,43 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// static assertion fail: YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY - -#include -#include -#include - - - - - - +#include "default_types.h" #include - -const char* ds_crouzeix_raviart_massmatrix = R"igl_Qu8mg5v7( - -CROUZEIX_RAVIART_MASSMATRIX Compute the Crouzeix-Raviart mass matrix where - M(e,e) is just the sum of the areas of the triangles on either side of an - edge e. - -Parameters ----------- -V #V by dim list of vertex positions -F #F by 3/4 list of triangle/tetrahedron indices - -Returns -------- -M #E by #E edge/face-based diagonal mass matrix -E #E by 2/3 list of edges/faces -EMAP #F*3/4 list of indices mapping allE to E - -See also --------- -crouzeix_raviart_cotmatrix - -Notes ------ +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto crouzeix_raviart_massmatrix( + const nb::DRef & V, + const nb::DRef & F, + const nb::DRef & E, + const nb::DRef & EMAP) + { + Eigen::SparseMatrix L; + igl::crouzeix_raviart_massmatrix(V, F, E, EMAP, L); + return L; + } +} + +void bind_crouzeix_raviart_massmatrix(nb::module_ &m) +{ + m.def("crouzeix_raviart_massmatrix", &pyigl::crouzeix_raviart_massmatrix, + "V"_a, + "F"_a, + "E"_a, + "EMAP"_a, + R"(CROUZEIX_RAVIART_MASSMATRIX Compute the Crouzeix-Raviart mass matrix where +M(e,e) is just the sum of the areas of the triangles on either side of an +edge e. See for example "Discrete Quadratic Curvature Energies" [Wardetzky, Bergou, - Harmon, Zorin, Grinspun 2007] - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(crouzeix_raviart_massmatrix) -npe_doc(ds_crouzeix_raviart_massmatrix) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_tet_or_tri_mesh_23d(v, f); - EigenSparseLike m; - EigenDenseLike e; - Eigen::Matrix emap; - igl::crouzeix_raviart_massmatrix(v, f, m, e, emap); - return std::make_tuple(npe::move(m), npe::move(e), npe::move(emap)); - -npe_end_code() - - - -const char* ds_crouzeix_raviart_massmatrix_known_e = R"igl_Qu8mg5v7( - -wrapper if E and EMAP are already computed (better match!) - -Parameters ----------- - - -Returns -------- - - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(crouzeix_raviart_massmatrix_known_e) -npe_doc(ds_crouzeix_raviart_massmatrix_known_e) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(e, npe_matches(f)) -npe_arg(emap, npe_matches(f)) - - -npe_begin_code() - - assert_valid_tet_or_tri_mesh(v, f); - //assert_rows_equals(emap, 3/4*f.rows(), "emap"); - //assert_cols_equals(e, 2/3, "emap"); - assert_cols_equals(emap, 1, "emap"); - EigenSparseLike m; - igl::crouzeix_raviart_massmatrix(v, f, e, emap, m); - return npe::move(m); - -npe_end_code() - - +Harmon, Zorin, Grinspun 2007] +@param[in] V #V by dim list of vertex positions +@param[in] F #F by 3/4 list of triangle/tetrahedron indices +@param[in] E #E by 2/3 list of edges/faces +@param[in] EMAP #F*3/4 list of indices mapping allE to E +@param[out] M #E by #E edge/face-based diagonal mass matrix +\see crouzeix_raviart_cotmatrix)"); +} diff --git a/src/cut_mesh.cpp b/src/cut_mesh.cpp index a5f50bbb..e2b55540 100644 --- a/src/cut_mesh.cpp +++ b/src/cut_mesh.cpp @@ -1,66 +1,56 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Francis Williams -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include +#include "default_types.h" #include -#include -#include - -const char* ds_cut_mesh = R"igl_Qu8mg5v7( -Compute the barycenter of every simplex - -Parameters ----------- -v : #v x dim matrix of vertex coordinates -f : #f x simplex_size matrix of indices of simplex corners into V -cuts : #F by 3 list of boolean flags, indicating the edges that need to - be cut (has 1 at the face edges that are to be cut, 0 otherwise) - -Returns -------- -A pair (vcut, fcut) where: - * vcut is a #v by 3 list of the vertex positions - of the cut mesh. This matrix will be similar to the original vertices except - some rows will be duplicated. - * fcut is a #f by 3 list of the faces of the cut mesh (must be triangles). This +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto cut_mesh( + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &C) + { + Eigen::MatrixXN Vn; + Eigen::MatrixXI Fn; + Eigen::VectorXI I; + igl::cut_mesh(V,F,C,Vn,Fn,I); + return std::make_tuple(Vn,Fn,I); + } + +} + +// Bind the wrapper to the Python module +void bind_cut_mesh(nb::module_ &m) +{ + m.def( + "cut_mesh", + &pyigl::cut_mesh, + "V"_a, + "F"_a, + "C"_a, +R"(Given a mesh and a list of edges that are to be cut, the function +generates a new disk-topology mesh that has the cuts at its boundary. + + +\note Assumes mesh is edge-manifold. +@param[in,out] V #V by 3 list of the vertex positions +@param[in,out] F #F by 3 list of the faces +@param[in] cuts #F by 3 list of boolean flags, indicating the edges that need to + be cut (has 1 at the face edges that are to be cut, 0 otherwise) +@param[out] Vn #V by 3 list of the vertex positions of the cut mesh. This matrix + will be similar to the original vertices except some rows will be + duplicated. +@param[out] Fn #F by 3 list of the faces of the cut mesh(must be triangles). This matrix will be similar to the original face matrix except some indices will be redirected to point to the newly duplicated vertices. +@param[out] I #V by 1 list of the map between Vn to original V index. +)"); +} -See also --------- - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(cut_mesh) -npe_doc(ds_cut_mesh) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(cuts, npe_matches(f)) -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - assert_shapes_match(f, cuts, "f", "cuts"); - - // FIXME: Copying is bad m'kay but libIGL's templates are broken - EigenDenseLike Vcopy = v; - EigenDenseLike Fcopy = f; - EigenDenseLike cutsCopy = cuts; - - EigenDenseLike Vcut; - EigenDenseLike Fcut; - - igl::cut_mesh(Vcopy, Fcopy, cutsCopy, Vcut, Fcut); - return std::make_tuple(npe::move(Vcut), npe::move(Fcut)); -npe_end_code() diff --git a/src/cut_mesh_from_singularities.cpp b/src/cut_mesh_from_singularities.cpp index 87d96a89..3ce69007 100644 --- a/src/cut_mesh_from_singularities.cpp +++ b/src/cut_mesh_from_singularities.cpp @@ -1,59 +1,37 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Francis Williams -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_cut_mesh_from_singularities = R"igl_Qu8mg5v7( -Given a mesh (v,f) and the integer mismatch of a cross field per edge -(mismatch), finds and returns the cut_graph connecting the singularities -(seams) - -Parameters ----------- -v : #v by 3 array of triangle vertices (each row is a vertex) -f : #f by 3 array of triangle indices into v -mismatch : #f by 3 array of per-corner integer mismatches - -Returns -------- -seams : #f by 3 array of per corner booleans that denotes if an edge is a - seam or not - -See also --------- -cut_mesh - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(cut_mesh_from_singularities) -npe_doc(ds_cut_mesh_from_singularities) -npe_arg(v, dense_double, dense_float) -npe_arg(f, dense_int32, dense_int64) -npe_arg(mismatch, npe_matches(f)) -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - assert_shapes_match(f, mismatch, "f", "mismatch"); - - // FIXME: LibIGL templates are broken so we need to do copies :'( - EigenDenseLike v_copy = v; - EigenDenseLike f_copy = f; - - Eigen::Matrix seams; - - igl::cut_mesh_from_singularities(v_copy, f_copy, mismatch, seams); - - return npe::move(seams); -npe_end_code() +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto cut_mesh_from_singularities( + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &MMatch) + { + Eigen::MatrixXB seams; + igl::cut_mesh_from_singularities(V, F, MMatch, seams); + return seams; + } +} + +void bind_cut_mesh_from_singularities(nb::module_ &m) +{ + m.def("cut_mesh_from_singularities", &pyigl::cut_mesh_from_singularities, + "V"_a, + "F"_a, + "MMatch"_a, + R"(Given a mesh (V,F) and the integer mismatch of a cross field per edge +(mismatch), finds the cut_graph connecting the singularities (seams) and the +degree of the singularities singularity_index +@param[in] V #V by 3 list of mesh vertex positions +@param[in] F #F by 3 list of faces +@param[in] mismatch #F by 3 list of per corner integer mismatch +@param[out] seams #F by 3 list of per corner booleans that denotes if an edge is a + seam or not)"); +} diff --git a/src/cut_to_disk.cpp b/src/cut_to_disk.cpp index 35b6b412..85e856b7 100644 --- a/src/cut_to_disk.cpp +++ b/src/cut_to_disk.cpp @@ -1,25 +1,33 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include +#include "default_types.h" #include - -const char *ds_cut_to_disk = R"igl_Qu8mg5v7( - -Given a triangle mesh, computes a set of edge cuts sufficient to carve the +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto cut_to_disk( const nb::DRef & F) + { + std::vector> cuts; + igl::cut_to_disk(F, cuts); + return cuts; + } +} + +void bind_cut_to_disk(nb::module_ &m) +{ + m.def("cut_to_disk", &pyigl::cut_to_disk, + "F"_a, + R"(Given a triangle mesh, computes a set of edge cuts sufficient to carve the mesh into a topological disk, without disconnecting any connected components. Nothing else about the cuts (including number, total length, or smoothness) is guaranteed to be optimal. - Simply-connected components without boundary (topological spheres) are left -untouched (delete any edge if you really want a disk). +untouched (delete any edge if you really want a disk). All other connected components are cut into disks. Meshes with boundary are supported; boundary edges will be included as cuts. @@ -28,44 +36,13 @@ The cut mesh itself can be materialized using cut_mesh(). Implements the triangle-deletion approach described by Gu et al's "Geometry Images." -Parameters ----------- - -F #F by 3 list of the faces (must be triangles) - -Returns -------- - -cuts List of cuts. Each cut is a sequence of vertex indices (where - pairs of consecutive vertices share a face), is simple, and is either - a closed loop (in which the first and last indices are identical) or - an open curve. Cuts are edge-disjoint. - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(cut_to_disk) -npe_doc(ds_cut_to_disk) - -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_tri_mesh_faces(f); - - std::vector > cuts; - igl::cut_to_disk(f, cuts); - return cuts; +@tparam Index Integrable type large enough to represent the total number of faces + and edges in the surface represented by F, and all entries of F. +@param[in] F #F by 3 list of the faces (must be triangles) +@param[out] cuts List of cuts. Each cut is a sequence of vertex indices (where + pairs of consecutive vertices share a face), is simple, and is either + a closed loop (in which the first and last indices are identical) or + an open curve. Cuts are edge-disjoint. -npe_end_code() +)"); +} diff --git a/src/cylinder.cpp b/src/cylinder.cpp index e8d551e3..8a6963de 100644 --- a/src/cylinder.cpp +++ b/src/cylinder.cpp @@ -1,61 +1,33 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example - -#include -#include -#include +#include "default_types.h" #include - -const char* ds_cylinder = R"igl_Qu8mg5v7( - -Construct a triangle mesh of a cylinder (without caps) - -Parameters ----------- -axis_devisions number of vertices _around the cylinder_ -height_devisions number of vertices _up the cylinder_ - -Returns -------- -V #V by 3 list of mesh vertex positions -F #F by 3 list of triangle indices into V - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(cylinder) -npe_doc(ds_cylinder) - -npe_arg(axis_devisions, int) -npe_arg(height_devisions, int) - - -npe_begin_code() - - Eigen::MatrixXd v; - Eigen::MatrixXi f; - igl::cylinder(axis_devisions, height_devisions, v, f); - EigenDenseFloat v_row_major = v; - EigenDenseInt f_row_major = f.template cast(); - return std::make_tuple(npe::move(v_row_major), npe::move(f_row_major)); - -npe_end_code() - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto cylinder( + int axis_devisions, + int height_devisions) + { + Eigen::MatrixXN V; + Eigen::MatrixXI F; + igl::cylinder(axis_devisions, height_devisions, V, F); + return std::make_tuple(V, F); + } +} + +void bind_cylinder(nb::module_ &m) +{ + m.def("cylinder", &pyigl::cylinder, + "axis_devisions"_a, + "height_devisions"_a, + R"(Construct a triangle mesh of a cylinder (without caps) +@param[in] axis_devisions number of vertices _around the cylinder_ +@param[in] height_devisions number of vertices _up the cylinder_ +@param[out] V #V by 3 list of mesh vertex positions +@param[out] F #F by 3 list of triangle indices into V)"); +} diff --git a/src/decimate.cpp b/src/decimate.cpp index c957c045..939fddf6 100644 --- a/src/decimate.cpp +++ b/src/decimate.cpp @@ -1,84 +1,64 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __miss 4 functions - -#include -#include -#include - - - - - - +#include "default_types.h" #include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // igl::decimate is a nightmare of poor templating. Suffer copies. + auto decimate( + const Eigen::MatrixXd &V, + const Eigen::MatrixXi &F, + const int max_m, + const bool block_intersections) + { + Eigen::MatrixXd U; + Eigen::MatrixXi G; + Eigen::VectorXi J,I; + igl::decimate(V,F,max_m,block_intersections,U,G,J,I); + return std::make_tuple( + U.cast().eval(), // If Numeric==double will this incur a copy? + G.cast().eval(), + J.cast().eval(), + I.cast().eval()); + } +} + +// Bind the wrapper to the Python module +void bind_decimate(nb::module_ &m) +{ + m.def( + "decimate", + &pyigl::decimate, + "V"_a, + "F"_a, + "max_m"_a = 0, + "block_intersections"_a = false, + R"(Assumes (V,F) is a manifold mesh (possibly with boundary) collapses edges +until desired number of faces is achieved. This uses default edge cost and +merged vertex placement functions {edge length, edge midpoint}. + +See \fileinfo for more details. + +@param[in] V #V by dim list of vertex positions +@param[in] F #F by 3 list of face indices into V. +@param[in] max_m desired number of output faces +@param[in] block_intersections whether to block intersections (see + intersection_blocking_collapse_edge_callbacks) +@param[out] U #U by dim list of output vertex posistions (can be same ref as V) +@param[out] G #G by 3 list of output face indices into U (can be same ref as G) +@param[out] J #G list of indices into F of birth face +@param[out] I #U list of indices into V of birth vertices +@return true if m was reached (otherwise #G > m))"); + +} -const char* ds_decimate = R"igl_Qu8mg5v7( - -Assumes (V,F) is a manifold mesh (possibly with boundary) Collapses edges - until desired number of faces is achieved. This uses default edge cost and - merged vertex placement functions {edge length, edge midpoint}. - -Parameters ----------- -V #V by dim list of vertex positions -F #F by 3 list of face indices into V. -max_m desired number of output faces -block_intersections whether to block intersections - -Returns -------- -U #U by dim list of output vertex posistions (can be same ref as V) -G #G by 3 list of output face indices into U (can be same ref as G) -J #G list of indices into F of birth face -I #U list of indices into V of birth vertices -Returns true if m was reached (otherwise #G > m) - -See also --------- - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(decimate) -npe_doc(ds_decimate) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(max_m, size_t) -npe_arg(block_intersections, bool) - - -npe_begin_code() - - assert_valid_23d_tri_mesh(v, f); - // TODO: remove __copy - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::MatrixXi f_copy = f.template cast(); - Eigen::MatrixXd u; - Eigen::MatrixXi g; - Eigen::VectorXi j; - Eigen::VectorXi i; - bool reach = igl::decimate(v_copy, f_copy, max_m, block_intersections, u, g, j, i); - EigenDenseFloat u_row_major = u; - EigenDenseInt g_row_major = g.template cast(); - // FIXME: vector not allowing row major, but they should be essentially the same so i feel we can leave it as col major - Eigen::Matrix j_row_major = j.template cast(); - Eigen::Matrix i_row_major = i.template cast(); - return std::make_tuple(reach, npe::move(u_row_major), npe::move(g_row_major), npe::move(j_row_major), npe::move(i_row_major)); -npe_end_code() diff --git a/src/deform_skeleton.cpp b/src/deform_skeleton.cpp deleted file mode 100644 index e3b82f02..00000000 --- a/src/deform_skeleton.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - - - -#include - -const char *ds_deform_skeleton = R"igl_Qu8mg5v7( -Deform a skeleton. -Parameters ----------- -C #C by 3 list of joint positions -BE #BE by 2 list of bone edge indices -T #BE*4 by 3 list of stacked transformation matrix - -Returns -------- -CT #BE*2 by 3 list of deformed joint positions -BET #BE by 2 list of bone edge indices (maintains order) -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(deform_skeleton) -npe_doc(ds_deform_skeleton) - -npe_arg(c, dense_float, dense_double) -npe_arg(be, dense_int32, dense_int64) -npe_arg(t, dense_float, dense_double) - -npe_begin_code() - assert_nonzero_rows(c, "C"); - assert_cols_equals(c, 3, "C"); - assert_rows_equals(t, be.rows()*4, "T"); - assert_cols_equals(be, 2, "BE"); - assert_cols_equals(t, 3, "T"); - - Eigen::MatrixXd c_copy = c.template cast(); - Eigen::MatrixXi be_copy = be.template cast(); - Eigen::MatrixXd t_copy = t.template cast(); - - Eigen::MatrixXd ct; - Eigen::MatrixXi bet; - igl::deform_skeleton(c_copy, be_copy, t_copy, ct, bet); - - EigenDenseLike ct_row_major = ct.template cast(); - EigenDenseLike bet_row_major = bet.template cast(); - return std::make_tuple(npe::move(ct_row_major), npe::move(bet_row_major)); - -npe_end_code() diff --git a/src/dihedral_angles.cpp b/src/dihedral_angles.cpp index 0b5d1643..84f22549 100644 --- a/src/dihedral_angles.cpp +++ b/src/dihedral_angles.cpp @@ -1,73 +1,33 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_dihedral_angles = R"igl_Qu8mg5v7( -Compute dihedral angles for all tets of a given tet mesh (v, t). - -Parameters ----------- -v : #v by 3 list of vertex positions -t : #v by 4 list of tet indices - -Returns -------- -theta : #t by 6 list of dihedral angles (in radians) -cos_theta : #t by 6 list of cosine of dihedral angles (in radians) - -See also --------- - - -Notes ------ -None - -Examples --------- -# TetMesh in (v, t) ->>> theta, cos_theta = dihedral_angles(v, t) -)igl_Qu8mg5v7"; - -npe_function(dihedral_angles) -npe_doc(ds_dihedral_angles) -npe_arg(v, dense_float, dense_double) -npe_arg(t, dense_int32, dense_int64) - -npe_begin_code() - assert_valid_tet_mesh(v, t); - EigenDenseLike theta; - EigenDenseLike cos_theta; - igl::dihedral_angles(v, t, theta, cos_theta); - return std::make_tuple(npe::move(theta), npe::move(cos_theta)); - -npe_end_code() - - -const char* ds_dihedral_angles_intrinsic = R"igl_Qu8mg5v7( -See dihedral_angles for the documentation. -)igl_Qu8mg5v7"; - -npe_function(dihedral_angles_intrinsic) -npe_doc(ds_dihedral_angles_intrinsic) -npe_arg(l, dense_float, dense_double) -npe_arg(a, npe_matches(l)) - -npe_begin_code() - - EigenDenseLike theta; - EigenDenseLike cos_theta; - igl::dihedral_angles_intrinsic(l, a, theta, cos_theta); - return std::make_tuple(npe::move(theta), npe::move(cos_theta)); - -npe_end_code() - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto dihedral_angles( + const nb::DRef& V, + const nb::DRef& T) + { + Eigen::MatrixXN theta, cos_theta; + igl::dihedral_angles(V, T, theta, cos_theta); + return std::make_tuple(theta, cos_theta); + + } +} + +void bind_dihedral_angles(nb::module_ &m) +{ + m.def("dihedral_angles", &pyigl::dihedral_angles, + "V"_a, + "T"_a, + R"(Compute dihedral angles for all tets of a given tet mesh (V,T). +@param[in] V #V by dim list of vertex positions +@param[in] T #V by 4 list of tet indices +@param[out] theta #T by 6 list of dihedral angles (in radians) +@param[out] cos_theta #T by 6 list of cosine of dihedral angles (in radians))"); +} diff --git a/src/dihedral_angles_intrinsic.cpp b/src/dihedral_angles_intrinsic.cpp new file mode 100644 index 00000000..3eb3d41e --- /dev/null +++ b/src/dihedral_angles_intrinsic.cpp @@ -0,0 +1,34 @@ +#include "default_types.h" +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto dihedral_angles_intrinsic( + const nb::DRef& A, + const nb::DRef& L) + { + Eigen::MatrixXN theta, cos_theta; + igl::dihedral_angles_intrinsic(A, L, theta, cos_theta); + return std::make_tuple(theta, cos_theta); + + } +} + +void bind_dihedral_angles_intrinsic(nb::module_ &m) +{ + m.def("dihedral_angles_intrinsic", &pyigl::dihedral_angles_intrinsic, + "A"_a, + "L"_a, + R"(Compute dihedral angles for all tets of a given tet mesh's intrinsics. +@param[in] V #V by dim list of vertex positions +@param[in] T #V by 4 list of tet indices +@param[out] theta #T by 6 list of dihedral angles (in radians) +@param[out] cos_theta #T by 6 list of cosine of dihedral angles (in radians))"); +} + diff --git a/src/direct_delta_mush.cpp b/src/direct_delta_mush.cpp deleted file mode 100644 index 82580c24..00000000 --- a/src/direct_delta_mush.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Kishore Venkateshan -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char* ds_direct_delta_mush = R"igl_Qu8mg5v7( -Perform Direct Delta Mush Skinning. -Computes Direct Delta Mesh Skinning (Variant 0) from -"Direct Delta Mush Skinning and Variants" - -Parameters ----------- -v #V by 3 list of rest pose vertex positions -t #E*4 by 3 list of bone pose transformations -omega #V by #E*10 list of precomputated matrix values - -Returns -------- -u #V by 3 list of output vertex positions - -See also --------- - - -Notes ------ -None - -Examples --------- -)igl_Qu8mg5v7"; - -npe_function(direct_delta_mush) -npe_doc(ds_direct_delta_mush) - -npe_arg(v, dense_double) -npe_arg(t, dense_double) -npe_arg(omega, dense_double) - -npe_begin_code() - assert_cols_equals(v, 3, "v"); - assert_valid_bone_transforms(t, "t"); - assert_rows_equals(t, (omega.cols() * 4) / 10, "t"); - - std::vector> - t_affine(t.rows() / 4); - - for(int bone = 0; bone < t_affine.size(); ++bone) - { - t_affine[bone] = Eigen::Affine3d::Identity(); - t_affine[bone].matrix().block(0, 0, 3, 4) = t.block(bone * 4, 0, 4, 3).transpose(); - } - - EigenDenseLike u; - igl::direct_delta_mush(v, t_affine, omega, u); - return npe::move(u); - -npe_end_code() - -const char* ds_direct_delta_mush_precomp = R"igl_Qu8mg5v7( -Do the Omega precomputation necessary for Direct Delta Mush Skinning. - -Parameters ----------- -v #V by 3 list of rest pose vertex positions -f #F by 3 list of triangle indices into rows of V -w #V by #Edges list of weights -p number of smoothing iterations -lambda rotation smoothing step size -kappa translation smoothness step size -alpha translation smoothness blending weight - -Returns -------- -omega : #V by #E*10 list of precomputated matrix values - -See also --------- - - -Notes ------ -None - -Examples --------- -)igl_Qu8mg5v7"; - -npe_function(direct_delta_mush_precomputation) -npe_doc(ds_direct_delta_mush_precomp) - -npe_arg(v, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(w, npe_matches(v)) -npe_arg(p, int) -npe_arg(lambda, double) -npe_arg(kappa, double) -npe_arg(alpha, double) - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - - Eigen::MatrixXd w_copy = w.template cast(); - - EigenDenseLike omega; - igl::direct_delta_mush_precomputation(v, f, w_copy, p, lambda, kappa, alpha, omega); - return npe::move(omega); - -npe_end_code() diff --git a/src/directed_edge_orientations.cpp b/src/directed_edge_orientations.cpp deleted file mode 100644 index 1a71a302..00000000 --- a/src/directed_edge_orientations.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - -#include - -const char *ds_directed_edge_orientations = R"igl_Qu8mg5v7( - Determine rotations that take each edge from the x-axis to its given rest - orientation. -Parameters ----------- -C #C by 3 list of edge vertex positions -E #E by 2 list of directed edges - -Returns -------- -Q #E list of quaternions - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(directed_edge_orientations) -npe_doc(ds_directed_edge_orientations) - -npe_arg(c, dense_float, dense_double) -npe_arg(e, dense_int32, dense_int64) - - -npe_begin_code() - assert_cols_equals(c, 3, "C"); - assert_cols_equals(e, 2, "E"); - Eigen::MatrixXd c_copy = c.template cast(); - std::vector > q; - igl::directed_edge_orientations(c_copy, e, q); - EigenDenseLike q_copy(q.size(), 4); - for(size_t i = 0; i < q.size(); ++i){ - q_copy(i, 0) = q[i].x(); - q_copy(i, 1) = q[i].y(); - q_copy(i, 2) = q[i].z(); - q_copy(i, 3) = q[i].w(); - } - return npe::move(q_copy); - - npe_end_code() diff --git a/src/directed_edge_parents.cpp b/src/directed_edge_parents.cpp deleted file mode 100644 index 23f827fb..00000000 --- a/src/directed_edge_parents.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char* ds_directed_edge_parents = R"igl_Qu8mg5v7( -Recover "parents" (preceding edges) in a tree given just directed edges. - -Parameters ----------- -e : #e by 2 list of directed edges - -Returns -------- -p : #e list of parent indices into e. (-1) means root - -See also --------- - - -Notes ------ -None - -Examples --------- -e.np.random.randint(0, 10, size=(10, 2)) -p = directed_edge_parents(e) -)igl_Qu8mg5v7"; - -npe_function(directed_edge_parents) -npe_doc(ds_directed_edge_parents) -npe_arg(e, dense_int32, dense_int64) -npe_begin_code() - - assert_nonzero_rows(e, "e"); - assert_cols_equals(e, 2, "e"); - EigenDenseLike p; - igl::directed_edge_parents(e, p); - return npe::move(p); - -npe_end_code() - - diff --git a/src/doublearea.cpp b/src/doublearea.cpp index e70b06ab..7b26414f 100644 --- a/src/doublearea.cpp +++ b/src/doublearea.cpp @@ -1,53 +1,91 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include +#include +#include +#include +#include -const char* ds_doublearea = R"igl_Qu8mg5v7( -Computes twice the area for each input triangle[quad] +namespace nb = nanobind; +using namespace nb::literals; -Parameters ----------- -v : #v by dim array of mesh vertex positions -f : #f by simplex_size array of mesh faces (must be triangles or quads) +namespace pyigl +{ + // Wrapper for doublearea function + auto doublearea_VF( + const nb::DRef &V, + const nb::DRef &F) + { + Eigen::VectorXN dblA; + igl::doublearea(V, F, dblA); + return dblA; + } + auto doublearea_ABC( + const nb::DRef &A, + const nb::DRef &B, + const nb::DRef &C) + { + Eigen::VectorXN dblA; + igl::doublearea(A, B, C, dblA); + return dblA; + } + auto doublearea_l( + const nb::DRef &l, + const Numeric nan_replacement) + { + Eigen::VectorXN dblA; + igl::doublearea(l, nan_replacement, dblA); + return dblA; + } +} -Returns -------- -d_area : #f list of triangle[quad] double areas (SIGNED only for 2D input) +// Bind the wrapper to the Python module +void bind_doublearea(nb::module_ &m) +{ + m.def( + "doublearea", + &pyigl::doublearea_VF, + "V"_a=Eigen::MatrixXN(), + "F"_a=Eigen::MatrixXI(), +R"(Computes twice the area for each input triangle or quad. -See also --------- -None +@param[in] V eigen matrix #V by 3 +@param[in] F #F by (3|4) list of mesh face indices into rows of V +@param[out] dblA #F list of triangle double areas + )" + ); + m.def( + "doublearea", + &pyigl::doublearea_ABC, + "A"_a=Eigen::MatrixXN(), + "B"_a=Eigen::MatrixXN(), + "C"_a=Eigen::MatrixXN(), +R"(Computes twice the area for each input triangle or quad. -Notes ------ -Known bug: For dim==3 complexity is O(#V + #F)!! Not just O(#F). This is a big deal if you have 1million unreferenced vertices and 1 face +@param[in] A #F by dim list of triangle corner positions +@param[in] B #F by dim list of triangle corner positions +@param[in] C #F by dim list of triangle corner positions +@param[out] dblA #F list of triangle double areas + )" + ); + m.def( + "doublearea", + &pyigl::doublearea_l, + "l"_a=Eigen::MatrixXN(), + "nan_replacement"_a=std::numeric_limits::quiet_NaN(), +R"(Computes twice the area for each input triangle or quad. -Examples --------- -# Mesh in (v, f) ->>> dbl_area = doublearea(v, f) -)igl_Qu8mg5v7"; - -npe_function(doublearea) -npe_doc(ds_doublearea) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - -npe_begin_code() - - assert_valid_tet_or_tri_mesh_23d(v, f); - EigenDenseLike d_area; - igl::doublearea(v, f, d_area); - return npe::move(d_area); - -npe_end_code() +@param[in] l #F by dim list of edge lengths using for triangles, columns correspond to edges 23,31,12 +@param[in] nan_replacement what value should be used for triangles whose given + edge lengths do not obey the triangle inequality. These may be very + wrong (e.g., [100 1 1]) or may be nearly degenerate triangles whose + floating point side length computation leads to breach of the triangle + inequality. One may wish to set this parameter to 0 if side lengths l + are _known_ to come from a valid embedding (e.g., some mesh (V,F)). In + that case, the only circumstance the triangle inequality is broken is + when the triangle is nearly degenerate and floating point error + dominates: hence replacing with zero is reasonable. +@param[out] dblA #F list of triangle double areas + )" + ); +} diff --git a/src/dqs.cpp b/src/dqs.cpp deleted file mode 100644 index 206df46b..00000000 --- a/src/dqs.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - - - -#include - -const char *ds_dqs = R"igl_Qu8mg5v7( - Dual quaternion skinning - -Parameters ----------- -V #V by 3 list of rest positions -W #W by #C list of weights -vQ #C list of rotation quaternions -vT #C list of translation vectors - -Returns -------- -U #V by 3 list of new positions - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(dqs) -npe_doc(ds_dqs) - -npe_arg(v, dense_float, dense_double) -npe_arg(w, npe_matches(v)) -npe_arg(v_q, npe_matches(v)) -npe_arg(v_t, npe_matches(v)) - -npe_begin_code() - assert_cols_equals(v, 3, "V"); - assert_rows_equals(v_q, w.cols(), "vQ"); - assert_cols_equals(v_q, 4, "vQ"); - assert_cols_equals(v_t, 3, "vT"); - assert_rows_match(v_q, v_t, "vQ", "vT"); - - typedef Eigen::Quaternion Quat; - - std::vector> v_q_copy(v_q.rows()); - for(size_t i = 0; i < v_q_copy.size(); ++i){ - v_q_copy[i].x() = v_q(i, 0); - v_q_copy[i].y() = v_q(i, 1); - v_q_copy[i].z() = v_q(i, 2); - v_q_copy[i].w() = v_q(i, 3); - } - - std::vector> v_t_copy(v_t.rows()); - for(size_t i = 0; i < v_t_copy.size(); ++i) - v_t_copy[i] = v_t.row(i); - - EigenDenseLike u; - igl::dqs(v, w, v_q_copy, v_t_copy, u); - return npe::move(u); - -npe_end_code() - - diff --git a/src/dual_contouring.cpp b/src/dual_contouring.cpp new file mode 100644 index 00000000..0888cf79 --- /dev/null +++ b/src/dual_contouring.cpp @@ -0,0 +1,75 @@ +#include "default_types.h" +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto dual_contouring( + const std::function &)> &f, + const std::function(const Eigen::Matrix &)> &f_grad, + /* Let these get copied if needed */ + const Eigen::Matrix &min_corner, + const Eigen::Matrix &max_corner, + const Integer nx, + const Integer ny, + const Integer nz, + const bool constrained, + const bool triangles, + const bool root_finding) + { + Eigen::MatrixXN V; + Eigen::MatrixXI Q; + igl::dual_contouring(f,f_grad,min_corner,max_corner,nx,ny,nz,constrained,triangles,root_finding,V,Q); + return std::make_tuple(V,Q); + } +} + +// Bind the wrapper to the Python module +void bind_dual_contouring(nb::module_ &m) +{ +// See https://github.com/libigl/libigl-python-bindings/issues/194 +// m.def( +// "dual_contouring", +// &pyigl::dual_contouring, +// "f"_a, +// "f_grad"_a, +// "min_corner"_a, +// "max_corner"_a, +// "nx"_a, +// "ny"_a, +// "nz"_a, +// "constrained"_a = false, +// "triangles"_a = false, +// "root_finding"_a = false, +// R"(Dual contouring to extract a pure quad mesh from differentiable implicit +//function using a dense grid. +// +//@param[in] f function returning >0 outside, <0 inside and =0 on the surface +//@param[in] f_grad function returning ∇f/‖∇f‖ +//@param[in] min_corner position of primal grid vertex at minimum corner +//@param[in] max_corner position of primal grid vertex at maximum corner +//@param[in] nx number of vertices on x side of primal grid +//@param[in] ny number of vertices on y side of primal grid +//@param[in] nz number of vertices on z side of primal grid +//@param[in] constrained whether to force dual vertices to lie strictly inside +// corresponding primal cell (prevents self-intersections at cost of +// surface quality; marginally slower) +//@param[in] triangles whether to output four triangles instead of one quad per +// crossing edge (quad mesh usually looks fine) +//@param[in] root_finding whether to use root finding to identify crossing point on +// each edge (improves quality a lot at cost of performance). If false, +// use linear interpolation. +//@param[out] V #V by 3 list of outputs vertex positions +//@param[out] Q #Q by 4 (or 3 if triangles=true) face indices into rows of V +// )"); +} + + + diff --git a/src/ears.cpp b/src/ears.cpp index 4b2931af..4649e628 100644 --- a/src/ears.cpp +++ b/src/ears.cpp @@ -1,55 +1,31 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_ears = R"igl_Qu8mg5v7( -FIND_EARS Find all ears (faces with two boundary edges) in a given mesh -Parameters ----------- -F #F by 3 list of triangle mesh indices - -Returns -------- - ears #ears list of indices into F of ears - ear_opp #ears list of indices indicating which edge is non-boundary - (connecting to flops) - -See also --------- - - -Notes ------ -None - -Examples --------- - ears,ear_opp = find_ears(F) - -)igl_Qu8mg5v7"; - -npe_function(ears) -npe_doc(ds_ears) - -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_tri_mesh_faces(f); - Eigen::Matrix ear; - Eigen::Matrix ear_opp; - igl::ears(f, ear, ear_opp); - return std::make_tuple(npe::move(ear), npe::move(ear_opp)); - -npe_end_code() - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto ears( + const nb::DRef & F) + { + Eigen::VectorXI ear,ear_opp; + igl::ears(F,ear,ear_opp); + return std::make_tuple(ear,ear_opp); + } +} + +void bind_ears(nb::module_ &m) +{ + m.def("ears", &pyigl::ears, + "F"_a, + R"(Find all ears (faces with two boundary edges) in a given mesh + +@param[in] F #F by 3 list of triangle mesh indices +@param[out] ears #ears list of indices into F of ears +@param[out] ear_opp #ears list of indices indicating which edge is non-boundary + (connecting to flops))"); +} diff --git a/src/edge_collapse_is_valid.cpp b/src/edge_collapse_is_valid.cpp deleted file mode 100644 index b62a788a..00000000 --- a/src/edge_collapse_is_valid.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - -#include - -const char *ds_edge_collapse_is_valid = R"igl_Qu8mg5v7( - - Assumes (V,F) is a closed manifold mesh (except for previouslly collapsed faces which should be set to: - [IGL_COLLAPSE_EDGE_NULL IGL_COLLAPSE_EDGE_NULL IGL_COLLAPSE_EDGE_NULL]. - Tests whether collapsing exactly two faces and exactly 3 edges from E (e - and one side of each face gets collapsed to the other) will result in a - mesh with the same topology. - -Parameters ----------- - e index into E of edge to try to collapse. E(e,:) = [s d] or [d s] so that sj) is the edge of F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1) "e=(j->i) - EI #E by 2 list of edge flap corners (see above). - -Returns -------- - Returns true if edge collapse is valid - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(edge_collapse_is_valid) -npe_doc(ds_edge_collapse_is_valid) - -npe_arg(edge, int) -npe_arg(F, dense_int32, dense_int64) -npe_arg(E, npe_matches(F)) -npe_arg(EMAP, npe_matches(F)) -npe_arg(EF, npe_matches(F)) -npe_arg(EI, npe_matches(F)) - - -npe_begin_code() - assert_valid_tri_mesh_faces(F); - assert_cols_equals(E, 2, "E"); - assert_rows_match(F, EMAP, "F", "EMAP"); - assert_cols_equals(EMAP, 3, "EMAP"); - assert_rows_match(E, EI, "E", "EI"); - assert_cols_equals(EI, 2, "EI"); - assert_rows_match(E, EF, "E", "EF"); - assert_cols_equals(EF, 2, "EF"); - - const Eigen::MatrixXi F_copy = F.template cast(); - const Eigen::MatrixXi E_copy = E.template cast(); - const Eigen::VectorXi EMAP_copy = EMAP.template cast(); - const Eigen::MatrixXi EF_copy = EF.template cast(); - const Eigen::MatrixXi EI_copy = EI.template cast(); - - bool ok = igl::edge_collapse_is_valid(edge, F_copy, E_copy, EMAP_copy, EF_copy, EI_copy); - return ok; - -npe_end_code() - diff --git a/src/edge_flaps.cpp b/src/edge_flaps.cpp index 55881cf2..922bbfe2 100644 --- a/src/edge_flaps.cpp +++ b/src/edge_flaps.cpp @@ -1,72 +1,66 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - +#include "default_types.h" #include - -const char *ds_edge_flaps = R"igl_Qu8mg5v7( - -Determine "edge flaps": two faces on either side of a unique edge (assumes edge-manifold mesh) - - -Parameters ----------- - -F #F by 3 list of face indices - -Returns -------- - -E #E by 2 list of edge indices into V. -EMAP #F*3 list of indices into E, mapping each directed edge to unique - unique edge in E -EF #E by 2 list of edge flaps, EF(e,0)=f means e=(i-->j) is the edge of - F(f,:) opposite the vth corner, where EI(e,0)=v. Similarly EF(e,1) " - e=(j->i) -EI #E by 2 list of edge flap corners (see above). - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(edge_flaps) -npe_doc(ds_edge_flaps) - -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_tri_mesh_faces(f); - - Eigen::MatrixXi f_copy = f.template cast(); - Eigen::MatrixXi e_copy; - Eigen::VectorXi emap_copy; - Eigen::MatrixXi ef_copy; - Eigen::MatrixXi ei_copy; - igl::edge_flaps(f_copy, e_copy, emap_copy, ef_copy, ei_copy); - - EigenDense e = e_copy.template cast(); - EigenDense emap = emap_copy.template cast(); - EigenDense ef = ef_copy.template cast(); - EigenDense ei = ei_copy.template cast(); - return std::make_tuple(npe::move(e), npe::move(emap), npe::move(ef), npe::move(ei)); - -npe_end_code() - - +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Binding for the first overload of edge_flaps with `uE` and `EMAP` as inputs + auto edge_flaps( + const nb::DRef &F, + const nb::DRef &uE, + const nb::DRef &EMAP) + { + Eigen::MatrixXI EF, EI; + igl::edge_flaps(F, uE, EMAP, EF, EI); + return std::make_tuple(EF, EI); + } + + // Binding for the second overload of edge_flaps where `uE` and `EMAP` are outputs + auto edge_flaps_F( + const nb::DRef &F) + { + Eigen::MatrixXI uE, EF, EI; + Eigen::VectorXI EMAP; + igl::edge_flaps(F, uE, EMAP, EF, EI); + return std::make_tuple(uE, EMAP, EF, EI); + } +} + +// Bind the wrapper to the Python module +void bind_edge_flaps(nb::module_ &m) +{ + m.def( + "edge_flaps", + &pyigl::edge_flaps, + "F"_a, + "uE"_a, + "EMAP"_a, + R"(Determine edge flaps with precomputed unique edge map and edge-face adjacency. + + @param[in] F #F by 3 list of face indices + @param[in] uE #uE by 2 list of unique edge indices + @param[in] EMAP #F*3 list of indices mapping each directed edge to unique edge in uE + @return Tuple containing EF and EI matrices, where: + EF - #E by 2 list of edge flaps + EI - #E by 2 list of edge flap corners)"); + + m.def( + "edge_flaps", + &pyigl::edge_flaps_F, + "F"_a, + R"(Determine edge flaps, unique edge map, and edge-face adjacency from face list only. + + @param[in] F #F by 3 list of face indices + @return Tuple containing uE, EMAP, EF, and EI where: + uE - #uE by 2 list of unique edge indices + EMAP - #F*3 list mapping each directed edge to unique edge in uE + EF - #E by 2 list of edge flaps + EI - #E by 2 list of edge flap corners)"); +} diff --git a/src/edge_lengths.cpp b/src/edge_lengths.cpp index 672ad801..0132ae5e 100644 --- a/src/edge_lengths.cpp +++ b/src/edge_lengths.cpp @@ -1,69 +1,40 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char *ds_edge_lengths = R"igl_Qu8mg5v7( - - - Constructs a list of lengths of edges opposite each index in a face - (triangle/tet) list - -Parameters ----------- -V eigen matrix #V by 3 -F #F by 2 list of mesh edges or -F #F by 3 list of mesh faces (must be triangles) or -T #T by 4 list of mesh elements (must be tets) - -Returns -------- -L #F by {1|3|6} list of edge lengths - for edges, column of lengths - for triangles, columns correspond to edges [1,2],[2,0],[0,1] - for tets, columns correspond to edges - [3 0],[3 1],[3 2],[1 2],[2 0],[0 1] - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(edge_lengths) -npe_doc(ds_edge_lengths) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_cols_equals(v, 3, "V"); - assert_nonzero_rows(v, "V"); - assert_nonzero_rows(f, "F"); - - const auto dim = f.cols(); - if(dim != 2 && dim != 3 && dim != 4) - throw pybind11::value_error("Invalid dimension for F must be 2, 3, or 4 but got " + std::to_string(dim)); - - EigenDenseLike l; - igl::edge_lengths(v, f, l); - return npe::move(l); - -npe_end_code() - - +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for edge_lengths function + auto edge_lengths( + const nb::DRef &V, + const nb::DRef &F) + { + Eigen::MatrixXN L; + igl::edge_lengths(V, F, L); + return L; + } +} + +// Bind the wrapper to the Python module +void bind_edge_lengths(nb::module_ &m) +{ + m.def( + "edge_lengths", + &pyigl::edge_lengths, + "V"_a, "F"_a, +R"(Constructs a list of lengths of edges opposite each index in a face +(triangle/tet) list. + +@param[in] V eigen matrix #V by 3 +@param[in] F #F by (2|3|4) list of mesh simplex indices into rows of V +@param[out] L #F by {1|3|6} list of edge lengths + - For edges, column of lengths + - For triangles, columns correspond to edges [1,2],[2,0],[0,1] + - For tets, columns correspond to edges [3 0],[3 1],[3 2],[1 2],[2 0],[0 1])" + ); +} diff --git a/src/edge_topology.cpp b/src/edge_topology.cpp deleted file mode 100644 index 4805b29c..00000000 --- a/src/edge_topology.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_edge_topology = R"igl_Qu8mg5v7( -Initialize Edges and their topological relations (assumes an edge-manifold mesh) - -Parameters ----------- -v : #v by dim, list of mesh vertex positions (unused) -f : #f by 3, list of triangle indices into V - -Returns -------- -ev : #e by 2, list of edges described as pair of vertices. -fe : #f by 3, list storing triangle-edge relation. -ef : #e by w, list storing edge-triangle relation, uses -1 to indicate boundaries. - -See also --------- - - -Notes ------ -None - -Examples --------- -# Mesh in (v, f) ->>> ev, fe, ef = edge_topology(v, f) -)igl_Qu8mg5v7"; - -npe_function(edge_topology) -npe_doc(ds_edge_topology) -npe_arg(v, dense_double, dense_float) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - - assert_valid_23d_tri_mesh(v, f); - EigenDenseLike ev, fe, ef; - igl::edge_topology(v, f, ev, fe, ef); - -return std::make_tuple(npe::move(ev), npe::move(fe), npe::move(ef)); - -npe_end_code() diff --git a/src/edges.cpp b/src/edges.cpp index 5ce6cab4..32af1e58 100644 --- a/src/edges.cpp +++ b/src/edges.cpp @@ -1,48 +1,79 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include +#include +#include +#include +#include #include +#include "default_types.h" -const char* ds_edges = R"igl_Qu8mg5v7( -Constructs a list of unique edges represented in a given mesh (v, f) +namespace nb = nanobind; +using namespace nb::literals; -Parameters ----------- -f : #F by dim list of mesh faces (must be triangles or tets) +namespace pyigl +{ + // Binding for edges function with face-based input + Eigen::MatrixXI edges_from_faces( + const nb::DRef &F) + { + Eigen::MatrixXI E; + igl::edges(F, E); + return E; + } -Returns -------- -#e by 2 list of edges in no particular order + // Binding for edges function with polygon corner indices + Eigen::MatrixXI edges_from_polygons( + const nb::DRef &I, + const nb::DRef &C) + { + Eigen::MatrixXI E; + igl::edges(I, C, E); + return E; + } -See also --------- -adjacency_matrix + // Binding for edges function with adjacency matrix input + Eigen::MatrixXI edges_from_adjacency( + const Eigen::SparseMatrix &A) + { + Eigen::MatrixXI E; + igl::edges(A, E); + return E; + } +} -Notes ------ +// Define bindings for each overload of the edges function +void bind_edges(nb::module_ &m) +{ + m.def( + "edges", + &pyigl::edges_from_faces, + "F"_a, + R"(Construct a list of unique edges from a given face matrix. -Examples --------- ->>> V, F, _ = igl.readOFF("test.off) ->>> E = igl.edges(F) +@param[in] F #F by (3|4) matrix of mesh face indices +@return #E by 2 matrix of unique edges +)" + ); -)igl_Qu8mg5v7"; + m.def( + "edges", + &pyigl::edges_from_polygons, + "I"_a, "C"_a, + R"(Construct a list of unique edges from a given list of polygon corner indices. -npe_function(edges) -npe_doc(ds_edges) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() +@param[in] I Vectorized list of polygon corner indices +@param[in] C #polygons+1 list of cumulative polygon sizes +@return #E by 2 matrix of unique edges +)" + ); - assert_valid_tet_or_tri_mesh_faces(f); - EigenDenseLike e; - igl::edges(f, e); - return npe::move(e); + m.def( + "edges", + &pyigl::edges_from_adjacency, // Using double for adjacency matrix example + "A"_a, + R"(Construct a list of unique edges from a given adjacency matrix. -npe_end_code() +@param[in] A #V by #V symmetric adjacency matrix +@return #E by 2 matrix of unique edges +)" + ); +} diff --git a/src/edges_to_path.cpp b/src/edges_to_path.cpp deleted file mode 100644 index 66989594..00000000 --- a/src/edges_to_path.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char* ds_edges_to_path = R"igl_Qu8mg5v7( - -EDGES_TO_PATH Given a set of undirected, unique edges such that all form a - single connected compoent with exactly 0 or 2 nodes with valence =1, - determine the/a path visiting all nodes. - -Parameters ----------- -E #E by 2 list of undirected edges - -Returns -------- -I #E+1 list of nodes in order tracing the chain (loop), if the output - is a loop then I(1) == I(end) -J #I-1 list of indices into E of edges tracing I -K #I-1 list of indices into columns of E {1,2} so that K(i) means that - E(i,K(i)) comes before the other (i.e., E(i,3-K(i)) ). This means that - I(i) == E(J(i),K(i)) for i<#I, or - I == E(sub2ind(size(E),J([1:end end]),[K;3-K(end)])))) - - -See also --------- - - -Notes ------ -None - -Examples --------- - - - -)igl_Qu8mg5v7"; - -npe_function(edges_to_path) -npe_doc(ds_edges_to_path) - -npe_arg(e, dense_int32, dense_int64) - - -npe_begin_code() - assert_nonzero_rows(e, "e"); - assert_cols_equals(e, 2, "e"); - - Eigen::MatrixXi e_copy = e.template cast(); - Eigen::VectorXi i_copy; - Eigen::VectorXi j_copy; - Eigen::VectorXi k_copy; - - igl::edges_to_path(e_copy, i_copy, j_copy, k_copy); - - EigenDenseLike i = i_copy.template cast(); - EigenDenseLike j = i_copy.template cast(); - EigenDenseLike k = i_copy.template cast(); - - return std::make_tuple(npe::move(i), npe::move(j), npe::move(k)); - -npe_end_code() - - diff --git a/src/embree/EmbreeIntersector.cpp b/src/embree/EmbreeIntersector.cpp new file mode 100644 index 00000000..763c169f --- /dev/null +++ b/src/embree/EmbreeIntersector.cpp @@ -0,0 +1,58 @@ +#include "default_types.h" +#include +#include +#include +#include +#include +#include +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto embree_intersect_ray_first( + igl::embree::EmbreeIntersector &ei, + const Eigen::RowVector3f& origin, + const Eigen::RowVector3f& direction, + const float tnear, + const float tfar) + { + igl::Hit h; + bool ret = ei.intersectRay(origin,direction,h,tnear,tfar); + if(!ret){ h.id = -1; } + return std::make_tuple( + (Integer)h.id, + (Numeric)h.t, + (Numeric)h.u, + (Numeric)h.v); + } + + auto embree_intersect_ray( + igl::embree::EmbreeIntersector &ei, + const Eigen::RowVector3f& origin, + const Eigen::RowVector3f& direction, + const float tnear, + const float tfar) + { + std::vector> hits; + int num_rays; + ei.intersectRay(origin,direction,hits,num_rays,tnear,tfar); + std::vector> out; + for(const auto &h : hits) + { + out.emplace_back(h.id,h.t,h.u,h.v); + } + return std::make_tuple(out,(Integer)num_rays); + } +} + +void bind_EmbreeIntersector(nb::module_ &m) +{ + using igl::embree::EmbreeIntersector; + nb::class_(m,"EmbreeIntersector") + .def(nb::init<>()) + .def("init", nb::overload_cast(&EmbreeIntersector::init), "V"_a, "F"_a, "isStatic"_a=false) + .def("intersectRay_first", &pyigl::embree_intersect_ray_first, "origin"_a, "direction"_a, "tnear"_a=0, "tfar"_a=std::numeric_limits::infinity()) + .def("intersectRay", &pyigl::embree_intersect_ray, "origin"_a, "direction"_a, "tnear"_a=0, "tfar"_a=std::numeric_limits::infinity()) + ; +} diff --git a/src/embree/ambient_occlusion.cpp b/src/embree/ambient_occlusion.cpp new file mode 100644 index 00000000..611ce081 --- /dev/null +++ b/src/embree/ambient_occlusion.cpp @@ -0,0 +1,71 @@ +#include "default_types.h" +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto ambient_occlusion( + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &P, + const nb::DRef &N, + const int num_samples) + { + Eigen::VectorXN S; + igl::embree::ambient_occlusion(V,F,P,N,num_samples,S); + return S; + } + auto ambient_occlusion_ei( + const igl::embree::EmbreeIntersector & ei, + const nb::DRef &P, + const nb::DRef &N, + const int num_samples) + { + Eigen::VectorXN S; + igl::embree::ambient_occlusion(ei,P,N,num_samples,S); + return S; + } +} + +// Bind the wrapper to the Python module +void bind_ambient_occlusion(nb::module_ &m) +{ + m.def( + "ambient_occlusion", + &pyigl::ambient_occlusion, + "V"_a, + "F"_a, + "P"_a, + "N"_a, + "num_samples"_a, +R"(Compute ambient occlusion per given point + +@param[in] V #V by 3 list of mesh vertex positiosn +@param[in] F #F by 3 list of mesh triangle indices into rows of V +@param[in] P #P by 3 list of origin points +@param[in] N #P by 3 list of origin normals +@param[out] S #P list of ambient occlusion values between 1 (fully occluded) and + 0 (not occluded) +)"); + m.def( + "ambient_occlusion", + &pyigl::ambient_occlusion_ei, + "ei"_a, + "P"_a, + "N"_a, + "num_samples"_a, +R"(Compute ambient occlusion per given point + +@param[in] ei EmbreeIntersector containing (V,F) +@param[in] P #P by 3 list of origin points +@param[in] N #P by 3 list of origin normals +@param[out] S #P list of ambient occlusion values between 1 (fully occluded) and + 0 (not occluded) +)"); +} diff --git a/src/embree/module.cpp b/src/embree/module.cpp new file mode 100644 index 00000000..077a4237 --- /dev/null +++ b/src/embree/module.cpp @@ -0,0 +1,12 @@ +#include +namespace nb = nanobind; + +// generated by cmake +#include "embree/BINDING_DECLARATIONS.in" + +NB_MODULE(pyigl_embree, m) { + m.doc() = "libigl embree module python bindings"; + // generated by cmake +#include "embree/BINDING_INVOCATIONS.in" +} + diff --git a/src/embree/reorient_facets_raycast.cpp b/src/embree/reorient_facets_raycast.cpp new file mode 100644 index 00000000..430286b8 --- /dev/null +++ b/src/embree/reorient_facets_raycast.cpp @@ -0,0 +1,60 @@ +#include "default_types.h" +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto reorient_facets_raycast( + const nb::DRef &V, + const nb::DRef &F, + const int rays_total_, + const int rays_minimum, + const bool facet_wise, + const bool use_parity, + const bool is_verbose) + { + const int rays_total = rays_total_==-1?F.rows()*100:rays_total_; + Eigen::VectorXB I; + Eigen::VectorXI C; + igl::embree::reorient_facets_raycast( + V,F,rays_total,rays_minimum,facet_wise,use_parity,is_verbose,I,C); + return std::make_tuple(I,C); + } +} + +// Bind the wrapper to the Python module +void bind_reorient_facets_raycast(nb::module_ &m) +{ + m.def( + "reorient_facets_raycast", + &pyigl::reorient_facets_raycast, + "V"_a, + "F"_a, + "rays_total"_a=-1, + "rays_minimum"_a=10, + "facet_wise"_a=false, + "use_parity"_a=false, + "is_verbose"_a=false, +R"(Orient each component (identified by C) of a mesh (V,F) using ambient +occlusion such that the front side is less occluded than back side, as +described in "A Simple Method for Correcting Facet Orientations in +Polygon Meshes Based on Ray Casting" [Takayama et al. 2014]. + +@param[in] V #V by 3 list of vertex positions +@param[in] F #F by 3 list of triangle indices +@param[in] rays_total Total number of rays that will be shot +@param[in] rays_minimum Minimum number of rays that each patch should receive +@param[in] facet_wise Decision made for each face independently, no use of patches + (i.e., each face is treated as a patch) +@param[in] use_parity Use parity mode +@param[in] is_verbose Verbose output to cout +@param[out] I #F list of whether face has been flipped +@param[out] C #F list of patch ID (output of bfs_orient > manifold patches) +)"); +} diff --git a/src/embree/shape_diameter_function.cpp b/src/embree/shape_diameter_function.cpp new file mode 100644 index 00000000..5ce04453 --- /dev/null +++ b/src/embree/shape_diameter_function.cpp @@ -0,0 +1,72 @@ +#include "default_types.h" +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto shape_diameter_function( + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &P, + const nb::DRef &N, + const int num_samples) + { + Eigen::VectorXN S; + igl::embree::shape_diameter_function(V,F,P,N,num_samples,S); + return S; + } + auto shape_diameter_function_ei( + const igl::embree::EmbreeIntersector & ei, + const nb::DRef &P, + const nb::DRef &N, + const int num_samples) + { + Eigen::VectorXN S; + igl::embree::shape_diameter_function(ei,P,N,num_samples,S); + return S; + } +} + +// Bind the wrapper to the Python module +void bind_shape_diameter_function(nb::module_ &m) +{ + m.def( + "shape_diameter_function", + &pyigl::shape_diameter_function, + "V"_a, + "F"_a, + "P"_a, + "N"_a, + "num_samples"_a, +R"(Compute shape diameter function per given point + +@param[in] V #V by 3 list of mesh vertex positiosn +@param[in] F #F by 3 list of mesh triangle indices into rows of V +@param[in] P #P by 3 list of origin points +@param[in] N #P by 3 list of origin normals +@param[out] S #P list of ambient occlusion values between 1 (fully occluded) and + 0 (not occluded) +)"); + m.def( + "shape_diameter_function", + &pyigl::shape_diameter_function_ei, + "ei"_a, + "P"_a, + "N"_a, + "num_samples"_a, +R"(Compute shape diameter function per given point + +@param[in] ei EmbreeIntersector containing (V,F) +@param[in] P #P by 3 list of origin points +@param[in] N #P by 3 list of origin normals +@param[out] S #P list of ambient occlusion values between 1 (fully occluded) and + 0 (not occluded) +)"); +} + diff --git a/src/euler_characteristic.cpp b/src/euler_characteristic.cpp deleted file mode 100644 index eb4aa117..00000000 --- a/src/euler_characteristic.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example - -#include -#include -#include -#include - -const char* ds_euler_characteristic = R"igl_Qu8mg5v7( - -Computes the Euler characteristic of a given mesh (V,F) - -Parameters ----------- -F #F by dim list of mesh faces (must be triangles) - -Returns -------- -Returns An int containing the Euler characteristic - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(euler_characteristic) -npe_doc(ds_euler_characteristic) - -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_tri_mesh_faces(f); - return igl::euler_characteristic(f); - -npe_end_code() - - - diff --git a/src/exact_geodesic.cpp b/src/exact_geodesic.cpp index d210c027..9c9ab026 100644 --- a/src/exact_geodesic.cpp +++ b/src/exact_geodesic.cpp @@ -1,64 +1,50 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include +#include +#include #include - -const char* ds_exact_geodesic = R"igl_Qu8mg5v7( -Exact geodesic algorithm for the calculation of geodesics on a triangular mesh. - -Parameters ----------- -v : #v by 3 array of 3D vertex positions -f : #f by 3 array of mesh faces -vs : #vs by 1 array specifying indices of source vertices -fs : #fs by 1 array specifying indices of source faces -vt : #vt by 1 array specifying indices of target vertices -ft : #ft by 1 array specifying indices of target faces - -Returns -------- -d : #vt+#ft by 1 array of geodesic distances of each target w.r.t. the nearest one in the source set - -See also --------- - - -Notes ------ -Specifying a face as target/source means its center. -Implementation from https:code.google.com/archive/p/geodesic/ with the algorithm first described by Mitchell, Mount and Papadimitriou in 1987. - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(exact_geodesic) -npe_doc(ds_exact_geodesic) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(vs, npe_matches(f)) //TODO somehow the matching is not working in this version, maybe a bug in numpyeigen? Therefore we hand in empty arrays for now -npe_arg(vt, npe_matches(f)) -npe_default_arg(fs, npe_matches(f), pybind11::array()) -npe_default_arg(ft, npe_matches(f), pybind11::array()) -npe_begin_code() - - assert_valid_3d_tri_mesh(v, f); - //assert_cols_equals(vs, 1, "vs"); - //assert_cols_equals(fs, 1, "fs"); - //assert_cols_equals(vt, 1, "vt"); - //assert_cols_equals(ft, 1, "ft"); - Eigen::Matrix d; - igl::exact_geodesic(v, f, vs, fs, vt, ft, d); - return npe::move(d); - -npe_end_code() - - +#include "default_types.h" + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for the exact_geodesic function + Eigen::VectorXN exact_geodesic( + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &VS, + const nb::DRef &FS, + const nb::DRef &VT, + const nb::DRef &FT) + { + Eigen::VectorXN D; + igl::exact_geodesic(V, F, VS, FS, VT, FT, D); + return D; + } +} + +// Bind the wrapper to the Python module +void bind_exact_geodesic(nb::module_ &m) +{ + m.def( + "exact_geodesic", + &pyigl::exact_geodesic, + "V"_a, + "F"_a, + "VS"_a=Eigen::VectorXI(), + "FS"_a=Eigen::VectorXI(), + "VT"_a=Eigen::VectorXI(), + "FT"_a=Eigen::VectorXI(), + R"(Exact geodesic algorithm for computing distances on a triangular mesh. + +@param[in] V #V by 3 matrix of vertex positions +@param[in] F #F by 3 matrix of face indices +@param[in] VS #VS by 1 vector of source vertex indices +@param[in] FS #FS by 1 vector of source face indices +@param[in] VT #VT by 1 vector of target vertex indices +@param[in] FT #FT by 1 vector of target face indices +@param[out] D #VT+#FT vector of geodesic distances from each target to the nearest source +)" + ); +} diff --git a/src/exterior_edges.cpp b/src/exterior_edges.cpp deleted file mode 100644 index fa4bc463..00000000 --- a/src/exterior_edges.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// need assertion for a.rows = b.rows-1 -// __copy -#include -#include -#include - - - - - -#include - -const char* ds_exterior_edges = R"igl_Qu8mg5v7( - -EXTERIOR_EDGES Determines boundary "edges" and also edges with an - odd number of occurrences where seeing edge (i,j) counts as +1 and seeing - the opposite edge (j,i) counts as -1 - -Parameters ----------- -F #F by simplex_size list of "faces" - -Returns -------- -E #E by simplex_size-1 list of exterior edges - -See also --------- - - -Notes ------ -None - -Examples --------- - - - -)igl_Qu8mg5v7"; - -npe_function(exterior_edges) -npe_doc(ds_exterior_edges) - -npe_arg(f, dense_int32, dense_int64) - -npe_begin_code() - assert_valid_tet_or_tri_mesh_faces(f); - - Eigen::MatrixXi e_copy; - Eigen::MatrixXi f_copy = f.template cast(); - igl::exterior_edges(f_copy, e_copy); - EigenDenseLike e = e_copy.template cast(); - return npe::move(e); - -npe_end_code() - diff --git a/src/extract_manifold_patches.cpp b/src/extract_manifold_patches.cpp deleted file mode 100644 index c2384e62..00000000 --- a/src/extract_manifold_patches.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_extract_manifold_patches = R"igl_Qu8mg5v7( -Extract a set of maximal patches from a given mesh. - A maximal patch is a subset of the input faces that are connected via - manifold edges; a patch is as large as possible. - -Parameters ----------- - F #F by 3 list representing triangles. - -Returns -------- -number of manifold patches. -P #F list of patch indices. - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(extract_manifold_patches) -npe_doc(ds_extract_manifold_patches) - -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_tri_mesh_faces(f); - EigenDenseLike p; - int n_patches = igl::extract_manifold_patches(f, p); - return std::make_tuple(n_patches, npe::move(p)); - -npe_end_code() diff --git a/src/extract_non_manifold_edge_curves.cpp b/src/extract_non_manifold_edge_curves.cpp deleted file mode 100644 index c6ec869f..00000000 --- a/src/extract_non_manifold_edge_curves.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - - - -#include - -const char *ds_extract_non_manifold_edge_curves = R"igl_Qu8mg5v7( - -Extract non-manifold curves from a given mesh. - A non-manifold curves are a set of connected non-manifold edges that - does not touch other non-manifold edges except at the end points. - They are also maximal in the sense that they cannot be expanded by - including more edges. - -Assumes the input mesh have all self-intersection resolved. See ``igl::cgal::remesh_self_intersection`` for more details. - -Parameters ----------- -F #F by 3 list representing triangles. -uE2E #uE list of lists of indices into E of coexisting edges. - -Returns -------- - -curves An array of arries of unique edge indices. - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(extract_non_manifold_edge_curves) -npe_doc(ds_extract_non_manifold_edge_curves) - -npe_arg(f, dense_int32, dense_int64) -npe_arg(u_e2_e, std::vector >) - - -npe_begin_code() - assert_valid_tri_mesh_faces(f); - - Eigen::Matrix emap; - - - std::vector > curves; - igl::extract_non_manifold_edge_curves(f, emap, u_e2_e, curves); - return curves; - -npe_end_code() - - diff --git a/src/face_areas.cpp b/src/face_areas.cpp new file mode 100644 index 00000000..c39b4ad0 --- /dev/null +++ b/src/face_areas.cpp @@ -0,0 +1,32 @@ +#include "default_types.h" +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto face_areas( + const nb::DRef& V, + const nb::DRef& T) + { + Eigen::MatrixXN A; + igl::face_areas(V, T, A); + return A; + } +} + +void bind_face_areas(nb::module_ &m) +{ + m.def("face_areas", &pyigl::face_areas, + "V"_a, + "T"_a, + R"(Constructs a list of face areas of faces opposite each index in a tet list +@param[in] V #V by 3 list of mesh vertex positions +@param[in] T #T by 3 list of tet mesh indices into V +@param[out] A #T by 4 list of face areas corresponding to faces opposite vertices + 0,1,2,3)"); +} diff --git a/src/face_occurrences.cpp b/src/face_occurrences.cpp deleted file mode 100644 index 6f165e65..00000000 --- a/src/face_occurrences.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - -#include - -const char *ds_face_occurrences = R"igl_Qu8mg5v7( - Count the occruances of each face (row) in a list of face indices (irrespecitive of order) -Parameters ----------- - F #F by simplex-size - -Returns -------- - C #F list of counts - -See also --------- - - -Notes ------ - Known bug: triangles/tets only (where ignoring order still gives simplex) - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(face_occurrences) -npe_doc(ds_face_occurrences) - -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_tet_or_tri_mesh_faces(f); - - EigenDenseLike c; - igl::face_occurrences(f, c); - return npe::move(c); - -npe_end_code() - - diff --git a/src/faces_first.cpp b/src/faces_first.cpp deleted file mode 100644 index 6d46c416..00000000 --- a/src/faces_first.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - -#include - -const char *ds_faces_first = R"igl_Qu8mg5v7( - - FACES_FIRST Reorder vertices so that vertices in face list come before - vertices that don't appear in the face list. This is especially useful if - the face list contains only surface faces and you want surface vertices - listed before internal vertices - [RV,RF,IM] = faces_first(V,T); - -Parameters ----------- - V # vertices by 3 vertex positions - F # faces by 3 list of face indices - -Returns -------- - RV # vertices by 3 vertex positions, order such that if the jth vertex is - some face in F, and the kth vertex is not then j comes before k - RF # faces by 3 list of face indices, reindexed to use RV - IM #V by 1 list of indices such that: RF = IM(F) and RT = IM(T) - and RV(IM,:) = V - -See also --------- - - -Notes ------ -None - -Examples --------- - Tet mesh in (V,T,F) - faces_first(V,F,IM); - T = T.unaryExpr(bind1st(mem_fun( static_cast(&VectorXi::operator())), - &IM)).eval(); -)igl_Qu8mg5v7"; - -npe_function(faces_first) -npe_doc(ds_faces_first) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - - EigenDenseLike v_copy = v; - EigenDenseLike f_copy = f; - - EigenDenseLike rv; - EigenDenseLike rf; - Eigen::Matrix im; - igl::faces_first(v_copy, f_copy, rv, rf, im); - - EigenDenseLike im_res = im; - return std::make_tuple(npe::move(rv), npe::move(rf), npe::move(im_res)); -npe_end_code() diff --git a/src/facet_adjacency_matrix.cpp b/src/facet_adjacency_matrix.cpp new file mode 100644 index 00000000..25cc8075 --- /dev/null +++ b/src/facet_adjacency_matrix.cpp @@ -0,0 +1,34 @@ +#include "default_types.h" +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for facet_adjacency_matrix with simplicial mesh (F) + auto facet_adjacency_matrix(const nb::DRef &F) + { + Eigen::SparseMatrixI A; + igl::facet_adjacency_matrix(F, A); + return A; + } + +} + +// Bind the wrapper to the Python module +void bind_facet_adjacency_matrix(nb::module_ &m) +{ + m.def( + "facet_adjacency_matrix", + &pyigl::facet_adjacency_matrix, + "F"_a, +R"(Construct a #F×#F adjacency matrix with A(i,j)>0 indicating that faces i and j +share an edge. + +@param[in] F #F by 3 list of facets +@param[out] A #F by #F adjacency matrix)"); +} diff --git a/src/facet_components.cpp b/src/facet_components.cpp index b5384b48..4409ad66 100644 --- a/src/facet_components.cpp +++ b/src/facet_components.cpp @@ -1,45 +1,36 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Nico -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_facet_components = R"igl_Qu8mg5v7( -Compute connected components of facets based on edge-edge adjacency, - -Parameters ----------- -f : #f x 3 array of triangle indices - -Returns -------- -An array, c, with shape (#f,), of component ids - -See also --------- -vertex_components -vertex_components_from_adjacency_matrix - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(facet_components) -npe_doc(ds_facet_components) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - assert_valid_tri_mesh_faces(f); - EigenDenseLike c; - igl::facet_components(f, c); - return npe::move(c); -npe_end_code() +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for facet_components with simplicial mesh (F) + auto facet_components(const nb::DRef &F) + { + Eigen::VectorXI C; + const int nc = igl::facet_components(F, C); + return std::make_tuple(nc, C); + } + +} + +// Bind the wrapper to the Python module +void bind_facet_components(nb::module_ &m) +{ + m.def( + "facet_components", + &pyigl::facet_components, + "F"_a, + R"(Compute connected components of facets based on edge-edge adjacency. + +For connected components on vertices see igl::vertex_components + +@param[in] F #F by 3 list of triangle indices +@param[out] C #F list of connected component ids +@return number of connected components)"); +} diff --git a/src/false_barycentric_subdivision.cpp b/src/false_barycentric_subdivision.cpp index 42c68a81..d236f17c 100644 --- a/src/false_barycentric_subdivision.cpp +++ b/src/false_barycentric_subdivision.cpp @@ -1,67 +1,35 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char *ds_false_barycentric_subdivision = R"igl_Qu8mg5v7( - - Refine the mesh by adding the barycenter of each face - -Parameters ----------- - V #V by 3 coordinates of the vertices - F #F by 3 list of mesh faces (must be triangles) - -Returns -------- - VD #V + #F by 3 coordinate of the vertices of the dual mesh - The added vertices are added at the end of VD (should not be - same references as (V,F) - FD #F*3 by 3 faces of the dual mesh - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(false_barycentric_subdivision) -npe_doc(ds_false_barycentric_subdivision) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - //TODO: only 2 templates in igl... - - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::MatrixXi f_copy = f.template cast(); - - Eigen::MatrixXd vd_copy; - Eigen::MatrixXi fd_copy; - - igl::false_barycentric_subdivision(v_copy, f_copy, vd_copy, fd_copy); - - EigenDenseLike vd = vd_copy.template cast(); - EigenDenseLike fd = fd_copy.template cast(); - return std::make_tuple(npe::move(vd), npe::move(fd)); - -npe_end_code() - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto false_barycentric_subdivision( + const nb::DRef& V, + const nb::DRef& F) + { + Eigen::MatrixXN VD; + Eigen::MatrixXI FD; + igl::false_barycentric_subdivision(V, F, VD, FD); + return std::make_tuple(VD, FD); + } +} + +void bind_false_barycentric_subdivision(nb::module_ &m) +{ + m.def("false_barycentric_subdivision", &pyigl::false_barycentric_subdivision, + "V"_a, + "F"_a, + R"(Refine the mesh by adding the barycenter of each face +@param[in] V #V by 3 coordinates of the vertices +@param[in] F #F by 3 list of mesh faces (must be triangles) +@param[out] VD #V + #F by 3 coordinate of the vertices of the dual mesh + The added vertices are added at the end of VD (should not be + same references as (V,F) +@param[out] FD #F*3 by 3 faces of the dual mesh)"); +} diff --git a/src/fast_winding_number.cpp b/src/fast_winding_number.cpp index 05987c09..421bce2a 100644 --- a/src/fast_winding_number.cpp +++ b/src/fast_winding_number.cpp @@ -1,118 +1,38 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include +#include #include - -const char *ds_fast_winding_number_for_points = R"igl_Qu8mg5v7xxx1( -Evaluate the fast winding number for point data, with default expansion -order and beta (both are set to 2). - -This function performes the precomputation and evaluation all in one. -If you need to acess the precomuptation for repeated evaluations, use the -two functions designed for exposed precomputation (described above). - -Parameters ----------- -P #P by 3 list of point locations -N #P by 3 list of point normals -A #P by 1 list of point areas -Q #Q by 3 list of query points for the winding number - -Returns -------- -WN #Q by 1 list of windinng number values at each query point - -See also --------- - - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7xxx1"; - -npe_function(fast_winding_number_for_points) -npe_doc(ds_fast_winding_number_for_points) -npe_arg(p, dense_float, dense_double) -npe_arg(n, npe_matches(p)) -npe_arg(a, npe_matches(p)) - -npe_arg(q, npe_matches(p)) - -npe_begin_code() - assert_nonzero_rows(p, "p"); - assert_cols_equals(p, 3, "p"); - - assert_cols_equals(n, 3, "p"); - assert_rows_match(p, n, "p", "n"); - - assert_size_equals(a, p.rows(), "a"); - - assert_cols_equals(q, 3, "q"); - assert_nonzero_rows(q, "q"); - - EigenDenseLike a_copy = a; - - EigenDenseLike wn; - igl::fast_winding_number(p, n, a_copy, q, wn); - return npe::move(wn); - -npe_end_code() - - - - -const char *ds_fast_winding_number_for_meshes = R"igl_Qu8mg5v7xxx2( -Compute approximate winding number of a triangle soup mesh according to -"Fast Winding Numbers for Soups and Clouds" [Barill et al. 2018]. - -Parameters ----------- -V #V by 3 list of mesh vertex positions -F #F by 3 list of triangle mesh indices into rows of V -Q #Q by 3 list of query points for the winding number - -Returns -------- -WN #Q by 1 list of windinng number values at each query point - -See also --------- - - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7xxx2"; - -npe_function(fast_winding_number_for_meshes) -npe_doc(ds_fast_winding_number_for_meshes) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - -npe_arg(q, npe_matches(v)) - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - assert_cols_equals(q, 3, "q"); - assert_nonzero_rows(q, "q"); - - EigenDenseLike wn; - igl::fast_winding_number(v, f, q, wn); - return npe::move(wn); - -npe_end_code() \ No newline at end of file +#include "default_types.h" + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto fast_winding_number( + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &Q) + { + Eigen::VectorXN W; // Winding number values for each query point + igl::fast_winding_number(V, F, Q, W); + return W; + } +} + +// Bind the wrapper to the Python module +void bind_fast_winding_number(nb::module_ &m) +{ + m.def( + "fast_winding_number", + &pyigl::fast_winding_number, + "V"_a, + "F"_a, + "Q"_a, +R"(Compute approximate winding number for each query point based on a triangle soup mesh. + +@param[in] V #V by 3 matrix of mesh vertex positions +@param[in] F #F by 3 matrix of triangle indices +@param[in] Q #Q by 3 matrix of query positions +@return W #Q vector of winding number values for each query point)" + ); +} diff --git a/src/find_cross_field_singularities.cpp b/src/find_cross_field_singularities.cpp deleted file mode 100644 index 338f1546..00000000 --- a/src/find_cross_field_singularities.cpp +++ /dev/null @@ -1,123 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_find_cross_field_singularities = R"igl_Qu8mg5v7( -Computes singularities of a cross field, assumed combed - -Parameters ----------- -V #V by 3 eigen Matrix of mesh vertex 3D positions -F #F by 3 eigen Matrix of face indices -Handle_MMatch #F by 3 eigen Matrix containing the integer missmatch of the cross field - across all face edges - -Returns -------- -isSingularity #V by 1 boolean eigen Vector indicating the presence of a singularity on a vertex -singularityIndex #V by 1 integer eigen Vector containing the singularity indices - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(find_cross_field_singularities) -npe_doc(ds_find_cross_field_singularities) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(handle_m_match, npe_matches(f)) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - assert_rows_match(f, handle_m_match, "F", "Handle_MMatch"); - assert_cols_equals(handle_m_match, 3, "Handle_MMatch"); - - EigenDenseLike is_singularity; - EigenDenseLike singularity_index; - igl::find_cross_field_singularities(v, f, handle_m_match, is_singularity, singularity_index); - return std::make_tuple(npe::move(is_singularity), npe::move(singularity_index)); - -npe_end_code() - - - -const char *ds_find_cross_field_singularities_from_field = R"igl_Qu8mg5v7( - -Wrapper that calculates the missmatch if it is not provided. -Note that the field in PD1 and PD2 MUST BE combed (see igl::comb_cross_field). - -Parameters ----------- - -V #V by 3 eigen Matrix of mesh vertex 3D positions -F #F by 3 eigen Matrix of face (quad) indices -PD1 #F by 3 eigen Matrix of the first per face cross field vector -PD2 #F by 3 eigen Matrix of the second per face cross field vector - -Returns -------- -isSingularity #V by 1 boolean eigen Vector indicating the presence of a singularity on a vertex -singularityIndex #V by 1 integer eigen Vector containing the singularity indices - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(find_cross_field_singularities_from_field) -npe_doc(ds_find_cross_field_singularities_from_field) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(pd1, npe_matches(v)) -npe_arg(pd2, npe_matches(v)) -npe_default_arg(is_combed, bool, false) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - assert_rows_match(f, pd1, "F", "PD1"); - assert_rows_match(f, pd2, "F", "PD2"); - assert_cols_equals(pd1, 3, "PD1"); - assert_cols_equals(pd2, 3, "PD2"); - - EigenDenseLike v_copy = v; - EigenDenseLike f_copy = f; - EigenDenseLike pd1_copy = pd1; - EigenDenseLike pd2_copy = pd2; - - EigenDenseLike is_singularity; - EigenDenseLike singularity_index; - igl::find_cross_field_singularities(v_copy, f_copy, pd1_copy, pd2_copy, is_singularity, singularity_index, is_combed); - return std::make_tuple(npe::move(is_singularity), npe::move(singularity_index)); - -npe_end_code() - - diff --git a/src/fit_cubic_bezier.cpp b/src/fit_cubic_bezier.cpp deleted file mode 100644 index a6e4ffe9..00000000 --- a/src/fit_cubic_bezier.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Alec Jacobson -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -// On macos with cp36, termios.h is getting included for some reason and it -// defines B0 -#ifdef B0 -# undef B0 -#endif -#include -#include -#include - - -const char *ds_fit_cubic_bezier = R"igl_Qu8mg5v7( -Fit a cubic bezier spline (G1 continuous) to an ordered list of input -points in any dimension, according to "An algorithm for automatically -fitting digitized curves" [Schneider 1990]. - -Parameters ----------- - d #d by dim list of points along a curve to be fit with a cubic bezier - spline (should probably be roughly uniformly spaced). If d(0)==d(end), - then will treat as a closed curve. - error maximum squared distance allowed -Returns -------- - cubics #cubics list of 4 by dim lists of cubic control points - -)igl_Qu8mg5v7"; - -npe_function(fit_cubic_bezier) -npe_doc(ds_fit_cubic_bezier) - -npe_arg(d, dense_float, dense_double) -npe_arg(error, double) - -npe_begin_code() - // igl::fit_cubic_bezier is hard-coded to double, so for now copy. - Eigen::MatrixXd d_cpy = d.template cast(); - std::vector c_cpy; - igl::fit_cubic_bezier(d_cpy,error,c_cpy); - std::vector> c(c_cpy.size()); - std::transform (c_cpy.begin(), c_cpy.end(), c.begin(), - [](const Eigen::MatrixXd & ci){ return ci.cast();}); - // numpyeigen's pybind11 fork `numpy_hacks_stable` is printing "Encapsulate move!" - // https://github.com/fwilliams/numpyeigen/issues/58 - return pybind11::detail::type_caster::cast(c, pybind11::return_value_policy::move, pybind11::none()); - -npe_end_code() - diff --git a/src/fit_plane.cpp b/src/fit_plane.cpp deleted file mode 100644 index a8910fc1..00000000 --- a/src/fit_plane.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example - -#include -#include -#include - - - - - - -#include - -const char* ds_fit_plane = R"igl_Qu8mg5v7( - -This function fits a plane to a point cloud. - -Parameters ----------- -V #Vx3 matrix. The 3D point cloud, one row for each vertex. - -Returns -------- -N 1x3 Vector. The normal of the fitted plane. -C 1x3 Vector. A point that lies in the fitted plane. - -See also --------- - -Notes ------ -From http:missingbytes.blogspot.com/2012/06/fitting-plane-to-point-cloud.html - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(fit_plane) -npe_doc(ds_fit_plane) - -npe_arg(v, dense_float, dense_double) - - -npe_begin_code() - - assert_nonzero_rows(v, "v"); - assert_cols_equals(v, 3, "v"); - // TODO: remove __copy - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::RowVector3d n; - Eigen::RowVector3d c; - igl::fit_plane(v_copy, n, c); - Eigen::Matrix n_row_major = n; - Eigen::Matrix c_row_major = c; - return std::make_tuple(npe::move(n_row_major), npe::move(c_row_major)); - -npe_end_code() - - diff --git a/src/flip_avoiding_line_search.cpp b/src/flip_avoiding_line_search.cpp deleted file mode 100644 index d6204f85..00000000 --- a/src/flip_avoiding_line_search.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example - -#include -#include -#include -#include -#include - - - - - - -#include - -const char* ds_flip_avoiding_line_search = R"igl_Qu8mg5v7( - -A bisection line search for a mesh based energy that avoids triangle flips as suggested in -"Bijective Parameterization with Free Boundaries" (Smith J. and Schaefer S., 2015). - -The user specifies an initial vertices position (that has no flips) and target one (that my have flipped triangles). -This method first computes the largest step in direction of the destination vertices that does not incur flips, and then minimizes a given energy using this maximal step and a bisection linesearch (see igl::line_search). - -Supports both triangle and tet meshes. - -Parameters ----------- -F #F by 3/4 list of mesh faces or tets -cur_v #V by dim list of variables -dst_v #V by dim list of target vertices. This mesh may have flipped triangles -energy A function to compute the mesh-based energy (return an energy that is bigger than 0) -cur_energy(OPTIONAL) The energy at the given point. Helps save redundant c omputations. This is optional. If not specified, the function will compute it. - - -Returns -------- -cur_v #V by dim list of variables at the new location -Returns the energy at the new point - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(flip_avoiding_line_search) -npe_doc(ds_flip_avoiding_line_search) - -npe_arg(f, dense_int32, dense_int64) -npe_arg(cur_v, dense_float, dense_double) -npe_arg(dst_v, npe_matches(cur_v)) -npe_arg(energy, std::function) -npe_arg(cur_energy, double) - -npe_begin_code() - assert_shapes_match(cur_v, dst_v, "cur_v", "dst_v"); - assert_valid_tet_or_tri_mesh_faces(f); - if(f.cols() == 3) - { - assert_cols_equals(cur_v, 2, "cur_v"); - assert_cols_equals(dst_v, 2, "dst_v"); - } - else - { - assert_cols_equals(cur_v, 3, "cur_v"); - assert_cols_equals(dst_v, 3, "dst_v"); - } - - //TODO: remove __copy - Eigen::MatrixXi f_copy = f.template cast(); - Eigen::MatrixXd cur_v_copy = cur_v.template cast(); - Eigen::MatrixXd dst_v_copy = dst_v.template cast(); - double ret_energy = igl::flip_avoiding_line_search(f_copy, cur_v_copy, dst_v_copy, energy, cur_energy); - EigenDenseFloat cur_v_row_major = cur_v_copy; - return std::make_tuple(ret_energy, npe::move(cur_v_row_major)); - -npe_end_code() diff --git a/src/flip_edge.cpp b/src/flip_edge.cpp deleted file mode 100644 index 5cd4d2ae..00000000 --- a/src/flip_edge.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Soboleva Natalia -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - -#include - -#include - - -const char* ds_flip_edge = R"igl_Qu8mg5v7( - -Flip an edge in a triangle mesh. The edge specified by uei must have -exactly two adjacent faces. Violation will result in exception. -Another warning: edge flipping could convert manifold mesh into -non-manifold. - -Parameters ----------- -F #F by 3 list of triangles. -E #F*3 by 2 list of all of directed edges -uE #uE by 2 list of unique undirected edges -EMAP #F*3 list of indices into uE, mapping each directed edge to unique - undirected edge -uE2E #uE list of lists of indices into E of coexisting edges -ue #index into uE the edge to be flipped. - -Returns -------- - -Updated F, E, uE, EMAP and uE2E. - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(flip_edge) -npe_doc(ds_flip_edge) - -npe_arg(f, dense_int32, dense_int64) -npe_arg(e, npe_matches(f)) -npe_arg(u_e, npe_matches(f)) -npe_arg(emap, npe_matches(f)) -npe_arg(u_e2_e, std::vector >) -npe_arg(uei, size_t) - -npe_begin_code() - - assert_valid_tri_mesh_faces(f); - EigenDenseLike f_copy = f; - EigenDenseLike e_copy = e; - EigenDenseLike u_e_copy = u_e; - EigenDenseLike emap_copy = emap; - - std::vector > u_e2_e_tmp; - u_e2_e_tmp.reserve(u_e2_e.size()); - - std::transform(begin(u_e2_e), end(u_e2_e), std::back_inserter(u_e2_e_tmp), - [](const std::vector& vi) { - return std::vector(begin(vi), end(vi)); - }); - - igl::flip_edge(f_copy, e_copy, u_e_copy, emap_copy, u_e2_e_tmp, uei); - - std::vector > u_e2_e_copy; - u_e2_e_copy.reserve(u_e2_e_tmp.size()); - std::transform(begin(u_e2_e_tmp), end(u_e2_e_tmp), std::back_inserter(u_e2_e_copy), - [](const std::vector& vi) { - return std::vector(begin(vi), end(vi)); - }); - return std::make_tuple(npe::move(f_copy), npe::move(e_copy), npe::move(u_e_copy), npe::move(emap_copy), u_e2_e_copy); - -npe_end_code() diff --git a/src/flipped_triangles.cpp b/src/flipped_triangles.cpp deleted file mode 100644 index 00d4d9f3..00000000 --- a/src/flipped_triangles.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_flipped_triangles = R"igl_Qu8mg5v7( - Finds the ids of the flipped triangles of the mesh V,F in the UV mapping uv - -Parameters ----------- - V #V by 2 list of mesh vertex positions - F #F by 3 list of mesh faces (must be triangles) - -Returns -------- - X #flipped list of containing the indices into F of the flipped triangles - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(flipped_triangles) -npe_doc(ds_flipped_triangles) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_2d_tri_mesh(v, f); - EigenDenseLike x; - igl::flipped_triangles(v, f, x); - return npe::move(x); - -npe_end_code() diff --git a/src/forward_kinematics.cpp b/src/forward_kinematics.cpp deleted file mode 100644 index 6b9c449b..00000000 --- a/src/forward_kinematics.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - -#include - -const char *ds_forward_kinematics = R"igl_Qu8mg5v7( -Given a skeleton and a set of relative bone rotations compute absolute rigid transformations for each bone. -Parameters ----------- -C #C by dim list of joint positions -BE #BE by 2 list of bone edge indices -P #BE list of parent indices into BE -dQ #BE list of relative rotations -dT #BE list of relative translations - -Returns -------- -vQ #BE list of absolute rotations -vT #BE list of absolute translations - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(forward_kinematics) -npe_doc(ds_forward_kinematics) - -npe_arg(c, dense_float, dense_double) -npe_arg(be, dense_int32, dense_int64) -npe_arg(p, npe_matches(be)) -npe_arg(d_q, npe_matches(c)) -npe_arg(d_t, dense_float, dense_double) -//TODO missing none for d_t - - -npe_begin_code() - assert_cols_equals(c, 3, "C"); - assert_cols_equals(be, 2, "BE"); - - assert_rows_match(d_q, be, "dQ", "BE"); - assert_cols_equals(d_q, 4, "dQ"); - if(d_t.size() > 0){ - assert_cols_equals(d_t, 3, "dT"); - assert_rows_match(d_q, d_t, "dQ", "dT"); - } - - Eigen::MatrixXd c_copy = c.template cast(); - Eigen::MatrixXi be_copy = be.template cast(); - Eigen::VectorXi p_copy = p.template cast(); - - std::vector> d_q_copy(d_q.rows()); - for (size_t i = 0; i < d_q_copy.size(); ++i) - { - d_q_copy[i].x() = d_q(i, 0); - d_q_copy[i].y() = d_q(i, 1); - d_q_copy[i].z() = d_q(i, 2); - d_q_copy[i].w() = d_q(i, 3); - } - - std::vector d_t_copy(d_t.rows()); - for (size_t i = 0; i < d_t_copy.size(); ++i) - d_t_copy[i] = d_t.row(i).template cast(); - - std::vector > v_q; - std::vector v_t; - if (d_t_copy.empty()) - igl::forward_kinematics(c_copy, be_copy, p_copy, d_q_copy, v_q, v_t); - else - igl::forward_kinematics(c_copy, be_copy, p_copy, d_q_copy, d_t_copy, v_q, v_t); - - EigenDenseLike v_q_out(v_q.size(), 4); - EigenDenseLike v_t_out(v_t.size(), 3); - - for (size_t i = 0; i < v_q.size(); ++i) - { - v_q_out(i, 0) = v_q[i].x(); - v_q_out(i, 1) = v_q[i].y(); - v_q_out(i, 2) = v_q[i].z(); - v_q_out(i, 3) = v_q[i].w(); - } - - for (size_t i = 0; i < v_t.size(); ++i) - v_t_out.row(i) = v_t[i].template cast(); - - return std::make_tuple(npe::move(v_q_out), npe::move(v_t_out)); - - npe_end_code() \ No newline at end of file diff --git a/src/gaussian_curvature.cpp b/src/gaussian_curvature.cpp index 12d567eb..6aa3cdd1 100644 --- a/src/gaussian_curvature.cpp +++ b/src/gaussian_curvature.cpp @@ -1,53 +1,35 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include +#include #include - -const char* ds_gaussian_curvature = R"igl_Qu8mg5v7( -Compute discrete local integral gaussian curvature (angle deficit, without -averaging by local area). - -Parameters ----------- -v : #v by 3 array of mesh vertex 3D positions -f : #f by 3 array of face (triangle) indices - -Returns -------- -k : #v by 1 array of discrete gaussian curvature values - -See also --------- -principal_curvature - -Notes ------ -None - -Examples --------- -# Mesh in (v, f) ->>> k = gaussian_curvature(v, f) -)igl_Qu8mg5v7"; - -npe_function(gaussian_curvature) -npe_doc(ds_gaussian_curvature) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - - assert_valid_3d_tri_mesh(v, f); - EigenDenseLike k; - igl::gaussian_curvature(v, f, k); - return npe::move(k); - -npe_end_code() - - +#include "default_types.h" + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto gaussian_curvature( + const nb::DRef &V, + const nb::DRef &F) + { + Eigen::VectorXN K; // Curvature values for each vertex + igl::gaussian_curvature(V, F, K); + return K; + } +} + +// Bind the wrapper to the Python module +void bind_gaussian_curvature(nb::module_ &m) +{ + m.def( + "gaussian_curvature", + &pyigl::gaussian_curvature, + "V"_a, + "F"_a, +R"(Compute discrete Gaussian curvature at each vertex of a 3D mesh. + +@param[in] V #V by 3 matrix of vertex positions +@param[in] F #F by 3 matrix of face indices +@return K #V vector of discrete Gaussian curvature values at each vertex)" + ); +} diff --git a/src/grad.cpp b/src/grad.cpp index 755049d0..a7dc9e53 100644 --- a/src/grad.cpp +++ b/src/grad.cpp @@ -1,60 +1,41 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_grad = R"igl_Qu8mg5v7( -Compute the numerical gradient operator. - -Parameters ----------- -v : #v by 3 list of mesh vertex positions -f : #f by 3 list of mesh face indices [or a #faces by 4 list of tetrahedral indices] -uniform : boolean (default false). Use a uniform mesh instead of the vertices v - -Returns -------- -g : #faces * dim by #v gradient operator - -See also --------- -cotmatrix, massmatrix - -Notes ------ -Gradient of a scalar function defined on piecewise linear elements (mesh) -is constant on each triangle [tetrahedron] i,j,k: -grad(Xijk) = (Xj-Xi) * (Vi - Vk)^R90 / 2A + (Xk-Xi) * (Vj - Vi)^R90 / 2A -where Xi is the scalar value at vertex i, Vi is the 3D position of vertex -i, and A is the area of triangle (i,j,k). ^R90 represent a rotation of -90 degrees. - -Examples --------- -# Mesh in (v, f) ->>> g = grad(v, f) -)igl_Qu8mg5v7"; - -npe_function(grad) -npe_doc(ds_grad) -npe_arg(v, dense_double, dense_float) -npe_arg(f, dense_int32, dense_int64) -npe_default_arg(uniform, bool, false) - -npe_begin_code() - - assert_valid_tet_or_tri_mesh(v, f); - EigenSparseLike g; - igl::grad(v, f, g, uniform); - return npe::move(g); - -npe_end_code() - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto grad( + const nb::DRef &V, + const nb::DRef &F, + bool uniform = false) + { + Eigen::SparseMatrixN G; + igl::grad(V, F, G, uniform); + return G; + } +} + +// Bind the wrapper to the Python module +void bind_grad(nb::module_ &m) +{ + m.def( + "grad", + &pyigl::grad, + "V"_a, + "F"_a, + "uniform"_a = false, + R"(Compute the gradient operator on a triangle mesh. + + @param[in] V #V by 3 list of mesh vertex positions + @param[in] F #F by 3 (or #F by 4 for tetrahedrons) list of mesh face indices + @param[out] G #F*dim by #V Gradient operator + @param[in] uniform boolean indicating whether to use a uniform mesh instead of the vertices V + @return Sparse gradient operator matrix G)" + ); +} diff --git a/src/grad_intrinsic.cpp b/src/grad_intrinsic.cpp deleted file mode 100644 index 0c9c181d..00000000 --- a/src/grad_intrinsic.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - - - -#include - -const char *ds_grad_intrinsic = R"igl_Qu8mg5v7( - -GRAD_INTRINSIC Construct an intrinsic gradient operator. - - -Parameters ----------- - -l #F by 3 list of edge lengths -F #F by 3 list of triangle indices into some vertex list V - -Returns -------- -G #F*2 by #V gradient matrix: G=[Gx;Gy] where x runs along the 23 edge and - y runs in the counter-clockwise 90° rotation. - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(grad_intrinsic) -npe_doc(ds_grad_intrinsic) - -npe_arg(l, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_rows_match(l, f, "l", "f"); - assert_cols_equals(l, 3, "l"); - assert_valid_tri_mesh_faces(f); - - EigenDense f_copy = f; - - Eigen::SparseMatrix g; - igl::grad_intrinsic(l, f_copy, g); - return npe::move(g); - -npe_end_code() - - diff --git a/src/grid.cpp b/src/grid.cpp new file mode 100644 index 00000000..2d5a0d1d --- /dev/null +++ b/src/grid.cpp @@ -0,0 +1,32 @@ +#include "default_types.h" +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for igl::grid + auto grid(const nb::DRef &res) + { + Eigen::MatrixXN GV; + igl::grid(res, GV); + return GV; + } +} + +// Bind the wrapper to the Python module +void bind_grid(nb::module_ &m) +{ + m.def( + "grid", + &pyigl::grid, + "res"_a, +R"(Construct vertices of a regular grid. + +@param[in] res Vector containing the number of vertices along each dimension +@return GV Matrix containing grid vertex positions suitable for input to igl::marching_cubes.)"); +} diff --git a/src/harmonic.cpp b/src/harmonic.cpp index 82e82588..80ae318d 100644 --- a/src/harmonic.cpp +++ b/src/harmonic.cpp @@ -1,258 +1,47 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_harmonic_w = R"igl_Qu8mg5v7( -Compute k-harmonic weight functions "coordinates". - -Parameters ----------- - V #V by dim vertex positions - F #F by simplex-size list of element indices - b #b boundary indices into V - bc #b by #W list of boundary values - k power of harmonic operation (1: harmonic, 2: biharmonic, etc) - -Returns -------- - W #V by #W list of weights - -See also --------- - - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(harmonic) -npe_doc(ds_harmonic_w) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(b, npe_matches(f)) -npe_arg(bc, npe_matches(v)) -npe_arg(k, int) - -npe_begin_code() - - assert_valid_tet_or_tri_mesh_23d(v, f); - assert_nonzero_rows(bc, "bc"); - assert_rows_match(b, bc, "b", "bc"); - EigenDenseLike w; - igl::harmonic(v, f, b, bc, k, w); - return npe::move(w); - -npe_end_code() -#include - - -const char* ds_harmonic_ul = R"igl_Qu8mg5v7( - -Compute harmonic map using uniform laplacian operator - -Parameters ----------- - F #F by simplex-size list of element indices - b #b boundary indices into V - bc #b by #W list of boundary values - k power of harmonic operation (1: harmonic, 2: biharmonic, etc) - -Returns -------- - W #V by #W list of weights - -See also --------- - - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(harmonic_uniform_laplacian) -npe_doc(ds_harmonic_ul) - -npe_arg(f, dense_int32, dense_int64) -npe_arg(b, npe_matches(f)) -npe_arg(bc, dense_float, dense_double) -npe_arg(k, int) - - -npe_begin_code() - - assert_valid_tet_or_tri_mesh_faces(f, "f"); - assert_nonzero_rows(bc, "bc"); - assert_rows_match(b, bc, "b", "bc"); - - EigenDenseLike w; - igl::harmonic(f, b, bc, k, w); - return npe::move(w); - -npe_end_code() - - - - - - -#include - -const char* ds_harmonic = R"igl_Qu8mg5v7( - -Compute a harmonic map using a given Laplacian and mass matrix - -Parameters ----------- - L #V by #V discrete (integrated) Laplacian - M #V by #V mass matrix - b #b boundary indices into V - bc #b by #W list of boundary values - k power of harmonic operation (1: harmonic, 2: biharmonic, etc) - -Returns -------- - W #V by #V list of weights - -See also --------- - - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(harmonic_from_laplacian_and_mass) -npe_doc(ds_harmonic) -//TODO: l and bc need to have same type, matching missing -npe_arg(l, sparse_float, sparse_double) -npe_arg(m, npe_matches(l)) -npe_arg(b, dense_int32, dense_int64) -npe_arg(bc, dense_float, dense_double) -// npe_arg(bc, npe_dense_like(l)) -npe_arg(k, int) - - -npe_begin_code() - - assert_shapes_match(l, m, "l", "m"); - assert_nonzero_rows(l, "l"); - assert_nonzero_rows(bc, "bc"); - assert_rows_match(b, bc, "b", "bc"); - - Eigen::Matrix bc_copy = bc.template cast(); - - Eigen::Matrix w; - igl::harmonic(l, m, b, bc_copy, k, w); - return npe::move(w); - -npe_end_code() -#include - -const char* ds_harmonic_int_lapl = R"igl_Qu8mg5v7( - - Build the discrete k-harmonic operator (computing integrated quantities). - That is, if the k-harmonic PDE is Q x = 0, then this minimizes x' Q x - - -Parameters ----------- - L #V by #V discrete (integrated) Laplacian - M #V by #V mass matrix - k power of harmonic operation (1: harmonic, 2: biharmonic, etc) - -Returns -------- - Q #V by #V discrete (integrated) k-Laplacian -See also --------- - - -Notes ------ -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(harmonic_integrated_from_laplacian_and_mass) -npe_doc(ds_harmonic_int_lapl) - -npe_arg(l, sparse_float, sparse_double) -npe_arg(m, npe_matches(l)) -npe_arg(k, int) - - -npe_begin_code() - assert_shapes_match(l, m, "l", "m"); - assert_nonzero_rows(l, "l"); - EigenSparseLike q; - igl::harmonic(l, m, k, q); - return npe::move(q); - -npe_end_code() -#include - -const char* ds_harmonic_int = R"igl_Qu8mg5v7( - -Parameters ----------- - V #V by dim vertex positions - F #F by simplex-size list of element indices - k power of harmonic operation (1: harmonic, 2: biharmonic, etc) - -Returns -------- - Q #V by #V discrete (integrated) k-Laplacian - -See also --------- - - -Notes ------ - - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(harmonic_integrated) -npe_doc(ds_harmonic_int) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(k, int) - - -npe_begin_code() - - assert_valid_tet_or_tri_mesh(v, f); - EigenSparseLike q; - igl::harmonic(v, f, k, q); - return npe::move(q); - -npe_end_code() - - +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for the harmonic function + auto harmonic( + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &b, + const nb::DRef &bc, + const int k) + { + Eigen::MatrixXN W; + if(!igl::harmonic(V, F, b, bc, k, W)) + { + throw std::runtime_error("Failed to compute harmonic map"); + } + return W; + } +} + +// Bind the wrapper to the Python module +void bind_harmonic(nb::module_ &m) +{ + m.def( + "harmonic", + &pyigl::harmonic, + "V"_a, + "F"_a, + "b"_a, + "bc"_a, + "k"_a, +R"(Compute k-harmonic weight functions "coordinates". + +@param[in] V #V by dim vertex positions +@param[in] F #F by simplex-size list of element indices +@param[in] b #b boundary indices into V +@param[in] bc #b by #W list of boundary values +@param[in] k power of harmonic operation (1: harmonic, 2: biharmonic, etc) +@return W #V by #W list of weights)"); +} diff --git a/src/hausdorff.cpp b/src/hausdorff.cpp deleted file mode 100644 index cf7e604d..00000000 --- a/src/hausdorff.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - - - -#include - -const char *ds_hausdorff = R"igl_Qu8mg5v7( - - HAUSDORFF compute the Hausdorff distance between mesh (VA,FA) and mesh - (VB,FB). This is the - - d(A,B) = max ( max min d(a,b) , max min d(b,a) ) - a∈A b∈B b∈B a∈A - -Parameters ----------- - VA #VA by 3 list of vertex positions - FA #FA by 3 list of face indices into VA - VB #VB by 3 list of vertex positions - FB #FB by 3 list of face indices into VB - -Returns -------- - d hausdorff distance - pair 2 by 3 list of "determiner points" so that pair(1,:) is from A - and pair(2,:) is from B - -See also --------- - - -Notes ------ - Known issue: This is only computing max(min(va,B),min(vb,A)). This is - better than max(min(va,Vb),min(vb,Va)). This (at least) is missing - "edge-edge" cases like the distance between the two different - triangulations of a non-planar quad in 3D. Even simpler, consider the - Hausdorff distance between the non-convex, block letter V polygon (with 7 - vertices) in 2D and its convex hull. The Hausdorff distance is defined by - the midpoint in the middle of the segment across the concavity and some - non-vertex point _on the edge_ of the V. - -Examples --------- -)igl_Qu8mg5v7"; - -npe_function(hausdorff) -npe_doc(ds_hausdorff) - -npe_arg(va, dense_float, dense_double) -npe_arg(fa, dense_int32, dense_int64) -npe_arg(vb, npe_matches(va)) -npe_arg(fb, npe_matches(fa)) - - -npe_begin_code() - assert_valid_3d_tri_mesh(va, fa, "va", "fa"); - assert_valid_3d_tri_mesh(vb, fb, "vb", "fb"); - - double d; - igl::hausdorff(va, fa, vb, fb, d); - return d; - -npe_end_code() - - - diff --git a/src/heat_geodesic.cpp b/src/heat_geodesic.cpp deleted file mode 100644 index bbffe719..00000000 --- a/src/heat_geodesic.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_heat_geodesic = R"igl_Qu8mg5v7xxx( -Compute fast approximate geodesic distances using precomputed data from a set of selected source vertices (gamma) - -Parameters ----------- -V #V by dim list of mesh vertex positions -F #F by 3 list of mesh face indices into V -t "heat" parameter (smaller --> more accurate, less stable) -gamma #gamma list of indices into V of source vertices - -Returns -------- -D #V list of distances to gamma - -See also --------- - - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7xxx"; - -npe_function(heat_geodesic) -npe_doc(ds_heat_geodesic) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(t, double) -npe_arg(gamma, npe_matches(f)) - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - - EigenDenseLike v_copy = v; - EigenDenseLike f_copy = f; - Eigen::Matrix gamma_copy = gamma; - - Eigen::Matrix d_copy; - igl::HeatGeodesicsData data; - igl::heat_geodesics_precompute(v_copy, f_copy, npe_Scalar_v(t), data); - igl::heat_geodesics_solve(data, gamma_copy, d_copy); - EigenDenseLike d = d_copy; - - return npe::move(d); - -npe_end_code() - - diff --git a/src/heat_geodesics.cpp b/src/heat_geodesics.cpp new file mode 100644 index 00000000..e7e276a7 --- /dev/null +++ b/src/heat_geodesics.cpp @@ -0,0 +1,89 @@ +#include "default_types.h" +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + void heat_geodesics_precompute_t( + const nb::DRef &V, + const nb::DRef &F, + const Numeric t, + igl::HeatGeodesicsData &data) + { + if(!igl::heat_geodesics_precompute(V,F,t,data)) + { + throw std::runtime_error("heat_geodesics: Precomputation failed."); + } + } + // Obnoxious way to have optional t + void heat_geodesics_precompute( + const nb::DRef &V, + const nb::DRef &F, + igl::HeatGeodesicsData &data) + { + if(!igl::heat_geodesics_precompute(V,F,data)) + { + throw std::runtime_error("heat_geodesics: Precomputation failed."); + } + } + + auto heat_geodesics_solve( + const igl::HeatGeodesicsData &data, + const nb::DRef &gamma) + { + Eigen::VectorXN D; + igl::heat_geodesics_solve(data, gamma, D); + return D; + } + +} + +// Bind the wrapper to the Python module +void bind_heat_geodesics(nb::module_ &m) +{ + nb::class_>(m, "HeatGeodesicsData") + .def(nb::init<>()) + .def_rw("use_intrinsic_delaunay", &igl::HeatGeodesicsData::use_intrinsic_delaunay) + ; + + m.def("heat_geodesics_precompute", + &pyigl::heat_geodesics_precompute_t, + "V"_a, "F"_a, "t"_a, "data"_a, + R"(Precompute factorized solvers for computing a fast approximation of +geodesic distances on a mesh (V,F). [Crane et al. 2013] + +@param[in] V #V by dim list of mesh vertex positions +@param[in] F #F by 3 list of mesh face indices into V +@param[in] t "heat" parameter (smaller --> more accurate, less stable) +@param[out] data precomputation data (see heat_geodesics_solve) + )"); + m.def("heat_geodesics_precompute", + &pyigl::heat_geodesics_precompute, + "V"_a, "F"_a, "data"_a, + R"(Precompute factorized solvers for computing a fast approximation of +geodesic distances on a mesh (V,F). [Crane et al. 2013] + +@param[in] V #V by dim list of mesh vertex positions +@param[in] F #F by 3 list of mesh face indices into V +@param[out] data precomputation data (see heat_geodesics_solve) + )"); + m.def("heat_geodesics_solve", + &pyigl::heat_geodesics_solve, + "data"_a, + "gamma"_a, + R"(Compute fast approximate geodesic distances using precomputed data from a +set of selected source vertices (gamma). + +@param[in] data precomputation data (see heat_geodesics_precompute) +@param[in] gamma #gamma list of indices into V of source vertices +@param[out] D #V list of distances to gamma + +\fileinfo + )"); +} diff --git a/src/hessian.cpp b/src/hessian.cpp deleted file mode 100644 index f35fec5a..00000000 --- a/src/hessian.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_hessian = R"igl_Qu8mg5v7( - Constructs the finite element Hessian matrix - as described in https:arxiv.org/abs/1707.04348, - Natural Boundary Conditions for Smoothing in Geometry Processing - (Oded Stein, Eitan Grinspun, Max Wardetzky, Alec Jacobson) - The interior vertices are NOT set to zero yet. - -Parameters ----------- -V #V by dim list of mesh vertex positions -F #F by 3 list of mesh faces (must be triangles) - -Returns -------- -H #V by #V Hessian energy matrix, each column i corresponding to V(i,:) - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(hessian) -npe_doc(ds_hessian) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_23d_tri_mesh(v, f); - EigenSparseLike h; - igl::hessian(v, f, h); - return npe::move(h); -npe_end_code() - - diff --git a/src/hessian_energy.cpp b/src/hessian_energy.cpp deleted file mode 100644 index 927838d8..00000000 --- a/src/hessian_energy.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// might be sparse matrix problem - -#include -#include -#include -#include - -const char* ds_hessian_energy = R"igl_Qu8mg5v7( - -Constructs the Hessian energy matrix using mixed FEM - as described in https:arxiv.org/abs/1707.04348 - Natural Boundary Conditions for Smoothing in Geometry Processing - (Oded Stein, Eitan Grinspun, Max Wardetzky, Alec Jacobson) - -Parameters ----------- -V #V by dim list of mesh vertex positions -F #F by 3 list of mesh faces (must be triangles) - - -Returns -------- -Q #V by #V Hessian energy matrix, each row/column i - corresponding to V(i,:) - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(hessian_energy) -npe_doc(ds_hessian_energy) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - - EigenSparseLike q; - igl::hessian_energy(v, f, q); - return npe::move(q); - -npe_end_code() - - diff --git a/src/icosahedron.cpp b/src/icosahedron.cpp new file mode 100644 index 00000000..25f26ade --- /dev/null +++ b/src/icosahedron.cpp @@ -0,0 +1,35 @@ +#include "default_types.h" +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto icosahedron() + { + Eigen::MatrixXN V; + Eigen::MatrixXI F; + igl::icosahedron(V,F); + return std::make_tuple(V,F); + } +} + +// Bind the wrapper to the Python module +void bind_icosahedron(nb::module_ &m) +{ + m.def( + "icosahedron", + &pyigl::icosahedron, +R"( +Construct a icosahedron with radius 1 centered at the origin + +Outputs: + V #V by 3 list of vertex positions + F #F by 3 list of triangle indices into rows of V)"); +} + diff --git a/src/in_element.cpp b/src/in_element.cpp index 71dfd96a..672a9fe4 100644 --- a/src/in_element.cpp +++ b/src/in_element.cpp @@ -1,63 +1,47 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Alec Jacobson -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include -#include - -using AABB_f64_3 = igl::AABB,3>; -using AABB_f64_2 = igl::AABB,2>; -const char* ds_in_element = R"igl_Qu8mg5v7( -Determine whether each point in a list of points is in the elements of a mesh. - -Parameters: ------- -V : #V by dim list of mesh vertex positions. -Ele : #Ele by dim+1 list of mesh indices into #V. -Q : #Q by dim list of query point positions -aabb : axis-aligned bounding box tree object (see AABB.h) - -Returns: -------- -I : #Q list of indices into Ele of first containing element (-1 means no -containing element) -)igl_Qu8mg5v7"; -npe_function(in_element_3) -npe_doc( ds_in_element) -npe_arg(V, Eigen::MatrixXd) -npe_arg(Ele, Eigen::MatrixXi) -npe_arg(Q, Eigen::MatrixXd) -npe_arg(aabb, AABB_f64_3) -npe_begin_code() - - Eigen::VectorXi I; - Eigen::Map V_map(V.data(),V.rows(),V.cols()); - igl::in_element(V_map,Ele,Q,aabb,I); - return npe::move(I); - -npe_end_code() - - -npe_function(in_element_2) -npe_doc( ds_in_element) -npe_arg(V, Eigen::MatrixXd) -npe_arg(Ele, Eigen::MatrixXi) -npe_arg(Q, Eigen::MatrixXd) -npe_arg(aabb, AABB_f64_2) -npe_begin_code() - - Eigen::VectorXi I; - Eigen::Map V_map(V.data(),V.rows(),V.cols()); - igl::in_element(V_map,Ele,Q,aabb,I); - return npe::move(I); - -npe_end_code() - - - +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for in_element with simplicial mesh (F) + auto in_element( + const nb::DRef &V, + const nb::DRef &Ele, + const nb::DRef &Q, + const igl::AABB,3> &aabb) + { + Eigen::VectorXI I; + igl::in_element(V,Ele,Q,aabb,I); + return I; + } + +} + +// Bind the wrapper to the Python module +void bind_in_element(nb::module_ &m) +{ + m.def( + "in_element", + &pyigl::in_element, + "V"_a, + "Ele"_a, + "Q"_a, + "aabb"_a, +R"(Determine whether each point in a list of points is in the elements of a +mesh. + +@tparam DIM dimension of vertices in V (# of columns) +@param[in] V #V by dim list of mesh vertex positions. +@param[in] Ele #Ele by dim+1 list of mesh indices into #V. +@param[in] Q #Q by dim list of query point positions +@param[in] aabb axis-aligned bounding box tree object (see AABB.h) +@param[out] I #Q list of indices into Ele of first containing element (-1 means no + containing element) +)"); +} diff --git a/src/include/common.h b/src/include/common.h deleted file mode 100644 index b33061e2..00000000 --- a/src/include/common.h +++ /dev/null @@ -1,331 +0,0 @@ -#include - -#ifdef WIN32 // FIXME: at the end remove me at the end, usefull only fo appveyour debug -#ifdef _DEBUG - #undef _DEBUG - #include - #define _DEBUG -#else - #include -#endif -#endif - -template -void assert_nonzero_rows(const T& mat, std::string name) { - if (mat.rows() == 0) { - throw pybind11::value_error("Parameter " + name + " has shape 0 at dimension 0. Expected " + name + ".shape[0] > 0."); - } -} - -template -void assert_cols_equals(const T& mat, int cols, std::string name) { - if (mat.cols() != cols) { - throw pybind11::value_error("Parameter " + name + " has invalid shape at dimension 1, expected " + name + ".shape[1] = " + std::to_string(cols) + - " but got " + name + ".shape = [" + std::to_string(mat.rows()) + ", " + std::to_string(mat.cols()) + "]"); - } -} - -template -void assert_rows_equals(const T& mat, int rows, std::string name) { - if (mat.rows() != rows) { - throw pybind11::value_error("Parameter " + name + " has invalid shape at dimension 0, expected " + name + ".shape[0] = " + std::to_string(rows) + - " but got " + name + ".shape = [" + std::to_string(mat.rows()) + ", " + std::to_string(mat.cols()) + "]"); - } -} - -template -void assert_size_equals(const T& mat, int size, std::string name) { - if (mat.size() != size) { - throw pybind11::value_error("Parameter " + name + " has invalid size expected " + name + ".size() = " + std::to_string(size) + - " but got " + name + ".shape = [" + std::to_string(mat.rows()) + ", " + std::to_string(mat.cols()) + "]"); - } -} - -template -void assert_shape_equals(const T& mat, int rows, int cols, std::string name) { - if (mat.rows() != rows || mat.cols() != cols) { - throw pybind11::value_error("Parameter " + name + " has invalid shape, expected " + name + ".shape = [" + std::to_string(rows) + ", " + std::to_string(cols) + - "] but got " + name + ".shape = [" + std::to_string(mat.rows()) + ", " + std::to_string(mat.cols()) + "]"); - } -} - - -template -void assert_shapes_match(const T1& mat1, const T2& mat2, std::string name1, std::string name2) { - if (mat1.rows() != mat2.rows() || mat1.cols() != mat2.cols()) { - throw pybind11::value_error("Parameters " + name1 + " and " + name2 + " must have the same shape but got " + - name1 + ".shape = [" + std::to_string(mat1.rows()) + ", " + std::to_string(mat1.cols()) + "] and " + - name2 + ".shape = [" + std::to_string(mat2.rows()) + ", " + std::to_string(mat2.cols()) + "]"); - } -} - -template -void assert_rows_match(const T1& mat1, const T2& mat2, std::string name1, std::string name2) { - if (mat1.rows() != mat2.rows()) { - throw pybind11::value_error("Parameters " + name1 + " and " + name2 + " must have the same shape at dimension 0 (rows) but got " + - name1 + ".shape = [" + std::to_string(mat1.rows()) + ", " + std::to_string(mat1.cols()) + "] and " + - name2 + ".shape = [" + std::to_string(mat2.rows()) + ", " + std::to_string(mat2.cols()) + "]"); - } -} - -template -void assert_cols_match(const T1& mat1, const T2& mat2, std::string name1, std::string name2) { - if (mat1.cols() != mat2.cols()) { - throw pybind11::value_error("Parameters " + name1 + " and " + name2 + " must have the same shape at dimension 1 (cols) but got " + - name1 + ".shape = [" + std::to_string(mat1.rows()) + ", " + std::to_string(mat1.cols()) + "] and " + - name2 + ".shape = [" + std::to_string(mat2.rows()) + ", " + std::to_string(mat2.cols()) + "]"); - } -} - - - -template -void assert_rows_match(const T1& mat1, int mat2_rows, int mat2_cols, std::string name1, std::string name2) { - if (mat1.rows() != mat2_rows) { - throw pybind11::value_error("Parameters " + name1 + " and " + name2 + " must have the same shape at dimension 0 (rows) but got " + - name1 + ".shape = [" + std::to_string(mat1.rows()) + ", " + std::to_string(mat1.cols()) + "] and " + - name2 + ".shape = [" + std::to_string(mat2_rows) + ", " + std::to_string(mat2_cols) + "]"); - } -} - -template -void assert_cols_match(const T1& mat1, int mat2_rows, int mat2_cols, std::string name1, std::string name2) { - if (mat1.cols() != mat2_cols) { - throw pybind11::value_error("Parameters " + name1 + " and " + name2 + " must have the same shape at dimension 1 (cols) but got " + - name1 + ".shape = [" + std::to_string(mat1.rows()) + ", " + std::to_string(mat1.cols()) + "] and " + - name2 + ".shape = [" + std::to_string(mat2_rows) + ", " + std::to_string(mat2_cols) + "]"); - } -} - -template -void assert_shapes_match(const T1& mat1, int mat2_rows, int mat2_cols, std::string name1, std::string name2) { - if (mat1.rows() != mat2_rows || mat1.cols() != mat2_cols) { - throw pybind11::value_error("Parameters " + name1 + " and " + name2 + " must have the same shape but got " + - name1 + ".shape = [" + std::to_string(mat1.rows()) + ", " + std::to_string(mat1.cols()) + "] and " + - name2 + ".shape = [" + std::to_string(mat2_rows) + ", " + std::to_string(mat2_cols) + "]"); - } -} - - -template -void assert_valid_tet_or_tri_mesh_faces(const TF& f, std::string f_name="f") { - if (f.rows() <= 0) { - throw pybind11::value_error("Invalid mesh indices, " + f_name + " has zero rows (" + f_name + ".shape = [" + - std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]) "); - } - - if (f.cols() != 3 && f.cols() != 4) { - throw pybind11::value_error("Invalid mesh indices, " + f_name + " must have shape [#faces, 3] (for a triangle mesh) or [#faces, 4] (for a tet mesh) " + - "but got " + f_name + ".shape = [" + std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]"); - } -} - -template -void assert_valid_simplex_idxs(const TF& f, std::string f_name="f") { - if (f.rows() <= 0) { - throw pybind11::value_error("Invalid simplex indices, " + f_name + " has zero rows (" + f_name + ".shape = [" + - std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]) "); - } - - if (f.cols() < 2) { - throw pybind11::value_error("Invalid simplex indices, " + f_name + " must have shape [#faces, #edges] with #edges >= 2 for a valid simplex." + - "but got " + f_name + ".shape = [" + std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]"); - } -} - -template -void assert_valid_tri_mesh_faces(const TF &f, std::string f_name = "f") -{ - if (f.rows() <= 0) - { - throw pybind11::value_error("Invalid mesh indices, " + f_name + " has zero rows (" + f_name + ".shape = [" + - std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]) "); - } - - if (f.cols() != 3) - { - throw pybind11::value_error("Invalid mesh indices, " + f_name + " must have shape [#faces, 3] " + - "but got " + f_name + ".shape = [" + std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]"); - } -} - -template -void assert_valid_tet_or_tri_mesh(const TV& v, const TF& f, std::string v_name="v", std::string f_name="f") { - if (v.rows() <= 0) { - throw pybind11::value_error("Invalid mesh vertices, " + v_name + " has zero rows (" + v_name + - ".shape = [" + std::to_string(v.rows()) + ", " + std::to_string(v.cols()) + "]) "); - } - if (f.rows() <= 0) { - throw pybind11::value_error("Invalid mesh indices, " + f_name + " has zero rows (" + f_name + ".shape = [" + - std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]) "); - } - - if (v.cols() != 3) { - throw pybind11::value_error("Invalid mesh vertices, " + v_name + " must have shape [#vertices, 3] but got " + v_name + - ".shape = [" + std::to_string(v.rows()) + ", " + std::to_string(v.cols()) + "]"); - } - if (f.cols() != 3 && f.cols() != 4) { - throw pybind11::value_error("Invalid mesh indices, " + f_name + " must have shape [#faces, 3] (for a triangle mesh) or [#faces, 4] (for a tet mesh) " + - "but got " + f_name + ".shape = [" + std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]"); - } -} - -template -void assert_valid_tet_or_tri_mesh_23d(const TV &v, const TF &f, std::string v_name = "v", std::string f_name = "f") -{ - if (v.rows() <= 0) - { - throw pybind11::value_error("Invalid mesh vertices, " + v_name + " has zero rows (" + v_name + - ".shape = [" + std::to_string(v.rows()) + ", " + std::to_string(v.cols()) + "]) "); - } - if (f.rows() <= 0) - { - throw pybind11::value_error("Invalid mesh indices, " + f_name + " has zero rows (" + f_name + ".shape = [" + - std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]) "); - } - - if (v.cols() != 3 && v.cols() != 2) - { - throw pybind11::value_error("Invalid mesh vertices, " + v_name + " must have shape [#vertices, 3] or [#vertices, 2] but got " + v_name + - ".shape = [" + std::to_string(v.rows()) + ", " + std::to_string(v.cols()) + "]"); - } - - if (f.cols() != 3 && f.cols() != 4) - { - throw pybind11::value_error("Invalid mesh indices, " + f_name + " must have shape [#faces, 3] (for a triangle mesh) or [#faces, 4] (for a tet mesh) " + - "but got " + f_name + ".shape = [" + std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]"); - } - - if (v.cols() == 2 && f.cols() == 4) - { - throw pybind11::value_error("Invalid mesh vertices, " + v_name + " must have shape [#vertices, 3] (and not 2) to be compatible with " + f_name + " which are tets, shape [#faces, 4]"); - } -} - -template -void assert_valid_3d_tri_mesh(const TV& v, const TF& f, std::string v_name="v", std::string f_name="f") { - if (v.rows() <= 0) { - throw pybind11::value_error("Invalid mesh vertices, " + v_name + " has zero rows (" + v_name + - ".shape = [" + std::to_string(v.rows()) + ", " + std::to_string(v.cols()) + "]) "); - } - if (f.rows() <= 0) { - throw pybind11::value_error("Invalid mesh indices, " + f_name + " has zero rows (" + f_name + ".shape = [" + - std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]) "); - } - - if (v.cols() != 3) { - throw pybind11::value_error("Invalid mesh vertices, " + v_name + " must have shape [#vertices, 3] but got " + v_name + - ".shape = [" + std::to_string(v.rows()) + ", " + std::to_string(v.cols()) + "]"); - } - if (f.cols() != 3) { - throw pybind11::value_error("Invalid mesh faces, " + f_name + " must have shape [#faces, 3] but got " + f_name + - ".shape = [" + std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]"); - } -} - -template -void assert_valid_3d_quad_mesh(const TV& v, const TF& f, std::string v_name="v", std::string f_name="f") { - if (v.rows() <= 0) { - throw pybind11::value_error("Invalid mesh vertices, " + v_name + " has zero rows (" + v_name + - ".shape = [" + std::to_string(v.rows()) + ", " + std::to_string(v.cols()) + "]) "); - } - if (f.rows() <= 0) { - throw pybind11::value_error("Invalid mesh indices, " + f_name + " has zero rows (" + f_name + ".shape = [" + - std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]) "); - } - - if (v.cols() != 3) { - throw pybind11::value_error("Invalid mesh vertices, " + v_name + " must have shape [#vertices, 3] but got " + v_name + - ".shape = [" + std::to_string(v.rows()) + ", " + std::to_string(v.cols()) + "]"); - } - if (f.cols() != 4) { - throw pybind11::value_error("Invalid mesh faces, " + f_name + " must have shape [#faces, 4] but got " + f_name + - ".shape = [" + std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]"); - } -} - -template -void assert_valid_23d_tri_mesh(const TV &v, const TF &f, std::string v_name = "v", std::string f_name = "f") -{ - if (v.rows() <= 0) - { - throw pybind11::value_error("Invalid mesh vertices, " + v_name + " has zero rows (" + v_name + - ".shape = [" + std::to_string(v.rows()) + ", " + std::to_string(v.cols()) + "]) "); - } - if (f.rows() <= 0) - { - throw pybind11::value_error("Invalid mesh indices, " + f_name + " has zero rows (" + f_name + ".shape = [" + - std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]) "); - } - - if (v.cols() != 3 && v.cols() != 2) - { - throw pybind11::value_error("Invalid mesh vertices, " + v_name + " must have shape [#vertices, 3] or [#vertices, 2] but got " + v_name + - ".shape = [" + std::to_string(v.rows()) + ", " + std::to_string(v.cols()) + "]"); - } - if (f.cols() != 3) - { - throw pybind11::value_error("Invalid mesh faces, " + f_name + " must have shape [#faces, 3] but got " + f_name + - ".shape = [" + std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]"); - } -} - -template -void assert_valid_tet_mesh(const TV& v, const TF& t, std::string v_name="v", std::string t_name="t") { - if (v.rows() <= 0) { - throw pybind11::value_error("Invalid mesh vertices, " + v_name + " has zero rows (" + v_name + - ".shape = [" + std::to_string(v.rows()) + ", " + std::to_string(v.cols()) + "]) "); - } - if (t.rows() <= 0) { - throw pybind11::value_error("Invalid mesh indices, " + t_name + " has zero rows (" + t_name + ".shape = [" + - std::to_string(t.rows()) + ", " + std::to_string(t.cols()) + "]) "); - } - - if (v.cols() != 3) { - throw pybind11::value_error("Invalid mesh vertices, " + v_name + " must have shape [#vertices, 3] but got " + v_name + - ".shape = [" + std::to_string(v.rows()) + ", " + std::to_string(v.cols()) + "]"); - } - if (t.cols() != 4) { - throw pybind11::value_error("Invalid mesh tets, " + t_name + " must have shape [#tets, 4] but got " + t_name + - ".shape = [" + std::to_string(t.rows()) + ", " + std::to_string(t.cols()) + "]"); - } -} - -template -void assert_valid_2d_tri_mesh(const TV& v, const TF& f, std::string v_name="v", std::string f_name="f") { - if (v.rows() <= 0) { - throw pybind11::value_error("Invalid mesh vertices, " + v_name + " has zero rows (" + v_name + - ".shape = [" + std::to_string(v.rows()) + ", " + std::to_string(v.cols()) + "]) "); - } - if (f.rows() <= 0) { - throw pybind11::value_error("Invalid mesh indices, " + f_name + " has zero rows (" + f_name + ".shape = [" + - std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]) "); - } - - if (v.cols() != 2) { - throw pybind11::value_error("Invalid mesh vertices, " + v_name + " must have shape [#vertices, 2] but got " + v_name + - ".shape = [" + std::to_string(v.rows()) + ", " + std::to_string(v.cols()) + "]"); - } - if (f.cols() != 3) { - throw pybind11::value_error("Invalid mesh faces, " + f_name + " must have shape [#faces, 3] but got " + f_name + - ".shape = [" + std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]"); - } -} - -template -void assert_valid_bone_transforms(const TV& t, std::string t_name="t") { - if (t.rows() <= 0) { - throw pybind11::value_error("Invalid number of transforms, " + t_name + " has zero rows (" + t_name + - ".shape = [" + std::to_string(t.rows()) + ", " + std::to_string(t.cols()) + "]) "); - } - - if (t.cols() != 3) { - throw pybind11::value_error("Invalid number of cols in transforms, " + t_name + " must have shape [#bones * 4, 3] but got " + t_name + - ".shape = [" + std::to_string(t.rows()) + ", " + std::to_string(t.cols()) + "]"); - } - - if (t.rows() % 4 != 0) { - throw pybind11::value_error("Invalid number of rows in transforms, " + t_name + " must have shape [#bones * 4, 3] but got " + t_name + - ".shape = [" + std::to_string(t.rows()) + ", " + std::to_string(t.cols()) + "]."); - } -} diff --git a/src/include/typedefs.h b/src/include/typedefs.h deleted file mode 100644 index 2afdc886..00000000 --- a/src/include/typedefs.h +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include - - -const int IglDefaultOptions = Eigen::RowMajor; - -constexpr bool opts_dontalign(int options) { - return (options == (Eigen::ColMajor | Eigen::DontAlign) || options == (Eigen::RowMajor | Eigen::DontAlign)); -} - -template -struct OptExtractor { - enum { - Options = Opts - }; -}; - -template -struct OptExtractor { - enum Options { - Options = Eigen::RowMajor - }; -}; - -template -using EigenSparseLike = Eigen::SparseMatrix; // FIXME: Maybe we should output CSR if LikeT is row major - -template -using EigenDenseLike = Eigen::Matrix::Options, Eigen::Dynamic, Eigen::Dynamic>; - -template -using EigenDense = Eigen::Matrix; - -typedef Eigen::Matrix EigenDenseF32; -typedef Eigen::Matrix EigenDenseF64; -typedef Eigen::Matrix EigenDenseI32; -typedef Eigen::Matrix EigenDenseI64; - -//TODO replace with fancy CMAKE -#ifdef WIN32 -typedef EigenDenseF64 EigenDenseFloat; -typedef EigenDenseI32 EigenDenseInt; -#else -typedef EigenDenseF64 EigenDenseFloat; -typedef EigenDenseI64 EigenDenseInt; -#endif - -typedef Eigen::SparseMatrix EigenSparseF32; -typedef Eigen::SparseMatrix EigenSparseF64; -typedef Eigen::SparseMatrix EigenSparseI32; -typedef Eigen::SparseMatrix EigenSparseI64; diff --git a/src/inradius.cpp b/src/inradius.cpp index 708c9364..37187f6c 100644 --- a/src/inradius.cpp +++ b/src/inradius.cpp @@ -1,53 +1,31 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char *ds_inradius = R"igl_Qu8mg5v7( - Compute the inradius of each triangle in a mesh (V,F) - -Parameters ----------- - V #V by dim list of mesh vertex positions - F #F by 3 list of triangle indices into V - -Returns -------- -R #F list of inradii - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(inradius) -npe_doc(ds_inradius) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_23d_tri_mesh(v, f); - EigenDenseLike r; - igl::inradius(v, f, r); - return npe::move(r); - -npe_end_code() - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto inradius( + const nb::DRef & V, + const nb::DRef & F) + { + Eigen::VectorXN R; + igl::inradius(V, F, R); + return R; + } +} + +void bind_inradius(nb::module_ &m) +{ + m.def("inradius", &pyigl::inradius, + "V"_a, + "F"_a, + R"(Compute the inradius of each triangle in a mesh (V,F) +@param[in] V #V by dim list of mesh vertex positions +@param[in] F #F by 3 list of triangle indices into V +@param[out] R #F list of inradii)"); +} diff --git a/src/internal_angles.cpp b/src/internal_angles.cpp deleted file mode 100644 index cdfa7e99..00000000 --- a/src/internal_angles.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char* ds_internal_angles = R"igl_Qu8mg5v7( -Computes internal angles for a triangle mesh. - -Parameters ----------- -v : #v by dim array of mesh vertex nD positions -f : #f by poly-size array of face (triangle) indices - -Returns -------- -k : #f by poly-size array of internal angles. For triangles, columns correspond to edges [1,2],[2,0],[0,1]. - -See also --------- -None - -Notes ------ -If poly-size ≠ 3 then dim must equal 3. - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(internal_angles) -npe_doc(ds_internal_angles) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - -npe_begin_code() - - assert_valid_23d_tri_mesh(v, f); - EigenDenseLike k; - igl::internal_angles(v, f, k); - return npe::move(k); - -npe_end_code() -//#include - -//const char* ds_internal_angles_using_squared_edge_lengths = R"igl_Qu8mg5v7( - -//Parameters -//---------- - - -//Returns -//------- - - -//See also -//-------- - - -//Notes -//----- -//None - -//Examples -//-------- - -// Inputs: -// L_sq #F by 3 list of squared edge lengths -// Output: -// K #F by poly-size eigen Matrix of internal angles -// for triangles, columns correspond to edges [1,2],[2,0],[0,1] -// -// Note: -// Usage of internal_angles_using_squared_edge_lengths is preferred to internal_angles_using_squared_edge_lengths -//)igl_Qu8mg5v7"; - -//npe_function(internal_angles_using_squared_edge_lengths) -//npe_doc(ds_internal_angles_using_squared_edge_lengths) - -//npe_arg(l_sq, dense_float, dense_double) - - -//npe_begin_code() - -// EigenDense k; -// igl::internal_angles_using_squared_edge_lengths(l_sq, k); -// return npe::move(k); - -//npe_end_code() -//#include - -//const char* ds_internal_angles_using_edge_lengths = R"igl_Qu8mg5v7( - -//Parameters -//---------- - - -//Returns -//------- - - -//See also -//-------- - - -//Notes -//----- -//None - -//Examples -//-------- - -// Inputs: -// L #F by 3 list of edge lengths -// Output: -// K #F by poly-size eigen Matrix of internal angles -// for triangles, columns correspond to edges [1,2],[2,0],[0,1] -// -// Note: -// Usage of internal_angles_using_squared_edge_lengths is preferred to internal_angles_using_squared_edge_lengths -// This function is deprecated and probably will be removed in future versions -//)igl_Qu8mg5v7"; - -//npe_function(internal_angles_using_edge_lengths) -//npe_doc(ds_internal_angles_using_edge_lengths) - -//npe_arg(l, dense_float, dense_double) - - -//npe_begin_code() - -// EigenDense k; -// igl::internal_angles_using_edge_lengths(l, k); -// return npe::move(k); - -//npe_end_code() - - diff --git a/src/intrinsic_delaunay_cotmatrix.cpp b/src/intrinsic_delaunay_cotmatrix.cpp index d0edc3da..59223ea9 100644 --- a/src/intrinsic_delaunay_cotmatrix.cpp +++ b/src/intrinsic_delaunay_cotmatrix.cpp @@ -1,61 +1,46 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char *ds_intrinsic_delaunay_cotmatrix = R"igl_Qu8mg5v7( - -INTRINSIC_DELAUNAY_COTMATRIX Computes the discrete cotangent Laplacian of a -mesh after converting it into its intrinsic Delaunay triangulation (see, -e.g., [Fisher et al. 2007]. - -Parameters ----------- - -V #V by dim list of mesh vertex positions -F #F by 3 list of mesh elements (triangles or tetrahedra) - -Returns -------- - -L #V by #V cotangent matrix, each row i corresponding to V(i,:) -l_intrinsic #F by 3 list of intrinsic edge-lengths used to compute L -F_intrinsic #F by 3 list of intrinsic face indices used to compute L - -See also --------- -intrinsic_delaunay_triangulation, cotmatrix, cotmatrix_intrinsic - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(intrinsic_delaunay_cotmatrix) -npe_doc(ds_intrinsic_delaunay_cotmatrix) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - - Eigen::SparseMatrix l; - EigenDense l_intrinsic; - EigenDense f_intrinsic; - igl::intrinsic_delaunay_cotmatrix(v, f, l, l_intrinsic, f_intrinsic); - return std::make_tuple(npe::move(l), npe::move(l_intrinsic), npe::move(f_intrinsic)); - -npe_end_code() - +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto intrinsic_delaunay_cotmatrix( + const nb::DRef &V, + const nb::DRef &F) + { + Eigen::SparseMatrixN L; + Eigen::MatrixXN il; + Eigen::MatrixXI iF; + igl::intrinsic_delaunay_cotmatrix(V,F,L,il,iF); + return std::make_tuple(L,il,iF); + } +} + +// Bind the wrapper to the Python module +void bind_intrinsic_delaunay_cotmatrix(nb::module_ &m) +{ + m.def( + "intrinsic_delaunay_cotmatrix", + &pyigl::intrinsic_delaunay_cotmatrix, + "V"_a, + "F"_a, +R"( +Computes the discrete cotangent Laplacian of a mesh after converting it +into its intrinsic Delaunay triangulation (see, e.g., [Fisher et al. +2007]. + +@param[in] V #V by dim list of mesh vertex positions +@param[in] F #F by 3 list of mesh elements (triangles or tetrahedra) +@param[out] L #V by #V cotangent matrix, each row i corresponding to V(i,:) +@param[out] l_intrinsic #F by 3 list of intrinsic edge-lengths used to compute L +@param[out] F_intrinsic #F by 3 list of intrinsic face indices used to compute L + +\see intrinsic_delaunay_triangulation, cotmatrix, cotmatrix_intrinsic)"); +} diff --git a/src/intrinsic_delaunay_triangulation.cpp b/src/intrinsic_delaunay_triangulation.cpp index 446611a0..0b141fe6 100644 --- a/src/intrinsic_delaunay_triangulation.cpp +++ b/src/intrinsic_delaunay_triangulation.cpp @@ -1,144 +1,63 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include +#include "default_types.h" #include - -const char *ds_intrinsic_delaunay_triangulation = R"igl_Qu8mg5v7( - -INTRINSIC_DELAUNAY_TRIANGULATION Flip edges _intrinsically_ until all are -"intrinsic Delaunay". See "An algorithm for the construction of intrinsic -delaunay triangulations with applications to digital geometry processing" -[Fisher et al. 2007]. - -Parameters ----------- - -l_in #F_in by 3 list of edge lengths (see edge_lengths) -F_in #F_in by 3 list of face indices into some unspecified vertex list V - -Returns -------- - -l #F by 3 list of edge lengths -F #F by 3 list of new face indices. Note: Combinatorially F may contain - non-manifold edges, duplicate faces and self-loops (e.g., an edge [1,1] - or a face [1,1,1]). However, the *intrinsic geometry* is still - well-defined and correct. See [Fisher et al. 2007] Figure 3 and 2nd to - last paragraph of 1st page. Since F may be "non-eddge-manifold" in the - usual combinatorial sense, it may be useful to call the more verbose - overload below if disentangling edges will be necessary later on. - Calling unique_edge_map on this F will give a _different_ result than - those outputs. - -See also --------- - -is_intrinsic_delaunay - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(intrinsic_delaunay_triangulation) -npe_doc(ds_intrinsic_delaunay_triangulation) - -npe_arg(l_in, dense_float, dense_double) -npe_arg(f_in, dense_int32, dense_int64) - - -npe_begin_code() - assert_rows_match(l_in, f_in, "l_in", "f_in"); - assert_cols_equals(l_in, 3, "l_in"); - assert_valid_tri_mesh_faces(f_in); - - EigenDense l; - EigenDense f; - igl::intrinsic_delaunay_triangulation(l_in, f_in, l, f); - return std::make_tuple(npe::move(l), npe::move(f)); - -npe_end_code() - - -const char *ds_intrinsic_delaunay_triangulation1 = R"igl_Qu8mg5v71( - +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto intrinsic_delaunay_triangulation( + const nb::DRef &l_in, + const nb::DRef &F_in) + { + Eigen::MatrixXN l; + Eigen::MatrixXI F,E,uE; + Eigen::VectorXI EMAP; + std::vector > uE2E; + igl::intrinsic_delaunay_triangulation(l_in,F_in,l,F,E,uE,EMAP,uE2E); + return std::make_tuple(l,F,E,uE,EMAP,uE2E); + + } +} + +// Bind the wrapper to the Python module +void bind_intrinsic_delaunay_triangulation(nb::module_ &m) +{ + m.def( + "intrinsic_delaunay_triangulation", + &pyigl::intrinsic_delaunay_triangulation, + "l"_a, + "F"_a, +R"( INTRINSIC_DELAUNAY_TRIANGULATION Flip edges _intrinsically_ until all are "intrinsic Delaunay". See "An algorithm for the construction of intrinsic delaunay triangulations with applications to digital geometry processing" [Fisher et al. 2007]. -Parameters ----------- - -l_in #F_in by 3 list of edge lengths (see edge_lengths) -F_in #F_in by 3 list of face indices into some unspecified vertex list V - -Returns -------- - -E #F*3 by 2 list of all directed edges, such that E.row(f+#F*c) is the - edge opposite F(f,c) -uE #uE by 2 list of unique undirected edges -EMAP #F*3 list of indices into uE, mapping each directed edge to unique - undirected edge -uE2E #uE list of lists of indices into E of coexisting edges - -See also --------- -unique_edge_map - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v71"; - -npe_function(intrinsic_delaunay_triangulation_edges) -npe_doc(ds_intrinsic_delaunay_triangulation1) - -npe_arg(l_in, dense_float, dense_double) -npe_arg(f_in, dense_int32, dense_int64) - - -npe_begin_code() - assert_rows_match(l_in, f_in, "l_in", "f_in"); - assert_cols_equals(l_in, 3, "l_in"); - assert_valid_tri_mesh_faces(f_in); - - EigenDense l; - EigenDense f; - EigenDense e; - EigenDense u_e; - Eigen::Matrix emap; - std::vector> u_e2_e; - igl::intrinsic_delaunay_triangulation(l_in, f_in, l, f, e, u_e, emap, u_e2_e); - std::vector> u_e2_e_copy; - - u_e2_e_copy.resize(u_e2_e.size()); - - for(size_t i = 0; i < u_e2_e_copy.size(); ++i) - { - u_e2_e_copy.resize(u_e2_e[i].size()); - for (size_t j = 0; j < u_e2_e_copy[i].size(); ++j) - u_e2_e_copy[i][j] = u_e2_e[i][j]; - } - - return std::make_tuple(npe::move(l), npe::move(f), npe::move(e), npe::move(u_e), npe::move(emap), u_e2_e_copy); - - npe_end_code() +@param[in] l_in #F_in by 3 list of edge lengths (see edge_lengths) +@param[in] F_in #F_in by 3 list of face indices into some unspecified vertex list V +@param[out] l #F by 3 list of edge lengths +@param[out] F #F by 3 list of new face indices. Note: Combinatorially F may contain + non-manifold edges, duplicate faces and self-loops (e.g., an edge [1,1] + or a face [1,1,1]). However, the *intrinsic geometry* is still + well-defined and correct. See [Fisher et al. 2007] Figure 3 and 2nd to + last paragraph of 1st page. Since F may be "non-eddge-manifold" in the + usual combinatorial sense, it may be useful to call the more verbose + overload below if disentangling edges will be necessary later on. + Calling unique_edge_map on this F will give a _different_ result than + those outputs. +@param[out] E #F*3 by 2 list of all directed edges, such that E.row(f+#F*c) is the +@param[out] edge opposite F(f,c) +@param[out] uE #uE by 2 list of unique undirected edges +@param[out] EMAP #F*3 list of indices into uE, mapping each directed edge to unique +@param[out] undirected edge +@param[out] uE2E #uE list of lists of indices into E of coexisting edges + +\see unique_edge_map +)"); +} diff --git a/src/is_border_vertex.cpp b/src/is_border_vertex.cpp index 964fc2fe..8339b10d 100644 --- a/src/is_border_vertex.cpp +++ b/src/is_border_vertex.cpp @@ -1,55 +1,30 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include -#include - -const char *ds_is_border_vertex = R"igl_Qu8mg5v7( -Determine vertices on open boundary of a (manifold) mesh with triangle faces F -Parameters ----------- - V #V by dim list of vertex positions - F #F by 3 list of triangle indices - -Returns -------- -#V vector of bools revealing whether vertices are on boundary - -See also --------- - - -Notes ------ -Known Bugs: - - assumes mesh is edge manifold - -Examples --------- - - - -)igl_Qu8mg5v7"; - -npe_function(is_border_vertex) -npe_doc(ds_is_border_vertex) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_23d_tri_mesh(v, f); - const auto res = igl::is_border_vertex(f); - return res; - -npe_end_code() - - +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for is_border_vertex + std::vector is_border_vertex(const nb::DRef &F) + { + return igl::is_border_vertex(F); + } +} + +// Bind the wrapper to the Python module +void bind_is_border_vertex(nb::module_ &m) +{ + m.def( + "is_border_vertex", + &pyigl::is_border_vertex, + "F"_a, +R"(Determine vertices on the open boundary of a manifold mesh with triangle faces. + +@param[in] F #F by 3 list of triangle indices +@return #V vector of bools indicating if vertices are on the boundary)"); +} diff --git a/src/is_delaunay.cpp b/src/is_delaunay.cpp deleted file mode 100644 index e49c8fe4..00000000 --- a/src/is_delaunay.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_is_delaunay = R"igl_Qu8mg5v7( - -IS_DELAUNAY Determine if each edge in the mesh (V,F) is Delaunay. - - -Parameters ----------- - -V #V by dim list of vertex positions -F #F by 3 list of triangles indices - -Returns -------- - -D #F by 3 list of bools revealing whether edges corresponding 23 31 12 - are locally Delaunay. Boundary edges are by definition Delaunay. - Non-Manifold edges are by definition not Delaunay. - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(is_delaunay) -npe_doc(ds_is_delaunay) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_23d_tri_mesh(v, f); - - EigenDense d; - igl::is_delaunay(v, f, d); - return npe::move(d); - -npe_end_code() - diff --git a/src/is_edge_manifold.cpp b/src/is_edge_manifold.cpp index b93a9481..97b11035 100644 --- a/src/is_edge_manifold.cpp +++ b/src/is_edge_manifold.cpp @@ -1,34 +1,43 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example, decide if to remove the first function - -#include -#include -#include +#include "default_types.h" #include - - - -const char* ds_is_edge_manifold = R"igl_Qu8mg5v7( -See is_edge_manifold for the documentation. -)igl_Qu8mg5v7"; - -npe_function(is_edge_manifold) -npe_doc(ds_is_edge_manifold) - -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_tri_mesh_faces(f); - return igl::is_edge_manifold(f); - -npe_end_code() - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for is_edge_manifold with overload handling + auto is_edge_manifold( + const nb::DRef &F) + { + bool result; + Eigen::MatrixXI BF; + Eigen::MatrixXI E; + Eigen::VectorXI EMAP; + Eigen::VectorX BE; + + result = igl::is_edge_manifold(F, BF, E, EMAP, BE); + return std::make_tuple(result, BF, E, EMAP, BE); + } +} + +// Bind the wrapper to the Python module +void bind_is_edge_manifold(nb::module_ &m) +{ + m.def( + "is_edge_manifold", + &pyigl::is_edge_manifold, + "F"_a, +R"(Check if the mesh is edge-manifold (every edge is incident to one or two oppositely oriented faces). + +@param[in] F #F by 3 list of triangle indices +@param[out] BF (if return_BF=True) #F by 3 list of flags for non-manifold edges opposite each vertex +@param[out] E (if return_E=True) #E by 2 list of unique edges +@param[out] EMAP (if return_EMAP=True) 3*#F list of indices of opposite edges in E +@param[out] BE (if return_BE=True) #E list of flags for whether each edge is non-manifold +@return True if all edges are manifold, otherwise False)"); +} diff --git a/src/is_intrinsic_delaunay.cpp b/src/is_intrinsic_delaunay.cpp deleted file mode 100644 index 3a504cc8..00000000 --- a/src/is_intrinsic_delaunay.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_is_intrinsic_delaunay = R"igl_Qu8mg5v7( - -IS_INTRINSIC_DELAUNAY Determine if each edge in the mesh (V,F) is Delaunay. - - -Parameters ----------- - l #l by dim list of edge lengths - F #F by 3 list of triangles indices - -Returns -------- - -D #F by 3 list of bools revealing whether edges corresponding 23 31 12 - are locally Delaunay. Boundary edges are by definition Delaunay. - Non-Manifold edges are by definition not Delaunay. - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(is_intrinsic_delaunay) -npe_doc(ds_is_intrinsic_delaunay) - -npe_arg(l, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_rows_match(l, f, "l", "f"); - assert_cols_equals(l, 3, "l"); - assert_valid_tri_mesh_faces(f); - - EigenDense d; - igl::is_intrinsic_delaunay(l, f, d); - return npe::move(d); - -npe_end_code() - diff --git a/src/is_irregular_vertex.cpp b/src/is_irregular_vertex.cpp deleted file mode 100644 index d29216f7..00000000 --- a/src/is_irregular_vertex.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include -#include - -const char* ds_is_irregular_vertex = R"igl_Qu8mg5v7( -Determine if a vertex is irregular, i.e. it has more than 6 (triangles) or 4 (quads) incident edges. Vertices on the boundary are ignored. - -Parameters ----------- -f : #f by 3[4] array of triangle[quads] indices - -Returns -------- -s : #v list of bools revealing whether vertices are singular - -See also --------- -None - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(is_irregular_vertex) -npe_doc(ds_is_irregular_vertex) - -npe_arg(f, dense_int32, dense_int64) - -npe_begin_code() - const std::vector res = igl::is_irregular_vertex(f); - return res; - -npe_end_code() - - diff --git a/src/ismember_rows.cpp b/src/ismember_rows.cpp new file mode 100644 index 00000000..b94ecdfa --- /dev/null +++ b/src/ismember_rows.cpp @@ -0,0 +1,44 @@ +#include "default_types.h" +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for ismember_rows with simplicial mesh (F) + auto ismember_rows( + const nb::DRef &A, + const nb::DRef &B) + { + Eigen::Array IA; + Eigen::VectorXI LOCB; + igl::ismember_rows(A,B,IA,LOCB); + return std::make_tuple(IA,LOCB); + } + +} + +// Bind the wrapper to the Python module +void bind_ismember_rows(nb::module_ &m) +{ + m.def( + "ismember_rows", + &pyigl::ismember_rows, + "A"_a, + "B"_a, +R"( +Determine if row of A exist in rows of B + +@param[in] A ma by na matrix of Integers +@param[in] B mb by nb matrix of Integers +@param[out] IA ma by 1 lest of flags whether corresponding element of A + exists in B +@param[out] LOCB ma by 1 list matrix of indices in B locating matching + element (-1 if not found), indices assume column major ordering +)"); +} + diff --git a/src/isolines.cpp b/src/isolines.cpp index 77f9983b..205f57ad 100644 --- a/src/isolines.cpp +++ b/src/isolines.cpp @@ -1,68 +1,60 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -//TODO: __example - -#include -#include -#include +#include "default_types.h" #include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto isolines( + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &S, + const nb::DRef &vals) + { + Eigen::MatrixXN iV; + Eigen::MatrixXI iE; + Eigen::VectorXI I; + igl::isolines(V,F,S,vals,iV,iE,I); + return std::make_tuple(iV,iE,I); + } +} + +// Bind the wrapper to the Python module +void bind_isolines(nb::module_ &m) +{ + m.def( + "isolines", + &pyigl::isolines, + "V"_a, + "F"_a, + "S"_a, + "vals"_a, +R"(Compute isolines of a scalar field on a triangle mesh. + +Isolines may cross perfectly at vertices. The output should not contain +degenerate segments (so long as the input does not contain degenerate +faces). The output segments are *oriented* so that isolines curl +counter-clockwise around local maxima (i.e., for 2D scalar fields). Unless +an isoline hits a boundary, it should be a closed loop. Isolines may run +perfectly along boundaries. Isolines should appear just "above" constants +regions. + +@param[in] V #V by dim list of mesh vertex positions +@param[in] F #F by 3 list of mesh triangle indices into V +@param[in] S #S by 1 list of per-vertex scalar values +@param[in] vals #vals by 1 list of values to compute isolines for +@param[out] iV #iV by dim list of isoline vertex positions +@param[out] iE #iE by 2 list of edge indices into iV +@param[out] I #iE by 1 list of indices into vals indicating which value + each segment belongs to + +\see isolines_intrinsic, edge_crossings)"); +} -const char *ds_isolines = R"igl_Qu8mg5v7( - Constructs isolines for a function z given on a mesh (V,F) - -Parameters ----------- - V #V by dim list of mesh vertex positions - F #F by 3 list of mesh triangle indices into V - S #S by 1 list of per-vertex scalar values - vals #vals by 1 list of values to compute isolines for - -Returns -------- - iV #iV by dim list of isoline vertex positions - iE #iE by 2 list of edge indices into iV - I #iE by 1 list of indices into vals indicating which value - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(isolines) -npe_doc(ds_isolines) - -npe_arg(V, dense_float, dense_double) -npe_arg(F, dense_int32, dense_int64) -npe_arg(S, npe_matches(V)) -npe_arg(vals, npe_matches(S)) - - -npe_begin_code() - - assert_valid_23d_tri_mesh(V, F); - assert_rows_match(V, S, "V", "S"); - assert_cols_equals(S, 1, "S"); - EigenDenseLike iV; - EigenDenseLike iE; - Eigen::Matrix I; - Eigen::Matrix vals_copy = vals; - igl::isolines(V, F, S.col(0), vals_copy, iV, iE, I); - return std::make_tuple(npe::move(iV), npe::move(iE), npe::move(I)); - -npe_end_code() diff --git a/src/isolines_intrinsic.cpp b/src/isolines_intrinsic.cpp new file mode 100644 index 00000000..c1dd12d6 --- /dev/null +++ b/src/isolines_intrinsic.cpp @@ -0,0 +1,112 @@ +#include "default_types.h" +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto isolines_intrinsic( + const nb::DRef &F, + const nb::DRef &S, + const nb::DRef &vals) + { + Eigen::MatrixXN iB; + Eigen::VectorXI iF; + Eigen::MatrixXI iE; + Eigen::VectorXI I; + igl::isolines_intrinsic(F,S,vals,iB,iF,iE,I); + return std::make_tuple(iB,iF,iE,I); + } + auto isolines_intrinsic_edges( + const nb::DRef &F, + const nb::DRef &S, + const Numeric val, + const nb::DRef &uE, + const nb::DRef &EMAP, + const nb::DRef &uEC, + const nb::DRef &uEE) + { + Eigen::MatrixXN iB; + Eigen::VectorXI iF; + Eigen::MatrixXI iE; + igl::isolines_intrinsic(F,S,uE,EMAP,uEC,uEE,val,iB,iF,iE); + return std::make_tuple(iB,iF,iE); + } +} + +// Bind the wrapper to the Python module +void bind_isolines_intrinsic(nb::module_ &m) +{ + m.def( + "isolines_intrinsic", + &pyigl::isolines_intrinsic, + "F"_a, + "S"_a, + "vals"_a, +R"(Compute isolines of a scalar field on a triangle mesh intrinsically. + +See isolines.h for details. + +@param[in] F #F by 3 list of mesh triangle indices into some V +@param[in] S #S by 1 list of per-vertex scalar values +@param[in] vals #vals by 1 list of values to compute isolines for +@param[out] iB #iB by 3 list of barycentric coordinates so that + iV.row(i) = iB(i,0)*V.row(F(iFI(i,0)) + + iB(i,1)*V.row(F(iFI(i,1)) + + iB(i,2)*V.row(F(iFI(i,2)) +@param[out] iF #iB list of triangle indices for each row of iB (all + points will either lie on an edge or vertex: an arbitrary incident face + will be given). +@param[out] iE #iE by 2 list of edge indices into iB +@param[out] I #iE by 1 list of indices into vals indicating which value + each segment belongs to + +\see isolines, edge_crossings +)"); + m.def( + "isolines_intrinsic", + &pyigl::isolines_intrinsic_edges, + "F"_a, + "S"_a, + "val"_a, + "uE"_a, + "EMAP"_a, + "uEC"_a, + "uEE"_a, +R"(Compute isolines of a scalar field on a triangle mesh intrinsically. + +See isolines.h for details. + +@param[in] F #F by 3 list of mesh triangle indices into some V +@param[in] S #S by 1 list of per-vertex scalar values +@param[in] val scalar value to compute isoline at +@param[in] uE #uE by 2 list of unique undirected edges +@param[in] EMAP #F*3 list of indices into uE, mapping each directed edge to unique + undirected edge so that uE(EMAP(f+#F*c)) is the unique edge + corresponding to E.row(f+#F*c) +@param[in] uEC #uE+1 list of cumulative counts of directed edges sharing each + unique edge so the uEC(i+1)-uEC(i) is the number of directed edges + sharing the ith unique edge. +@param[in] uEE #E list of indices into E, so that the consecutive segment of + indices uEE.segment(uEC(i),uEC(i+1)-uEC(i)) lists all directed edges + sharing the ith unique edge. +@param[out] iB #iB by 3 list of barycentric coordinates so that + iV.row(i) = iB(i,0)*V.row(F(iFI(i,0)) + + iB(i,1)*V.row(F(iFI(i,1)) + + iB(i,2)*V.row(F(iFI(i,2)) +@param[out] iF #iB list of triangle indices for each row of iB (all + points will either lie on an edge or vertex: an arbitrary incident face + will be given). +@param[out] iE #iE by 2 list of edge indices into iB + +\see unique_edge_map +)"); +} + + + diff --git a/src/iterative_closest_point.cpp b/src/iterative_closest_point.cpp deleted file mode 100644 index ed9e3043..00000000 --- a/src/iterative_closest_point.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_iterative_closest_point = R"igl_Qu8mg5v7( - -Solve for the rigid transformation that places mesh X onto mesh Y using the -iterative closest point method. In particular, optimize: - -min ∫_X inf ‖x*R+t - y‖² dx -R∈SO(3) y∈Y -t∈R³ - -Typically optimization strategies include using Gauss Newton -("point-to-plane" linearization) and stochastic descent (sparse random -sampling each iteration). - -Parameters ----------- -VX #VX by 3 list of mesh X vertices -FX #FX by 3 list of mesh X triangle indices into rows of VX -VY #VY by 3 list of mesh Y vertices -FY #FY by 3 list of mesh Y triangle indices into rows of VY -num_samples number of random samples to use (larger --> more accurate, - but also more suceptible to sticking to local minimum) - -Returns -------- -R 3x3 rotation matrix so that (VX*R+t,FX) ~~ (VY,FY) -t 1x3 translation row vector - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(iterative_closest_point) -npe_doc(ds_iterative_closest_point) - -npe_arg(vx, dense_float, dense_double) -npe_arg(fx, dense_int32, dense_int64) -npe_arg(vy, npe_matches(vx)) -npe_arg(fy, npe_matches(fx)) -npe_arg(num_samples, int) -npe_arg(max_iters, int) - - -npe_begin_code() - assert_valid_3d_tri_mesh(vx, fx, "vx", "fx"); - assert_valid_3d_tri_mesh(vy, fy, "vy", "fy"); - - Eigen::MatrixXd vx_copy = vx.template cast(); - Eigen::MatrixXd vy_copy = vy.template cast(); - - Eigen::MatrixXi fx_copy = fx.template cast(); - Eigen::MatrixXi fy_copy = fy.template cast(); - - Eigen::Matrix3d r_copy; - Eigen::RowVector3d t_copy; - igl::iterative_closest_point(vx_copy, fx_copy, vy_copy, fy_copy, num_samples, max_iters, r_copy, t_copy); - - EigenDense r = r_copy.template cast(); - EigenDense t = t_copy.template cast(); - - return std::make_tuple(npe::move(r), npe::move(t)); - -npe_end_code() - - - - - - -//TODO requires the abb class - -// const char* ds_iterative_closest_point = R"igl_Qu8mg5v7( - -// Parameters -// ---------- - - -// Returns -// ------- - - -// See also -// -------- - - -// Notes -// ----- -// None - -// Examples -// -------- - -// Inputs: -// Ytree precomputed AABB tree for accelerating closest point queries -// NY #FY by 3 list of precomputed unit face normals -// )igl_Qu8mg5v7"; - -// npe_function(iterative_closest_point) -// npe_doc(ds_iterative_closest_point) - -// npe_arg(vx, dense_f32, dense_f64) -// npe_arg(fx, dense_f32, dense_f64) -// npe_arg(vy, dense_f32, dense_f64) -// npe_arg(fy, dense_f32, dense_f64) -// npe_arg(ytree, igl::AABB &) -// npe_arg(ny, dense_f32, dense_f64) -// npe_arg(num_samples, int) -// npe_arg(max_iters, int) - - -// npe_begin_code() - -// EigenDense r; -// EigenDense t; -// igl::iterative_closest_point(vx, fx, vy, fy, ytree, ny, num_samples, max_iters, r, t); -// return std::make_tuple(npe::move(r), npe::move(t)); - -// npe_end_code() - - diff --git a/src/kelvinlets.cpp b/src/kelvinlets.cpp new file mode 100644 index 00000000..0382ff9d --- /dev/null +++ b/src/kelvinlets.cpp @@ -0,0 +1,69 @@ +#include "default_types.h" +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto kelvinlets( + const nb::DRef &V, + const nb::DRef &x0, + const nb::DRef &f, + const nb::DRef &F, + const Numeric epsilon, + const Numeric falloff, + const igl::BrushType brushType) + { + Eigen::MatrixXN result; + igl::kelvinlets( + V, + x0, + f, + F, + igl::KelvinletParams(epsilon, falloff, brushType), + result); + return result; + } +} + +// Bind the wrapper to the Python module +void bind_kelvinlets(nb::module_ &m) +{ + nb::enum_(m, "BrushType") + .value("GRAB", igl::BrushType::GRAB) + .value("SCALE", igl::BrushType::SCALE) + .value("TWIST", igl::BrushType::TWIST) + .value("PINCH", igl::BrushType::PINCH) + .export_values(); + m.def( + "kelvinlets", + &pyigl::kelvinlets, + "V"_a, + "x0"_a, + "f"_a, + "F"_a, + "epsilon"_a =1.0, + "falloff"_a =1.0, + "brushType"_a=igl::BrushType::GRAB, +R"(Implements Pixar's Regularized Kelvinlets (Pixar Technical Memo #17-03): +Sculpting Brushes based on Fundamental Solutions of Elasticity, a technique +for real-time physically based volume sculpting of virtual elastic materials + +@param[in] V #V by dim list of input points in space +@param[in] x0 dim-vector of brush tip +@param[in] f dim-vector of brush force (translation) +@param[in] F dim by dim matrix of brush force matrix (linear) +@param[in] params parameters for the kelvinlet brush like brush radius, scale etc +@param[out] X #V by dim list of output points in space +)"); + +} + + + diff --git a/src/knn.cpp b/src/knn.cpp new file mode 100644 index 00000000..13817808 --- /dev/null +++ b/src/knn.cpp @@ -0,0 +1,63 @@ +#include "default_types.h" +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto knn( + const nb::DRef &P, + const nb::DRef &V, + const Integer k, + const std::vector> &point_indices, + const nb::DRef &CH, + const nb::DRef &CN, + const nb::DRef &W) + { + Eigen::MatrixXI I; + igl::knn(P,V,k,point_indices,CH,CN,W,I); + return I; + } +} + +// Bind the wrapper to the Python module +void bind_knn(nb::module_ &m) +{ + m.def( + "knn", + &pyigl::knn, + "P"_a, + "V"_a, + "k"_a, + "point_indices"_a, + "CH"_a, + "CN"_a, + "W"_a, + R"( +Given a 3D set of points P, an whole number k, and an octree +find the indicies of the k nearest neighbors for each point in P. +Note that each point is its own neighbor. + +The octree data structures used in this function are intended to be the +same ones output from igl::octree + +@param[in] P #P by 3 list of point locations +@param[in] V #V by 3 list of point locations for which may be neighbors +@param[in] k number of neighbors to find +@param[in] point_indices a vector of vectors, where the ith entry is a vector of + the indices into P that are the ith octree cell's points +@param[in] CH #OctreeCells by 8, where the ith row is the indices of + the ith octree cell's children +@param[in] CN #OctreeCells by 3, where the ith row is a 3d row vector + representing the position of the ith cell's center +@param[in] W #OctreeCells, a vector where the ith entry is the width + of the ith octree cell +@param[out] I #P by k list of k-nearest-neighbor indices into V +)"); +} diff --git a/src/lbs_matrix.cpp b/src/lbs_matrix.cpp deleted file mode 100644 index c36421ac..00000000 --- a/src/lbs_matrix.cpp +++ /dev/null @@ -1,220 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_lbs_matrix = R"igl_Qu8mg5v7( -LBS_MATRIX Linear blend skinning can be expressed by V' = M * T where V' is - a #V by dim matrix of deformed vertex positions (one vertex per row), M is a #V by (dim+1)*#T (composed of weights and rest positions) and T is a #T*(dim+1) by dim matrix of #T stacked transposed transformation matrices. - See equations (1) and (2) in "Fast Automatic Skinning Transformations" [Jacobson et al 2012] - -Parameters ----------- - V #V by dim list of rest positions - W #V+ by #T list of weights - - -Returns -------- -M #V by #T*(dim+1) - -See also --------- - - -Notes ------ -None - -Examples --------- - In MATLAB: - kron(ones(1,size(W,2)),[V ones(size(V,1),1)]).*kron(W,ones(1,size(V,2)+1)) -)igl_Qu8mg5v7"; - -npe_function(lbs_matrix) -npe_doc(ds_lbs_matrix) - -npe_arg(v, dense_float, dense_double) -npe_arg(w, dense_float, dense_double) - - -npe_begin_code() - assert_rows_match(v, w, "V", "W"); - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::MatrixXd w_copy = w.template cast(); - Eigen::MatrixXd m; - igl::lbs_matrix(v_copy, w_copy, m); - EigenDenseLike m_row_maj = m.template cast(); - return npe::move(m_row_maj); - -npe_end_code() - -//skpping because of wierd comments... - - -// #include - -// const char* ds_lbs_matrix_column = R"igl_Qu8mg5v7( - -// Parameters -// ---------- - - -// Returns -// ------- - - -// See also -// -------- - - -// Notes -// ----- -// None - -// Examples -// -------- - -// LBS_MATRIX construct a matrix that when multiplied against a column of -// affine transformation entries computes new coordinates of the vertices - -// I'm not sure it makes since that the result is stored as a sparse matrix. -// The number of non-zeros per row *is* dependent on the number of mesh -// vertices and handles. - -// Inputs: -// V #V by dim list of vertex rest positions -// W #V by #handles list of correspondence weights -// Output: -// M #V * dim by #handles * dim * (dim+1) matrix such that -// new_V(:) = LBS(V,W,A) = reshape(M * A,size(V)), where A is a column -// vectors formed by the entries in each handle's dim by dim+1 -// transformation matrix. Specifcally, A = -// reshape(permute(Astack,[3 1 2]),n*dim*(dim+1),1) -// or A = [Lxx;Lyx;Lxy;Lyy;tx;ty], and likewise for other dim -// if Astack(:,:,i) is the dim by (dim+1) transformation at handle i -// )igl_Qu8mg5v7"; - -// npe_function(lbs_matrix_column) -// npe_doc(ds_lbs_matrix_column) - -// npe_arg(v, Eigen::MatrixXd &) -// npe_arg(w, Eigen::MatrixXd &) - - -// npe_begin_code() - -// Eigen::SparseMatrix & m; -// igl::lbs_matrix_column(v, w, m); -// return npe::move(m); - -// npe_end_code() -// #include - -// const char* ds_lbs_matrix_column = R"igl_Qu8mg5v7( -// See lbs_matrix_column for the documentation. -// )igl_Qu8mg5v7"; - -// npe_function(lbs_matrix_column) -// npe_doc(ds_lbs_matrix_column) - -// npe_arg(v, Eigen::MatrixXd &) -// npe_arg(w, Eigen::MatrixXd &) - - -// npe_begin_code() - -// Eigen::MatrixXd & m; -// igl::lbs_matrix_column(v, w, m); -// return npe::move(m); - -// npe_end_code() -// #include - -// const char* ds_lbs_matrix_column = R"igl_Qu8mg5v7( - -// Parameters -// ---------- - - -// Returns -// ------- - - -// See also -// -------- - - -// Notes -// ----- -// None - -// Examples -// -------- - -// Same as LBS_MATRIX above but instead of giving W as a full matrix of weights -// (each vertex has #handles weights), a constant number of weights are given -// for each vertex. - -// Inputs: -// V #V by dim list of vertex rest positions -// W #V by k list of k correspondence weights per vertex -// WI #V by k list of k correspondence weight indices per vertex. Such that -// W(j,WI(i)) gives the ith most significant correspondence weight on vertex j -// Output: -// M #V * dim by #handles * dim * (dim+1) matrix such that -// new_V(:) = LBS(V,W,A) = reshape(M * A,size(V)), where A is a column -// vectors formed by the entries in each handle's dim by dim+1 -// transformation matrix. Specifcally, A = -// reshape(permute(Astack,[3 1 2]),n*dim*(dim+1),1) -// or A = [Lxx;Lyx;Lxy;Lyy;tx;ty], and likewise for other dim -// if Astack(:,:,i) is the dim by (dim+1) transformation at handle i - -// )igl_Qu8mg5v7"; - -// npe_function(lbs_matrix_column) -// npe_doc(ds_lbs_matrix_column) - -// npe_arg(v, Eigen::MatrixXd &) -// npe_arg(w, Eigen::MatrixXd &) -// npe_arg(wi, Eigen::MatrixXi &) - - -// npe_begin_code() - -// Eigen::SparseMatrix & m; -// igl::lbs_matrix_column(v, w, wi, m); -// return npe::move(m); - -// npe_end_code() -// #include - -// const char* ds_lbs_matrix_column = R"igl_Qu8mg5v7( -// See lbs_matrix_column for the documentation. -// )igl_Qu8mg5v7"; - -// npe_function(lbs_matrix_column) -// npe_doc(ds_lbs_matrix_column) - -// npe_arg(v, Eigen::MatrixXd &) -// npe_arg(w, Eigen::MatrixXd &) -// npe_arg(wi, Eigen::MatrixXi &) - - -// npe_begin_code() - -// Eigen::MatrixXd & m; -// igl::lbs_matrix_column(v, w, wi, m); -// return npe::move(m); - -// npe_end_code() - - diff --git a/src/line_segment_in_rectangle.cpp b/src/line_segment_in_rectangle.cpp deleted file mode 100644 index 11238d10..00000000 --- a/src/line_segment_in_rectangle.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - - - -#include - -const char *ds_line_segment_in_rectangle = R"igl_Qu8mg5v7( -Determine whether a line segment overlaps with a rectangle. - -Parameters ----------- -s source point of line segment -d dest point of line segment -A first corner of rectangle -B opposite corner of rectangle - -Returns -------- -Returns true if line segment is at all inside rectangle - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(line_segment_in_rectangle) -npe_doc(ds_line_segment_in_rectangle) - -npe_arg(s, dense_float, dense_double) -npe_arg(d, npe_matches(s)) -npe_arg(a, npe_matches(s)) -npe_arg(b, npe_matches(s)) - - -npe_begin_code() - assert_size_equals(s, 2, "s"); - assert_size_equals(d, 2, "d"); - assert_size_equals(a, 2, "a"); - assert_size_equals(b, 2, "b"); - - Eigen::Vector2d s_copy = s.template cast(); - Eigen::Vector2d d_copy = d.template cast(); - Eigen::Vector2d a_copy = a.template cast(); - Eigen::Vector2d b_copy = b.template cast(); - - return igl::line_segment_in_rectangle(s_copy, d_copy, a_copy, b_copy); - -npe_end_code() diff --git a/src/local_basis.cpp b/src/local_basis.cpp index 87673294..fc40c791 100644 --- a/src/local_basis.cpp +++ b/src/local_basis.cpp @@ -1,57 +1,40 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_local_basis = R"igl_Qu8mg5v7( -Compute a local orthogonal reference system for each triangle in the given mesh. - -Parameters ----------- -v : #v by 3 vertex array -f : #f by 3 array of mesh faces (must be triangles) - -Returns -------- -b1 : #f by 3 array, each vector is tangent to the triangle -b2 : #f by 3 array, each vector is tangent to the triangle and perpendicular to B1 -b3 : #f by 3 array, normal of the triangle - -See also --------- -adjacency_matrix - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(local_basis) -npe_doc(ds_local_basis) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - EigenDenseLike v_copy = v; - EigenDenseLike f_copy = f; - - EigenDenseLike b1; - EigenDenseLike b2; - EigenDenseLike b3; - igl::local_basis(v_copy, f_copy, b1, b2, b3); - return std::make_tuple(npe::move(b1), npe::move(b2), npe::move(b3)); - -npe_end_code() - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto local_basis( + const nb::DRef &V, + const nb::DRef &F) + { + Eigen::MatrixXN B1, B2, B3; + igl::local_basis(V, F, B1, B2, B3); + return std::make_tuple(B1, B2, B3); + } +} + +// Bind the wrapper to the Python module +void bind_local_basis(nb::module_ &m) +{ + m.def( + "local_basis", + &pyigl::local_basis, + "V"_a, + "F"_a, + R"(Compute a local orthogonal reference system for each triangle in the given mesh. + + @param[in] V #V by 3 eigen matrix of vertex positions + @param[in] F #F by 3 list of mesh faces (must be triangles) + @param[out] B1 #F by 3 matrix of tangent vectors for each triangle + @param[out] B2 #F by 3 matrix of tangent vectors perpendicular to B1 for each triangle + @param[out] B3 #F by 3 matrix of normal vectors for each triangle + )" + ); +} diff --git a/src/look_at.cpp b/src/look_at.cpp deleted file mode 100644 index 7535a5b3..00000000 --- a/src/look_at.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_look_at = R"igl_Qu8mg5v7( -Implementation of the deprecated gluLookAt function. - - -Parameters ----------- -eye 3-vector of eye position -center 3-vector of center reference point -up 3-vector of up vector - -Returns -------- -R 4x4 rotation matrix - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(look_at) -npe_doc(ds_look_at) - -npe_arg(eye, dense_float, dense_double) -npe_arg(center, npe_matches(eye)) -npe_arg(up, npe_matches(eye)) - - -npe_begin_code() - assert_size_equals(eye, 3, "eye"); - assert_size_equals(center, 3, "center"); - assert_size_equals(up, 3, "up"); - - Eigen::Vector3d eye_copy = eye.template cast(); - Eigen::Vector3d center_copy = center.template cast(); - Eigen::Vector3d up_copy = up.template cast(); - - Eigen::MatrixXd r_copy; - igl::look_at(eye_copy, center_copy, up_copy, r_copy); - Eigen::Matrix r = r_copy.template cast(); - return npe::move(r); - -npe_end_code() - - diff --git a/src/loop.cpp b/src/loop.cpp index 6a8cc9b2..5fa02482 100644 --- a/src/loop.cpp +++ b/src/loop.cpp @@ -1,108 +1,70 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -//TODO: __example -#include -#include -#include +#include "default_types.h" #include - -const char *ds_loop_mat = R"igl_Qu8mg5v7( - - LOOP Given the triangle mesh [V, F], where n_verts = V.rows(), computes - newV and a sparse matrix S s.t. [newV, newF] is the subdivided mesh where - newV = S*V. - -Parameters ----------- -n_verts an integer (number of mesh vertices) -F an m by 3 matrix of integers of triangle faces - -Returns -------- - -S a sparse matrix (will become the subdivision matrix) -newF a matrix containing the new faces - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(loop_subdivision_matrix) -npe_doc(ds_loop_mat) - -npe_arg(n_verts, int) -npe_arg(f, dense_int32, dense_int64) - -npe_begin_code() - assert_valid_tri_mesh_faces(f); - Eigen::SparseMatrix S; - EigenDenseLike nf; - igl::loop(n_verts, f, S, nf); - return std::make_tuple(npe::move(S), npe::move(nf)); -npe_end_code() - - - - - -const char *ds_loop = R"igl_Qu8mg5v7( - - LOOP Given the triangle mesh [V, F], where n_verts = V.rows(), computes - newV and a sparse matrix S s.t. [newV, newF] is the subdivided mesh where - newV = S*V. - -Parameters ----------- -V an n by 3 matrix of vertices -F an m by 3 matrix of integers of triangle faces -number_of_subdivs an integer that specifies how many subdivision steps to do - -Returns -------- - -NV a matrix containing the new vertices -NF a matrix containing the new faces - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(loop) -npe_doc(ds_loop) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_default_arg(number_of_subdivs, int, 1) - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - - EigenDenseLike nv; - EigenDenseLike nf; - - igl::loop(v, f, nv, nf, number_of_subdivs); - return std::make_tuple(npe::move(nv), npe::move(nf)); -npe_end_code() +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for the first overload of loop that computes S and newF + auto loop_matrix( + const nb::DRef &F, + int n_) + { + const auto n = n_? n_ : F.maxCoeff() + 1; + Eigen::SparseMatrix S; + Eigen::MatrixXI NF; + igl::loop(n, F, S, NF); + return std::make_tuple(S, NF); + } + + // Wrapper for the second overload of loop that returns NV and NF + auto loop( + const nb::DRef &V, + const nb::DRef &F, + int number_of_subdivs) + { + Eigen::MatrixXN NV; + Eigen::MatrixXI NF; + igl::loop(V, F, NV, NF, number_of_subdivs); + return std::make_tuple(NV, NF); + } + +} + +// Bind the wrapper to the Python module +void bind_loop(nb::module_ &m) +{ + m.def( + "loop_matrix", + &pyigl::loop_matrix, + "F"_a, + "n"_a = 0, + R"(Subdivide a mesh without moving vertices. Returns the subdivision matrix and new faces. + +@param[in] n_verts Number of mesh vertices +@param[in] F #F by 3 matrix of triangle faces +@return A tuple containing: + - S: Sparse subdivision matrix + - NF: Matrix of new faces)"); + + m.def( + "loop", + &pyigl::loop, + "V"_a, + "F"_a, + "number_of_subdivs"_a = 1, + R"(Subdivide a mesh without moving vertices using loop subdivision. Returns new vertices and faces. + +@param[in] V #V by dim matrix of mesh vertices +@param[in] F #F by 3 matrix of triangle faces +@param[in] number_of_subdivs Number of subdivisions (default is 1) +@return A tuple containing: + - NV: New vertex positions with original vertices at the top + - NF: Matrix of new face indices)"); + +} diff --git a/src/lscm.cpp b/src/lscm.cpp index 9b135ecd..51e21556 100644 --- a/src/lscm.cpp +++ b/src/lscm.cpp @@ -1,74 +1,52 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -//TODO: __example -#include -#include -#include +#include "default_types.h" #include - -const char* ds_lscm = R"igl_Qu8mg5v7( -Compute a Least-squares conformal map parametrization. - -Parameters ----------- -v : #v by 3 array of mesh vertex positions -f : #f by 3 array of mesh faces (must be triangles) -b : #b boundary indices into v -bc : #b by 2 list of boundary values - -Returns -------- -uv #v by 2 list of 2D mesh vertex positions in UV space - -See also --------- -None - -Notes ------ -Derived in "Intrinsic Parameterizations of Surface Meshes" [Desbrun et al. -2002] and "Least Squares Conformal Maps for Automatic Texture Atlas -Generation" [Lévy et al. 2002]), though this implementation follows the -derivation in: "Spectral Conformal Parameterization" [Mullen et al. 2008] -(note, this does **not** implement the Eigen-decomposition based method in -[Mullen et al. 2008], which is not equivalent. Input should be a manifold -mesh (also no unreferenced vertices) and "boundary" (fixed vertices) `b` -should contain at least two vertices per connected component. -Returns true only on solver success. - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(lscm) -npe_doc(ds_lscm) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(b, npe_matches(f)) -npe_arg(bc, npe_matches(v)) -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - assert_cols_equals(b, 1, "b"); - assert_rows_match(b, bc, "b", "bc"); - // assert_cols_match(f, bc, "f", "bc"); - assert_cols_equals(bc, 2, "bc"); - - // TODO: remove __copy - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::MatrixXi f_copy = f.template cast(); - Eigen::VectorXi b_copy = b.template cast(); - Eigen::MatrixXd bc_copy = bc.template cast(); - Eigen::MatrixXd uv; //TODO: major - bool success = igl::lscm(v_copy, f_copy, b_copy, bc_copy, uv); - EigenDenseFloat uv_row_major = uv; - return std::make_tuple(success, npe::move(uv_row_major)); - -npe_end_code() - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto lscm( + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &b, + const nb::DRef &bc) + { + Eigen::MatrixXN V_uv; + Eigen::SparseMatrix Q; + if(!igl::lscm(V, F, b, bc, V_uv, Q)) + { + throw std::runtime_error("igl::lscm failed"); + } + return std::make_tuple(V_uv, Q); + } +} + +// Bind the wrapper to the Python module +void bind_lscm(nb::module_ &m) +{ + m.def( + "lscm", + &pyigl::lscm, + "V"_a, + "F"_a, + "b"_a, + "bc"_a, + R"(Compute a Least-squares conformal map parametrization. + + @param[in] V #V by 3 list of mesh vertex positions + @param[in] F #F by 3 list of mesh faces (must be triangles) + @param[in] b #b list of boundary indices into V + @param[in] bc #b by 2 list of boundary values + @param[out] V_uv #V by 2 list of 2D mesh vertex positions in UV space + @param[out] Q #Vx2 by #Vx2 symmetric positive semi-definite matrix for computing LSCM energy + @return Tuple containing: + - V_uv: UV coordinates of vertices + - Q: Symmetric positive semi-definite matrix for LSCM energy)" + ); +} diff --git a/src/map_vertices_to_circle.cpp b/src/map_vertices_to_circle.cpp deleted file mode 100644 index a9c6c0ef..00000000 --- a/src/map_vertices_to_circle.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -//TODO: __example -#include -#include -#include -#include - -const char* ds_map_vertices_to_circle = R"igl_Qu8mg5v7( -Map the vertices whose indices are in a given boundary loop (bnd) on the unit circle with spacing proportional to the original boundary edge lengths. - -Parameters ----------- -v : #v by dim array of mesh vertex positions -b : #w list of vertex ids - -Returns -------- -uv : #w by 2 list of 2D positions on the unit circle for the vertices in b - -See also --------- -None - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(map_vertices_to_circle) -npe_doc(ds_map_vertices_to_circle) -npe_arg(v, dense_float, dense_double) -npe_arg(bnd, dense_int32, dense_int64) - -npe_begin_code() - - assert_nonzero_rows(v, "v"); - assert_nonzero_rows(bnd, "bnd"); - // TODO: remove __copy - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::VectorXi bnd_copy = bnd.template cast(); - Eigen::MatrixXd uv; - igl::map_vertices_to_circle(v_copy, bnd_copy, uv); - EigenDenseFloat uv_row_major = uv; - return npe::move(uv_row_major); - -npe_end_code() - - diff --git a/src/marching_cubes.cpp b/src/marching_cubes.cpp index c1965213..a196c612 100644 --- a/src/marching_cubes.cpp +++ b/src/marching_cubes.cpp @@ -1,87 +1,90 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Thomas -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - +#include "default_types.h" #include - -const char* ds_marching_cubes = R"igl_Qu8mg5v7( - -marching_cubes performs marching cubes reconstruction on a grid defined by values, and +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto marching_cubes( + const nb::DRef &S, + const nb::DRef &GV, + const unsigned nx, + const unsigned ny, + const unsigned nz, + const Numeric isovalue) + { + Eigen::MatrixXN V; + Eigen::MatrixXI F; + std::unordered_map E2V; + igl::marching_cubes(S,GV,nx,ny,nz,isovalue,V,F,E2V); + return std::make_tuple(V,F,E2V); + } + + auto marching_cubes_sparse( + const nb::DRef &S, + const nb::DRef &GV, + const nb::DRef &GI, + const Numeric isovalue) + { + Eigen::MatrixXN V; + Eigen::MatrixXI F; + igl::marching_cubes(S,GV,GI,isovalue,V,F); + return std::make_tuple(V,F); + } +} + +// Bind the wrapper to the Python module +void bind_marching_cubes(nb::module_ &m) +{ + + m.def( + "marching_cubes", + &pyigl::marching_cubes, + "S"_a, + "GV"_a, + "nx"_a, + "ny"_a, + "nz"_a, + "isovalue"_a=0, +R"(Performs marching cubes reconstruction on a grid defined by values, and points, and generates a mesh defined by vertices and faces -Parameters ----------- -S nx*ny*nz list of values at each grid corner i.e. S(x + y*xres + z*xres*yres) for corner (x,y,z) -GV nx*ny*nz by 3 array of corresponding grid corner vertex locations - points, ordered in x,y,z order: - points[index] = the point at (x,y,z) where : - x = (index % (xres -1), - y = (index / (xres-1)) %(yres-1), - z = index / (xres -1) / (yres -1) ). - where x,y,z index x, y, z dimensions - i.e. index = x + y*xres + z*xres*yres -nx resolutions of the grid in x dimension -ny resolutions of the grid in y dimension -nz resolutions of the grid in z dimension -isovalue the isovalue of the surface to reconstruct - - -Returns -------- -V #V by 3 list of mesh vertex positions -F #F by 3 list of mesh triangle indices into rows of V - -See also --------- - - -Notes ------ -None - -Examples --------- ->> import numpy as np ->> K = np.linspace( -1.0, 1.0, 64) ->> pts = np.array([[x,y,z] for x in K for y in K for z in K]) ->> S = signed_distance(pts, v, f) ->> v, f = marching_cubes(S, pts, nx, ny, nz, 0.0) - -)igl_Qu8mg5v7"; - -npe_function(marching_cubes) -npe_doc(ds_marching_cubes) -npe_arg(s, dense_float, dense_double) -npe_arg(gv, dense_float, dense_double) -npe_arg(nx, int) -npe_arg(ny, int) -npe_arg(nz, int) -npe_default_arg(isovalue, double, 0.0) - -npe_begin_code() - // input checks - assert_rows_match(s, gv, "S", "GV"); - assert_cols_equals(s, 1, "S"); - assert_cols_equals(gv, 3, "GV"); - - // vertices and faces of marched iso surface - Eigen::MatrixXd SV; - Eigen::MatrixXi SF; - - Eigen::MatrixXd s_copy = s.template cast(); - Eigen::MatrixXd gv_copy = gv.template cast(); - - igl::marching_cubes(s_copy, gv_copy, nx, ny, nz, isovalue, SV, SF); - - EigenDenseLike svRowMajor = SV.template cast(); - EigenDenseLike sfRowMajor = SF.template cast(); +@param[in] S nx*ny*nz list of values at each grid corner + i.e. S(x + y*xres + z*xres*yres) for corner (x,y,z) +@param[in] GV nx*ny*nz by 3 array of corresponding grid corner vertex locations +@param[in] nx resolutions of the grid in x dimension +@param[in] ny resolutions of the grid in y dimension +@param[in] nz resolutions of the grid in z dimension +@param[in] isovalue the isovalue of the surface to reconstruct +@param[out] V #V by 3 list of mesh vertex positions +@param[out] F #F by 3 list of mesh triangle indices into rows of V +@param[out] E2V map from edge key to index into rows of V + +# unpack keys into (i,j,v) index triplets +EV = np.array([[k & 0xFFFFFFFF, k >> 32, v] for k, v in E2V.items()], dtype=np.int64) +)"); + + m.def( + "marching_cubes", + &pyigl::marching_cubes_sparse, + "S"_a, + "GV"_a, + "GI"_a, + "isovalue"_a=0, +R"(Performs marching cubes reconstruction on a grid defined by values, and +points, and generates a mesh defined by vertices and faces - return std::make_tuple(npe::move(svRowMajor), npe::move(sfRowMajor)); -npe_end_code() \ No newline at end of file +@param[in] S #S list of scalar field values +@param[in] GV #S by 3 list of referenced grid vertex positions +@param[in] GI #GI by 8 list of grid corner indices into rows of GV +@param[in] isovalue the isovalue of the surface to reconstruct +@param[out] V #V by 3 list of mesh vertex positions +@param[out] F #F by 3 list of mesh triangle indices into rows of V)"); +} diff --git a/src/marching_tets.cpp b/src/marching_tets.cpp deleted file mode 100644 index c601090a..00000000 --- a/src/marching_tets.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_marching_tets = R"igl_Qu8mg5v7( -Performs the marching tetrahedra algorithm on a tet mesh defined by TV and -TT with scalar values defined at each vertex in TV. The output is a -triangle mesh approximating the isosurface coresponding to the value -isovalue. - -Parameters ----------- -TV #tet_vertices x 3 array -- The vertices of the tetrahedral mesh -TT #tets x 4 array -- The indexes of each tet in the tetrahedral mesh -S #tet_vertices x 1 array -- The values defined on each tet vertex -isovalue scalar -- The isovalue of the level set we want to compute - -Returns -------- -SV : #SV x 3 array -- The vertices of the output level surface mesh -SF : #SF x 3 array -- The face indexes of the output level surface mesh -J : #SF list of indices into TT revealing which tet each face comes from -BC : #SV x #TV list of barycentric coordinates so that SV = BC*TV - -See also --------- - - -Notes ------ -None - -Examples --------- -sv, sf, j, bc = igl.marching_tets(tv, tt, s, isovalue) - -)igl_Qu8mg5v7"; - -npe_function(marching_tets) -npe_doc(ds_marching_tets) - -npe_arg(TV, dense_float, dense_double) -npe_arg(TT, dense_int32, dense_int64) -npe_arg(S, npe_matches(TV)) -npe_arg(isovalue, double) - -npe_begin_code() - assert_valid_tet_mesh(TV, TT, "TV", "TT"); - assert_rows_match(TV, S, "TV", "S"); - assert_cols_equals(S, 1, "S"); - - EigenDenseLike SV; - EigenDenseLike SF; - Eigen::Matrix J; - EigenSparseLike BC; - igl::marching_tets(TV, TT, S, isovalue, SV, SF, J, BC); - return std::make_tuple(npe::move(SV), npe::move(SF), npe::move(J), npe::move(BC)); - -npe_end_code() diff --git a/src/massmatrix.cpp b/src/massmatrix.cpp index 25364e5f..f22e334d 100644 --- a/src/massmatrix.cpp +++ b/src/massmatrix.cpp @@ -1,68 +1,51 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_massmatrix = R"igl_Qu8mg5v7( -Constructs the mass (area) matrix for a given mesh (V,F). - -Parameters ----------- -v : #v by dim list of mesh vertex positions -f : #f by simplex_size list of mesh faces (must be triangles) -type : one of the following types: - -igl.MASSMATRIX_TYPE_BARYCENTRIC barycentric - -igl.MASSMATRIX_TYPE_VORONOI voronoi-hybrid (default) - -igl.MASSMATRIX_TYPE_FULL full (not implemented) - -Returns -------- -m : #v by #v mass matrix - -See also --------- -adjacency_matrix, cotmatrix, grad - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(massmatrix) -npe_doc(ds_massmatrix) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_default_arg(type, int, 3) -npe_begin_code() - - assert_valid_tet_or_tri_mesh_23d(v, f); - static_assert(int(igl::MASSMATRIX_TYPE_BARYCENTRIC) == 0, "MASSMATRIX enum changed!"); - static_assert(int(igl::MASSMATRIX_TYPE_VORONOI) == 1, "MASSMATRIX enum changed!"); - static_assert(int(igl::MASSMATRIX_TYPE_FULL) == 2, "MASSMATRIX enum changed!"); - static_assert(int(igl::MASSMATRIX_TYPE_DEFAULT) == 3, "MASSMATRIX enum changed!"); - static_assert(int(igl::NUM_MASSMATRIX_TYPES) == 4, "MASSMATRIX enum changed!"); - - if (type >= igl::NUM_MASSMATRIX_TYPES ) { - std::string errmsg = - std::string("Invalid enum for type should be in the range 0 to ") + - std::to_string(igl::NUM_MASSMATRIX_TYPES-1); - throw pybind11::value_error(errmsg); +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto massmatrix( + const nb::DRef &V, + const nb::DRef &F, + const igl::MassMatrixType type) + { + Eigen::SparseMatrixN M; + igl::massmatrix(V,F,type,M); + return M; } - - EigenSparseLike m; - igl::massmatrix(v, f, igl::MassMatrixType(type), m); - return npe::move(m); - -npe_end_code() - - +} + +// Bind the wrapper to the Python module +void bind_massmatrix(nb::module_ &m) +{ + m.def( + "massmatrix", + &pyigl::massmatrix, + "V"_a, + "F"_a, + "type"_a=igl::MASSMATRIX_TYPE_DEFAULT, +R"(Constructs the mass (area) matrix 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[in] type one of the following ints: + MASSMATRIX_TYPE_BARYCENTRIC barycentric {default for tetrahedra} + MASSMATRIX_TYPE_VORONOI voronoi-hybrid {default for triangles} + MASSMATRIX_TYPE_FULL full +@param[out] M #V by #V mass matrix + +\see cotmatrix)" + ); +} diff --git a/src/massmatrix_intrinsic.cpp b/src/massmatrix_intrinsic.cpp index 87f89d3d..e758646d 100644 --- a/src/massmatrix_intrinsic.cpp +++ b/src/massmatrix_intrinsic.cpp @@ -1,81 +1,45 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - - - +#include "default_types.h" #include - -const char *ds_massmatrix_intrinsic = R"igl_Qu8mg5v7( - -Constructs the mass (area) matrix for a given mesh (V,F). - - -Parameters ----------- -l #l by simplex_size list of mesh edge lengths -F #F by simplex_size list of mesh elements (triangles or tetrahedra) -type one of the following ints: - -igl.MASSMATRIX_TYPE_BARYCENTRIC barycentric - -igl.MASSMATRIX_TYPE_VORONOI voronoi-hybrid (default) - -igl.MASSMATRIX_TYPE_FULL full (not implemented) - -Returns -------- -M #V by #V mass matrix - -See also --------- -adjacency_matrix - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(massmatrix_intrinsic) -npe_doc(ds_massmatrix_intrinsic) - -npe_arg(l, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_default_arg(type, int, 1) - - -npe_begin_code() - assert_rows_match(l, f, "l", "f"); - assert_cols_equals(l, 3, "l"); - assert_valid_tri_mesh_faces(f); - - static_assert(int(igl::MASSMATRIX_TYPE_BARYCENTRIC) == 0, "MASSMATRIX enum changed!"); - static_assert(int(igl::MASSMATRIX_TYPE_VORONOI) == 1, "MASSMATRIX enum changed!"); - static_assert(int(igl::MASSMATRIX_TYPE_FULL) == 2, "MASSMATRIX enum changed!"); - static_assert(int(igl::MASSMATRIX_TYPE_DEFAULT) == 3, "MASSMATRIX enum changed!"); - static_assert(int(igl::NUM_MASSMATRIX_TYPES) == 4, "MASSMATRIX enum changed!"); - - if (type >= igl::NUM_MASSMATRIX_TYPES ) +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto massmatrix_intrinsic( + const nb::DRef &l, + const nb::DRef &F, + const igl::MassMatrixType type) { - std::string errmsg = - std::string("Invalid enum for type should be in the range 0 to ") + - std::to_string(igl::NUM_MASSMATRIX_TYPES - 1); - throw pybind11::value_error(errmsg); + Eigen::SparseMatrixN M; + igl::massmatrix_intrinsic(l,F,type,M); + return M; } - - Eigen::SparseMatrix m; - igl::massmatrix_intrinsic(l, f, igl::MassMatrixType(type), m); - return npe::move(m); - -npe_end_code() +} + +// Bind the wrapper to the Python module +void bind_massmatrix_intrinsic(nb::module_ &m) +{ + m.def( + "massmatrix_intrinsic", + &pyigl::massmatrix_intrinsic, + "l"_a, + "F"_a, + "type"_a=igl::MASSMATRIX_TYPE_DEFAULT, +R"( +Constructs the mass matrix for a given +mesh with faces F and edge lengths l. + +@param[in] l #F by 3 list of (half-)edge lengths +@param[in] F #F by 3 list of face indices into some (not necessarily + determined/embedable) list of vertex positions V. It is assumed #V == + F.maxCoeff()+1 +@param[out] L #V by #V sparse Laplacian matrix + +\see massmatrix, intrinsic_delaunay_massmatrix)"); +} diff --git a/src/matlab_format.cpp b/src/matlab_format.cpp new file mode 100644 index 00000000..26fc3d46 --- /dev/null +++ b/src/matlab_format.cpp @@ -0,0 +1,93 @@ +#include "default_types.h" +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + std::string matlab_format_dense(const nb::DRef &M, const std::string &name = "") + { + std::ostringstream oss; + oss << igl::matlab_format(M, name); + return oss.str(); + } + + std::string matlab_format_sparse(const Eigen::SparseMatrix &S, const std::string &name = "") + { + return igl::matlab_format(S, name); + } + + std::string matlab_format_scalar(Numeric v, const std::string &name = "") + { + return igl::matlab_format(v, name); + } + + std::string matlab_format_index(const nb::DRef &M, const std::string &name = "") + { + return igl::matlab_format_index(M, name); + } + + std::string matlab_format_index_vector(const nb::DRef &M, const std::string &name = "") + { + return igl::matlab_format_index(M, name); + } + + std::string matlab_format_dense_vector(const nb::DRef &M, const std::string &name = "") + { + std::ostringstream oss; + oss << igl::matlab_format(M, name); + return oss.str(); + } +} + +// Bind the wrapper to the Python module +void bind_matlab_format(nb::module_ &m) +{ + m.def( + "matlab_format", + &pyigl::matlab_format_dense, + "M"_a, + "name"_a = "", + R"(Format a dense matrix for MATLAB-style output.)"); + + m.def( + "matlab_format", + &pyigl::matlab_format_dense_vector, + "M"_a, + "name"_a = "", + R"(Format a dense matrix for MATLAB-style output.)"); + + m.def( + "matlab_format", + &pyigl::matlab_format_sparse, + "S"_a, + "name"_a = "", + R"(Format a sparse matrix for MATLAB-style output in IJV format.)"); + + m.def( + "matlab_format", + nb::overload_cast(&pyigl::matlab_format_scalar), + "v"_a, + "name"_a = "", + R"(Format a double scalar for MATLAB-style output.)"); + + m.def( + "matlab_format_index", + &pyigl::matlab_format_index, + "M"_a, + "name"_a = "", + R"(Format a matrix for MATLAB-style output with 1-based indexing.)"); + + m.def( + "matlab_format_index", + &pyigl::matlab_format_index_vector, + "M"_a, + "name"_a = "", + R"(Format a matrix for MATLAB-style output with 1-based indexing.)"); +} diff --git a/src/min_quad_with_fixed.cpp b/src/min_quad_with_fixed.cpp index 16c2fbda..bfd40ecd 100644 --- a/src/min_quad_with_fixed.cpp +++ b/src/min_quad_with_fixed.cpp @@ -1,96 +1,113 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -//TODO: __example remove __copy - -#include -#include -#include - - - - - - +#include "default_types.h" #include - -const char *ds_min_quad_with_fixed = R"igl_Qu8mg5v7( - -MIN_QUAD_WITH_FIXED Minimize a quadratic energy of the form -trace( 0.5*Z'*A*Z + Z'*B + constant ) -subject to -Z(known,:) = Y, and -Aeq*Z = Beq - -Parameters ----------- -A n by n matrix of quadratic coefficients -B n by 1 column of linear coefficients -known list of indices to known rows in Z -Y list of fixed values corresponding to known rows in Z -Aeq m by n list of linear equality constraint coefficients -Beq m by 1 list of linear equality constraint constant values -is_A_pd flag specifying whether A(unknown,unknown) is positive definite - - -Returns -------- -Z n by k solution - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(min_quad_with_fixed) -npe_doc(ds_min_quad_with_fixed) -//TODO missing npe_dense_like -npe_arg(A, sparse_float, sparse_double) -// npe_arg(B, npe_dense_like(A)) -npe_arg(B, dense_float, dense_double) -npe_arg(known, dense_int32, dense_int64) -npe_arg(Y, npe_matches(B)) -npe_arg(Aeq, npe_matches(A)) -npe_arg(Beq, npe_matches(B)) -npe_arg(is_A_pd, bool) - - -npe_begin_code() - - assert_nonzero_rows(A, "A"); - if(Aeq.size() > 0) - assert_cols_match(A, Aeq, "A", "Aeq"); - assert_rows_match(A, B, "A", "B"); - assert_cols_match(B, Y, "B", "Y"); - if (Beq.size() > 0) - assert_cols_match(B, Beq, "B", "Beq"); - assert_rows_match(Aeq, Beq, "Aeq", "Beq"); - - Eigen::SparseMatrix A_copy = A.template cast(); - Eigen::SparseMatrix Aeq_copy = Aeq.template cast(); - - Eigen::MatrixXd B_copy = B.template cast(); - Eigen::MatrixXd Y_copy = Y.template cast(); - Eigen::MatrixXd Beq_copy = Beq.template cast(); - - Eigen::MatrixXd sol; - - bool ok = igl::min_quad_with_fixed(A_copy, B_copy, known, Y_copy, Aeq_copy, Beq_copy, is_A_pd, sol); - // Eigen::Matrix sol_row_major = sol; - EigenDenseLike sol_row_major = sol.template cast < typename npe_Matrix_B::Scalar >(); - return std::make_pair(ok, npe::move(sol_row_major)); - -npe_end_code() +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto min_quad_with_fixed( + const Eigen::SparseMatrixN &A, + const nb::DRef &B, + const nb::DRef &known, + const nb::DRef &Y, + const Eigen::SparseMatrixN &Aeq, + const nb::DRef &Beq, + const bool pd) + { + Eigen::MatrixXN Z; + bool success = igl::min_quad_with_fixed(A, B, known, Y, Aeq, Beq, pd, Z); + + if (!success) + { + throw std::runtime_error("min_quad_with_fixed: Optimization failed."); + } + + return Z; + } + + void min_quad_with_fixed_precompute( + const Eigen::SparseMatrixN &A, + const nb::DRef &known, + const Eigen::SparseMatrixN &Aeq, + const bool pd, + igl::min_quad_with_fixed_data &data) + { + if(!igl::min_quad_with_fixed_precompute(A, known, Aeq, pd, data)) + { + throw std::runtime_error("min_quad_with_fixed: Precomputation failed."); + } + } + + auto min_quad_with_fixed_solve( + const igl::min_quad_with_fixed_data &data, + const nb::DRef &B, + const nb::DRef &Y, + const nb::DRef &Beq) + { + Eigen::MatrixXN Z; + if(!igl::min_quad_with_fixed_solve(data, B, Y, Beq, Z)) + { + throw std::runtime_error("min_quad_with_fixed: Optimization failed."); + } + return Z; + } + +} + +// Bind the wrapper to the Python module +void bind_min_quad_with_fixed(nb::module_ &m) +{ + nb::class_>(m, "min_quad_with_fixed_data") + .def(nb::init<>()) + ; + + m.def("min_quad_with_fixed_solve", + &pyigl::min_quad_with_fixed_solve, + "data"_a, + "B"_a=Eigen::MatrixXN(), + "Y"_a=Eigen::MatrixXN(), + "Beq"_a=Eigen::MatrixXN(), + R"(Solve the precomputed convex quadratic optimization problem.)") + ; + m.def("min_quad_with_fixed_precompute", + &pyigl::min_quad_with_fixed_precompute, + "A"_a = Eigen::SparseMatrixN(), + "known"_a = Eigen::VectorXI() , + "Aeq"_a = Eigen::SparseMatrixN(), + "pd"_a = true , + "data"_a, + R"(Precompute convex quadratic optimization problem.)") + ; + + m.def( + "min_quad_with_fixed", + &pyigl::min_quad_with_fixed, + "A"_a = Eigen::SparseMatrixN(), + "B"_a = Eigen::MatrixXN() , + "known"_a = Eigen::VectorXI() , + "Y"_a = Eigen::MatrixXN() , + "Aeq"_a = Eigen::SparseMatrixN(), + "Beq"_a = Eigen::MatrixXN() , + "pd"_a = true , +R"(Minimize a convex quadratic energy subject to fixed values and linear equality constraints. + +The function minimizes trace(0.5 * Z' * A * Z + Z' * B) subject to: + Z(known,:) = Y, and + Aeq * Z = Beq + +@param[in] A n by n matrix of quadratic coefficients +@param[in] B n by k matrix of linear coefficients +@param[in] known list of indices to known rows in Z +@param[in] Y n by k matrix of fixed values corresponding to known rows in Z +@param[in] Aeq m by n matrix of linear equality constraint coefficients +@param[in] Beq m by k matrix of linear equality constraint target values +@param[in] pd flag specifying whether A(unknown,unknown) is positive definite +@param[out] Z solution matrix that minimizes the objective under constraints +@return Z solution matrix +)"); +} diff --git a/src/module.cpp b/src/module.cpp new file mode 100644 index 00000000..8fe7fb66 --- /dev/null +++ b/src/module.cpp @@ -0,0 +1,12 @@ +#include +namespace nb = nanobind; + +// generated by cmake +#include "BINDING_DECLARATIONS.in" + +NB_MODULE(pyigl_core, m) { + m.doc() = "libigl python bindings"; + // generated by cmake +#include "BINDING_INVOCATIONS.in" +} + diff --git a/src/moments.cpp b/src/moments.cpp index 9a7d8189..e81e0987 100644 --- a/src/moments.cpp +++ b/src/moments.cpp @@ -1,42 +1,44 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Alec Jacobson -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include +#include +#include +#include +#include -const char *ds_moments = R"igl_Qu8mg5v7( -Computes the moments of mass for a solid object bound by a triangle mesh. - -Parameters ----------- - V #V by dim list of rest domain positions - F #F by 3 list of triangle indices into V -Returns -------- - m0 zeroth moment of mass, total signed volume of solid. - m1 first moment of mass, center of mass times total mass - m2 second moment of mass, moment of inertia with center of mass as reference point +namespace nb = nanobind; +using namespace nb::literals; -)igl_Qu8mg5v7"; +namespace pyigl +{ + auto moments( + const nb::DRef &V, + const nb::DRef &F) + { + Numeric m0; + Eigen::VectorXN m1; + Eigen::MatrixXN m2; + igl::moments(V,F,m0,m1,m2); + return std::make_tuple(m0,m1,m2); + } -npe_function(moments) -npe_doc(ds_moments) +} -npe_arg(V, dense_float, dense_double) -npe_arg(F, dense_int32, dense_int64) +// Bind the wrapper to the Python module +void bind_moments(nb::module_ &m) +{ + m.def( + "moments", + &pyigl::moments, + "V"_a, + "F"_a, +R"(Computes the moments of mass for a solid object bound by a triangle mesh. -npe_begin_code() - assert_valid_3d_tri_mesh(V, F); - Eigen::Matrix m1; - Eigen::Matrix m2; - double m0; - igl::moments(V, F, m0, m1, m2); - return std::make_tuple(m0,npe::move(m1),npe::move(m2)); -npe_end_code() +@param[in] V #V by 3 list of rest domain positions +@param[in] F #F by 3 list of triangle indices into V +@param[out] m0 zeroth moment of mass, total signed volume of solid. +@param[out] m1 first moment of mass, center of mass (centroid) times total mass +@param[out] m2 second moment of mass, moment of inertia with center of mass as reference point +\see centroid +)"); +} diff --git a/src/mvc.cpp b/src/mvc.cpp deleted file mode 100644 index 77f9c236..00000000 --- a/src/mvc.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - - - -#include - -const char *ds_mvc = R"igl_Qu8mg5v7( -MEAN VALUE COORDINATES - -Parameters ----------- -V #V x dim list of vertex positions (dim = 2 or dim = 3) -C #C x dim list of polygon vertex positions in counter-clockwise order (dim = 2 or dim = 3) - -Returns -------- -W weights, #V by #C matrix of weights - -See also --------- - - -Notes ------ -Known Bugs: implementation is listed as "Broken" - -Examples --------- -W = mvc(V,C) - -)igl_Qu8mg5v7"; - -npe_function(mvc) -npe_doc(ds_mvc) - -npe_arg(v, dense_float, dense_double) -npe_arg(c, npe_matches(v)) - - -npe_begin_code() - assert_nonzero_rows(v, "v"); - assert_nonzero_rows(c, "c"); - assert_cols_match(v, c, "v", "c"); - - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::MatrixXd c_copy = c.template cast(); - - Eigen::MatrixXd w_copy; - igl::mvc(v_copy, c_copy, w_copy); - EigenDenseLike w = w_copy.template cast(); - return npe::move(w); -npe_end_code() - - diff --git a/src/noop.cpp b/src/noop.cpp new file mode 100644 index 00000000..ef70f252 --- /dev/null +++ b/src/noop.cpp @@ -0,0 +1,37 @@ +#include "default_types.h" +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + + +namespace pyigl +{ + auto noop( + const nb::DRef &N, + const nb::DRef &I, + const Eigen::SparseMatrixN &SN, + const Eigen::SparseMatrixI &SI) + { + return nb::none(); + } +} + +// Bind the wrapper to the Python module +void bind_noop(nb::module_ &m) +{ + m.def( + "noop", + &pyigl::noop, + "N"_a=Eigen::MatrixXN(), + "I"_a=Eigen::MatrixXI(), + "SN"_a=Eigen::SparseMatrixN(), + "SI"_a=Eigen::SparseMatrixI(), +R"(Dummy function that does nothing. Useful for timing bindings overhead.)" + ); +} + + diff --git a/src/normal_derivative.cpp b/src/normal_derivative.cpp deleted file mode 100644 index bbefd98a..00000000 --- a/src/normal_derivative.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_normal_derivative = R"igl_Qu8mg5v7( -NORMAL_DERIVATIVE Computes the directional derivative **normal** to - **all** (half-)edges of a triangle mesh (not just boundary edges). These - are integrated along the edge: they're the per-face constant gradient dot - the rotated edge vector (unit rotated edge vector for direction then - magnitude for integration). - -Parameters ----------- - V #V by dim list of mesh vertex positions - F #F by 3|4 list of triangle|tetrahedron indices into V - -Returns -------- - DD #F*3|4 by #V sparse matrix representing operator to compute - directional derivative with respect to each facet of each element. - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(normal_derivative) -npe_doc(ds_normal_derivative) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_tet_or_tri_mesh(v, f); - //inside there are plenty of MatrixXi... - Eigen::MatrixXi f_copy = f.template cast(); - - EigenSparseLike dd; - igl::normal_derivative(v, f_copy, dd); - return npe::move(dd); - -npe_end_code() - - diff --git a/src/octree.cpp b/src/octree.cpp new file mode 100644 index 00000000..98cea257 --- /dev/null +++ b/src/octree.cpp @@ -0,0 +1,61 @@ +#include "default_types.h" +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto octree(const nb::DRef &P) + { + Eigen::MatrixXI CH; + Eigen::MatrixXN CN; + Eigen::VectorXN W; + std::vector> point_indices; + igl::octree(P,point_indices,CH,CN,W); + return std::make_tuple(point_indices,CH,CN,W); + } +} + +// Bind the wrapper to the Python module +void bind_octree(nb::module_ &m) +{ + m.def( + "octree", + &pyigl::octree, + "P"_a, + R"(Given a set of 3D points P, generate data structures for a pointerless +octree. Each cell stores its points, children, center location and width. +Our octree is not dense. We use the following rule: if the current cell +has any number of points, it will have all 8 children. A leaf cell will +have -1's as its list of child indices. + +We use a binary numbering of children. Treating the parent cell's center +as the origin, we number the octants in the following manner: +The first bit is 1 iff the octant's x coordinate is positive +The second bit is 1 iff the octant's y coordinate is positive +The third bit is 1 iff the octant's z coordinate is positive + +For example, the octant with negative x, positive y, positive z is: +110 binary = 6 decimal + +@param[in] P #P by 3 list of point locations +@param[out] point_indices a vector of vectors, where the ith entry is a + vector of the indices into P that are the ith octree cell's points +@param[out] CH #OctreeCells by 8, where the ith row is the indices of the + ith octree cell's children +@param[out] CN #OctreeCells by 3, where the ith row is a 3d row vector + representing the position of the ith cell's center +@param[out] W #OctreeCells, a vector where the ith entry is the width of + the ith octree cell)" +); +} + + + + diff --git a/src/offset_surface.cpp b/src/offset_surface.cpp deleted file mode 100644 index 55a1e1cb..00000000 --- a/src/offset_surface.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -//TODO: __example -// TODO: remove __copy -// copy is necessary since the winding number only supports matrices - -#include -#include -#include -#include - -const char *ds_offset_surface = R"igl_Qu8mg5v7( -Compute a triangulated offset surface using marching cubes on a grid of -signed distance values from the input triangle mesh. - -Parameters ----------- - V #V by 3 list of mesh vertex positions - F #F by 3 list of mesh triangle indices into V - isolevel iso level to extract (signed distance: negative inside) - s number of grid cells along longest side (controls resolution) - signed_distance_type type of signing to use one of SIGNED_DISTANCE_TYPE_PSEUDONORMAL, SIGNED_DISTANCE_TYPE_WINDING_NUMBER, SIGNED_DISTANCE_TYPE_DEFAULT, SIGNED_DISTANCE_TYPE_UNSIGNED, SIGNED_DISTANCE_TYPE_FAST_WINDING_NUMBER - -Returns -------- - SV #SV by 3 list of output surface mesh vertex positions - SF #SF by 3 list of output mesh triangle indices into SV - GV #GV=side(0)*side(1)*side(2) by 3 list of grid cell centers - side list of number of grid cells in x, y, and z directions - So #GV by 3 list of signed distance values _near_ `isolevel` ("far" from `isolevel` these values are incorrect) - -See also --------- - - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(offset_surface) -npe_doc(ds_offset_surface) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(isolevel, double) -npe_arg(s, int) -npe_arg(signed_distance_type, int) -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - - // Alec: I don't understand why all these copies are needed here. - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::MatrixXi f_copy = f.template cast(); - - Eigen::MatrixXd sv_copy; - Eigen::MatrixXi sf_copy; - Eigen::MatrixXd gv_copy; - Eigen::MatrixXi side_copy; - Eigen::MatrixXd so_copy; - - igl::offset_surface(v_copy, f_copy, isolevel, s, igl::SignedDistanceType(signed_distance_type), sv_copy, sf_copy, gv_copy, side_copy, so_copy); - - EigenDenseLike sv = sv_copy.template cast(); - EigenDenseLike sf = sf_copy.template cast(); - EigenDenseLike gv = gv_copy.template cast(); - EigenDenseLike side = side_copy.template cast(); - EigenDenseLike so = so_copy.template cast(); - - return std::make_tuple(npe::move(sv), npe::move(sf), npe::move(gv), npe::move(side), npe::move(so)); - -npe_end_code() diff --git a/src/on_boundary.cpp b/src/on_boundary.cpp new file mode 100644 index 00000000..251cc8ff --- /dev/null +++ b/src/on_boundary.cpp @@ -0,0 +1,38 @@ +#include "default_types.h" +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto on_boundary( + const nb::DRef &T) + { + Eigen::VectorX I; // Output: boundary indicator for each element + Eigen::Matrix C; // Output: boundary facets per element + + igl::on_boundary(T, I, C); + + return std::make_tuple(I, C); + } +} + +// Bind the wrapper to the Python module +void bind_on_boundary(nb::module_ &m) +{ + m.def( + "on_boundary", + &pyigl::on_boundary, + "T"_a, +R"(Determine boundary facets of mesh elements stored in T. + +@param[in] T m by 3|4 list of triangle or tetrahedron indices, where m is the number of elements +@return Tuple containing: + - I: m-length vector of bools indicating if each element is on the boundary + - C: m by 3|4 matrix of bools indicating if each opposite facet is on the boundary)"); +} diff --git a/src/orient_halfedges.cpp b/src/orient_halfedges.cpp new file mode 100644 index 00000000..25069c33 --- /dev/null +++ b/src/orient_halfedges.cpp @@ -0,0 +1,41 @@ +#include "default_types.h" +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto orient_halfedges( + const nb::DRef &F) + { + Eigen::MatrixXI E,oE; + igl::orient_halfedges(F,E,oE); + return std::make_tuple(E,oE); + } +} + +// Bind the wrapper to the Python module +void bind_orient_halfedges(nb::module_ &m) +{ + m.def( + "orient_halfedges", + &pyigl::orient_halfedges, + "F"_a, +R"(Orients halfedges for a triangle mesh, assigning them to a unique edge. + +@param[in] F #F by 3 input mesh connectivity +@param[out] E #F by 3 a mapping from each halfedge to each edge +@param[out] oE #F by 3 the orientation (e.g., -1 or 1) of each halfedge compared to + the orientation of the actual edge. Every edge appears positively oriented + exactly once. + +\see unique_simplices)" + ); +} + + diff --git a/src/orient_outward.cpp b/src/orient_outward.cpp deleted file mode 100644 index 3a4ca6c2..00000000 --- a/src/orient_outward.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_orient_outward = R"igl_Qu8mg5v7( - - Orient each component (identified by C) of a mesh (V,F) so the normals on - average point away from the patch's centroid. - -Parameters ----------- - V #V by 3 list of vertex positions - F #F by 3 list of triangle indices - C #F list of components (output of orientable_patches) - -Returns -------- - FF #F by 3 list of new triangle indices such that FF(~I,:) = F(~I,:) and - FF(I,:) = fliplr(F(I,:)) (OK if &FF = &F) - I max(C)+1 list of whether face has been flipped - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(orient_outward) -npe_doc(ds_orient_outward) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(c, npe_matches(f)) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - assert_rows_match(f, c, "f", "c"); - //problem with if(&FF != &F) - //problem with DerivedV as type - EigenDenseLike v_copy = v; - EigenDenseLike f_copy = f; - Eigen::Matrix c_copy = c; - - EigenDenseLike ff; - Eigen::Matrix i_copy; - igl::orient_outward(v_copy, f_copy, c_copy, ff, i_copy); - EigenDenseLike i = i_copy; - return std::make_tuple(npe::move(ff), npe::move(i)); - -npe_end_code() - - diff --git a/src/orientable_patches.cpp b/src/orientable_patches.cpp deleted file mode 100644 index 2db872c1..00000000 --- a/src/orientable_patches.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char* ds_orientable_patches = R"igl_Qu8mg5v7( -Compute connected components of facets connected by manifold edges. - -Parameters ----------- -f : n by dim array of face ids - -Returns -------- -A tuple (c, A) where c is an array of component ids (starting with 0) -and A is a #f x #f adjacency matri - -See also --------- -components - -Notes ------ -Known bugs: This will detect a moebius strip as a single patch (manifold, non-orientable) and also non-manfiold, yet orientable patches. - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(orientable_patches) -npe_doc(ds_orientable_patches) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - - assert_valid_tri_mesh_faces(f); - EigenDenseLike c; - EigenSparseLike A; - igl::orientable_patches(f, c, A); - return std::make_tuple(npe::move(c), npe::move(A)); - -npe_end_code() - diff --git a/src/oriented_facets.cpp b/src/oriented_facets.cpp index 6f74c734..22708160 100644 --- a/src/oriented_facets.cpp +++ b/src/oriented_facets.cpp @@ -1,55 +1,33 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_oriented_facets = R"igl_Qu8mg5v7( -Determines all 'directed [facets](https:en.wikipedia.org/wiki/Simplex#Elements)' of a given set -of simplicial elements. For a manifold triangle mesh, this computes all half-edges. -For a manifold tetrahedral mesh, this computes all half-faces. - -Parameters ----------- -f : #F by simplex_size list of simplices - - -Returns -------- -#E : by simplex_size-1 list of half-edges/facets - -See also --------- -edges - -Notes ------ -This is not the same as igl::edges because this includes every -directed edge including repeats (meaning interior edges on a surface will -show up once for each direction and non-manifold edges may appear more than -once for each direction). - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(oriented_facets) -npe_doc(ds_oriented_facets) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - - assert_valid_tet_or_tri_mesh_faces(f); - EigenDenseLike e; - igl::oriented_facets(f, e); - return npe::move(e); - -npe_end_code() - - +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for oriented_facets + Eigen::MatrixXI oriented_facets(const nb::DRef &F) + { + Eigen::MatrixXI E; + igl::oriented_facets(F, E); + return E; + } +} + +// Bind the wrapper to the Python module +void bind_oriented_facets(nb::module_ &m) +{ + m.def( + "oriented_facets", + &pyigl::oriented_facets, + "F"_a, +R"(Determines all directed facets of a given set of simplicial elements. + +@param[in] F #F by simplex_size matrix of simplices +@return E #E by (simplex_size-1) matrix of directed facets, such that each row in E + represents a facet opposite to a vertex in F)"); +} diff --git a/src/outer_element.cpp b/src/outer_element.cpp deleted file mode 100644 index dadebbcf..00000000 --- a/src/outer_element.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - -#include - -const char *ds_outer_vertex = R"igl_Qu8mg5v7( - -Find a vertex that is reachable from infinite without crossing any faces. -Such vertex is called "outer vertex." - -Precondition: The input mesh must have all self-intersection resolved and -no duplicated vertices. See cgal::remesh_self_intersections.h for how to -obtain such input. - -Parameters ----------- -V #V by 3 list of vertex positions -F #F by 3 list of triangle indices into V -I #I list of facets to consider - -Returns -------- -v_index index of outer vertex -A #A list of facets incident to the outer vertex - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(outer_vertex) -npe_doc(ds_outer_vertex) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(i, npe_matches(f)) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - assert_cols_equals(i, 1, "i"); - - int v_index; - EigenDenseLike a; - igl::outer_vertex(v, f, i, v_index, a); - return std::make_tuple(v_index, npe::move(a)); - -npe_end_code() - -const char *ds_outer_edge = R"igl_Qu8mg5v7( - -Find an edge that is reachable from infinity without crossing any faces. - Such edge is called "outer edge." - - Precondition: The input mesh must have all self-intersection resolved and - no duplicated vertices. The correctness of the output depends on the fact - that there is no edge overlap. See cgal::remesh_self_intersections.h for - how to obtain such input. - -Parameters ----------- -V #V by 3 list of vertex positions -F #F by 3 list of triangle indices into V -I #I list of facets to consider - -Returns -------- - -v1 index of the first end point of outer edge -v2 index of the second end point of outer edge -A #A list of facets incident to the outer edge - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(outer_edge) -npe_doc(ds_outer_edge) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(i, npe_matches(f)) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - assert_cols_equals(i, 1, "i"); - - int v1; - int v2; - EigenDenseLike a; - igl::outer_edge(v, f, i, v1, v2, a); - return std::make_tuple(v1, v2, npe::move(a)); - -npe_end_code() - - -const char *ds_outer_facet = R"igl_Qu8mg5v7( - -Find a facet that is reachable from infinity without crossing any faces. -Such facet is called "outer facet." - -Precondition: The input mesh must have all self-intersection resolved. I.e -there is no duplicated vertices, no overlapping edge and no intersecting -faces (the only exception is there could be topologically duplicated faces). -See cgal::remesh_self_intersections.h for how to obtain such input. - -Parameters ----------- -V #V by 3 list of vertex positions -F #F by 3 list of triangle indices into V -N #N by 3 list of face normals -I #I list of facets to consider - -Returns -------- -f Index of the outer facet. -flipped true iff the normal of f points inwards. - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(outer_facet) -npe_doc(ds_outer_facet) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(n, npe_matches(v)) -npe_arg(i, npe_matches(f)) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - assert_cols_equals(n, 3, "n"); - assert_rows_match(f, n, "f", "n"); - assert_cols_equals(i, 1, "i"); - - int face; - bool flipped; - igl::outer_facet(v, f, n, i, face, flipped); - return std::make_tuple(face, flipped); - -npe_end_code() - - diff --git a/src/partition.cpp b/src/partition.cpp deleted file mode 100644 index 8088083a..00000000 --- a/src/partition.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - - - -#include - -const char *ds_partition = R"igl_Qu8mg5v7( - -PARTITION partition vertices into groups based on each -vertex's vector: vertices with similar coordinates (close in -space) will be put in the same group. - -Parameters ----------- -W #W by dim coordinate matrix -k desired number of groups default is dim - -Returns -------- -G #W list of group indices (1 to k) for each vertex, such that vertex i is assigned to group G(i) -S k list of seed vertices -D #W list of squared distances for each vertex to it's corresponding closest seed - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(partition) -npe_doc(ds_partition) - -npe_arg(w, dense_float, dense_double) -npe_arg(k, int) - - -npe_begin_code() - assert_nonzero_rows(w, "w"); - - Eigen::MatrixXd w_copy = w.template cast(); - Eigen::Matrix g_copy; - Eigen::Matrix s_copy; - Eigen::Matrix d_copy; - igl::partition(w_copy, k, g_copy, s_copy, d_copy); - - EigenDenseInt g = g_copy.template cast(); - EigenDenseInt s = s_copy.template cast(); - EigenDenseLike d = d_copy.template cast(); - - return std::make_tuple(npe::move(g), npe::move(s), npe::move(d)); - -npe_end_code() - - diff --git a/src/path_to_edges.cpp b/src/path_to_edges.cpp deleted file mode 100644 index 9be57637..00000000 --- a/src/path_to_edges.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - -#include - -const char *ds_path_to_edges = R"igl_Qu8mg5v7( - - -Given a path as an ordered list of N>=2 vertex indices I[0], I[1], ..., I[N-1] - construct a list of edges [[I[0],I[1]], [I[1],I[2]], ..., [I[N-2], I[N-1]]] - connecting each sequential pair of vertices. - -Parameters ----------- -I #I list of vertex indices -make_loop bool If true, include an edge connecting I[N-1] to I[0] - -Returns -------- -E #I-1 by 2 list of edges - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(path_to_edges) -npe_doc(ds_path_to_edges) - -npe_arg(i, dense_int32, dense_int64) -npe_default_arg(make_loop, bool, false) - - -npe_begin_code() - EigenDense i_copy = i; - - EigenDense e; - igl::path_to_edges(i_copy, e, make_loop); - return npe::move(e); - -npe_end_code() diff --git a/src/per_corner_normals.cpp b/src/per_corner_normals.cpp deleted file mode 100644 index 14db23ee..00000000 --- a/src/per_corner_normals.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_per_corner_normals = R"igl_Qu8mg5v7( - -Compute vertex normals via vertex position list, face list - -Parameters ----------- -V #V by 3 eigen Matrix of mesh vertex 3D positions -F #F by 3 eigen Matrix of face (triangle) indices -corner_threshold threshold in degrees on sharp angles - -Returns -------- -CN #F*3 by 3 eigen Matrix of mesh vertex 3D normals, where the normal for corner F(i,j) is at CN(i*3+j,:) - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(per_corner_normals) -npe_doc(ds_per_corner_normals) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(corner_threshold, double) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - EigenDenseLike n; - igl::per_corner_normals(v, f, corner_threshold, n); - return npe::move(n); - -npe_end_code() - diff --git a/src/per_edge_normals.cpp b/src/per_edge_normals.cpp deleted file mode 100644 index 82e0fbea..00000000 --- a/src/per_edge_normals.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -//TODO: __miss __example - -#include -#include -#include -#include - -const char* ds_per_edge_normals = R"igl_Qu8mg5v7( - -Compute face normals via vertex position list, face list - -Parameters ----------- -V #V by 3 eigen Matrix of mesh vertex 3D positions -F #F by 3 eigen Matrix of face (triangle) indices -weight weighting type -FN #F by 3 matrix of 3D face normals per face - - -Returns -------- -N #2 by 3 matrix of mesh edge 3D normals per row -E #E by 2 matrix of edge indices per row -EMAP #E by 1 matrix of indices from all edges to E - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(per_edge_normals) -npe_doc(ds_per_edge_normals) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_default_arg(weight, int , 0) -npe_arg(fn, npe_matches(v)) - - -npe_begin_code() - - assert_valid_3d_tri_mesh(v, f); - EigenDenseLike n; - EigenDenseLike e; - Eigen::Matrix emap; - igl::per_edge_normals(v, f, igl::PerEdgeNormalsWeightingType(weight), fn, n, e, emap); - return std::make_tuple(npe::move(n), npe::move(e), npe::move(emap)); - -npe_end_code() - - diff --git a/src/per_face_normals.cpp b/src/per_face_normals.cpp index 4734ff50..c9908b5a 100644 --- a/src/per_face_normals.cpp +++ b/src/per_face_normals.cpp @@ -1,60 +1,72 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __miss - -#include -#include -#include +#include "default_types.h" #include - -const char* ds_per_face_normals = R"igl_Qu8mg5v7( - -Compute face normals via vertex position list, face list - -Parameters ----------- -V #V by 3 eigen Matrix of mesh vertex 3D positions -F #F by 3 eigen Matrix of face (triangle) indices -Z 3 vector normal given to faces with degenerate normal. - -Returns -------- -N #F by 3 eigen Matrix of mesh face (triangle) 3D normals - -See also --------- - - -Notes ------ -None - -Examples --------- -Give degenerate faces (1/3,1/3,1/3)^0.5 -per_face_normals(V,F,Vector3d(1,1,1).normalized(),N); - -)igl_Qu8mg5v7"; - -npe_function(per_face_normals) -npe_doc(ds_per_face_normals) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(z, npe_matches(v)) - - -npe_begin_code() - - assert_valid_3d_tri_mesh(v, f); - EigenDenseLike n; - igl::per_face_normals(v, f, z, n); - return npe::move(n); - -npe_end_code() - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for per_face_normals function + auto per_face_normals_VF( + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &Z) + { + Eigen::MatrixXN N; + igl::per_face_normals(V,F,Z,N); + return N; + } + // Wrapper for per_face_normals function + auto per_face_normals_VIC( + const nb::DRef &V, + const nb::DRef &I, + const nb::DRef &C) + { + Eigen::MatrixXN N; + Eigen::MatrixXN VV; + Eigen::MatrixXI FF; + Eigen::VectorXI J; + igl::per_face_normals(V,I,C,N,VV,FF,J); + return std::make_tuple(N,VV,FF,J); + } +} + +// Bind the wrapper to the Python module +void bind_per_face_normals(nb::module_ &m) +{ + m.def( + "per_face_normals", + &pyigl::per_face_normals_VF, + "V"_a, + "F"_a, + "Z"_a=Eigen::RowVectorXN(), +R"(Compute face normals via vertex position list, face list + +@param[in] V #V by 3 eigen Matrix of mesh vertex 3D positions +@param[in] F #F by 3 eigen Matrix of face (triangle) indices +@param[in] Z 3 vector normal given to faces with degenerate normal. +@param[out] N #F by 3 eigen Matrix of mesh face (triangle) 3D normals)" + ); + m.def( + "per_face_normals", + &pyigl::per_face_normals_VIC, + "V"_a, + "I"_a, + "C"_a, +R"(Compute face normals via vertex position list, polygon stream + +@param[in] V #V by 3 eigen Matrix of mesh vertex 3D positions +@param[in] I #I vectorized list of polygon corner indices into rows of some matrix V +@param[in] C #polygons+1 list of cumulative polygon sizes so that C(i+1)-C(i) = size of + the ith polygon, and so I(C(i)) through I(C(i+1)-1) are the indices of + the ith polygon +@param[out] N #F by 3 eigen Matrix of mesh face (triangle) 3D normals +@param[out] VV #I+#polygons by 3 list of auxiliary triangle mesh vertex positions +@param[out] FF #I by 3 list of triangle indices into rows of VV +@param[out] J #I list of indices into original polygons)" + ); +} diff --git a/src/per_vertex_attribute_smoothing.cpp b/src/per_vertex_attribute_smoothing.cpp deleted file mode 100644 index 97ab9d7a..00000000 --- a/src/per_vertex_attribute_smoothing.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example - -#include -#include -#include -#include - -const char* ds_per_vertex_attribute_smoothing = R"igl_Qu8mg5v7( - -Smooth vertex attributes using uniform Laplacian - -Parameters ----------- -Ain #V by #A eigen Matrix of mesh vertex attributes (each vertex has #A attributes) -F #F by 3 eigne Matrix of face (triangle) indices - -Returns -------- -Aout #V by #A eigen Matrix of mesh vertex attributes - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(per_vertex_attribute_smoothing) -npe_doc(ds_per_vertex_attribute_smoothing) - -npe_arg(ain, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_3d_tri_mesh(ain, f); - // remove __copy - Eigen::MatrixXd ain_copy = ain.template cast(); - Eigen::MatrixXd aout; - igl::per_vertex_attribute_smoothing(ain_copy, f, aout); - EigenDenseFloat aout_row_major = aout; - return npe::move(aout_row_major); - -npe_end_code() - - diff --git a/src/per_vertex_normals.cpp b/src/per_vertex_normals.cpp index ecc78c50..99352b24 100644 --- a/src/per_vertex_normals.cpp +++ b/src/per_vertex_normals.cpp @@ -1,62 +1,72 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include +#include +#include +#include +#include +#include +#include -const char* ds_per_vertex_normals = R"igl_Qu8mg5v7( -Compute vertex normals via vertex position list, face list. +namespace nb = nanobind; +using namespace nb::literals; -Parameters ----------- -v : #v by 3 array of mesh vertex 3D positions -f : #f by 3 array of face (triangle) indices -weighting : Weighting type, one of the following - -igl.PER_VERTEX_NORMALS_WEIGHTING_TYPE_UNIFORM uniform influence - -igl.PER_VERTEX_NORMALS_WEIGHTING_TYPE_AREA area weighted - -igl.PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE angle weighted +namespace pyigl +{ + auto per_vertex_normals( + const nb::DRef &V, + const nb::DRef &F, + const igl::PerVertexNormalsWeightingType weight_enum) + { + Eigen::MatrixXN N; + igl::per_vertex_normals(V, F, weight_enum, N); + return N; + } + auto per_vertex_normals_FN( + const nb::DRef &V, + const nb::DRef &F, + const igl::PerVertexNormalsWeightingType weight_enum, + const nb::DRef &FN) + { + Eigen::MatrixXN N; + igl::per_vertex_normals(V, F, weight_enum, FN, N); + return N; + } +} -Returns -------- -n #v by 3 array of mesh vertex 3D normals - -See also --------- -per_face_normals, per_edge_normals - -Notes ------ -None - -Examples --------- -# Mesh in (v, f) ->>> n = per_vertex_normals(v, f) -)igl_Qu8mg5v7"; - -npe_function(per_vertex_normals) -npe_doc(ds_per_vertex_normals) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_default_arg(weighting, int, 0) - -npe_begin_code() - - assert_valid_3d_tri_mesh(v, f); - static_assert(int(igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_UNIFORM) == 0, "PerVertexNormalWeightingType enum changed!"); - static_assert(int(igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_AREA) == 1, "PerVertexNormalWeightingType enum changed!"); - static_assert(int(igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE) == 2, "PerVertexNormalWeightingType enum changed!"); - - EigenDenseLike n; - igl::per_vertex_normals(v, f, igl::PerVertexNormalsWeightingType(weighting), n); - return npe::move(n); - -npe_end_code() +// Bind the wrapper to the Python module +void bind_per_vertex_normals(nb::module_ &m) +{ + nb::enum_(m, "PerVertexNormalsWeightingType") + .value("PER_VERTEX_NORMALS_WEIGHTING_TYPE_UNIFORM", igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_UNIFORM) + .value("PER_VERTEX_NORMALS_WEIGHTING_TYPE_AREA", igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_AREA) + .value("PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE", igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE) + .value("PER_VERTEX_NORMALS_WEIGHTING_TYPE_DEFAULT", igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_DEFAULT) + .export_values() + ; + m.def( + "per_vertex_normals", + &pyigl::per_vertex_normals, + "V"_a, + "F"_a, + "weighting"_a = igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_DEFAULT, +R"(Compute per-vertex normals with optional weighting and face normals. +@param[in] V #V by 3 matrix of vertex positions +@param[in] F #F by 3 matrix of face indices +@param[in] weighting Optional string for weighting type ("uniform", "area", "angle", or "default") +@return N #V by 3 matrix of vertex normals)"); + m.def( + "per_vertex_normals", + &pyigl::per_vertex_normals_FN, + "V"_a, + "F"_a, + "weighting"_a = igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_DEFAULT, + "FN"_a, +R"(Compute per-vertex normals with optional weighting and face normals. +@param[in] V #V by 3 matrix of vertex positions +@param[in] F #F by 3 matrix of face indices +@param[in] weighting Optional string for weighting type ("uniform", "area", "angle", or "default") +@param[in] FN Optional #F by 3 matrix of face normals +@return N #V by 3 matrix of vertex normals)"); +} diff --git a/src/piecewise_constant_winding_number.cpp b/src/piecewise_constant_winding_number.cpp deleted file mode 100644 index f5b31b2e..00000000 --- a/src/piecewise_constant_winding_number.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __miss __example - -#include -#include -#include -#include - - - - - - - -#include - -const char* ds_piecewise_constant_winding_number = R"igl_Qu8mg5v7( - -PIECEWISE_CONSTANT_WINDING_NUMBER Determine if a given mesh induces a - piecewise constant winding number field: Is this mesh valid input to solid - set operations. **Assumes** that `(V,F)` contains no self-intersections - (including degeneracies and co-incidences). If there are co-planar and - co-incident vertex placements, a mesh could _fail_ this combinatorial test - but still induce a piecewise-constant winding number _geometrically_. For - example, consider a hemisphere with boundary and then pinch the boundary - "shut" along a line segment. The **_bullet-proof_** check is to first - resolve all self-intersections in `(V,F) -> (SV,SF)` (i.e. what the - `igl::copyleft::cgal::piecewise_constant_winding_number` overload does). - -Parameters ----------- -F #F by 3 list of triangle indices into some (abstract) list of - vertices V - -Returns -------- -Returns true if the mesh _combinatorially_ induces a piecewise constant - winding number field. - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(piecewise_constant_winding_number) -npe_doc(ds_piecewise_constant_winding_number) - -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_tri_mesh_faces(f); - return igl::piecewise_constant_winding_number(f); - -npe_end_code() - - diff --git a/src/planarize_quad_mesh.cpp b/src/planarize_quad_mesh.cpp deleted file mode 100644 index 9c57cc92..00000000 --- a/src/planarize_quad_mesh.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char* ds_planarize_quad_mesh = R"igl_Qu8mg5v7( -Planarize a quad mesh. - -Parameters ----------- -v : #v by 3 array of mesh vertex 3D positions -f : #f by 4 array of face (quad) indices -max_iter : maximum numbers of iterations -threshold : minimum allowed threshold for non-planarity - -Returns -------- -out : #v by 3 array of planar mesh vertex 3D positions - -See also --------- -None - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(planarize_quad_mesh) -npe_doc(ds_planarize_quad_mesh) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(max_iter, int) -npe_arg(threshold, double) - - -npe_begin_code() - assert_valid_3d_quad_mesh(v, f); - EigenDenseLike v_copy = v; - EigenDenseLike f_copy = f; - - EigenDenseLike out; - igl::planarize_quad_mesh(v_copy, f_copy, max_iter, threshold, out); - return npe::move(out); - -npe_end_code() - - diff --git a/src/point_in_circle.cpp b/src/point_in_circle.cpp deleted file mode 100644 index 573e7b80..00000000 --- a/src/point_in_circle.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - -const char *ds_point_in_circle = R"igl_Qu8mg5v7( - Determine if 2d point is in a circle - -Parameters ----------- -qx x-coordinate of query point -qy y-coordinate of query point -cx x-coordinate of circle center -cy y-coordinate of circle center -r radius of circle - -Returns -------- -Returns true if query point is in circle, false otherwise - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(point_in_circle) -npe_doc(ds_point_in_circle) - -npe_arg(qx, double) -npe_arg(qy, double) -npe_arg(cx, double) -npe_arg(cy, double) -npe_arg(r, double) - - -npe_begin_code() - - bool in = igl::point_in_circle(qx, qy, cx, cy, r); - return in; - -npe_end_code() - - diff --git a/src/point_mesh_squared_distance.cpp b/src/point_mesh_squared_distance.cpp index c54dd959..6d930682 100644 --- a/src/point_mesh_squared_distance.cpp +++ b/src/point_mesh_squared_distance.cpp @@ -1,89 +1,53 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - - - +#include "default_types.h" #include - -const char *ds_point_mesh_squared_distance = R"igl_Qu8mg5v7( - Compute distances from a set of points P to a triangle mesh (V,F) - -Parameters ----------- -P #P by 3 list of query point positions -V #V by 3 list of vertex positions -Ele #Ele by (3|2|1) list of (triangle|edge|point) indices - -Returns -------- -sqrD #P list of smallest squared distances -I #P list of primitive indices corresponding to smallest distances -C #P by 3 list of closest points - -See also --------- - - -Notes ------ -Known bugs: This only computes distances to given primitivess. So +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for point_mesh_squared_distance function + auto point_mesh_squared_distance( + const nb::DRef &P, + const nb::DRef &V, + const nb::DRef &Ele) + { + Eigen::VectorXN sqrD; + Eigen::VectorXI I; + Eigen::MatrixXN C; + igl::point_mesh_squared_distance(P, V, Ele, sqrD, I, C); + return std::make_tuple(sqrD, I, C); + } +} + +// Bind the wrapper to the Python module +void bind_point_mesh_squared_distance(nb::module_ &m) +{ + m.def( + "point_mesh_squared_distance", + &pyigl::point_mesh_squared_distance, + "P"_a, + "V"_a, + "Ele"_a, +R"(Compute distances from a set of points P to a triangle mesh (V,F) + +@param[in] P #P by 3 list of query point positions +@param[in] V #V by 3 list of vertex positions +@param[in] Ele #Ele by (3|2|1) list of (triangle|edge|point) indices +@param[out] sqrD #P list of smallest squared distances +@param[out] I #P list of primitive indices corresponding to smallest distances +@param[out] C #P by 3 list of closest points + +\bug This only computes distances to given primitives. So unreferenced vertices are ignored. However, degenerate primitives are handled correctly: triangle [1 2 2] is treated as a segment [1 2], and triangle [1 1 1] is treated as a point. So one _could_ add extra combinatorially degenerate rows to Ele for all unreferenced vertices to also get distances to points. - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(point_mesh_squared_distance) -npe_doc(ds_point_mesh_squared_distance) - -npe_arg(p, dense_float, dense_double) -npe_arg(v, dense_float, dense_double) -npe_arg(ele, dense_int32, dense_int64) - - -npe_begin_code() - assert_nonzero_rows(p, "P"); - assert_nonzero_rows(v, "V"); - assert_nonzero_rows(ele, "Ele"); - - Eigen::MatrixXd p_copy; - if(p.size() == 2 || p.size() == 3) - { - p_copy.resize(1, p.size()); - for(int i = 0; i < p.size(); ++i) - p_copy(0, i) = p(i, 0); - } - else - p_copy = p.template cast(); - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::MatrixXi ele_copy = ele.template cast(); - - Eigen::MatrixXd sqr_d; - Eigen::MatrixXi i; - Eigen::MatrixXd c; - - igl::point_mesh_squared_distance(p_copy, v_copy, ele_copy, sqr_d, i, c); - EigenDenseLike sqr_d_row_maj = sqr_d.template cast(); - EigenDenseLike i_row_maj = i.template cast(); - EigenDenseLike c_row_maj = c.template cast(); -return std::make_tuple(npe::move(sqr_d_row_maj), npe::move(i_row_maj), npe::move(c_row_maj)); - -npe_end_code() - +)"); +} diff --git a/src/point_simplex_squared_distance.cpp b/src/point_simplex_squared_distance.cpp deleted file mode 100644 index 8a6b24e2..00000000 --- a/src/point_simplex_squared_distance.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_point_simplex_squared_distance = R"igl_Qu8mg5v7( - - Determine squared distance from a point to linear simplex. - Also return barycentric coordinate of closest point. - -Parameters ----------- - -p d-long query point -V #V by d list of vertices -Ele #Ele by ss<=d+1 list of simplex indices into V -i index into Ele of simplex - -Returns -------- -sqr_d squared distance of Ele(i) to p -c closest point on Ele(i) -b barycentric coordinates of closest point on Ele(i) - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(point_simplex_squared_distance) -npe_doc(ds_point_simplex_squared_distance) - -npe_arg(p, dense_float, dense_double) -npe_arg(v, npe_matches(p)) -npe_arg(ele, dense_int32, dense_int64) -npe_arg(i, int) - - -npe_begin_code() - assert_valid_tet_or_tri_mesh(v, ele); - assert_size_equals(p, v.cols(), "p"); - - double sqr_d; - Eigen::Matrix c; - Eigen::Matrix b; - Eigen::Matrix p_copy = p; - if(p.size() == 2) - igl::point_simplex_squared_distance<2>(p_copy, v, ele, i, sqr_d, c, b); - else - igl::point_simplex_squared_distance<3>(p_copy, v, ele, i, sqr_d, c, b); - return std::make_tuple(sqr_d, npe::move(c), npe::move(b)); - -npe_end_code() - - diff --git a/src/polar_dec.cpp b/src/polar_dec.cpp deleted file mode 100644 index c8cdc3e0..00000000 --- a/src/polar_dec.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_polar_dec = R"igl_Qu8mg5v7( - -Computes the polar decomposition (R,T) of a matrix A - - -Parameters ----------- -A 3 by 3 matrix to be decomposed - -Returns -------- - -R 3 by 3 orthonormal matrix part of decomposition -T 3 by 3 stretch matrix part of decomposition - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(polar_dec) -npe_doc(ds_polar_dec) - -npe_arg(a, dense_float, dense_double) - -npe_begin_code() - assert_rows_equals(a, 3, "a"); - assert_cols_equals(a, 3, "a"); - - EigenDense a_copy = a; - EigenDense r; - EigenDense t; - igl::polar_dec(a_copy, r, t); - return std::make_tuple(npe::move(r), npe::move(t)); - -npe_end_code() - - diff --git a/src/polar_svd.cpp b/src/polar_svd.cpp new file mode 100644 index 00000000..cdf7f1f7 --- /dev/null +++ b/src/polar_svd.cpp @@ -0,0 +1,39 @@ +#include "default_types.h" +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto polar_svd( + const nb::DRef &A, + bool include_reflections) + { + Eigen::MatrixXN R, T, U, V; + Eigen::VectorXN S; + igl::polar_svd(A, include_reflections, R, T, U, S, V); + return std::make_tuple(std::move(R), std::move(T), std::move(U), std::move(S), std::move(V)); + } +} + +// Bind the wrapper to the Python module +void bind_polar_svd(nb::module_ &m) +{ + m.def( + "polar_svd", + &pyigl::polar_svd, + "A"_a, + "include_reflections"_a = false, + R"(Computes the polar decomposition of a NxN matrix A using SVD. + + @param[in] A NxN matrix to be decomposed + @param[in] includeReflections Whether to allow R to be a reflection (default is False) + @param[in] return_U If true, include the left-singular vectors U in the output (default is False) + @param[in] return_S If true, include the singular values S in the output (default is False) + @param[in] return_V If true, include the right-singular vectors V in the output (default is False) + @return Tuple containing (R,T) and selected outputs in the order specified by the flags)"); +} diff --git a/src/polygon_corners.cpp b/src/polygon_corners.cpp new file mode 100644 index 00000000..ad620577 --- /dev/null +++ b/src/polygon_corners.cpp @@ -0,0 +1,66 @@ +#include "default_types.h" +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto polygon_corners(const std::vector > & P) + { + Eigen::VectorXI I, C; + igl::polygon_corners(P,I,C); + return std::make_tuple(I,C); + } + + auto polygon_corners_kgon( + const nb::DRef &Q) + { + Eigen::VectorXI I, C; + igl::polygon_corners(Q,I,C); + return std::make_tuple(I,C); + } +} + +// Bind the wrapper to the Python module +void bind_polygon_corners(nb::module_ &m) +{ + m.def( + "polygon_corners", + &pyigl::polygon_corners, + "P"_a, +R"(Convert a list-of-lists polygon mesh faces representation to list of +polygon corners and sizes + +@param[in] P #P list of lists of vertex indices into rows of some matrix V +@param[out] I #I vectorized list of polygon corner indices into rows of some matrix V +@param[out] C #P+1 list of cumulative polygon sizes so that C(i+1)-C(i) = size of + the ith polygon, and so I(C(i)) through I(C(i+1)-1) are the indices of + the ith polygon + +)"); + m.def( + "polygon_corners", + &pyigl::polygon_corners_kgon, + "Q"_a, +R"( +\brief Convert a pure k-gon list of polygon mesh indices to list of +polygon corners and sizes +@param[in] Q #Q by k list of polygon indices (ith row is a k-gon, unless Q(i,j) = + -1 then it's a j-gon) +@param[out] I #I vectorized list of polygon corner indices into rows of some matrix V +@param[out] C #P+1 list of cumulative polygon sizes so that C(i+1)-C(i) = size of + the ith polygon, and so I(C(i)) through I(C(i+1)-1) are the indices of + the ith polygon + +)"); +} + + + + diff --git a/src/polygons_to_triangles.cpp b/src/polygons_to_triangles.cpp new file mode 100644 index 00000000..436c024d --- /dev/null +++ b/src/polygons_to_triangles.cpp @@ -0,0 +1,49 @@ +#include "default_types.h" +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto polygons_to_triangles( + const nb::DRef &I, + const nb::DRef &C) + { + Eigen::MatrixXI F; + Eigen::VectorXI J; + igl::polygons_to_triangles(I,C,F,J); + return std::make_tuple(F,J); + } + +} + +// Bind the wrapper to the Python module +void bind_polygons_to_triangles(nb::module_ &m) +{ + m.def( + "polygons_to_triangles", + &pyigl::polygons_to_triangles, + "I"_a, + "C"_a, +R"(Given a polygon mesh, trivially triangulate each polygon with a fan. This +purely combinatorial triangulation will work well for convex/flat polygons +and degrade otherwise. + +@param[in] I #I vectorized list of polygon corner indices into rows of some matrix V +@param[in] C #polygons+1 list of cumulative polygon sizes so that C(i+1)-C(i) = + size of the ith polygon, and so I(C(i)) through I(C(i+1)-1) are the + indices of the ith polygon +@param[out] F #F by 3 list of triangle indices into rows of V +@param[out] J #F list of indices into 0:#P-1 of corresponding polygon + +)"); +} + + + + diff --git a/src/principal_curvature.cpp b/src/principal_curvature.cpp index 56e3f88a..3eef0df1 100644 --- a/src/principal_curvature.cpp +++ b/src/principal_curvature.cpp @@ -1,63 +1,54 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include +#include +#include +#include +#include #include - -const char* ds_principal_curvature = R"igl_Qu8mg5v7( -Compute the principal curvature directions and magnitude of the given triangle mesh. - -Parameters ----------- -v : vertex array of size #V by 3 -f : face index array #F by 3 list of mesh faces (must be triangles) -radius : controls the size of the neighbourhood used, 1 = average edge length (default: 5) -use_k_ring : (default: True) - -Returns -------- -pd1 : #v by 3 maximal curvature direction for each vertex -pd2 : #v by 3 minimal curvature direction for each vertex -pv1 : #v by 1 maximal curvature value for each vertex -pv2 : #v by 1 minimal curvature value for each vertex - -See also --------- -average_onto_faces, average_onto_vertices - -Notes ------ -This function has been developed by: Nikolas De Giorgis, Luigi Rocca and Enrico Puppo. -The algorithm is based on: Efficient Multi-scale Curvature and Crease Estimation -Daniele Panozzo, Enrico Puppo, Luigi Rocca GraVisMa, 2010 - -Examples --------- -# Mesh in (v, f) ->>> pd1, pd2, pv1, pv2 = principal_curvature(v, f) -)igl_Qu8mg5v7"; - -npe_function(principal_curvature) -npe_doc(ds_principal_curvature) -npe_arg(v, dense_double, dense_float) -npe_arg(f, dense_int32, dense_int64) -npe_default_arg(radius, int, 5) -npe_default_arg(use_k_ring, bool, true) -npe_begin_code() - - assert_valid_3d_tri_mesh(v, f); - EigenDenseLike pd1, pd2, pv1, pv2; - - igl::principal_curvature(v, f, pd1, pd2, pv1, pv2, radius, use_k_ring); - return std::make_tuple(npe::move(pd1), npe::move(pd2), npe::move(pv1), npe::move(pv2)); - -npe_end_code() - - +#include "default_types.h" + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto principal_curvature( + const nb::DRef &V, + const nb::DRef &F, + unsigned radius = 5, + bool useKring = true) + { + Eigen::MatrixXN PD1; // Maximal curvature directions + Eigen::MatrixXN PD2; // Minimal curvature directions + Eigen::VectorXN PV1; // Maximal curvature values + Eigen::VectorXN PV2; // Minimal curvature values + std::vector bad_vertices; // Indices of bad vertices + + igl::principal_curvature(V, F, PD1, PD2, PV1, PV2, bad_vertices, radius, useKring); + + return std::make_tuple(PD1, PD2, PV1, PV2, bad_vertices); + } +} + +// Bind the wrapper to the Python module +void bind_principal_curvature(nb::module_ &m) +{ + m.def( + "principal_curvature", + &pyigl::principal_curvature, + "V"_a, + "F"_a, + "radius"_a = 5, + "useKring"_a = true, +R"(Compute principal curvature directions and magnitudes for each vertex in a 3D mesh. + +@param[in] V #V by 3 matrix of vertex positions +@param[in] F #F by 3 matrix of face indices (triangular mesh) +@param[in] radius controls the size of the neighborhood, where 1 corresponds to average edge length +@param[in] useKring boolean to use Kring neighborhood instead of ball neighborhood +@return Tuple containing: + - PD1: #V by 3 maximal curvature direction for each vertex + - PD2: #V by 3 minimal curvature direction for each vertex + - PV1: #V vector of maximal curvature values for each vertex + - PV2: #V vector of minimal curvature values for each vertex + - bad_vertices: list of indices of bad vertices, if any)" + ); +} diff --git a/src/procrustes.cpp b/src/procrustes.cpp deleted file mode 100644 index dd25e4f2..00000000 --- a/src/procrustes.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __miss - -#include -#include -#include -#include - - - - -const char* ds_procrustes = R"igl_Qu8mg5v7( - -Solve Procrustes problem in d dimensions. Given two point sets X,Y in R^d - find best scale s, orthogonal R and translation t s.t. |s*X*R + t - Y|^2 - is minimized. - -Parameters ----------- -X #V by DIM first list of points -Y #V by DIM second list of points -includeScaling if scaling should be allowed -includeReflections if R is allowed to be a reflection - - -Returns -------- -scale scaling -R orthogonal matrix -t translation - - -See also --------- - - -Notes ------ -None - -Examples --------- -MatrixXd X, Y; (containing 3d points as rows) -double scale; -MatrixXd R; -VectorXd t; -igl::procrustes(X,Y,true,false,scale,R,t); -R *= scale; -MatrixXd Xprime = (X * R).rowwise() + t.transpose(); - -)igl_Qu8mg5v7"; - -npe_function(procrustes) -npe_doc(ds_procrustes) - -npe_arg(x, dense_float, dense_double) -npe_arg(y, npe_matches(x)) -npe_arg(include_scaling, bool) -npe_arg(include_reflections, bool) - -npe_begin_code() - - assert_cols_match(x, y, "x", "y"); - assert_nonzero_rows(x, "x"); - double scale; - EigenDenseLike r, t; - igl::procrustes(x, y, include_scaling, include_reflections, scale, r, t); - return std::make_tuple(scale, npe::move(r), npe::move(t)); - -npe_end_code() - diff --git a/src/project.cpp b/src/project.cpp index 48235c91..85f1a546 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -1,72 +1,42 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char *ds_project = R"igl_Qu8mg5v7( -Project - -Parameters ----------- -V #V by 3 list of object points -model model matrix -proj projection matrix -viewport viewport vector - -Returns -------- -P #V by 3 list of screen space points - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(project) -npe_doc(ds_project) - -npe_arg(v, dense_float, dense_double) -npe_arg(model, npe_matches(v)) -npe_arg(proj, npe_matches(v)) -npe_arg(viewport, npe_matches(v)) - - -npe_begin_code() - assert_nonzero_rows(v, "v"); - assert_cols_equals(v, 3, "v"); - assert_rows_equals(model, 4, "model"); - assert_cols_equals(model, 4, "model"); - - assert_rows_equals(proj, 4, "proj"); - assert_cols_equals(proj, 4, "proj"); - - assert_size_equals(viewport, 4, "viewport"); - - Eigen::Matrix viewport_copy; - for(int i = 0; i < 4; ++i){ - if(viewport.cols() == 1) - viewport_copy(i) = viewport(i, 0); - else - viewport_copy(i) = viewport(0, i); +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for project with batch mode + auto project( + const nb::DRef &scene, + const nb::DRef &model, + const nb::DRef &proj, + const nb::DRef &viewport) + { + Eigen::MatrixXN win; + igl::project(scene, model, proj, viewport, win); + return win; } - - EigenDense p; - igl::project(v, model, proj, viewport_copy, p); - return npe::move(p); - -npe_end_code() +} + +// Bind the wrapper to the Python module +void bind_project(nb::module_ &m) +{ + m.def( + "project", + &pyigl::project, + "scene"_a, + "model"_a, + "proj"_a, + "viewport"_a, +R"(Eigen reimplementation of gluUnproject for batch processing. + +@param[in] scne #P by 3 matrix of screen space x, y, and z coordinates +@param[in] model 4x4 model-view matrix +@param[in] proj 4x4 projection matrix +@param[in] viewport 4-long viewport vector +@return win #P by 3 matrix of the projected x, y, and z coordinates)"); + +} diff --git a/src/project_isometrically_to_plane.cpp b/src/project_isometrically_to_plane.cpp deleted file mode 100644 index 5fbb26b8..00000000 --- a/src/project_isometrically_to_plane.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_project_isometrically_to_plane = R"igl_Qu8mg5v7( - Project each triangle to the plane - -Parameters ----------- -V #V by 3 list of vertex positions -F #F by 3 list of mesh indices - -Returns -------- -U #F*3 by 2 list of triangle positions -UF #F by 3 list of mesh indices into U -I #V by #F*3 such that I(i,j) = 1 implies U(j,:) corresponds to V(i,:) - -See also --------- - - -Notes ------ -None - -Examples --------- - [U,UF,I] = project_isometrically_to_plane(V,F) - -)igl_Qu8mg5v7"; - -npe_function(project_isometrically_to_plane) -npe_doc(ds_project_isometrically_to_plane) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - - EigenDense u; - EigenDense uf; - Eigen::SparseMatrix < npe_Scalar_f> i; - igl::project_isometrically_to_plane(v, f, u, uf, i); - return std::make_tuple(npe::move(u), npe::move(uf), npe::move(i)); - -npe_end_code() - - diff --git a/src/project_to_line.cpp b/src/project_to_line.cpp index a344bdf7..b7f0f95e 100644 --- a/src/project_to_line.cpp +++ b/src/project_to_line.cpp @@ -1,85 +1,44 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char *ds_project_to_line = R"igl_Qu8mg5v7( -PROJECT_TO_LINE project points onto vectors, that is find the parameter - t for a point p such that proj_p = (y-x).*t, additionally compute the - squared distance from p to the line of the vector, such that - |p - proj_p|² = sqr_d - - -Parameters ----------- -P #P by dim list of points to be projected -S size dim start position of line vector -D size dim destination position of line vector - -Returns -------- -T #P by 1 list of parameters -sqrD #P by 1 list of squared distances - - -See also --------- - - -Notes ------ -None - -Examples --------- - -[T,sqrD] = project_to_line(P,S,D) - -)igl_Qu8mg5v7"; - -npe_function(project_to_line) -npe_doc(ds_project_to_line) - -npe_arg(p, dense_float, dense_double) -npe_arg(s, npe_matches(p)) -npe_arg(d, npe_matches(p)) - - -npe_begin_code() - assert_nonzero_rows(p, "p"); - assert_size_equals(s, p.cols(), "s"); - assert_size_equals(d, p.cols(), "d"); - - typedef Eigen::Matrix Mat; - Mat s_copy(s.size()); - Mat d_copy(d.size()); - - for(int i = 0; i < s.size(); ++i) +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for the multiple points overload of project_to_line + auto project_to_line( + const nb::DRef &P, + const nb::DRef &S, + const nb::DRef &D) { - if(s.cols() == 1) - s_copy[i] = s(i, 0); - else - s_copy[i] = s(0, i); - - if (d.cols() == 1) - d_copy[i] = d(i, 0); - else - d_copy[i] = d(0, i); + Eigen::VectorXN t; + Eigen::VectorXN sqrD; + igl::project_to_line(P, S, D, t, sqrD); + return std::make_tuple(t, sqrD); } - - Eigen::Matrix p_copy = p; - - EigenDense t; - EigenDense sqr_d; - igl::project_to_line(p_copy, s_copy, d_copy, t, sqr_d); - return std::make_tuple(npe::move(t), npe::move(sqr_d)); - -npe_end_code() - +} + +// Bind the wrapper to the Python module +void bind_project_to_line(nb::module_ &m) +{ + m.def( + "project_to_line", + &pyigl::project_to_line, + "P"_a, + "S"_a, + "D"_a, + R"(Project multiple points onto a line defined by points S and D. + + @param[in] P #P by dim list of points to be projected + @param[in] S 1 by dim starting position of line + @param[in] D 1 by dim ending position of line + @return Tuple containing: + - t: #P by 1 list of parameters along the line for each point + - sqrD: #P by 1 list of squared distances from each point to the line)"); + +} diff --git a/src/project_to_line_segment.cpp b/src/project_to_line_segment.cpp index dc7e0198..e01f3b1e 100644 --- a/src/project_to_line_segment.cpp +++ b/src/project_to_line_segment.cpp @@ -1,85 +1,44 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char *ds_project_to_line_segment = R"igl_Qu8mg5v7( -PROJECT_TO_LINE_SEGMENT project points onto vectors, that is find the parameter - t for a point p such that proj_p = (y-x).*t, additionally compute the - squared distance from p to the line of the vector, such that - |p - proj_p|² = sqr_d - - -Parameters ----------- -P #P by dim list of points to be projected -S size dim start position of line vector -D size dim destination position of line vector - -Returns -------- -T #P by 1 list of parameters -sqrD #P by 1 list of squared distances - -See also --------- - - -Notes ------ -None - -Examples --------- - [T,sqrD] = project_to_line_segment(P,S,D) - -)igl_Qu8mg5v7"; - -npe_function(project_to_line_segment) -npe_doc(ds_project_to_line_segment) - -npe_arg(p, dense_float, dense_double) -npe_arg(s, npe_matches(p)) -npe_arg(d, npe_matches(p)) - - -npe_begin_code() - assert_nonzero_rows(p, "p"); - assert_size_equals(s, p.cols(), "s"); - assert_size_equals(d, p.cols(), "d"); - - typedef Eigen::Matrix Mat; - Mat s_copy(s.size()); - Mat d_copy(d.size()); - - for (int i = 0; i < s.size(); ++i) +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for project_to_line_segment function + auto project_to_line_segment( + const nb::DRef &P, + const nb::DRef &S, + const nb::DRef &D) { - if (s.cols() == 1) - s_copy[i] = s(i, 0); - else - s_copy[i] = s(0, i); - - if (d.cols() == 1) - d_copy[i] = d(i, 0); - else - d_copy[i] = d(0, i); + Eigen::VectorXN t; + Eigen::VectorXN sqrD; + igl::project_to_line_segment(P, S, D, t, sqrD); + return std::make_tuple(t, sqrD); } - - Eigen::Matrix p_copy = p; - - EigenDense t; - EigenDense sqr_d; - - igl::project_to_line_segment(p_copy, s_copy, d_copy, t, sqr_d); - return std::make_tuple(npe::move(t), npe::move(sqr_d)); - -npe_end_code() - - +} + +// Bind the wrapper to the Python module +void bind_project_to_line_segment(nb::module_ &m) +{ + m.def( + "project_to_line_segment", + &pyigl::project_to_line_segment, + "P"_a, + "S"_a, + "D"_a, + R"(Project points onto a line segment defined by points S and D. + + @param[in] P #P by dim list of points to be projected + @param[in] S 1 by dim starting position of line segment + @param[in] D 1 by dim ending position of line segment + @return Tuple containing: + - t: #P by 1 list of parameters along the line segment for each point + - sqrD: #P by 1 list of squared distances from each point to its projection)" + ); +} diff --git a/src/pso.cpp b/src/pso.cpp deleted file mode 100644 index 8a6883d4..00000000 --- a/src/pso.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include -#include -#include - -const char *ds_pso = R"igl_Qu8mg5v7ss( -Solve the problem: - - minimize f(x) - subject to lb ≤ x ≤ ub - -by particle swarm optimization (PSO). - - -Parameters ----------- -f function that evaluates the objective for a given "particle" location -LB #X vector of lower bounds -UB #X vector of upper bounds -max_iters maximum number of iterations -population number of particles in swarm - -Returns -------- -f(X) objective corresponding to best particle seen so far -X best particle seen so far - - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7ss"; - -npe_function(pso) -npe_doc(ds_pso) - -npe_arg(f, const std::function< typename EigenDenseFloat::Scalar (Eigen::Matrix &) >) -npe_arg(lb, EigenDenseFloat) -npe_arg(ub, EigenDenseFloat) -npe_arg(max_iters, int) -npe_arg(population, int) - - -npe_begin_code() - Eigen::Matrix x_copy; - const auto obj = igl::pso(f, lb, ub, max_iters, population, x_copy); - EigenDenseFloat x = x_copy; - return std::make_tuple(obj, npe::move(x)); - -npe_end_code() - - - -// const char *ds_psop = R"igl_Qu8mg5v7ssp( -// Solve the problem: -// -// minimize f(x) -// subject to lb ≤ x ≤ ub -// -// by particle swarm optimization (PSO). -// -// -// Parameters -// ---------- -// f function that evaluates the objective for a given "particle" location -// LB #X vector of lower bounds -// UB #X vector of upper bounds -// P whether each DOF is periodic -// max_iters maximum number of iterations -// population number of particles in swarm -// -// Returns -// ------- -// X best particle seen so far -// Returns objective corresponding to best particle seen so far -// -// See also -// -------- -// -// -// Notes -// ----- -// None -// -// Examples -// -------- -// -// )igl_Qu8mg5v7ssp"; - -// npe_function(pso_periodic) -// npe_doc(ds_psop) -// -// npe_arg(f, const std::function< typename EigenDenseFloat::Scalar (Eigen::Matrix &) >) -// npe_arg(lb, EigenDenseFloat) -// npe_arg(ub, EigenDenseFloat) -// npe_arg(p, EigenDenseFloat) //SHOULD BE BOOLEAN, BUT IS NOT USED ANYWAYS -// npe_arg(max_iters, int) -// npe_arg(population, int) -// -// -// npe_begin_code() -// Eigen::Matrix x_copy; -// const auto obj = igl::pso(f, lb, ub, p, max_iters, population, x_copy); -// EigenDenseFloat x = x_copy; -// return std::make_tuple(obj, npe::move(x)); -// -// npe_end_code() diff --git a/src/qslim.cpp b/src/qslim.cpp index b684e0c0..dc66d20d 100644 --- a/src/qslim.cpp +++ b/src/qslim.cpp @@ -1,87 +1,60 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example - -#include -#include -#include - - - - - - +#include "default_types.h" #include - -const char* ds_qslim = R"igl_Qu8mg5v7( - -Decimate (simplify) a triangle mesh in nD according to the paper - "Simplifying Surfaces with Color and Texture using Quadric Error Metrics" - by [Garland and Heckbert, 1987] (technically a followup to qslim). The - mesh can have open boundaries but should be edge-manifold. - -Parameters ----------- -V #V by dim list of vertex positions. Assumes that vertices w -F #F by 3 list of triangle indices into V -max_m desired number of output faces -block_intersections whether to block intersections - - -Returns -------- -U #U by dim list of output vertex posistions (can be same ref as V) -G #G by 3 list of output face indices into U (can be same ref as G) -J #G list of indices into F of birth face -I #U list of indices into V of birth vertices - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(qslim) -npe_doc(ds_qslim) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(max_m, size_t) -npe_arg(block_intersections, bool) - - -npe_begin_code() - - assert_valid_23d_tri_mesh(v, f); - // TODO: remove __copy - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::MatrixXi f_copy = f.template cast(); - Eigen::MatrixXd u; - Eigen::MatrixXi g; - Eigen::VectorXi j; - Eigen::VectorXi i; - bool success = igl::qslim(v_copy, f_copy, max_m, block_intersections, u, g, j, i); - EigenDenseFloat u_row_major = u; - EigenDenseInt g_row_major = g.template cast(); - // FIXME: vector not allowing row major, but they should be essentially the same so i feel we can leave it as col major - Eigen::Matrix j_row_major = j.template cast(); - Eigen::Matrix i_row_major = i.template cast(); - return std::make_tuple(success, npe::move(u_row_major), npe::move(g_row_major), npe::move(j_row_major), npe::move(i_row_major)); - -npe_end_code() - - +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // igl::qslim is a nightmare of poor templating. Suffer copies. + auto qslim( + const Eigen::MatrixXd &V, + const Eigen::MatrixXi &F, + const int max_m, + const bool block_intersections) + { + Eigen::MatrixXd U; + Eigen::MatrixXi G; + Eigen::VectorXi J,I; + igl::qslim(V,F,max_m,block_intersections,U,G,J,I); + return std::make_tuple( + U.cast().eval(), // If Numeric==double will this incur a copy? + G.cast().eval(), + J.cast().eval(), + I.cast().eval()); + } +} + +// Bind the wrapper to the Python module +void bind_qslim(nb::module_ &m) +{ + m.def( + "qslim", + &pyigl::qslim, + "V"_a, + "F"_a, + "max_m"_a = 0, + "block_intersections"_a = false, + R"(Assumes (V,F) is a manifold mesh (possibly with boundary) collapses edges +until desired number of faces is achieved. This uses default edge cost and +merged vertex placement functions {edge length, edge midpoint}. + +See \fileinfo for more details. + +@param[in] V #V by dim list of vertex positions +@param[in] F #F by 3 list of face indices into V. +@param[in] max_m desired number of output faces +@param[in] block_intersections whether to block intersections (see + intersection_blocking_collapse_edge_callbacks) +@param[out] U #U by dim list of output vertex posistions (can be same ref as V) +@param[out] G #G by 3 list of output face indices into U (can be same ref as G) +@param[out] J #G list of indices into F of birth face +@param[out] I #U list of indices into V of birth vertices +@return true if m was reached (otherwise #G > m))"); + +} diff --git a/src/quad_grid.cpp b/src/quad_grid.cpp index 79e30a5e..4b67d493 100644 --- a/src/quad_grid.cpp +++ b/src/quad_grid.cpp @@ -1,60 +1,46 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - +#include "default_types.h" #include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto quad_grid( + const int nx, + const int ny) + { + Eigen::MatrixXN V; + Eigen::MatrixXI Q,E; + igl::quad_grid(nx,ny, V, Q, E); + return std::make_tuple(V,Q,E); + } +} + +// Bind the wrapper to the Python module +void bind_quad_grid(nb::module_ &m) +{ + m.def( + "quad_grid", + &pyigl::quad_grid, + "nx"_a, + "ny"_a, +R"(Create a regular quad quad_grid of elements (only 2D supported, currently) Vertex +position order is compatible with `igl::quad_grid` + +@param[in] nx number of vertices in the x direction +@param[in] ny number of vertices in the y direction +@param[out] V nx*ny by 2 list of vertex positions +@param[out] Q (nx-1)*(ny-1) by 4 list of quad indices into V +@param[out] E (nx-1)*ny+(ny-1)*nx by 2 list of undirected quad edge indices into V + +\see quad_grid, triangulated_quad_grid)" + ); +} -const char *ds_quad_grid = R"igl_Qu8mg5v7( - -Generate a quad mesh over a regular grid. - -Parameters ----------- -nx number of vertices in the x direction -ny number of vertices in the y direction - - -Returns -------- - -V nx*ny by 2 list of vertex positions -Q (nx-1)*(ny-1) by 4 list of quad indices into V -E (nx-1)*ny+(ny-1)*nx by 2 list of undirected quad edge indices into V - -See also --------- -grid, triangulated_grid - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(quad_grid) -npe_doc(ds_quad_grid) - -npe_arg(nx, int) -npe_arg(ny, int) - - -npe_begin_code() - EigenDenseFloat gv; - EigenDenseInt gf; - EigenDenseInt e; - igl::quad_grid(nx, ny, gv, gf, e); - return std::make_tuple(npe::move(gv), npe::move(gf), npe::move(e)); -npe_end_code() diff --git a/src/quad_planarity.cpp b/src/quad_planarity.cpp deleted file mode 100644 index c9e17ab9..00000000 --- a/src/quad_planarity.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: missing __example - -#include -#include -#include -#include - -const char* ds_quad_planarity = R"igl_Qu8mg5v7( -Compute planarity of the faces of a quad mesh. - -Parameters ----------- -v : #v by 3 array of mesh vertex 3D positions -f : #f by 4 array of face (quad) indices - -Returns -------- -p : #f by 1 array of mesh face (quad) planarities - -See also --------- -None - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(quad_planarity) -npe_doc(ds_quad_planarity) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - - assert_valid_tet_mesh(v, f); - EigenDenseLike p; - igl::quad_planarity(v, f, p); - return npe::move(p); - -npe_end_code() - - diff --git a/src/ramer_douglas_peucker.cpp b/src/ramer_douglas_peucker.cpp deleted file mode 100644 index 47f3d737..00000000 --- a/src/ramer_douglas_peucker.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_ramer_douglas_peucker = R"igl_Qu8mg5v7( -Run (Ramer-)Duglass-Peucker curve simplification but keep track of where -every point on the original curve maps to on the simplified curve. - - -Parameters ----------- -P #P by dim list of points, (use P([1:end 1],:) for loops) -tol DP tolerance - -Returns -------- -S #S by dim list of points along simplified curve -J #S indices into P of simplified points -Q #P by dim list of points mapping along simplified curve - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(ramer_douglas_peucker) -npe_doc(ds_ramer_douglas_peucker) - -npe_arg(p, dense_float, dense_double) -npe_arg(tol, double) - - -npe_begin_code() - assert_nonzero_rows(p, "p"); - - - EigenDense s; - Eigen::VectorXi j_copy; - EigenDense q; - igl::ramer_douglas_peucker(p, tol, s, j_copy, q); - EigenDenseInt j = j_copy.template cast(); - return std::make_tuple(npe::move(s), npe::move(j), npe::move(q)); - -npe_end_code() - - diff --git a/src/random_points_on_mesh.cpp b/src/random_points_on_mesh.cpp index 4940128e..7fd51f2a 100644 --- a/src/random_points_on_mesh.cpp +++ b/src/random_points_on_mesh.cpp @@ -1,59 +1,49 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -//TODO: __example -#include -#include -#include +#include "default_types.h" #include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for vertex_components with face indices + auto random_points_on_mesh( + const Integer n, + const nb::DRef &V, + const nb::DRef &F) + { + Eigen::VectorXI FI; + Eigen::MatrixXN B,X; + igl::random_points_on_mesh(n, V, F, B, FI, X); + return std::make_tuple(B, FI, X); + } +} + +// Bind the wrappers to the Python module +void bind_random_points_on_mesh(nb::module_ &m) +{ + // Binding for vertex_components with adjacency matrix and counts + m.def( + "random_points_on_mesh", + &pyigl::random_points_on_mesh, + "n"_a, + "V"_a, + "F"_a, + R"( + Randomly sample a mesh (V,F) n times. + + @param[in] n number of samples + @param[in] V #V by dim list of mesh vertex positions + @param[in] F #F by 3 list of mesh triangle indices + @param[out] B n by 3 list of barycentric coordinates, ith row are coordinates of + ith sampled point in face FI(i) + @param[in] urbg An instance of UnformRandomBitGenerator (e.g., + `std::minstd_rand(0)`) + @param[out] FI n list of indices into F + @param[in,out] urbg An instance of UnformRandomBitGenerator. + @param[out] X n by dim list of sample positions.)"); +} -const char* ds_random_points_on_mesh = R"igl_Qu8mg5v7( -RANDOM_POINTS_ON_MESH Randomly sample a mesh (V,F) n times. -Parameters ----------- - n number of samples - V #V by dim list of mesh vertex positions - F #F by 3 list of mesh triangle indices - -Returns -------- - B n by 3 list of barycentric coordinates, ith row are coordinates of - ith sampled point in face FI(i) - FI n list of indices into F - X n by dim list of sampled points - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(random_points_on_mesh) -npe_doc(ds_random_points_on_mesh) - -npe_arg(n, int) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_23d_tri_mesh(v, f); - EigenDenseLike b; - Eigen::Matrix fi; - EigenDenseLike x; - igl::random_points_on_mesh(n, v, f, b, fi, x); - return std::make_tuple(npe::move(b), npe::move(fi), npe::move(x)); - -npe_end_code() diff --git a/src/random_points_on_mesh_intrinsic.cpp b/src/random_points_on_mesh_intrinsic.cpp deleted file mode 100644 index b4417754..00000000 --- a/src/random_points_on_mesh_intrinsic.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include -#include -#include - -const char* ds_random_points_on_mesh_intrinsic = R"igl_Qu8mg5v7( -RANDOM_POINTS_ON_MESH Randomly sample a mesh (V,F) n times. -Parameters ----------- - n number of samples - dblA #F list of double areas of triangles - -Returns -------- - B n by 3 list of barycentric coordinates, ith row are coordinates of - ith sampled point in face FI(i) - FI n list of indices into F - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(random_points_on_mesh_intrinsic) -npe_doc(ds_random_points_on_mesh_intrinsic) - -npe_arg(n, int) -npe_arg(dblA, dense_float, dense_double) - - -npe_begin_code() - - EigenDenseLike b; - EigenDenseInt fi; - igl::random_points_on_mesh_intrinsic(n, dblA, b, fi); - return std::make_tuple(npe::move(b), npe::move(fi)); - -npe_end_code() - diff --git a/src/random_search.cpp b/src/random_search.cpp deleted file mode 100644 index 25fb39d9..00000000 --- a/src/random_search.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - - - -#include - -const char *ds_random_search = R"igl_Qu8mg5v7( - - Solve the problem: - - minimize f(x) - subject to lb ≤ x ≤ ub - - by uniform random search. - -Parameters ----------- -f function to minimize -LB #X vector of finite lower bounds -UB #X vector of finite upper bounds -iters number of iterations - -Returns -------- -f(X) -X #X optimal parameter vector - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(random_search) -npe_doc(ds_random_search) - -npe_arg(f, const std::function< typename EigenDenseFloat::Scalar (Eigen::Matrix &) >) -npe_arg(lb, EigenDenseFloat) -npe_arg(ub, EigenDenseFloat) -npe_arg(iters, int) - - -npe_begin_code() - - Eigen::Matrix x; - const auto obj = igl::random_search(f, lb, ub, iters, x); - return std::make_tuple(obj, npe::move(x)); - -npe_end_code() diff --git a/src/ray_box_intersect.cpp b/src/ray_box_intersect.cpp deleted file mode 100644 index 8dd913f9..00000000 --- a/src/ray_box_intersect.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - -#include - -const char *ds_ray_box_intersect = R"igl_Qu8mg5v7( - Determine whether a ray origin+t*dir and box intersect within the ray's parameterized - range (t0,t1) - -Parameters ----------- -source 3-vector origin of ray -dir 3-vector direction of ray -box_min min axis aligned box -box_max max axis aligned box -t0 hit only if hit.t less than t0 -t1 hit only if hit.t greater than t1 - -Returns -------- -true if hit -tmin minimum of interval of overlap within [t0,t1] -tmax maximum of interval of overlap within [t0,t1] - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(ray_box_intersect) -npe_doc(ds_ray_box_intersect) - -npe_arg(source, dense_float, dense_double) -npe_arg(dir, npe_matches(source)) -npe_arg(box_min, npe_matches(source)) -npe_arg(box_max, npe_matches(source)) -npe_arg(t0, double) -npe_arg(t1, double) - - -npe_begin_code() - assert_size_equals(source, 3, "source"); - assert_size_equals(dir, 3, "dir"); - assert_size_equals(box_min, 3, "box_min"); - assert_size_equals(box_max, 3, "box_max"); - - Eigen::Matrix dir_copy = dir.template cast(); - Eigen::Matrix source_copy = source.template cast(); - - Eigen::AlignedBox box_copy(box_min.template cast(), box_max.template cast()); - - double tmin; - double tmax; - - double t0_copy = t0; - double t1_copy = t1; - bool hit = igl::ray_box_intersect(source_copy, dir_copy, box_copy, t0_copy, t1_copy, tmin, tmax); - return std::make_tuple(hit, tmin, tmax); - -npe_end_code() - - diff --git a/src/ray_mesh_intersect.cpp b/src/ray_mesh_intersect.cpp index a0e7e85c..80998cea 100644 --- a/src/ray_mesh_intersect.cpp +++ b/src/ray_mesh_intersect.cpp @@ -1,72 +1,64 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - +#include "default_types.h" #include -#include -#include -#include - -const char *ds_ray_mesh_intersect = R"igl_Qu8mg5v7( -Shoot a ray against a mesh (V,F) and collect the first hit. - -Parameters ----------- -source 3-vector origin of ray -dir 3-vector direction of ray -V #V by 3 list of mesh vertex positions -F #F by 3 list of mesh face indices into V - -Returns -------- -hits **sorted** list of hits: id, gid, u, v, t - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(ray_mesh_intersect) -npe_doc(ds_ray_mesh_intersect) - -npe_arg(source, dense_float, dense_double) -npe_arg(dir, npe_matches(source)) -// This does not work and seems to be a bug in numpyeigen -//// npe_arg(v, npe_matches(source)) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_size_equals(source, 3, "source"); - assert_size_equals(dir, 3, "dir"); - assert_valid_3d_tri_mesh(v, f); - - std::vector hits; - igl::ray_mesh_intersect( - source.template cast().eval(), - dir.template cast().eval(), - v, f, hits); - std::vector> hits_res; - - for(const auto &h : hits) - hits_res.emplace_back(h.id, h.gid, h.u, h.v, h.t); - - return hits_res; - -npe_end_code() +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for ray_mesh_intersect with overload handling + std::vector> + ray_mesh_intersect( + const Eigen::VectorXN &source, + const Eigen::VectorXN &dir, + const nb::DRef &V, + const nb::DRef &F, + const bool first = false) + { + std::vector> out; + if (first) + { + // Overload for a single hit + igl::Hit hit; + bool result = igl::ray_mesh_intersect(source, dir, V, F, hit); + out.emplace_back(hit.id, hit.t, hit.u, hit.v); + } + else + { + // Overload for all hits + std::vector > hits; + bool result = igl::ray_mesh_intersect(source, dir, V, F, hits); + for (const auto &hit : hits) + { + out.emplace_back(hit.id, hit.t, hit.u, hit.v); + } + } + return out; + } +} + +// Bind the wrapper to the Python module +void bind_ray_mesh_intersect(nb::module_ &m) +{ + m.def( + "ray_mesh_intersect", + &pyigl::ray_mesh_intersect, + "source"_a, + "dir"_a, + "V"_a, + "F"_a, + "first"_a = false, +R"(Shoot a ray against a mesh (V, F) and collect hits. + +@param[in] source 3-vector origin of the ray +@param[in] dir 3-vector direction of the ray +@param[in] V #V by 3 list of mesh vertex positions +@param[in] F #F by 3 list of mesh face indices into V +@param[in] first If True, only return the first hit (if any) +@return Sorted list of hits if any exist, otherwise None)"); +} diff --git a/src/ray_sphere_intersect.cpp b/src/ray_sphere_intersect.cpp deleted file mode 100644 index 13c27f18..00000000 --- a/src/ray_sphere_intersect.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - - - -#include - -const char *ds_ray_sphere_intersect = R"igl_Qu8mg5v7( -Compute the intersection between a ray from O in direction D and a sphere centered at C with radius r - -Parameters ----------- -source origin of ray -dir direction of ray -center center of sphere -r radius of sphere - -Returns -------- -Returns the number of hits -t0 parameterization of first hit (set only if exists) so that hit position = o + t0*d -t1 parameterization of second hit (set only if exists) - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(ray_sphere_intersect) -npe_doc(ds_ray_sphere_intersect) - -npe_arg(source, dense_float, dense_double) -npe_arg(dir, npe_matches(source)) -npe_arg(center, npe_matches(source)) -npe_arg(r, double) - - -npe_begin_code() - assert_size_equals(source, 3, "source"); - assert_size_equals(dir, 3, "dir"); - assert_size_equals(center, 3, "center"); - - Eigen::Matrix dir_copy = dir.template cast(); - Eigen::Matrix source_copy = source.template cast(); - Eigen::Matrix center_copy = center.template cast(); - - double t0; - double t1; - int inters = igl::ray_sphere_intersect(source_copy, dir_copy, center_copy, r, t0, t1); - return std::make_tuple(inters, t0, t1); - -npe_end_code() - - diff --git a/src/readDMAT.cpp b/src/readDMAT.cpp index 28862c7b..04001635 100644 --- a/src/readDMAT.cpp +++ b/src/readDMAT.cpp @@ -1,73 +1,38 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include - - - - - - +#include "default_types.h" #include - -const char* ds_read_dmat = R"igl_Qu8mg5v7( -Read a matrix from an ascii/binary dmat file, a simple ascii matrix file type, defined as follows. The first line is always: -<#columns> <#rows> -Then the coefficients of the matrix are given separated by whitespace with columns running fastest. - -Parameters ----------- -filename : string, path to .dmat file -dtype : data-type of the returned matrix. Default is `float64`. - (returned faces always have type int32.) - -Returns -------- -w : array containing read-in coefficients - -See also --------- -read_triangle_mesh, read_off - -Notes ------ -None - -Examples --------- ->>> w = read_dmat("my_model.dmat") -)igl_Qu8mg5v7"; - -npe_function(read_dmat) -npe_doc(ds_read_dmat) -npe_arg(filename, std::string) -npe_default_arg(dtype, npe::dtype, "float64") -npe_begin_code() - - if (dtype.type() == npe::type_f32) { - EigenDenseF32 w; - bool ret = igl::readDMAT(filename, w); - if (!ret) { - throw std::invalid_argument("File '" + filename + "' not found."); - } - return npe::move(w); - } else if (dtype.type() == npe::type_f64) { - EigenDenseF64 w; - bool ret = igl::readDMAT(filename, w); - if (!ret) { - throw std::invalid_argument("File '" + filename + "' not found."); +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for readDMAT with Eigen matrix output + Eigen::MatrixXN readDMAT(const std::filesystem::path &file_name) + { + Eigen::MatrixXN W; + if (!igl::readDMAT(file_name.generic_string(), W)) + { + throw std::runtime_error("readDMAT: Failed to read DMAT file."); } - return npe::move(w); - } else { - throw pybind11::type_error("Only float32 and float64 dtypes are supported."); + return W; } -npe_end_code() +} +// Bind the wrapper to the Python module +void bind_readDMAT(nb::module_ &m) +{ + m.def( + "readDMAT", + &pyigl::readDMAT, + "file_name"_a, + R"(Read a matrix from a .dmat file. + @param[in] file_name path to .dmat file + @return Eigen matrix containing read-in coefficients + )"); +} diff --git a/src/readMESH.cpp b/src/readMESH.cpp index 66e210db..4f6fe2ba 100644 --- a/src/readMESH.cpp +++ b/src/readMESH.cpp @@ -1,67 +1,45 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_read_mesh = R"igl_Qu8mg5v7( -Load a tetrahedral volume mesh from a .mesh file. - -Parameters ----------- -filename : path of .mesh file -dtype : data-type of the returned vertices, optional. Default is `float64`. - (returned faces always have type int32.) - -Returns -------- -v : array of vertex positions #v by 3 -t : #t by 4 array of tet indices into vertex positions -f : #f by 3 array of face indices into vertex positions - -See also --------- -None - -Notes ------ -Known bugs: Holes and regions are not supported - -Examples --------- ->>> v, t, f = read_mesh("my_mesh.mesh") -)igl_Qu8mg5v7"; - -npe_function(read_mesh) -npe_doc(ds_read_mesh) -npe_arg(filename, std::string) -npe_default_arg(dtypef, npe::dtype, "float") -npe_begin_code() - - if (dtypef.type() == npe::type_f32) { - EigenDenseF32 v; - EigenDenseInt f, t; - bool ret = igl::readMESH(filename, v, t, f); - if (!ret) { - throw std::invalid_argument("File '" + filename + "' not found."); - } - return std::make_tuple(npe::move(v), npe::move(t), npe::move(f)); - } else if (dtypef.type() == npe::type_f64) { - EigenDenseF64 v; - EigenDenseInt f, t; - bool ret = igl::readMESH(filename, v, t, f); - if (!ret) { - throw std::invalid_argument("File '" + filename + "' not found."); +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto readMESH(const std::filesystem::path& mesh_file_name) + { + Eigen::MatrixXN V; // Vertex positions + Eigen::MatrixXI T; // Tetrahedral indices + Eigen::MatrixXI F; // Face indices + + if (!igl::readMESH(mesh_file_name.generic_string(), V, T, F)) + { + throw std::runtime_error("Failed to read .mesh file: " + mesh_file_name.generic_string()); } - return std::make_tuple(npe::move(v), npe::move(t), npe::move(f)); - } else { - throw pybind11::type_error("Only float32 and float64 dtypes are supported."); - } -npe_end_code() + return std::make_tuple(V, T, F); + } +} + +// Bind the wrapper to the Python module +void bind_readMESH(nb::module_ &m) +{ + m.def( + "readMESH", + &pyigl::readMESH, + "mesh_file_name"_a, +R"(Load a tetrahedral volume mesh from a .mesh file. + +@param[in] mesh_file_name Path of the .mesh file to read +@return Tuple containing: + - V: #V by 3 matrix of vertex positions + - T: #T by 4 matrix of tetrahedral indices into vertices + - F: #F by 3 matrix of face indices into vertices +@throws std::runtime_error if file reading fails +)"); +} diff --git a/src/readMSH.cpp b/src/readMSH.cpp index e51cc02a..42e495ab 100644 --- a/src/readMSH.cpp +++ b/src/readMSH.cpp @@ -1,70 +1,66 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include +#include "default_types.h" #include - -const char *ds_read_msh = R"igl_Qu8mg5v7( -Read a mesh (e.g., tet mesh) from a gmsh .msh file - -Parameters ----------- -filename path to .msh file -dtype : data-type of the returned vertices, optional. Default is `float64`. - (returned faces always have type int32.) - -Returns -------- - -V #V by 3 list of 3D mesh vertex positions -T #T by ss list of 3D ss-element indices into V (e.g., ss=4 for tets) - -See also --------- - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(read_msh) -npe_doc(ds_read_msh) - -npe_arg(filename, std::string) -npe_default_arg(dtypef, npe::dtype, "float") - -npe_begin_code() - Eigen::MatrixXd v; - Eigen::MatrixXi t; - - //NOTE: readMSH only support vertices as matrix of doubles. We instead cast the output accordingly. - bool ret = igl::readMSH(filename, v, t); - if (!ret) { - throw std::invalid_argument("File '" + filename + "' not found."); +#include +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto readMSH(const std::filesystem::path& msh_file_name) + { + Eigen::MatrixXN V; + Eigen::MatrixXI Tri; + Eigen::MatrixXI Tet; + Eigen::VectorXI TriTag; + Eigen::VectorXI TetTag; + std::vector XFields; + std::vector XF; + std::vector EFields; + std::vector TriF; + std::vector TetF; + + if (!igl::readMSH(msh_file_name.generic_string(), V, Tri, Tet, TriTag, TetTag, XFields, XF, EFields, TriF, TetF)) + { + throw std::runtime_error("Failed to read .msh file: " + msh_file_name.generic_string()); + } + + return std::make_tuple(V, Tri, Tet, TriTag, TetTag, XFields, XF, EFields, TriF, TetF); } - - EigenDenseInt t_row_maj = t.template cast(); - - if (dtypef.type() == npe::type_f32) { - EigenDenseF32 v_row_maj = v.template cast (); - return std::make_tuple(npe::move(v_row_maj ), npe::move(t_row_maj)); - } else if (dtypef.type() == npe::type_f64) { - EigenDenseF64 v_row_maj = v.template cast (); - return std::make_tuple(npe::move(v_row_maj ), npe::move(t_row_maj)); - } - - throw pybind11::type_error("Only float32 and float64 dtypes are supported."); - -npe_end_code() - +} + +// Bind the wrapper to the Python module +void bind_readMSH(nb::module_ &m) +{ + m.def( + "readMSH", + &pyigl::readMSH, + "msh_file_name"_a, +R"(read triangle surface mesh and tetrahedral volume mesh from .msh file + +@param[in] msh - file name +@param[out] X eigen double matrix of vertex positions #X by 3 +@param[out] Tri #Tri eigen integer matrix of triangular faces indices into vertex positions +@param[out] Tet #Tet eigen integer matrix of tetrahedral indices into vertex positions +@param[out] TriTag #Tri eigen integer vector of tags associated with surface faces +@param[out] TetTag #Tet eigen integer vector of tags associated with volume elements +@param[out] XFields #XFields list of strings with field names associated with nodes +@param[out] XF #XFields list of eigen double matrices, fields associated with nodes +@param[out] EFields #EFields list of strings with field names associated with elements +@param[out] TriF #EFields list of eigen double matrices, fields associated with surface elements +@param[out] TetF #EFields list of eigen double matrices, fields associated with volume elements +@return true on success +\bug only version 2.2 of .msh file is supported (gmsh 3.X) +\bug only triangle surface elements and tetrahedral volumetric elements are supported +\bug only 3D information is supported +\bug only the 1st tag per element is returned (physical) +\bug same element fields are expected to be associated with surface elements and volumetric elements +)"); +} diff --git a/src/readOBJ.cpp b/src/readOBJ.cpp index e00c9cc1..2e33d7d9 100644 --- a/src/readOBJ.cpp +++ b/src/readOBJ.cpp @@ -1,71 +1,50 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_read_obj = R"igl_Qu8mg5v7( +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto readOBJ(const std::filesystem::path filename) + { + Eigen::MatrixXN V,TC,CN; + Eigen::MatrixXI F,FTC,FN; + if(!igl::readOBJ(filename.generic_string(),V,TC,CN,F,FTC,FN)) + { + // throw runtime exception + throw std::runtime_error("Failed to read mesh from: " + filename.generic_string()); + } + return std::make_tuple(V,TC,CN,F,FTC,FN); + } +} + +// Bind the wrapper to the Python module +void bind_readOBJ(nb::module_ &m) +{ + m.def( + "readOBJ", + &pyigl::readOBJ, + "filename"_a, +R"( Read a mesh from an ascii obj file, filling in vertex positions, normals -and texture coordinates. Only meshes with constant face degree are supported. - -Parameters ----------- -filename : string, path to .obj file -dtype : data-type of the returned faces, texture coordinates and normals, optional. Default is `float64`. - (returned faces always have type int32.) - -Returns -------- -v : array of vertex positions #v by 3 -tc : array of texture coordinats #tc by 2 -n : array of corner normals #n by 3 -f : #f array of face indices into vertex positions -ftc : #f array of face indices into vertex texture coordinates -fn : #f array of face indices into vertex normals +and texture coordinates. Mesh may have faces of any number of degree -See also --------- -read_triangle_mesh, read_off +@param[in] str path to .obj file +@param[out] V double matrix of vertex positions #V by 3 +@param[out] TC double matrix of texture coordinats #TC by 2 +@param[out] N double matrix of corner normals #N by 3 +@param[out] F #F list of face indices into vertex positions +@param[out] FTC #F list of face indices into vertex texture coordinates +@param[out] FN #F list of face indices into vertex normals)" + ); +} -Notes ------ -None -Examples --------- ->>> v, _, n, f, _, _ = read_obj("my_model.obj") -)igl_Qu8mg5v7"; -npe_function(read_obj) -npe_doc(ds_read_obj) -npe_arg(filename, std::string) -npe_default_arg(dtype, npe::dtype, "float64") -npe_begin_code() - - if (dtype.type() == npe::type_f32) { - EigenDenseF32 v, tc, n; - EigenDenseInt f, ftc, fn; - bool ret = igl::readOBJ(filename, v, tc, n, f, ftc, fn); - if (!ret) { - throw std::invalid_argument("File '" + filename + "' not found or containing faces of varying degree (which is not supported yet)."); - } - return std::make_tuple(npe::move(v), npe::move(tc), npe::move(n), npe::move(f), npe::move(ftc), npe::move(fn)); - } else if (dtype.type() == npe::type_f64) { - EigenDenseF64 v, tc, n; - EigenDenseInt f, ftc, fn; // TODO weird problem, int64 gives int128 dtype in numpy - bool ret = igl::readOBJ(filename, v, tc, n, f, ftc, fn); - if (!ret) { - throw std::invalid_argument("File '" + filename + "' not found or containing faces of varying degree (which is not supported yet)."); - } - return std::make_tuple(npe::move(v), npe::move(tc), npe::move(n), npe::move(f), npe::move(ftc), npe::move(fn)); - } else { - throw pybind11::type_error("Only float32 and float64 dtypes are supported."); - } -npe_end_code() diff --git a/src/readOFF.cpp b/src/readOFF.cpp index 460dee82..cdf6767b 100644 --- a/src/readOFF.cpp +++ b/src/readOFF.cpp @@ -1,86 +1,46 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_read_off = R"igl_Qu8mg5v7( -Read a mesh from an ascii off file, filling in vertex positions, normals -and texture coordinates. Mesh may have faces of any number of degree. - -Parameters ----------- -filename : string, path to .off file -read_normals : bool, determines whether normals are read. If false, returns [] -dtype : data-type of the returned vertices, faces, and normals, optional. Default is `float64`. - (returned faces always have type int32.) - -Returns -------- -v : array of vertex positions #v by 3 -f : #f list of face indices into vertex positions -n : list of vertex normals #v by 3 - -See also --------- -read_triangle_mesh, read_obj - -Notes ------ -None - -Examples --------- ->>> v, f, n, c = read_off("my_model.off") -)igl_Qu8mg5v7"; - -npe_function(read_off) -npe_doc(ds_read_off) -npe_arg(filename, std::string) -npe_default_arg(read_normals, bool, true) -npe_default_arg(dtype, npe::dtype, "float64") -npe_begin_code() - - if (dtype.type() == npe::type_f32) { - EigenDenseF32 v, n; - EigenDenseInt f; - bool ret; - if (read_normals) { - ret = igl::readOFF(filename, v, f, n); - } - else { - ret = igl::readOFF(filename, v, f); +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto readOFF(const std::filesystem::path filename) + { + Eigen::MatrixXN V,N; + Eigen::MatrixXI F; + if(!igl::readOFF(filename.generic_string(),V,F,N)) + { + // throw runtime exception + throw std::runtime_error("Failed to read mesh from: " + filename.generic_string() ); } + return std::make_tuple(V,F,N); + } +} - if (!ret) { - throw std::invalid_argument("File '" + filename + "' not found."); - } +// Bind the wrapper to the Python module +void bind_readOFF(nb::module_ &m) +{ + m.def( + "readOFF", + &pyigl::readOFF, + "filename"_a, +R"(Read mesh from an ascii .off file + +@param[in] filename path to file +@param[out] V double matrix #V by 3 +@param[out] F int matrix #F by 3 +@param[out] N,C double matrix #V by 3 normals or colors +@return true iff success)" + ); +} - return std::make_tuple(npe::move(v), npe::move(f), npe::move(n)); - } else if (dtype.type() == npe::type_f64) { - EigenDenseF64 v, n; - EigenDenseInt f; - bool ret; - if (read_normals) { - ret = igl::readOFF(filename, v, f, n); - } - else { - ret = igl::readOFF(filename, v, f); - } - if (!ret) { - throw std::invalid_argument("File '" + filename + "' not found."); - } - return std::make_tuple(npe::move(v), npe::move(f), npe::move(n)); - } else { - throw pybind11::type_error("Only float32 and float64 dtypes are supported."); - } -npe_end_code() diff --git a/src/readTGF.cpp b/src/readTGF.cpp deleted file mode 100644 index 285ae553..00000000 --- a/src/readTGF.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include - - - -#include - -const char *ds_read_tgf = R"igl_Qu8mg5v7( -Read a graph from a .tgf file - -Parameters ----------- -filename .tgf file name - -Returns -------- - - V # vertices by 3 list of vertex positions - E # edges by 2 list of edge indices - P # point-handles list of point handle indices - BE # bone-edges by 2 list of bone-edge indices - CE # cage-edges by 2 list of cage-edge indices - PE # pseudo-edges by 2 list of pseudo-edge indices - -See also --------- - - -Notes ------ -Assumes that graph vertices are 3 dimensional - -Examples --------- - V,E,P,BE,CE,PE = igl.read_tgf(filename) - -)igl_Qu8mg5v7"; - -npe_function(read_tgf) -npe_doc(ds_read_tgf) - -npe_arg(tgf_filename, std::string) - - -npe_begin_code() - - Eigen::MatrixXd v; - Eigen::MatrixXi e; - Eigen::VectorXi p; - Eigen::MatrixXi be; - Eigen::MatrixXi ce; - Eigen::MatrixXi pe; - bool ok = igl::readTGF(tgf_filename, v, e, p, be, ce, pe); - if (!ok) - { - throw std::invalid_argument("File '" + tgf_filename + "' not found."); - } - - EigenDenseFloat v_row_major = v.template cast(); - EigenDenseInt e_row_major = e.template cast(); - EigenDenseInt p_row_major = p.template cast(); - EigenDenseInt be_row_major = be.template cast(); - EigenDenseInt ce_row_major = ce.template cast(); - EigenDenseInt pe_row_major = pe.template cast(); - - return std::make_tuple(npe::move(v_row_major), npe::move(e_row_major), npe::move(p_row_major), npe::move(be_row_major), npe::move(ce_row_major), npe::move(pe_row_major)); - -npe_end_code() diff --git a/src/read_triangle_mesh.cpp b/src/read_triangle_mesh.cpp index 0eb32ec2..083c02a2 100644 --- a/src/read_triangle_mesh.cpp +++ b/src/read_triangle_mesh.cpp @@ -1,74 +1,47 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include +#include +#include +#include +#include +#include +#include -const char *ds_read_triangle_mesh = R"igl_Qu8mg5v7( -Read mesh from an ascii file with automatic detection of file format. -Supported: obj, off, stl, wrl, ply, mesh. +namespace nb = nanobind; +using namespace nb::literals; -Parameters ----------- -filename : string, path to mesh file -dtype : data-type of the returned vertices, optional. Default is `float64`. - (returned faces always have type int32.) - -Returns -------- -v : array of vertex positions #v by 3 -f : #f list of face indices into vertex positions - -See also --------- - - -Notes ------ -None - -Examples --------- ->>> v, f = read_triangle_mesh("my_model.obj") -)igl_Qu8mg5v7"; - -npe_function(read_triangle_mesh) - npe_doc(ds_read_triangle_mesh) - npe_arg(filename, std::string) - npe_default_arg(dtypef, npe::dtype, "float") - npe_begin_code() - - if (dtypef.type() == npe::type_f32) +namespace pyigl { - EigenDenseF32 v; - EigenDenseInt f; - bool ret = igl::read_triangle_mesh(filename, v, f); - if (!ret) + auto read_triangle_mesh( + const std::filesystem::path & path) { - throw std::invalid_argument("File '" + filename + "' not found."); + Eigen::MatrixXN V; + Eigen::MatrixXI F; + if(!igl::read_triangle_mesh(path.generic_string(),V,F)) + { + // throw runtime exception + throw std::runtime_error("Failed to read mesh from: " + path.generic_string()); + } + return std::make_tuple(V,F); } - return std::make_tuple(npe::move(v), npe::move(f)); } -else if (dtypef.type() == npe::type_f64) -{ - EigenDenseF64 v; - EigenDenseInt f; - bool ret = igl::read_triangle_mesh(filename, v, f); - if (!ret) - { - throw std::invalid_argument("File '" + filename + "' not found."); - } - return std::make_tuple(npe::move(v), npe::move(f)); -} -else + +// Bind the wrapper to the Python module +void bind_read_triangle_mesh(nb::module_ &m) { - throw pybind11::type_error("Only float32 and float64 dtypes are supported."); + m.def( + "read_triangle_mesh", + &pyigl::read_triangle_mesh, + "filename"_a, +R"(Read mesh from an ascii file with automatic detection of file format +among: mesh, msh obj, off, ply, stl, wrl. + +@param[in] filename path to file +@param[out] V double matrix #V by 3 +@param[out] F int matrix #F by 3 +@return true iff success)" + ); } -npe_end_code() + + diff --git a/src/remove_duplicate_vertices.cpp b/src/remove_duplicate_vertices.cpp index 339ffbaa..b20d77e2 100644 --- a/src/remove_duplicate_vertices.cpp +++ b/src/remove_duplicate_vertices.cpp @@ -1,76 +1,71 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - +#include "default_types.h" #include - -const char* ds_remove_duplicate_vertices = R"igl_Qu8mg5v7( - -REMOVE_DUPLICATE_VERTICES Remove duplicate vertices upto a uniqueness - tolerance (epsilon) - -Parameters ----------- -V #V by dim list of vertex positions -F #F by 3 list of triangle indices -epsilon uniqueness tolerance (significant digit), can probably think of - this as a tolerance on L1 distance - - -Returns -------- -SV #SV by dim new list of vertex positions -SVI #V by 1 list of indices so SV = V(SVI,:) -SVJ #SV by 1 list of indices so V = SV(SVJ,:) -SF #SF by dim new list of faces so SF = F(SVJ,:) - -See also --------- - - -Notes ------ -None - -Examples --------- -% Mesh in (V,F) -[SV,SVI,SVJ,SF] = remove_duplicate_vertices(V,F,1e-7); -% remap faces -SF = SVJ(F); - - -)igl_Qu8mg5v7"; - -npe_function(remove_duplicate_vertices) -npe_doc(ds_remove_duplicate_vertices) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(epsilon, double) - - -npe_begin_code() - - // TODO: remove __copy - // I believe we can prevent this. The libigl function uses "DerivedV rv" - // which calls Eigen::Map(Eigen::Matrix<>)::Map() and DNE - assert_nonzero_rows(v, "v"); - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::MatrixXd sv; - Eigen::Matrix svi, svj; - EigenDenseLike sf; - igl::remove_duplicate_vertices(v_copy, f, epsilon, sv, svi, svj, sf); - EigenDenseFloat sv_row_major = sv; - return std::make_tuple(npe::move(sv_row_major), npe::move(svi), npe::move(svj), npe::move(sf)); - -npe_end_code() - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Binding for remove_duplicate_vertices without face remapping + auto remove_duplicate_vertices( + const nb::DRef &V, + const double epsilon) + { + Eigen::MatrixXN SV; + Eigen::VectorXI SVI, SVJ; + igl::remove_duplicate_vertices(V, epsilon, SV, SVI, SVJ); + return std::make_tuple(SV, SVI, SVJ); + } + + // Binding for remove_duplicate_vertices with face remapping + auto remove_duplicate_vertices_F( + const nb::DRef &V, + const nb::DRef &F, + const double epsilon) + { + Eigen::MatrixXN SV; + Eigen::VectorXI SVI, SVJ; + Eigen::MatrixXI SF; + igl::remove_duplicate_vertices(V, F, epsilon, SV, SVI, SVJ, SF); + return std::make_tuple(SV, SVI, SVJ, SF); + } +} + +// Bind the wrapper to the Python module +void bind_remove_duplicate_vertices(nb::module_ &m) +{ + m.def( + "remove_duplicate_vertices", + &pyigl::remove_duplicate_vertices, + "V"_a, + "epsilon"_a, + R"(Remove duplicate vertices up to a uniqueness tolerance (epsilon). + + @param[in] V #V by dim list of vertex positions + @param[in] epsilon Uniqueness tolerance used coordinate-wise + @return Tuple containing: + - SV: #SV by dim new list of unique vertex positions + - SVI: #SV list of indices so SV = V(SVI,:) + - SVJ: #V list of indices so V = SV(SVJ,:))"); + + m.def( + "remove_duplicate_vertices", + &pyigl::remove_duplicate_vertices_F, + "V"_a, + "F"_a, + "epsilon"_a, + R"(Remove duplicate vertices and remap faces to new indices. + + @param[in] V #V by dim list of vertex positions + @param[in] F #F by dim list of face indices + @param[in] return_SVJ If true, return the SVJ mapping indices + @return Tuple containing: + - SV: #SV by dim new list of unique vertex positions + - SVI: #SV list of indices so SV = V(SVI,:) + - SVJ: #V list of indices so V = SV(SVJ,:) + - SF: #F by dim list of remapped face indices into SV)"); +} diff --git a/src/remove_unreferenced.cpp b/src/remove_unreferenced.cpp index 81e59509..a23d6b16 100644 --- a/src/remove_unreferenced.cpp +++ b/src/remove_unreferenced.cpp @@ -1,65 +1,41 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example - -#include -#include -#include +#include "default_types.h" #include - -const char* ds_remove_unreferenced = R"igl_Qu8mg5v7( - -Remove unreferenced vertices from V, updating F accordingly - -Parameters ----------- -V #V by dim list of mesh vertex positions -F #F by ss list of simplices (Values of -1 are quitely skipped) - - -Returns -------- -NV #NV by dim list of mesh vertex positions -NF #NF by ss list of simplices -IM #V by 1 list of indices such that: NF = IM(F) and NT = IM(T) - and V(find(IM<=size(NV,1)),:) = NV -J #RV by 1 list, such that RV = V(J,:) - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(remove_unreferenced) -npe_doc(ds_remove_unreferenced) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_tet_or_tri_mesh_23d(v, f); - EigenDenseLike nv; - EigenDenseLike nf; - Eigen::Matrix i, j; - igl::remove_unreferenced(v, f, nv, nf, i, j); - return std::make_tuple(npe::move(nv), npe::move(nf), npe::move(i), npe::move(j)); - -npe_end_code() +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto remove_unreferenced( + const nb::DRef &V, + const nb::DRef &F) + { + Eigen::MatrixXN NV; + Eigen::MatrixXI NF; + Eigen::VectorXI I,J; + igl::remove_unreferenced(V, F, NV, NF, I, J); + return std::make_tuple(NV, NF, I, J); + } +} + +// Bind the wrapper to the Python module +void bind_remove_unreferenced(nb::module_ &m) +{ + m.def( + "remove_unreferenced", + &pyigl::remove_unreferenced, + "F"_a, + "n"_a=0, +R"(Remove unreferenced vertices from V, updating F accordingly +@param[in] V #V by dim list of mesh vertex positions +@param[in] F #F by ss list of simplices (Values of -1 are quitely skipped) +@param[out] NF #NF by ss list of simplices +@param[out] I #V by 1 list of indices such that: NF = IM(F) and NT = IM(T) + and V(find(IM<=size(NV,1)),:) = NV +@param[out] J #NV by 1 list, such that NV = V(J,:))"); +} diff --git a/src/resolve_duplicated_faces.cpp b/src/resolve_duplicated_faces.cpp deleted file mode 100644 index a5ea1234..00000000 --- a/src/resolve_duplicated_faces.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char* ds_resolve_duplicated_faces = R"igl_Qu8mg5v7( - -Resolve duplicated faces according to the following rules per unique face: - - If the number of positively oriented faces equals the number of - negatively oriented faces, remove all duplicated faces at this triangle. - - If the number of positively oriented faces equals the number of - negatively oriented faces plus 1, keeps one of the positively oriented - face. - - If the number of positively oriented faces equals the number of - negatively oriented faces minus 1, keeps one of the negatively oriented - face. - - If the number of postively oriented faces differ with the number of - negativley oriented faces by more than 1, the mesh is not orientable. - An exception will be thrown. - -Parameters ----------- -F1 #F1 by 3 array of input faces. - -Returns -------- -F2 #F2 by 3 array of output faces without duplicated faces. -J #F2 list of indices into F1. - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(resolve_duplicated_faces) -npe_doc(ds_resolve_duplicated_faces) - -npe_arg(f1, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_tri_mesh_faces(f1, "f1"); - // TODO: remove __copy - // same problem that DerivedF1 causes problem, can be prevented - Eigen::MatrixXi f1_copy = f1.template cast(); - Eigen::MatrixXi f2_copy; - Eigen::Matrix j; - igl::resolve_duplicated_faces(f1_copy, f2_copy, j); - EigenDenseLike f2 = f2_copy.template cast(); - return std::make_tuple(npe::move(f2), npe::move(j)); - -npe_end_code() - - diff --git a/src/rigid_alignment.cpp b/src/rigid_alignment.cpp deleted file mode 100644 index 4f77b3b9..00000000 --- a/src/rigid_alignment.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_rigid_alignment = R"igl_Qu8mg5v7( - -Find the rigid transformation that best aligns the 3D points X to their -corresponding points P with associated normals N. - -min ‖(X*R+t-P)'N‖² -R∈SO(3) -t∈R³ - -Parameters ----------- - -X #X by 3 list of query points -P #X by 3 list of corresponding (e.g., closest) points -N #X by 3 list of unit normals for each row in P - -Returns -------- - -R 3 by 3 rotation matrix -t 1 by 3 translation vector - -See also --------- -icp - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(rigid_alignment) -npe_doc(ds_rigid_alignment) - -npe_arg(x, dense_float, dense_double) -npe_arg(p, npe_matches(x)) -npe_arg(n, npe_matches(x)) - - -npe_begin_code() - assert_cols_equals(x, 3, "x"); - - assert_rows_match(x, p, "x", "p"); - assert_rows_match(x, n, "x", "n"); - - assert_cols_match(x, p, "x", "p"); - assert_cols_match(x, n, "x", "n"); - - EigenDense x_copy = x; - - EigenDense r; - Eigen::Matrix t; - igl::rigid_alignment(x_copy, p, n, r, t); - return std::make_tuple(npe::move(r), npe::move(t)); - -npe_end_code() - - diff --git a/src/rotate_vectors.cpp b/src/rotate_vectors.cpp deleted file mode 100644 index c78e1693..00000000 --- a/src/rotate_vectors.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - - - -#include - -const char *ds_rotate_vectors = R"igl_Qu8mg5v7( - Rotate the vectors V by A radiants on the tangent plane spanned by B1 and B2 - -Parameters ----------- -V #V by 3 eigen Matrix of vectors -A #V eigen vector of rotation angles or a single angle to be applied to all vectors -B1 #V by 3 eigen Matrix of base vector 1 -B2 #V by 3 eigen Matrix of base vector 2 -Returns -------- -Returns the rotated vectors - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(rotate_vectors) -npe_doc(ds_rotate_vectors) - -npe_arg(v, dense_float, dense_double) -npe_arg(a, npe_matches(v)) -npe_arg(b1, npe_matches(v)) -npe_arg(b2, npe_matches(v)) - - -npe_begin_code() - if(a.size() > 1) - assert_rows_match(v, a, "V", "A"); - assert_rows_match(v, b1, "V", "B1"); - assert_rows_match(v, b2, "V", "B2"); - - assert_cols_equals(v, 3, "V"); - assert_cols_equals(b1, 3, "B1"); - assert_cols_equals(b2, 3, "B2"); - assert_cols_equals(a, 1, "A"); - - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::VectorXd a_copy = a.template cast(); - Eigen::MatrixXd b1_copy = b1.template cast(); - Eigen::MatrixXd b2_copy = b2.template cast(); - - Eigen::MatrixXd res = igl::rotate_vectors(v_copy, a_copy, b1_copy, b2_copy); - EigenDenseLike res_row_maj = res.template cast(); - return npe::move(res_row_maj); - - npe_end_code() diff --git a/src/sample_edges.cpp b/src/sample_edges.cpp deleted file mode 100644 index 2ed96c45..00000000 --- a/src/sample_edges.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - - - -#include - -const char *ds_sample_edges = R"igl_Qu8mg5v7( -Compute samples_per_edge extra points along each edge in E defined over - vertices of V. - -Parameters ----------- -V vertices over which edges are defined, # vertices by dim -E edge list, # edges by 2 -k number of extra samples to be computed along edge not including start and end points - -Returns -------- -S sampled vertices, size less than # edges * (2+k) by dim always begins - with V so that E is also defined over S - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(sample_edges) -npe_doc(ds_sample_edges) - -npe_arg(v, dense_float, dense_double) -npe_arg(e, dense_int32, dense_int64) -npe_arg(k, int) - - -npe_begin_code() - assert_nonzero_rows(v, "v"); - assert_nonzero_rows(e, "e"); - assert_cols_equals(e, 2, "e"); - - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::MatrixXi e_copy = e.template cast(); - Eigen::MatrixXd s_copy; - igl::sample_edges(v_copy, e_copy, k, s_copy); - EigenDenseLike s = s_copy.template cast(); - return npe::move(s); - -npe_end_code() - - diff --git a/src/segment_segment_intersect.cpp b/src/segment_segment_intersect.cpp deleted file mode 100644 index be7edf1d..00000000 --- a/src/segment_segment_intersect.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example -// error at line 33 and 41, saying cross is only for certain size matrices - -#include -#include -#include - - - - - - -#include - -const char* ds_segments_intersect = R"igl_Qu8mg5v7( - -Determine whether two line segments A,B intersect - A: p + t*r : t \in [0,1] - B: q + u*s : u \in [0,1] - -Parameters ----------- -p 3-vector origin of segment A -r 3-vector direction of segment A -q 3-vector origin of segment B -s 3-vector direction of segment B -eps precision - - -Returns -------- -t scalar point of intersection along segment A, t \in [0,1] -u scalar point of intersection along segment B, u \in [0,1] -Returns true if intersection - - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(segments_intersect) -npe_doc(ds_segments_intersect) - -npe_arg(p, dense_float, dense_double) -npe_arg(r, npe_matches(p)) -npe_arg(q, npe_matches(p)) -npe_arg(s, npe_matches(p)) - - -npe_begin_code() - assert_shape_equals(p, 3, 1, "p"); - assert_shapes_match(p, r, "p", "r"); - assert_shapes_match(p, q, "p", "q"); - assert_shapes_match(p, s, "p", "s"); - // TODO: remove __copy - Eigen::Vector3d p_copy = p.template cast(); - Eigen::Vector3d r_copy = r.template cast(); - Eigen::Vector3d q_copy = q.template cast(); - Eigen::Vector3d s_copy = s.template cast(); - - double t; - double u; - double eps; - bool is_intersect = igl::segment_segment_intersect(p_copy, r_copy, q_copy, s_copy, t, u, eps); - return std::make_tuple(is_intersect, t, u, eps); - -npe_end_code() - - diff --git a/src/shape_diameter_function.cpp b/src/shape_diameter_function.cpp deleted file mode 100644 index 3b9806aa..00000000 --- a/src/shape_diameter_function.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example __miss - - -#include -#include -#include -#include - -const char* ds_shape_diameter_function = R"igl_Qu8mg5v7( - -Compute shape diamater function per given point. In the parlence of the - paper "Consistent Mesh Partitioning and Skeletonisation using the Shape - Diameter Function" [Shapiro et al. 2008], this implementation uses a 180° - cone and a _uniform_ average (_not_ a average weighted by inverse angles). - -Parameters ----------- -V #V by 3 list of mesh vertex positions -F #F by 3 list of mesh face indices into V -P #P by 3 list of origin points -N #P by 3 list of origin normals - - -Returns -------- -S #P list of shape diamater function values between bounding box - diagonal (perfect sphere) and 0 (perfect needle hook) - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(shape_diameter_function) -npe_doc(ds_shape_diameter_function) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(p, npe_matches(v)) -npe_arg(n, npe_matches(v)) -npe_arg(num_samples, int) - -npe_begin_code() - - assert_valid_3d_tri_mesh(v, f); - assert_nonzero_rows(p, "p"); - assert_shapes_match(p, n, "p", "n"); - EigenDenseLike s; - igl::shape_diameter_function(v, f, p, n, num_samples, s); - return npe::move(s); - -npe_end_code() - - diff --git a/src/sharp_edges.cpp b/src/sharp_edges.cpp deleted file mode 100644 index d57fffcc..00000000 --- a/src/sharp_edges.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -#include - -const char *ds_sharp_edges = R"igl_Qu8mg5v7xxxx( -SHARP_EDGES Given a mesh, compute sharp edges. - -Parameters ----------- -V #V by 3 list of vertex positions -F #F by 3 list of triangle mesh indices into V -angle dihedral angle considered to sharp (e.g., igl::PI * 0.11) - -Returns -------- -SE #SE by 2 list of edge indices into V -E #e by 2 list of edges in no particular order -uE #uE by 2 list of unique undirected edges -EMAP #F*3 list of indices into uE, mapping each directed edge to unique -undirected edge so that uE(EMAP(f+#F*c)) is the unique edge -corresponding to E.row(f+#F*c) -uE2E #uE list of lists of indices into E of coexisting edges, so that -E.row(uE2E[i][j]) corresponds to uE.row(i) for all j in -0..uE2E[i].size()-1. -sharp #SE list of indices into uE revealing sharp undirected edges - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7xxxx"; - -npe_function(sharp_edges) -npe_doc(ds_sharp_edges) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(angle, double) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - - // Eigen::MatrixXd v_copy = v.template cast(); - // Eigen::MatrixXi e_copy = e.template cast(); - // Eigen::MatrixXd s_copy; - - EigenDense se; - EigenDense e; - EigenDense ue; - Eigen::Matrix emap; - std::vector> u_e2_e; - std::vector sharp; - igl::sharp_edges(v, f, angle, se, e, ue, emap, u_e2_e, sharp); - return std::make_tuple(npe::move(se), npe::move(e), npe::move(ue), npe::move(emap), u_e2_e, sharp); - - npe_end_code() diff --git a/src/signed_angle.cpp b/src/signed_angle.cpp deleted file mode 100644 index 36b1ac40..00000000 --- a/src/signed_angle.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_signed_angle = R"igl_Qu8mg5v7( - - Compute the signed angle subtended by the oriented 3d triangle (A,B,C) at some point P - - -Parameters ----------- -A 2D position of corner -B 2D position of corner -P 2D position of query point - -Returns -------- -returns signed angle - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(signed_angle) -npe_doc(ds_signed_angle) - -npe_arg(a, dense_float, dense_double) -npe_arg(b, npe_matches(a)) -npe_arg(p, npe_matches(a)) - - -npe_begin_code() - assert_size_equals(a, 2, "a"); - assert_size_equals(b, 2, "b"); - assert_size_equals(p, 2, "p"); - - Eigen::Vector2d a_copy, b_copy, p_copy; - - if(a.cols() == 1) - { - a_copy(0) = a(0, 0); - a_copy(1) = a(1, 0); - } - else - { - a_copy(0) = a(0, 0); - a_copy(1) = a(0, 1); - } - - if (b.cols() == 1) - { - b_copy(0) = b(0, 0); - b_copy(1) = b(1, 0); - } - else - { - b_copy(0) = b(0, 0); - b_copy(1) = b(0, 1); - } - - if (p.cols() == 1) - { - p_copy(0) = p(0, 0); - p_copy(1) = p(1, 0); - } - else - { - p_copy(0) = p(0, 0); - p_copy(1) = p(0, 1); - } - - const auto angle = igl::signed_angle(a_copy, b_copy, p_copy); - return angle; - -npe_end_code() - - diff --git a/src/signed_distance.cpp b/src/signed_distance.cpp index 4da33ef5..6b55e09f 100644 --- a/src/signed_distance.cpp +++ b/src/signed_distance.cpp @@ -1,108 +1,63 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Francis Williams -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - +#include "default_types.h" #include - -const char *ds_signed_distance = R"igl_Qu8mg5v7( -SIGNED_DISTANCE computes signed distance to a mesh - - -Parameters ----------- - P #P by 3 list of query point positions - V #V by 3 list of vertex positions - F #F by ss list of triangle indices, ss should be 3 unless sign_type - sign_type (Optional, defaults to psuedo-normal) Sign type method for - determining sign on signed distance - return_normals (Optional, defaults to False) If set to True, will return pseudonormals of - closest points to each query point in P -Returns -------- - S #P list of smallest signed distances - I #P list of facet indices corresponding to smallest distances - C #P by 3 list of closest points - -See also --------- - - -Notes ------ - Known issue: This only computes distances to triangles. So unreferenced - vertices and degenerate triangles are ignored. - -Examples --------- ->>> S, I, C = signed_distance(P, V, F, sign_type=igl.SIGNED_DISTANCE_TYPE_FAST_WINDING_NUMBER, return_normals=False) ->>> S, I, C, N = signed_distance(P, V, F, return_normals=True) - -)igl_Qu8mg5v7"; - -npe_function(signed_distance) -npe_doc(ds_signed_distance) - -npe_arg(p, dense_float, dense_double) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_default_arg(sign_type, int, int(igl::SIGNED_DISTANCE_TYPE_DEFAULT)) -npe_default_arg(return_normals, bool, false) - -npe_begin_code() - assert_cols_equals(p, 3, "p"); - assert_nonzero_rows(p, "p"); - assert_valid_3d_tri_mesh(v, f, "v", "f"); - - // ensure valid sign_type given - if ((sign_type < 0) || (sign_type > igl::NUM_SIGNED_DISTANCE_TYPE)) { - //NOTE: compiler concats adjacent string literals. - throw pybind11::value_error( - "Parameter sign_type invalid, must be one of:" - "\n\t0: Use fast pseudo-normal test [Bærentzen & Aanæs 2005]" - "\n\t1: Use winding number [Jacobson, Kavan Sorking-Hornug 2013]" - "\n\t2: Default (pseudo-normal)" - "\n\t3: Unsigned" - "\n\t4: Use Fast winding number [Barill, Dickson, Schmidt, Levin, Jacobson 2018]\n" - ); - } - - //Ensure if normals requested, sign type is also SIGNED_DISTANCE_TYPE_PSEUDONORMAL - if (return_normals) { - // note: if sign_type is default, we assume they don't care and switch to pseudonormal! - if (sign_type == igl::SIGNED_DISTANCE_TYPE_DEFAULT) { - sign_type = igl::SIGNED_DISTANCE_TYPE_PSEUDONORMAL; - } else if (sign_type != igl::SIGNED_DISTANCE_TYPE_PSEUDONORMAL ) { - throw pybind11::value_error("Parameter sign_type must be SIGNED_DISTANCE_TYPE_PSEUDONORMAL for normals to be returned. Or return_normals should be false."); - } - } - - Eigen::MatrixXd V = v.template cast(); - Eigen::MatrixXd P = p.template cast(); - Eigen::MatrixXi F = f.template cast(); - - EigenDenseLike S; - Eigen::Matrix I; - EigenDenseLike C; - EigenDenseLike N; - - if (return_normals) { - igl::signed_distance(P, V, F, igl::SIGNED_DISTANCE_TYPE_PSEUDONORMAL, S, I, C, N); - return pybind11::make_tuple(npe::move(S), npe::move(I), npe::move(C), npe::move(N)); - } else { - // N is only populated when sign_type == SIGNED_DISTANCE_TYPE_PSEUDONORMAL - igl::signed_distance(P, V, F, static_cast(sign_type), S, I, C, N); - return pybind11::make_tuple(npe::move(S), npe::move(I), npe::move(C)); - } - -npe_end_code() - - - +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto signed_distance( + const nb::DRef &P, + const nb::DRef &V, + const nb::DRef &F, + const igl::SignedDistanceType sign_type, + const Numeric lower_bound = -std::numeric_limits::infinity(), + const Numeric upper_bound = std::numeric_limits::infinity()) + { + Eigen::VectorXN S; + Eigen::VectorXI I; + Eigen::MatrixXN C,N; + igl::signed_distance(P, V, F, sign_type, lower_bound, upper_bound, S, I, C, N); + return std::make_tuple(S, I, C, N); + } +} + +void bind_signed_distance(nb::module_ &m) +{ + nb::enum_(m, "SignedDistanceType") + .value("SIGNED_DISTANCE_TYPE_PSEUDONORMAL", igl::SIGNED_DISTANCE_TYPE_PSEUDONORMAL) + .value("SIGNED_DISTANCE_TYPE_WINDING_NUMBER", igl::SIGNED_DISTANCE_TYPE_WINDING_NUMBER) + .value("SIGNED_DISTANCE_TYPE_UNSIGNED", igl::SIGNED_DISTANCE_TYPE_UNSIGNED) + .value("SIGNED_DISTANCE_TYPE_FAST_WINDING_NUMBER", igl::SIGNED_DISTANCE_TYPE_FAST_WINDING_NUMBER) + .value("SIGNED_DISTANCE_TYPE_DEFAULT", igl::SIGNED_DISTANCE_TYPE_DEFAULT) + .export_values() + ; + m.def( + "signed_distance", + &pyigl::signed_distance, + "P"_a, + "V"_a, + "F"_a, + "sign_type"_a = igl::SIGNED_DISTANCE_TYPE_DEFAULT, + "lower_bound"_a = -std::numeric_limits::infinity(), + "upper_bound"_a = std::numeric_limits::infinity(), +R"(Computes signed distance to a mesh. + +@param[in] P #P by (2|3) list of query point positions +@param[in] V #V by (2|3) list of vertex positions +@param[in] F #F by ss list of triangle indices +@param[in] sign_type method for computing distance sign: "pseudonormal", "winding_number", "unsigned", "fast_winding_number", or "default" +@param[in] lower_bound lower bound of distances needed (default: -inf) +@param[in] upper_bound upper bound of distances needed (default: inf) +@return Tuple containing: + - S: #P list of smallest signed distances + - I: #P list of facet indices corresponding to smallest distances + - C: #P by (2|3) list of closest points + - N: #P by (2|3) list of closest normals (empty unless sign_type="pseudonormal"))"); +} diff --git a/src/simplify_polyhedron.cpp b/src/simplify_polyhedron.cpp deleted file mode 100644 index 9f642cfc..00000000 --- a/src/simplify_polyhedron.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - - - -#include - -const char *ds_simplify_polyhedron = R"igl_Qu8mg5v7( - - Simplify a polyhedron represented as a triangle mesh (OV,OF) by collapsing - any edge that doesn't contribute to defining surface's pointset. This - _would_ also make sense for open and non-manifold meshes, but the current - implementation only works with closed manifold surfaces with well defined - triangle normals. - -Parameters ----------- - OV #OV by 3 list of input mesh vertex positions - OF #OF by 3 list of input mesh triangle indices into OV - -Returns -------- - V #V by 3 list of output mesh vertex positions - F #F by 3 list of input mesh triangle indices into V - J #F list of indices into OF of birth parents - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(simplify_polyhedron) -npe_doc(ds_simplify_polyhedron) - -npe_arg(ov, dense_float, dense_double) -npe_arg(of, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_3d_tri_mesh(ov, of, "ov", "of"); - Eigen::MatrixXd ov_copy = ov.template cast(); - Eigen::MatrixXi of_copy = of.template cast(); - Eigen::MatrixXd v_copy; - Eigen::MatrixXi f_copy; - Eigen::VectorXi j_copy; - igl::simplify_polyhedron(ov_copy, of_copy, v_copy, f_copy, j_copy); - - EigenDenseLike v = v_copy.template cast < typename npe_Matrix_ov::Scalar >(); - EigenDenseLike f = f_copy.template cast(); - Eigen::Matrix j = j_copy.template cast(); - - return std::make_tuple(npe::move(v), npe::move(f), npe::move(j)); - - npe_end_code() diff --git a/src/slim.cpp b/src/slim.cpp new file mode 100644 index 00000000..4948fa37 --- /dev/null +++ b/src/slim.cpp @@ -0,0 +1,78 @@ +#include "default_types.h" +// Not templated at all +#include +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto slim_precompute( + const Eigen::MatrixXd &V, // maybe copy here to avoid yet another copy in slim + const Eigen::MatrixXi &F, + const Eigen::MatrixXd &V_init, + const igl::MappingEnergyType slim_energy, + const Eigen::VectorXi &b, + const Eigen::MatrixXd &bc, + const double soft_p) + { + igl::SLIMData data; + igl::slim_precompute(V, F, V_init, data, slim_energy, b, bc, soft_p); + return data; + } + + auto slim_solve( + igl::SLIMData &data, + int iter_num) + { + return igl::slim_solve(data, iter_num); + } + +} + +// Bind the wrapper to the Python module +void bind_slim(nb::module_ &m) +{ + nb::class_(m, "SLIMData") + .def(nb::init()); + m.def( + "slim_precompute", + &pyigl::slim_precompute, + "V"_a, + "F"_a, + "V_init"_a, + "slim_energy"_a, + "b"_a, + "bc"_a, + "soft_p"_a=1e5, + R"(Precompute data for SLIM optimization. + + @param[in] V #V by 3 list of mesh vertex positions + @param[in] F #F by (3|4) list of mesh elements (triangles or tetrahedra) + @param[in] V_init #V by 3 list of initial mesh vertex positions + @param[in] slim_energy Energy to minimize + @param[in] b list of boundary indices into V + @param[in] bc #b by 3 list of boundary conditions + @param[in] soft_p Soft penalty factor (can be zero)"); + m.def( + "slim_solve", + &pyigl::slim_solve, + "data"_a, + "iter_num"_a=1, + R"(Run iterations of SLIM optimization. + + @param[in] data Precomputation data structure + @param[in] iter_num Number of iterations to run + @return #V by 3 list of mesh vertex positions)"); +} + + + + + diff --git a/src/snap_points.cpp b/src/snap_points.cpp deleted file mode 100644 index 65791641..00000000 --- a/src/snap_points.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - - -const char *ds_snap_points = R"igl_Qu8mg5v7( -SNAP_POINTS snap list of points C to closest of another list of points V -[I,minD,VI] = snap_points(C,V) - -Parameters ----------- -C #C by dim list of query point positions -V #V by dim list of data point positions - -Returns -------- -I #C list of indices into V of closest points to C -minD #C list of squared (^p) distances to closest points -VI #C by dim list of new point positions, VI = V(I,:) - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(snap_points) -npe_doc(ds_snap_points) - -npe_arg(c, dense_float, dense_double) -npe_arg(v, npe_matches(c)) - - -npe_begin_code() - assert_cols_match(c, v, "c", "v"); - assert_nonzero_rows(c, "c"); - assert_nonzero_rows(v, "v"); - - EigenDenseInt i; - EigenDense min_d; - EigenDense vi; - igl::snap_points(c, v, i, min_d, vi); - return std::make_tuple(npe::move(i), npe::move(min_d), npe::move(vi)); - -npe_end_code() diff --git a/src/solid_angle.cpp b/src/solid_angle.cpp deleted file mode 100644 index 7eb0118f..00000000 --- a/src/solid_angle.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_solid_angle = R"igl_Qu8mg5v7( - Compute the signed solid angle subtended by the oriented 3d triangle (A,B,C) at some point P - -Parameters ----------- - A 3D position of corner - B 3D position of corner - C 3D position of corner - P 3D position of query point - -Returns -------- - Returns signed solid angle - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(solid_angle) -npe_doc(ds_solid_angle) - -npe_arg(a, dense_float, dense_double) -npe_arg(b, npe_matches(a)) -npe_arg(c, npe_matches(a)) -npe_arg(p, npe_matches(a)) - - -npe_begin_code() - assert_size_equals(a, 3, "a"); - assert_size_equals(b, 3, "b"); - assert_size_equals(c, 3, "c"); - assert_size_equals(p, 3, "p"); - - using Scalar = typename npe_Matrix_a::Scalar; - using MatType = Eigen::Matrix; - //fixme whit eigen flag - Eigen::Map a_tmp(&a(0, 0), a.size()); - Eigen::Map b_tmp(&b(0, 0), b.size()); - Eigen::Map c_tmp(&c(0, 0), c.size()); - Eigen::Map p_tmp(&p(0, 0), p.size()); - - double res = igl::solid_angle(a_tmp, b_tmp, c_tmp, p_tmp); - return res; - -npe_end_code() - - diff --git a/src/sort_angles.cpp b/src/sort_angles.cpp deleted file mode 100644 index 2cd96779..00000000 --- a/src/sort_angles.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example - -#include -#include -#include -#include - -const char* ds_sort_angles = R"igl_Qu8mg5v7( - -Sort angles in ascending order in a numerically robust way. -Instead of computing angles using atan2(y, x), sort directly on (y, x). -Parameters ---------- -M: m by n matrix of scalars. (n >= 2). Assuming the first column of M - contains values for y, and the second column is x. Using the rest - of the columns as tie-breaker. - -Returns -------- -R: an array of m indices. M.row(R[i]) contains the i-th smallest - angle. - -See also --------- - - -Notes ------ -None. - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(sort_angles) -npe_doc(ds_sort_angles) - -npe_arg(m, dense_float, dense_double) - -npe_begin_code() - - assert_nonzero_rows(m, "m"); - //TODO: need to check column >= 2 - // In the libigl code r is column major, and using row major fails assertion - // EIGEN_STATIC_ASSERT((EIGEN_IMPLIES(MaxRowsAtCompileTime==1 && - // MaxColsAtCompileTime!=1, (Options&RowMajor)==RowMajor) - // FIXME: vector not allowing row major, but they should be essentially the same so i feel we can leave it as col major - Eigen::Matrix r; - igl::sort_angles(m, r); - return npe::move(r); - -npe_end_code() - - diff --git a/src/sparse_voxel_grid.cpp b/src/sparse_voxel_grid.cpp deleted file mode 100644 index 08127abf..00000000 --- a/src/sparse_voxel_grid.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - -#include -#include - -#include - -const char *ds_sparse_voxel_grid = R"igl_Qu8mg5v7( - -Given a point, p0, on an isosurface, construct a shell of epsilon sized cubes surrounding the surface. -These cubes can be used as the input to marching cubes. - - -Parameters ----------- -p0 A 3D point on the isosurface surface defined by scalarFunc(x) = 0 -scalarFunc A scalar function from R^3 to R -- points which map to 0 lie - on the surface, points which are negative lie inside the surface, - and points which are positive lie outside the surface -eps The edge length of the cubes surrounding the surface -expected_number_of_cubes This pre-allocates internal data structures to speed things up - -Returns -------- - -CS #cube-vertices by 1 list of scalar values at the cube vertices -CV #cube-vertices by 3 list of cube vertex positions -CI #number of cubes by 8 list of indexes into CS and CV. Each row represents a cube - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(sparse_voxel_grid) -npe_doc(ds_sparse_voxel_grid) - -npe_arg(p0, EigenDenseFloat) -npe_arg(scalar_func, const std::function< typename EigenDenseFloat::Scalar (Eigen::Matrix &) >) -npe_arg(eps, double) -npe_arg(expected_number_of_cubes, int) - - -npe_begin_code() - assert_size_equals(p0, 3, "p0"); - - EigenDenseFloat cs; - EigenDenseFloat cv; - EigenDenseInt ci; - Eigen::Matrix p0_copy; - if(p0.cols() == 1) - p0_copy = p0.transpose(); - else - p0_copy = p0; - - igl::sparse_voxel_grid(p0_copy, scalar_func, eps, expected_number_of_cubes, cs, cv, ci); - return std::make_tuple(npe::move(cs), npe::move(cv), npe::move(ci)); - -npe_end_code() - - diff --git a/src/split_nonmanifold.cpp b/src/split_nonmanifold.cpp new file mode 100644 index 00000000..96f076e9 --- /dev/null +++ b/src/split_nonmanifold.cpp @@ -0,0 +1,55 @@ +#include "default_types.h" +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto split_nonmanifold( + const nb::DRef & F) + { + Eigen::MatrixXI SF; + Eigen::VectorXI SVI; + igl::split_nonmanifold(F, SF, SVI); + return std::make_tuple(SF, SVI); + } + auto split_nonmanifold_VF( + const nb::DRef & V, + const nb::DRef & F) + { + Eigen::MatrixXI SF; + Eigen::MatrixXN SV; + Eigen::VectorXI SVI; + igl::split_nonmanifold(V, F, SV, SF, SVI); + return std::make_tuple(SV, SF, SVI); + } +} + +void bind_split_nonmanifold(nb::module_ &m) +{ + m.def("split_nonmanifold", &pyigl::split_nonmanifold, + "F"_a, + R"(Split a non-manifold (or non-orientable) mesh into a orientable manifold +mesh possibly with more connected components and geometrically duplicate +vertices. +@param[in] F #F by 3 list of mesh triangle indices into rows of some V +@param[out] SF #F by 3 list of mesh triangle indices into rows of a new vertex list + SV = V(SVI,:) +@param[out] SVI #SV list of indices into V identifying vertex positions)"); + m.def("split_nonmanifold", &pyigl::split_nonmanifold_VF, + "V"_a, + "F"_a, + R"(Split a non-manifold (or non-orientable) mesh into a orientable manifold +mesh possibly with more connected components and geometrically duplicate +vertices. +@param[in] V #V by dim explicit list of vertex positions +@param[in] F #F by 3 list of mesh triangle indices into rows of some V +@param[out] SV #SV by dim explicit list of vertex positions +@param[out] SF #F by 3 list of mesh triangle indices into rows of a new vertex list + SV = V(SVI,:) +@param[out] SVI #SV list of indices into V identifying vertex positions)"); +} diff --git a/src/squared_edge_lengths.cpp b/src/squared_edge_lengths.cpp new file mode 100644 index 00000000..c2513558 --- /dev/null +++ b/src/squared_edge_lengths.cpp @@ -0,0 +1,39 @@ +#include "default_types.h" +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for squared_edge_lengths + Eigen::MatrixXN squared_edge_lengths( + const nb::DRef &V, + const nb::DRef &F) + { + Eigen::MatrixXN L; + igl::squared_edge_lengths(V, F, L); + return L; + } +} + +// Bind the wrapper to the Python module +void bind_squared_edge_lengths(nb::module_ &m) +{ + m.def( + "squared_edge_lengths", + &pyigl::squared_edge_lengths, + "V"_a, + "F"_a, +R"(Constructs a list of squared lengths of edges opposite each index in a face (triangle/tet) list. + +@param[in] V #V by 3 eigen matrix of vertex positions +@param[in] F #F by (2|3|4) list of mesh edges, triangles, or tets +@return L #F by {1|3|6} matrix of squared edge lengths + - For edges, a single column of lengths + - For triangles, columns correspond to edges [1,2], [2,0], [0,1] + - For tets, columns correspond to edges [3 0], [3 1], [3 2], [1 2], [2 0], [0 1])"); +} diff --git a/src/swept_volume_bounding_box.cpp b/src/swept_volume_bounding_box.cpp deleted file mode 100644 index 980d85b3..00000000 --- a/src/swept_volume_bounding_box.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - -#include - -const char *ds_swept_volume_bounding_box = R"igl_Qu8mg5v7( -Construct an axis-aligned bounding box containing a shape undergoing a - motion sampled at `steps` discrete momements. - -Parameters ----------- -n number of mesh vertices -V function handle so that V(i,t) returns the 3d position of vertex i at time t, for t∈[0,1] -steps number of time steps: steps=3 --> t∈{0,0.5,1} - -Returns -------- -min,max corners of box containing mesh under motion - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(swept_volume_bounding_box) -npe_doc(ds_swept_volume_bounding_box) - -npe_arg(n, int) -npe_arg(v, std::function &) -npe_arg(steps, int &) - - -npe_begin_code() - - Eigen::AlignedBox3d box; - igl::swept_volume_bounding_box(n, v, steps, box); - - EigenDenseFloat mmin = box.min().template cast(); - EigenDenseFloat mmax = box.max().template cast(); - - return std::make_tuple(npe::move(mmin), npe::move(mmax)); - - npe_end_code() diff --git a/src/tet_tet_adjacency.cpp b/src/tet_tet_adjacency.cpp deleted file mode 100644 index b024e75d..00000000 --- a/src/tet_tet_adjacency.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -//TODO: __example - -#include -#include -#include -#include - -const char *ds_tet_tet_adjacency = R"igl_Qu8mg5v7( -Constructs the tet_tet adjacency matrix for a given tet mesh with tets T - -Parameters ----------- -T #T by 4 list of tets - -Returns -------- -TT #T by #4 adjacency matrix, the element i,j is the id of the tet adjacent to the j face of tet i -TTi #T by #4 adjacency matrix, the element i,j is the id of face of the tet TT(i,j) that is adjacent to tet i - -See also --------- - -Notes ------ -the first face of a tet is [0,1,2], the second [0,1,3], the third [1,2,3], and the fourth [2,0,3]. - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(tet_tet_adjacency) -npe_doc(ds_tet_tet_adjacency) -npe_arg(t, dense_int32, dense_int64) -npe_begin_code() - - assert_nonzero_rows(t, "t"); - assert_cols_equals(t, 4, "t"); - EigenDenseLike tt, tti; - igl::tet_tet_adjacency(t, tt, tti); - return std::make_tuple(npe::move(tt), npe::move(tti)); - -npe_end_code() diff --git a/src/topological_hole_fill.cpp b/src/topological_hole_fill.cpp deleted file mode 100644 index a5c7a55f..00000000 --- a/src/topological_hole_fill.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - -#include - -#include - -const char *ds_topological_hole_fill = R"igl_Qu8mg5v7( - -Topological fill hole on a mesh, with one additional vertex each hole -Index of new abstract vertices will be F.maxCoeff() + (index of hole) - -Parameters ----------- - -F #F by simplex-size list of element indices -holes vector of hole loops to fill - -Returns -------- -F_filled input F stacked with filled triangles. - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(topological_hole_fill) -npe_doc(ds_topological_hole_fill) - -npe_arg(f, dense_int32, dense_int64) -npe_arg(holes, std::vector>) - - -npe_begin_code() - - EigenDense f_filled; - igl::topological_hole_fill(f, holes, f_filled); - return npe::move(f_filled); - -npe_end_code() diff --git a/src/triangle/module.cpp b/src/triangle/module.cpp new file mode 100644 index 00000000..c9dd02d2 --- /dev/null +++ b/src/triangle/module.cpp @@ -0,0 +1,12 @@ +#include +namespace nb = nanobind; + +// generated by cmake +#include "triangle/BINDING_DECLARATIONS.in" + +NB_MODULE(pyigl_restricted_triangle, m) { + m.doc() = "libigl triangle module python bindings"; + // generated by cmake +#include "triangle/BINDING_INVOCATIONS.in" +} + diff --git a/src/triangle/scaf.cpp b/src/triangle/scaf.cpp new file mode 100644 index 00000000..c3ef5749 --- /dev/null +++ b/src/triangle/scaf.cpp @@ -0,0 +1,99 @@ +#include "default_types.h" +// Not templated at all +#include +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto scaf_precompute( + const Eigen::MatrixXd &V, // maybe copy here to avoid yet another copy in scaf + const Eigen::MatrixXi &F, + const Eigen::MatrixXd &V_init, + const igl::MappingEnergyType scaf_energy, + const Eigen::VectorXi &b, + const Eigen::MatrixXd &bc, + const double soft_p, + igl::triangle::SCAFData & data) + { + igl::triangle::scaf_precompute(V, F, V_init, scaf_energy, b, bc, soft_p, data); + } + auto scaf_solve( + const int iter_num, + igl::triangle::SCAFData &data) + { + return igl::triangle::scaf_solve(iter_num, data); + } + auto scaf_system( igl::triangle::SCAFData &s) + { + Eigen::SparseMatrix L; + Eigen::VectorXd rhs; + igl::triangle::scaf_system(s, L, rhs); + return std::make_tuple(L, rhs); + } + +} + +// Bind the wrapper to the Python module +void bind_scaf(nb::module_ &m) +{ + nb::class_(m, "SCAFData") + .def(nb::init<>()); + m.def( + "scaf_precompute", + &pyigl::scaf_precompute, + "V"_a, + "F"_a, + "V_init"_a, + "scaf_energy"_a, + "b"_a, + "bc"_a, + "soft_p"_a, + "data"_a, + R"(Compute necessary information to start using SCAF + +@param[in] V #V by 3 list of mesh vertex positions +@param[in] F #F by 3/3 list of mesh faces (triangles/tets) +@param[in] V_init #V by 3 list of initial mesh vertex positions +@param[in,out] data resulting precomputed data +@param[in] slim_energy Energy type to minimize +@param[in] b list of boundary indices into V (soft constraint) +@param[in] bc #b by dim list of boundary conditions (soft constraint) +@param[in] soft_p Soft penalty factor (can be zero) +)"); + m.def( + "scaf_solve", + &pyigl::scaf_solve, + "iter_num"_a, + "data"_a, + R"(Run iter_num iterations of SCAF, with precomputed data +@param[in] data precomputed data +@param[in] iter_num number of iterations to run +@returns resulting V_o (in SLIMData): #V by dim list of mesh vertex positions + )"); + m.def( + "scaf_system", + &pyigl::scaf_system, + "s"_a, + R"(Set up the SCAF system L * uv = rhs, without solving it. +@param[in] s: igl::SCAFData. Will be modified by energy and Jacobian computation. +@param[out] L: m by m matrix +@param[out] rhs: m by 1 vector +with m = dim * (#V_mesh + #V_scaf - #V_frame) + )"); + + +} + + + + + + diff --git a/src/triangle/triangulate.cpp b/src/triangle/triangulate.cpp index 0037a305..cd08212a 100644 --- a/src/triangle/triangulate.cpp +++ b/src/triangle/triangulate.cpp @@ -1,70 +1,60 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Alec Jacobson -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include - -#include -#include - +#include "default_types.h" #include +#include +#include +#include +#include +#include +#include -const char* ds_triangulate = R"igl_Qu8mg5v7( -Triangulate the interior of a polygon using the triangle library. - -Parameters ----------- - V : #V by 2 list of 2D vertex positions - E : #E by 2 list of vertex ids forming unoriented edges of the boundary of the polygon - H : #H by 2 coordinates of points contained inside holes of the polygon - flags : string of options pass to triangle (see triangle documentation) -Returns --------- - V2 #V2 by 2 coordinates of the vertives of the generated triangulation - F2 #F2 by 3 list of indices forming the faces of the generated triangulation - -)igl_Qu8mg5v7"; +namespace nb = nanobind; +using namespace nb::literals; -npe_function(triangulate) -npe_doc(ds_triangulate) - -npe_arg(V, dense_float, dense_double) -npe_arg(E, dense_int32, dense_int64) -npe_default_arg(H, npe_matches(V) ,pybind11::array()) -npe_default_arg(flags, std::string, "") -npe_default_arg(VM, npe_matches(E), pybind11::array()) -npe_default_arg(EM, npe_matches(E), pybind11::array()) -npe_begin_code() - -EigenDenseLike V2; -EigenDenseLike F2; -EigenDenseLike VM2,E2,EM2; -using V2Type = decltype(V2); -using F2Type = decltype(F2); -using VM2Type = decltype(VM2); -using E2Type = decltype(E2); -using EM2Type = decltype(EM2); -if(VM.size() == 0 && EM.size() == 0) -{ - igl::triangle::triangulate(V, E, H, flags, V2, F2); - return std::list({npe::move(V2), npe::move(F2)}); -}else +namespace pyigl { - igl::triangle::triangulate(V, E, H, VM.reshaped(), EM.reshaped(), flags, V2, F2, VM2, E2, EM2); - if(VM.size() && EM.size()) + auto triangulate( + const nb::DRef &V, + const nb::DRef &E, + const nb::DRef &H, + const nb::DRef &VM, + const nb::DRef &EM, + const std::string &flags) { - return std::list({npe::move(V2), npe::move(F2), npe::move(VM2), npe::move(E2), npe::move(EM2)}); - }else if(VM.size()) - { - return std::list({npe::move(V2), npe::move(F2), npe::move(VM2)}); - }else - { - return std::list({npe::move(V2), npe::move(F2), npe::move(E2), npe::move(EM2)}); + Eigen::MatrixXN V2; + Eigen::MatrixXI F2,E2; + Eigen::VectorXI VM2,EM2; + igl::triangle::triangulate(V,E,H,VM,EM,flags,V2,F2,VM2,E2,EM2); + return std::make_tuple(V2,F2,VM2,E2,EM2); } } -npe_end_code() +// Bind the wrapper to the Python module +void bind_triangulate(nb::module_ &m) +{ + m.def( + "triangulate", + &pyigl::triangulate, + "V"_a, + "E"_a = Eigen::MatrixXI(), + "H"_a = Eigen::MatrixXN(), + "VM"_a = Eigen::VectorXI(), + "EM"_a = Eigen::VectorXI(), + "flags"_a = "", +R"(Triangulate the interior of a polygon using the triangle library. + +@param[in] V #V by 2 list of 2D vertex positions +@param[in] E #E by 2 list of vertex ids forming unoriented edges of the boundary of the polygon +@param[in] H #H by 2 coordinates of points contained inside holes of the polygon +@param[in] VM #V list of markers for input vertices +@param[in] EM #E list of markers for input edges +@param[in] flags string of options pass to triangle (see triangle documentation) +@param[out] V2 #V2 by 2 coordinates of the vertives of the generated triangulation +@param[out] F2 #F2 by 3 list of indices forming the faces of the generated triangulation +@param[out] VM2 #V2 list of markers for output vertices +@param[out] E2 #E2 by 2 list of output edges +@param[out] EM2 #E2 list of markers for output edges)"); +} + + + diff --git a/src/triangle_fan.cpp b/src/triangle_fan.cpp index 85beb604..701e0708 100644 --- a/src/triangle_fan.cpp +++ b/src/triangle_fan.cpp @@ -1,56 +1,29 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - +#include "default_types.h" #include - -const char *ds_triangle_fan = R"igl_Qu8mg5v7( -Given a list of faces tessellate all of the "exterior" edges forming another - list of - -Parameters ----------- -E #E by 2 list of exterior edges (see exterior_edges.h) - -Returns -------- - -cap #cap by simplex_size list of "faces" tessellating the boundary edges - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(triangle_fan) -npe_doc(ds_triangle_fan) - -npe_arg(e, dense_int32, dense_int64) - - -npe_begin_code() - assert_nonzero_rows(e, "e"); - assert_cols_equals(e, 2, "e"); - Eigen::MatrixXi e_copy = e.template cast(); - Eigen::MatrixXi cap_copy; - igl::triangle_fan(e_copy, cap_copy); - EigenDenseLike cap = cap_copy.template cast(); - return npe::move(cap); - -npe_end_code() +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; +namespace pyigl +{ + auto triangle_fan( + const nb::DRef & E) + { + Eigen::MatrixXI cap; + igl::triangle_fan(E, cap); + return cap; + } +} + +void bind_triangle_fan(nb::module_ &m) +{ + m.def("triangle_fan", &pyigl::triangle_fan, + "E"_a, + R"(Given a list of faces tessellate all of the "exterior" edges forming another +list of +@param[in] E #E by simplex_size-1 list of exterior edges (see exterior_edges.h) +@param[out] cap #cap by simplex_size list of "faces" tessellating the boundary edges)"); +} diff --git a/src/triangle_triangle_adjacency.cpp b/src/triangle_triangle_adjacency.cpp index 36b8b541..6a6caa52 100644 --- a/src/triangle_triangle_adjacency.cpp +++ b/src/triangle_triangle_adjacency.cpp @@ -1,62 +1,57 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __miss __example - -#include -#include -#include +#include "default_types.h" #include - -const char* ds_triangle_triangle_adjacency = R"igl_Qu8mg5v7( - -Constructs the triangle-triangle adjacency matrix for a given - mesh (V,F). - -Parameters ----------- -F #F by simplex_size list of mesh faces (must be triangles) - -Returns -------- -TT #F by #3 adjacent matrix, the element i,j is the id of the triangle - adjacent to the j edge of triangle i -TTi #F by #3 adjacent matrix, the element i,j is the id of edge of the - triangle TT(i,j) that is adjacent with triangle i - - -See also --------- - - -Notes ------ -NOTE: the first edge of a triangle is [0,1] the second [1,2] and the third - [2,3]. this convention is DIFFERENT from cotmatrix_entries.h - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(triangle_triangle_adjacency) -npe_doc(ds_triangle_triangle_adjacency) - -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_tri_mesh_faces(f); - EigenDenseLike tt, t_ti; - igl::triangle_triangle_adjacency(f, tt, t_ti); - return std::make_tuple(npe::move(tt), npe::move(t_ti)); - -npe_end_code() - - +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto triangle_triangle_adjacency( const nb::DRef &F) + { + Eigen::MatrixXI TT; + Eigen::MatrixXI TTi; + igl::triangle_triangle_adjacency(F, TT, TTi); + return nb::make_tuple(TT, TTi); + } + auto triangle_triangle_adjacency_lists( const nb::DRef &F) + { + std::vector>> TT; + std::vector>> TTi; + igl::triangle_triangle_adjacency(F, true, TT, TTi); + return std::make_tuple(TT,TTi); + } +} + +// Bind the wrapper to the Python module +void bind_triangle_triangle_adjacency(nb::module_ &m) +{ + m.def( + "triangle_triangle_adjacency", + &pyigl::triangle_triangle_adjacency, + "F"_a, +R"(Constructs the triangle-triangle adjacency matrix for a given mesh (V,F). + +@param[in] F #F by 3 list of mesh faces (must be triangles) +@param[out] TT #F by 3 adjacent matrix, where each element represents the id of the triangle adjacent to the corresponding edge +@param[out] TTi (if return_TTi=True) #F by 3 adjacent matrix, where each element represents the id of the edge of the adjacent triangle that shares an edge with the current triangle + +- If `use_lists=True`, returns adjacency data as lists of lists for compatibility with non-manifold meshes. +)"); + m.def( + "triangle_triangle_adjacency_lists", + &pyigl::triangle_triangle_adjacency_lists, + "F"_a, +R"(Constructs the triangle-triangle adjacency matrix for a given mesh (V,F). + +@param[in] F #F by 3 list of mesh faces (must be triangles) +@param[out] TT #F by 3 adjacent matrix, where each element represents the id of the triangle adjacent to the corresponding edge +@param[out] TTi (if return_TTi=True) #F by 3 adjacent matrix, where each element represents the id of the edge of the adjacent triangle that shares an edge with the current triangle + +- If `use_lists=True`, returns adjacency data as lists of lists for compatibility with non-manifold meshes. +)"); +} diff --git a/src/triangles_from_strip.cpp b/src/triangles_from_strip.cpp deleted file mode 100644 index 4e9adabe..00000000 --- a/src/triangles_from_strip.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - -const char *ds_triangles_from_strip = R"igl_Qu8mg5v7( - -TRIANGLES_FROM_STRIP Create a list of triangles from a stream of indices - along a strip. - -Parameters ----------- -S #S list of indices - -Returns -------- -F #S-2 by 3 list of triangle indices - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(triangles_from_strip) -npe_doc(ds_triangles_from_strip) - -npe_arg(s, dense_int32, dense_int64) - - -npe_begin_code() - Eigen::Matrix s_copy(s.size()); - - for(int i = 0; i < s_copy.size(); ++i) - { - if(s.cols() == 1) - s_copy(i) = s(i, 0); - else if (s.rows() == 1) - s_copy(i) = s(0, i); - else - throw std::invalid_argument("Input s must have rows or cols equal to 1"); - } - - EigenDense f; - igl::triangles_from_strip(s_copy, f); - return npe::move(f); - -npe_end_code() - - diff --git a/src/triangulated_grid.cpp b/src/triangulated_grid.cpp index 0440bb0f..b62e9458 100644 --- a/src/triangulated_grid.cpp +++ b/src/triangulated_grid.cpp @@ -1,58 +1,45 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - +#include "default_types.h" #include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto triangulated_grid( + const int nx, + const int ny) + { + Eigen::MatrixXN GV; + Eigen::MatrixXI GF; + igl::triangulated_grid(nx,ny, GV,GF); + return std::make_tuple(GV,GF); + } +} + +// Bind the wrapper to the Python module +void bind_triangulated_grid(nb::module_ &m) +{ + m.def( + "triangulated_grid", + &pyigl::triangulated_grid, + "nx"_a, + "ny"_a, +R"(Create a regular grid of elements (only 2D supported, currently) Vertex +position order is compatible with `igl::grid` + +@param[in] nx number of vertices in the x direction +@param[in] ny number of vertices in the y direction +@param[out] GV nx*ny by 2 list of mesh vertex positions. +@param[out] GF 2*(nx-1)*(ny-1) by 3 list of triangle indices + +\see grid, quad_grid)" + ); +} -const char *ds_triangulated_grid = R"igl_Qu8mg5v7( - -Create a regular grid of elements (only 2D supported, currently) - Vertex position order is compatible with `igl::grid` - -Parameters ----------- -nx number of vertices in the x direction -ny number of vertices in the y direction - -Returns -------- - -GV nx*ny by 2 list of mesh vertex positions. -GF 2*(nx-1)*(ny-1) by 3 list of triangle indices - -See also --------- -grid, quad_grid - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(triangulated_grid) -npe_doc(ds_triangulated_grid) - -npe_arg(nx, int) -npe_arg(ny, int) - - -npe_begin_code() - EigenDenseFloat gv; - EigenDenseInt gf; - igl::triangulated_grid(nx, ny, gv, gf); - return std::make_tuple(npe::move(gv), npe::move(gf)); -npe_end_code() diff --git a/src/two_axis_valuator_fixed_up.cpp b/src/two_axis_valuator_fixed_up.cpp deleted file mode 100644 index 58657313..00000000 --- a/src/two_axis_valuator_fixed_up.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - - - - - -#include - -const char *ds_two_axis_valuator_fixed_up = R"igl_Qu8mg5v7( - -Applies a two-axis valuator drag rotation (as seen in Maya/Studio max) to a given rotation. - -Parameters ----------- -w width of the trackball context -h height of the trackball context -speed controls how fast the trackball feels, 1 is normal -down_quat rotation at mouse down, i.e. the rotation we're applying the - trackball motion to (as quaternion). **Note:** Up-vector that is fixed - is with respect to this rotation. -down_x position of mouse down -down_y position of mouse down -mouse_x current x position of mouse -mouse_y current y position of mouse - -Returns -------- -quat the resulting rotation (as quaternion) - -See also --------- -snap_to_fixed_up - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(two_axis_valuator_fixed_up) -npe_doc(ds_two_axis_valuator_fixed_up) - -npe_arg(w, int) -npe_arg(h, int) -npe_arg(speed, double) -npe_arg(down_quat, dense_float, dense_double) -npe_arg(down_x, int) -npe_arg(down_y, int) -npe_arg(mouse_x, int) -npe_arg(mouse_y, int) - - -npe_begin_code() - assert_size_equals(down_quat, 4, "down_quat"); - Eigen::Quaternion down_quat_copy; - if (down_quat.cols() == 1) - { - down_quat_copy.x() = down_quat(0, 0); - down_quat_copy.y() = down_quat(1, 0); - down_quat_copy.z() = down_quat(2, 0); - down_quat_copy.w() = down_quat(3, 0); - } - else - { - down_quat_copy.x() = down_quat(0, 0); - down_quat_copy.y() = down_quat(0, 1); - down_quat_copy.z() = down_quat(0, 2); - down_quat_copy.w() = down_quat(0, 3); - } - - Eigen::Quaternion quat_copy; - igl::two_axis_valuator_fixed_up(w, h, speed, down_quat_copy, down_x, down_y, mouse_x, mouse_y, quat_copy); - EigenDenseLike quat(4, 1); - quat(0) = quat_copy.x(); - quat(1) = quat_copy.y(); - quat(2) = quat_copy.z(); - quat(3) = quat_copy.w(); - - return npe::move(quat); - -npe_end_code() - - diff --git a/src/uniformly_sample_two_manifold.cpp b/src/uniformly_sample_two_manifold.cpp deleted file mode 100644 index 430262b9..00000000 --- a/src/uniformly_sample_two_manifold.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __example - -#include -#include -#include - - - - - - -#include - -const char* ds_uniformly_sample_two_manifold_internal = R"igl_Qu8mg5v7( - -UNIFORMLY_SAMPLE_TWO_MANIFOLD Attempt to sample a mesh uniformly by - furthest point relaxation as described in "Fast Automatic Skinning - Transformations" - - [Jacobson et al. 12] Section 3.3. - -Parameters ----------- -W #W by dim positions of mesh in weight space -F #F by 3 indices of triangles -k number of samplse -push factor by which corners should be pushed away - - -Returns -------- -WS k by dim locations in weights space - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(uniformly_sample_two_manifold_internal) -npe_doc(ds_uniformly_sample_two_manifold_internal) - -npe_arg(w, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(k, int) -npe_arg(push, double) - - -npe_begin_code() - - assert_valid_23d_tri_mesh(w, f); - // TODO: remove __copy - Eigen::MatrixXd w_copy = w.template cast(); - Eigen::MatrixXi f_copy = f.template cast(); - Eigen::MatrixXd ws; - igl::uniformly_sample_two_manifold(w_copy, f_copy, k, push, ws); - EigenDenseFloat ws_row_major = ws; - return npe::move(ws_row_major); - -npe_end_code() - - - - - -const char* ds_uniformly_sample_two_manifold_at_vertices = R"igl_Qu8mg5v7( - -Find uniform sampling up to placing samples on mesh vertices - -Parameters ----------- - - -Returns -------- - - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(uniformly_sample_two_manifold_at_vertices) -npe_doc(ds_uniformly_sample_two_manifold_at_vertices) - -npe_arg(ow, dense_float, dense_double) -npe_arg(k, int) -npe_arg(push, double) - - -npe_begin_code() - - // TODO: remove __copy - Eigen::MatrixXd ow_copy = ow.template cast(); - // FIXME: vector not allowing row major, but they should be essentially the same so i feel we can leave it as col major - Eigen::VectorXi s; - igl::uniformly_sample_two_manifold_at_vertices(ow_copy, k, push, s); - Eigen::Matrix s_out = s.template cast(); - return npe::move(s_out); - -npe_end_code() - - diff --git a/src/unique_edge_map.cpp b/src/unique_edge_map.cpp index 69996722..ade6eddb 100644 --- a/src/unique_edge_map.cpp +++ b/src/unique_edge_map.cpp @@ -1,66 +1,68 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include +#include "default_types.h" #include - -const char *ds_unique_edge_map = R"igl_Qu8mg5v7xxx( -Construct relationships between facet "half"-(or rather "viewed")-edges E +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto unique_edge_map(const nb::DRef &F) + { + Eigen::MatrixXI E; + Eigen::MatrixXI uE; + Eigen::VectorXI EMAP; + Eigen::VectorXI uEC, uEE; + igl::unique_edge_map(F, E, uE, EMAP, uEC, uEE); + return nb::make_tuple(E, uE, EMAP, uEC, uEE); + } + + auto unique_edge_map_lists( const nb::DRef &F) + { + Eigen::MatrixXI E; + Eigen::MatrixXI uE; + Eigen::VectorXI EMAP; + std::vector> uE2E; + igl::unique_edge_map(F, E, uE, EMAP, uE2E); + return std::make_tuple(E, uE, EMAP, uE2E); + } +} + +// Bind the wrapper to the Python module +void bind_unique_edge_map(nb::module_ &m) +{ + m.def( + "unique_edge_map", + &pyigl::unique_edge_map, + "F"_a, +R"(Construct relationships between facet "half"-(or rather "viewed")-edges E to unique edges of the mesh seen as a graph. -Parameters ----------- -F #F by 3 list of simplices - - -Returns -------- -E #F*3 by 2 list of all directed edges, such that E.row(f+#F*c) is the -edge opposite F(f,c) -uE #uE by 2 list of unique undirected edges -EMAP #F*3 list of indices into uE, mapping each directed edge to unique -undirected edge so that uE(EMAP(f+#F*c)) is the unique edge -corresponding to E.row(f+#F*c) -uE2E #uE list of lists of indices into E of coexisting edges, so that -E.row(uE2E[i][j]) corresponds to uE.row(i) for all j in -0..uE2E[i].size()-1. - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7xxx"; - -npe_function(unique_edge_map) -npe_doc(ds_unique_edge_map) - -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_tri_mesh_faces(f); - - EigenDenseLike E; - EigenDenseLike uE; - Eigen::Matrix EMAP; - std::vector> uE2E; - igl::unique_edge_map(f, E, uE, EMAP, uE2E); - return std::make_tuple(npe::move(E), npe::move(uE), npe::move(EMAP), uE2E); +@param[in] F #F by 3 list of simplices +@param[out] E #F*3 by 2 list of all directed edges +@param[out] uE #uE by 2 list of unique undirected edges +@param[out] EMAP #F*3 list of indices into uE, mapping each directed edge to a unique + undirected edge so that uE(EMAP(f+#F*c)) is the unique edge + corresponding to E.row(f+#F*c) +@param[out] uE2E (if return_uE2E=True) #uE list of lists of indices into E of coexisting edges +)"); + m.def( + "unique_edge_map_lists", + &pyigl::unique_edge_map_lists, + "F"_a, +R"(Construct relationships between facet "half"-(or rather "viewed")-edges E +to unique edges of the mesh seen as a graph. - npe_end_code() +@param[in] F #F by 3 list of simplices +@param[out] E #F*3 by 2 list of all directed edges +@param[out] uE #uE by 2 list of unique undirected edges +@param[out] EMAP #F*3 list of indices into uE, mapping each directed edge to a unique + undirected edge so that uE(EMAP(f+#F*c)) is the unique edge + corresponding to E.row(f+#F*c) +@param[out] uE2E (if return_uE2E=True) #uE list of lists of indices into E of coexisting edges +)"); +} diff --git a/src/unique_simplices.cpp b/src/unique_simplices.cpp index 1082f8ed..22ad7cbf 100644 --- a/src/unique_simplices.cpp +++ b/src/unique_simplices.cpp @@ -1,55 +1,36 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char *ds_unique_simplices = R"igl_Qu8mg5v7( -Find *combinatorially* unique simplices in F. **Order independent** -Parameters ----------- -F #F by simplex-size list of simplices - -Returns -------- - FF #FF by simplex-size list of unique simplices in F - IA #FF index vector so that FF == sort(F(IA,:),2); - IC #F index vector so that sort(F,2) == FF(IC,:); - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(unique_simplices) -npe_doc(ds_unique_simplices) - -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_tet_or_tri_mesh_faces(f); - - EigenDenseLike ff; - Eigen::Matrix ia; - Eigen::Matrix ic; - igl::unique_simplices(f, ff, ia, ic); - return std::make_tuple(npe::move(ff), npe::move(ia), npe::move(ic)); - -npe_end_code() - - +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for unique_simplices with overload handling + auto unique_simplices( const nb::DRef &F) + { + Eigen::MatrixXI FF; + Eigen::VectorXI IA,IC; + igl::unique_simplices(F,FF,IA,IC); + return nb::make_tuple(FF,IA,IC); + } +} + +// Bind the wrapper to the Python module +void bind_unique_simplices(nb::module_ &m) +{ + m.def( + "unique_simplices", + &pyigl::unique_simplices, + "F"_a, +R"(Find combinatorially unique simplices in F. Order independent. + +@param[in] F #F by simplex-size list of simplices +@param[out] FF #FF by simplex-size list of unique simplices in F +@param[out] IA (if return_IA=True) #FF index vector so that FF == sort(F(IA,:),2) +@param[out] IC (if return_IC=True) #F index vector so that sort(F,2) == FF(IC,:) +)"); +} diff --git a/src/unproject.cpp b/src/unproject.cpp index e8e0028a..d5a1e418 100644 --- a/src/unproject.cpp +++ b/src/unproject.cpp @@ -1,61 +1,42 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -//TODO: __example -#include -#include -#include +#include "default_types.h" #include - -const char* ds_unproject = R"igl_Qu8mg5v7( - Reimplementation of gluUnproject - -Parameters ----------- - win #P by 3 or 3-vector (#P=1) of screen space x, y, and z coordinates - model 4x4 model-view matrix - proj 4x4 projection matrix - viewport 4-long viewport vector - -Returns -------- - scene #P by 3 or 3-vector (#P=1) the unprojected x, y, and z coordinates - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(unproject) -npe_doc(ds_unproject) - -npe_arg(win, dense_float, dense_double) -npe_arg(model, npe_matches(win)) -npe_arg(proj, npe_matches(win)) -npe_arg(viewport, dense_float, dense_double) - - -npe_begin_code() - - assert_cols_equals(win, 3, "win"); - assert_rows_equals(model, 4, "model"); - assert_cols_equals(model, 4, "model"); - assert_shapes_match(model, proj, "model", "proj"); - assert_rows_equals(viewport, 4, "viewport"); - EigenDenseLike scene; - igl::unproject(win, model, proj, viewport, scene); - return npe::move(scene); - -npe_end_code() +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for unproject with batch mode + auto unproject( + const nb::DRef &win, + const nb::DRef &model, + const nb::DRef &proj, + const nb::DRef &viewport) + { + Eigen::MatrixXN scene; + igl::unproject(win, model, proj, viewport, scene); + return scene; + } +} + +// Bind the wrapper to the Python module +void bind_unproject(nb::module_ &m) +{ + m.def( + "unproject", + &pyigl::unproject, + "win"_a, + "model"_a, + "proj"_a, + "viewport"_a, +R"(Eigen reimplementation of gluUnproject for batch processing. + +@param[in] win #P by 3 matrix of screen space x, y, and z coordinates +@param[in] model 4x4 model-view matrix +@param[in] proj 4x4 projection matrix +@param[in] viewport 4-long viewport vector +@return scene #P by 3 matrix of the unprojected x, y, and z coordinates)"); + +} diff --git a/src/unproject_in_mesh.cpp b/src/unproject_in_mesh.cpp deleted file mode 100644 index 7ab50360..00000000 --- a/src/unproject_in_mesh.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - - - - - -#include - -const char* ds_unproject_in_mesh = R"igl_Qu8mg5v7( - -Unproject a screen location (using current opengl viewport, projection, and - model view) to a 3D position _inside_ a given mesh. If the ray through the - given screen location (x,y) _hits_ the mesh more than twice then the 3D - midpoint between the first two hits is return. If it hits once, then that - point is return. If it does not hit the mesh then obj is not set. - -Parameters ----------- -pos screen space coordinates -model model matrix -proj projection matrix -viewport vieweport vector -V #V by 3 list of mesh vertex positions -F #F by 3 list of mesh triangle indices into V - -Returns -------- -obj 3d unprojected mouse point in mesh -hits vector of hits -Returns number of hits - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(unproject_in_mesh) -npe_doc(ds_unproject_in_mesh) - -npe_arg(pos, dense_float, dense_double) -npe_arg(model, npe_matches(pos)) -npe_arg(proj, npe_matches(pos)) -npe_arg(viewport, npe_matches(pos)) -npe_arg(v, npe_matches(pos)) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_3d_tri_mesh(v, f); - assert_rows_equals(pos, 2, "pos"); - assert_rows_equals(model, 4, "model"); - assert_cols_equals(model, 4, "model"); - assert_shapes_match(model, proj, "model", "proj"); - assert_rows_equals(viewport, 4, "viewport"); - - // TODO: remove __copy - Eigen::Vector2f pos_copy = pos.template cast(); - Eigen::Matrix4f model_copy = model.template cast(); - Eigen::Matrix4f proj_copy = proj.template cast(); - Eigen::Vector4f viewport_copy = viewport.template cast(); - - EigenDenseLike obj; - std::vector > hits; - igl::unproject_in_mesh(pos_copy, model_copy, proj_copy, viewport_copy, v, f, obj, hits); - return std::make_tuple(npe::move(obj), hits); - -npe_end_code() - diff --git a/src/unproject_on_line.cpp b/src/unproject_on_line.cpp deleted file mode 100644 index 2fc701a1..00000000 --- a/src/unproject_on_line.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - - -#include - -const char *ds_unproject_on_line = R"igl_Qu8mg5v7( - - -Given a screen space point (u,v) and the current projection matrix (e.g. -gl_proj * gl_modelview) and viewport, _unproject_ the point into the scene -so that it lies on given line (origin and dir) and projects as closely as -possible to the given screen space point. - -Parameters ----------- -UV 2-long uv-coordinates of screen space point -M 4 by 4 projection matrix -VP 4-long viewport: (corner_u, corner_v, width, height) -origin point on line -dir vector parallel to line - -Returns -------- -t line parameter so that closest poin on line to viewer ray through UV - lies at origin+t*dir -Z 3d position of closest point on line to viewing ray through UV - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(unproject_on_line) -npe_doc(ds_unproject_on_line) - -npe_arg(uv, dense_float, dense_double) -npe_arg(m, npe_matches(uv)) -npe_arg(vp, npe_matches(uv)) -npe_arg(origin, npe_matches(uv)) -npe_arg(dir, npe_matches(uv)) - - -npe_begin_code() - assert_size_equals(uv, 2, "uv"); - assert_rows_equals(m, 4, "m"); - assert_cols_equals(m, 4, "m"); - assert_size_equals(vp, 4, "uv"); - - assert_size_equals(origin, 3, "origin"); - assert_size_equals(dir, 3, "dir"); - - EigenDense uv_copy = uv; - EigenDense m_copy = m; - EigenDense vp_copy = vp; - EigenDense origin_copy = origin; - EigenDense dir_copy = dir; - - npe_Scalar_uv t; - igl::unproject_on_line(uv_copy, m_copy, vp_copy, origin_copy, dir_copy, t); - npe_Matrix_uv Z = origin + dir * t; - - return std::make_tuple(double(t), npe::move(Z)); - -npe_end_code() - diff --git a/src/unproject_on_plane.cpp b/src/unproject_on_plane.cpp deleted file mode 100644 index c09f860e..00000000 --- a/src/unproject_on_plane.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char *ds_unproject_on_plane = R"igl_Qu8mg5v7( - -Given a screen space point (u,v) and the current projection matrix (e.g. -gl_proj * gl_modelview) and viewport, _unproject_ the point into the scene -so that it lies on given plane. - - -Parameters ----------- - -UV 2-long uv-coordinates of screen space point -M 4 by 4 projection matrix -VP 4-long viewport: (corner_u, corner_v, width, height) -P 4-long plane equation coefficients: P*(X 1) = 0 - -Returns -------- -Z 3-long world coordinate -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(unproject_on_plane) -npe_doc(ds_unproject_on_plane) - -npe_arg(uv, dense_float, dense_double) -npe_arg(m, npe_matches(uv)) -npe_arg(vp, npe_matches(uv)) -npe_arg(p, npe_matches(uv)) - - -npe_begin_code() - assert_size_equals(uv, 2, "uv"); - assert_rows_equals(m, 4, "m"); - assert_cols_equals(m, 4, "m"); - assert_size_equals(vp, 4, "uv"); - assert_size_equals(p, 4, "p"); - - EigenDense uv_copy = uv; - EigenDense m_copy = m; - EigenDense vp_copy = vp; - Eigen::Matrix p_copy = p; - - EigenDense z; - igl::unproject_on_plane(uv_copy, m_copy, vp_copy, p_copy, z); - return npe::move(z); - -npe_end_code() - - diff --git a/src/unproject_onto_mesh.cpp b/src/unproject_onto_mesh.cpp deleted file mode 100644 index 91ee6c1f..00000000 --- a/src/unproject_onto_mesh.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -//TODO: __miss __example - -#include -#include -#include - - - - - - -#include - -const char* ds_unproject_onto_mesh = R"igl_Qu8mg5v7( - -Unproject a screen location (using current opengl viewport, projection, and - model view) to a 3D position _onto_ a given mesh, if the ray through the - given screen location (x,y) _hits_ the mesh. - -Parameters ----------- -pos screen space coordinates -model model matrix -proj projection matrix -viewport vieweport vector -V #V by 3 list of mesh vertex positions -F #F by 3 list of mesh triangle indices into V - -Returns -------- -fid id of the first face hit -bc barycentric coordinates of hit -Returns true if there's a hit - - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(unproject_onto_mesh) -npe_doc(ds_unproject_onto_mesh) - -npe_arg(pos, dense_float, dense_double) -npe_arg(model, npe_matches(pos)) -npe_arg(proj, npe_matches(pos)) -npe_arg(viewport, npe_matches(pos)) -npe_arg(v, npe_matches(pos)) -npe_arg(f, dense_int32, dense_int64) - - -npe_begin_code() - - assert_valid_3d_tri_mesh(v, f); - assert_rows_equals(pos, 2, "pos"); - assert_rows_equals(model, 4, "model"); - assert_cols_equals(model, 4, "model"); - assert_shapes_match(model, proj, "model", "proj"); - assert_rows_equals(viewport, 4, "viewport"); - - // TODO: remove __copy - Eigen::Vector2f pos_copy = pos.template cast(); - Eigen::Matrix4f model_copy = model.template cast(); - Eigen::Matrix4f proj_copy = proj.template cast(); - Eigen::Vector4f viewport_copy = viewport.template cast(); - int fid; - EigenDenseLike bc; - bool success = igl::unproject_onto_mesh(pos_copy, model_copy, proj_copy, viewport_copy, v, f, fid, bc); - return std::make_tuple(success, fid, npe::move(bc)); - -npe_end_code() - diff --git a/src/unproject_ray.cpp b/src/unproject_ray.cpp deleted file mode 100644 index c7c677f5..00000000 --- a/src/unproject_ray.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. - -#include -#include -#include -#include - -const char* ds_unproject_ray = R"igl_Qu8mg5v7( - -Construct a ray (source point + direction vector) given a screen space - positions (e.g. mouse) and a model-view projection constellation. - -Parameters ----------- -pos 2d screen-space position (x,y) -model 4x4 model-view matrix -proj 4x4 projection matrix -viewport 4-long viewport vector - -Returns -------- -s source of ray (pos unprojected with z=0) -dir direction of ray (d - s) where d is pos unprojected with z=1 - - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(unproject_ray) -npe_doc(ds_unproject_ray) - -npe_arg(pos, dense_float, dense_double) -npe_arg(model, npe_matches(pos)) -npe_arg(proj, npe_matches(pos)) -npe_arg(viewport, npe_matches(pos)) - - -npe_begin_code() - - assert_rows_equals(pos, 2, "pos"); - assert_rows_equals(model, 4, "model"); - assert_cols_equals(model, 4, "model"); - assert_shapes_match(model, proj, "model", "proj"); - assert_rows_equals(viewport, 4, "viewport"); - - Eigen::Matrix s; - Eigen::Matrix dir; - igl::unproject_ray(pos, model, proj, viewport, s, dir); - return std::make_tuple(npe::move(s), npe::move(dir)); - -npe_end_code() - - diff --git a/src/upsample.cpp b/src/upsample.cpp index 59d301db..4a4354fe 100644 --- a/src/upsample.cpp +++ b/src/upsample.cpp @@ -1,59 +1,70 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -//TODO: __example -//TODO: __miss upsample that retuns a sparse matrix and inplace -#include -#include -#include - +#include "default_types.h" #include +#include +#include +#include +#include +namespace nb = nanobind; +using namespace nb::literals; -const char* ds_upsample = R"igl_Qu8mg5v7( - Subdivide a mesh without moving vertices: loop subdivision but odd - vertices stay put and even vertices are just edge midpoints -Parameters ----------- - V #V by dim mesh vertices - F #F by 3 mesh triangles - -Returns -------- - NV new vertex positions, V is guaranteed to be at top - NF new list of face indices - -See also --------- - - -Notes ------ -- assumes (V,F) is edge-manifold. - -Examples --------- +namespace pyigl +{ + // Wrapper for the first overload of upsample that computes S and newF + auto upsample_matrix( + const nb::DRef &F, + int n_) + { + const auto n = n_? n_ : F.maxCoeff() + 1; + Eigen::SparseMatrix S; + Eigen::MatrixXI NF; + igl::upsample(n, F, S, NF); + return std::make_tuple(S, NF); + } -)igl_Qu8mg5v7"; + // Wrapper for the second overload of upsample that returns NV and NF + auto upsample( + const nb::DRef &V, + const nb::DRef &F, + int number_of_subdivs) + { + Eigen::MatrixXN NV; + Eigen::MatrixXI NF; + igl::upsample(V, F, NV, NF, number_of_subdivs); + return std::make_tuple(NV, NF); + } -npe_function(upsample) -npe_doc(ds_upsample) +} -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_default_arg(number_of_subdivs, int, 1) +// Bind the wrapper to the Python module +void bind_upsample(nb::module_ &m) +{ + m.def( + "upsample_matrix", + &pyigl::upsample_matrix, + "F"_a, + "n"_a = 0, + R"(Subdivide a mesh without moving vertices. Returns the subdivision matrix and new faces. +@param[in] n_verts Number of mesh vertices +@param[in] F #F by 3 matrix of triangle faces +@return A tuple containing: + - S: Sparse subdivision matrix + - NF: Matrix of new faces)"); -npe_begin_code() + m.def( + "upsample", + &pyigl::upsample, + "V"_a, + "F"_a, + "number_of_subdivs"_a = 1, + R"(Subdivide a mesh without moving vertices using loop subdivision. Returns new vertices and faces. - assert_valid_23d_tri_mesh(v, f); - EigenDenseLike nv; - EigenDenseLike nf; - igl::upsample(v, f, nv, nf, number_of_subdivs); - return std::make_tuple(npe::move(nv), npe::move(nf)); +@param[in] V #V by dim matrix of mesh vertices +@param[in] F #F by 3 matrix of triangle faces +@param[in] number_of_subdivs Number of subdivisions (default is 1) +@return A tuple containing: + - NV: New vertex positions with original vertices at the top + - NF: Matrix of new face indices)"); -npe_end_code() +} diff --git a/src/vector_area_matrix.cpp b/src/vector_area_matrix.cpp deleted file mode 100644 index 942263dc..00000000 --- a/src/vector_area_matrix.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include -#include - -const char* ds_vector_area_matrix = R"igl_Qu8mg5v7( -Constructs the symmetric area matrix A, s.t. [V.col(0)' V.col(1)'] * A * - [V.col(0); V.col(1)] is the **vector area** of the mesh (V,F). -Parameters ----------- -f : #f by 3 array of mesh faces (must be triangles) - -Returns -------- -a : #vx2 by #vx2 area matrix - -See also --------- -None - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(vector_area_matrix) -npe_doc(ds_vector_area_matrix) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - - assert_valid_tri_mesh_faces(f); - Eigen::SparseMatrix a; - igl::vector_area_matrix(f, a); - return npe::move(a); - -npe_end_code() - - diff --git a/src/vertex_components.cpp b/src/vertex_components.cpp index a934950f..40be01c5 100644 --- a/src/vertex_components.cpp +++ b/src/vertex_components.cpp @@ -1,85 +1,33 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_vertex_components = R"igl_Qu8mg5v7( -Compute connected components of the vertices of a mesh given the mesh' face indices. - -Parameters ----------- -f : #f x dim array of face indices - -Returns -------- -An array of component ids (starting with 0) - -See also --------- -vertex_components_from_adjacency_matrix -facet_components - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(vertex_components) -npe_doc(ds_vertex_components) -npe_arg(f, dense_int32, dense_int64) -npe_begin_code() - assert_valid_tri_mesh_faces(f); - EigenDenseLike c; - igl::vertex_components(f, c); - return npe::move(c); -npe_end_code() - - -const char* ds_vertex_components_from_adjacency_matrix = R"igl_Qu8mg5v7( -Compute connected components of a graph represented by a sparse adjacency -matrix. - -Parameters ----------- -a : n by n sparse adjacency matrix - -Returns -------- -A tuple (c, counts) where c is an array of component ids (starting with 0) -and counts is a #components array of counts for each component - -See also --------- -vertex_components -facet_components - -Notes ------ - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(vertex_components_from_adjacency_matrix) -npe_doc(ds_vertex_components_from_adjacency_matrix) -npe_arg(a, sparse_int32, sparse_int64) -npe_begin_code() - EigenDense c; - EigenDense counts; - igl::vertex_components(a, c, counts); - return std::make_tuple(npe::move(c), npe::move(counts)); -npe_end_code() - - - +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for vertex_components with face indices + auto vertex_components( + const nb::DRef &F) + { + Eigen::VectorXI C; + igl::vertex_components(F, C); + return C; + } +} + +// Bind the wrappers to the Python module +void bind_vertex_components(nb::module_ &m) +{ + // Binding for vertex_components with adjacency matrix and counts + m.def( + "vertex_components", + &pyigl::vertex_components, + "F"_a, + R"(Compute the connected components of a graph using an adjacency matrix, returning component IDs and counts. + +@param[in] F Matrix of triangle indices +@return Vector C of component IDs per vertex)"); +} diff --git a/src/vertex_triangle_adjacency.cpp b/src/vertex_triangle_adjacency.cpp index e29a2f0f..50fed664 100644 --- a/src/vertex_triangle_adjacency.cpp +++ b/src/vertex_triangle_adjacency.cpp @@ -1,65 +1,76 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 KarlLeell -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -// TODO: __miss __example - -#include -#include -#include +#include "default_types.h" #include +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto vertex_triangle_adjacency( + const nb::DRef &F, + Integer n) + { + if(n==0 && F.size()>0) + { + n = F.maxCoeff()+1; + } + Eigen::VectorXI VF,NI; + igl::vertex_triangle_adjacency(F,n,VF,NI); + return std::make_tuple(VF,NI); + } + auto vertex_triangle_adjacency_lists( + const nb::DRef &F, + Integer n) + { + if(n==0 && F.size()>0) + { + n = F.maxCoeff()+1; + } + std::vector> VF,VFi; + igl::vertex_triangle_adjacency(n,F,VF,VFi); + return std::make_tuple(VF,VFi); + } +} + +// Bind the wrapper to the Python module +void bind_vertex_triangle_adjacency(nb::module_ &m) +{ + m.def( + "vertex_triangle_adjacency", + &pyigl::vertex_triangle_adjacency, + "F"_a, + "n"_a=0, +R"(vertex_face_adjacency constructs the vertex-face topology of a given mesh (V,F) + + @param[in] F #F by dim list of mesh faces (must be triangles) + @param[in] n number of vertices #V (e.g. `F.maxCoeff()+1` or `V.rows()`) + @param[out] VF #V list of lists of incident faces (adjacency list) + @param[out] VI #V list of lists of index of incidence within incident faces listed + in VF + );)" + ); + m.def( + "vertex_triangle_adjacency_lists", + &pyigl::vertex_triangle_adjacency_lists, + "F"_a, + "n"_a=0, +R"(vertex_face_adjacency constructs the vertex-face topology of a given mesh (V,F) + + @param[in] F #F by dim list of mesh faces (must be triangles) + @param[in] n number of vertices #V (e.g. `F.maxCoeff()+1` or `V.rows()`) + if using lists + @param[out] VF #V list of lists of incident faces (adjacency list) + @param[out] VI #V list of lists of index of incidence within incident faces listed + in VF)" + ); +} -const char* ds_vertex_triangle_adjacency = R"igl_Qu8mg5v7( - -vertex_face_adjacency constructs the vertex-face topology of a given mesh (V,F) - -Parameters ----------- -F #F by 3 list of triangle indices into some vertex list V -n number of vertices, #V (e.g., F.maxCoeff()+1) - -Returns -------- -VF 3*#F list List of faces indice on each vertex, so that VF(NI(i)+j) = - f, means that face f is the jth face (in no particular order) incident - on vertex i. -NI #V+1 list cumulative sum of vertex-triangle degrees with a - preceeding zero. "How many faces" have been seen before visiting this - vertex and its incident faces. - - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(vertex_triangle_adjacency) -npe_doc(ds_vertex_triangle_adjacency) - -npe_arg(f, dense_int32, dense_int64) -npe_arg(n, int) - - -npe_begin_code() - - assert_valid_tri_mesh_faces(f); - Eigen::VectorXi vf; - Eigen::VectorXi ni; - igl::vertex_triangle_adjacency(f, n, vf, ni); - return std::make_tuple(npe::move(vf), npe::move(ni)); - -npe_end_code() diff --git a/src/volume.cpp b/src/volume.cpp index 75367f52..57999e05 100644 --- a/src/volume.cpp +++ b/src/volume.cpp @@ -1,198 +1,76 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char *ds_volume = R"igl_Qu8mg5v7( -Computes volume for all tets of a given tet mesh (V,T) - -Parameters ----------- -V #V by dim list of vertex positions -T #V by 4 list of tet indices - -Returns -------- -vol #T list of dihedral angles (in radians) - -See also --------- - - -Notes ------ -None - -Examples --------- - vol = volume(V,T) - -)igl_Qu8mg5v7"; - -npe_function(volume) -npe_doc(ds_volume) - -npe_arg(v, dense_float, dense_double) -npe_arg(t, dense_int32, dense_int64) - - -npe_begin_code() - assert_valid_tet_mesh(v, t); - -EigenDenseLike vol; -igl::volume(v, t, vol); -return npe::move(vol); -npe_end_code() - - - - -const char* ds_volume1 = R"igl_Qu8mg5v71( -Compute volumes of a list of tets defined by a, b, c, d - -Parameters ----------- -a,b,c,d list of vertices vertices of the tets - -Returns -------- -vol volume of the tets - -See also --------- - - -Notes ------ -None - -Examples --------- -)igl_Qu8mg5v71"; - -npe_function(volume_from_vertices) -npe_doc(ds_volume1) - -npe_arg(a, dense_float, dense_double) -npe_arg(b, npe_matches(a)) -npe_arg(c, npe_matches(a)) -npe_arg(d, npe_matches(a)) - - -npe_begin_code() - assert_cols_equals(a, 3, "a"); - // assert_cols_equals(b, 3, "b"); - // assert_cols_equals(c, 3, "c"); - // assert_cols_equals(d, 3, "d"); - - assert_nonzero_rows(a, "a"); - // assert_nonzero_rows(b, "b"); - // assert_nonzero_rows(c, "c"); - // assert_nonzero_rows(d, "d"); - - assert_shapes_match(a, b, "a", "b"); - assert_shapes_match(a, c, "a", "c"); - assert_shapes_match(a, d, "a", "d"); - - EigenDenseLike vol; - igl::volume(a, b, c, d, vol); - return npe::move(vol); -npe_end_code() - - - -const char *ds_volume2 = R"igl_Qu8mg5v72( -Computes volume for all tets from edge lengths -Parameters ----------- -L #V by 6 list of edge lengths (see edge_lengths) - -Returns -------- -vol volume of the tets - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v72"; - -npe_function(volume_from_edges) -npe_doc(ds_volume2) - -npe_arg(l, dense_float, dense_double) - -npe_begin_code() - assert_cols_equals(l, 6, "l"); - assert_nonzero_rows(l, "l"); - - EigenDenseLike vol; - igl::volume(l, vol); - return npe::move(vol); -npe_end_code() - - -const char* ds_volume_single = R"igl_Qu8mg5v73( -Volume of a single tet -Parameters ----------- -a,b,c,d vertices - -Returns -------- -volume - -See also --------- - - -Notes ------ -None - -Examples --------- - - Single tet -)igl_Qu8mg5v73"; - -npe_function(volume_single) -npe_doc(ds_volume_single) - -npe_arg(a, dense_float, dense_double) -npe_arg(b, npe_matches(a)) -npe_arg(c, npe_matches(a)) -npe_arg(d, npe_matches(a)) - - -npe_begin_code() - assert_size_equals(a, 3, "a"); - assert_size_equals(b, 3, "b"); - assert_size_equals(c, 3, "c"); - assert_size_equals(d, 3, "d"); - const bool must_transpose = a.rows() == 3; - - const Eigen::Matrix a_copy = must_transpose ? a.transpose().eval() : a; - const Eigen::Matrix b_copy = must_transpose ? b.transpose().eval() : b; - const Eigen::Matrix c_copy = must_transpose ? c.transpose().eval() : c; - const Eigen::Matrix d_copy = must_transpose ? d.transpose().eval() : d; - - double vol = igl::volume_single(a_copy, b_copy, c_copy, d_copy); - return vol; -npe_end_code() - - +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto volume_VT( + const nb::DRef &V = Eigen::MatrixXN(), + const nb::DRef &T = Eigen::MatrixXI()) + { + Eigen::VectorXN vol; + igl::volume(V, T, vol); + return vol; + } + auto volume_L( + const nb::DRef &L = Eigen::MatrixXN()) + { + Eigen::VectorXN vol; + igl::volume(L, vol); + return vol; + } + auto volume_ABCD( + const nb::DRef &A = Eigen::MatrixXN(), + const nb::DRef &B = Eigen::MatrixXN(), + const nb::DRef &C = Eigen::MatrixXN(), + const nb::DRef &D = Eigen::MatrixXN()) + { + Eigen::VectorXN vol; + igl::volume(A, B, C, D, vol); + return vol; + } + +} + +// Bind the wrapper to the Python module +void bind_volume(nb::module_ &m) +{ + m.def( + "volume", + &pyigl::volume_VT, + "V"_a = Eigen::MatrixXN(), + "T"_a = Eigen::MatrixXI(), +R"(Compute volume for tetrahedrons in various input formats. + +@param[in] V #V by dim list of vertex positions or first corner positions +@param[in] T #T by 4 list of tet indices +@return vol #T list of tetrahedron volumes)"); + m.def( + "volume", + &pyigl::volume_L, + "L"_a = Eigen::MatrixXN(), +R"(Compute volume for tetrahedrons in various input formats. + +@param[in] L #V by 6 list of edge lengths (see edge_lengths) +@return vol #T list of tetrahedron volumes)"); + m.def( + "volume", + &pyigl::volume_ABCD, + "A"_a = Eigen::MatrixXN(), + "B"_a = Eigen::MatrixXN(), + "C"_a = Eigen::MatrixXN(), + "D"_a = Eigen::MatrixXN(), +R"(Compute volume for tetrahedrons in various input formats. + +@param[in] A #A by dim list of vertex positions or first corner positions +@param[in] B #A by dim list of second corner positions +@param[in] C #A by dim list of third corner positions +@param[in] D #A by dim list of fourth corner positions + +@return vol #T list of tetrahedron volumes)"); +} diff --git a/src/voxel_grid.cpp b/src/voxel_grid.cpp new file mode 100644 index 00000000..934166aa --- /dev/null +++ b/src/voxel_grid.cpp @@ -0,0 +1,42 @@ +#include "default_types.h" +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + auto voxel_grid( + const nb::DRef &V, + const Numeric offset, + const int s, + const int pad_count) + { + Eigen::MatrixXN GV; + Eigen::VectorXI side; + igl::voxel_grid(V, offset, s, pad_count, GV, side); + return std::make_tuple(GV, side); + } +} + +// Bind the wrappers to the Python module +void bind_voxel_grid(nb::module_ &m) +{ + m.def( + "voxel_grid", + &pyigl::voxel_grid, + "V"_a, + "offset"_a =0.0, + "s"_a, + "pad_count"_a=0, + R"(Constructs a voxel grid with an offset applied to each cell center. + +@param[in] V Matrix of input vertices +@param[in] offset Offset to add to each cell center +@param[in] s Number of cell centers on the largest side +@param[in] pad_count Number of cells beyond the box +@return Tuple (GV, side) where GV contains cell center positions and side defines grid dimensions)"); +} diff --git a/src/winding_number.cpp b/src/winding_number.cpp index a167799d..e3b7d23a 100644 --- a/src/winding_number.cpp +++ b/src/winding_number.cpp @@ -1,111 +1,66 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -//TODO: __example -// TODO: remove __copy -// copy is necessary since the winding number only supports matrices -#include -#include -#include +#include "default_types.h" #include - -const char *ds_winding_number = R"igl_Qu8mg5v7( - WINDING_NUMBER Compute the sum of solid angles of a triangle/tetrahedron - described by points (vectors) V -Parameters ----------- - V n by 3 list of vertex positions - F #F by 3 list of triangle indices, minimum index is 0 - O no by 3 list of origin positions - -Returns -------- - S no by 1 list of winding numbers - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(winding_number) -npe_doc(ds_winding_number) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(o, npe_matches(v)) - - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - assert_cols_match(v, o, "v", "o"); - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::MatrixXi f_copy = f.template cast(); - Eigen::MatrixXd o_copy = o.template cast(); - - Eigen::MatrixXd w; - igl::winding_number(v_copy, f_copy, o_copy, w); - return npe::move(w); - -npe_end_code() - - - - -const char* ds_winding_number1 = R"igl_Qu8mg5v7( - Compute winding number of a single point - -Parameters ----------- - V n by dim list of vertex positions - F #F by dim list of triangle indices, minimum index is 0 - p single origin position - -Returns -------- - w winding number of this point - -See also --------- - - -Notes ------ -None - -Examples --------- - - -)igl_Qu8mg5v7"; - -npe_function(winding_number_for_point) -npe_doc(ds_winding_number1) - -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(p, dense_float, dense_double) - - -npe_begin_code() - assert_valid_tet_or_tri_mesh(v, f); - assert_cols_match(v, f, "v", "f"); - Eigen::MatrixXd v_copy = v.template cast(); - Eigen::MatrixXi f_copy = f.template cast(); - Eigen::MatrixXd p_copy = p.template cast(); - return igl::winding_number(v_copy, f_copy, p_copy); - -npe_end_code() - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Binding for winding_number over multiple query points + auto winding_number( + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &O) + { + Eigen::VectorXN W; + // The DerivedF on WindingNumberAABB and WindingNumberTree is + // broken and must be MatrixXi. + // Similarly DerivedV is broken and probably has to be MatrixX*. + Eigen::MatrixXN V_cpy = V; + Eigen::MatrixXi F_cpy = F.cast(); + igl::winding_number(V_cpy, F_cpy, O, W); + return W; + } + // Binding for winding_number over multiple query points + Numeric winding_number_single( + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &o) + { + return igl::winding_number(V, F, o.transpose()); + } + +} + +// Bind the wrapper to the Python module +void bind_winding_number(nb::module_ &m) +{ + m.def( + "winding_number", + &pyigl::winding_number, + "V"_a, + "F"_a, + "O"_a, + R"(Computes the generalized winding number at each query point with respect to the mesh. + + @param[in] V #V by dim list of mesh vertex positions + @param[in] F #F by dim list of mesh facets as indices into rows of V + @param[in] O #O by dim list of query points + @return Vector of winding numbers for each query point)"); + m.def( + "winding_number", + &pyigl::winding_number_single, + "V"_a, + "F"_a, + "o"_a, + R"(Computes the generalized winding number at each query point with respect to the mesh. + + @param[in] V #V by dim list of mesh vertex positions + @param[in] F #F by dim list of mesh facets as indices into rows of V + @param[in] o dim-vector of query point + @return winding number)"); +} diff --git a/src/writeDMAT.cpp b/src/writeDMAT.cpp index 1f6c87c5..aaf390a4 100644 --- a/src/writeDMAT.cpp +++ b/src/writeDMAT.cpp @@ -1,58 +1,42 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Alec Jacobson -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include - - - - - - +#include "default_types.h" #include - -const char* ds_write_dmat = R"igl_Qu8mg5v7( -Write a matrix from an ascii/binary dmat file, a simple ascii matrix file type, defined as follows. The first line is always: -<#columns> <#rows> -Then the coefficients of the matrix are given separated by whitespace with columns running fastest. - -Parameters ----------- -filename : string, path to .dmat file -W : m × n matrix -ascii : whether to use ascii format {true} - -Returns -------- -ret : whether writing was successful - -See also --------- -read_dmat - -Notes ------ -None - -Examples --------- ->>> write_dmat("my_model.dmat",W,ascii = false) -)igl_Qu8mg5v7"; - -npe_function(write_dmat) -npe_doc(ds_write_dmat) -npe_arg(filename, std::string) -npe_arg(w, dense_double, dense_float, dense_int32, dense_int64) -npe_default_arg(ascii, bool, true) -npe_begin_code() - -return igl::writeDMAT(filename, w, ascii); - -npe_end_code() - - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + // Wrapper for writeDMAT with Eigen matrix input + bool writeDMAT( + const std::filesystem::path &file_name, + const nb::DRef &W, + const bool ascii) + { + if (!igl::writeDMAT(file_name.generic_string(), W, ascii)) + { + throw std::runtime_error("writeDMAT: Failed to write DMAT file."); + } + return true; + } +} + +// Bind the wrappers to the Python module +void bind_writeDMAT(nb::module_ &m) +{ + m.def( + "writeDMAT", + &pyigl::writeDMAT, + "file_name"_a, + "W"_a, + "ascii"_a = true, + R"(Write a matrix to a .dmat file in ASCII or binary format. + + @param[in] file_name path to .dmat file + @param[in] W Eigen matrix containing coefficients to write + @param[in] ascii flag for ASCII format (default: true) + @return True if the operation is successful)"); +} diff --git a/src/writeMESH.cpp b/src/writeMESH.cpp index 9cf73ec4..6088daf7 100644 --- a/src/writeMESH.cpp +++ b/src/writeMESH.cpp @@ -1,44 +1,44 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Alec Jacobson -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_writeMESH = R"igl_Qu8mg5v7( - /// save a tetrahedral volume mesh to a .mesh file - /// - /// @tparam Scalar type for positions and vectors (will be cast as double) - /// @tparam Index type for indices (will be cast to int) - /// @param[in] mesh_file_name path of .mesh file - /// @param[in] V double matrix of vertex positions #V by 3 - /// @param[in] T #T list of tet indices into vertex positions - /// @param[in] F #F list of face indices into vertex positions - /// @return true on success, false on errors - /// - /// \bug Holes and regions are not supported -)igl_Qu8mg5v7"; - -npe_function(writeMESH) -npe_doc(ds_writeMESH) -npe_arg(filename, std::string) -npe_arg(V, dense_double, dense_float) -npe_arg(T, dense_int32, dense_int64) -npe_default_arg(F, npe_matches(T), pybind11::array()) - -npe_begin_code() - - assert_valid_tet_or_tri_mesh(V,T); - return igl::writeMESH(filename, V, T, F); - -npe_end_code() - - - - - +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + void writeMESH( + const std::filesystem::path &mesh_file_name, + const nb::DRef &V, + const nb::DRef &T, + const nb::DRef &F) + { + if (!igl::writeMESH(mesh_file_name.generic_string(), V, T, F)) + { + throw std::runtime_error("Failed to write .mesh file: " + mesh_file_name.generic_string()); + } + } +} + +// Bind the wrapper to the Python module +void bind_writeMESH(nb::module_ &m) +{ + m.def( + "writeMESH", + &pyigl::writeMESH, + "mesh_file_name"_a, + "V"_a=Eigen::MatrixXN(), + "T"_a=Eigen::MatrixXI(), + "F"_a=Eigen::MatrixXI(), +R"(Save a tetrahedral volume mesh to a .mesh file. + +@param[in] mesh_file_name Path to the .mesh file to save +@param[in] V #V by 3 matrix of vertex positions +@param[in] T #T by 4 matrix of tetrahedral indices +@param[in] F #F by 3 matrix of face indices +@throws std::runtime_error if file writing fails +)"); +} diff --git a/src/writeMSH.cpp b/src/writeMSH.cpp index 7f5645e1..853afdaf 100644 --- a/src/writeMSH.cpp +++ b/src/writeMSH.cpp @@ -1,74 +1,58 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Alec Jacobson -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_writeMSH = R"igl_Qu8mg5v7( - /// write triangle surface mesh and tetrahedral volume mesh to .msh file - /// - /// @param[in] msh - file name - /// @param[in] X eigen double matrix of vertex positions #X by 3 - /// @param[in] Tri #Tri eigen integer matrix of triangular faces indices into vertex positions - /// @param[in] Tet #Tet eigen integer matrix of tetrahedral indices into vertex positions - /// @param[in] TriTag #Tri eigen integer vector of tags associated with surface faces - /// @param[in] TetTag #Tet eigen integer vector of tags associated with volume elements - /// @param[in] XFields #XFields list of strings with field names associated with nodes - /// @param[in] XF #XFields list of eigen double matrices, fields associated with nodes - /// @param[in] EFields #EFields list of strings with field names associated with elements - /// @param[in] TriF #EFields list of eigen double matrices, fields associated with surface elements - /// @param[in] TetF #EFields list of eigen double matrices, fields associated with volume elements - /// - /// \bug files are always stored in binary format - /// \bug file format is 2.2 - /// \bug only triangle surface elements and tetrahedral volumetric elements are supported - /// \bug only 3D information is supported - /// \bug the tag id is duplicated for physical (0) and elementary (1) - /// \bug same element fields are expected to be associated with surface elements and volumetric elements -)igl_Qu8mg5v7"; - -npe_function(writeMSH) -npe_doc(ds_writeMSH) -npe_arg(filename, std::string) -// float not allowed because writeMSH is not templated -npe_arg(X, dense_double) -npe_arg(Tri, dense_int32, dense_int64) -npe_default_arg(Tet, npe_matches(Tri), pybind11::array()) -npe_default_arg(TriTag, npe_matches(Tri), pybind11::array()) -npe_default_arg(TetTag, npe_matches(Tri), pybind11::array()) - -npe_begin_code() - -std::vector XFields, EFields; -std::vector XF, TriF, TetF; -Eigen::MatrixXd X_copy = X.template cast(); -Eigen::MatrixXi Tri_copy = Tri.template cast(); -Eigen::MatrixXi Tet_copy = Tet.template cast(); -Eigen::MatrixXi TriTag_copy = TriTag.template cast(); -Eigen::MatrixXi TetTag_copy = TetTag.template cast(); - -return igl::writeMSH( - filename, - X_copy, - Tri_copy, - Tet_copy, - TriTag_copy, - TetTag_copy, - XFields, - XF, - EFields, - TriF, - TetF); - -npe_end_code() - - - - +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + void writeMSH( + const std::filesystem::path &mesh_file_name, + const nb::DRef &V, + const nb::DRef &Tri, + const nb::DRef &Tet, + const nb::DRef &TriTag, + const nb::DRef &TetTag) + { + std::vector XF; + std::vector XFields; + std::vector EFields; + std::vector TriF; + std::vector TetF; + if(!igl::writeMSH(mesh_file_name.generic_string(), V, Tri, Tet, TriTag, TetTag, XFields, XF, EFields, TriF, TetF)) + { + throw std::runtime_error("Failed to write .msh file: " + mesh_file_name.generic_string()); + } + } +} + +// Bind the wrapper to the Python module +void bind_writeMSH(nb::module_ &m) +{ + m.def( + "writeMSH", + &pyigl::writeMSH, + "mesh_file_name"_a, + "V"_a=Eigen::MatrixXN(), + "Tri"_a=Eigen::MatrixXI(), + "Tet"_a=Eigen::MatrixXI(), + "TriTag"_a=Eigen::VectorXI(), + "TetTag"_a=Eigen::VectorXI(), +R"(Save a tetrahedral volume mesh to a .msh file. + +@param[in] mesh_file_name Path to the .mesh file to save +@param[in] V #V by 3 matrix of vertex positions +@param[in] Tri #Tri by 3 matrix of face indices +@param[in] Tet #Tet by 4 matrix of tetrahedral indices +@param[in] TriTag #Tri vector of face tags +@param[in] TetTag #Tet vector of tetrahedral tags +@throws std::runtime_error if file writing fails +)"); +} diff --git a/src/writeOBJ.cpp b/src/writeOBJ.cpp index 66009e2a..acbb7467 100644 --- a/src/writeOBJ.cpp +++ b/src/writeOBJ.cpp @@ -1,59 +1,63 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Sebastian Koch -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include +#include "default_types.h" #include - -const char* ds_write_obj = R"igl_Qu8mg5v7( -Write a mesh in an ascii obj file. - -Parameters ----------- -filename : path to outputfile -v : array of vertex positions #v by 3 -f : #f list of face indices into vertex positions - -Returns -------- -ret : bool if output was successful - -See also --------- -read_obj - -Notes ------ -None - -Examples --------- -# Mesh in (v, f) ->>> success = write_obj(v, f) -)igl_Qu8mg5v7"; - -npe_function(write_obj) -npe_doc(ds_write_obj) -npe_arg(filename, std::string) -npe_arg(v, dense_double, dense_float) -npe_arg(f, dense_int32, dense_int64) -//npe_default_arg(cn, npe_matches(v), pybind11::array()) TODO: NPE Support none arrays as option -//npe_default_arg(fn, npe_matches(f), pybind11::array()) -//npe_default_arg(tc, npe_matches(v), pybind11::array()) -//npe_default_arg(ftc, npe_matches(f), pybind11::array()) -npe_begin_code() - - assert_valid_tet_or_tri_mesh(v, f); - //return igl::writeOBJ(filename, v, f, cn, fn, tc, ftc); - return igl::writeOBJ(filename, v, f); - -npe_end_code() - +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + void writeOBJ( + const std::filesystem::path & filename, + const nb::DRef &V, + const nb::DRef &F, + const nb::DRef &CN, + const nb::DRef &FN, + const nb::DRef &TC, + const nb::DRef &FTC) + { + if(!igl::writeOBJ(filename.generic_string(),V,F,CN,FN,TC,FTC)) + { + // throw runtime exception + throw std::runtime_error("Failed to write mesh to: " + filename.generic_string()); + } + } +} + +// Bind the wrapper to the Python module +void bind_writeOBJ(nb::module_ &m) +{ + m.def( + "writeOBJ", + &pyigl::writeOBJ, + "filename"_a, + "V"_a, + "F"_a, + "CN"_a = Eigen::MatrixXN(), + "FN"_a = Eigen::MatrixXI(), + "TC"_a = Eigen::MatrixXN(), + "FTC"_a = Eigen::MatrixXI(), +R"(Write a mesh in an ascii obj file + +@param[in] str path to outputfile +@param[in] V #V by 3 mesh vertex positions +@param[in] F #F by 3|4 mesh indices into V +@param[in] CN #CN by 3 normal vectors +@param[in] FN #F by 3|4 corner normal indices into CN +@param[in] TC #TC by 2|3 texture coordinates +@param[in] FTC #F by 3|4 corner texture coord indices into TC +@return true on success, false on error + +\bug Horrifyingly, this does not have the same order of parameters as +readOBJ. + +\see readOBJ)" + ); +} diff --git a/src/writeOFF.cpp b/src/writeOFF.cpp deleted file mode 100644 index 300a1206..00000000 --- a/src/writeOFF.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Daniele Panozzo -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -#include -#include -#include - -// TODO None arguments - - -#include - -const char* ds_write_off = R"igl_Qu8mg5v7( -Export geometry and colors-by-vertex - Export a mesh from an ascii OFF file, filling in vertex positions. - Only triangle meshes are supported - -Parameters ----------- - str path to .off output file - V #V by 3 mesh vertex positions - F #F by 3 mesh indices into V - C double matrix of rgb values per vertex #V by 3 - - -Returns -------- - Returns true on success, false on errors - -See also --------- - - -Notes ------ -None - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(write_off) -npe_doc(ds_write_off) - -npe_arg(str, std::string) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_arg(c, npe_matches(v)) - - -npe_begin_code() - - assert_valid_3d_tri_mesh(v, f); - return igl::writeOFF(str, v, f, c); - -npe_end_code() -// #include - -// const char* ds_write_off = R"igl_Qu8mg5v7( -// See writeOFF for the documentation. -// )igl_Qu8mg5v7"; - -// npe_function(write_off) -// npe_doc(ds_write_off) - -// npe_arg(str, std::string) -// npe_arg(v, dense_float, dense_double) -// npe_arg(f, dense_int32, dense_int64) - - -// npe_begin_code() - -// igl::writeOFF(str, v, f); -// return ; - -// npe_end_code() - - diff --git a/src/writePLY.cpp b/src/writePLY.cpp new file mode 100644 index 00000000..dfa8b049 --- /dev/null +++ b/src/writePLY.cpp @@ -0,0 +1,97 @@ +#include "default_types.h" +#include +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + void writePLY( + const std::filesystem::path & filename, + const Eigen::MatrixXN & V, + const Eigen::MatrixXI & F_, + const Eigen::MatrixXI & E_, + const Eigen::MatrixXN & N, + const Eigen::MatrixXN & UV, + const Eigen::MatrixXN & VD, + const std::vector & VDheader, + const Eigen::MatrixXN & FD, + const std::vector & FDheader, + const Eigen::MatrixXN & ED, + const std::vector & EDheader, + const std::vector & comments, + const igl::FileEncoding encoding) + { + // tinyply doesn't support int64 + Eigen::MatrixXi F = F_.cast(); + Eigen::MatrixXi E = E_.cast(); + + if(!igl::writePLY( + filename.generic_string(), + V, + F, + E, + N, + UV, + VD, + VDheader, + FD, + FDheader, + ED, + EDheader, + comments, + encoding + )) + { + throw std::runtime_error("Error writing " + filename.generic_string()); + } + } +} + +// Bind the wrapper to the Python module +void bind_writePLY(nb::module_ &m) +{ + m.def( + "writePLY", + &pyigl::writePLY, + "filename"_a, + "V"_a = Eigen::MatrixXN(), + "F"_a = Eigen::MatrixXI(), + "E"_a = Eigen::MatrixXI(), + "N"_a = Eigen::MatrixXN(), + "UV"_a = Eigen::MatrixXN(), + "VD"_a = Eigen::MatrixXN(), + "VDheader"_a = std::vector(), + "FD"_a = Eigen::MatrixXN(), + "FDheader"_a = std::vector(), + "ED"_a = Eigen::MatrixXN(), + "EDheader"_a = std::vector(), + "comments"_a = std::vector(), + "encoding"_a = igl::FileEncoding::Binary, + R"(Write a mesh to a .ply file. + + @tparam Derived from Eigen matrix parameters + @param[in] ply_stream ply file output stream + @param[in] V (#V,3) matrix of vertex positions + @param[in] F (#F,3) list of face indices into vertex positions + @param[in] E (#E,2) list of edge indices into vertex positions + @param[in] N (#V,3) list of normals + @param[in] UV (#V,2) list of texture coordinates + @param[in] VD (#V,*) additional vertex data + @param[in] Vheader (#V) list of vertex data headers + @param[in] FD (#F,*) additional face data + @param[in] Fheader (#F) list of face data headers + @param[in] ED (#E,*) additional edge data + @param[in] Eheader (#E) list of edge data headers + @param[in] comments (*) file comments + @param[in] encoding - enum, to set binary or ascii file format + )"); + +} + diff --git a/src/write_triangle_mesh.cpp b/src/write_triangle_mesh.cpp index 2d4c2837..c07b2130 100644 --- a/src/write_triangle_mesh.cpp +++ b/src/write_triangle_mesh.cpp @@ -1,70 +1,59 @@ -// This file is part of libigl, a simple c++ geometry processing library. -// -// Copyright (C) 2023 Teseo Schneider -// -// This Source Code Form is subject to the terms of the Mozilla Public License -// v. 2.0. If a copy of the MPL was not distributed with this file, You can -// obtain one at http://mozilla.org/MPL/2.0/. -//TODO: __example - -#include -#include -#include - - +#include "default_types.h" #include -#include - -const char *ds_write_triangle_mesh = R"igl_Qu8mg5v7( - write mesh to a file with automatic detection of file format. supported: obj, off, stl, wrl, ply, mesh). - -Parameters ----------- - str path to file - V double matrix #V by 3 - F int matrix #F by 3 - force_ascii=True force ascii format even if binary is available -Returns -------- - Returns true iff success - -See also --------- - - -Notes ------ - - -Examples --------- - -)igl_Qu8mg5v7"; - -npe_function(write_triangle_mesh) -npe_doc(ds_write_triangle_mesh) - -npe_arg(str, std::string) -npe_arg(v, dense_float, dense_double) -npe_arg(f, dense_int32, dense_int64) -npe_default_arg(force_ascii, bool, bool(true)) - -npe_begin_code() - assert_valid_3d_tri_mesh(v, f); - // TODO: remove __copy - //copy is necessary for the ply library - Eigen::MatrixXi f_copy = f.template cast(); - Eigen::MatrixXd v_copy = v.template cast(); - - igl::FileEncoding encoding = igl::FileEncoding::Ascii; - if (!force_ascii) { - //NOTE: if not ascii, we default to binary - encoding = igl::FileEncoding::Binary; +#include +#include +#include +#include +#include + +namespace nb = nanobind; +using namespace nb::literals; + +namespace pyigl +{ + void write_triangle_mesh( + const std::filesystem::path & filename, + const nb::DRef V, + const nb::DRef F, + const igl::FileEncoding encoding_enum) + { + // Throw an error if entries in F are bigger than can be cast to int32_t + if(F.maxCoeff() > std::numeric_limits::max()) + { + // throw runtime exception + throw std::runtime_error("Entries in F are too big to be cast to int32_t (required by igl::write_triangle_mesh)"); + } + Eigen::MatrixXi F32 = F.cast(); + if(!igl::write_triangle_mesh(filename.generic_string(),V,F32,encoding_enum)) + { + // throw runtime exception + throw std::runtime_error("Failed to write mesh to: " + filename.generic_string()); + } } +} + +// Bind the wrapper to the Python module +void bind_write_triangle_mesh(nb::module_ &m) +{ + m.def( + "write_triangle_mesh", + &pyigl::write_triangle_mesh, + "filename"_a, + "V"_a, + "F"_a, + "encoding"_a=igl::FileEncoding::Ascii, +R"(write mesh to a file with automatic detection of file format. supported: +obj, off, stl, wrl, ply, mesh). + +@tparam Scalar type for positions and vectors (will be read as double and cast + to Scalar) +@tparam Index type for indices (will be read as int and cast to Index) +@param[in] str path to file +@param[in] V eigen double matrix #V by 3 +@param[in] F eigen int matrix #F by 3 +@param[in] encoding set file encoding (ascii or binary) when both are available)" + ); +} - bool ok = igl::write_triangle_mesh(str, v_copy, f_copy, encoding); - return ok; - -npe_end_code() diff --git a/tests/test_all.py b/tests/test_all.py new file mode 100644 index 00000000..1dd1a13e --- /dev/null +++ b/tests/test_all.py @@ -0,0 +1,553 @@ +import pytest +import numpy as np +# scipy sparse matrices +import scipy.sparse +import time +import warnings +import igl + +import igl.triangle +import igl.copyleft +import igl.copyleft.tetgen +import igl.copyleft.cgal +import igl.embree + + +#def rand_sparse(n,density): +# n_features = n +# n_samples = n +# rng1 = np.random.RandomState(42) +# rng2 = np.random.RandomState(43) +# +# nnz = int(n_samples*n_features*density) +# +# row = rng1.randint(n_samples, size=nnz) +# cols = rng2.randint(n_features, size=nnz) +# data = rng1.rand(nnz) +# +# S = scipy.sparse.coo_matrix((data, (row, cols)), shape=(n_samples, n_features)) +# return S.tocsc() +# +#def time_noop(): +# def helper(N,I,SN,SI): +# igl.noop(SN=SN) +# # start timer +# runs = 100 +# start = time.time() +# for i in range(runs): +# igl.noop(SN=SN) +# # end timer +# end = time.time() +# return (end - start)/runs +# n = 10000 +# m = 10 +# N64_f = np.asfortranarray(np.random.randn(n,m).astype(np.float64)) +# I64_f = np.asfortranarray(np.random.randn(n,m).astype(np.int64)) +# # random sparse matrix +# SN64 = rand_sparse(n,1.0/(n)) +# # print number of nonzeros +# SI64 = (rand_sparse(n,1.0/(n))*1000).astype(np.int64) +# print(f"noop<{n},{m}>: {helper(N64_f,I64_f,SN64,SI64)} secs") +# +#time_noop() + +# print(igl.matlab_format(V,"V")) +# print(igl.matlab_format_index(F,"F")) +# print(igl.matlab_format(dV,"dV")) +# print(igl.matlab_format_index(dF,"dF")) + + +#seed numpy's random number generator + +def triangulated_square(): + V = np.array([[0,0,0],[1,0,0],[1,1,0],[0,1,0]],dtype=np.float64) + F = np.array([[0,1,2],[0,2,3]],dtype=np.int64) + return V,F + +def single_tet(): + V = np.array([[0,0,0],[1,0,0],[0,1,0],[0,0,1]],dtype=np.float64) + F = np.array([[2,1,0],[1,3,0],[3,2,0],[2,3,1]],dtype=np.int64) + T = np.array([[0,1,2,3]],dtype=np.int64) + return V,F,T + +def test_edges(): + F = np.array([[0,1,2],[0,2,3]],dtype=np.int64) + E,oE = igl.orient_halfedges(F) + ne = E.max()+1 + np.random.seed(42) + uE = np.random.rand(ne).astype(np.float64) + + uV = igl.average_from_edges_onto_vertices(F,E,oE,uE) + V = np.array([[0,0,0],[1,0,0],[1,1,0],[0,1,0]],dtype=np.float64) + +def test_read_write(): + V,F = triangulated_square() + igl.write_triangle_mesh("out.obj",V,F) + igl.write_triangle_mesh("out.off",V,F) + igl.write_triangle_mesh("out.ply",V,F,encoding=igl.Binary) + igl.writePLY("out.ply",V,F) + igl.writeOBJ("out.obj",V,F) + igl.writeOBJ("out.obj",V,F,V,F,V,F) + V,F,_ = igl.readOFF("out.off") + V,_,_,F,_,_ = igl.readOBJ("out.obj") + V,F = igl.read_triangle_mesh("out.ply") + igl.writeDMAT("out.dmat",V) + igl.writeDMAT("out.dmat",V,ascii=True) + V = igl.readDMAT("out.dmat") + igl.writeDMAT("out.dmat",V,ascii=False) + V = igl.readDMAT("out.dmat") + +def test_operators(): + V,F = triangulated_square() + L = igl.cotmatrix(V,F) + I = np.array([0,1,2,3],dtype=np.int64) + C = np.array([0,4],dtype=np.int64) + L,M,P = igl.cotmatrix(V,I,C) + L = igl.squared_edge_lengths(V,F) + K = igl.cotmatrix_entries(V,F) + l = igl.edge_lengths(V,F) + K = igl.cotmatrix_entries(l=l) + B1,B2,B3 = igl.local_basis(V,F) + G = igl.grad(V,F) + + M = igl.massmatrix(V,F) + M = igl.massmatrix(V,F,type=igl.MASSMATRIX_TYPE_BARYCENTRIC) + M = igl.massmatrix(V,F,type=igl.MASSMATRIX_TYPE_VORONOI) + M = igl.massmatrix(V,F,type=igl.MASSMATRIX_TYPE_DEFAULT) + + A = igl.facet_adjacency_matrix(F) + A = igl.adjacency_matrix(F) + A = igl.adjacency_matrix(I,C) + E = igl.edges(F) + E = igl.edges(I,C) + E = igl.edges(A) + n,C,K = igl.connected_components(A) + C = igl.vertex_components(F) + nc,C = igl.facet_components(F) + +def test_normals_and_distances(): + V,F = triangulated_square() + I = np.array([0,1,2,3],dtype=np.int64) + C = np.array([0,4],dtype=np.int64) + FN,_,_,_ = igl.per_face_normals(V,I,C) + FN = igl.per_face_normals(V,F) + FN = igl.per_face_normals(V,F,Z=np.array([0,0,1],dtype=np.float64)) + VN = igl.per_vertex_normals(V,F) + VN = igl.per_vertex_normals(V,F, weighting=igl.PER_VERTEX_NORMALS_WEIGHTING_TYPE_UNIFORM) + VN = igl.per_vertex_normals(V,F, weighting=igl.PER_VERTEX_NORMALS_WEIGHTING_TYPE_AREA) + VN = igl.per_vertex_normals(V,F, weighting=igl.PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE) + VN = igl.per_vertex_normals(V,F,FN=FN,weighting=igl.PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE) + dblA = igl.doublearea(V,F) + l = igl.edge_lengths(V,F) + dblA = igl.doublearea(l=l) + dblA = igl.doublearea(l=l,nan_replacement=0.0) + P = np.array([[0.5,0.5,0.0],[0.5,0.5,0.5]],dtype=np.float64) + sqrD,I,C = igl.point_mesh_squared_distance(P,V,F) + W = igl.winding_number(V,F,P) + W = igl.winding_number(V,F,P[1,:]) + W = igl.fast_winding_number(V,F,P) + S,I,C,N = igl.signed_distance(P,V,F) + S,I,C,N = igl.signed_distance(P,V,F,sign_type=igl.SignedDistanceType.SIGNED_DISTANCE_TYPE_FAST_WINDING_NUMBER) + BC = igl.barycenter(V,F) + +def test_harmonic(): + V,F = triangulated_square() + b = np.array([0,3],dtype=np.int64) + bc = np.array([[1,0],[0,1]],dtype=np.float64) + W = igl.bbw(V,F,b,bc) + W = igl.harmonic(V,F,b,bc,k=1) + W = igl.harmonic(V,F,b,bc,k=2) + +def test_tets(): + V,F,T = single_tet() + F,J,K = igl.boundary_facets(T) + igl.writeMESH("out.mesh",V,T) + igl.writeMESH("out.mesh",V,T,F=F) + V,T,F = igl.readMESH("out.mesh") + igl.writeMSH("out.msh",V,F,T) + V,F,T,_,_,_,_,_,_,_ = igl.readMSH("out.msh") + +def test_bvh(): + V,F,T = single_tet() + tree = igl.AABB() + tree.init(V,T) + + P = np.array([[0.5,0.5,0.0],[0.5,0.5,0.5]],dtype=np.float64) + # first row of P + q = P[0,:] + I = tree.find(V,T,q) + i = tree.find(V,T,q,first=True) + + Q = igl.barycenter(V,T) + I = igl.in_element(V,T,Q,tree) + + tree = igl.AABB() + tree.init(V,F) + sqrD,_,_ = tree.squared_distance(V,F,P) + + O = np.array([[2,1,1],[0.2,0.2,-1]],np.float64); + D = V.mean(axis=0)-O + I,T,UV = tree.intersect_ray_first(V,F,O,D) + hits = tree.intersect_ray(V,F,O,D) + o = O[0,:] + d = D[0,:] + hit = igl.ray_mesh_intersect(o,d,V,F,first=True) + hits = igl.ray_mesh_intersect(o,d,V,F) + + +def test_upsample(): + V,F,T = single_tet() + # upsample so there's something to collapse + V,F = igl.upsample(V,F,number_of_subdivs=1) + E,uE,EMAP,uE2E = igl.unique_edge_map_lists(F) + E,uE,EMAP,uEC,uEE = igl.unique_edge_map(F) + EF,EI = igl.edge_flaps(F,uE,EMAP) + uE,EMAP,EF,EI = igl.edge_flaps(F) + Nv,Nf = igl.circulation(0,True,F,EMAP,EF,EI) + + e = 0 + p = V[uE[e,0],:] + e1,e2,f1,f2 = igl.collapse_edge(e,p,V,F,uE,EMAP,EF,EI) + +def test_cut(): + V,F,T = single_tet() + C = np.ones(F.shape,dtype=bool) + Vn,Fn,I = igl.cut_mesh(V,F,C) + +def test_adjacency(): + V,F,T = single_tet() + TT,TTi = igl.triangle_triangle_adjacency(F) + TT,TTi = igl.triangle_triangle_adjacency_lists(F) + + VF,NI = igl.vertex_triangle_adjacency(F,n=V.shape[0]) + VF,VFi = igl.vertex_triangle_adjacency(F) + + F012 = F; + F120 = np.roll(F012,1,axis=1) + FF = np.vstack((F012,F120)) + F,IA,IC = igl.unique_simplices(FF) + A = np.array([[1,2,3],[1,2,4],[3,2,1],[5,6,7]],dtype=np.int64) + B = np.array([[1,2,3],[5,6,2],[1,2,4],[3,2,1]],dtype=np.int64) + IA,LOCB = igl.ismember_rows(A,B) + A = igl.adjacency_list(F) + A = igl.adjacency_list(F,sorted=True) + +def test_min_quad(): + V,F,T = single_tet() + A = -igl.cotmatrix(V,F) + B = np.zeros((V.shape[0],1),dtype=np.float64) + known = np.array([1,2],dtype=np.int64) + Y = np.array([-1,1],dtype=np.float64).reshape(-1, 1) + Aeq = scipy.sparse.csc_matrix(([-1,1],([0,0],[0,3])),shape=(1,V.shape[0])) + Beq = np.zeros((1,1),dtype=np.float64) + Z = igl.min_quad_with_fixed(A,B,known,Y,Aeq,Beq,pd=True) + data = igl.min_quad_with_fixed_data() + igl.min_quad_with_fixed_precompute(A,known,Aeq,True,data) + Z = igl.min_quad_with_fixed_solve(data,B,Y,Beq) + +def test_volume(): + V = np.array([[0,0,0],[1,0,0],[0,1,0],[0,0,1]],dtype=np.float64) + T = np.array([[0,1,2,3]],dtype=np.int64) + L = igl.edge_lengths(V,T) + vol = igl.volume(V,T) + vol = igl.volume(L=L) + vol = igl.volume(A=V[T[:,0],:],B=V[T[:,1],:],C=V[T[:,2],:],D=V[T[:,3],:]) + F,_,_ = igl.boundary_facets(T) + # remove last face + F = F[:-1,:] + B = igl.is_border_vertex(F) + I,C = igl.on_boundary(F) + I,C = igl.on_boundary(T) + F = np.array([[2,1,3]],dtype=np.int64) + NV,NF,I,J = igl.remove_unreferenced(V,F) + +def test_oriented_facets(): + V,F,T = single_tet() + E = igl.oriented_facets(F) + F = igl.oriented_facets(T) + + res,BF,EF,EMAP,BE = igl.is_edge_manifold(F) + res,_,_,_,_ = igl.is_edge_manifold(F) + +def test_matlab_format(): + V,F = triangulated_square() + L = igl.cotmatrix(V,F) + s = igl.matlab_format(V,"V") + s = igl.matlab_format_index(F,"F") + s = igl.matlab_format(L,"L") + +def test_polar(): + # random randn 3x3 matrix + np.random.seed(42) + A = np.random.randn(3,3).astype(np.float64) + igl.polar_svd(A) + out = igl.polar_svd(A) + R,T,_,_,_ = igl.polar_svd(A) + R,T,sU,sS,sV = igl.polar_svd(A,include_reflections=True) + +def test_remove(): + V,F = triangulated_square() + SV,SVI,SVJ = igl.remove_duplicate_vertices(V,epsilon=1e-10) + SV,SVI,SVJ,F = igl.remove_duplicate_vertices(V,F,epsilon=1e-10) + +def test_project(): + V,F,_ = single_tet() + model = np.eye(4).astype(np.float64) + proj = np.eye(4).astype(np.float64) + viewport = np.array([0,0,640,480],dtype=np.float64) + win = igl.project(V,model,proj,viewport) + scene = igl.unproject(win,model,proj,viewport) + + S = np.array([0,0,0],dtype=np.float64) + D = np.array([0.2,0.2,0.2],dtype=np.float64) + t,sqrD = igl.project_to_line(V,S,D) + t,sqrD = igl.project_to_line_segment(V,S,D) + +def test_boundary_loop(): + V,F,_ = single_tet() + S,_ = igl.upsample_matrix(F,n=V.shape[0]) + S,_ = igl.loop_matrix(F,n=V.shape[0]) + V,F = igl.upsample(V,F,number_of_subdivs=1) + V,F = igl.loop(V,F,number_of_subdivs=1) + # remove first and last + F = F[1:-1,:] + L_all = igl.boundary_loop_all(F) + L = igl.boundary_loop(F) + +def test_voxel(): + V,_,_ = single_tet() + GV,side = igl.voxel_grid(V,s=10) + GV,side = igl.voxel_grid(V,s=10,offset=0.1,pad_count=2) + + +def test_sample(): + V,F = igl.icosahedron() + B,I,X = igl.random_points_on_mesh(10,V,F) + point_indices, CH,CN,W = igl.octree(X) + I = igl.knn(X,X,1,point_indices,CH,CN,W) + B,FI,P = igl.blue_noise(V,F,0.5) + +def test_curvature(): + V,F = igl.icosahedron() + K = igl.gaussian_curvature(V,F) + PD1,PD2,PV1,PV2,_ = igl.principal_curvature(V,F) + PD1,PD2,PV1,PV2,_ = igl.principal_curvature(V,F,radius=10,useKring=False) + +def test_geodesic(): + V,F = igl.icosahedron() + VS = np.array([0],dtype=np.int64) + VT = np.array([1],dtype=np.int64) + D = igl.exact_geodesic(V,F,VS=VS,VT=VT) + +def test_decimate(): + V,F = igl.icosahedron() + dV,dF,J,I = igl.decimate(V,F) + dV,dF,J,I = igl.qslim(V,F) + +def test_parameterization(): + V,Q,E = igl.quad_grid(3,3); + V,F = igl.triangulated_grid(3,3); + # slim needs 3D data even if the problem is 2D + V = np.c_[V, np.zeros(V.shape[0])] + V_init = V[:,:2] + V_init[:,0] = V_init[:,0] * 2 + b = np.array([0,1],dtype=np.int64) + bc = V[b,:2] + data = igl.slim_precompute(V,F,V_init,igl.MappingEnergyType.ARAP,b,bc,soft_p=1e10) + U = igl.slim_solve(data,iter_num=1) + + data = igl.ARAPData() + data.energy = igl.ARAPEnergyType.ARAP_ENERGY_TYPE_SPOKES_AND_RIMS + V = V[:,:2] + igl.arap_precomputation(V,F,V.shape[1],b,data) + U = igl.arap_solve(bc,data,U) + + U,Q = igl.lscm(V,F,b,bc) + + +def test_implicit(): + res = np.array([3,3,3],dtype=np.int64) + GV = igl.grid(res) + S = np.sqrt(((GV - np.array([0.5,0.5,0.5],dtype=np.float64))**2).sum(axis=1))-0.25; + V,F,E2V = igl.marching_cubes(S,GV,res[0],res[1],res[2]) + # unpack keys into (i,j,v) index triplets + EV = np.array([[k & 0xFFFFFFFF, k >> 32, v] for k, v in E2V.items()], dtype=np.int64) + + h = igl.avg_edge_length(V,F) + m0,m1,m2 = igl.moments(V,F) + D = V[:,0] + vals = np.array([D.min(),D.mean(),D.max()],dtype=np.float64) + iV,iE,I = igl.isolines(V,F,S,vals) + iB,iF,iE,I = igl.isolines_intrinsic(F,S,vals) + + +def test_kelvinlets(): + V,F = igl.icosahedron() + x0 = V[0,:] + f = np.array([0,0,1],dtype=np.float64) + R = np.eye(3).astype(np.float64) + U = igl.kelvinlets(V,x0,f,R,epsilon=1.0,falloff=1.0,brushType=igl.GRAB) + + SV = V[:,0] + SF = igl.average_onto_faces(F,SV) + SV = igl.average_onto_vertices(V,F,SF) + +def test_polygons(): + P = [[0,1,2],[2,1,3,4]] + I,C = igl.polygon_corners(P) + Q = np.array([[0,1,2,3],[2,1,4,5]],dtype=np.int64) + I,C = igl.polygon_corners(Q) + F,J = igl.polygons_to_triangles(I,C) + +def test_heat(): + V = np.array([[0,0,0],[1,0,0],[0,1,0],[0,0,1]],dtype=np.float64) + F = np.array([[1,3,0],[3,2,0],[2,3,1]],dtype=np.int64) + V,F = igl.upsample(V,F,number_of_subdivs=1) + data = igl.HeatGeodesicsData() + igl.heat_geodesics_precompute(V,F,t=1e-3,data=data) + data = igl.HeatGeodesicsData() + data.use_intrinsic_delaunay = True + igl.heat_geodesics_precompute(V,F,data) + gamma = np.array([0],dtype=np.int64) + D = igl.heat_geodesics_solve(data,gamma) + l = igl.edge_lengths(V,F) + iV,iF,E,uE,EMAP,uE2E = igl.intrinsic_delaunay_triangulation(l,F) + L,il,iF = igl.intrinsic_delaunay_cotmatrix(V,F) + L = igl.cotmatrix_intrinsic(il,iF) + M = igl.massmatrix_intrinsic(il,iF) + +def test_nonmanifold(): + V,F = igl.icosahedron() + FF,C = igl.bfs_orient(F) + SF,SVI = igl.split_nonmanifold(F) + SV,SF,SVI = igl.split_nonmanifold(V,F) + +def test_biharmonic(): + V = np.array([[1,0,0],[0,1,0],[0,0,1],[-1,0,0],[0,-1,0],[0,0,-1]],dtype=np.float64) + T = np.array([ [2,1,5,3], [0,1,5,2], [5,4,0,2], [2,5,4,3]],dtype=np.int64) + S = [[0],[5]] + W = igl.biharmonic_coordinates(V,T,S) + +def test_bijective(): + # 2D triangulated grid + V,F = igl.triangulated_grid(3,3) + ear,ear_opp = igl.ears(F) + # we don't have igl.flip_ears so construct it by hand + res = np.array([3,3],dtype=np.int64) + E,_,_ = igl.boundary_facets(F) + F = E + # add a 3rd column of 4s + F = np.hstack((F,np.ones((F.shape[0],1),dtype=np.int64)*4)) + # boundary b + b = np.array([0,1,2,3,5,6,7,8],dtype=np.int64) + bc = V[b,:] + bc[-1,:] = [0.5,0.5] + U = igl.bijective_composite_harmonic_mapping(V,F,b,bc) + + +def test_copyleft(): + V = np.array([[0,0,-1],[2,0,-1],[0,2,-1],[1,1,1]],dtype=np.float64) + T = np.array([[0,1,2,3]],dtype=np.int64) + F,_,_ = igl.boundary_facets(T) + V,F = igl.loop(V,F) + + dV,dF,J = igl.copyleft.progressive_hulls(V,F) + + +def test_cgal(): + # tetrahedron + VA = np.array([[0,0,-1],[2,0,-1],[0,2,-1],[1,1,1]],dtype=np.float64) + T = np.array([[0,1,2,3]],dtype=np.int64) + FA,_,_ = igl.boundary_facets(T) + # flip z + VB = np.array([[0,0,1],[2,0,1],[0,2,1],[1,1,-1]],dtype=np.float64) + FB = FA[:,::-1] + IF,_,_,_,_ = igl.copyleft.cgal.intersect_other(VA,FA,VB,FB) + IF,_,_,_,_ = igl.copyleft.cgal.intersect_other(VA,FA,VB,FB,detect_only=True,first_only=True) + VC,FC,J = igl.copyleft.cgal.mesh_boolean(VA,FA,VB,FB,"union") + H = igl.copyleft.cgal.convex_hull(VC) + # concatenate A and B meshes + VC = np.vstack((VA,VB)) + FC = np.vstack((FA,FB+VA.shape[0])) + VV,FF,IF,J,IM = igl.copyleft.cgal.remesh_self_intersections(VC,FC) + _,_,IF,_,_ = igl.copyleft.cgal.remesh_self_intersections(VC,FC,detect_only=True,first_only=True) + + p = np.array([0,0,0],dtype=np.float64) + n = np.array([1,1,1],dtype=np.float64) + VV,FF,J = igl.copyleft.cgal.intersect_with_half_space(VC,FC,p,n) + equ = np.hstack((n,-n.dot(p))) + VV,FF,J = igl.copyleft.cgal.intersect_with_half_space(VC,FC,equ) + + P = np.array([[0.5,0.5,0.0],[0.5,0.5,0.5]],dtype=np.float64) + W = igl.copyleft.cgal.fast_winding_number(VA,FA,P) + W = igl.copyleft.cgal.fast_winding_number(VA,FA,P,expansion_order=2,beta=2.0) + + VC,FC,D,J = igl.copyleft.cgal.trim_with_solid(VA,FA,VB,FB) + + _,I,X = igl.random_points_on_mesh(1000,VC,FC) + N = igl.per_face_normals(VC,FC) + N = N[I,:] + point_indices, CH,CN,W = igl.octree(X) + I = igl.knn(X,X,20,point_indices,CH,CN,W) + A,T = igl.copyleft.cgal.point_areas(X,I,N) + +def test_embree(): + # octahedron + V = np.array([[1,0,0],[0,1,0],[0,0,1],[-1,0,0],[0,-1,0],[0,0,-1]],dtype=np.float64) + F = np.array([[0,1,2], [0,2,4], [0,4,5], [0,5,1], [1,3,2], [1,5,3], [2,3,4], [3,5,4]],dtype=np.int64) + N = igl.per_vertex_normals(V,F) + ei = igl.embree.EmbreeIntersector(); + ei.init(V,F) + S = igl.embree.ambient_occlusion(V,F,V,N,100) + S = igl.embree.ambient_occlusion(ei,V,N,100) + N = -N + S = igl.embree.shape_diameter_function(V,F,V,N,100) + S = igl.embree.shape_diameter_function(ei,V,N,100) + origin = np.array([2,2,2],dtype=np.float64) + direction = np.array([-2,-2,-2],dtype=np.float64) + hit = ei.intersectRay_first(origin,direction) + hits = ei.intersectRay(origin,direction) + hits = ei.intersectRay(origin,direction,tnear=0,tfar=1) + I,C = igl.embree.reorient_facets_raycast(V,F) + +def test_tetgen(): + # octahedron + V = np.array([[1,0,0],[0,1,0],[0,0,1],[-1,0,0],[0,-1,0],[0,0,-1]],dtype=np.float64) + F = np.array([[0,1,2], [0,2,4], [0,4,5], [0,5,1], [1,3,2], [1,5,3], [2,3,4], [3,5,4]],dtype=np.int64) + V,T,F,_,_,_,_,_,_ = igl.copyleft.tetgen.tetrahedralize(V,F,flags="Q") + +def test_triangle(): + V = np.array([[0,0],[1,0],[1,1],[0,1]],dtype=np.float64) + E = np.array([[0,1],[1,2],[2,3],[3,0]],dtype=np.int64) + V,F,_,_,_ = igl.triangle.triangulate(V,E,flags="Qc") + V,F,_,_,_ = igl.triangle.triangulate(V,E,flags="Q") + V,F,_,_,_ = igl.triangle.triangulate(V,E,flags="Qqa0.1") + V = np.array([[0,0,0],[1,0,0],[0,1,0],[0,0,1]],dtype=np.float64) + F = np.array([[1,3,0],[3,2,0],[2,3,1]],dtype=np.int64) + scaf_data = igl.triangle.SCAFData() + b = np.array([0,1,2],dtype=np.int64) + bc = np.array([[0,0],[1,0],[0,1]],dtype=np.float64) + V_init = np.array([[0,0],[1,0],[0,1],[0.1,0.1]],dtype=np.float64) + soft_p = 0; + igl.triangle.scaf_precompute(V,F,V_init,igl.ARAP,b,bc,soft_p,scaf_data) + L,rhs = igl.triangle.scaf_system(scaf_data) + U = igl.triangle.scaf_solve(1,scaf_data) + +def test_misc(): + V,F = igl.icosahedron() + BV,BF = igl.bounding_box(V,pad=1.0) + R,C,B = igl.circumradius(V,F) + R = igl.inradius(V,F) + _,E,EMAP,_,_ = igl.unique_edge_map(F) + L = igl.crouzeix_raviart_cotmatrix(V,F,E,EMAP) + M = igl.crouzeix_raviart_massmatrix(V,F,E,EMAP) + cuts = igl.cut_to_disk(F) + V,F = igl.cylinder(10,10) + VD,FD = igl.false_barycentric_subdivision(V,F) + V,F,T = single_tet() + theta, cos_theta = igl.dihedral_angles(V,T) + L = igl.edge_lengths(V,T) + A = igl.face_areas(V,T) + theta, cos_theta = igl.dihedral_angles_intrinsic(L,A) + + diff --git a/tests/test_basic.py b/tests/test_basic.py deleted file mode 100644 index b8f3cb2f..00000000 --- a/tests/test_basic.py +++ /dev/null @@ -1,2636 +0,0 @@ -import unittest -import os -import platform - -import igl -print("Using igl found at: ",igl.__file__) -import igl.triangle -import igl.copyleft.tetgen -import igl.copyleft.cgal -import numpy as np -import scipy as sp -import scipy.sparse as csc -import math -import sys -from git import Repo -import faulthandler -faulthandler.enable() - - - - -DOUBLE_EPS = 1.0e-14 -DOUBLE_EPS_SQ = 1.0e-28 -FLOAT_EPS = 1.0e-7 -FLOAT_EPS_SQ = 1.0e-14 - - -class TestBasic(unittest.TestCase): - - def setUp(self): - # This is called once for every sub-test. - - # Some global datastructures to use in the tests - np.random.seed(42) - # https://stackoverflow.com/a/45230996/148668 - self.test_data_path = os.path.join("./data","") - if not os.path.isdir(self.test_data_path): - Repo.clone_from("https://github.com/libigl/libigl-tests-data.git", self.test_data_path) - - self.v1, self.f1 = igl.read_triangle_mesh( - os.path.join(self.test_data_path, "bunny_small.off")) - self.v2, self.f2 = igl.read_triangle_mesh( - os.path.join(self.test_data_path, "fertility.off")) - - ## Alec: I don't understand why it makes sense to use junk random data - ## for the tests. Especially for f and t. These will almost never be - ## non-degenerate meshes. - #self.v = np.random.rand(10, 3).astype(self.v1.dtype) - #self.t = np.random.rand(10, 4) - #self.f = np.random.randint(0, 10, size=(20, 3), dtype=self.f1.dtype) - #self.g = np.random.randint(0, 10, size=(20, 4), dtype="int32") - # This model is a quad mesh that's been trivially triangulated - self.v3, self.f3 = igl.read_triangle_mesh(os.path.join(self.test_data_path, "face.obj")) - self.v4, self.t4, self.f4 = igl.read_mesh(os.path.join(self.test_data_path, "decimated-knight.mesh")) - self.q3 = np.concatenate((self.f3[0::2,0:3], self.f3[1::2,2:3]), axis=1) - # Use the bunny rather than random junk. Ideally we'd loop over meshes - # by category like the libigl tests. - self.v = self.v1 - self.f = self.f1 - - - self.default_int = np.array(range(2)).dtype - self.default_float = np.zeros((2,2)).dtype - - def tearDown(self): - vv1, ff1 = igl.read_triangle_mesh( - os.path.join(self.test_data_path, "bunny_small.off")) - vv2, ff2 = igl.read_triangle_mesh( - os.path.join(self.test_data_path, "fertility.off")) - self.assertTrue((vv1 == self.v1).all()) - self.assertTrue((ff1 == self.f1).all()) - - self.assertTrue((vv2 == self.v2).all()) - self.assertTrue((ff2 == self.f2).all()) - - def test_z_module(self): - # Extract all implemented functions from the module - funcs = [] - flist = ["helpers", "os", "print_usage", - "pyigl", "viewer", "check_dependencies"] - for att in dir(igl): - if str.istitle(att[0]) or att[:2] == "__" or att in flist: - continue - else: - funcs.append(att) - funcs = sorted(funcs) - - # Extract all tests from this file - tests = [] - flist = ["test_module"] - for att in dir(self): - if not att.startswith("test") or att in flist: - continue - else: - tests.append(att[5:]) - - # Check that there are tests for all functions - print("") - for f in funcs: - if f not in tests: - if f == "igl" or f == "np" or f == "pyigl_classes" or f == "sparse" or f == "spsolve": - continue - print("WARNING: Test for function %s missing." % f) - #self.assertTrue(f in tests) - - # sparse matrix, no flag attribute - def test_adjacency_matrix(self): - a = igl.adjacency_matrix(self.f) - b = igl.adjacency_matrix(self.f[:,:2]) # Test with edges only - self.assertTrue(a.shape == (self.v.shape[0], self.v.shape[0])) - self.assertTrue(b.shape == (self.v.shape[0], self.v.shape[0])) - self.assertTrue(a.dtype == self.f.dtype) - self.assertTrue(b.dtype == self.f.dtype) - self.assertTrue(type(a) == csc.csc_matrix) - self.assertTrue(type(b) == csc.csc_matrix) - - def test_avg_edge_length(self): - l = igl.avg_edge_length(self.v1, self.f1) - self.assertTrue(np.isclose(l, 0.004661094877063719)) - - # sparse matrix - def test_cotmatrix(self): - l = igl.cotmatrix(self.v, self.f) - self.assertTrue(l.shape == (self.v.shape[0], self.v.shape[0])) - self.assertTrue(l.dtype == self.v.dtype) - self.assertTrue(type(l) == csc.csc_matrix) - - def test_cotmatrix_intrinsic(self): - el = igl.edge_lengths(self.v, self.f) - l = igl.cotmatrix_intrinsic(el, self.f) - self.assertTrue(l.shape == (self.v.shape[0], self.v.shape[0])) - self.assertTrue(l.dtype == el.dtype) - self.assertTrue(type(l) == csc.csc_matrix) - - def test_ears(self): - ears, ears_opp = igl.ears(self.f1) - self.assertTrue(ears.shape == ears_opp.shape) - self.assertTrue(ears.dtype == self.f.dtype) - self.assertTrue(ears_opp.dtype == self.f.dtype) - self.assertTrue(ears.flags.c_contiguous) - self.assertTrue(ears_opp.flags.c_contiguous) - - def test_gaussian_curvature(self): - g = igl.gaussian_curvature(self.v, self.f) - self.assertTrue(g.shape == (self.v.shape[0],)) - self.assertTrue(g.dtype == self.v.dtype) - self.assertTrue(type(g) == np.ndarray) - self.assertTrue(g.flags.c_contiguous) - - # sparse matrix, no flag attribute - def test_grad_intrinsic(self): - el = igl.edge_lengths(self.v, self.f) - g = igl.grad_intrinsic(el, self.f) - self.assertTrue(g.shape == (self.f.shape[0] * 2, self.v.shape[0])) - self.assertTrue(type(g) == csc.csc_matrix) - - def test_grad(self): - g = igl.grad(self.v, self.f) - h = igl.grad(self.v, self.f, uniform=True) - self.assertTrue(g.shape == ( - self.f.shape[0] * self.v.shape[1], self.v.shape[0])) - self.assertTrue(h.shape == ( - self.f.shape[0] * self.v.shape[1], self.v.shape[0])) - self.assertTrue(type(g) == type(h) == csc.csc_matrix) - - # sparse matrix, no flag attribute - def test_massmatrix(self): - a = igl.massmatrix(self.v, self.f) - b = igl.massmatrix( - self.v, self.f, type=igl.MASSMATRIX_TYPE_BARYCENTRIC) - self.assertTrue(a.shape == (self.v.shape[0], self.v.shape[0])) - self.assertTrue(b.shape == (self.v.shape[0], self.v.shape[0])) - self.assertTrue(b.dtype == self.v.dtype) - self.assertTrue(a.dtype == self.v.dtype) - self.assertTrue(type(a) == type(b) == csc.csc_matrix) - - def test_massmatrix_intrinsic(self): - el = igl.edge_lengths(self.v, self.f) - a = igl.massmatrix_intrinsic(el, self.f) - b = igl.massmatrix_intrinsic( - el, self.f, type=igl.MASSMATRIX_TYPE_BARYCENTRIC) - self.assertTrue(a.shape == (self.v.shape[0], self.v.shape[0])) - self.assertTrue(b.shape == (self.v.shape[0], self.v.shape[0])) - self.assertTrue(b.dtype == el.dtype) - self.assertTrue(a.dtype == el.dtype) - self.assertTrue(type(a) == type(b) == csc.csc_matrix) - - def test_principal_curvature(self): - pd1, pd2, pv1, pv2 = igl.principal_curvature(self.v, self.f) - qd1, qd2, qv1, qv2 = igl.principal_curvature( - self.v, self.f, radius=7, use_k_ring=False) - self.assertTrue(pd1.shape == qd1.shape == pd2.shape == - qd2.shape == self.v.shape) - self.assertTrue(pv1.shape == qv1.shape == pv2.shape == - qv2.shape == (self.v.shape[0],)) - self.assertTrue(pd1.dtype == pd2.dtype == - pv1.dtype == pv2.dtype == self.v.dtype) - v = self.v.copy() - - pd1, pd2, pv1, pv2 = igl.principal_curvature(v, self.f) - self.assertTrue(pd1.dtype == pd2.dtype == - pv1.dtype == pv2.dtype == v.dtype) - self.assertTrue(type(pd1) == type(pd2) == type(pv1) - == type(pv2) == np.ndarray) - self.assertTrue(pd1.flags.c_contiguous) - self.assertTrue(pd2.flags.c_contiguous) - self.assertTrue(pv1.flags.c_contiguous) - self.assertTrue(pv2.flags.c_contiguous) - self.assertTrue(qd1.flags.c_contiguous) - self.assertTrue(qd2.flags.c_contiguous) - self.assertTrue(qv1.flags.c_contiguous) - self.assertTrue(qv2.flags.c_contiguous) - - def test_read_obj(self): - v, _, n, f, _, _ = igl.read_obj(self.test_data_path + "face.obj") - self.assertTrue(type(v) == type(f) == type(n) == np.ndarray) - self.assertTrue(v.shape == (25905, 3) and n.shape == - (0, 0) and f.shape == (51712, 3)) - self.assertTrue(v.dtype == self.default_float) - self.assertTrue(f.dtype == self.f.dtype) - v, _, n, f, _, _ = igl.read_obj( - self.test_data_path + "face.obj", dtype="float32") - self.assertTrue(v.shape == (25905, 3) and n.shape == - (0, 0) and f.shape == (51712, 3)) - self.assertTrue(v.dtype == np.float32) - self.assertTrue(v.flags.c_contiguous) - self.assertTrue(f.flags.c_contiguous) - self.assertTrue(n.flags.c_contiguous) - - def test_read_off(self): - v, f, n = igl.read_off(self.test_data_path + "bunny_small.off") - self.assertTrue(type(v) == type(f) == type(n) == np.ndarray) - self.assertTrue(v.shape == (3485, 3) and n.shape == - (0, 0) and f.shape == (6966, 3)) - self.assertTrue(v.dtype == self.default_float) - v, f, n = igl.read_off( - self.test_data_path + "bunny_small.off", read_normals=False, dtype="float32") - self.assertTrue(v.shape == (3485, 3) and n.shape == - (0, 0) and f.shape == (6966, 3)) - self.assertTrue(v.dtype == np.float32) - self.assertTrue(v.flags.c_contiguous) - self.assertTrue(f.flags.c_contiguous) - self.assertTrue(n.flags.c_contiguous) - - def test_read_mesh(self): - v, t, f = igl.read_mesh(os.path.join( - self.test_data_path, "octopus-low.mesh")) - self.assertTrue(type(v) == type(t) == type(f) == np.ndarray) - self.assertTrue(v.flags.c_contiguous) - self.assertTrue(t.flags.c_contiguous) - self.assertTrue(f.flags.c_contiguous) - - self.assertTrue(v.dtype == self.default_float) - self.assertTrue(t.dtype == self.f1.dtype) - self.assertTrue(f.dtype == self.f1.dtype) - - def test_read_triangle_mesh(self): - v, f = igl.read_triangle_mesh(self.test_data_path + "octopus-low.mesh") - #print(v.shape, f.shape) - v, f = igl.read_triangle_mesh(self.test_data_path + "face.obj") - #print(v.shape, f.shape) - v, f = igl.read_triangle_mesh(self.test_data_path + "bunny_small.off") - #print(v.shape, f.shape) - self.assertTrue(f.dtype == self.f.dtype) - self.assertTrue(v.flags.c_contiguous) - self.assertTrue(f.flags.c_contiguous) - - def test_read_triangle_mesh_type_issue(self): - v, f = igl.read_triangle_mesh(self.test_data_path + "face.obj") - vs = np.array([0]) - vt = np.arange(v.shape[0]) - d = igl.exact_geodesic(v, f, vs, vt) - self.assertTrue(d.dtype == v.dtype) - - # def test_triangulate(self): - # v = np.array([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]) - # e = np.array([[0, 1], [1, 2], [2, 3], [3, 0]], dtype="int32") - # h = np.array([[]]) - # #print("v.dtype = %s, h.dtype = %s" % (v.dtype, h.dtype)) - # v2, f2 = igl.triangulate(v, e, h, flags="a0.005qQ") - # self.assertTrue(v2.dtype == v.dtype) - # self.assertTrue(f2.dtype == e.dtype) - # self.assertTrue(type(v2) == type(f2) == np.ndarray) - # self.assertTrue(v2.flags.c_contiguous) - # self.assertTrue(f2.flags.c_contiguous) - - def test_write_obj(self): - suc = igl.write_obj("test.obj", self.v, self.f) - self.assertTrue(suc) - self.assertTrue(os.path.isfile("test.obj")) - - def test_write_off(self): - suc = igl.write_off("test.off", self.v, self.f, self.v) - self.assertTrue(suc) - self.assertTrue(os.path.isfile("test.off")) - - def test_adjacency_list(self): - a = igl.adjacency_list(self.f1) - self.assertEqual(len(a), self.v1.shape[0]) - - # all are sparse matrices - def test_arap_linear_block(self): - kd = igl.arap_linear_block(self.v1, self.f1, d=2, energy=0) - self.assertTrue(kd.shape[0] > 0) - - def test_arap_linear_block_elements(self): - kd = igl.arap_linear_block_elements(self.v1, self.f1, d=2) - self.assertTrue(kd.shape[0] > 0) - - def test_arap_linear_block_spokes(self): - kd = igl.arap_linear_block_spokes(self.v1, self.f1, d=2) - self.assertTrue(kd.shape[0] > 0) - - def test_arap_linear_block_spokes_and_rims(self): - kd = igl.arap_linear_block_spokes_and_rims(self.v1, self.f1, d=2) - self.assertTrue(kd.shape[0] > 0) - - def test_arap_rhs(self): - k = igl.arap_rhs(self.v1, self.f1, d=2, energy=0) - self.assertTrue(k.shape[0] > 0) - - def test_average_onto_faces(self): - s = np.random.rand(self.f1.shape[0]) - sf = igl.average_onto_faces(self.f1, s) - self.assertEqual(sf.shape[0], self.f1.shape[0]) - self.assertEqual(sf.dtype, s.dtype) - - def test_average_onto_vertices(self): - s = np.random.rand(self.f1.shape[0], self.v1.shape[1]) - sv = igl.average_onto_vertices(self.v1, self.f1, s) - self.assertEqual(sv.shape[0], self.v1.shape[0]) - self.assertEqual(sv.dtype, s.dtype) - self.assertTrue(sv.flags.c_contiguous) - - def test_barycentric_coordinates(self): - a, b, c = self.v1[self.f1[:, 0] - ], self.v1[self.f1[:, 1]], self.v1[self.f1[:, 2]] - bc = igl.barycentric_coordinates_tri(a, a, b, c) - self.assertEqual(bc.shape, a.shape) - expected_bc = np.zeros(a.shape) - expected_bc[:, 0] = np.ones(a.shape[0]) - self.assertTrue(np.linalg.norm(expected_bc-bc) < 1e-6) - self.assertTrue(bc.flags.c_contiguous) - - d = 0.5*a + 0.5*c + np.array([0.1, 0.1, 0.1]) - bc = igl.barycentric_coordinates_tet(d, a, b, c, d) - self.assertEqual(bc.shape, (a.shape[0], 4)) - self.assertTrue(bc.flags.c_contiguous) - - def test_barycentric_coordinates_tri(self): - # tested in test_barycentric_coordinates - pass - - def test_barycentric_coordinates_tet(self): - # tested in test_barycentric_coordinates - pass - - def test_vertex_components(self): - a = igl.adjacency_matrix(self.f1) - c, count = igl.vertex_components_from_adjacency_matrix(a) - self.assertEqual(c.shape[0], self.v1.shape[0]) - self.assertTrue(c.flags.c_contiguous) - - c = igl.vertex_components(self.f1) - self.assertEqual(c.shape[0], self.v1.shape[0]) - self.assertTrue(c.flags.c_contiguous) - - def test_facet_components(self): - c = igl.facet_components(self.f1) - self.assertEqual(c.shape, (self.f1.shape[0],)) - self.assertTrue(np.array_equal(c, np.zeros_like(c))) - self.assertTrue(c.flags.c_contiguous) - - def test_bfs(self): - a = igl.adjacency_matrix(self.f1) - p, d = igl.bfs(a, 0) - self.assertEqual(p.shape, (self.v1.shape[0],)) - self.assertEqual(p.shape, (self.v1.shape[0],)) - - try: - p, d, = igl.bfs(a, -1) - self.assertTrue(False) - except IndexError as e: - pass - - a = csc.csc_matrix(np.zeros([0, 0]), dtype=np.int32) - try: - p, d, = igl.bfs(a, 0) - self.assertTrue(False) - except ValueError as e: - pass - - a = csc.csc_matrix(np.zeros([10, 11]), dtype=np.int32) - try: - p, d, = igl.bfs(a, 0) - self.assertTrue(False) - except ValueError as e: - pass - - a = csc.csc_matrix(np.zeros([10, 10]), dtype=np.int32) - p, d, = igl.bfs(a, 0) - self.assertEqual(p.shape, ()) - self.assertTrue(np.array_equal(d, -np.ones(10))) - self.assertTrue(p.flags.c_contiguous) - - def test_bfs_orient(self): - ff, c = igl.bfs_orient(self.f1) - self.assertEqual(ff.shape, self.f1.shape) - self.assertEqual(c.shape, (self.f1.shape[0],)) - self.assertTrue(np.array_equal(self.f1, ff)) - self.assertTrue(ff.flags.c_contiguous) - self.assertTrue(c.flags.c_contiguous) - - def test_oriented_facets(self): - e = igl.oriented_facets(self.f1) - self.assertTrue(e.shape[0] > self.f1.shape[0]) - self.assertTrue(0 <= np.max(e) < self.v1.shape[0]) - self.assertTrue(e.flags.c_contiguous) - - # sparse matrix, no flag attribute - def test_orientable_patches(self): - c, a = igl.orientable_patches(self.f1) - - self.assertTrue(np.array_equal(c, np.zeros(self.f1.shape[0]))) - self.assertEqual(a.shape, (self.f1.shape[0], self.f1.shape[0])) - - def test_edge_topology(self): - ev, fe, ef = igl.edge_topology(self.v1, self.f1) - self.assertEqual(fe.shape, self.f1.shape) - self.assertEqual(ef.shape, (ev.shape[0], 2)) - self.assertEqual(np.max(ev), self.v1.shape[0] - 1) - self.assertEqual(np.min(ef), 0) - self.assertTrue(fe.flags.c_contiguous) - self.assertTrue(ef.flags.c_contiguous) - self.assertTrue(ev.flags.c_contiguous) - - def test_edges(self): - e = igl.edges(self.f1) - self.assertTrue(e.shape[0] > self.f1.shape[0]) - self.assertEqual(e.shape[1], 2) - self.assertTrue(e.flags.c_contiguous) - - def test_bone_parents(self): - e = igl.edges(self.f1) - res = igl.bone_parents(e) - self.assertEqual(res.shape[0], e.shape[0]) - self.assertTrue(e.flags.c_contiguous) - - def test_sort_angles(self): - r = igl.sort_angles(self.v) - self.assertTrue(r.dtype == self.f.dtype) - self.assertEqual(r.shape[0], self.v.shape[0]) - self.assertTrue(r.flags.c_contiguous) - - def test_circumradius(self): - r = igl.circumradius(self.v, self.f) - self.assertTrue(r.dtype == self.v.dtype) - self.assertEqual(r.shape[0], self.f.shape[0]) - self.assertTrue(r.flags.c_contiguous) - - def test_quad_planarity(self): - p = igl.quad_planarity(self.v3, self.q3) - self.assertTrue(p.dtype == self.v3.dtype) - self.assertEqual(p.shape[0], self.q3.shape[0]) - self.assertTrue(p.flags.c_contiguous) - - def test_collapse_small_triangles(self): - ff = igl.collapse_small_triangles(self.v, self.f, 0.5) - self.assertEqual(ff.shape[1], self.f.shape[1]) - self.assertTrue(ff.flags.c_contiguous) - - def test_bounding_box(self): - bv, bf = igl.bounding_box(self.v) - self.assertEqual(bv.shape[1], self.v.shape[1]) - self.assertEqual(bf.shape[1], self.v.shape[1]) - self.assertTrue(bv.flags.c_contiguous) - self.assertTrue(bf.flags.c_contiguous) - - def test_per_face_normals(self): - n = igl.per_face_normals(self.v2, self.f2, self.v2) - - self.assertEqual(n.dtype, self.v2.dtype) - self.assertEqual(n.shape[0], self.f2.shape[0]) - self.assertEqual(n.shape[1], 3) - self.assertTrue(n.flags.c_contiguous) - - def test_ambient_occlusion(self): - n = igl.per_vertex_normals(self.v2, self.f2) - s = igl.ambient_occlusion(self.v2, self.f2, self.v2, n, 2) - - self.assertEqual(s.dtype, self.v1.dtype) - self.assertEqual(len(s.shape), 1) - self.assertTrue(s.flags.c_contiguous) - - def test_write_triangle_mesh(self): - ok = igl.write_triangle_mesh("out.obj", self.v, self.f) - self.assertTrue(ok) - self.assertTrue(os.path.isfile("out.obj")) - - def test_barycenter(self): - bc = igl.barycenter(self.v, self.f) - self.assertEqual(bc.dtype, self.v.dtype) - self.assertEqual(bc.shape[0], self.f.shape[0]) - self.assertEqual(bc.shape[1], 3) - self.assertTrue(bc.flags.c_contiguous) - - def test_read_dmat(self): - # TODO: maybe a vector - mat = igl.read_dmat(self.test_data_path + "decimated-knight-selection.dmat") - self.assertEqual(mat.dtype, "float64") - self.assertTrue(mat.flags.c_contiguous) - - def test_write_dmat(self): - igl.write_dmat(self.test_data_path + "decimated-knight-selection.dmat",self.v) - - # sparse matrix, no flag attribute - def test_vector_area_matrix(self): - a = igl.vector_area_matrix(self.f) - self.assertEqual(a.dtype, "float64") - self.assertEqual(a.shape[0], a.shape[1]) - self.assertEqual(a.shape[0], self.v.shape[0]*2) - - # def test_tetrahedralize(self): - # status, tv, tt, tf = igl.tetrahedralize(self.v2, self.f2) - - # self.assertEqual(status, 0) - # self.assertEqual(tv.dtype, self.v1.dtype) - # self.assertEqual(tt.dtype, self.f1.dtype) - # self.assertEqual(tf.dtype, self.f1.dtype) - - # self.assertEqual(tv.shape[1], 3) - # self.assertEqual(tf.shape[1], 3) - # self.assertEqual(tt.shape[1], 4) - # self.assertTrue(tv.flags.c_contiguous) - # self.assertTrue(tt.flags.c_contiguous) - # self.assertTrue(tf.flags.c_contiguous) - - def test_hausdorff(self): - dist = igl.hausdorff(self.v, self.f, self.v1, self.f1) - # print(dist) - - def test_isolines(self): - func = np.random.rand(self.v1.shape[0], 1) - vals = np.linspace(0,1, 10) - iso_v, iso_e, I = igl.isolines(self.v1, self.f1, func, vals) - - self.assertEqual(iso_v.dtype, func.dtype) - self.assertEqual(iso_e.dtype, self.f1.dtype) - self.assertEqual(iso_e.shape[1], 2) - self.assertEqual(iso_e.shape[0], I.shape[0]) - self.assertTrue(iso_v.flags.c_contiguous) - self.assertTrue(iso_e.flags.c_contiguous) - self.assertTrue(I.flags.c_contiguous) - - def test_unproject_ray(self): - pos = np.random.rand(2, 1) - model = np.random.rand(4, 4) - proj = np.random.rand(4, 4) - viewport = np.random.rand(4, 1) - source, direction = igl.unproject_ray(pos, model, proj, viewport) - self.assertEqual(source.dtype, self.v.dtype) - self.assertEqual(direction.dtype, self.v.dtype) - self.assertEqual(len(source.shape), 1) - self.assertTrue(source.flags.c_contiguous) - - def test_winding_number(self): - s = igl.winding_number(self.v1, self.f1, self.v) - - self.assertEqual(s.shape[0], self.v.shape[0]) - - def test_winding_number_for_point(self): - p = np.zeros((1, 3)) - s = igl.winding_number_for_point(self.v1, self.f1, p) - - def test_unproject(self): - model = np.array([[1., 0, 0, 0], [0, 1, 0, 0], - [0, 0, 1, 0], [0, 0, 0, 1]]) - proj = np.array([[1., 0, 0, 0], [0, 1, 0, 0], - [0, 0, 1, 0], [0, 0, 0, 1]]) - viewport = np.array([1., 1, 1, 1]) - scene = igl.unproject(self.v, model, proj, viewport) - - self.assertEqual(scene.dtype, self.v.dtype) - self.assertEqual(scene.shape[0], self.v.shape[0]) - self.assertEqual(scene.shape[1], 3) - self.assertTrue(scene.flags.c_contiguous) - - def test_upsample(self): - nv, nf = igl.upsample(self.v1, self.f1) - - self.assertEqual(nv.dtype, self.v1.dtype) - self.assertEqual(nf.dtype, self.f1.dtype) - - self.assertEqual(nv.shape[1], self.v1.shape[1]) - self.assertEqual(nf.shape[1], self.f1.shape[1]) - self.assertTrue(nv.flags.c_contiguous) - self.assertTrue(nf.flags.c_contiguous) - - def test_random_points_on_mesh(self): - n = 10 - b, fi, x = igl.random_points_on_mesh(n, self.v1, self.f1) - - self.assertEqual(b.dtype, self.v1.dtype) - self.assertEqual(x.dtype, self.v1.dtype) - self.assertEqual(fi.dtype, self.f1.dtype) - - self.assertEqual(b.shape[0], n) - self.assertEqual(b.shape[1], 3) - self.assertEqual(x.shape[0], n) - self.assertEqual(x.shape[1], self.v1.shape[1]) - self.assertEqual(fi.shape[0], n) - self.assertTrue(b.flags.c_contiguous) - self.assertTrue(fi.flags.c_contiguous) - self.assertTrue(x.flags.c_contiguous) - - def test_boundary_loop(self): - l = igl.boundary_loop(self.f) - self.assertEqual(len(l.shape), 1) - self.assertEqual(l.dtype, self.f.dtype) - self.assertTrue(l.flags.c_contiguous) - - def test_all_boundary_loop(self): - l = igl.all_boundary_loop(self.f3) - self.assertEqual(type(l), type([])) - self.assertTrue(len(l) > 0) - - def test_bounding_box_diagonal(self): - length = igl.bounding_box_diagonal(self.v1) - self.assertEqual(type(length), float) - - def test_boundary_facets(self): - b = igl.boundary_facets(self.f1) - self.assertEqual(b.dtype, self.f1.dtype) - self.assertTrue(b.shape[1] == 3 or b.shape[1] == 2) - self.assertTrue(b.flags.c_contiguous) - - def test_connect_boundary_to_infinity(self): - fo = igl.connect_boundary_to_infinity(self.f1) - self.assertEqual(fo.dtype, self.f1.dtype) - self.assertEqual(fo.shape[1], 3) - self.assertTrue(fo.flags.c_contiguous) - - def test_connect_boundary_to_infinity_face(self): - vof, fof = igl.connect_boundary_to_infinity_face(self.v1, self.f1) - self.assertEqual(fof.dtype, self.f1.dtype) - self.assertEqual(fof.shape[1], 3) - self.assertEqual(vof.dtype, self.v1.dtype) - self.assertEqual(vof.shape[1], 3) - self.assertTrue(vof.flags.c_contiguous) - self.assertTrue(fof.flags.c_contiguous) - - def test_connect_boundary_to_infinity_index(self): - foi = igl.connect_boundary_to_infinity_index(self.f1, 0) - self.assertEqual(foi.dtype, self.f1.dtype) - self.assertEqual(foi.shape[1], 3) - self.assertTrue(foi.flags.c_contiguous) - - def test_cotmatrix_entries(self): - c = igl.cotmatrix_entries(self.v1, self.f1) - self.assertEqual(c.dtype, self.v1.dtype) - self.assertEqual(c.shape[0], self.f1.shape[0]) - self.assertTrue(c.shape[1] == 3 or c.shape[1] == 6) - self.assertTrue(c.flags.c_contiguous) - - # sparse matrix, no flag attribute - def test_crouzeix_raviart_cotmatrix(self): - l, e, emap = igl.crouzeix_raviart_cotmatrix(self.v1, self.f1) - self.assertEqual(l.dtype, self.v1.dtype) - self.assertEqual(l.shape[0], e.shape[0]) - self.assertEqual(l.shape[1], e.shape[0]) - self.assertTrue(e.shape[1] == 2 or e.shape[1] == 3) - self.assertTrue(emap.shape[0] == 3*self.f1.shape[0] - or emap.shape[0] == 4*self.f1.shape[0]) - self.assertTrue(e.flags.c_contiguous) - self.assertTrue(emap.flags.c_contiguous) - l2 = igl.crouzeix_raviart_cotmatrix_known_e(self.v1, self.f1, e, emap) - self.assertEqual(l2.dtype, l.dtype) - self.assertEqual(l2.shape, l.shape) - - def test_crouzeix_raviart_cotmatrix_known_e(self): - pass - - def test_crouzeix_raviart_massmatrix(self): - m, e, emap = igl.crouzeix_raviart_massmatrix(self.v1, self.f1) - self.assertEqual(m.dtype, self.v1.dtype) - self.assertEqual(m.shape[0], e.shape[0]) - self.assertEqual(m.shape[1], e.shape[0]) - self.assertTrue(e.shape[1] == 2 or e.shape[1] == 3) - self.assertTrue(emap.shape[0] == 3*self.f1.shape[0] - or emap.shape[0] == 4*self.f1.shape[0]) - self.assertTrue(e.flags.c_contiguous) - self.assertTrue(emap.flags.c_contiguous) - m2 = igl.crouzeix_raviart_massmatrix_known_e(self.v1, self.f1, e, emap) - self.assertEqual(m2.dtype, m.dtype) - self.assertEqual(m2.shape, m.shape) - - def test_crouzeix_raviart_massmatrix_known_e(self): - pass - - def test_cylinder(self): - v, f = igl.cylinder(100, 100) - self.assertEqual(v.dtype, self.v.dtype) - self.assertEqual(f.dtype, self.f.dtype) - self.assertEqual(v.shape[1], 3) - self.assertEqual(f.shape[1], 3) - self.assertTrue(v.flags.c_contiguous) - self.assertTrue(f.flags.c_contiguous) - - def test_decimate(self): - success, u, g, j, i = igl.decimate(self.v1, self.f1, 100, False) - self.assertEqual(u.shape[1], self.v1.shape[1]) - self.assertEqual(g.shape[1], 3) - self.assertEqual(j.shape[0], g.shape[0]) - self.assertTrue(len(j.shape) == len(i.shape) and len(i.shape) == 1) - self.assertEqual(type(success), bool) - self.assertTrue(u.dtype == self.v.dtype) - self.assertTrue(g.dtype == j.dtype and j.dtype == - i.dtype and i.dtype == self.f.dtype) - self.assertTrue(u.flags.c_contiguous) - self.assertTrue(g.flags.c_contiguous) - self.assertTrue(j.flags.c_contiguous) - self.assertTrue(i.flags.c_contiguous) - - def test_dihedral_angles(self): - theta, cos_theta = igl.dihedral_angles(self.v4, self.t4) - self.assertEqual(theta.dtype, self.v4.dtype) - self.assertEqual(cos_theta.dtype, self.v4.dtype) - self.assertTrue( - theta.shape == cos_theta.shape and cos_theta.shape == (self.t4.shape[0], 6)) - self.assertTrue(theta.flags.c_contiguous) - self.assertTrue(cos_theta.flags.c_contiguous) - - def test_dihedral_angles_intrinsic(self): - # intrinsic function of dihedral_angles - pass - - def test_directed_edge_parents(self): - e = np.random.randint(0, 10, size=(10, 2)) - p = igl.directed_edge_parents(e) - self.assertEqual(p.dtype, e.dtype) - self.assertEqual(p.shape[0], e.shape[0]) - self.assertEqual(len(p.shape), 1) - self.assertTrue(p.flags.c_contiguous) - - def test_doublearea(self): - a = igl.doublearea(self.v1, self.f1) - self.assertEqual(a.shape[0], self.f1.shape[0]) - self.assertEqual(a.dtype, self.v1.dtype) - self.assertTrue(a.flags.c_contiguous) - - def test_euler_characteristic(self): - eu = igl.euler_characteristic(self.f1) - self.assertEqual(type(eu), int) - - def test_fit_plane(self): - n, c = igl.fit_plane(self.v1) - self.assertTrue(n.dtype == c.dtype == self.v1.dtype) - self.assertTrue(n.shape == c.shape == (3,)) - self.assertTrue(n.flags.c_contiguous) - self.assertTrue(c.flags.c_contiguous) - - def test_internal_angles(self): - k = igl.internal_angles(self.v1, self.f1) - self.assertEqual(k.dtype, self.v1.dtype) - self.assertEqual(k.shape, self.f1.shape) - self.assertTrue(k.flags.c_contiguous) - - def test_is_edge_manifold(self): - is_m = igl.is_edge_manifold(self.f1) - self.assertEqual(type(is_m), bool) - - def test_map_vertices_to_circle(self): - bnd = np.random.randint(0, self.v1.shape[0], size=(100, 1)) - uv = igl.map_vertices_to_circle(self.v1, bnd) - self.assertEqual(uv.dtype, self.v1.dtype) - self.assertEqual(uv.shape, (bnd.shape[0], 2)) - self.assertTrue(uv.flags.c_contiguous) - - def test_marching_cubes(self): - #test empty level set. - n = 50 - emptyField = np.zeros((n*n*n,1)) - K = np.linspace( -1.0, 1.0, n) - pts = np.array([[x,y,z] for x in K for y in K for z in K]) - V,F = igl.marching_cubes(emptyField, pts, n, n, n, 0.0) - self.assertEqual(V.shape, (0, 3)) - self.assertEqual(F.shape, (0, 3)) - - #test marching over a sphere - sphereField = np.linalg.norm(pts, axis=1) - 1 - V,F = igl.marching_cubes(sphereField, pts, n, n, n, 0.0) - - self.assertTrue(V.dtype == pts.dtype) - self.assertTrue(F.dtype == self.default_int) - - self.assertNotEqual(V.shape, (0,3)) - self.assertNotEqual(F.shape, (0,3)) - self.assertTrue(F.flags.c_contiguous) - self.assertTrue(F.flags.c_contiguous) - - def test_per_vertex_normals(self): - n = igl.per_vertex_normals(self.v1, self.f1, 0) - self.assertEqual(n.shape, (self.v1.shape[0], 3)) - self.assertEqual(n.dtype, self.v1.dtype) - self.assertTrue(n.flags.c_contiguous) - - def test_per_corner_normals(self): - n = igl.per_corner_normals(self.v1, self.f1, 80) - self.assertEqual(n.shape, (self.f1.shape[0]*3, 3)) - self.assertEqual(n.dtype, self.v1.dtype) - self.assertTrue(n.flags.c_contiguous) - - def test_per_vertex_attribute_smoothing(self): - aout = igl.per_vertex_attribute_smoothing(self.v1, self.f1) - self.assertEqual(aout.shape, self.v1.shape) - self.assertTrue(aout.flags.c_contiguous) - - def test_piecewise_constant_winding_number(self): - is_w = igl.piecewise_constant_winding_number(self.f1) - self.assertEqual(type(is_w), bool) - - def test_procrustes(self): - s, r, t = igl.procrustes(self.v1, self.v1, True, True) - self.assertEqual(type(s), float) - self.assertTrue(r.dtype == t.dtype == self.v1.dtype) - - def test_qslim(self): - success, u, g, j, i = igl.qslim(self.v1, self.f1, 100, False) - self.assertEqual(u.dtype, self.v1.dtype) - self.assertTrue(g.dtype == j.dtype == i.dtype == self.f1.dtype) - self.assertEqual(u.shape[1], self.v1.shape[1]) - self.assertEqual(g.shape[1], 3) - self.assertTrue(j.shape[0] > 0 and i.shape[0] > 0) - self.assertTrue(u.flags.c_contiguous) - self.assertTrue(g.flags.c_contiguous) - self.assertTrue(j.flags.c_contiguous) - self.assertTrue(i.flags.c_contiguous) - - def test_per_edge_normals(self): - fn = np.random.rand(self.f1.shape[0], 3) - n, e, emap = igl.per_edge_normals(self.v1, self.f1, 0, fn) - self.assertEqual(e.shape[1], 2) - # incorrect documentation saying emap and e have the same number of rows - #self.assertEqual(e.shape[0], emap.shape[0]) - self.assertTrue(n.flags.c_contiguous) - self.assertTrue(e.flags.c_contiguous) - self.assertTrue(emap.flags.c_contiguous) - - def test_remove_duplicate_vertices(self): - epsilon = 1e-6 - sv, svi, svj, sf = igl.remove_duplicate_vertices( - self.v1, self.f1, epsilon) - self.assertTrue(sv.dtype == self.v1.dtype) - self.assertTrue(svi.dtype == svj.dtype == sf.dtype == self.f1.dtype) - self.assertEqual(sv.shape[1], self.v1.shape[1]) - self.assertTrue(len(svi.shape) == len(svj.shape) == 1) - self.assertTrue(sv.flags.c_contiguous) - self.assertTrue(svi.flags.c_contiguous) - self.assertTrue(svj.flags.c_contiguous) - self.assertTrue(sf.flags.c_contiguous) - - def test_remove_unreferenced(self): - nv, nf, i, j = igl.remove_unreferenced(self.v1, self.f1) - self.assertEqual(nv.shape[1], self.v1.shape[1]) - self.assertEqual(nf.shape[1], self.f1.shape[1]) - self.assertEqual(i.shape[0], self.v1.shape[0]) - self.assertEqual(nv.dtype, self.v1.dtype) - self.assertTrue(nf.dtype == i.dtype == j.dtype == self.f1.dtype) - self.assertTrue(nv.flags.c_contiguous) - self.assertTrue(nf.flags.c_contiguous) - self.assertTrue(i.flags.c_contiguous) - self.assertTrue(j.flags.c_contiguous) - - def test_resolve_duplicated_faces(self): - f2, j = igl.resolve_duplicated_faces(self.f1) - self.assertTrue(f2.dtype == self.f1.dtype == j.dtype) - self.assertEqual(self.f1.shape[1], f2.shape[1]) - self.assertEqual(f2.shape[0], j.shape[0]) - self.assertTrue(f2.flags.c_contiguous) - self.assertTrue(j.flags.c_contiguous) - - def test_shape_diameter_function(self): - s = igl.shape_diameter_function( - self.v1, self.f1, self.v1, self.v1, 100) - self.assertEqual(s.shape[0], self.v1.shape[0]) - self.assertEqual(s.dtype, self.v1.dtype) - self.assertTrue(s.flags.c_contiguous) - - def test_triangle_triangle_adjacency(self): - tt, tti = igl.triangle_triangle_adjacency(self.f1) - self.assertTrue(tt.shape == tti.shape == (self.f1.shape[0], 3)) - self.assertTrue(tt.dtype == tti.dtype == self.f1.dtype) - self.assertTrue(tt.flags.c_contiguous) - self.assertTrue(tti.flags.c_contiguous) - - def test_uniformly_sample_two_manifold_at_vertices(self): - s = igl.uniformly_sample_two_manifold_at_vertices(self.v1, 100, 1.0) - self.assertEqual(s.dtype, self.f1.dtype) - self.assertTrue(s.shape[0] > 0) - self.assertTrue(s.flags.c_contiguous) - - def test_uniformly_sample_two_manifold_internal(self): - # internal function tested in test_uniformly_sample_two_manifold - pass - - def test_unproject_in_mesh(self): - pos = np.array([10., 10.]) - eye = np.eye(4) - viewport = np.array([0., 0., 100., 100.]) - obj, hits = igl.unproject_in_mesh( - pos, eye, eye, viewport, self.v1, self.f1) - - self.assertTrue(obj.flags.c_contiguous) - self.assertTrue(obj.dtype == self.v1.dtype) - - def test_unproject_onto_mesh(self): - pos = np.array([10., 10.]) - eye = np.eye(4) - viewport = np.array([0., 0., 100., 100.]) - ok, fid, bc = igl.unproject_onto_mesh( - pos, eye, eye, viewport, self.v1, self.f1) - - self.assertTrue(type(ok) == bool) - self.assertTrue(bc.flags.c_contiguous) - self.assertTrue(bc.dtype == self.v1.dtype) - - def test_vertex_components_from_adjacency_matrix(self): - # tested in test_vertex_components - pass - - def test_vertex_triangle_adjacency(self): - vf, ni = igl.vertex_triangle_adjacency(self.f1, self.v1.shape[0]) - self.assertEqual(vf.shape[0], 3*self.f1.shape[0]) - self.assertTrue(len(vf.shape) == len(ni.shape) == 1) - self.assertEqual(ni.shape[0], self.v1.shape[0]+1) - self.assertTrue(vf.flags.c_contiguous) - self.assertTrue(ni.flags.c_contiguous) - - def test_tet_tet_adjacency(self): - tet = np.array([[0, 1, 2, 3], [4, 5, 6, 7]]) - tt, tti = igl.tet_tet_adjacency(tet) - - self.assertEqual(tt.shape, tet.shape) - self.assertEqual(tti.shape, tet.shape) - self.assertEqual(tti.dtype, tet.dtype) - self.assertTrue(tt.flags.c_contiguous) - self.assertTrue(tti.flags.c_contiguous) - - def test_arap1(self): - v, f, _ = igl.read_off(os.path.join(self.test_data_path, "camelhead.off")) - b = igl.boundary_loop(f) - thetas = np.linspace(0, 2 * np.pi, len(b))[:, np.newaxis] - bc = np.concatenate([np.cos(thetas), np.sin( - thetas), np.zeros_like(thetas)], axis=1) - uv_initial_guess = igl.harmonic(v, f, b, bc, 1) - - v2d = v[:, :2].copy() - arap1 = igl.ARAP(v2d, f, 2, b) - vp1 = arap1.solve(bc[:, :2], uv_initial_guess[:, :2]) - self.assertEqual(vp1.shape[0], v.shape[0]) - self.assertTrue(vp1.flags.c_contiguous) - - arap2 = igl.ARAP(v, f, 3, b) - vp2 = arap2.solve(bc, uv_initial_guess) - self.assertEqual(vp2.shape[0], v.shape[0]) - self.assertTrue(vp2.flags.c_contiguous) - - def test_arap2(self): - num_b = 100 - - thetas = np.linspace(0, 2 * np.pi, num_b)[:, np.newaxis] - r = thetas / (2 * np.pi) - boundary = np.concatenate( - [r * np.cos(thetas), np.sin(thetas), np.zeros([num_b, 1])], axis=1) - edges = np.array([(i, (i + 1) % boundary.shape[0]) - for i in range(boundary.shape[0])]) - v = np.load(os.path.join(self.test_data_path, "test_arap2_v.npy")) - f = np.load(os.path.join(self.test_data_path, "test_arap2_f.npy")) - v = np.concatenate([v, np.zeros([v.shape[0], 1])], axis=1) - b = igl.boundary_loop(f) - - thetas = np.linspace(0, 2 * np.pi, len(b))[:, np.newaxis] - circle_b = np.concatenate( - [np.cos(thetas), np.sin(thetas), np.zeros([len(b), 1])], axis=1) - - v0 = igl.harmonic(v, f, b, circle_b, 1) - arap = igl.ARAP(v, f, 2, b) - - v2 = arap.solve(circle_b[:, :2], v0[:, :2]) - self.assertEqual(v2.shape[0], v0.shape[0]) - self.assertTrue(v2.flags.c_contiguous) - - def test_arap3(self): - v, f = igl.read_triangle_mesh( - os.path.join(self.test_data_path, "camelhead.off")) - - # Find the open boundary - bnd = igl.boundary_loop(f) - - # Map the boundary to a circle, preserving edge proportions - bnd_uv = igl.map_vertices_to_circle(v, bnd) - - # Harmonic parametrization for the internal vertices - uv = igl.harmonic(v, f, bnd, bnd_uv, 1) - - arap = igl.ARAP(v, f, 2, np.zeros((0))) - uva = arap.solve(np.zeros((0, 0)), uv) - - def test_arap4(self): - v, f = igl.read_triangle_mesh( - os.path.join(self.test_data_path, "camelhead.off")) - b = igl.boundary_loop(f) - thetas = np.linspace(0, 2 * np.pi, len(b))[:, np.newaxis] - bc = np.concatenate([np.cos(thetas), np.sin( - thetas), np.zeros_like(thetas)], axis=1) - uv_initial_guess = igl.harmonic(v, f, b, bc, 1) - - arap = igl.ARAP(v, f, 3, b, igl.ARAP_ENERGY_TYPE_SPOKES) - uva = arap.solve(bc, uv_initial_guess) - - def test_slim(self): - v, f, _ = igl.read_off(os.path.join(self.test_data_path, "camelhead.off")) - b = igl.boundary_loop(f) - thetas = np.linspace(0, 2 * np.pi, len(b))[:, np.newaxis] - bc = np.concatenate([np.cos(thetas), np.sin( - thetas), np.zeros_like(thetas)], axis=1) - uv_initial_guess = igl.harmonic(v, f, b, bc, 1) - - slim = igl.SLIM( - v, f, uv_initial_guess[:, :2], b, bc[:, :2], igl.SLIM_ENERGY_TYPE_ARAP, 0.0) - slim.solve(1) - v2 = slim.vertices() - self.assertEqual(v2.shape[0], v.shape[0]) - self.assertTrue(v2.flags.c_contiguous) - - def test_bbw(self): - V, T, F = igl.read_mesh(os.path.join(self.test_data_path, "hand.mesh")) - C, BE, _, _, _, _ = igl.read_tgf( - os.path.join(self.test_data_path, "hand.tgf")) - - ok, b, bc = igl.boundary_conditions( - V, T, C, np.array([], dtype=T.dtype), BE, - np.array([], dtype=T.dtype), - np.array([], dtype=T.dtype)) - - self.assertTrue(b.flags.c_contiguous) - self.assertTrue(bc.flags.c_contiguous) - self.assertTrue(b.dtype == T.dtype) - self.assertTrue(bc.dtype == V.dtype) - - bbw = igl.BBW(0, 2) - W = bbw.solve(V, T, b, bc) - self.assertTrue(W.dtype == V.dtype) - self.assertTrue(W.flags.c_contiguous) - - def test_shapeup(self): - VQC, FQC, _ = igl.read_off(os.path.join( - self.test_data_path, "halftunnel.off")) - array_of_fours = np.ones((FQC.shape[0], 1), dtype="int32")*4 - - E = np.zeros((FQC.shape[0]*FQC.shape[1], 2), dtype="int32") - E[:, 0] = np.concatenate((FQC[:, 0], FQC[:, 1], FQC[:, 2], FQC[:, 3])) - E[:, 1] = np.concatenate((FQC[:, 1], FQC[:, 2], FQC[:, 3], FQC[:, 0])) - - b = np.array([0]) - wShape = np.ones((FQC.shape[0], 1)) - wSmooth = np.ones((E.shape[0], 1)) - - shapeup = igl.shapeup(VQC, array_of_fours, FQC, E, - b, wShape, wSmooth, maxIterations=3) - - bc = VQC[0, :] - func = 'regular_face_projection' - P = shapeup.solve(bc, VQC, local_projection=func) - - self.assertTrue(P.flags.c_contiguous) - self.assertTrue(P.dtype == VQC.dtype) - - def test_boundary_conditions(self): - # tested in test bbw - pass - - def test_harmonic(self): - # tested in test_slim, test_arap2, and test_arap1 - pass - - def test_harmonic_integrated(self): - Q = igl.harmonic_integrated(self.v1, self.f1, 1) - self.assertTrue(Q.dtype == self.v1.dtype) - - def test_harmonic_uniform_laplacian(self): - b = np.array([0, 10]) - bc = np.array([ - [0, 0], [10., 10.]]) - W = igl.harmonic_uniform_laplacian(self.f1, b, bc, 1) - - self.assertTrue(W.dtype == self.v1.dtype) - self.assertTrue(W.flags.c_contiguous) - - def test_harmonic_integrated_from_laplacian_and_mass(self): - l = igl.cotmatrix(self.v1, self.f1) - m = igl.massmatrix(self.v1, self.f1, igl.MASSMATRIX_TYPE_VORONOI) - - Q = igl.harmonic_integrated_from_laplacian_and_mass(l, m, 1) - self.assertTrue(Q.dtype == self.v1.dtype) - - # deal with igl::PerEdgeNormalsWeightingType - # def test_per_edge_normals(self): - # fn = np.random.rand(self.f1.shape[0], 3) - # n, e, emap = igl.per_edge_normals(self.v1, self.f1, 0, fn) - - def test_lscm(self): - b = np.array([1, 2, 3]) - # print(b.dtype) - bc = np.array([ - [1., 0], - [1, 1], - [2, 2]]) - success, uv = igl.lscm(self.v1, self.f1, b, bc) - self.assertEqual(type(success), bool) - self.assertEqual(uv.dtype, self.v1.dtype) - self.assertEqual(uv.shape, (self.v1.shape[0], 2)) - self.assertTrue(uv.flags.c_contiguous) - - def test_is_irregular_vertex(self): - is_i = igl.is_irregular_vertex(self.f1) - self.assertEqual(type(is_i[0]), bool) - - def test_harmonic(self): - l = igl.cotmatrix(self.v1, self.f1) - m = igl.massmatrix(self.v1, self.f1, igl.MASSMATRIX_TYPE_VORONOI) - b = np.array([1, 2, 10, 7]) - bc = self.v1[b, :] - k = 1 - w = igl.harmonic_from_laplacian_and_mass(l, m, b, bc, k) - self.assertTrue(w.flags.c_contiguous) - - def test_harmonic_from_laplacian_and_mass(self): - # tested in test_harmonic - pass - - # this test is creating a matrix which is not spd and an assertion fails... - # def test_bijective_composite_harmonic_mapping(self): - # v, f = igl.read_triangle_mesh(os.path.join(self.test_data_path, "circle.obj")) - # f = np.array(f[:, [0, 2, 1]]) - # v = np.array(v[:, 0:2]) - # b = np.array([943, 1356] - # bc = np.array([ - # [0, 0], - # [10., 10.] - # ]) - - # ok, u = igl.bijective_composite_harmonic_mapping(v, f, b, bc) - - # self.assertTrue(u.flags.c_contiguous) - # self.assertTrue(u.dtype == v1.dtype) - # self.assertTrue(type(ok) == bool) - - def test_exact_geodesic(self): - vs = np.array([0]) - vt = np.arange(self.v1.shape[0]) - - d = igl.exact_geodesic(self.v1, self.f1, vs, vt) - self.assertEqual(d.dtype, self.v1.dtype) - self.assertTrue(d.flags.c_contiguous) - - def test_heat_geodesic(self): - vs = np.array([0]) - d = igl.heat_geodesic(self.v1, self.f1, 1.0, vs) - # TODO: Should this not return distances for all sources? - self.assertEqual(d.dtype, self.v1.dtype) - self.assertTrue(d.flags.c_contiguous) - - def test_cut_mesh(self): - cuts = np.random.randint(0, 2, size=self.f1.shape, dtype=self.f1.dtype) - vcut, fcut = igl.cut_mesh(self.v1, self.f1, cuts) - - self.assertTrue(vcut.flags.c_contiguous) - self.assertTrue(fcut.flags.c_contiguous) - - self.assertTrue(vcut.dtype == self.v1.dtype) - self.assertTrue(vcut.shape[1] == 3) - self.assertTrue(vcut.shape[0] >= self.v1.shape[0]) - self.assertTrue(fcut.dtype == self.f1.dtype) - self.assertTrue(fcut.shape[1] == 3) - self.assertTrue(fcut.shape[0] == self.f1.shape[0]) - - def test_cut_mesh_from_singularities(self): - mismatch = np.random.randint( - 0, 10, size=self.f1.shape, dtype=self.f1.dtype) - seams = igl.cut_mesh_from_singularities(self.v1, self.f1, mismatch) - self.assertEqual(seams.shape, (self.f1.shape[0], 3)) - self.assertEqual(seams.dtype, bool) - self.assertTrue(seams.flags.c_contiguous) - - # def test_BBW(self): - # BBW = igl.BBW() - # w = BBW.solve(self.v1, self.f1, self.bc, self.b0) - - def test_loop_subdivision_matrix(self): - S, nf = igl.loop_subdivision_matrix(len(self.v1), self.f1) - self.assertEqual(nf.dtype, self.f1.dtype) - self.assertEqual(nf.shape[1], self.f1.shape[1]) - self.assertTrue(nf.flags.c_contiguous) - - def test_loop(self): - nv, nf = igl.loop(self.v1, self.f1) - self.assertEqual(nv.dtype, self.v1.dtype) - self.assertEqual(nv.shape[1], self.v1.shape[1]) - self.assertEqual(nf.dtype, self.f1.dtype) - self.assertEqual(nf.shape[1], self.f1.shape[1]) - self.assertTrue(nv.flags.c_contiguous) - self.assertTrue(nf.flags.c_contiguous) - - def test_segments_intersect(self): - p = np.float32([0, 0, 0]) - r = np.float32([1, 2, 3]) - q = np.float32([5, 5, 5]) - s = np.float32([3, 2, 1]) - is_intersect, t, u, eps = igl.segments_intersect(p, r, q, s) - self.assertEqual(type(is_intersect), bool) - self.assertEqual(type(t), float) - self.assertEqual(type(u), float) - - def test_hessian_energy(self): - q = igl.hessian_energy(self.v1, self.f1) - self.assertEqual(q.dtype, self.v1.dtype) - self.assertEqual(q.shape, (self.v1.shape[0], self.v1.shape[0])) - - def test_signed_distance(self): - min_v = np.min(self.v1, axis=0) - max_v = np.max(self.v1, axis=0) - n = 16 - g = np.mgrid[min_v[0]:max_v[0]:complex( - n), min_v[1]:max_v[1]:complex(n), min_v[2]:max_v[2]:complex(n)] - p = np.vstack(list(map(np.ravel, g))).T - # test default type - s, i, c = igl.signed_distance(p, self.v1, self.f1) - - self.assertEqual(s.shape[0], p.shape[0]) - self.assertEqual(i.shape[0], p.shape[0]) - self.assertEqual(c.shape, p.shape) - - signTypes = [ - igl.SIGNED_DISTANCE_TYPE_PSEUDONORMAL, - igl.SIGNED_DISTANCE_TYPE_WINDING_NUMBER, - igl.SIGNED_DISTANCE_TYPE_DEFAULT, - igl.SIGNED_DISTANCE_TYPE_UNSIGNED, - igl.SIGNED_DISTANCE_TYPE_FAST_WINDING_NUMBER - ] - - # test each specific type. - for signType in signTypes: - s, i, c = igl.signed_distance( - p, self.v1, self.f1, sign_type=signType) - self.assertEqual(s.shape[0], p.shape[0]) - self.assertEqual(i.shape[0], p.shape[0]) - self.assertEqual(c.shape, p.shape) - self.assertTrue(s.flags.c_contiguous) - self.assertTrue(i.flags.c_contiguous) - self.assertTrue(c.flags.c_contiguous) - - self.assertTrue(s.dtype == self.v1.dtype) - self.assertTrue(c.dtype == self.v1.dtype) - self.assertTrue(i.dtype == self.f1.dtype) - - # test return_normals, default changes to psuedonormal - s, i, c, n = igl.signed_distance( - p, self.v1, self.f1, return_normals=True) - self.assertEqual(n.shape, p.shape) - self.assertTrue(n.flags.c_contiguous) - self.assertTrue(n.dtype == self.v1.dtype) - - # ensure error raised when trying param other than pseudonormal for normals - with self.assertRaises(ValueError): - igl.signed_distance( - p, self.v1, self.f1, sign_type=igl.SIGNED_DISTANCE_TYPE_WINDING_NUMBER, return_normals=True) - - # ensure error raise when invalid param given - with self.assertRaises(ValueError): - igl.signed_distance(p, self.v1, self.f1, sign_type=345) - - def test_offset_surface(self): - sv, sf, gv, side, so = igl.offset_surface( - self.v1, self.f1, 1, 10, igl.SIGNED_DISTANCE_TYPE_DEFAULT) - self.assertTrue(sv.dtype == self.v1.dtype) - self.assertTrue(sf.dtype == self.f1.dtype) - - self.assertTrue(gv.dtype == self.v1.dtype) - self.assertTrue(side.dtype == self.f1.dtype) - self.assertTrue(so.dtype == self.f1.dtype) - - self.assertTrue(sv.shape[1] == 3) - self.assertTrue(sf.shape[1] == 3) - self.assertTrue(gv.shape[1] == 3) - - self.assertTrue(gv.shape[0] == so.shape[0]) - - def test_biharmonic_coordinates(self): - w = igl.biharmonic_coordinates(self.v1, self.f1, [[0]]) - - self.assertTrue(w.flags.c_contiguous) - self.assertTrue(w.dtype == self.v1.dtype) - - # std::function in python - # def test_flip_avoid_line_search(self): - # pass - - def test_min_quad_with_fixed(self): - v, f = igl.read_triangle_mesh( - os.path.join(self.test_data_path, "cheburashka.off")) - - # Two fixed points: Left hand, left foot should have values 1 and -1 - b = np.array([4331, 5957]) - bc = np.array([1., -1.]) - B = np.zeros((v.shape[0], 1)) - - # Construct Laplacian and mass matrix - L = igl.cotmatrix(v, f) - M = igl.massmatrix(v, f, igl.MASSMATRIX_TYPE_VORONOI) - Minv = sp.sparse.diags(1 / M.diagonal()) - - # Bi-Laplacian - Q = L.dot(Minv.dot(L)) - - # Solve with only equality constraints - Aeq = sp.sparse.csc_matrix((0, 0)) - Beq = np.array([]) - ok, z1 = igl.min_quad_with_fixed(Q, B, b, bc, Aeq, Beq, True) - self.assertTrue(z1.flags.c_contiguous) - self.assertTrue(z1.dtype == B.dtype) - self.assertTrue(ok) - - def test_marching_tets(self): - TV = np.array([ - [0., 0., 0.], - [1., 0., 0.], - [0., 1., 0.], - [0., 0., 1.], - [0., 0., -1.] - ]) - TT = np.array([[0, 1, 2, 3], [0, 1, 2, 4]]) - S = np.array([0., 1., 1., 1., 1.]) - - v, f, j, bc = igl.marching_tets(TV, TT, S, 0.5) - - self.assertTrue(v.flags.c_contiguous) - self.assertTrue(f.flags.c_contiguous) - self.assertTrue(j.flags.c_contiguous) - - self.assertTrue(v.dtype == TV.dtype) - self.assertTrue(v.shape[0] >= 3) - self.assertTrue(v.shape[1] == 3) - - self.assertTrue(f.dtype == TT.dtype) - self.assertTrue(f.shape[1] == 3) - - self.assertTrue(j.dtype == TT.dtype) - self.assertTrue(j.shape[0] == f.shape[0]) - - self.assertTrue(bc.dtype == TV.dtype) - self.assertTrue(bc.shape[0] == v.shape[0]) - self.assertTrue(bc.shape[1] == TV.shape[0]) - - def test_read_tgf(self): - filename = os.path.join(self.test_data_path, "hand.tgf") - tf = np.array([1.]) - ti = np.array([1]) - - V, E, P, BE, CE, PE = igl.read_tgf(filename) - - self.assertTrue(V.flags.c_contiguous) - self.assertTrue(E.flags.c_contiguous) - self.assertTrue(P.flags.c_contiguous) - self.assertTrue(BE.flags.c_contiguous) - self.assertTrue(CE.flags.c_contiguous) - self.assertTrue(PE.flags.c_contiguous) - - self.assertTrue(V.dtype == tf.dtype) - self.assertTrue(E.dtype == ti.dtype) - self.assertTrue(P.dtype == ti.dtype) - self.assertTrue(BE.dtype == ti.dtype) - self.assertTrue(CE.dtype == ti.dtype) - self.assertTrue(PE.dtype == ti.dtype) - - def test_deform_skeleton(self): - hand_file = os.path.join(self.test_data_path, "hand.tgf") - C, BE, _, _, _, _ = igl.read_tgf(hand_file) - - T = np.zeros((BE.shape[0]*4, 3)) - I = np.eye(3) - for i in range(0, T.shape[0], 4): - T[i:i+3, :] = I - - CT, BET = igl.deform_skeleton(C, BE, T) - self.assertTrue(CT.flags.c_contiguous) - self.assertTrue(BET.flags.c_contiguous) - - self.assertTrue(CT.dtype == C.dtype) - self.assertTrue(BET.dtype == BE.dtype) - - def test_edge_lengths(self): - l = igl.edge_lengths(self.v1, self.f1) - - self.assertTrue(l.flags.c_contiguous) - self.assertTrue(l.dtype == self.v1.dtype) - self.assertTrue(l.shape == self.f1.shape) - - def test_planarize_quad_mesh(self): - v, f, _ = igl.read_off(os.path.join( - self.test_data_path, "inspired_mesh_quads_Conjugate.off")) - out = igl.planarize_quad_mesh(v, f, 1, 1e-2) - - self.assertTrue(out.dtype == v.dtype) - self.assertTrue(out.flags.c_contiguous) - - def test_local_basis(self): - b1, b2, b3 = igl.local_basis(self.v1, self.f1) - self.assertTrue(b1.flags.c_contiguous) - self.assertTrue(b2.flags.c_contiguous) - self.assertTrue(b3.flags.c_contiguous) - - self.assertTrue(b1.dtype == self.v1.dtype) - self.assertTrue(b2.dtype == self.v1.dtype) - self.assertTrue(b3.dtype == self.v1.dtype) - - self.assertTrue(b1.shape == self.f1.shape) - self.assertTrue(b2.shape == self.f1.shape) - self.assertTrue(b3.shape == self.f1.shape) - - def test_cross_fields(self): - V, F = igl.read_triangle_mesh( - os.path.join(self.test_data_path, "3holes.off")) - - B = igl.barycenter(V, F) - b = np.array([0]) - bc = np.array([[1., 0., 0.]]) - - # if platform.system() == "Windows": - X1 = np.load(os.path.join(self.test_data_path, "X1.npy")) - S = np.load(os.path.join(self.test_data_path, "S.npy")) - - self.assertTrue(X1.flags.c_contiguous) - self.assertTrue(S.flags.c_contiguous) - self.assertTrue(X1.dtype == V.dtype) - self.assertTrue(S.dtype == V.dtype) - - B1, B2, B3 = igl.local_basis(V, F) - - X2 = igl.rotate_vectors(X1, np.array([math.pi/2]), B1, B2) - self.assertTrue(X2.flags.c_contiguous) - self.assertTrue(X2.dtype == V.dtype) - - BIS1, BIS2 = igl.compute_frame_field_bisectors_no_basis(V, F, X1, X2) - self.assertTrue(BIS1.flags.c_contiguous) - self.assertTrue(BIS1.dtype == V.dtype) - self.assertTrue(BIS2.flags.c_contiguous) - self.assertTrue(BIS2.dtype == V.dtype) - - BIS1t, BIS2t = igl.compute_frame_field_bisectors( - V, F, X1, X2, BIS1, BIS2) - self.assertTrue(BIS1t.flags.c_contiguous) - self.assertTrue(BIS1t.dtype == V.dtype) - self.assertTrue(BIS2t.flags.c_contiguous) - self.assertTrue(BIS2t.dtype == V.dtype) - - BIS1_combed, BIS2_combed = igl.comb_cross_field(V, F, BIS1, BIS2) - self.assertTrue(BIS1_combed.flags.c_contiguous) - self.assertTrue(BIS1_combed.dtype == V.dtype) - self.assertTrue(BIS2_combed.flags.c_contiguous) - self.assertTrue(BIS2_combed.dtype == V.dtype) - - BI_combed = igl.comb_line_field(V, F, BIS1) - self.assertTrue(BI_combed.flags.c_contiguous) - self.assertTrue(BI_combed.dtype == V.dtype) - - MMatch = igl.cross_field_mismatch(V, F, BIS1_combed, BIS2_combed, True) - self.assertTrue(MMatch.flags.c_contiguous) - self.assertTrue(MMatch.dtype == F.dtype) - - isSingularity, singularityIndex = igl.find_cross_field_singularities( - V, F, MMatch) - self.assertTrue(isSingularity.flags.c_contiguous) - self.assertTrue(isSingularity.dtype == F.dtype) - self.assertTrue(singularityIndex.flags.c_contiguous) - self.assertTrue(singularityIndex.dtype == F.dtype) - - isSingularityt, singularityIndext = igl.find_cross_field_singularities_from_field( - V, F, BIS1_combed, BIS2_combed) - self.assertTrue(isSingularityt.flags.c_contiguous) - self.assertTrue(isSingularityt.dtype == F.dtype) - self.assertTrue(singularityIndext.flags.c_contiguous) - self.assertTrue(singularityIndext.dtype == F.dtype) - - X1_combed, X2_combed = igl.comb_frame_field( - V, F, X1, X2, BIS1_combed, BIS2_combed) - self.assertTrue(X1_combed.flags.c_contiguous) - self.assertTrue(X1_combed.dtype == V.dtype) - self.assertTrue(X2_combed.flags.c_contiguous) - self.assertTrue(X2_combed.dtype == V.dtype) - - def test_comb_cross_field(self): - #tested in test_cross_fields - pass - - def test_comb_frame_field(self): - #tested in test_cross_fields - pass - - def test_comb_line_field(self): - #tested in test_cross_fields - pass - - def test_compute_frame_field_bisectors(self): - #tested in test_cross_fields - pass - - def test_compute_frame_field_bisectors_no_basis(self): - #tested in test_cross_fields - pass - - def test_cross_field_mismatch(self): - #tested in test_cross_fields - pass - - def test_find_cross_field_singularities(self): - #tested in test_cross_fields - pass - - def test_find_cross_field_singularities_from_field(self): - #tested in test_cross_fields - pass - - def test_rotate_vectors(self): - #tested in test_cross_fields - pass - - def test_directed_edge_orientations(self): - v = np.array([[0.0, 0.0, 0.], [1.0, 0.0, 0.], - [1.0, 1.0, 0.], [0.0, 1.0, 0.]]) - e = np.array([[0, 1], [1, 2], [2, 3], [3, 0]]) - - q = igl.directed_edge_orientations(v, e) - self.assertTrue(q.flags.c_contiguous) - self.assertTrue(q.dtype == v.dtype) - self.assertTrue(q.shape[0] == e.shape[0]) - self.assertTrue(q.shape[1] == 4) - - def test_lbs_matrix(self): - V, _ = igl.read_triangle_mesh(os.path.join(self.test_data_path, "arm.obj")) - W = igl.read_dmat(os.path.join(self.test_data_path, "arm-weights.dmat")) - M = igl.lbs_matrix(V, W) - self.assertTrue(M.flags.c_contiguous) - self.assertTrue(M.dtype == V.dtype) - self.assertTrue(M.shape[0] == V.shape[0]) - self.assertTrue(M.shape[1] == W.shape[1]*4) - - def test_direct_delta_mush(self): - V, F = igl.read_triangle_mesh(os.path.join(self.test_data_path, "arm.obj")) - W = igl.read_dmat(os.path.join(self.test_data_path, "arm-weights.dmat")) - _, BE, _, _, _, _ = igl.read_tgf(os.path.join(self.test_data_path, "arm.tgf")) - - # Use same values as tutorial - # https://github.com/libigl/libigl/blob/main/tutorial/408_DirectDeltaMush/main.cpp - p = 20 - l = 3 - k = 1 - a = 0.8 - omega = igl.direct_delta_mush_precomputation(V, F,W, p, l, k, a) - - self.assertTrue(omega.shape[0] == V.shape[0]) - self.assertTrue(omega.shape[1] == BE.shape[0] * 10) - self.assertTrue(omega.dtype == np.double) - - T = np.zeros((BE.shape[0]*4, 3)) - I = np.eye(3) - for i in range(0, T.shape[0], 4): - T[i:i+3, :] = I - - U = igl.direct_delta_mush(V, T, omega) - - self.assertTrue(U.shape[0] == V.shape[0]) - self.assertTrue(U.shape[1] == 3) - self.assertTrue(U.dtype == np.double) - self.assertFalse(np.isnan(U).any()) - - def test_direct_delta_mush_precomputation(self): - # covered in test_direct_delta_mush - pass - - - def test_point_mesh_squared_distance(self): - dist, i, c = igl.point_mesh_squared_distance( - np.array([0., 0., 0.]), self.v1, self.f1) - - self.assertTrue(dist.flags.c_contiguous) - self.assertTrue(i.flags.c_contiguous) - self.assertTrue(c.flags.c_contiguous) - - self.assertTrue(dist.dtype == self.v1.dtype) - self.assertTrue(i.dtype == self.f1.dtype) - self.assertTrue(c.dtype == self.v1.dtype) - - self.assertTrue(dist.shape == ()) - self.assertTrue(i.shape == ()) - self.assertTrue(c.shape == (3,)) - ######################### - dist, i, c = igl.point_mesh_squared_distance( - np.array([[0., 0., 0.], [0., 0., 0.]]), self.v1, self.f1) - - self.assertTrue(dist.flags.c_contiguous) - self.assertTrue(i.flags.c_contiguous) - self.assertTrue(c.flags.c_contiguous) - - self.assertTrue(dist.dtype == self.v1.dtype) - self.assertTrue(i.dtype == self.f1.dtype) - self.assertTrue(c.dtype == self.v1.dtype) - - self.assertTrue(dist.shape[0] == 2) - self.assertTrue(i.shape[0] == 2) - self.assertTrue(c.shape[0] == 2) - self.assertTrue(c.shape[1] == 3) - - def test_dual_quat_skinning(self): - V, _ = igl.read_triangle_mesh(os.path.join(self.test_data_path, "arm.obj")) - U = np.copy(V) - W = igl.read_dmat(os.path.join(self.test_data_path, "arm-weights.dmat")) - C, BE, _, _, _, _ = igl.read_tgf( - os.path.join(self.test_data_path, "arm.tgf")) - P = igl.directed_edge_parents(BE) - rest_pose = igl.directed_edge_orientations(C, BE) - - M = igl.lbs_matrix(V, W) - - vQ, vT = igl.forward_kinematics(C, BE, P, rest_pose, np.array([])) - self.assertTrue(vQ.flags.c_contiguous) - self.assertTrue(vT.flags.c_contiguous) - self.assertTrue(vQ.shape[1] == 4) - self.assertTrue(vT.shape[1] == 3) - self.assertTrue(vQ.shape[0] == BE.shape[0]) - self.assertTrue(vT.shape[0] == BE.shape[0]) - - U = igl.dqs(V, W, vQ, vT) - self.assertTrue(U.flags.c_contiguous) - self.assertTrue(U.shape == V.shape) - - def test_dqs(self): - # tested in test_dual_quat_skinning - pass - - def test_forward_kinematics(self): - # tested in test_dual_quat_skinning - pass - - def test_active_set(self): - V, F = igl.read_triangle_mesh( - os.path.join(self.test_data_path, "cheburashka.off")) - - b = np.array([2556]) - bc = np.array([1.0]) - - L = igl.cotmatrix(V, F) - M = igl.massmatrix(V, F, igl.MASSMATRIX_TYPE_VORONOI) - Minv = sp.sparse.csr_matrix(M) - Minv.setdiag(1./M.diagonal()) - Q = L.T * (Minv * L) - B = np.zeros((V.shape[0], 1)) - lx = np.zeros((V.shape[0], 1)) - ux = np.ones((V.shape[0], 1)) - - Beq = np.array([0.08]) - Aeq = sp.sparse.csr_matrix(M.diagonal()) - - Aieq = sp.sparse.csr_matrix((0, 0)) - Bieq = np.zeros((0, 0)) - - status, Z = igl.active_set( - Q, B, b, bc, Aeq, Beq, Aieq, Bieq, lx, ux, max_iter=5) - - self.assertTrue(status != 2) - self.assertTrue(Z.shape[0] == Q.shape[0]) - self.assertTrue(Z.dtype == V.dtype) - - def test_face_occurrences(self): - c = igl.face_occurrences(self.f) - self.assertTrue(c.flags.c_contiguous) - self.assertTrue(c.dtype == self.f.dtype) - self.assertTrue(c.shape[0] == self.f.shape[0]) - - def test_false_barycentric_subdivision(self): - vd, fd = igl.false_barycentric_subdivision(self.v, self.f) - self.assertTrue(vd.flags.c_contiguous) - self.assertTrue(fd.flags.c_contiguous) - - self.assertTrue(vd.dtype == self.v.dtype) - self.assertTrue(fd.dtype == self.f.dtype) - - self.assertTrue(vd.shape[0] == self.v.shape[0]+self.f.shape[0]) - self.assertTrue(vd.shape[1] == self.v.shape[1]) - self.assertTrue(fd.shape[1] == self.f.shape[1]) - self.assertTrue(fd.shape[0] == self.f.shape[0]*3) - - def test_flipped_triangles(self): - flipped = igl.flipped_triangles(self.v[:, :2], self.f) - self.assertTrue(flipped.flags.c_contiguous) - self.assertTrue(flipped.dtype == self.f.dtype) - self.assertTrue(len(flipped.shape) == 1) - - def test_inradius(self): - r = igl.inradius(self.v, self.f) - self.assertTrue(r.flags.c_contiguous) - self.assertTrue(r.dtype == self.v.dtype) - self.assertTrue(r.shape[0] == self.f.shape[0]) - self.assertTrue(len(r.shape) == 1) - - def test_is_border_vertex(self): - res = igl.is_border_vertex(self.v, self.f) - - self.assertTrue(len(res) == self.v.shape[0]) - self.assertTrue(type(res) == list) - self.assertTrue(type(res[0]) == bool) - - def test_extract_manifold_patches(self): - n, p = igl.extract_manifold_patches(self.f2) - self.assertTrue(p.flags.c_contiguous) - self.assertTrue(p.dtype == self.f2.dtype) - self.assertTrue(len(p.shape) == 1) - self.assertTrue(p.shape[0] == self.f2.shape[0]) - self.assertTrue(type(n) == int) - self.assertTrue(n == 1) - - def test_faces_first(self): - RV, RF, IM = igl.faces_first(self.v, self.f) - self.assertTrue(RV.flags.c_contiguous) - self.assertTrue(RF.flags.c_contiguous) - self.assertTrue(IM.flags.c_contiguous) - - self.assertTrue(RV.dtype == self.v.dtype) - self.assertTrue(RF.dtype == self.f.dtype) - self.assertTrue(IM.dtype == self.f.dtype) - - self.assertTrue(RV.shape[0] == self.v.shape[0]) - self.assertTrue(RF.shape[0] == self.f.shape[0]) - self.assertTrue(IM.shape[0] == self.v.shape[0]) - self.assertTrue(len(IM.shape) == 1) - - def test_is_intrinsic_delaunay(self): - # Tested above - pass - - def test_intrinsic_delaunay_triangulation(self): - el = igl.edge_lengths(self.v1, self.f1) - l, f = igl.intrinsic_delaunay_triangulation(el, self.f1) - # print(l, f) - self.assertTrue(f.flags.c_contiguous) - self.assertTrue(f.dtype == self.f1.dtype) - self.assertTrue(f.shape[1] == 3) - - def test_intrinsic_delaunay_triangulation_edges(self): - el = igl.edge_lengths(self.v1, self.f1) - l, f, e, u_e, emap, ue2e = igl.intrinsic_delaunay_triangulation_edges( - el, self.f1) - self.assertTrue(f.flags.c_contiguous) - self.assertTrue(f.dtype == self.f.dtype) - self.assertTrue(f.shape[1] == 3) - - def test_edges_to_path(self): - e = igl.edges(self.f1) - e = e[:1, :] - i, j, k = igl.edges_to_path(e) - - self.assertTrue(i.flags.c_contiguous) - self.assertTrue(j.flags.c_contiguous) - self.assertTrue(k.flags.c_contiguous) - - self.assertTrue(i.dtype == e.dtype) - self.assertTrue(j.dtype == e.dtype) - self.assertTrue(k.dtype == e.dtype) - - def test_data_path_to_edges(self): - v_indices = np.array(range(20)) - e1 = igl.path_to_edges(v_indices, False) - e2 = igl.path_to_edges(v_indices, True) - r2 = np.vstack([v_indices, np.array(range(1, 21))]).T - r2[19, 1] = 0 - self.assertTrue(np.allclose(e2, r2)) - self.assertTrue(e1.flags.c_contiguous) - self.assertTrue(e2.flags.c_contiguous) - self.assertTrue(e1.dtype == v_indices.dtype) - self.assertTrue(e2.dtype == v_indices.dtype) - - def test_exterior_edges(self): - e = igl.exterior_edges(self.f1) - - self.assertTrue(e.flags.c_contiguous) - self.assertTrue(e.dtype == self.f1.dtype) - self.assertTrue(e.shape[1] == 2) - - def test_normal_derivative(self): - d = igl.normal_derivative(self.v1, self.f1) - - self.assertTrue(d.shape == (self.f1.shape[0]*3, self.v1.shape[0])) - self.assertTrue(d.dtype == self.v1.dtype) - self.assertTrue(type(d) == csc.csc_matrix) - - def test_orient_outward(self): - v,f = igl.read_triangle_mesh(os.path.join(self.test_data_path, "truck.obj")) - c, _ = igl.orientable_patches(f) - ff, i = igl.orient_outward(v, f, c) - - self.assertTrue(ff.flags.c_contiguous) - self.assertTrue(i.flags.c_contiguous) - self.assertTrue(ff.dtype == f.dtype) - self.assertTrue(i.dtype == f.dtype) - self.assertTrue(ff.shape[0] == f.shape[0]) - self.assertTrue(ff.shape[1] == 3) - self.assertTrue(len(i.shape) == 1) - self.assertTrue(i.shape[0] == np.max(c)+1) - - def test_solid_angle(self): - v0 = np.array(self.v1[self.f1[0, 0], :]) - v1 = np.array(self.v1[self.f1[0, 1], :]) - v2 = np.array(self.v1[self.f1[0, 2], :]) - p = np.array(self.v1[10, :]) - d = igl.solid_angle(v0, v1, v2, p) - - def test_simplify_polyhedron(self): - v, f, j = igl.simplify_polyhedron(self.v1, self.f1) - - self.assertTrue(v.flags.c_contiguous) - self.assertTrue(f.flags.c_contiguous) - self.assertTrue(j.flags.c_contiguous) - - self.assertTrue(v.dtype == self.v1.dtype) - self.assertTrue(f.dtype == self.f1.dtype) - self.assertTrue(j.dtype == self.f1.dtype) - - self.assertTrue(v.shape[1] == self.v1.shape[1]) - self.assertTrue(f.shape[1] == self.f1.shape[1]) - self.assertTrue(len(j.shape) == 1) - - def test_unique_simplices(self): - fa, ia, ic = igl.unique_simplices(self.f1) - - self.assertTrue(fa.flags.c_contiguous) - self.assertTrue(ia.flags.c_contiguous) - self.assertTrue(ic.flags.c_contiguous) - - self.assertTrue(fa.dtype == self.f1.dtype) - self.assertTrue(ia.dtype == self.f1.dtype) - self.assertTrue(ic.dtype == self.f1.dtype) - - self.assertTrue(fa.shape[1] == self.f1.shape[1]) - self.assertTrue(ia.shape[0] == ia.shape[0]) - self.assertTrue(ic.shape[0] == self.f1.shape[0]) - self.assertTrue(len(ic.shape) == 1) - self.assertTrue(len(ia.shape) == 1) - - def test_swept_volume_bounding_box(self): - def func(i, t): return (1-t) * \ - self.v1[self.f1[0, i], :] + t*self.v1[self.f1[1, i], :] - - bmin, bmax = igl.swept_volume_bounding_box(3, func, 3) - self.assertTrue(bmin.flags.c_contiguous) - self.assertTrue(bmax.flags.c_contiguous) - - self.assertTrue(bmin.dtype == self.v1.dtype) - self.assertTrue(bmax.dtype == self.v1.dtype) - - self.assertTrue(bmin.shape == (3,)) - self.assertTrue(bmax.shape == (3,)) - - def test_hessian(self): - H = igl.hessian(self.v1, self.f1) - self.assertTrue(H.dtype == self.v1.dtype) - - def test_snap_points(self): - I, minD, VI = igl.snap_points(self.v1, self.v) - - self.assertTrue(I.flags.c_contiguous) - self.assertTrue(minD.flags.c_contiguous) - self.assertTrue(VI.flags.c_contiguous) - - self.assertTrue(I.dtype == self.f1.dtype) - self.assertTrue(minD.dtype == self.v1.dtype) - self.assertTrue(VI.dtype == self.v1.dtype) - - self.assertTrue(I.shape == (self.v1.shape[0], )) - self.assertTrue(I.shape == (self.v1.shape[0], )) - self.assertTrue(VI.shape == (self.v1.shape[0], 3)) - - def test_ray_box_intersect(self): - bmin = np.array([0., 0., 0.]) - bmax = np.array([1., 1., 1.]) - - source = np.array([-1., -1, -1]) - dire = np.array([1., 1., 1.]) - - hit, tmin, tmax = igl.ray_box_intersect( - source, dire, bmin, bmax, 0, 100) - - self.assertTrue(hit) - self.assertTrue(tmin > 0) - self.assertTrue(tmax < 100) - - def test_ray_mesh_intersect(self): - source = np.array([-1., -1, -1]) - dire = self.v1[self.f1[0, :], :].mean(axis=0) - source - - hits = igl.ray_mesh_intersect(source, dire, self.v1, self.f1) - - self.assertTrue(len(hits) > 0) - self.assertTrue(len(hits[0]) == 5) - - def test_ray_sphere_intersect(self): - center = np.array([1., 1., 1.]) - - source = np.array([-1., -1, -1]) - dire = np.array([1., 1., 1.]) - - hits, tmin, tmax = igl.ray_sphere_intersect( - source, dire, center, 1) - - self.assertTrue(hits == 2) - - def test_volume(self): - v, t, f = igl.read_mesh(os.path.join( - self.test_data_path, "octopus-low.mesh")) - vol = igl.volume(v, t) - self.assertTrue(vol.flags.c_contiguous) - self.assertTrue(vol.dtype == v.dtype) - self.assertTrue(len(vol.shape) == 1) - self.assertTrue(vol.shape[0] == t.shape[0]) - - a = v[t[:, 0], :] - b = v[t[:, 1], :] - c = v[t[:, 2], :] - d = v[t[:, 3], :] - - vol = igl.volume_from_vertices(a, b, c, d) - self.assertTrue(vol.flags.c_contiguous) - self.assertTrue(vol.dtype == v.dtype) - self.assertTrue(len(vol.shape) == 1) - self.assertTrue(vol.shape[0] == a.shape[0]) - - vol = igl.volume_single(a[0, :], b[0, :], c[0, :], d[0, :]) - - l = igl.edge_lengths(v, t) - vol = igl.volume_from_edges(l) - self.assertTrue(vol.flags.c_contiguous) - self.assertTrue(vol.dtype == v.dtype) - self.assertTrue(len(vol.shape) == 1) - self.assertTrue(vol.shape[0] == t.shape[0]) - - def test_volume_from_edges(self): - #tested in volume - pass - - def test_volume_from_vertices(self): - #tested in volume - pass - - def test_volume_single(self): - #tested in volume - pass - - def test_mvc(self): - pts = np.random.rand(10, 2) - poly = np.array([[0., 0.], [1., 1.], [2., 2.], [0., 3.]]) - w = igl.mvc(pts, poly) - - self.assertTrue(w.flags.c_contiguous) - self.assertTrue(w.dtype == pts.dtype) - self.assertTrue(w.shape[0] == pts.shape[0]) - self.assertTrue(w.shape[1] == poly.shape[0]) - - def test_all_pairs_distances(self): - u = np.random.rand(10, 2) - v = np.random.rand(5, 2) - - d = igl.all_pairs_distances(u, v, True) - self.assertTrue(d.flags.c_contiguous) - self.assertTrue(d.dtype == u.dtype) - self.assertTrue(d.shape[0] == u.shape[0]) - self.assertTrue(d.shape[1] == v.shape[0]) - - def test_line_segment_in_rectangle(self): - s = np.array([0., 0.]) - d = np.array([10., 10.]) - - A = np.array([1., 1.]) - B = np.array([2., 2.]) - - inter = igl.line_segment_in_rectangle(s, d, A, B) - self.assertTrue(inter) - - def test_look_at(self): - eye = np.random.rand(3, 1) - center = np.random.rand(3, 1) - up = np.random.rand(3, 1) - - R = igl.look_at(eye, center, up) - - self.assertTrue(R.flags.c_contiguous) - self.assertTrue(R.dtype == eye.dtype) - self.assertTrue(R.shape[0] == 4) - self.assertTrue(R.shape[1] == 4) - - def test_outer_vertex(self): - v, a = igl.outer_vertex( - self.v1, self.f1, np.zeros((1, 1), dtype=self.f1.dtype)) - - self.assertTrue(a.flags.c_contiguous) - self.assertTrue(a.dtype == self.f1.dtype) - - def test_outer_edge(self): - v1, v2, a = igl.outer_edge( - self.v1, self.f1, np.zeros((1, 1), dtype=self.f1.dtype)) - - self.assertTrue(a.flags.c_contiguous) - self.assertTrue(a.dtype == self.f1.dtype) - - def test_outer_facet(self): - n = igl.per_face_normals(self.v1, self.f1, self.v1) - index, flipped = igl.outer_facet( - self.v1, self.f1, n, np.zeros((1, 1), dtype=self.f1.dtype)) - - def test_partition(self): - g, s, d = igl.partition(self.v1, 3) - - self.assertTrue(g.flags.c_contiguous) - self.assertTrue(s.flags.c_contiguous) - self.assertTrue(d.flags.c_contiguous) - - self.assertTrue(g.dtype == self.f1.dtype) - self.assertTrue(s.dtype == self.f1.dtype) - self.assertTrue(d.dtype == self.v1.dtype) - - self.assertTrue(g.shape[0] == self.v1.shape[0]) - self.assertTrue(s.shape[0] == 3) - self.assertTrue(d.shape[0] == self.v1.shape[0]) - - self.assertTrue(len(g.shape) == 1) - self.assertTrue(len(s.shape) == 1) - self.assertTrue(len(d.shape) == 1) - - def test_point_in_circle(self): - inside = igl.point_in_circle(1, 2, 3, 4, 1) - - def test_point_simplex_squared_distance(self): - dist, pt, bary = igl.point_simplex_squared_distance( - np.array([3., 3, 1.]), self.v1, self.f1, 0) - - self.assertTrue(pt.flags.c_contiguous) - self.assertTrue(bary.flags.c_contiguous) - - self.assertTrue(pt.dtype == self.v1.dtype) - self.assertTrue(bary.dtype == self.v1.dtype) - - self.assertTrue(pt.shape[0] == 3) - self.assertTrue(bary.shape[0] == 3) - - def test_polar_dec(self): - A = np.random.rand(3, 3) - - r, t = igl.polar_dec(A) - - self.assertTrue(r.flags.c_contiguous) - self.assertTrue(t.flags.c_contiguous) - - self.assertTrue(r.dtype == self.v1.dtype) - self.assertTrue(t.dtype == self.v1.dtype) - - self.assertTrue(r.shape[0] == 3) - self.assertTrue(t.shape[0] == 3) - self.assertTrue(r.shape[1] == 3) - self.assertTrue(t.shape[1] == 3) - - def test_project(self): - model = np.random.rand(4, 4) - proj = np.random.rand(4, 4) - viewport = np.random.rand(4, 1) - proj = igl.project(self.v, model, proj, viewport) - self.assertEqual(proj.dtype, self.v.dtype) - self.assertEqual(proj.shape, self.v.shape) - self.assertTrue(proj.flags.c_contiguous) - - def test_project_isometrically_to_plane(self): - U, UF, I = igl.project_isometrically_to_plane(self.v1, self.f1) - - self.assertEqual(U.dtype, self.v1.dtype) - self.assertEqual(U.shape, (3*self.f1.shape[0], 2)) - self.assertTrue(U.flags.c_contiguous) - - self.assertEqual(UF.dtype, self.f1.dtype) - self.assertEqual(UF.shape, self.f1.shape) - self.assertTrue(UF.flags.c_contiguous) - - self.assertEqual(I.dtype, self.f1.dtype) - self.assertEqual(I.shape, (self.v1.shape[0], 3*self.f1.shape[0])) - self.assertTrue(type(I) == csc.csc_matrix) - - def test_project_to_line(self): - pts = np.random.rand(10, 3) - s = np.random.rand(1, 3) - d = np.random.rand(1, 3) - t, sqrt_d = igl.project_to_line(pts, s, d) - - self.assertTrue(t.flags.c_contiguous) - self.assertTrue(t.dtype == pts.dtype) - self.assertTrue(t.shape[0] == pts.shape[0]) - - self.assertTrue(sqrt_d.flags.c_contiguous) - self.assertTrue(sqrt_d.dtype == pts.dtype) - self.assertTrue(sqrt_d.shape[0] == pts.shape[0]) - - def test_project_to_line_segment(self): - pts = np.random.rand(10, 3) - s = np.random.rand(1, 3) - d = np.random.rand(1, 3) - t, sqrt_d = igl.project_to_line_segment(pts, s, d) - - self.assertTrue(t.flags.c_contiguous) - self.assertTrue(t.dtype == pts.dtype) - self.assertTrue(t.shape[0] == pts.shape[0]) - - self.assertTrue(sqrt_d.flags.c_contiguous) - self.assertTrue(sqrt_d.dtype == pts.dtype) - self.assertTrue(sqrt_d.shape[0] == pts.shape[0]) - - def test_ramer_douglas_peucker(self): - pts = np.random.rand(10, 3) - s, j, q = igl.ramer_douglas_peucker(pts, 1e-1) - - self.assertTrue(s.flags.c_contiguous) - self.assertTrue(s.dtype == pts.dtype) - self.assertTrue(s.shape[1] == pts.shape[1]) - - self.assertTrue(j.flags.c_contiguous) - self.assertTrue(j.dtype == self.f1.dtype) - self.assertTrue(j.shape[0] == s.shape[0]) - - self.assertTrue(q.flags.c_contiguous) - self.assertTrue(q.dtype == pts.dtype) - self.assertTrue(q.shape == pts.shape) - - def test_sample_edges(self): - e = igl.edges(self.f1) - s = igl.sample_edges(self.v1, e, 4) - # print(e.shape, s.shape) - - self.assertTrue(s.flags.c_contiguous) - self.assertTrue(s.dtype == self.v1.dtype) - self.assertTrue(s.shape[0] < e.shape[0]*6) - self.assertTrue(s.shape[1] == self.v1.shape[1]) - - def test_read_msh(self): - v, t = igl.read_msh(os.path.join(self.test_data_path, "car.msh")) - self.assertTrue(type(v) == type(t) == np.ndarray) - self.assertTrue(v.flags.c_contiguous) - self.assertTrue(t.flags.c_contiguous) - - self.assertTrue(v.dtype == self.default_float) - self.assertTrue(t.dtype == self.f1.dtype) - - def test_two_axis_valuator_fixed_up(self): - down_quat = np.random.rand(4, 1) - - quat = igl.two_axis_valuator_fixed_up( - 20, 20, 1, down_quat, 10, 10, 9, 9) - - self.assertTrue(quat.flags.c_contiguous) - self.assertTrue(quat.dtype == down_quat.dtype) - self.assertTrue(quat.shape[0] == 4) - - def test_triangle_fan(self): - _, f = igl.read_triangle_mesh( - os.path.join(self.test_data_path, "camelhead.off")) - e = igl.exterior_edges(f) - cap = igl.triangle_fan(e) - - self.assertTrue(cap.flags.c_contiguous) - self.assertTrue(cap.dtype == e.dtype) - self.assertTrue(cap.shape[1] == 3) - - def test_triangles_from_strip(self): - s = self.f[:, 0] - f = igl.triangles_from_strip(s) - - self.assertTrue(f.flags.c_contiguous) - self.assertTrue(f.dtype == self.f.dtype) - self.assertTrue(f.shape[0] == self.f.shape[0]-2) - self.assertTrue(f.shape[1] == 3) - - def test_signed_angle(self): - a = np.random.rand(2, 1) - b = np.random.rand(2, 1) - p = np.random.rand(2, 1) - - angle = igl.signed_angle(a, b, p) - - a = np.random.rand(1, 2) - b = np.random.rand(1, 2) - p = np.random.rand(1, 2) - - angle = igl.signed_angle(a, b, p) - - def test_unique_edge_map(self): - E, uE, EMAP, uE2E = igl.unique_edge_map(self.f1) - - self.assertTrue(E.flags.c_contiguous) - self.assertTrue(uE.flags.c_contiguous) - self.assertTrue(EMAP.flags.c_contiguous) - - self.assertTrue(E.dtype == self.f1.dtype) - self.assertTrue(uE.dtype == self.f1.dtype) - self.assertTrue(EMAP.dtype == self.f1.dtype) - - self.assertTrue(E.shape == (self.f1.shape[0]*3, 2)) - self.assertTrue(uE.shape[1] == 2) - self.assertTrue(E.shape[0] == self.f1.shape[0]*3) - - def test_connected_components(self): - a = igl.adjacency_matrix(self.f) - comps, c, k = igl.connected_components(a) - - self.assertTrue(c.flags.c_contiguous) - self.assertTrue(k.flags.c_contiguous) - - self.assertTrue(c.dtype == self.f1.dtype) - self.assertTrue(k.dtype == self.f1.dtype) - - self.assertTrue(c.shape[0] == a.shape[0]) - - def test_pso(self): - def banana(x): - x1 = x[0] - x2 = x[1] - return x1**4 - 2*x2*x1**2 + x2**2 + x1**2 - 2*x1 + 5 - - lb = np.array([-3.0, -1.0]) - ub = np.array([2.0, 6.0]) - - fopt, xopt = igl.pso(banana, lb, ub, max_iters=10, population=10) - - self.assertTrue(xopt.flags.c_contiguous) - self.assertTrue(xopt.dtype == lb.dtype) - self.assertTrue(xopt.shape == (2, )) - - def test_random_search(self): - def banana(x): - x1 = x[0] - x2 = x[1] - return x1**4 - 2*x2*x1**2 + x2**2 + x1**2 - 2*x1 + 5 - - lb = np.array([-3.0, -1.0]) - ub = np.array([2.0, 6.0]) - - fopt, xopt = igl.random_search(banana, lb, ub, iters=10) - - self.assertTrue(xopt.flags.c_contiguous) - self.assertTrue(xopt.dtype == lb.dtype) - self.assertTrue(xopt.shape == (2, )) - - def test_bijective_composite_harmonic_mapping(self): - v, f, _ = igl.read_off(os.path.join(self.test_data_path, "camelhead.off")) - b = igl.boundary_loop(f) - thetas = np.linspace(0, 2 * np.pi, len(b))[:, np.newaxis] - bc = np.concatenate([np.cos(thetas), np.sin(thetas)], axis=1) - v2d = igl.harmonic(v, f, b, bc, 1)[:, :2] - ret0, mapping0 = igl.bijective_composite_harmonic_mapping( - v2d, f, b, bc) - self.assertTrue(ret0) - self.assertTrue(mapping0.flags.c_contiguous) - self.assertTrue(mapping0.dtype == v2d.dtype) - self.assertTrue(mapping0.shape == v2d.shape) - - ret1, mapping1 = igl.bijective_composite_harmonic_mapping_with_steps( - v2d, f, b, bc, min_steps=1, max_steps=5, num_inner_iters=2, test_for_flips=True) - self.assertTrue(ret1) - self.assertTrue(mapping1.flags.c_contiguous) - self.assertTrue(mapping1.dtype == v2d.dtype) - self.assertTrue(mapping1.shape == v2d.shape) - - ret2, mapping2 = igl.bijective_composite_harmonic_mapping_with_steps( - v2d, f, b, bc, min_steps=1, max_steps=5, num_inner_iters=2, test_for_flips=False) - self.assertTrue(ret2) - self.assertTrue(mapping2.flags.c_contiguous) - self.assertTrue(mapping2.dtype == v2d.dtype) - self.assertTrue(mapping2.shape == v2d.shape) - - self.assertTrue(np.allclose(mapping0, mapping1)) - - def test_bijective_composite_harmonic_mapping_with_steps(self): - # Tested above - pass - - def test_extract_non_manifold_edge_curves(self): - _ = igl.extract_non_manifold_edge_curves(self.f1, []) - curves = igl.extract_non_manifold_edge_curves(self.f1, [range(10)]) - self.assertTrue(len(curves) == 1) - self.assertTrue(curves[0][0] == 0) - - def test_intrinsic_delaunay_cotmatrix(self): - l, l_int, f_int = igl.intrinsic_delaunay_cotmatrix(self.v1, self.f1) - # print(l.shape, l_int.shape, f_int.shape) - self.assertTrue(l.shape == (self.v1.shape[0], self.v1.shape[0])) - self.assertTrue(l.dtype == self.v1.dtype) - self.assertTrue(type(l) == csc.csc_matrix) - - def test_cut_to_disk(self): - cuts = igl.cut_to_disk(self.f2) - # This test assumes fertility.off - self.assertTrue(len(cuts) == 9) - - def test_iterative_closest_point(self): - r, t = igl.iterative_closest_point( - self.v1, self.f1, self.v1, self.f1, 3, 20) - self.assertEqual(r.shape, (3, 3)) - self.assertEqual(t.shape, (3,)) - self.assertTrue(r.flags.c_contiguous) - self.assertTrue(t.flags.c_contiguous) - self.assertTrue(r.dtype == t.dtype == self.v.dtype) - - def test_rigid_alignment(self): - n = igl.per_vertex_normals(self.v1, self.f1) - r, t = igl.rigid_alignment(self.v1, self.v1+1, n) - self.assertTrue(np.allclose(r, np.eye(3))) - self.assertTrue(np.allclose(t, np.ones(3))) - self.assertEqual(r.shape, (3, 3)) - self.assertEqual(t.shape, (3,)) - self.assertTrue(r.flags.c_contiguous) - self.assertTrue(t.flags.c_contiguous) - self.assertTrue(r.dtype == t.dtype == self.v1.dtype) - - def test_sharp_edges(self): - se, e, ue, emap, ue2e, sharp = igl.sharp_edges( - self.v1, self.f1, np.pi*0.11) - self.assertTrue(se.shape[1] == 2) - self.assertTrue(ue.shape[1] == 2) - self.assertTrue(emap.shape[0] == self.f1.shape[0]*3) - self.assertTrue(se.shape[0] == len(sharp)) - self.assertTrue(se.flags.c_contiguous) - self.assertTrue(e.flags.c_contiguous) - self.assertTrue(ue.flags.c_contiguous) - self.assertTrue(emap.flags.c_contiguous) - - def test_quad_grid(self): - v, q, e = igl.quad_grid(3, 3) - self.assertTrue(v.shape == (3*3, 2)) - self.assertTrue(q.shape == (2*2, 4)) - self.assertTrue(v.flags.c_contiguous) - self.assertTrue(q.flags.c_contiguous) - self.assertTrue(v.dtype == self.default_float) - self.assertTrue(q.dtype == self.default_int) - self.assertTrue(e.dtype == self.default_int) - - def test_sparse_voxel_grid(self): - def sphere1(point): - return np.sqrt(point[0]**2 + point[1]**2 + point[2]**2) - 1.0 - point = np.array([1.0, 0.0, 0.0]) - cs, cv, ci = igl.sparse_voxel_grid(point, sphere1, 1.0, 100) - self.assertTrue(cv.flags.c_contiguous) - self.assertTrue(cv.dtype == self.default_float) - self.assertTrue(cv.shape == (len(cs), 3)) - self.assertTrue(ci.flags.c_contiguous) - self.assertTrue(ci.dtype == self.default_int) - self.assertTrue(ci.shape[1] == 8) - - def test_topological_hole_fill(self): - f = self.f1 - h = [range(10, 20)] - ff = igl.topological_hole_fill(f, h) - self.assertTrue(ff.flags.c_contiguous) - self.assertTrue(ff.shape[1] == 3) - self.assertTrue(ff.dtype == f.dtype) - self.assertTrue(ff.shape[0] != f.shape[0]) - - def test_triangulated_grid(self): - v, f = igl.triangulated_grid(10, 10) - self.assertTrue(v.shape == (100, 2)) - self.assertTrue(f.shape == (162, 3)) - self.assertTrue(f.flags.c_contiguous) - self.assertTrue(v.flags.c_contiguous) - self.assertTrue(v.dtype == self.default_float) - self.assertTrue(f.dtype == self.default_int) - - def test_unproject_on_line(self): - pos = np.array([10., 10.]) - eye = np.eye(4) - viewport = np.array([0., 0., 100., 100.]) - p = np.array([15.0, 20.0, 13.0]) - d = np.array([0.1, 0.2, 1.0]) - t, z = igl.unproject_on_line(pos, eye, viewport, p, d) - - self.assertTrue(z.flags.c_contiguous) - self.assertTrue(z.shape == (3, )) - self.assertTrue(z.dtype == pos.dtype) - - def test_unproject_on_plane(self): - pos = np.array([10., 10.]) - eye = np.eye(4) - viewport = np.array([0., 0., 100., 100.]) - p = np.array([1.0, 2.0, 3.0, 2.0]) - z = igl.unproject_on_plane(pos, eye, viewport, p) - - self.assertTrue(z.flags.c_contiguous) - self.assertTrue(z.shape == (3, )) - self.assertTrue(z.dtype == pos.dtype) - - def test_fast_winding_number_for_points(self): - xs = np.linspace(-5.0, 5.0, 10) - grid = np.meshgrid(xs, xs, xs, indexing='ij') - grid = np.stack(grid).reshape(3, -1, order='F').T - n = igl.per_vertex_normals(self.v1, self.f1) - a = np.ones((n.shape[0], )) / n.shape[0] - - wn = igl.fast_winding_number_for_points(self.v1, n, a, grid) - self.assertTrue(wn.flags.c_contiguous) - self.assertTrue(wn.shape == (grid.shape[0], )) - self.assertTrue(wn.dtype == self.v1.dtype) - - def test_fast_winding_number_for_meshes(self): - xs = np.linspace(-5.0, 5.0, 10) - grid = np.meshgrid(xs, xs, xs, indexing='ij') - grid = np.stack(grid).reshape(3, -1, order='F').T - - wn = igl.fast_winding_number_for_meshes(self.v1, self.f1, grid) - self.assertTrue(wn.flags.c_contiguous) - self.assertTrue(wn.shape == (grid.shape[0], )) - self.assertTrue(wn.dtype == self.v1.dtype) - - def test_flip_avoiding_line_search(self): - def fun(v): - return np.random.rand(1)[0] - - energy, vr = igl.flip_avoiding_line_search( - self.f1, self.v1[:, :2], self.v1[:, :2], fun, 10.0) - self.assertTrue(vr.shape == (self.v1.shape[0], 2)) - self.assertTrue(vr.flags.c_contiguous) - - def test_edge_flaps(self): - e, emap, ef, ei = igl.edge_flaps(self.f2) - self.assertTrue(e.shape[1] == ef.shape[1] == ei.shape[1] == 2) - self.assertTrue(e.shape[0] == ef.shape[0] == ei.shape[0]) - self.assertTrue(emap.shape[0] == self.f2.shape[0] * 3) - self.assertTrue(np.min(e) >= 0 and np.max(e) < self.v2.shape[0]) - self.assertTrue(e.flags.c_contiguous) - self.assertTrue(emap.flags.c_contiguous) - self.assertTrue(ef.flags.c_contiguous) - self.assertTrue(ei.flags.c_contiguous) - self.assertTrue(e.dtype == emap.dtype == - ef.dtype == ei.dtype == self.f2.dtype) - - def test_circulation(self): - pass - #e, emap, ef, ei = igl.edge_flaps(self.f2) - #fac = igl.circulation(667, True, emap, ef, ei) - # print(fac) - - def test_edge_collapse_is_valid(self): - pass - #e, emap, ef, ei = igl.edge_flaps(self.f2) - #emap = emap.reshape(-1, 3) - #val = igl.edge_collapse_is_valid(0, self.f2, e, emap, ef, ei) - # print(val) - - def test_flip_edge(self): - e, ue, emap, ue2e = igl.unique_edge_map(self.f1) - f, e, ue, emap, ue2e = igl.flip_edge(self.f1, e, ue, emap, ue2e, 1) - - self.assertTrue(f.shape == self.f1.shape) - self.assertTrue(e.shape[1] == ue.shape[1] == - np.array(ue2e).shape[1] == 2) - self.assertTrue(emap.shape[0] == self.f1.shape[0] * 3) - - self.assertTrue(np.min(e) >= 0 and np.max(e) < self.v2.shape[0]) - - self.assertTrue(e.flags.c_contiguous) - self.assertTrue(emap.flags.c_contiguous) - self.assertTrue(ue.flags.c_contiguous) - - self.assertTrue(f.dtype == e.dtype == ue.dtype == - emap.dtype == self.f1.dtype) - self.assertTrue(np.array(ue2e).dtype == self.f1.dtype) - - def test_AABB(self): - tree = igl.AABB_f64_3() - v1_f = np.asarray(self.v1, order='F') - f1_f = np.asarray(self.f1, order='F') - tree.init(v1_f,f1_f) - bc = igl.barycenter(v1_f,f1_f) - sqrD = tree.squared_distance(v1_f,f1_f,bc) - self.assertTrue(sqrD.shape[0] == bc.shape[0]) - self.assertTrue(np.max(sqrD) <= 1e-16) - sqrD,I,C = tree.squared_distance(v1_f,f1_f,bc,return_index=True,return_closest_point=True) - self.assertTrue(sqrD.shape[0] == bc.shape[0]) - self.assertTrue(I.shape[0] == bc.shape[0]) - self.assertTrue(C.shape == bc.shape) - - def test_in_element_3(self): - V = np.array([ [0.,0,0], [1,0,0], [0,1,0], [0,0,1], [1,1,1]],dtype='float64',order='f') - T = np.array([[0,1,2,3],[4,3,2,1]],dtype='int32',order='f') - Q = np.array([[0.1,0.1,0.1],[0.9,0.9,0.9]],dtype='float64',order='f') - tree = igl.AABB_f64_3() - tree.init(V,T) - I = igl.in_element_3(V,T,Q,tree) - self.assertTrue(I.shape[0] == Q.shape[0]) - self.assertTrue(I[0] == 0) - self.assertTrue(I[1] == 1) - - def test_in_element_2(self): - V = np.array([ [0.,0], [1,0], [0,1], [1,1]],dtype='float64',order='f') - F = np.array([[0,1,2],[2,1,3]],'int32',order='f') - Q = np.array([[0.1,0.1],[0.9,0.9]],dtype='float64',order='f') - tree = igl.AABB_f64_2() - tree.init(V,F) - I = igl.in_element_2(V,F,Q,tree) - self.assertTrue(I.shape[0] == Q.shape[0]) - self.assertTrue(I[0] == 0) - self.assertTrue(I[1] == 1) - - def test_triangulate(self): - V = np.array([[0,0],[1,0],[1,1],[0,1]],dtype='float64') - E = np.array([[0,1],[1,2],[2,3],[3,0]]) - V2,F2 = igl.triangle.triangulate(V,E,flags='Q') - self.assertTrue(V2.shape == V.shape) - self.assertTrue(F2.shape == (2,3)) - V = np.array([[0,0],[4,0],[0,4],[1,1],[1,2],[2,1]],dtype='float64') - E = np.array([[0,1],[1,2],[2,0],[3,4],[4,5],[5,3]]) - H = np.array([[1.1,1.1]]) - # Markers can't be 0 - VM = 1+np.array(range(V.shape[0])) - EM = 1+np.array(range(E.shape[0])) - V2,F2,VM2,E2,EM2 = igl.triangle.triangulate(V,E,H,flags='Q',VM=VM,EM=EM) - self.assertTrue(V2.shape == V.shape) - self.assertTrue(F2.shape == (3*2,3)) - self.assertTrue(VM2.shape == VM.shape) - self.assertTrue(E2.shape == E.shape) - self.assertTrue(EM2.shape == EM.shape) - - def test_tetrahedralize(self): - V = np.array([[0,0,0],[1,0,0],[0,1,0],[0,0,1]],dtype='float64') - F = np.array([[0,1,2],[0,1,3],[0,2,3],[1,2,3]]) - TV,TT,TF = igl.copyleft.tetgen.tetrahedralize(V,F); - self.assertTrue(TV.shape == V.shape) - self.assertTrue(TT.shape == (4,)) - self.assertTrue(TF.shape == F.shape) - V = np.array([[1,1,0],[1,-1,0],[-1,-1,0],[-1,1,0],[0,0,1]],dtype='float64') - TV,TT,TF = igl.copyleft.tetgen.tetrahedralize(V,switches="cQ"); - self.assertTrue(TV.shape == V.shape) - self.assertTrue(TT.shape == (2,4)) - self.assertTrue(TF.shape == (6,3)) - TV,TT,TF = igl.copyleft.tetgen.tetrahedralize(self.v1,self.f1,switches="pQq1.34"); - igl.writeMESH("test.mesh",TV,TT,TF) - self.assertTrue(TV.shape[0] > self.v1.shape[0]) - self.assertTrue(TT.shape[0] > self.f1.shape[0]) - - - def test_writeMESH(self): - igl.writeMESH("test.mesh",self.v4,self.t4,self.f4) - - def test_writeMSH(self): - igl.writeMSH("test.msh",self.v4,self.f4,self.t4) - - # copyleft.cgal - def test_convex_hull(self): - V = np.array([[0,0,0],[1,0,0],[0,1,0],[0,0,1]],dtype="float64") - F = igl.copyleft.cgal.convex_hull(V) - F = np.sort(F) - F = F[np.lexsort(F.T[::-1],axis=0)] - F_gt = np.array([[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]],dtype="int64") - self.assertTrue((F == F_gt).all()) - - def test__version(self): - self.assertTrue(isinstance(igl.__version__, str)) - - def test_blue_noise(self): - r = igl.avg_edge_length(self.v, self.f) - b,fi,p = igl.blue_noise(self.v, self.f, r) - self.assertTrue(b.shape[0] == fi.shape[0]) - self.assertTrue(b.shape[0] == p.shape[0]) - self.assertTrue(self.v.shape[1] == p.shape[1]) - self.assertTrue(self.f.shape[1] == b.shape[1]) - - def test_fit_cubic_bezier(self): - r = igl.avg_edge_length(self.v, self.f) - cubics = igl.fit_cubic_bezier(self.v, r) - [self.assertTrue(cubic.shape[1] == self.v.shape[1]) for cubic in cubics] - - def test_is_delaunay(self): - D = igl.is_delaunay(self.v, self.f) - self.assertTrue(D.shape[0] == self.f.shape[0]) - self.assertTrue(D.shape[1] == self.f.shape[1]) - - def test_random_points_on_mesh(self): - b,fi,x = igl.random_points_on_mesh(100, self.v, self.f) - self.assertTrue(b.shape[0] == 100) - self.assertTrue(b.shape[0] == fi.shape[0]) - self.assertTrue(b.shape[0] == x.shape[0]) - self.assertTrue(self.v.shape[1] == x.shape[1]) - self.assertTrue(self.f.shape[1] == b.shape[1]) - - def test_random_points_on_mesh_intrinsic(self): - dblA = igl.doublearea(self.v, self.f) - b,fi = igl.random_points_on_mesh_intrinsic(100, dblA) - self.assertTrue(b.shape[0] == 100) - self.assertTrue(b.shape[0] == fi.shape[0]) - self.assertTrue(self.f.shape[1] == b.shape[1]) - - def test_moments(self): - m0,m1,m2 = igl.moments(self.v1, self.f1) - - def test_path_to_edges(self): - I = self.f1[:,0] - e = igl.path_to_edges(I) - self.assertTrue(e.shape[1] == 2) - self.assertTrue(e.shape[0] == I.shape[0]-1) - - - def test_copyleft(self): - # check that type is - self.assertTrue(type(igl.copyleft) == type(igl)) - - def test_triangle(self): - # check that type is - self.assertTrue(type(igl.copyleft) == type(igl)) - - def test_copyleft_tetgen(self): - # check that type is - self.assertTrue(type(igl.copyleft.tetgen) == type(igl)) - - - -if __name__ == '__main__': - unittest.main()