From c7513c189426458c762942a5b4e67d36dffe3cd2 Mon Sep 17 00:00:00 2001 From: Jan Bender Date: Mon, 20 Dec 2021 17:08:25 +0100 Subject: [PATCH 1/5] - added Python bindings --- .github/workflows/build-linux.yml | 49 + .github/workflows/build-windows.yml | 40 + CMake/Common.cmake | 9 + CMake/DataCopyTargets.cmake | 23 + CMakeLists.txt | 9 +- Changelog.txt | 3 + Demos/BarDemo/CMakeLists.txt | 2 +- Demos/BarDemo/main.cpp | 295 +- Demos/CMakeLists.txt | 2 +- Demos/ClothDemo/CMakeLists.txt | 2 +- Demos/ClothDemo/main.cpp | 347 ++- Demos/Common/DemoBase.cpp | 33 +- Demos/Common/DemoBase.h | 4 +- Demos/CosseratRodsDemo/CMakeLists.txt | 2 +- Demos/CosseratRodsDemo/main.cpp | 47 +- Demos/CouplingDemos/CMakeLists.txt | 2 +- .../RigidBodyClothCouplingDemo.cpp | 363 ++- Demos/DistanceFieldDemos/CMakeLists.txt | 2 +- .../DistanceFieldDemos/ClothCollisionDemo.cpp | 361 ++- .../DeformableCollisionDemo.cpp | 282 +- .../RigidBodyCollisionDemo.cpp | 10 +- Demos/FluidDemo/CMakeLists.txt | 2 +- Demos/FluidDemo/main.cpp | 1 - Demos/GenericConstraintsDemos/CMakeLists.txt | 2 +- .../GenericConstraints.cpp | 17 +- .../GenericConstraints.h | 6 +- .../GenericConstraintsModel.cpp | 8 +- .../GenericConstraintsModel.h | 4 +- .../GenericParticleConstraintsDemo.cpp | 123 +- .../GenericRigidBodyConstraintsDemo.cpp | 2 +- .../CMakeLists.txt | 2 +- .../PositionBasedElasticRodsDemo.cpp | 36 +- Demos/RigidBodyDemos/CMakeLists.txt | 2 +- Demos/RigidBodyDemos/ChainDemo.cpp | 6 +- Demos/RigidBodyDemos/JointDemo.cpp | 2 +- Demos/SceneLoaderDemo/CMakeLists.txt | 2 +- Demos/SceneLoaderDemo/SceneLoaderDemo.cpp | 516 ++-- Demos/StiffRodsDemos/CMakeLists.txt | 2 +- ...ectPositionBasedSolverForStiffRodsDemo.cpp | 496 ++-- .../StretchBendingTwistingDemo.cpp | 4 +- MANIFEST.in | 1 + .../PositionBasedDynamics.cpp | 57 +- PositionBasedDynamics/PositionBasedDynamics.h | 17 +- README.md | 2 + Simulation/CMakeLists.txt | 8 +- Simulation/Constraints.cpp | 164 +- Simulation/Constraints.h | 88 +- Simulation/Simulation.h | 2 + Simulation/SimulationModel.cpp | 554 +++- Simulation/SimulationModel.h | 119 +- Simulation/TetModel.cpp | 5 - Simulation/TetModel.h | 5 +- Simulation/TimeStepController.cpp | 3 +- Simulation/TriangleModel.cpp | 5 - Simulation/TriangleModel.h | 4 +- Utils/CMakeLists.txt | 5 +- Utils/IndexedFaceMesh.cpp | 69 +- Utils/IndexedFaceMesh.h | 96 +- Utils/IndexedTetMesh.cpp | 63 +- Utils/IndexedTetMesh.h | 71 +- Utils/OBJLoader.h | 6 +- Utils/SceneLoader.cpp | 3 + Utils/SceneLoader.h | 2 + Utils/Version.h.in | 2 + doc/bibliography.bib | 19 +- extern/pybind/CMakeLists.txt | 270 ++ extern/pybind/LICENSE | 29 + extern/pybind/MANIFEST.in | 6 + extern/pybind/README.rst | 175 ++ extern/pybind/include/pybind11/attr.h | 551 ++++ extern/pybind/include/pybind11/buffer_info.h | 146 + extern/pybind/include/pybind11/cast.h | 2219 +++++++++++++++ extern/pybind/include/pybind11/chrono.h | 191 ++ extern/pybind/include/pybind11/common.h | 2 + extern/pybind/include/pybind11/complex.h | 65 + extern/pybind/include/pybind11/detail/class.h | 710 +++++ .../pybind/include/pybind11/detail/common.h | 853 ++++++ extern/pybind/include/pybind11/detail/descr.h | 100 + extern/pybind/include/pybind11/detail/init.h | 336 +++ .../include/pybind11/detail/internals.h | 363 +++ .../pybind/include/pybind11/detail/typeid.h | 55 + extern/pybind/include/pybind11/eigen.h | 607 +++++ extern/pybind/include/pybind11/embed.h | 201 ++ extern/pybind/include/pybind11/eval.h | 152 ++ extern/pybind/include/pybind11/functional.h | 104 + extern/pybind/include/pybind11/iostream.h | 216 ++ extern/pybind/include/pybind11/numpy.h | 1693 ++++++++++++ extern/pybind/include/pybind11/operators.h | 173 ++ extern/pybind/include/pybind11/options.h | 65 + extern/pybind/include/pybind11/pybind11.h | 2378 +++++++++++++++++ extern/pybind/include/pybind11/pytypes.h | 1659 ++++++++++++ extern/pybind/include/pybind11/stl.h | 388 +++ extern/pybind/include/pybind11/stl_bind.h | 661 +++++ extern/pybind/pybind11/__init__.py | 12 + extern/pybind/pybind11/__main__.py | 52 + extern/pybind/pybind11/_version.py | 12 + extern/pybind/pybind11/_version.pyi | 6 + extern/pybind/pybind11/commands.py | 22 + extern/pybind/pybind11/py.typed | 0 extern/pybind/pybind11/setup_helpers.py | 398 +++ extern/pybind/pybind11/setup_helpers.pyi | 50 + extern/pybind/pyproject.toml | 3 + extern/pybind/setup.cfg | 71 + extern/pybind/setup.py | 115 + extern/pybind/tools/FindCatch.cmake | 70 + extern/pybind/tools/FindEigen3.cmake | 83 + extern/pybind/tools/FindPythonLibsNew.cmake | 255 ++ extern/pybind/tools/check-style.sh | 44 + extern/pybind/tools/cmake_uninstall.cmake.in | 23 + extern/pybind/tools/libsize.py | 38 + extern/pybind/tools/mkdoc.py | 379 +++ extern/pybind/tools/pybind11Common.cmake | 393 +++ extern/pybind/tools/pybind11Config.cmake.in | 232 ++ extern/pybind/tools/pybind11NewTools.cmake | 246 ++ extern/pybind/tools/pybind11Tools.cmake | 200 ++ extern/pybind/tools/pyproject.toml | 3 + extern/pybind/tools/setup_global.py.in | 63 + extern/pybind/tools/setup_main.py.in | 36 + pyPBD/CMakeLists.txt | 31 + pyPBD/CollisionDetectionModule.cpp | 190 ++ pyPBD/ConstraintsModule.cpp | 170 ++ pyPBD/ParameterObjectModule.cpp | 30 + pyPBD/ParticleDataModule.cpp | 59 + pyPBD/RigidBodyModule.cpp | 118 + pyPBD/SimulationModelModule.cpp | 265 ++ pyPBD/SimulationModule.cpp | 34 + pyPBD/TimeModule.cpp | 23 + pyPBD/TimeStepModule.cpp | 32 + pyPBD/UtilitiesModule.cpp | 282 ++ pyPBD/bind_pointer_vector.h | 252 ++ pyPBD/common.h | 18 + pyPBD/examples/armadillo.py | 147 + pyPBD/examples/beam_model.py | 117 + pyPBD/examples/bunny_cloth.py | 175 ++ pyPBD/examples/chain_model.py | 123 + pyPBD/examples/cloth_collision.py | 167 ++ pyPBD/examples/cloth_model.py | 126 + pyPBD/examples/math_tools.py | 73 + pyPBD/examples/pendulum.py | 97 + pyPBD/examples/render_tools.py | 199 ++ pyPBD/examples/rigid_body_cloth_coupling.py | 264 ++ pyPBD/main.cpp | 37 + setup.cfg | 5 + setup.py | 125 + version | 1 + 145 files changed, 22557 insertions(+), 2267 deletions(-) create mode 100644 CMake/DataCopyTargets.cmake create mode 100644 MANIFEST.in create mode 100644 extern/pybind/CMakeLists.txt create mode 100644 extern/pybind/LICENSE create mode 100644 extern/pybind/MANIFEST.in create mode 100644 extern/pybind/README.rst create mode 100644 extern/pybind/include/pybind11/attr.h create mode 100644 extern/pybind/include/pybind11/buffer_info.h create mode 100644 extern/pybind/include/pybind11/cast.h create mode 100644 extern/pybind/include/pybind11/chrono.h create mode 100644 extern/pybind/include/pybind11/common.h create mode 100644 extern/pybind/include/pybind11/complex.h create mode 100644 extern/pybind/include/pybind11/detail/class.h create mode 100644 extern/pybind/include/pybind11/detail/common.h create mode 100644 extern/pybind/include/pybind11/detail/descr.h create mode 100644 extern/pybind/include/pybind11/detail/init.h create mode 100644 extern/pybind/include/pybind11/detail/internals.h create mode 100644 extern/pybind/include/pybind11/detail/typeid.h create mode 100644 extern/pybind/include/pybind11/eigen.h create mode 100644 extern/pybind/include/pybind11/embed.h create mode 100644 extern/pybind/include/pybind11/eval.h create mode 100644 extern/pybind/include/pybind11/functional.h create mode 100644 extern/pybind/include/pybind11/iostream.h create mode 100644 extern/pybind/include/pybind11/numpy.h create mode 100644 extern/pybind/include/pybind11/operators.h create mode 100644 extern/pybind/include/pybind11/options.h create mode 100644 extern/pybind/include/pybind11/pybind11.h create mode 100644 extern/pybind/include/pybind11/pytypes.h create mode 100644 extern/pybind/include/pybind11/stl.h create mode 100644 extern/pybind/include/pybind11/stl_bind.h create mode 100644 extern/pybind/pybind11/__init__.py create mode 100644 extern/pybind/pybind11/__main__.py create mode 100644 extern/pybind/pybind11/_version.py create mode 100644 extern/pybind/pybind11/_version.pyi create mode 100644 extern/pybind/pybind11/commands.py create mode 100644 extern/pybind/pybind11/py.typed create mode 100644 extern/pybind/pybind11/setup_helpers.py create mode 100644 extern/pybind/pybind11/setup_helpers.pyi create mode 100644 extern/pybind/pyproject.toml create mode 100644 extern/pybind/setup.cfg create mode 100644 extern/pybind/setup.py create mode 100644 extern/pybind/tools/FindCatch.cmake create mode 100644 extern/pybind/tools/FindEigen3.cmake create mode 100644 extern/pybind/tools/FindPythonLibsNew.cmake create mode 100644 extern/pybind/tools/check-style.sh create mode 100644 extern/pybind/tools/cmake_uninstall.cmake.in create mode 100644 extern/pybind/tools/libsize.py create mode 100644 extern/pybind/tools/mkdoc.py create mode 100644 extern/pybind/tools/pybind11Common.cmake create mode 100644 extern/pybind/tools/pybind11Config.cmake.in create mode 100644 extern/pybind/tools/pybind11NewTools.cmake create mode 100644 extern/pybind/tools/pybind11Tools.cmake create mode 100644 extern/pybind/tools/pyproject.toml create mode 100644 extern/pybind/tools/setup_global.py.in create mode 100644 extern/pybind/tools/setup_main.py.in create mode 100644 pyPBD/CMakeLists.txt create mode 100644 pyPBD/CollisionDetectionModule.cpp create mode 100644 pyPBD/ConstraintsModule.cpp create mode 100644 pyPBD/ParameterObjectModule.cpp create mode 100644 pyPBD/ParticleDataModule.cpp create mode 100644 pyPBD/RigidBodyModule.cpp create mode 100644 pyPBD/SimulationModelModule.cpp create mode 100644 pyPBD/SimulationModule.cpp create mode 100644 pyPBD/TimeModule.cpp create mode 100644 pyPBD/TimeStepModule.cpp create mode 100644 pyPBD/UtilitiesModule.cpp create mode 100644 pyPBD/bind_pointer_vector.h create mode 100644 pyPBD/common.h create mode 100644 pyPBD/examples/armadillo.py create mode 100644 pyPBD/examples/beam_model.py create mode 100644 pyPBD/examples/bunny_cloth.py create mode 100644 pyPBD/examples/chain_model.py create mode 100644 pyPBD/examples/cloth_collision.py create mode 100644 pyPBD/examples/cloth_model.py create mode 100644 pyPBD/examples/math_tools.py create mode 100644 pyPBD/examples/pendulum.py create mode 100644 pyPBD/examples/render_tools.py create mode 100644 pyPBD/examples/rigid_body_cloth_coupling.py create mode 100644 pyPBD/main.cpp create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 version diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index c2c5c898..6209885f 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -9,9 +9,58 @@ jobs: steps: - uses: actions/checkout@v1 + - name: apt-get update + run: sudo apt-get update --fix-missing - name: Install packages run: sudo apt-get -y install xorg-dev freeglut3-dev - name: configure run: mkdir build-release && cd build-release && cmake -DCMAKE_BUILD_TYPE=Release .. - name: build run: cmake --build build-release + + build-manylinux-python: + + runs-on: ubuntu-latest + strategy: + matrix: + # python-version: [3.7] + python-version: [cp36-cp36m, cp37-cp37m, cp38-cp38] + + steps: + - uses: actions/checkout@v1 + + # Set up python + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + + # Install dependencies + - name: Install dependencies + run: python -m pip install --upgrade twine + + - name: Build manylinux Python wheels + uses: digitalillusions/python-wheels-manylinux-build@master + with: + # python-versions: 'cp37-cp37m' + python-versions: '${{ matrix.python-version }}' + build-requirements: '' + system-packages: 'cmake3' + package-path: '' + pip-wheel-args: '--manylinux-build' + + # Upload artifacts + - name: Upload compiled wheel + uses: actions/upload-artifact@master + with: + name: pypbd-linux-${{ matrix.python-version }} + path: wheelhouse + if: always() + + # Publish to pypi + - name: Publish wheels to PyPI + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + twine upload wheelhouse/*-manylinux*.whl --skip-existing diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 01e0913d..6e11ec8c 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -19,3 +19,43 @@ jobs: run: cmake --build build-release --config Release + # Build the python wheel in parallel + build-windows-python: + runs-on: windows-latest + strategy: + matrix: + # python-version: [3.7] + python-version: [2.7, 3.5, 3.6, 3.7, 3.8] + + steps: + # Checkout repo + - uses: actions/checkout@v1 + + # Set up python + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + + # Install python dependencies + - name: Install dependencies + run: python -m pip install --upgrade pip setuptools wheel twine + + # Change directory and run setup py + - name: Run setup py + run: python setup.py bdist_wheel + + # Upload artifacts + - name: Upload compiled wheel + uses: actions/upload-artifact@master + with: + name: pypbd-windows-${{ matrix.python-version }} + path: build/dist + if: always() + + # Upload wheel to pypi + - name: Upload wheel to pypi + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: twine upload build/dist/* --skip-existing diff --git a/CMake/Common.cmake b/CMake/Common.cmake index a61d49e4..6e22b79d 100644 --- a/CMake/Common.cmake +++ b/CMake/Common.cmake @@ -12,6 +12,8 @@ set(PBD_BINARY_DEBUG_POSTFIX "_d" CACHE INTERNAL "Postfix for executables") set(PBD_BINARY_RELWITHDEBINFO_POSTFIX "_rd" CACHE INTERNAL "Postfix for executables") set(PBD_BINARY_MINSIZEREL_POSTFIX "_ms" CACHE INTERNAL "Postfix for executables") +include(CMakeDependentOption) + if (NOT WIN32) if (NOT EXISTS ${CMAKE_BINARY_DIR}/CMakeCache.txt) if (NOT CMAKE_BUILD_TYPE) @@ -65,3 +67,10 @@ OPTION(USE_DOUBLE_PRECISION "Use double precision" ON) if (USE_DOUBLE_PRECISION) add_definitions( -DUSE_DOUBLE) endif (USE_DOUBLE_PRECISION) + +cmake_dependent_option(USE_PYTHON_BINDINGS "Generate Python Bindings using PyBind11" ON "PYTHON_EXECUTABLE" OFF) +if (USE_PYTHON_BINDINGS AND UNIX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + message(STATUS "Adding -fPIC option when generating Python bindings using GCC") +endif () diff --git a/CMake/DataCopyTargets.cmake b/CMake/DataCopyTargets.cmake new file mode 100644 index 00000000..fe3c5f11 --- /dev/null +++ b/CMake/DataCopyTargets.cmake @@ -0,0 +1,23 @@ +add_custom_target(CopyPBDShaders + ${CMAKE_COMMAND} -E copy_directory + ${PROJECT_SOURCE_DIR}/data/shaders + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/resources/shaders + COMMENT "Copying PBD shaders" +) +set_target_properties(CopyPBDShaders PROPERTIES FOLDER "Data copy") + +add_custom_target(CopyPBDModels + ${CMAKE_COMMAND} -E copy_directory + ${PROJECT_SOURCE_DIR}/data/models + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/resources/models + COMMENT "Copying PBD models" +) +set_target_properties(CopyPBDModels PROPERTIES FOLDER "Data copy") + +add_custom_target(CopyPBDScenes + ${CMAKE_COMMAND} -E copy_directory + ${PROJECT_SOURCE_DIR}/data/scenes + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/resources/scenes + COMMENT "Copying PBD scenes" +) +set_target_properties(CopyPBDScenes PROPERTIES FOLDER "Data copy") diff --git a/CMakeLists.txt b/CMakeLists.txt index 99df510e..7393978c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,8 +13,6 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON) include(${PROJECT_PATH}/CMake/Common.cmake) -add_definitions(-DPBD_DATA_PATH="../data") - if (NOT WIN32) message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") endif() @@ -78,11 +76,16 @@ endif() add_subdirectory(PositionBasedDynamics) add_subdirectory(Simulation) add_subdirectory(Utils) -if (NOT PBD_NO_DEMOS) +if (NOT PBD_LIBS_ONLY) + include(DataCopyTargets) add_subdirectory(extern/glfw) add_subdirectory(extern/AntTweakBar) add_subdirectory(extern/md5) add_subdirectory(Demos) + if (USE_PYTHON_BINDINGS) + add_subdirectory(extern/pybind) + add_subdirectory(pyPBD) + endif () endif() diff --git a/Changelog.txt b/Changelog.txt index 547c58ea..4c343e77 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,6 @@ +2.0.0 + - added Python binding + - cleaned up demos - added XPBD distance constraint - added XPBD isometric bending constraint - added XPBD volume constraint diff --git a/Demos/BarDemo/CMakeLists.txt b/Demos/BarDemo/CMakeLists.txt index 302b0bc5..145caaf0 100644 --- a/Demos/BarDemo/CMakeLists.txt +++ b/Demos/BarDemo/CMakeLists.txt @@ -1,5 +1,5 @@ set(SIMULATION_LINK_LIBRARIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) -set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) +set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils CopyPBDShaders) if(WIN32) set(SIMULATION_LINK_LIBRARIES opengl32.lib glu32.lib ${SIMULATION_LINK_LIBRARIES}) diff --git a/Demos/BarDemo/main.cpp b/Demos/BarDemo/main.cpp index 40ad00e5..5e3ec8fc 100644 --- a/Demos/BarDemo/main.cpp +++ b/Demos/BarDemo/main.cpp @@ -25,28 +25,35 @@ using namespace Eigen; using namespace std; using namespace Utilities; -void initParameters(); void timeStep (); void buildModel (); void createMesh(); void render (); void reset(); -void TW_CALL setStiffness(const void *value, void *clientData); -void TW_CALL getStiffness(void *value, void *clientData); -void TW_CALL setPoissonRatio(const void *value, void *clientData); -void TW_CALL getPoissonRatio(void *value, void *clientData); -void TW_CALL setNormalizeStretch(const void *value, void *clientData); -void TW_CALL getNormalizeStretch(void *value, void *clientData); -void TW_CALL setNormalizeShear(const void *value, void *clientData); -void TW_CALL getNormalizeShear(void *value, void *clientData); void TW_CALL setSimulationMethod(const void *value, void *clientData); void TW_CALL getSimulationMethod(void *value, void *clientData); +void TW_CALL setStiffness(const void* value, void* clientData); +void TW_CALL getStiffness(void* value, void* clientData); +void TW_CALL setPoissonRatio(const void* value, void* clientData); +void TW_CALL getPoissonRatio(void* value, void* clientData); +void TW_CALL setVolumeStiffness(const void* value, void* clientData); +void TW_CALL getVolumeStiffness(void* value, void* clientData); +void TW_CALL setNormalizeStretch(const void* value, void* clientData); +void TW_CALL getNormalizeStretch(void* value, void* clientData); +void TW_CALL setNormalizeShear(const void* value, void* clientData); +void TW_CALL getNormalizeShear(void* value, void* clientData); + DemoBase *base; const unsigned int width = 30; const unsigned int depth = 5; const unsigned int height = 5; short simulationMethod = 2; +Real stiffness = 1.0; +Real poissonRatio = 0.3; +bool normalizeStretch = false; +bool normalizeShear = false; +Real volumeStiffness = 1.0; // main int main( int argc, char **argv ) @@ -62,7 +69,7 @@ int main( int argc, char **argv ) buildModel(); - initParameters(); + base->createParameterGUI(); // OpenGL MiniGL::setClientIdleFunc (timeStep); @@ -73,12 +80,13 @@ int main( int argc, char **argv ) TwType enumType2 = TwDefineEnum("SimulationMethodType", NULL, 0); TwAddVarCB(MiniGL::getTweakBar(), "SimulationMethod", enumType2, setSimulationMethod, getSimulationMethod, &simulationMethod, " label='Simulation method' enum='0 {None}, 1 {Volume constraints}, 2 {FEM based PBD}, 3 {Strain based dynamics (no inversion handling)}, 4 {Shape matching (no inversion handling)}, 5 {XPBD volume constraints}' group=Simulation"); - TwAddVarCB(MiniGL::getTweakBar(), "Stiffness", TW_TYPE_REAL, setStiffness, getStiffness, model, " label='Stiffness' min=0.0 step=0.1 precision=4 group='Simulation' "); - TwAddVarCB(MiniGL::getTweakBar(), "PoissonRatio", TW_TYPE_REAL, setPoissonRatio, getPoissonRatio, model, " label='Poisson ratio XY' min=0.0 step=0.1 precision=4 group='Simulation' "); - TwAddVarCB(MiniGL::getTweakBar(), "NormalizeStretch", TW_TYPE_BOOL32, setNormalizeStretch, getNormalizeStretch, model, " label='Normalize stretch' group='Strain based dynamics' "); - TwAddVarCB(MiniGL::getTweakBar(), "NormalizeShear", TW_TYPE_BOOL32, setNormalizeShear, getNormalizeShear, model, " label='Normalize shear' group='Strain based dynamics' "); - + TwAddVarCB(MiniGL::getTweakBar(), "stiffness", TW_TYPE_REAL, setStiffness, getStiffness, model, " label='Stiffness' min=0.0 step=0.1 precision=4 group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "poissonRatio", TW_TYPE_REAL, setPoissonRatio, getPoissonRatio, model, " label='Poisson ratio' min=0.0 step=0.1 precision=4 group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeStretch", TW_TYPE_BOOL32, setNormalizeStretch, getNormalizeStretch, model, " label='Normalize stretch' group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeShear", TW_TYPE_BOOL32, setNormalizeShear, getNormalizeShear, model, " label='Normalize shear' group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "volumeStiffness", TW_TYPE_REAL, setVolumeStiffness, getVolumeStiffness, model, " label='Volume stiffness' min=0.0 step=0.1 precision=4 group='Solid' "); MiniGL::mainLoop(); + base->cleanup(); Utilities::Timing::printAverageTimes(); Utilities::Timing::printTimeSums(); @@ -90,20 +98,6 @@ int main( int argc, char **argv ) return 0; } -void initParameters() -{ - TwRemoveAllVars(MiniGL::getTweakBar()); - TweakBarParameters::cleanup(); - - MiniGL::initTweakBarParameters(); - - TweakBarParameters::createParameterGUI(); - TweakBarParameters::createParameterObjectGUI(base); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getModel()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getTimeStep()); -} - void reset() { Utilities::Timing::printAverageTimes(); @@ -157,71 +151,11 @@ void render () void createMesh() { - Vector3r points[width*height*depth]; - for (unsigned int i = 0; i < width; i++) - { - for (unsigned int j = 0; j < height; j++) - { - for (unsigned int k = 0; k < depth; k++) - { - points[i*height*depth + j*depth + k] = 0.3*Vector3r((Real)i, (Real)j, (Real)k); - } - } - } + SimulationModel* model = Simulation::getCurrent()->getModel(); + model->addRegularTetModel(width, height, depth, + Vector3r(5, 0, 0), Matrix3r::Identity(), Vector3r(10.0, 1.5, 1.5)); - vector indices; - for (unsigned int i = 0; i < width - 1; i++) - { - for (unsigned int j = 0; j < height - 1; j++) - { - for (unsigned int k = 0; k < depth - 1; k++) - { - // For each block, the 8 corners are numerated as: - // 4*-----*7 - // /| /| - // / | / | - // 5*-----*6 | - // | 0*--|--*3 - // | / | / - // |/ |/ - // 1*-----*2 - unsigned int p0 = i*height*depth + j*depth + k; - unsigned int p1 = p0 + 1; - unsigned int p3 = (i + 1)*height*depth + j*depth + k; - unsigned int p2 = p3 + 1; - unsigned int p7 = (i + 1)*height*depth + (j + 1)*depth + k; - unsigned int p6 = p7 + 1; - unsigned int p4 = i*height*depth + (j + 1)*depth + k; - unsigned int p5 = p4 + 1; - - // Ensure that neighboring tetras are sharing faces - if ((i + j + k) % 2 == 1) - { - indices.push_back(p2); indices.push_back(p1); indices.push_back(p6); indices.push_back(p3); - indices.push_back(p6); indices.push_back(p3); indices.push_back(p4); indices.push_back(p7); - indices.push_back(p4); indices.push_back(p1); indices.push_back(p6); indices.push_back(p5); - indices.push_back(p3); indices.push_back(p1); indices.push_back(p4); indices.push_back(p0); - indices.push_back(p6); indices.push_back(p1); indices.push_back(p4); indices.push_back(p3); - } - else - { - indices.push_back(p0); indices.push_back(p2); indices.push_back(p5); indices.push_back(p1); - indices.push_back(p7); indices.push_back(p2); indices.push_back(p0); indices.push_back(p3); - indices.push_back(p5); indices.push_back(p2); indices.push_back(p7); indices.push_back(p6); - indices.push_back(p7); indices.push_back(p0); indices.push_back(p5); indices.push_back(p4); - indices.push_back(p0); indices.push_back(p2); indices.push_back(p7); indices.push_back(p5); - } - } - } - } - SimulationModel *model = Simulation::getCurrent()->getModel(); - model->addTetModel(width*height*depth, (unsigned int)indices.size() / 4u, points, indices.data()); - - ParticleData &pd = model->getParticles(); - for (unsigned int i = 0; i < pd.getNumberOfParticles(); i++) - { - pd.setMass(i, 1.0); - } + ParticleData& pd = model->getParticles(); for (unsigned int i = 0; i < 1; i++) { for (unsigned int j = 0; j < height; j++) @@ -232,159 +166,94 @@ void createMesh() } // init constraints - model->setValue(SimulationModel::CLOTH_STIFFNESS, 1.0); - model->setValue(SimulationModel::SOLID_STIFFNESS, 1.0); + stiffness = 1.0; + if (simulationMethod == 5) + stiffness = 100000; + + volumeStiffness = 1.0; + if (simulationMethod == 5) + volumeStiffness = 100000; for (unsigned int cm = 0; cm < model->getTetModels().size(); cm++) { - const unsigned int nTets = model->getTetModels()[cm]->getParticleMesh().numTets(); - const unsigned int *tets = model->getTetModels()[cm]->getParticleMesh().getTets().data(); - const IndexedTetMesh::VertexTets *vTets = model->getTetModels()[cm]->getParticleMesh().getVertexTets().data(); - if (simulationMethod == 1) - { - const unsigned int offset = model->getTetModels()[cm]->getIndexOffset(); - const unsigned int nEdges = model->getTetModels()[cm]->getParticleMesh().numEdges(); - const IndexedTetMesh::Edge *edges = model->getTetModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint(v1, v2); - } - - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i]; - const unsigned int v2 = tets[4 * i + 1]; - const unsigned int v3 = tets[4 * i + 2]; - const unsigned int v4 = tets[4 * i + 3]; - - model->addVolumeConstraint(v1, v2, v3, v4); - } - } - else if (simulationMethod == 2) - { - TetModel::ParticleMesh &mesh = model->getTetModels()[cm]->getParticleMesh(); - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i]; - const unsigned int v2 = tets[4 * i + 1]; - const unsigned int v3 = tets[4 * i + 2]; - const unsigned int v4 = tets[4 * i + 3]; - - model->addFEMTetConstraint(v1, v2, v3, v4); - } - } - else if (simulationMethod == 3) - { - TetModel::ParticleMesh &mesh = model->getTetModels()[cm]->getParticleMesh(); - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i]; - const unsigned int v2 = tets[4 * i + 1]; - const unsigned int v3 = tets[4 * i + 2]; - const unsigned int v4 = tets[4 * i + 3]; - - model->addStrainTetConstraint(v1, v2, v3, v4); - } - } - else if (simulationMethod == 4) - { - TetModel::ParticleMesh &mesh = model->getTetModels()[cm]->getParticleMesh(); - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v[4] = { tets[4 * i], tets[4 * i + 1], tets[4 * i + 2], tets[4 * i + 3] }; - // Important: Divide position correction by the number of clusters - // which contain the vertex. - const unsigned int nc[4] = { vTets[v[0]].m_numTets, vTets[v[1]].m_numTets, vTets[v[2]].m_numTets, vTets[v[3]].m_numTets }; - model->addShapeMatchingConstraint(4, v, nc); - } - } - else if (simulationMethod == 5) - { - model->setValue(SimulationModel::CLOTH_STIFFNESS, 100000); - model->setValue(SimulationModel::SOLID_STIFFNESS, 100000); - const unsigned int offset = model->getTetModels()[cm]->getIndexOffset(); - const unsigned int nEdges = model->getTetModels()[cm]->getParticleMesh().numEdges(); - const IndexedTetMesh::Edge* edges = model->getTetModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint_XPBD(v1, v2); - } - - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i]; - const unsigned int v2 = tets[4 * i + 1]; - const unsigned int v3 = tets[4 * i + 2]; - const unsigned int v4 = tets[4 * i + 3]; - - model->addVolumeConstraint_XPBD(v1, v2, v3, v4); - } - } + model->addSolidConstraints(model->getTetModels()[cm], simulationMethod, stiffness, + poissonRatio, volumeStiffness, normalizeStretch, normalizeShear); + model->getTetModels()[cm]->updateMeshNormals(pd); - } - LOG_INFO << "Number of tets: " << indices.size() / 4; - LOG_INFO << "Number of vertices: " << width*height*depth; + LOG_INFO << "Number of tets: " << model->getTetModels()[cm]->getParticleMesh().numTets(); + LOG_INFO << "Number of vertices: " << width * height * depth; + } +} +void TW_CALL setSimulationMethod(const void *value, void *clientData) +{ + const short val = *(const short *)(value); + *((short*)clientData) = val; + reset(); } -void TW_CALL setStiffness(const void *value, void *clientData) +void TW_CALL getSimulationMethod(void *value, void *clientData) { - const Real val = *(const Real *)(value); - ((SimulationModel*) clientData)->setValue(SimulationModel::SOLID_STIFFNESS, val); + *(short *)(value) = *((short*)clientData); } -void TW_CALL getStiffness(void *value, void *clientData) +void TW_CALL setStiffness(const void* value, void* clientData) { - *(Real *)(value) = ((SimulationModel*)clientData)->getValue(SimulationModel::SOLID_STIFFNESS); + stiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(stiffness); + ((SimulationModel*)clientData)->setConstraintValue(stiffness); + ((SimulationModel*)clientData)->setConstraintValue(stiffness); + ((SimulationModel*)clientData)->setConstraintValue(stiffness); + ((SimulationModel*)clientData)->setConstraintValue(stiffness); + ((SimulationModel*)clientData)->setConstraintValue(stiffness); } -void TW_CALL setPoissonRatio(const void *value, void *clientData) +void TW_CALL getStiffness(void* value, void* clientData) { - const Real val = *(const Real *)(value); - ((SimulationModel*)clientData)->setValue(SimulationModel::SOLID_POISSON_RATIO, val); + *(Real*)(value) = stiffness; } -void TW_CALL getPoissonRatio(void *value, void *clientData) +void TW_CALL setVolumeStiffness(const void* value, void* clientData) { - *(Real *)(value) = ((SimulationModel*)clientData)->getValue(SimulationModel::SOLID_POISSON_RATIO); + volumeStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(volumeStiffness); + ((SimulationModel*)clientData)->setConstraintValue(volumeStiffness); } -void TW_CALL setNormalizeStretch(const void *value, void *clientData) +void TW_CALL getVolumeStiffness(void* value, void* clientData) { - const bool val = *(const bool *)(value); - ((SimulationModel*)clientData)->setValue(SimulationModel::SOLID_NORMALIZE_STRETCH, val); + *(Real*)(value) = volumeStiffness; } -void TW_CALL getNormalizeStretch(void *value, void *clientData) +void TW_CALL getPoissonRatio(void* value, void* clientData) { - *(bool *)(value) = ((SimulationModel*)clientData)->getValue(SimulationModel::SOLID_NORMALIZE_STRETCH); + *(Real*)(value) = poissonRatio; } -void TW_CALL setNormalizeShear(const void *value, void *clientData) +void TW_CALL setPoissonRatio(const void* value, void* clientData) { - const bool val = *(const bool *)(value); - ((SimulationModel*)clientData)->setValue(SimulationModel::SOLID_NORMALIZE_SHEAR, val); + poissonRatio = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(poissonRatio); } -void TW_CALL getNormalizeShear(void *value, void *clientData) +void TW_CALL getNormalizeStretch(void* value, void* clientData) { - *(bool *)(value) = ((SimulationModel*)clientData)->getValue(SimulationModel::SOLID_NORMALIZE_SHEAR); + *(bool*)(value) = normalizeStretch; } -void TW_CALL setSimulationMethod(const void *value, void *clientData) +void TW_CALL setNormalizeStretch(const void* value, void* clientData) { - const short val = *(const short *)(value); - *((short*)clientData) = val; - reset(); + normalizeStretch = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(normalizeStretch); } -void TW_CALL getSimulationMethod(void *value, void *clientData) +void TW_CALL getNormalizeShear(void* value, void* clientData) { - *(short *)(value) = *((short*)clientData); + *(bool*)(value) = normalizeShear; } + +void TW_CALL setNormalizeShear(const void* value, void* clientData) +{ + normalizeShear = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(normalizeShear); +} \ No newline at end of file diff --git a/Demos/CMakeLists.txt b/Demos/CMakeLists.txt index f378ca10..0ffaa501 100644 --- a/Demos/CMakeLists.txt +++ b/Demos/CMakeLists.txt @@ -15,7 +15,7 @@ set(PBD_DEMOS StiffRodsDemos ) -if (NOT PBD_NO_DEMOS) +if (NOT PBD_LIBS_ONLY) foreach (_demo_name ${PBD_DEMOS}) option(Build_${_demo_name} "Build ${_demo_name}" ON) if (Build_${_demo_name}) diff --git a/Demos/ClothDemo/CMakeLists.txt b/Demos/ClothDemo/CMakeLists.txt index 158617df..0af6bf95 100644 --- a/Demos/ClothDemo/CMakeLists.txt +++ b/Demos/ClothDemo/CMakeLists.txt @@ -1,5 +1,5 @@ set(SIMULATION_LINK_LIBRARIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) -set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) +set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils CopyPBDShaders) if(WIN32) set(SIMULATION_LINK_LIBRARIES opengl32.lib glu32.lib ${SIMULATION_LINK_LIBRARIES}) diff --git a/Demos/ClothDemo/main.cpp b/Demos/ClothDemo/main.cpp index e78f0a13..0fee82f3 100644 --- a/Demos/ClothDemo/main.cpp +++ b/Demos/ClothDemo/main.cpp @@ -29,11 +29,29 @@ void buildModel (); void createMesh(); void render (); void reset(); -void initParameters(); void TW_CALL setBendingMethod(const void *value, void *clientData); void TW_CALL getBendingMethod(void *value, void *clientData); void TW_CALL setSimulationMethod(const void *value, void *clientData); void TW_CALL getSimulationMethod(void *value, void *clientData); +void TW_CALL setBendingStiffness(const void* value, void* clientData); +void TW_CALL getBendingStiffness(void* value, void* clientData); +void TW_CALL setDistanceStiffness(const void* value, void* clientData); +void TW_CALL getDistanceStiffness(void* value, void* clientData); +void TW_CALL setXXStiffness(const void* value, void* clientData); +void TW_CALL getXXStiffness(void* value, void* clientData); +void TW_CALL setYYStiffness(const void* value, void* clientData); +void TW_CALL getYYStiffness(void* value, void* clientData); +void TW_CALL setXYStiffness(const void* value, void* clientData); +void TW_CALL getXYStiffness(void* value, void* clientData); +void TW_CALL setXYPoissonRatio(const void* value, void* clientData); +void TW_CALL getXYPoissonRatio(void* value, void* clientData); +void TW_CALL setYXPoissonRatio(const void* value, void* clientData); +void TW_CALL getYXPoissonRatio(void* value, void* clientData); +void TW_CALL setNormalizeStretch(const void* value, void* clientData); +void TW_CALL getNormalizeStretch(void* value, void* clientData); +void TW_CALL setNormalizeShear(const void* value, void* clientData); +void TW_CALL getNormalizeShear(void* value, void* clientData); + const int nRows = 50; const int nCols = 50; @@ -41,6 +59,15 @@ const Real width = 10.0; const Real height = 10.0; short simulationMethod = 2; short bendingMethod = 2; +Real distanceStiffness = 1.0; +Real xxStiffness = 1.0; +Real yyStiffness = 1.0; +Real xyStiffness = 1.0; +Real xyPoissonRatio = 0.3; +Real yxPoissonRatio = 0.3; +bool normalizeStretch = false; +bool normalizeShear = false; +Real bendingStiffness = 0.01; DemoBase *base; // main @@ -57,7 +84,7 @@ int main( int argc, char **argv ) buildModel(); - initParameters(); + base->createParameterGUI(); // OpenGL MiniGL::setClientIdleFunc (timeStep); @@ -71,6 +98,15 @@ int main( int argc, char **argv ) TwType enumType3 = TwDefineEnum("BendingMethodType", NULL, 0); TwAddVarCB(MiniGL::getTweakBar(), "BendingMethod", enumType3, setBendingMethod, getBendingMethod, &bendingMethod, " label='Bending method' enum='0 {None}, 1 {Dihedral angle}, 2 {Isometric bending}, 3 {XPBD isometric bending}' group=Bending"); + TwAddVarCB(MiniGL::getTweakBar(), "BendingStiffness", TW_TYPE_REAL, setBendingStiffness, getBendingStiffness, model, " label='Bending stiffness' min=0.0 step=0.1 precision=4 group='Bending' "); + TwAddVarCB(MiniGL::getTweakBar(), "DistanceStiffness", TW_TYPE_REAL, setDistanceStiffness, getDistanceStiffness, model, " label='Distance constraint stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "xxStiffness", TW_TYPE_REAL, setXXStiffness, getXXStiffness, model, " label='xx stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "yyStiffness", TW_TYPE_REAL, setYYStiffness, getYYStiffness, model, " label='yy stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "xyStiffness", TW_TYPE_REAL, setXYStiffness, getXYStiffness, model, " label='xy stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "xyPoissonRatio", TW_TYPE_REAL, setXYPoissonRatio, getXYPoissonRatio, model, " label='xy Poisson ratio' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "yxPoissonRatio", TW_TYPE_REAL, setYXPoissonRatio, getYXPoissonRatio, model, " label='yx Poisson ratio' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeStretch", TW_TYPE_BOOL32, setNormalizeStretch, getNormalizeStretch, model, " label='Normalize stretch' group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeShear", TW_TYPE_BOOL32, setNormalizeShear, getNormalizeShear, model, " label='Normalize shear' group='Cloth' "); MiniGL::mainLoop(); @@ -86,21 +122,6 @@ int main( int argc, char **argv ) return 0; } -void initParameters() -{ - TwRemoveAllVars(MiniGL::getTweakBar()); - TweakBarParameters::cleanup(); - - MiniGL::initTweakBarParameters(); - - TweakBarParameters::createParameterGUI(); - TweakBarParameters::createParameterObjectGUI(base); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getModel()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getTimeStep()); -} - - void reset() { Utilities::Timing::printAverageTimes(); @@ -155,189 +176,31 @@ void render () */ void createMesh() { - TriangleModel::ParticleMesh::UVs uvs; - uvs.resize(nRows*nCols); - - const Real dy = width / (Real)(nCols - 1); - const Real dx = height / (Real)(nRows - 1); - - Vector3r points[nRows*nCols]; - for (int i = 0; i < nRows; i++) - { - for (int j = 0; j < nCols; j++) - { - const Real y = (Real)dy*j; - const Real x = (Real)dx*i; - points[i*nCols + j] = Vector3r(x, 1.0, y); - - uvs[i*nCols + j][0] = x/width; - uvs[i*nCols + j][1] = y/height; - } - } - const int nIndices = 6 * (nRows - 1)*(nCols - 1); - - TriangleModel::ParticleMesh::UVIndices uvIndices; - uvIndices.resize(nIndices); - - unsigned int indices[nIndices]; - int index = 0; - for (int i = 0; i < nRows - 1; i++) - { - for (int j = 0; j < nCols - 1; j++) - { - int helper = 0; - if (i % 2 == j % 2) - helper = 1; - - indices[index] = i*nCols + j; - indices[index + 1] = i*nCols + j + 1; - indices[index + 2] = (i + 1)*nCols + j + helper; - - uvIndices[index] = i*nCols + j; - uvIndices[index + 1] = i*nCols + j + 1; - uvIndices[index + 2] = (i + 1)*nCols + j + helper; - index += 3; - - indices[index] = (i + 1)*nCols + j + 1; - indices[index + 1] = (i + 1)*nCols + j; - indices[index + 2] = i*nCols + j + 1 - helper; - - uvIndices[index] = (i + 1)*nCols + j + 1; - uvIndices[index + 1] = (i + 1)*nCols + j; - uvIndices[index + 2] = i*nCols + j + 1 - helper; - index += 3; - } - } - SimulationModel *model = Simulation::getCurrent()->getModel(); - model->addTriangleModel(nRows*nCols, nIndices / 3, &points[0], &indices[0], uvIndices, uvs); + model->addRegularTriangleModel(nCols, nRows, + Vector3r(0,1,0), AngleAxisr(M_PI*0.5, Vector3r(1,0,0)).matrix(), Vector2r(width, height)); - ParticleData &pd = model->getParticles(); - for (unsigned int i = 0; i < pd.getNumberOfParticles(); i++) - { - pd.setMass(i, 1.0); - } - // Set mass of points to zero => make it static + ParticleData& pd = model->getParticles(); pd.setMass(0, 0.0); - pd.setMass((nRows-1)*nCols, 0.0); + pd.setMass(nRows-1, 0.0); // init constraints for (unsigned int cm = 0; cm < model->getTriangleModels().size(); cm++) { - model->setValue(SimulationModel::CLOTH_STIFFNESS, 1.0); - model->setValue(SimulationModel::CLOTH_BENDING_STIFFNESS, 0.01); - if (simulationMethod == 1) - { - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - const unsigned int nEdges = model->getTriangleModels()[cm]->getParticleMesh().numEdges(); - const IndexedFaceMesh::Edge *edges = model->getTriangleModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint(v1, v2); - } - } - else if (simulationMethod == 2) - { - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - TriangleModel::ParticleMesh &mesh = model->getTriangleModels()[cm]->getParticleMesh(); - const unsigned int *tris = mesh.getFaces().data(); - const unsigned int nFaces = mesh.numFaces(); - for (unsigned int i = 0; i < nFaces; i++) - { - const unsigned int v1 = tris[3 * i] + offset; - const unsigned int v2 = tris[3 * i + 1] + offset; - const unsigned int v3 = tris[3 * i + 2] + offset; - model->addFEMTriangleConstraint(v1, v2, v3); - } - } - else if (simulationMethod == 3) - { - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - TriangleModel::ParticleMesh &mesh = model->getTriangleModels()[cm]->getParticleMesh(); - const unsigned int *tris = mesh.getFaces().data(); - const unsigned int nFaces = mesh.numFaces(); - for (unsigned int i = 0; i < nFaces; i++) - { - const unsigned int v1 = tris[3 * i] + offset; - const unsigned int v2 = tris[3 * i + 1] + offset; - const unsigned int v3 = tris[3 * i + 2] + offset; - model->addStrainTriangleConstraint(v1, v2, v3); - } - } - else if (simulationMethod == 4) - { - model->setValue(SimulationModel::CLOTH_STIFFNESS, 100000); - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - const unsigned int nEdges = model->getTriangleModels()[cm]->getParticleMesh().numEdges(); - const IndexedFaceMesh::Edge* edges = model->getTriangleModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint_XPBD(v1, v2); - } - } - if (bendingMethod != 0) - { - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - TriangleModel::ParticleMesh &mesh = model->getTriangleModels()[cm]->getParticleMesh(); - unsigned int nEdges = mesh.numEdges(); - const TriangleModel::ParticleMesh::Edge *edges = mesh.getEdges().data(); - const unsigned int *tris = mesh.getFaces().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const int tri1 = edges[i].m_face[0]; - const int tri2 = edges[i].m_face[1]; - if ((tri1 != 0xffffffff) && (tri2 != 0xffffffff)) - { - // Find the triangle points which do not lie on the axis - const int axisPoint1 = edges[i].m_vert[0]; - const int axisPoint2 = edges[i].m_vert[1]; - int point1 = -1; - int point2 = -1; - for (int j = 0; j < 3; j++) - { - if ((tris[3 * tri1 + j] != axisPoint1) && (tris[3 * tri1 + j] != axisPoint2)) - { - point1 = tris[3 * tri1 + j]; - break; - } - } - for (int j = 0; j < 3; j++) - { - if ((tris[3 * tri2 + j] != axisPoint1) && (tris[3 * tri2 + j] != axisPoint2)) - { - point2 = tris[3 * tri2 + j]; - break; - } - } - if ((point1 != -1) && (point2 != -1)) - { - const unsigned int vertex1 = point1 + offset; - const unsigned int vertex2 = point2 + offset; - const unsigned int vertex3 = edges[i].m_vert[0] + offset; - const unsigned int vertex4 = edges[i].m_vert[1] + offset; - if (bendingMethod == 1) - model->addDihedralConstraint(vertex1, vertex2, vertex3, vertex4); - else if (bendingMethod == 2) - model->addIsometricBendingConstraint(vertex1, vertex2, vertex3, vertex4); - else if (bendingMethod == 3) - { - model->setValue(SimulationModel::CLOTH_BENDING_STIFFNESS, 100.0); - model->addIsometricBendingConstraint_XPBD(vertex1, vertex2, vertex3, vertex4); - } - } - } - } - } + distanceStiffness = 1.0; + if (simulationMethod == 4) + distanceStiffness = 100000; + model->addClothConstraints(model->getTriangleModels()[cm], simulationMethod, distanceStiffness, xxStiffness, + yyStiffness, xyStiffness, xyPoissonRatio, yxPoissonRatio, normalizeStretch, normalizeShear); + + bendingStiffness = 0.01; + if (bendingMethod == 3) + bendingStiffness = 100.0; + model->addBendingConstraints(model->getTriangleModels()[cm], bendingMethod, bendingStiffness); } - LOG_INFO << "Number of triangles: " << nIndices / 3; + LOG_INFO << "Number of triangles: " << model->getTriangleModels()[0]->getParticleMesh().numFaces(); LOG_INFO << "Number of vertices: " << nRows*nCols; } @@ -366,3 +229,107 @@ void TW_CALL getSimulationMethod(void *value, void *clientData) *(short *)(value) = *((short*)clientData); } +void TW_CALL setBendingStiffness(const void* value, void* clientData) +{ + bendingStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); +} + +void TW_CALL getBendingStiffness(void* value, void* clientData) +{ + *(Real*)(value) = bendingStiffness; +} + +void TW_CALL setDistanceStiffness(const void* value, void* clientData) +{ + distanceStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(distanceStiffness); + ((SimulationModel*)clientData)->setConstraintValue(distanceStiffness); +} + +void TW_CALL getDistanceStiffness(void* value, void* clientData) +{ + *(Real*)(value) = distanceStiffness; +} + +void TW_CALL setXXStiffness(const void* value, void* clientData) +{ + xxStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(xxStiffness); + ((SimulationModel*)clientData)->setConstraintValue(xxStiffness); +} + +void TW_CALL getXXStiffness(void* value, void* clientData) +{ + *(Real*)(value) = xxStiffness; +} + +void TW_CALL getYYStiffness(void* value, void* clientData) +{ + *(Real*)(value) = yyStiffness; +} + +void TW_CALL setYYStiffness(const void* value, void* clientData) +{ + yyStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(yyStiffness); + ((SimulationModel*)clientData)->setConstraintValue(yyStiffness); +} + +void TW_CALL getXYStiffness(void* value, void* clientData) +{ + *(Real*)(value) = xyStiffness; +} + +void TW_CALL setXYStiffness(const void* value, void* clientData) +{ + xyStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(xyStiffness); + ((SimulationModel*)clientData)->setConstraintValue(xyStiffness); +} + +void TW_CALL getXYPoissonRatio(void* value, void* clientData) +{ + *(Real*)(value) = xyPoissonRatio; +} + +void TW_CALL setXYPoissonRatio(const void* value, void* clientData) +{ + xyPoissonRatio = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(xyPoissonRatio); +} + +void TW_CALL getYXPoissonRatio(void* value, void* clientData) +{ + *(Real*)(value) = yxPoissonRatio; +} + +void TW_CALL setYXPoissonRatio(const void* value, void* clientData) +{ + yxPoissonRatio = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(yxPoissonRatio); +} + +void TW_CALL getNormalizeStretch(void* value, void* clientData) +{ + *(bool*)(value) = normalizeStretch; +} + +void TW_CALL setNormalizeStretch(const void* value, void* clientData) +{ + normalizeStretch = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(normalizeStretch); +} + +void TW_CALL getNormalizeShear(void* value, void* clientData) +{ + *(bool*)(value) = normalizeShear; +} + +void TW_CALL setNormalizeShear(const void* value, void* clientData) +{ + normalizeShear= *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(normalizeShear); +} \ No newline at end of file diff --git a/Demos/Common/DemoBase.cpp b/Demos/Common/DemoBase.cpp index 678a02f4..be54a97b 100644 --- a/Demos/Common/DemoBase.cpp +++ b/Demos/Common/DemoBase.cpp @@ -12,6 +12,7 @@ #include "Utils/SystemInfo.h" #include "../Visualization/Visualization.h" #include "Simulation/DistanceFieldCollisionDetection.h" +#include "Demos/Common/TweakBarParameters.h" INIT_LOGGING INIT_TIMING @@ -109,6 +110,20 @@ void DemoBase::initParameters() } +void DemoBase::createParameterGUI() +{ + TwRemoveAllVars(MiniGL::getTweakBar()); + TweakBarParameters::cleanup(); + + MiniGL::initTweakBarParameters(); + + TweakBarParameters::createParameterGUI(); + TweakBarParameters::createParameterObjectGUI(this); + TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()); + TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getModel()); + TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getTimeStep()); +} + void DemoBase::cleanup() { m_scene.m_rigidBodyData.clear(); @@ -134,9 +149,8 @@ void DemoBase::init(int argc, char **argv, const char *demoName) { initParameters(); m_exePath = FileSystem::getProgramPath(); - m_dataPath = FileSystem::normalizePath(getExePath() + "/" + std::string(PBD_DATA_PATH)); - m_sceneFile = getDataPath() + "/scenes/DeformableSolidCollisionScene.json"; + m_sceneFile = ""; setUseCache(true); for (int i = 1; i < argc; i++) { @@ -171,6 +185,7 @@ void DemoBase::init(int argc, char **argv, const char *demoName) LOG_DEBUG << "Git SHA1: " << GIT_SHA1; LOG_DEBUG << "Git status: " << GIT_LOCAL_STATUS; LOG_DEBUG << "Host name: " << SystemInfo::getHostName(); + LOG_INFO << "PositionBasedDynamics " << PBD_VERSION; // OpenGL MiniGL::init(argc, argv, 1280, 1024, demoName); @@ -205,8 +220,8 @@ void DemoBase::readScene() void DemoBase::initShaders() { - std::string vertFile = m_dataPath + "/shaders/vs_smooth.glsl"; - std::string fragFile = m_dataPath + "/shaders/fs_smooth.glsl"; + std::string vertFile = m_exePath + "/resources/shaders/vs_smooth.glsl"; + std::string fragFile = m_exePath + "/resources/shaders/fs_smooth.glsl"; m_shader.compileShaderFile(GL_VERTEX_SHADER, vertFile); m_shader.compileShaderFile(GL_FRAGMENT_SHADER, fragFile); m_shader.createAndLinkProgram(); @@ -218,8 +233,8 @@ void DemoBase::initShaders() m_shader.addUniform("specular_factor"); m_shader.end(); - vertFile = m_dataPath + "/shaders/vs_smoothTex.glsl"; - fragFile = m_dataPath + "/shaders/fs_smoothTex.glsl"; + vertFile = m_exePath + "/resources/shaders/vs_smoothTex.glsl"; + fragFile = m_exePath + "/resources/shaders/fs_smoothTex.glsl"; m_shaderTex.compileShaderFile(GL_VERTEX_SHADER, vertFile); m_shaderTex.compileShaderFile(GL_FRAGMENT_SHADER, fragFile); m_shaderTex.createAndLinkProgram(); @@ -231,9 +246,9 @@ void DemoBase::initShaders() m_shaderTex.addUniform("specular_factor"); m_shaderTex.end(); - vertFile = m_dataPath + "/shaders/vs_flat.glsl"; - std::string geomFile = m_dataPath + "/shaders/gs_flat.glsl"; - fragFile = m_dataPath + "/shaders/fs_flat.glsl"; + vertFile = m_exePath + "/resources/shaders/vs_flat.glsl"; + std::string geomFile = m_exePath + "/resources/shaders/gs_flat.glsl"; + fragFile = m_exePath + "/resources/shaders/fs_flat.glsl"; m_shaderFlat.compileShaderFile(GL_VERTEX_SHADER, vertFile); m_shaderFlat.compileShaderFile(GL_GEOMETRY_SHADER, geomFile); m_shaderFlat.compileShaderFile(GL_FRAGMENT_SHADER, fragFile); diff --git a/Demos/Common/DemoBase.h b/Demos/Common/DemoBase.h index 334912a3..6dd8b5a8 100644 --- a/Demos/Common/DemoBase.h +++ b/Demos/Common/DemoBase.h @@ -16,7 +16,6 @@ namespace PBD protected: unsigned int m_numberOfStepsPerRenderUpdate; std::string m_exePath; - std::string m_dataPath; std::string m_outputPath; std::string m_sceneFile; std::string m_sceneName; @@ -87,6 +86,8 @@ namespace PBD void init(int argc, char **argv, const char *demoName); + void createParameterGUI(); + void render(); void cleanup(); @@ -98,7 +99,6 @@ namespace PBD void setSceneLoader(Utilities::SceneLoader *sceneLoader) { m_sceneLoader = sceneLoader; } const std::string& getExePath() const { return m_exePath; } - const std::string& getDataPath() const { return m_dataPath; } const std::string& getSceneFile() const { return m_sceneFile; } const std::string& getSceneName() const { return m_sceneName; } diff --git a/Demos/CosseratRodsDemo/CMakeLists.txt b/Demos/CosseratRodsDemo/CMakeLists.txt index df2f3eb4..298d8225 100644 --- a/Demos/CosseratRodsDemo/CMakeLists.txt +++ b/Demos/CosseratRodsDemo/CMakeLists.txt @@ -1,5 +1,5 @@ set(SIMULATION_LINK_LIBRARIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) -set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) +set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils CopyPBDShaders) if(WIN32) set(SIMULATION_LINK_LIBRARIES opengl32.lib glu32.lib ${SIMULATION_LINK_LIBRARIES}) diff --git a/Demos/CosseratRodsDemo/main.cpp b/Demos/CosseratRodsDemo/main.cpp index 80dcf7a7..53eee1b4 100644 --- a/Demos/CosseratRodsDemo/main.cpp +++ b/Demos/CosseratRodsDemo/main.cpp @@ -51,6 +51,13 @@ const Real helixHeight = -5.0; const Real helixTotalAngle = static_cast(10.0*M_PI); const Matrix3r helixOrientation = AngleAxisr(-static_cast(0.5 * M_PI), Vector3r(0,1,0)).toRotationMatrix(); bool drawFrames = false; +Real shearingStiffness1 = 1.0; +Real shearingStiffness2 = 1.0; +Real stretchingStiffness = 1.0; +Real bendingStiffness1 = 0.5; +Real bendingStiffness2 = 0.5; +Real twistingStiffness = 0.5; + // main int main( int argc, char **argv ) @@ -280,7 +287,7 @@ void createHelix(const Vector3r &position, const Matrix3r &orientation, Real rad const unsigned int v1 = edges[i].m_vert[0] + offset; const unsigned int v2 = edges[i].m_vert[1] + offset; const unsigned int q1 = edges[i].m_quat + offsetQuaternions; - model->addStretchShearConstraint(v1, v2, q1); + model->addStretchShearConstraint(v1, v2, q1, stretchingStiffness, shearingStiffness1, shearingStiffness2); } //bendTwist constraints @@ -288,7 +295,7 @@ void createHelix(const Vector3r &position, const Matrix3r &orientation, Real rad { const unsigned int q1 = edges[i].m_quat + offsetQuaternions; const unsigned int q2 = edges[i + 1].m_quat + offsetQuaternions; - model->addBendTwistConstraint(q1, q2); + model->addBendTwistConstraint(q1, q2, twistingStiffness, bendingStiffness1, bendingStiffness2); } // LOG_INFO << "Number of particles: " << nPoints; @@ -297,66 +304,66 @@ void createHelix(const Vector3r &position, const Matrix3r &orientation, Real rad void TW_CALL setStretchingStiffness(const void *value, void *clientData) { - const Real val = *(const Real *)(value); - ((SimulationModel*)clientData)->setRodStretchingStiffness(val); + stretchingStiffness = *(const Real *)(value); + ((SimulationModel*)clientData)->setConstraintValue(stretchingStiffness); } void TW_CALL getStretchingStiffness(void *value, void *clientData) { - *(Real *)(value) = ((SimulationModel*)clientData)->getRodStretchingStiffness(); + *(Real *)(value) = stretchingStiffness; } void TW_CALL setShearingStiffness1(const void *value, void *clientData) { - const Real val = *(const Real *)(value); - ((SimulationModel*)clientData)->setRodShearingStiffness1(val); + shearingStiffness1 = *(const Real *)(value); + ((SimulationModel*)clientData)->setConstraintValue(shearingStiffness1); } void TW_CALL getShearingStiffness1(void *value, void *clientData) { - *(Real *)(value) = ((SimulationModel*)clientData)->getRodShearingStiffness1(); + *(Real *)(value) = shearingStiffness1; } void TW_CALL setShearingStiffness2(const void *value, void *clientData) { - const Real val = *(const Real *)(value); - ((SimulationModel*)clientData)->setRodShearingStiffness2(val); + shearingStiffness2 = *(const Real *)(value); + ((SimulationModel*)clientData)->setConstraintValue(shearingStiffness2); } void TW_CALL getShearingStiffness2(void *value, void *clientData) { - *(Real *)(value) = ((SimulationModel*)clientData)->getRodShearingStiffness2(); + *(Real *)(value) = shearingStiffness2; } void TW_CALL setBendingStiffness1(const void *value, void *clientData) { - const Real val = *(const Real *)(value); - ((SimulationModel*)clientData)->setRodBendingStiffness1(val); + bendingStiffness1 = *(const Real *)(value); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness1); } void TW_CALL getBendingStiffness1(void *value, void *clientData) { - *(Real *)(value) = ((SimulationModel*)clientData)->getRodBendingStiffness1(); + *(Real *)(value) = bendingStiffness1; } void TW_CALL setBendingStiffness2(const void *value, void *clientData) { - const Real val = *(const Real *)(value); - ((SimulationModel*)clientData)->setRodBendingStiffness2(val); + bendingStiffness2 = *(const Real *)(value); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness2); } void TW_CALL getBendingStiffness2(void *value, void *clientData) { - *(Real *)(value) = ((SimulationModel*)clientData)->getRodBendingStiffness2(); + *(Real *)(value) = bendingStiffness2; } void TW_CALL setTwistingStiffness(const void *value, void *clientData) { - const Real val = *(const Real *)(value); - ((SimulationModel*)clientData)->setRodTwistingStiffness(val); + twistingStiffness = *(const Real *)(value); + ((SimulationModel*)clientData)->setConstraintValue(twistingStiffness); } void TW_CALL getTwistingStiffness(void *value, void *clientData) { - *(Real *)(value) = ((SimulationModel*)clientData)->getRodTwistingStiffness(); + *(Real *)(value) = twistingStiffness; } \ No newline at end of file diff --git a/Demos/CouplingDemos/CMakeLists.txt b/Demos/CouplingDemos/CMakeLists.txt index 8a2c6b19..0bbd8f1e 100644 --- a/Demos/CouplingDemos/CMakeLists.txt +++ b/Demos/CouplingDemos/CMakeLists.txt @@ -1,5 +1,5 @@ set(SIMULATION_LINK_LIBRARIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) -set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) +set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils CopyPBDShaders CopyPBDModels) if(WIN32) set(SIMULATION_LINK_LIBRARIES opengl32.lib glu32.lib ${SIMULATION_LINK_LIBRARIES}) diff --git a/Demos/CouplingDemos/RigidBodyClothCouplingDemo.cpp b/Demos/CouplingDemos/RigidBodyClothCouplingDemo.cpp index cd0e7df5..1f8ab83e 100644 --- a/Demos/CouplingDemos/RigidBodyClothCouplingDemo.cpp +++ b/Demos/CouplingDemos/RigidBodyClothCouplingDemo.cpp @@ -29,7 +29,6 @@ using namespace Eigen; using namespace std; using namespace Utilities; -void initParameters(); void timeStep (); void buildModel (); void createRigidBodyModel(); @@ -40,6 +39,24 @@ void TW_CALL setBendingMethod(const void *value, void *clientData); void TW_CALL getBendingMethod(void *value, void *clientData); void TW_CALL setSimulationMethod(const void *value, void *clientData); void TW_CALL getSimulationMethod(void *value, void *clientData); +void TW_CALL setBendingStiffness(const void* value, void* clientData); +void TW_CALL getBendingStiffness(void* value, void* clientData); +void TW_CALL setDistanceStiffness(const void* value, void* clientData); +void TW_CALL getDistanceStiffness(void* value, void* clientData); +void TW_CALL setXXStiffness(const void* value, void* clientData); +void TW_CALL getXXStiffness(void* value, void* clientData); +void TW_CALL setYYStiffness(const void* value, void* clientData); +void TW_CALL getYYStiffness(void* value, void* clientData); +void TW_CALL setXYStiffness(const void* value, void* clientData); +void TW_CALL getXYStiffness(void* value, void* clientData); +void TW_CALL setXYPoissonRatio(const void* value, void* clientData); +void TW_CALL getXYPoissonRatio(void* value, void* clientData); +void TW_CALL setYXPoissonRatio(const void* value, void* clientData); +void TW_CALL getYXPoissonRatio(void* value, void* clientData); +void TW_CALL setNormalizeStretch(const void* value, void* clientData); +void TW_CALL getNormalizeStretch(void* value, void* clientData); +void TW_CALL setNormalizeShear(const void* value, void* clientData); +void TW_CALL getNormalizeShear(void* value, void* clientData); DemoBase *base; const int nRows = 20; @@ -51,6 +68,15 @@ const Real height = static_cast(2.0); const Real depth = static_cast(0.2); short simulationMethod = 2; short bendingMethod = 2; +Real distanceStiffness = 1.0; +Real xxStiffness = 1.0; +Real yyStiffness = 1.0; +Real xyStiffness = 1.0; +Real xyPoissonRatio = 0.3; +Real yxPoissonRatio = 0.3; +bool normalizeStretch = false; +bool normalizeShear = false; +Real bendingStiffness = 0.01; // main @@ -67,7 +93,7 @@ int main( int argc, char **argv ) buildModel(); - initParameters(); + base->createParameterGUI(); // OpenGL MiniGL::setClientIdleFunc (timeStep); @@ -79,9 +105,19 @@ int main( int argc, char **argv ) TwAddVarCB(MiniGL::getTweakBar(), "SimulationMethod", enumType2, setSimulationMethod, getSimulationMethod, &simulationMethod, " label='Simulation method' enum='0 {None}, 1 {Distance constraints}, 2 {FEM based PBD}, 3 {Strain based dynamics}, 4 {XPBD distance constraints}' group=Simulation"); TwType enumType3 = TwDefineEnum("BendingMethodType", NULL, 0); - TwAddVarCB(MiniGL::getTweakBar(), "BendingMethod", enumType3, setBendingMethod, getBendingMethod, &bendingMethod, " label='Bending method' enum='0 {None}, 1 {Dihedral angle}, 2 {Isometric bending}, 3 {XPBD isometric bending}' group=Bending"); - - MiniGL::mainLoop (); + TwAddVarCB(MiniGL::getTweakBar(), "BendingMethod", enumType3, setBendingMethod, getBendingMethod, &bendingMethod, + " label='Bending method' enum='0 {None}, 1 {Dihedral angle}, 2 {Isometric bending}, 3 {XPBD isometric bending}' group=Bending"); + TwAddVarCB(MiniGL::getTweakBar(), "BendingStiffness", TW_TYPE_REAL, setBendingStiffness, getBendingStiffness, model, " label='Bending stiffness' min=0.0 step=0.1 precision=4 group='Bending' "); + TwAddVarCB(MiniGL::getTweakBar(), "DistanceStiffness", TW_TYPE_REAL, setDistanceStiffness, getDistanceStiffness, model, " label='Distance constraint stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "xxStiffness", TW_TYPE_REAL, setXXStiffness, getXXStiffness, model, " label='xx stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "yyStiffness", TW_TYPE_REAL, setYYStiffness, getYYStiffness, model, " label='yy stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "xyStiffness", TW_TYPE_REAL, setXYStiffness, getXYStiffness, model, " label='xy stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "xyPoissonRatio", TW_TYPE_REAL, setXYPoissonRatio, getXYPoissonRatio, model, " label='xy Poisson ratio' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "yxPoissonRatio", TW_TYPE_REAL, setYXPoissonRatio, getYXPoissonRatio, model, " label='yx Poisson ratio' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeStretch", TW_TYPE_BOOL32, setNormalizeStretch, getNormalizeStretch, model, " label='Normalize stretch' group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeShear", TW_TYPE_BOOL32, setNormalizeShear, getNormalizeShear, model, " label='Normalize shear' group='Cloth' "); + + MiniGL::mainLoop(); base->cleanup(); @@ -91,22 +127,8 @@ int main( int argc, char **argv ) delete Simulation::getCurrent(); delete base; delete model; - - return 0; -} - -void initParameters() -{ - TwRemoveAllVars(MiniGL::getTweakBar()); - TweakBarParameters::cleanup(); - - MiniGL::initTweakBarParameters(); - TweakBarParameters::createParameterGUI(); - TweakBarParameters::createParameterObjectGUI(base); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getModel()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getTimeStep()); + return 0; } void reset() @@ -122,6 +144,7 @@ void reset() buildModel(); } + void timeStep () { const Real pauseAt = base->getValue(DemoBase::PAUSE_AT); @@ -222,9 +245,8 @@ void createRigidBodyModel() { SimulationModel *model = Simulation::getCurrent()->getModel(); SimulationModel::RigidBodyVector &rb = model->getRigidBodies(); - SimulationModel::ConstraintVector &constraints = model->getConstraints(); - - string fileName = FileSystem::normalizePath(base->getDataPath() + "/models/cube.obj"); + + string fileName = FileSystem::normalizePath(base->getExePath() + "/resources/models/cube.obj"); IndexedFaceMesh mesh; VertexData vd; loadObj(fileName, vd, mesh, Vector3r(width, height, depth)); @@ -355,9 +377,9 @@ void createRigidBodyModel() model->addRigidBodyParticleBallJoint(2, 0); - model->addRigidBodyParticleBallJoint(5, (nRows - 1)*nCols); + model->addRigidBodyParticleBallJoint(5, nCols - 1); model->addRigidBodyParticleBallJoint(8, nRows*nCols - 1); - model->addRigidBodyParticleBallJoint(11, nCols-1); + model->addRigidBodyParticleBallJoint(11, (nRows -1)*nCols); } @@ -366,185 +388,27 @@ void createRigidBodyModel() */ void createClothMesh() { - TriangleModel::ParticleMesh::UVs uvs; - uvs.resize(nRows*nCols); - - const Real dy = clothWidth / (Real)(nCols - 1); - const Real dx = clothHeight / (Real)(nRows - 1); - - Vector3r points[nRows*nCols]; - for (int i = 0; i < nRows; i++) - { - for (int j = 0; j < nCols; j++) - { - const Real y = (Real)dy*j; - const Real x = (Real)dx*i; - points[i*nCols + j] = Vector3r(x - static_cast(5.0), static_cast(4.0), y - static_cast(5.0)); - - uvs[i*nCols + j][0] = x / clothWidth; - uvs[i*nCols + j][1] = y / clothHeight; - } - } - const int nIndices = 6 * (nRows - 1)*(nCols - 1); - - TriangleModel::ParticleMesh::UVIndices uvIndices; - uvIndices.resize(nIndices); - - unsigned int indices[nIndices]; - int index = 0; - for (int i = 0; i < nRows - 1; i++) - { - for (int j = 0; j < nCols - 1; j++) - { - int helper = 0; - if (i % 2 == j % 2) - helper = 1; - - indices[index] = i*nCols + j; - indices[index + 1] = i*nCols + j + 1; - indices[index + 2] = (i + 1)*nCols + j + helper; - - uvIndices[index] = i*nCols + j; - uvIndices[index + 1] = i*nCols + j + 1; - uvIndices[index + 2] = (i + 1)*nCols + j + helper; - index += 3; - - indices[index] = (i + 1)*nCols + j + 1; - indices[index + 1] = (i + 1)*nCols + j; - indices[index + 2] = i*nCols + j + 1 - helper; - - uvIndices[index] = (i + 1)*nCols + j + 1; - uvIndices[index + 1] = (i + 1)*nCols + j; - uvIndices[index + 2] = i*nCols + j + 1 - helper; - index += 3; - } - } - - SimulationModel *model = Simulation::getCurrent()->getModel(); - model->addTriangleModel(nRows*nCols, nIndices / 3, &points[0], &indices[0], uvIndices, uvs); + SimulationModel* model = Simulation::getCurrent()->getModel(); + model->addRegularTriangleModel(nCols, nRows, + Vector3r(-5, 4, -5), AngleAxisr(M_PI * 0.5, Vector3r(1, 0, 0)).matrix(), Vector2r(clothWidth, clothHeight)); - ParticleData &pd = model->getParticles(); - for (unsigned int i = 0; i < pd.getNumberOfParticles(); i++) - { - pd.setMass(i, 1.0); - } // init constraints for (unsigned int cm = 0; cm < model->getTriangleModels().size(); cm++) { - model->setValue(SimulationModel::CLOTH_STIFFNESS, 1.0); - model->setValue(SimulationModel::CLOTH_BENDING_STIFFNESS, 0.01); - if (simulationMethod == 1) - { - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - const unsigned int nEdges = model->getTriangleModels()[cm]->getParticleMesh().numEdges(); - const IndexedFaceMesh::Edge *edges = model->getTriangleModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint(v1, v2); - } - } - else if (simulationMethod == 2) - { - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - TriangleModel::ParticleMesh &mesh = model->getTriangleModels()[cm]->getParticleMesh(); - const unsigned int *tris = mesh.getFaces().data(); - const unsigned int nFaces = mesh.numFaces(); - for (unsigned int i = 0; i < nFaces; i++) - { - const unsigned int v1 = tris[3 * i] + offset; - const unsigned int v2 = tris[3 * i + 1] + offset; - const unsigned int v3 = tris[3 * i + 2] + offset; - model->addFEMTriangleConstraint(v1, v2, v3); - } - } - else if (simulationMethod == 3) - { - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - TriangleModel::ParticleMesh &mesh = model->getTriangleModels()[cm]->getParticleMesh(); - const unsigned int *tris = mesh.getFaces().data(); - const unsigned int nFaces = mesh.numFaces(); - for (unsigned int i = 0; i < nFaces; i++) - { - const unsigned int v1 = tris[3 * i] + offset; - const unsigned int v2 = tris[3 * i + 1] + offset; - const unsigned int v3 = tris[3 * i + 2] + offset; - model->addStrainTriangleConstraint(v1, v2, v3); - } - } - else if (simulationMethod == 4) - { - model->setValue(SimulationModel::CLOTH_STIFFNESS, 100000); - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - const unsigned int nEdges = model->getTriangleModels()[cm]->getParticleMesh().numEdges(); - const IndexedFaceMesh::Edge* edges = model->getTriangleModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint_XPBD(v1, v2); - } - } - if (bendingMethod != 0) - { - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - TriangleModel::ParticleMesh &mesh = model->getTriangleModels()[cm]->getParticleMesh(); - unsigned int nEdges = mesh.numEdges(); - const TriangleModel::ParticleMesh::Edge *edges = mesh.getEdges().data(); - const unsigned int *tris = mesh.getFaces().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const int tri1 = edges[i].m_face[0]; - const int tri2 = edges[i].m_face[1]; - if ((tri1 != 0xffffffff) && (tri2 != 0xffffffff)) - { - // Find the triangle points which do not lie on the axis - const int axisPoint1 = edges[i].m_vert[0]; - const int axisPoint2 = edges[i].m_vert[1]; - int point1 = -1; - int point2 = -1; - for (int j = 0; j < 3; j++) - { - if ((tris[3 * tri1 + j] != axisPoint1) && (tris[3 * tri1 + j] != axisPoint2)) - { - point1 = tris[3 * tri1 + j]; - break; - } - } - for (int j = 0; j < 3; j++) - { - if ((tris[3 * tri2 + j] != axisPoint1) && (tris[3 * tri2 + j] != axisPoint2)) - { - point2 = tris[3 * tri2 + j]; - break; - } - } - if ((point1 != -1) && (point2 != -1)) - { - const unsigned int vertex1 = point1 + offset; - const unsigned int vertex2 = point2 + offset; - const unsigned int vertex3 = edges[i].m_vert[0] + offset; - const unsigned int vertex4 = edges[i].m_vert[1] + offset; - if (bendingMethod == 1) - model->addDihedralConstraint(vertex1, vertex2, vertex3, vertex4); - else if (bendingMethod == 2) - model->addIsometricBendingConstraint(vertex1, vertex2, vertex3, vertex4); - else if (bendingMethod == 3) - { - model->setValue(SimulationModel::CLOTH_BENDING_STIFFNESS, 100.0); - model->addIsometricBendingConstraint_XPBD(vertex1, vertex2, vertex3, vertex4); - } - } - } - } - } + distanceStiffness = 1.0; + if (simulationMethod == 4) + distanceStiffness = 100000; + model->addClothConstraints(model->getTriangleModels()[cm], simulationMethod, distanceStiffness, xxStiffness, + yyStiffness, xyStiffness, xyPoissonRatio, yxPoissonRatio, normalizeStretch, normalizeShear); + + bendingStiffness = 0.01; + if (bendingMethod == 3) + bendingStiffness = 100.0; + model->addBendingConstraints(model->getTriangleModels()[cm], bendingMethod, bendingStiffness); } - LOG_INFO << "Number of triangles: " << nIndices / 3; + LOG_INFO << "Number of triangles: " << model->getTriangleModels()[0]->getParticleMesh().numFaces(); LOG_INFO << "Number of vertices: " << nRows*nCols; } @@ -571,4 +435,109 @@ void TW_CALL setSimulationMethod(const void *value, void *clientData) void TW_CALL getSimulationMethod(void *value, void *clientData) { *(short *)(value) = *((short*)clientData); +} + +void TW_CALL setBendingStiffness(const void* value, void* clientData) +{ + bendingStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); +} + +void TW_CALL getBendingStiffness(void* value, void* clientData) +{ + *(Real*)(value) = bendingStiffness; +} + +void TW_CALL setDistanceStiffness(const void* value, void* clientData) +{ + distanceStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(distanceStiffness); + ((SimulationModel*)clientData)->setConstraintValue(distanceStiffness); +} + +void TW_CALL getDistanceStiffness(void* value, void* clientData) +{ + *(Real*)(value) = distanceStiffness; +} + +void TW_CALL setXXStiffness(const void* value, void* clientData) +{ + xxStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(xxStiffness); + ((SimulationModel*)clientData)->setConstraintValue(xxStiffness); +} + +void TW_CALL getXXStiffness(void* value, void* clientData) +{ + *(Real*)(value) = xxStiffness; +} + +void TW_CALL getYYStiffness(void* value, void* clientData) +{ + *(Real*)(value) = yyStiffness; +} + +void TW_CALL setYYStiffness(const void* value, void* clientData) +{ + yyStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(yyStiffness); + ((SimulationModel*)clientData)->setConstraintValue(yyStiffness); +} + +void TW_CALL getXYStiffness(void* value, void* clientData) +{ + *(Real*)(value) = xyStiffness; +} + +void TW_CALL setXYStiffness(const void* value, void* clientData) +{ + xyStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(xyStiffness); + ((SimulationModel*)clientData)->setConstraintValue(xyStiffness); +} + +void TW_CALL getXYPoissonRatio(void* value, void* clientData) +{ + *(Real*)(value) = xyPoissonRatio; +} + +void TW_CALL setXYPoissonRatio(const void* value, void* clientData) +{ + xyPoissonRatio = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(xyPoissonRatio); +} + +void TW_CALL getYXPoissonRatio(void* value, void* clientData) +{ + *(Real*)(value) = yxPoissonRatio; +} + +void TW_CALL setYXPoissonRatio(const void* value, void* clientData) +{ + yxPoissonRatio = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(yxPoissonRatio); +} + +void TW_CALL getNormalizeStretch(void* value, void* clientData) +{ + *(bool*)(value) = normalizeStretch; +} + +void TW_CALL setNormalizeStretch(const void* value, void* clientData) +{ + normalizeStretch = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(normalizeStretch); +} + +void TW_CALL getNormalizeShear(void* value, void* clientData) +{ + *(bool*)(value) = normalizeShear; +} + +void TW_CALL setNormalizeShear(const void* value, void* clientData) +{ + normalizeShear= *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(normalizeShear); } \ No newline at end of file diff --git a/Demos/DistanceFieldDemos/CMakeLists.txt b/Demos/DistanceFieldDemos/CMakeLists.txt index b8f91c70..f90439ae 100644 --- a/Demos/DistanceFieldDemos/CMakeLists.txt +++ b/Demos/DistanceFieldDemos/CMakeLists.txt @@ -1,5 +1,5 @@ set(SIMULATION_LINK_LIBRARIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) -set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) +set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils CopyPBDShaders CopyPBDModels) if(WIN32) set(SIMULATION_LINK_LIBRARIES opengl32.lib glu32.lib ${SIMULATION_LINK_LIBRARIES}) diff --git a/Demos/DistanceFieldDemos/ClothCollisionDemo.cpp b/Demos/DistanceFieldDemos/ClothCollisionDemo.cpp index ea18d3df..e481a783 100644 --- a/Demos/DistanceFieldDemos/ClothCollisionDemo.cpp +++ b/Demos/DistanceFieldDemos/ClothCollisionDemo.cpp @@ -32,11 +32,28 @@ void buildModel (); void createMesh(); void render (); void reset(); -void initParameters(); void TW_CALL setBendingMethod(const void *value, void *clientData); void TW_CALL getBendingMethod(void *value, void *clientData); void TW_CALL setSimulationMethod(const void *value, void *clientData); void TW_CALL getSimulationMethod(void *value, void *clientData); +void TW_CALL setBendingStiffness(const void* value, void* clientData); +void TW_CALL getBendingStiffness(void* value, void* clientData); +void TW_CALL setDistanceStiffness(const void* value, void* clientData); +void TW_CALL getDistanceStiffness(void* value, void* clientData); +void TW_CALL setXXStiffness(const void* value, void* clientData); +void TW_CALL getXXStiffness(void* value, void* clientData); +void TW_CALL setYYStiffness(const void* value, void* clientData); +void TW_CALL getYYStiffness(void* value, void* clientData); +void TW_CALL setXYStiffness(const void* value, void* clientData); +void TW_CALL getXYStiffness(void* value, void* clientData); +void TW_CALL setXYPoissonRatio(const void* value, void* clientData); +void TW_CALL getXYPoissonRatio(void* value, void* clientData); +void TW_CALL setYXPoissonRatio(const void* value, void* clientData); +void TW_CALL getYXPoissonRatio(void* value, void* clientData); +void TW_CALL setNormalizeStretch(const void* value, void* clientData); +void TW_CALL getNormalizeStretch(void* value, void* clientData); +void TW_CALL setNormalizeShear(const void* value, void* clientData); +void TW_CALL getNormalizeShear(void* value, void* clientData); const int nRows = 50; @@ -45,6 +62,15 @@ const Real width = 10.0; const Real height = 10.0; short simulationMethod = 2; short bendingMethod = 2; +Real distanceStiffness = 1.0; +Real xxStiffness = 1.0; +Real yyStiffness = 1.0; +Real xyStiffness = 1.0; +Real xyPoissonRatio = 0.3; +Real yxPoissonRatio = 0.3; +bool normalizeStretch = false; +bool normalizeShear = false; +Real bendingStiffness = 0.01; bool doPause = true; DemoBase *base; DistanceFieldCollisionDetection cd; @@ -63,7 +89,7 @@ int main( int argc, char **argv ) buildModel(); - initParameters(); + base->createParameterGUI(); // OpenGL MiniGL::setClientIdleFunc (timeStep); @@ -75,11 +101,21 @@ int main( int argc, char **argv ) TwAddVarCB(MiniGL::getTweakBar(), "SimulationMethod", enumType2, setSimulationMethod, getSimulationMethod, &simulationMethod, " label='Simulation method' enum='0 {None}, 1 {Distance constraints}, 2 {FEM based PBD}, 3 {Strain based dynamics}, 4 {XPBD distance constraints}' group=Simulation"); TwType enumType3 = TwDefineEnum("BendingMethodType", NULL, 0); - TwAddVarCB(MiniGL::getTweakBar(), "BendingMethod", enumType3, setBendingMethod, getBendingMethod, &bendingMethod, " label='Bending method' enum='0 {None}, 1 {Dihedral angle}, 2 {Isometric bending}, 3 {XPBD isometric bending}' group=Bending"); - - MiniGL::mainLoop (); - - base->cleanup (); + TwAddVarCB(MiniGL::getTweakBar(), "BendingMethod", enumType3, setBendingMethod, getBendingMethod, &bendingMethod, + " label='Bending method' enum='0 {None}, 1 {Dihedral angle}, 2 {Isometric bending}, 3 {XPBD isometric bending}' group=Bending"); + TwAddVarCB(MiniGL::getTweakBar(), "BendingStiffness", TW_TYPE_REAL, setBendingStiffness, getBendingStiffness, model, " label='Bending stiffness' min=0.0 step=0.1 precision=4 group='Bending' "); + TwAddVarCB(MiniGL::getTweakBar(), "DistanceStiffness", TW_TYPE_REAL, setDistanceStiffness, getDistanceStiffness, model, " label='Distance constraint stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "xxStiffness", TW_TYPE_REAL, setXXStiffness, getXXStiffness, model, " label='xx stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "yyStiffness", TW_TYPE_REAL, setYYStiffness, getYYStiffness, model, " label='yy stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "xyStiffness", TW_TYPE_REAL, setXYStiffness, getXYStiffness, model, " label='xy stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "xyPoissonRatio", TW_TYPE_REAL, setXYPoissonRatio, getXYPoissonRatio, model, " label='xy Poisson ratio' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "yxPoissonRatio", TW_TYPE_REAL, setYXPoissonRatio, getYXPoissonRatio, model, " label='yx Poisson ratio' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeStretch", TW_TYPE_BOOL32, setNormalizeStretch, getNormalizeStretch, model, " label='Normalize stretch' group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeShear", TW_TYPE_BOOL32, setNormalizeShear, getNormalizeShear, model, " label='Normalize shear' group='Cloth' "); + + MiniGL::mainLoop(); + + base->cleanup(); Utilities::Timing::printAverageTimes(); Utilities::Timing::printTimeSums(); @@ -91,20 +127,6 @@ int main( int argc, char **argv ) return 0; } -void initParameters() -{ - TwRemoveAllVars(MiniGL::getTweakBar()); - TweakBarParameters::cleanup(); - - MiniGL::initTweakBarParameters(); - - TweakBarParameters::createParameterGUI(); - TweakBarParameters::createParameterObjectGUI(base); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getModel()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getTimeStep()); -} - void reset() { Utilities::Timing::printAverageTimes(); @@ -198,13 +220,13 @@ void buildModel () createMesh(); // create static rigid body - string fileName = FileSystem::normalizePath(base->getDataPath() + "/models/cube.obj"); + string fileName = FileSystem::normalizePath(base->getExePath() + "/resources/models/cube.obj"); IndexedFaceMesh mesh; VertexData vd; loadObj(fileName, vd, mesh, Vector3r::Ones()); mesh.setFlatShading(true); - string fileNameTorus = FileSystem::normalizePath(base->getDataPath() + "/models/torus.obj"); + string fileNameTorus = FileSystem::normalizePath(base->getExePath() + "/resources/models/torus.obj"); IndexedFaceMesh meshTorus; VertexData vdTorus; loadObj(fileNameTorus, vdTorus, meshTorus, Vector3r::Ones()); @@ -254,7 +276,6 @@ void buildModel () } } - void render () { base->render(); @@ -265,185 +286,26 @@ void render () */ void createMesh() { - TriangleModel::ParticleMesh::UVs uvs; - uvs.resize(nRows*nCols); - - const Real dy = width / (Real)(nCols-1); - const Real dx = height / (Real)(nRows-1); - - Vector3r points[nRows*nCols]; - for (int i = 0; i < nRows; i++) - { - for (int j = 0; j < nCols; j++) - { - const Real y = (Real)dy*j; - const Real x = (Real)dx*i; - points[i*nCols + j] = Vector3r(x-5.0, 4.0, y-5.0); - - uvs[i*nCols + j][0] = x/width; - uvs[i*nCols + j][1] = y/height; - } - } - const int nIndices = 6 * (nRows - 1)*(nCols - 1); - - TriangleModel::ParticleMesh::UVIndices uvIndices; - uvIndices.resize(nIndices); - - unsigned int indices[nIndices]; - int index = 0; - for (int i = 0; i < nRows - 1; i++) - { - for (int j = 0; j < nCols - 1; j++) - { - int helper = 0; - if (i % 2 == j % 2) - helper = 1; - - indices[index] = i*nCols + j; - indices[index + 1] = i*nCols + j + 1; - indices[index + 2] = (i + 1)*nCols + j + helper; - - uvIndices[index] = i*nCols + j; - uvIndices[index + 1] = i*nCols + j + 1; - uvIndices[index + 2] = (i + 1)*nCols + j + helper; - index += 3; - - indices[index] = (i + 1)*nCols + j + 1; - indices[index + 1] = (i + 1)*nCols + j; - indices[index + 2] = i*nCols + j + 1 - helper; - - uvIndices[index] = (i + 1)*nCols + j + 1; - uvIndices[index + 1] = (i + 1)*nCols + j; - uvIndices[index + 2] = i*nCols + j + 1 - helper; - index += 3; - } - } - - SimulationModel *model = Simulation::getCurrent()->getModel(); - model->addTriangleModel(nRows*nCols, nIndices / 3, &points[0], &indices[0], uvIndices, uvs); - - ParticleData &pd = model->getParticles(); - for (unsigned int i = 0; i < pd.getNumberOfParticles(); i++) - { - pd.setMass(i, 1.0); - } + SimulationModel* model = Simulation::getCurrent()->getModel(); + model->addRegularTriangleModel(nCols, nRows, + Vector3r(-5, 4, -5), AngleAxisr(M_PI * 0.5, Vector3r(1, 0, 0)).matrix(), Vector2r(width, height)); // init constraints for (unsigned int cm = 0; cm < model->getTriangleModels().size(); cm++) { - model->setValue(SimulationModel::CLOTH_STIFFNESS, 1.0); - model->setValue(SimulationModel::CLOTH_BENDING_STIFFNESS, 0.01); - if (simulationMethod == 1) - { - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - const unsigned int nEdges = model->getTriangleModels()[cm]->getParticleMesh().numEdges(); - const IndexedFaceMesh::Edge *edges = model->getTriangleModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint(v1, v2); - } - } - else if (simulationMethod == 2) - { - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - TriangleModel::ParticleMesh &mesh = model->getTriangleModels()[cm]->getParticleMesh(); - const unsigned int *tris = mesh.getFaces().data(); - const unsigned int nFaces = mesh.numFaces(); - for (unsigned int i = 0; i < nFaces; i++) - { - const unsigned int v1 = tris[3 * i] + offset; - const unsigned int v2 = tris[3 * i + 1] + offset; - const unsigned int v3 = tris[3 * i + 2] + offset; - model->addFEMTriangleConstraint(v1, v2, v3); - } - } - else if (simulationMethod == 3) - { - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - TriangleModel::ParticleMesh &mesh = model->getTriangleModels()[cm]->getParticleMesh(); - const unsigned int *tris = mesh.getFaces().data(); - const unsigned int nFaces = mesh.numFaces(); - for (unsigned int i = 0; i < nFaces; i++) - { - const unsigned int v1 = tris[3 * i] + offset; - const unsigned int v2 = tris[3 * i + 1] + offset; - const unsigned int v3 = tris[3 * i + 2] + offset; - model->addStrainTriangleConstraint(v1, v2, v3); - } - } - else if (simulationMethod == 4) - { - model->setValue(SimulationModel::CLOTH_STIFFNESS, 100000); - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - const unsigned int nEdges = model->getTriangleModels()[cm]->getParticleMesh().numEdges(); - const IndexedFaceMesh::Edge* edges = model->getTriangleModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint_XPBD(v1, v2); - } - } - if (bendingMethod != 0) - { - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - TriangleModel::ParticleMesh &mesh = model->getTriangleModels()[cm]->getParticleMesh(); - unsigned int nEdges = mesh.numEdges(); - const TriangleModel::ParticleMesh::Edge *edges = mesh.getEdges().data(); - const unsigned int *tris = mesh.getFaces().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const int tri1 = edges[i].m_face[0]; - const int tri2 = edges[i].m_face[1]; - if ((tri1 != 0xffffffff) && (tri2 != 0xffffffff)) - { - // Find the triangle points which do not lie on the axis - const int axisPoint1 = edges[i].m_vert[0]; - const int axisPoint2 = edges[i].m_vert[1]; - int point1 = -1; - int point2 = -1; - for (int j = 0; j < 3; j++) - { - if ((tris[3 * tri1 + j] != axisPoint1) && (tris[3 * tri1 + j] != axisPoint2)) - { - point1 = tris[3 * tri1 + j]; - break; - } - } - for (int j = 0; j < 3; j++) - { - if ((tris[3 * tri2 + j] != axisPoint1) && (tris[3 * tri2 + j] != axisPoint2)) - { - point2 = tris[3 * tri2 + j]; - break; - } - } - if ((point1 != -1) && (point2 != -1)) - { - const unsigned int vertex1 = point1 + offset; - const unsigned int vertex2 = point2 + offset; - const unsigned int vertex3 = edges[i].m_vert[0] + offset; - const unsigned int vertex4 = edges[i].m_vert[1] + offset; - if (bendingMethod == 1) - model->addDihedralConstraint(vertex1, vertex2, vertex3, vertex4); - else if (bendingMethod == 2) - model->addIsometricBendingConstraint(vertex1, vertex2, vertex3, vertex4); - else if (bendingMethod == 3) - { - model->setValue(SimulationModel::CLOTH_BENDING_STIFFNESS, 100.0); - model->addIsometricBendingConstraint_XPBD(vertex1, vertex2, vertex3, vertex4); - } - } - } - } - } + distanceStiffness = 1.0; + if (simulationMethod == 4) + distanceStiffness = 100000; + model->addClothConstraints(model->getTriangleModels()[cm], simulationMethod, distanceStiffness, xxStiffness, + yyStiffness, xyStiffness, xyPoissonRatio, yxPoissonRatio, normalizeStretch, normalizeShear); + + bendingStiffness = 0.01; + if (bendingMethod == 3) + bendingStiffness = 100.0; + model->addBendingConstraints(model->getTriangleModels()[cm], bendingMethod, bendingStiffness); } - LOG_INFO << "Number of triangles: " << nIndices / 3; + LOG_INFO << "Number of triangles: " << model->getTriangleModels()[0]->getParticleMesh().numFaces(); LOG_INFO << "Number of vertices: " << nRows*nCols; } @@ -471,3 +333,108 @@ void TW_CALL getSimulationMethod(void *value, void *clientData) { *(short *)(value) = *((short*)clientData); } + +void TW_CALL setBendingStiffness(const void* value, void* clientData) +{ + bendingStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); +} + +void TW_CALL getBendingStiffness(void* value, void* clientData) +{ + *(Real*)(value) = bendingStiffness; +} + +void TW_CALL setDistanceStiffness(const void* value, void* clientData) +{ + distanceStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(distanceStiffness); + ((SimulationModel*)clientData)->setConstraintValue(distanceStiffness); +} + +void TW_CALL getDistanceStiffness(void* value, void* clientData) +{ + *(Real*)(value) = distanceStiffness; +} + +void TW_CALL setXXStiffness(const void* value, void* clientData) +{ + xxStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(xxStiffness); + ((SimulationModel*)clientData)->setConstraintValue(xxStiffness); +} + +void TW_CALL getXXStiffness(void* value, void* clientData) +{ + *(Real*)(value) = xxStiffness; +} + +void TW_CALL getYYStiffness(void* value, void* clientData) +{ + *(Real*)(value) = yyStiffness; +} + +void TW_CALL setYYStiffness(const void* value, void* clientData) +{ + yyStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(yyStiffness); + ((SimulationModel*)clientData)->setConstraintValue(yyStiffness); +} + +void TW_CALL getXYStiffness(void* value, void* clientData) +{ + *(Real*)(value) = xyStiffness; +} + +void TW_CALL setXYStiffness(const void* value, void* clientData) +{ + xyStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(xyStiffness); + ((SimulationModel*)clientData)->setConstraintValue(xyStiffness); +} + +void TW_CALL getXYPoissonRatio(void* value, void* clientData) +{ + *(Real*)(value) = xyPoissonRatio; +} + +void TW_CALL setXYPoissonRatio(const void* value, void* clientData) +{ + xyPoissonRatio = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(xyPoissonRatio); +} + +void TW_CALL getYXPoissonRatio(void* value, void* clientData) +{ + *(Real*)(value) = yxPoissonRatio; +} + +void TW_CALL setYXPoissonRatio(const void* value, void* clientData) +{ + yxPoissonRatio = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(yxPoissonRatio); +} + +void TW_CALL getNormalizeStretch(void* value, void* clientData) +{ + *(bool*)(value) = normalizeStretch; +} + +void TW_CALL setNormalizeStretch(const void* value, void* clientData) +{ + normalizeStretch = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(normalizeStretch); +} + +void TW_CALL getNormalizeShear(void* value, void* clientData) +{ + *(bool*)(value) = normalizeShear; +} + +void TW_CALL setNormalizeShear(const void* value, void* clientData) +{ + normalizeShear= *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(normalizeShear); +} \ No newline at end of file diff --git a/Demos/DistanceFieldDemos/DeformableCollisionDemo.cpp b/Demos/DistanceFieldDemos/DeformableCollisionDemo.cpp index 1ab8cb25..d4b46b0d 100644 --- a/Demos/DistanceFieldDemos/DeformableCollisionDemo.cpp +++ b/Demos/DistanceFieldDemos/DeformableCollisionDemo.cpp @@ -26,20 +26,21 @@ using namespace Eigen; using namespace std; using namespace Utilities; -void initParameters(); void timeStep (); void buildModel (); void createMesh(); void render (); void reset(); -void TW_CALL setStiffness(const void *value, void *clientData); -void TW_CALL getStiffness(void *value, void *clientData); -void TW_CALL setPoissonRatio(const void *value, void *clientData); -void TW_CALL getPoissonRatio(void *value, void *clientData); -void TW_CALL setNormalizeStretch(const void *value, void *clientData); -void TW_CALL getNormalizeStretch(void *value, void *clientData); -void TW_CALL setNormalizeShear(const void *value, void *clientData); -void TW_CALL getNormalizeShear(void *value, void *clientData); +void TW_CALL setStiffness(const void* value, void* clientData); +void TW_CALL getStiffness(void* value, void* clientData); +void TW_CALL setPoissonRatio(const void* value, void* clientData); +void TW_CALL getPoissonRatio(void* value, void* clientData); +void TW_CALL setVolumeStiffness(const void* value, void* clientData); +void TW_CALL getVolumeStiffness(void* value, void* clientData); +void TW_CALL setNormalizeStretch(const void* value, void* clientData); +void TW_CALL getNormalizeStretch(void* value, void* clientData); +void TW_CALL setNormalizeShear(const void* value, void* clientData); +void TW_CALL getNormalizeShear(void* value, void* clientData); void TW_CALL setSimulationMethod(const void *value, void *clientData); void TW_CALL getSimulationMethod(void *value, void *clientData); void TW_CALL setContactTolerance(const void *value, void *clientData); @@ -57,6 +58,11 @@ const unsigned int width = 30; const unsigned int depth = 5; const unsigned int height = 5; short simulationMethod = 2; +Real stiffness = 1.0; +Real poissonRatio = 0.3; +bool normalizeStretch = false; +bool normalizeShear = false; +Real volumeStiffness = 1.0; // main int main( int argc, char **argv ) @@ -72,7 +78,7 @@ int main( int argc, char **argv ) buildModel(); - initParameters(); + base->createParameterGUI(); // OpenGL MiniGL::setClientIdleFunc (timeStep); @@ -83,15 +89,15 @@ int main( int argc, char **argv ) TwType enumType2 = TwDefineEnum("SimulationMethodType", NULL, 0); TwAddVarCB(MiniGL::getTweakBar(), "SimulationMethod", enumType2, setSimulationMethod, getSimulationMethod, &simulationMethod, " label='Simulation method' enum='0 {None}, 1 {Volume constraints}, 2 {FEM based PBD}, 3 {Strain based dynamics (no inversion handling)}, 4 {Shape matching (no inversion handling)}, 5 {XPBD volume constraints}' group=Simulation"); - TwAddVarCB(MiniGL::getTweakBar(), "Stiffness", TW_TYPE_REAL, setStiffness, getStiffness, model, " label='Stiffness' min=0.0 step=0.1 precision=4 group='Simulation' "); - TwAddVarCB(MiniGL::getTweakBar(), "PoissonRatio", TW_TYPE_REAL, setPoissonRatio, getPoissonRatio, model, " label='Poisson ratio XY' min=0.0 step=0.1 precision=4 group='Simulation' "); - TwAddVarCB(MiniGL::getTweakBar(), "NormalizeStretch", TW_TYPE_BOOL32, setNormalizeStretch, getNormalizeStretch, model, " label='Normalize stretch' group='Strain based dynamics' "); - TwAddVarCB(MiniGL::getTweakBar(), "NormalizeShear", TW_TYPE_BOOL32, setNormalizeShear, getNormalizeShear, model, " label='Normalize shear' group='Strain based dynamics' "); + TwAddVarCB(MiniGL::getTweakBar(), "stiffness", TW_TYPE_REAL, setStiffness, getStiffness, model, " label='Stiffness' min=0.0 step=0.1 precision=4 group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "poissonRatio", TW_TYPE_REAL, setPoissonRatio, getPoissonRatio, model, " label='Poisson ratio' min=0.0 step=0.1 precision=4 group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeStretch", TW_TYPE_BOOL32, setNormalizeStretch, getNormalizeStretch, model, " label='Normalize stretch' group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeShear", TW_TYPE_BOOL32, setNormalizeShear, getNormalizeShear, model, " label='Normalize shear' group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "volumeStiffness", TW_TYPE_REAL, setVolumeStiffness, getVolumeStiffness, model, " label='Volume stiffness' min=0.0 step=0.1 precision=4 group='Solid' "); TwAddVarCB(MiniGL::getTweakBar(), "ContactTolerance", TW_TYPE_REAL, setContactTolerance, getContactTolerance, &cd, " label='Contact tolerance' min=0.0 step=0.001 precision=3 group=Simulation "); TwAddVarCB(MiniGL::getTweakBar(), "ContactStiffnessRigidBody", TW_TYPE_REAL, setContactStiffnessRigidBody, getContactStiffnessRigidBody, model, " label='Contact stiffness RB' min=0.0 step=0.1 precision=2 group=Simulation "); TwAddVarCB(MiniGL::getTweakBar(), "ContactStiffnessParticleRigidBody", TW_TYPE_REAL, setContactStiffnessParticleRigidBody, getContactStiffnessParticleRigidBody, model, " label='Contact stiffness Particle-RB' min=0.0 step=0.1 precision=2 group=Simulation "); - MiniGL::mainLoop (); base->cleanup(); @@ -106,20 +112,6 @@ int main( int argc, char **argv ) return 0; } -void initParameters() -{ - TwRemoveAllVars(MiniGL::getTweakBar()); - TweakBarParameters::cleanup(); - - MiniGL::initTweakBarParameters(); - - TweakBarParameters::createParameterGUI(); - TweakBarParameters::createParameterObjectGUI(base); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getModel()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getTimeStep()); -} - void reset() { Utilities::Timing::printAverageTimes(); @@ -214,13 +206,13 @@ void buildModel () createMesh(); // create static rigid body - string fileName = FileSystem::normalizePath(base->getDataPath() + "/models/cube.obj"); + string fileName = FileSystem::normalizePath(base->getExePath() + "/resources/models/cube.obj"); IndexedFaceMesh mesh; VertexData vd; loadObj(fileName, vd, mesh, Vector3r::Ones()); mesh.setFlatShading(true); - string fileNameTorus = FileSystem::normalizePath(base->getDataPath() + "/models/torus.obj"); + string fileNameTorus = FileSystem::normalizePath(base->getExePath() + "/resources/models/torus.obj"); IndexedFaceMesh meshTorus; VertexData vdTorus; loadObj(fileNameTorus, vdTorus, meshTorus, Vector3r::Ones()); @@ -278,216 +270,92 @@ void render () void createMesh() { - Vector3r points[width*height*depth]; - for (unsigned int i = 0; i < width; i++) - { - for (unsigned int j = 0; j < height; j++) - { - for (unsigned int k = 0; k < depth; k++) - { - points[i*height*depth + j*depth + k] = static_cast(0.3)*Vector3r((Real)i, (Real)j + static_cast(3.0), (Real)k); - } - } - } + SimulationModel* model = Simulation::getCurrent()->getModel(); + model->addRegularTetModel(width, height, depth, + Vector3r(4.5, 3, 0), Matrix3r::Identity(), Vector3r(9.0, 1.5, 1.5)); - vector indices; - for (unsigned int i = 0; i < width - 1; i++) - { - for (unsigned int j = 0; j < height - 1; j++) - { - for (unsigned int k = 0; k < depth - 1; k++) - { - // For each block, the 8 corners are numerated as: - // 4*-----*7 - // /| /| - // / | / | - // 5*-----*6 | - // | 0*--|--*3 - // | / | / - // |/ |/ - // 1*-----*2 - unsigned int p0 = i*height*depth + j*depth + k; - unsigned int p1 = p0 + 1; - unsigned int p3 = (i + 1)*height*depth + j*depth + k; - unsigned int p2 = p3 + 1; - unsigned int p7 = (i + 1)*height*depth + (j + 1)*depth + k; - unsigned int p6 = p7 + 1; - unsigned int p4 = i*height*depth + (j + 1)*depth + k; - unsigned int p5 = p4 + 1; - - // Ensure that neighboring tetras are sharing faces - if ((i + j + k) % 2 == 1) - { - indices.push_back(p2); indices.push_back(p1); indices.push_back(p6); indices.push_back(p3); - indices.push_back(p6); indices.push_back(p3); indices.push_back(p4); indices.push_back(p7); - indices.push_back(p4); indices.push_back(p1); indices.push_back(p6); indices.push_back(p5); - indices.push_back(p3); indices.push_back(p1); indices.push_back(p4); indices.push_back(p0); - indices.push_back(p6); indices.push_back(p1); indices.push_back(p4); indices.push_back(p3); - } - else - { - indices.push_back(p0); indices.push_back(p2); indices.push_back(p5); indices.push_back(p1); - indices.push_back(p7); indices.push_back(p2); indices.push_back(p0); indices.push_back(p3); - indices.push_back(p5); indices.push_back(p2); indices.push_back(p7); indices.push_back(p6); - indices.push_back(p7); indices.push_back(p0); indices.push_back(p5); indices.push_back(p4); - indices.push_back(p0); indices.push_back(p2); indices.push_back(p7); indices.push_back(p5); - } - } - } - } - SimulationModel *model = Simulation::getCurrent()->getModel(); - model->addTetModel(width*height*depth, (unsigned int)indices.size() / 4u, points, indices.data()); + // init constraints + stiffness = 1.0; + if (simulationMethod == 5) + stiffness = 100000; - ParticleData &pd = model->getParticles(); - for (unsigned int i = 0; i < pd.getNumberOfParticles(); i++) - { - pd.setMass(i, 1.0); - } + volumeStiffness = 1.0; + if (simulationMethod == 5) + volumeStiffness = 100000; - // init constraints - model->setValue(SimulationModel::CLOTH_STIFFNESS, 1.0); - model->setValue(SimulationModel::SOLID_STIFFNESS, 1.0); + ParticleData& pd = model->getParticles(); for (unsigned int cm = 0; cm < model->getTetModels().size(); cm++) { - const unsigned int nTets = model->getTetModels()[cm]->getParticleMesh().numTets(); - const unsigned int *tets = model->getTetModels()[cm]->getParticleMesh().getTets().data(); - const IndexedTetMesh::VertexTets *vTets = model->getTetModels()[cm]->getParticleMesh().getVertexTets().data(); - if (simulationMethod == 1) - { - const unsigned int offset = model->getTetModels()[cm]->getIndexOffset(); - const unsigned int nEdges = model->getTetModels()[cm]->getParticleMesh().numEdges(); - const IndexedTetMesh::Edge *edges = model->getTetModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint(v1, v2); - } - - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i]; - const unsigned int v2 = tets[4 * i + 1]; - const unsigned int v3 = tets[4 * i + 2]; - const unsigned int v4 = tets[4 * i + 3]; - - model->addVolumeConstraint(v1, v2, v3, v4); - } - } - else if (simulationMethod == 2) - { - TetModel::ParticleMesh &mesh = model->getTetModels()[cm]->getParticleMesh(); - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i]; - const unsigned int v2 = tets[4 * i + 1]; - const unsigned int v3 = tets[4 * i + 2]; - const unsigned int v4 = tets[4 * i + 3]; - - model->addFEMTetConstraint(v1, v2, v3, v4); - } - } - else if (simulationMethod == 3) - { - TetModel::ParticleMesh &mesh = model->getTetModels()[cm]->getParticleMesh(); - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i]; - const unsigned int v2 = tets[4 * i + 1]; - const unsigned int v3 = tets[4 * i + 2]; - const unsigned int v4 = tets[4 * i + 3]; + model->addSolidConstraints(model->getTetModels()[cm], simulationMethod, stiffness, + poissonRatio, volumeStiffness, normalizeStretch, normalizeShear); - model->addStrainTetConstraint(v1, v2, v3, v4); - } - } - else if (simulationMethod == 4) - { - TetModel::ParticleMesh &mesh = model->getTetModels()[cm]->getParticleMesh(); - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v[4] = { tets[4 * i], tets[4 * i + 1], tets[4 * i + 2], tets[4 * i + 3] }; - // Important: Divide position correction by the number of clusters - // which contain the vertex. - const unsigned int nc[4] = { vTets[v[0]].m_numTets, vTets[v[1]].m_numTets, vTets[v[2]].m_numTets, vTets[v[3]].m_numTets }; - model->addShapeMatchingConstraint(4, v, nc); - } - } - else if (simulationMethod == 5) - { - model->setValue(SimulationModel::CLOTH_STIFFNESS, 100000); - model->setValue(SimulationModel::SOLID_STIFFNESS, 100000); - const unsigned int offset = model->getTetModels()[cm]->getIndexOffset(); - const unsigned int nEdges = model->getTetModels()[cm]->getParticleMesh().numEdges(); - const IndexedTetMesh::Edge* edges = model->getTetModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint_XPBD(v1, v2); - } - - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i]; - const unsigned int v2 = tets[4 * i + 1]; - const unsigned int v3 = tets[4 * i + 2]; - const unsigned int v4 = tets[4 * i + 3]; - - model->addVolumeConstraint_XPBD(v1, v2, v3, v4); - } - } model->getTetModels()[cm]->updateMeshNormals(pd); + + LOG_INFO << "Number of tets: " << model->getTetModels()[cm]->getParticleMesh().numTets(); + LOG_INFO << "Number of vertices: " << width * height * depth; } +} - LOG_INFO << "Number of tets: " << indices.size() / 4; - LOG_INFO << "Number of vertices: " << width*height*depth; +void TW_CALL setStiffness(const void* value, void* clientData) +{ + stiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(stiffness); + ((SimulationModel*)clientData)->setConstraintValue(stiffness); + ((SimulationModel*)clientData)->setConstraintValue(stiffness); + ((SimulationModel*)clientData)->setConstraintValue(stiffness); + ((SimulationModel*)clientData)->setConstraintValue(stiffness); + ((SimulationModel*)clientData)->setConstraintValue(stiffness); } -void TW_CALL setStiffness(const void *value, void *clientData) +void TW_CALL getStiffness(void* value, void* clientData) { - const Real val = *(const Real *)(value); - ((SimulationModel*) clientData)->setValue(SimulationModel::SOLID_STIFFNESS, val); + *(Real*)(value) = stiffness; } -void TW_CALL getStiffness(void *value, void *clientData) +void TW_CALL setVolumeStiffness(const void* value, void* clientData) { - *(Real *)(value) = ((SimulationModel*)clientData)->getValue(SimulationModel::SOLID_STIFFNESS); + volumeStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(volumeStiffness); + ((SimulationModel*)clientData)->setConstraintValue(volumeStiffness); } -void TW_CALL setPoissonRatio(const void *value, void *clientData) +void TW_CALL getVolumeStiffness(void* value, void* clientData) { - const Real val = *(const Real *)(value); - ((SimulationModel*)clientData)->setValue(SimulationModel::SOLID_POISSON_RATIO, val); + *(Real*)(value) = volumeStiffness; +} + +void TW_CALL getPoissonRatio(void* value, void* clientData) +{ + *(Real*)(value) = poissonRatio; } -void TW_CALL getPoissonRatio(void *value, void *clientData) +void TW_CALL setPoissonRatio(const void* value, void* clientData) { - *(Real *)(value) = ((SimulationModel*)clientData)->getValue(SimulationModel::SOLID_POISSON_RATIO); + poissonRatio = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(poissonRatio); } -void TW_CALL setNormalizeStretch(const void *value, void *clientData) +void TW_CALL getNormalizeStretch(void* value, void* clientData) { - const bool val = *(const bool *)(value); - ((SimulationModel*)clientData)->setValue(SimulationModel::SOLID_NORMALIZE_STRETCH, val); + *(bool*)(value) = normalizeStretch; } -void TW_CALL getNormalizeStretch(void *value, void *clientData) +void TW_CALL setNormalizeStretch(const void* value, void* clientData) { - *(bool *)(value) = ((SimulationModel*)clientData)->getValue(SimulationModel::SOLID_NORMALIZE_STRETCH); + normalizeStretch = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(normalizeStretch); } -void TW_CALL setNormalizeShear(const void *value, void *clientData) +void TW_CALL getNormalizeShear(void* value, void* clientData) { - const bool val = *(const bool *)(value); - ((SimulationModel*)clientData)->setValue(SimulationModel::SOLID_NORMALIZE_SHEAR, val); + *(bool*)(value) = normalizeShear; } -void TW_CALL getNormalizeShear(void *value, void *clientData) +void TW_CALL setNormalizeShear(const void* value, void* clientData) { - *(bool *)(value) = ((SimulationModel*)clientData)->getValue(SimulationModel::SOLID_NORMALIZE_SHEAR); + normalizeShear = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(normalizeShear); } void TW_CALL setSimulationMethod(const void *value, void *clientData) diff --git a/Demos/DistanceFieldDemos/RigidBodyCollisionDemo.cpp b/Demos/DistanceFieldDemos/RigidBodyCollisionDemo.cpp index 56e3a72d..6780cea7 100644 --- a/Demos/DistanceFieldDemos/RigidBodyCollisionDemo.cpp +++ b/Demos/DistanceFieldDemos/RigidBodyCollisionDemo.cpp @@ -204,29 +204,29 @@ void createBodyModel() SimulationModel::RigidBodyVector &rb = model->getRigidBodies(); SimulationModel::ConstraintVector &constraints = model->getConstraints(); - string fileNameBox = FileSystem::normalizePath(base->getDataPath() + "/models/cube.obj"); + string fileNameBox = FileSystem::normalizePath(base->getExePath() + "/resources/models/cube.obj"); IndexedFaceMesh meshBox; VertexData vdBox; loadObj(fileNameBox, vdBox, meshBox, Vector3r::Ones()); meshBox.setFlatShading(true); - string fileNameCylinder = FileSystem::normalizePath(base->getDataPath() + "/models/cylinder.obj"); + string fileNameCylinder = FileSystem::normalizePath(base->getExePath() + "/resources/models/cylinder.obj"); IndexedFaceMesh meshCylinder; VertexData vdCylinder; loadObj(fileNameCylinder, vdCylinder, meshCylinder, Vector3r::Ones()); - string fileNameTorus = FileSystem::normalizePath(base->getDataPath() + "/models/torus.obj"); + string fileNameTorus = FileSystem::normalizePath(base->getExePath() + "/resources/models/torus.obj"); IndexedFaceMesh meshTorus; VertexData vdTorus; loadObj(fileNameTorus, vdTorus, meshTorus, Vector3r::Ones()); - string fileNameCube = FileSystem::normalizePath(base->getDataPath() + "/models/cube_5.obj"); + string fileNameCube = FileSystem::normalizePath(base->getExePath() + "/resources/models/cube_5.obj"); IndexedFaceMesh meshCube; VertexData vdCube; loadObj(fileNameCube, vdCube, meshCube, Vector3r::Ones()); meshCube.setFlatShading(true); - string fileNameSphere = FileSystem::normalizePath(base->getDataPath() + "/models/sphere.obj"); + string fileNameSphere = FileSystem::normalizePath(base->getExePath() + "/resources/models/sphere.obj"); IndexedFaceMesh meshSphere; VertexData vdSphere; loadObj(fileNameSphere, vdSphere, meshSphere, 2.0*Vector3r::Ones()); diff --git a/Demos/FluidDemo/CMakeLists.txt b/Demos/FluidDemo/CMakeLists.txt index e85d10f8..9bff5e2b 100644 --- a/Demos/FluidDemo/CMakeLists.txt +++ b/Demos/FluidDemo/CMakeLists.txt @@ -1,5 +1,5 @@ set(SIMULATION_LINK_LIBRARIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) -set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) +set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils CopyPBDShaders) if(WIN32) set(SIMULATION_LINK_LIBRARIES opengl32.lib glu32.lib ${SIMULATION_LINK_LIBRARIES}) diff --git a/Demos/FluidDemo/main.cpp b/Demos/FluidDemo/main.cpp index 31df5526..466c0c55 100644 --- a/Demos/FluidDemo/main.cpp +++ b/Demos/FluidDemo/main.cpp @@ -79,7 +79,6 @@ int main( int argc, char **argv ) logger.addSink(unique_ptr(new FileSink(LogLevel::DEBUG, logPath + "/PBD.log"))); exePath = FileSystem::getProgramPath(); - dataPath = exePath + "/" + std::string(PBD_DATA_PATH); // OpenGL MiniGL::init (argc, argv, 1280, 1024, "Fluid demo"); diff --git a/Demos/GenericConstraintsDemos/CMakeLists.txt b/Demos/GenericConstraintsDemos/CMakeLists.txt index 680e7efb..9fbc928a 100644 --- a/Demos/GenericConstraintsDemos/CMakeLists.txt +++ b/Demos/GenericConstraintsDemos/CMakeLists.txt @@ -1,5 +1,5 @@ set(SIMULATION_LINK_LIBRARIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) -set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) +set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils CopyPBDShaders CopyPBDModels) if(WIN32) set(SIMULATION_LINK_LIBRARIES opengl32.lib glu32.lib ${SIMULATION_LINK_LIBRARIES}) diff --git a/Demos/GenericConstraintsDemos/GenericConstraints.cpp b/Demos/GenericConstraintsDemos/GenericConstraints.cpp index e9e32192..a89bbc27 100644 --- a/Demos/GenericConstraintsDemos/GenericConstraints.cpp +++ b/Demos/GenericConstraintsDemos/GenericConstraints.cpp @@ -43,8 +43,9 @@ void GenericDistanceConstraint::gradientFct( jacobian = n.transpose(); } -bool GenericDistanceConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2) +bool GenericDistanceConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, const Real stiffness) { + m_stiffness = stiffness; m_bodies[0] = particle1; m_bodies[1] = particle2; ParticleData &pd = model.getParticles(); @@ -81,7 +82,7 @@ bool GenericDistanceConstraint::solvePositionConstraint(SimulationModel &model, if (res) { - const Real stiffness = model.getValue(SimulationModel::CLOTH_STIFFNESS); + const Real stiffness = m_stiffness; if (invMass1 != 0.0) x1 += stiffness * corr[0]; if (invMass2 != 0.0) @@ -114,8 +115,9 @@ void GenericIsometricBendingConstraint::constraintFct( } bool GenericIsometricBendingConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) + const unsigned int particle3, const unsigned int particle4, const Real stiffness) { + m_stiffness = stiffness; m_bodies[0] = particle3; m_bodies[1] = particle4; m_bodies[2] = particle1; @@ -191,15 +193,14 @@ bool GenericIsometricBendingConstraint::solvePositionConstraint(SimulationModel if (res) { - const Real stiffness = model.getValue(SimulationModel::CLOTH_BENDING_STIFFNESS); if (invMass0 != 0.0) - x0 += stiffness*corr[0]; + x0 += m_stiffness*corr[0]; if (invMass1 != 0.0) - x1 += stiffness*corr[1]; + x1 += m_stiffness *corr[1]; if (invMass2 != 0.0) - x2 += stiffness*corr[2]; + x2 += m_stiffness *corr[2]; if (invMass3 != 0.0) - x3 += stiffness*corr[3]; + x3 += m_stiffness *corr[3]; } return res; } diff --git a/Demos/GenericConstraintsDemos/GenericConstraints.h b/Demos/GenericConstraintsDemos/GenericConstraints.h index 29581734..5bdbbf8b 100644 --- a/Demos/GenericConstraintsDemos/GenericConstraints.h +++ b/Demos/GenericConstraintsDemos/GenericConstraints.h @@ -13,6 +13,7 @@ namespace PBD public: static int TYPE_ID; Real m_restLength; + Real m_stiffness; static void constraintFct( const unsigned int numberOfParticles, @@ -32,7 +33,7 @@ namespace PBD GenericDistanceConstraint() : Constraint(2) {} virtual int &getTypeId() const { return TYPE_ID; } - virtual bool initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2); + virtual bool initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, const Real stiffness); virtual bool solvePositionConstraint(SimulationModel &model, const unsigned int iter); }; @@ -41,6 +42,7 @@ namespace PBD public: static int TYPE_ID; Matrix4r m_Q; + Real m_stiffness; static void constraintFct( const unsigned int numberOfParticles, @@ -53,7 +55,7 @@ namespace PBD virtual int &getTypeId() const { return TYPE_ID; } virtual bool initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); + const unsigned int particle3, const unsigned int particle4, const Real stiffness); virtual bool solvePositionConstraint(SimulationModel &model, const unsigned int iter); }; diff --git a/Demos/GenericConstraintsDemos/GenericConstraintsModel.cpp b/Demos/GenericConstraintsDemos/GenericConstraintsModel.cpp index 93f503e3..2dd643f4 100644 --- a/Demos/GenericConstraintsDemos/GenericConstraintsModel.cpp +++ b/Demos/GenericConstraintsDemos/GenericConstraintsModel.cpp @@ -14,20 +14,20 @@ GenericConstraintsModel::~GenericConstraintsModel(void) { } -bool GenericConstraintsModel::addGenericDistanceConstraint(const unsigned int particle1, const unsigned int particle2) +bool GenericConstraintsModel::addGenericDistanceConstraint(const unsigned int particle1, const unsigned int particle2, const Real stiffness) { GenericDistanceConstraint *c = new GenericDistanceConstraint(); - const bool res = c->initConstraint(*this, particle1, particle2); + const bool res = c->initConstraint(*this, particle1, particle2, stiffness); if (res) m_constraints.push_back(c); return res; } bool GenericConstraintsModel::addGenericIsometricBendingConstraint(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) + const unsigned int particle3, const unsigned int particle4, const Real stiffness) { GenericIsometricBendingConstraint *c = new GenericIsometricBendingConstraint(); - const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4); + const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4, stiffness); if (res) m_constraints.push_back(c); return res; diff --git a/Demos/GenericConstraintsDemos/GenericConstraintsModel.h b/Demos/GenericConstraintsDemos/GenericConstraintsModel.h index d7756d32..6c37c4de 100644 --- a/Demos/GenericConstraintsDemos/GenericConstraintsModel.h +++ b/Demos/GenericConstraintsDemos/GenericConstraintsModel.h @@ -14,9 +14,9 @@ namespace PBD GenericConstraintsModel(); virtual ~GenericConstraintsModel(); - bool addGenericDistanceConstraint(const unsigned int particle1, const unsigned int particle2); + bool addGenericDistanceConstraint(const unsigned int particle1, const unsigned int particle2, const Real stiffness); bool addGenericIsometricBendingConstraint(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); + const unsigned int particle3, const unsigned int particle4, const Real stiffness); bool addGenericHingeJoint(const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &pos, const Vector3r &axis); bool addGenericSliderJoint(const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &pos, const Vector3r &axis); bool addGenericBallJoint(const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &pos); diff --git a/Demos/GenericConstraintsDemos/GenericParticleConstraintsDemo.cpp b/Demos/GenericConstraintsDemos/GenericParticleConstraintsDemo.cpp index 2b580001..b007bffc 100644 --- a/Demos/GenericConstraintsDemos/GenericParticleConstraintsDemo.cpp +++ b/Demos/GenericConstraintsDemos/GenericParticleConstraintsDemo.cpp @@ -13,6 +13,7 @@ #include "Demos/Common/DemoBase.h" #include "Demos/Common/TweakBarParameters.h" #include "Simulation/Simulation.h" +#include "GenericConstraints.h" // Enable memory leak detection #if defined(_DEBUG) && !defined(EIGEN_ALIGN) @@ -24,12 +25,15 @@ using namespace Eigen; using namespace std; using namespace Utilities; -void initParameters(); void timeStep (); void buildModel (); void createMesh(); void render (); void reset(); +void TW_CALL setBendingStiffness(const void* value, void* clientData); +void TW_CALL getBendingStiffness(void* value, void* clientData); +void TW_CALL setDistanceStiffness(const void* value, void* clientData); +void TW_CALL getDistanceStiffness(void* value, void* clientData); DemoBase *base; @@ -37,6 +41,8 @@ const int nRows = 30; const int nCols = 30; const Real width = 10.0; const Real height = 10.0; +Real bendingStiffness = 0.01; +Real distanceStiffness = 1.0; // main int main( int argc, char **argv ) @@ -52,7 +58,7 @@ int main( int argc, char **argv ) buildModel(); - initParameters(); + base->createParameterGUI(); // OpenGL MiniGL::setClientIdleFunc (timeStep); @@ -60,6 +66,9 @@ int main( int argc, char **argv ) MiniGL::setClientSceneFunc(render); MiniGL::setViewport (40.0f, 0.1f, 500.0f, Vector3r (5.0, 10.0, 30.0), Vector3r (5.0, 0.0, 0.0)); + TwAddVarCB(MiniGL::getTweakBar(), "BendingStiffness", TW_TYPE_REAL, setBendingStiffness, getBendingStiffness, model, " label='Bending stiffness' min=0.0 step=0.1 precision=4 group='Bending' "); + TwAddVarCB(MiniGL::getTweakBar(), "DistanceStiffness", TW_TYPE_REAL, setDistanceStiffness, getDistanceStiffness, model, " label='Distance constraint stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + MiniGL::mainLoop(); Utilities::Timing::printAverageTimes(); @@ -72,20 +81,6 @@ int main( int argc, char **argv ) return 0; } -void initParameters() -{ - TwRemoveAllVars(MiniGL::getTweakBar()); - TweakBarParameters::cleanup(); - - MiniGL::initTweakBarParameters(); - - TweakBarParameters::createParameterGUI(); - TweakBarParameters::createParameterObjectGUI(base); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getModel()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getTimeStep()); -} - void reset() { Utilities::Timing::printAverageTimes(); @@ -135,72 +130,14 @@ void render () */ void createMesh() { - TriangleModel::ParticleMesh::UVs uvs; - uvs.resize(nRows*nCols); - - const Real dy = width / (Real)(nCols - 1); - const Real dx = height / (Real)(nRows - 1); - - Vector3r points[nRows*nCols]; - for (int i = 0; i < nRows; i++) - { - for (int j = 0; j < nCols; j++) - { - const Real y = (Real)dy*j; - const Real x = (Real)dx*i; - points[i*nCols + j] = Vector3r(x, 1.0, y); - - uvs[i*nCols + j][0] = x/width; - uvs[i*nCols + j][1] = y/height; - } - } - const int nIndices = 6 * (nRows - 1)*(nCols - 1); - - TriangleModel::ParticleMesh::UVIndices uvIndices; - uvIndices.resize(nIndices); - - unsigned int indices[nIndices]; - int index = 0; - for (int i = 0; i < nRows - 1; i++) - { - for (int j = 0; j < nCols - 1; j++) - { - int helper = 0; - if (i % 2 == j % 2) - helper = 1; - - indices[index] = i*nCols + j; - indices[index + 1] = i*nCols + j + 1; - indices[index + 2] = (i + 1)*nCols + j + helper; - - uvIndices[index] = i*nCols + j; - uvIndices[index + 1] = i*nCols + j + 1; - uvIndices[index + 2] = (i + 1)*nCols + j + helper; - index += 3; - - indices[index] = (i + 1)*nCols + j + 1; - indices[index + 1] = (i + 1)*nCols + j; - indices[index + 2] = i*nCols + j + 1 - helper; - - uvIndices[index] = (i + 1)*nCols + j + 1; - uvIndices[index + 1] = (i + 1)*nCols + j; - uvIndices[index + 2] = i*nCols + j + 1 - helper; - index += 3; - } - } - - GenericConstraintsModel *model = (GenericConstraintsModel*) Simulation::getCurrent()->getModel(); - model->addTriangleModel(nRows*nCols, nIndices / 3, &points[0], &indices[0], uvIndices, uvs); - - ParticleData &pd = model->getParticles(); - for (unsigned int i = 0; i < pd.getNumberOfParticles(); i++) - { - pd.setMass(i, 1.0); - } + GenericConstraintsModel* model = (GenericConstraintsModel*)Simulation::getCurrent()->getModel(); + model->addRegularTriangleModel(nCols, nRows, + Vector3r(0, 1, 0), AngleAxisr(M_PI * 0.5, Vector3r(1, 0, 0)).matrix(), Vector2r(width, height)); // Set mass of points to zero => make it static + ParticleData& pd = model->getParticles(); pd.setMass(0, 0.0); - pd.setMass((nRows-1)*nCols, 0.0); + pd.setMass(nRows-1, 0.0); // init constraints for (unsigned int cm = 0; cm < model->getTriangleModels().size(); cm++) @@ -216,7 +153,7 @@ void createMesh() const unsigned int v1 = edges[i].m_vert[0] + offset; const unsigned int v2 = edges[i].m_vert[1] + offset; - model->addGenericDistanceConstraint(v1, v2); + model->addGenericDistanceConstraint(v1, v2, distanceStiffness); } // bending constraints @@ -254,13 +191,35 @@ void createMesh() const unsigned int vertex2 = point2 + offset; const unsigned int vertex3 = edges[i].m_vert[0] + offset; const unsigned int vertex4 = edges[i].m_vert[1] + offset; - model->addGenericIsometricBendingConstraint(vertex1, vertex2, vertex3, vertex4); + model->addGenericIsometricBendingConstraint(vertex1, vertex2, vertex3, vertex4, bendingStiffness); } } } } - LOG_INFO << "Number of triangles: " << nIndices / 3; + LOG_INFO << "Number of triangles: " << model->getTriangleModels()[0]->getParticleMesh().numFaces(); LOG_INFO << "Number of vertices: " << nRows*nCols; +} + +void TW_CALL setBendingStiffness(const void* value, void* clientData) +{ + bendingStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); +} + +void TW_CALL getBendingStiffness(void* value, void* clientData) +{ + *(Real*)(value) = bendingStiffness; +} + +void TW_CALL setDistanceStiffness(const void* value, void* clientData) +{ + distanceStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(distanceStiffness); +} + +void TW_CALL getDistanceStiffness(void* value, void* clientData) +{ + *(Real*)(value) = distanceStiffness; } \ No newline at end of file diff --git a/Demos/GenericConstraintsDemos/GenericRigidBodyConstraintsDemo.cpp b/Demos/GenericConstraintsDemos/GenericRigidBodyConstraintsDemo.cpp index 7e4f0967..030b7051 100644 --- a/Demos/GenericConstraintsDemos/GenericRigidBodyConstraintsDemo.cpp +++ b/Demos/GenericConstraintsDemos/GenericRigidBodyConstraintsDemo.cpp @@ -191,7 +191,7 @@ void createBodyModel() GenericConstraintsModel *model = (GenericConstraintsModel*)Simulation::getCurrent()->getModel(); SimulationModel::RigidBodyVector &rb = model->getRigidBodies(); - string fileName = FileSystem::normalizePath(base->getDataPath() + "/models/cube.obj"); + string fileName = FileSystem::normalizePath(base->getExePath() + "/resources/models/cube.obj"); IndexedFaceMesh mesh; VertexData vd; loadObj(fileName, vd, mesh, Vector3r(width, height, depth)); diff --git a/Demos/PositionBasedElasticRodsDemo/CMakeLists.txt b/Demos/PositionBasedElasticRodsDemo/CMakeLists.txt index 95367d5d..fad270bd 100644 --- a/Demos/PositionBasedElasticRodsDemo/CMakeLists.txt +++ b/Demos/PositionBasedElasticRodsDemo/CMakeLists.txt @@ -1,5 +1,5 @@ set(SIMULATION_LINK_LIBRARIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) -set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) +set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils CopyPBDShaders) if(WIN32) set(SIMULATION_LINK_LIBRARIES opengl32.lib glu32.lib ${SIMULATION_LINK_LIBRARIES}) diff --git a/Demos/PositionBasedElasticRodsDemo/PositionBasedElasticRodsDemo.cpp b/Demos/PositionBasedElasticRodsDemo/PositionBasedElasticRodsDemo.cpp index 89e1ebab..119143ca 100644 --- a/Demos/PositionBasedElasticRodsDemo/PositionBasedElasticRodsDemo.cpp +++ b/Demos/PositionBasedElasticRodsDemo/PositionBasedElasticRodsDemo.cpp @@ -27,13 +27,15 @@ using namespace Eigen; using namespace std; using namespace Utilities; -void initParameters(); void timeStep (); void buildModel (); void createRod(); void render (); void reset(); +void TW_CALL setDistanceStiffness(const void* value, void* clientData); +void TW_CALL getDistanceStiffness(void* value, void* clientData); + void TW_CALL setRestDarbouxX(const void *value, void *clientData); void TW_CALL setRestDarbouxY(const void *value, void *clientData); void TW_CALL setRestDarbouxZ(const void *value, void *clientData); @@ -53,6 +55,7 @@ void TW_CALL setBendingAndTwistingStiffnessZ(const void *value, void *clientData DemoBase *base; PositionBasedElasticRodsTSC sim; +Real distanceStiffness = 1.0; const int numberOfPoints = 32; @@ -74,7 +77,7 @@ int main( int argc, char **argv ) buildModel(); - initParameters(); + base->createParameterGUI(); // OpenGL MiniGL::setClientIdleFunc (timeStep); @@ -82,6 +85,8 @@ int main( int argc, char **argv ) MiniGL::setClientSceneFunc(render); MiniGL::setViewport (40.0f, 0.1f, 500.0f, Vector3r (5.0, 5.0, 10.0), Vector3r (5.0, 0.0, 0.0)); + TwAddVarCB(MiniGL::getTweakBar(), "DistanceStiffness", TW_TYPE_REAL, setDistanceStiffness, getDistanceStiffness, model, " label='Distance constraint stiffness' min=0.0 step=0.1 precision=4 group='BendTwist' "); + TwAddVarCB(MiniGL::getTweakBar(), "RestDarbouxX", TW_TYPE_REAL, setRestDarbouxX, getRestDarbouxX, model, " label='Rest Darboux X' min=-1.0 max = 1.0 step=0.01 precision=2 group=BendTwist "); TwAddVarCB(MiniGL::getTweakBar(), "RestDarbouxY", TW_TYPE_REAL, setRestDarbouxY, getRestDarbouxY, model, " label='Rest Darboux Y' min=-1.0 max = 1.0 step=0.01 precision=2 group=BendTwist "); TwAddVarCB(MiniGL::getTweakBar(), "RestDarbouxZ", TW_TYPE_REAL, setRestDarbouxZ, getRestDarbouxZ, model, " label='Rest Darboux Z' min=-1.0 max = 1.0 step=0.01 precision=2 group=BendTwist "); @@ -102,20 +107,6 @@ int main( int argc, char **argv ) return 0; } -void initParameters() -{ - TwRemoveAllVars(MiniGL::getTweakBar()); - TweakBarParameters::cleanup(); - - MiniGL::initTweakBarParameters(); - - TweakBarParameters::createParameterGUI(); - TweakBarParameters::createParameterObjectGUI(base); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getModel()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getTimeStep()); -} - void reset() { Utilities::Timing::printAverageTimes(); @@ -215,7 +206,7 @@ void createRod() for (unsigned int i = 0; i < numberOfPoints - 1; i++) { - model->addDistanceConstraint(i, i + 1); + model->addDistanceConstraint(i, i + 1, distanceStiffness); model->addPerpendiculaBisectorConstraint(i, i + 1, i); model->addGhostPointEdgeDistanceConstraint(i, i + 1, i); @@ -297,3 +288,14 @@ void TW_CALL getBendingAndTwistingStiffnessZ(void *value, void *clientData) *(Real *)(value) = ((PositionBasedElasticRodsModel*)clientData)->getBendingAndTwistingStiffness()[2]; } +void TW_CALL setDistanceStiffness(const void* value, void* clientData) +{ + distanceStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(distanceStiffness); + ((SimulationModel*)clientData)->setConstraintValue(distanceStiffness); +} + +void TW_CALL getDistanceStiffness(void* value, void* clientData) +{ + *(Real*)(value) = distanceStiffness; +} \ No newline at end of file diff --git a/Demos/RigidBodyDemos/CMakeLists.txt b/Demos/RigidBodyDemos/CMakeLists.txt index 188621d4..a7b698b7 100644 --- a/Demos/RigidBodyDemos/CMakeLists.txt +++ b/Demos/RigidBodyDemos/CMakeLists.txt @@ -1,5 +1,5 @@ set(SIMULATION_LINK_LIBRARIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) -set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils) +set(SIMULATION_DEPENDENCIES AntTweakBar glfw PositionBasedDynamics Simulation Utils CopyPBDShaders CopyPBDModels) if(WIN32) set(SIMULATION_LINK_LIBRARIES opengl32.lib glu32.lib ${SIMULATION_LINK_LIBRARIES}) diff --git a/Demos/RigidBodyDemos/ChainDemo.cpp b/Demos/RigidBodyDemos/ChainDemo.cpp index 7fe02a26..ea3c7c64 100644 --- a/Demos/RigidBodyDemos/ChainDemo.cpp +++ b/Demos/RigidBodyDemos/ChainDemo.cpp @@ -189,15 +189,14 @@ void createBodyModel() { SimulationModel *model = Simulation::getCurrent()->getModel(); SimulationModel::RigidBodyVector &rb = model->getRigidBodies(); - SimulationModel::ConstraintVector &constraints = model->getConstraints(); - string fileName = FileSystem::normalizePath(base->getDataPath() + "/models/cube.obj"); + string fileName = FileSystem::normalizePath(base->getExePath() + "/resources/models/cube.obj"); IndexedFaceMesh mesh; VertexData vd; loadObj(fileName, vd, mesh, Vector3r(width, height, depth)); mesh.setFlatShading(true); - string fileName2 = FileSystem::normalizePath(base->getDataPath() + "/models/bunny_10k.obj"); + string fileName2 = FileSystem::normalizePath(base->getExePath() + "/resources/models/bunny_10k.obj"); IndexedFaceMesh mesh2; VertexData vd2; loadObj(fileName2, vd2, mesh2, Vector3r(2.0, 2.0, 2.0)); @@ -221,7 +220,6 @@ void createBodyModel() rb[numberOfBodies - 1] = new RigidBody(); rb[numberOfBodies - 1]->initBody(density, t, q, vd2, mesh2); - constraints.reserve(numberOfBodies - 1); for (unsigned int i = 0; i < numberOfBodies-1; i++) { model->addBallJoint(i, i + 1, Vector3r((Real)i*width + static_cast(0.5)*width, 0.0, 0.0)); diff --git a/Demos/RigidBodyDemos/JointDemo.cpp b/Demos/RigidBodyDemos/JointDemo.cpp index ae076b99..fe59b988 100644 --- a/Demos/RigidBodyDemos/JointDemo.cpp +++ b/Demos/RigidBodyDemos/JointDemo.cpp @@ -238,7 +238,7 @@ void createBodyModel() SimulationModel *model = Simulation::getCurrent()->getModel(); SimulationModel::RigidBodyVector &rb = model->getRigidBodies(); - string fileName = FileSystem::normalizePath(base->getDataPath() + "/models/cube.obj"); + string fileName = FileSystem::normalizePath(base->getExePath() + "/resources/models/cube.obj"); IndexedFaceMesh mesh; VertexData vd; loadObj(fileName, vd, mesh, Vector3r(width, height, depth)); diff --git a/Demos/SceneLoaderDemo/CMakeLists.txt b/Demos/SceneLoaderDemo/CMakeLists.txt index 3ed3abb7..6afda7a5 100644 --- a/Demos/SceneLoaderDemo/CMakeLists.txt +++ b/Demos/SceneLoaderDemo/CMakeLists.txt @@ -1,5 +1,5 @@ set(SIMULATION_LINK_LIBRARIES AntTweakBar glfw MD5 PositionBasedDynamics Simulation Utils) -set(SIMULATION_DEPENDENCIES AntTweakBar glfw MD5 PositionBasedDynamics Simulation Utils) +set(SIMULATION_DEPENDENCIES AntTweakBar glfw MD5 PositionBasedDynamics Simulation Utils CopyPBDShaders) if(WIN32) set(SIMULATION_LINK_LIBRARIES opengl32.lib glu32.lib ${SIMULATION_LINK_LIBRARIES}) diff --git a/Demos/SceneLoaderDemo/SceneLoaderDemo.cpp b/Demos/SceneLoaderDemo/SceneLoaderDemo.cpp index 3bf2fba9..73637d37 100644 --- a/Demos/SceneLoaderDemo/SceneLoaderDemo.cpp +++ b/Demos/SceneLoaderDemo/SceneLoaderDemo.cpp @@ -32,7 +32,6 @@ using namespace Eigen; using namespace std; using namespace Utilities; -void initParameters(); void timeStep (); void buildModel (); void readScene(const bool readFile); @@ -51,6 +50,34 @@ void TW_CALL setClothSimulationMethod(const void *value, void *clientData); void TW_CALL getClothSimulationMethod(void *value, void *clientData); void TW_CALL setSolidSimulationMethod(const void *value, void *clientData); void TW_CALL getSolidSimulationMethod(void *value, void *clientData); +void TW_CALL setBendingStiffness(const void* value, void* clientData); +void TW_CALL getBendingStiffness(void* value, void* clientData); +void TW_CALL setDistanceStiffness(const void* value, void* clientData); +void TW_CALL getDistanceStiffness(void* value, void* clientData); +void TW_CALL setXXStiffness(const void* value, void* clientData); +void TW_CALL getXXStiffness(void* value, void* clientData); +void TW_CALL setYYStiffness(const void* value, void* clientData); +void TW_CALL getYYStiffness(void* value, void* clientData); +void TW_CALL setXYStiffness(const void* value, void* clientData); +void TW_CALL getXYStiffness(void* value, void* clientData); +void TW_CALL setXYPoissonRatio(const void* value, void* clientData); +void TW_CALL getXYPoissonRatio(void* value, void* clientData); +void TW_CALL setYXPoissonRatio(const void* value, void* clientData); +void TW_CALL getYXPoissonRatio(void* value, void* clientData); +void TW_CALL setNormalizeStretch(const void* value, void* clientData); +void TW_CALL getNormalizeStretch(void* value, void* clientData); +void TW_CALL setNormalizeShear(const void* value, void* clientData); +void TW_CALL getNormalizeShear(void* value, void* clientData); +void TW_CALL setSolidStiffness(const void* value, void* clientData); +void TW_CALL getSolidStiffness(void* value, void* clientData); +void TW_CALL setSolidPoissonRatio(const void* value, void* clientData); +void TW_CALL getSolidPoissonRatio(void* value, void* clientData); +void TW_CALL setVolumeStiffness(const void* value, void* clientData); +void TW_CALL getVolumeStiffness(void* value, void* clientData); +void TW_CALL setSolidNormalizeStretch(const void* value, void* clientData); +void TW_CALL getSolidNormalizeStretch(void* value, void* clientData); +void TW_CALL setSolidNormalizeShear(const void* value, void* clientData); +void TW_CALL getSolidNormalizeShear(void* value, void* clientData); DemoBase *base; Vector3r camPos; @@ -60,6 +87,20 @@ CubicSDFCollisionDetection cd; short clothSimulationMethod = 2; short solidSimulationMethod = 2; short bendingMethod = 2; +Real distanceStiffness = 1.0; +Real xxStiffness = 1.0; +Real yyStiffness = 1.0; +Real xyStiffness = 1.0; +Real xyPoissonRatio = 0.3; +Real yxPoissonRatio = 0.3; +bool normalizeStretch = false; +bool normalizeShear = false; +Real bendingStiffness = 0.01; +Real solidStiffness = 1.0; +Real solidPoissonRatio = 0.3; +bool solidNormalizeStretch = false; +bool solidNormalizeShear = false; +Real volumeStiffness = 1.0; bool enableExportOBJ = false; unsigned int exportFPS = 25; Real nextFrameTime = 0.0; @@ -72,13 +113,15 @@ int main( int argc, char **argv ) REPORT_MEMORY_LEAKS std::string exePath = FileSystem::getProgramPath(); - std::string dataPath = exePath + "/" + std::string(PBD_DATA_PATH); std::string sceneFileName; if (argc > 1) sceneFileName = string(argv[1]); else - sceneFileName = FileSystem::normalizePath(dataPath + sceneFileName); + { + std::cerr << "Usage: SceneLoaderDemo [scene_file]\n"; + exit(1); + } base = new DemoBase(); base->init(argc, argv, sceneFileName.c_str()); @@ -89,7 +132,7 @@ int main( int argc, char **argv ) buildModel(); - initParameters(); + base->createParameterGUI(); base->readParameters(); // OpenGL @@ -97,6 +140,9 @@ int main( int argc, char **argv ) MiniGL::addKeyFunc('r', reset); MiniGL::setClientSceneFunc(render); MiniGL::setViewport(40.0f, 0.1f, 500.0f, camPos, camLookat); + + SimulationModel::TriangleModelVector &triModels = model->getTriangleModels(); + SimulationModel::TetModelVector &tetModels = model->getTetModels(); TwType enumType = TwDefineEnum("VelocityUpdateMethodType", NULL, 0); TwAddVarCB(MiniGL::getTweakBar(), "ContactTolerance", TW_TYPE_REAL, setContactTolerance, getContactTolerance, &cd, " label='Contact tolerance' min=0.0 step=0.001 precision=3 group=Simulation "); @@ -105,11 +151,33 @@ int main( int argc, char **argv ) TwType enumType2 = TwDefineEnum("ClothSimulationMethodType", NULL, 0); TwAddVarCB(MiniGL::getTweakBar(), "ClothSimulationMethod", enumType2, setClothSimulationMethod, getClothSimulationMethod, &clothSimulationMethod, " label='Cloth sim. method' enum='0 {None}, 1 {Distance constraints}, 2 {FEM based PBD}, 3 {Strain based dynamics}, 4 {XPBD distance constraints}' group=Simulation"); - TwType enumType3 = TwDefineEnum("SolidSimulationMethodType", NULL, 0); - TwAddVarCB(MiniGL::getTweakBar(), "SolidSimulationMethod", enumType3, setSolidSimulationMethod, getSolidSimulationMethod, &solidSimulationMethod, - " label='Solid sim. method' enum='0 {None}, 1 {Volume constraints}, 2 {FEM based PBD}, 3 {Strain based dynamics (no inversion handling)}, 4 {Shape matching (no inversion handling)}, 5 {XPBD volume constraints}' group=Simulation"); - TwType enumType4 = TwDefineEnum("BendingMethodType", NULL, 0); - TwAddVarCB(MiniGL::getTweakBar(), "BendingMethod", enumType4, setBendingMethod, getBendingMethod, &bendingMethod, " label='Bending method' enum='0 {None}, 1 {Dihedral angle}, 2 {Isometric bending}, 3 {XPBD isometric bending}' group=Bending"); + if (tetModels.size() > 0) + { + TwType enumType3 = TwDefineEnum("SolidSimulationMethodType", NULL, 0); + TwAddVarCB(MiniGL::getTweakBar(), "SolidSimulationMethod", enumType3, setSolidSimulationMethod, getSolidSimulationMethod, &solidSimulationMethod, + " label='Solid sim. method' enum='0 {None}, 1 {Volume constraints}, 2 {FEM based PBD}, 3 {Strain based dynamics (no inversion handling)}, 4 {Shape matching (no inversion handling)}, 5 {XPBD volume constraints}' group=Simulation"); + TwAddVarCB(MiniGL::getTweakBar(), "stiffness", TW_TYPE_REAL, setSolidStiffness, getSolidStiffness, model, " label='Stiffness' min=0.0 step=0.1 precision=4 group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "poissonRatio", TW_TYPE_REAL, setSolidPoissonRatio, getSolidPoissonRatio, model, " label='Poisson ratio' min=0.0 step=0.1 precision=4 group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeStretch", TW_TYPE_BOOL32, setSolidNormalizeStretch, getSolidNormalizeStretch, model, " label='Normalize stretch' group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeShear", TW_TYPE_BOOL32, setSolidNormalizeShear, getSolidNormalizeShear, model, " label='Normalize shear' group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "volumeStiffness", TW_TYPE_REAL, setVolumeStiffness, getVolumeStiffness, model, " label='Volume stiffness' min=0.0 step=0.1 precision=4 group='Solid' "); + } + if (triModels.size() > 0) + { + TwType enumType4 = TwDefineEnum("BendingMethodType", NULL, 0); + TwAddVarCB(MiniGL::getTweakBar(), "BendingMethod", enumType4, setBendingMethod, getBendingMethod, &bendingMethod, " label='Bending method' enum='0 {None}, 1 {Dihedral angle}, 2 {Isometric bending}, 3 {XPBD isometric bending}' group=Bending"); + TwAddVarCB(MiniGL::getTweakBar(), "BendingStiffness", TW_TYPE_REAL, setBendingStiffness, getBendingStiffness, model, " label='Bending stiffness' min=0.0 step=0.1 precision=4 group='Bending' "); + TwAddVarCB(MiniGL::getTweakBar(), "DistanceStiffness", TW_TYPE_REAL, setDistanceStiffness, getDistanceStiffness, model, " label='Distance constraint stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "xxStiffness", TW_TYPE_REAL, setXXStiffness, getXXStiffness, model, " label='xx stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "yyStiffness", TW_TYPE_REAL, setYYStiffness, getYYStiffness, model, " label='yy stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "xyStiffness", TW_TYPE_REAL, setXYStiffness, getXYStiffness, model, " label='xy stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "xyPoissonRatio", TW_TYPE_REAL, setXYPoissonRatio, getXYPoissonRatio, model, " label='xy Poisson ratio' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "yxPoissonRatio", TW_TYPE_REAL, setYXPoissonRatio, getYXPoissonRatio, model, " label='yx Poisson ratio' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeStretch", TW_TYPE_BOOL32, setNormalizeStretch, getNormalizeStretch, model, " label='Normalize stretch' group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeShear", TW_TYPE_BOOL32, setNormalizeShear, getNormalizeShear, model, " label='Normalize shear' group='Cloth' "); + } + TwAddVarRW(MiniGL::getTweakBar(), "ExportOBJ", TW_TYPE_BOOL32, &enableExportOBJ, " label='Export OBJ'"); + TwAddVarRW(MiniGL::getTweakBar(), "ExportFPS", TW_TYPE_UINT32, &exportFPS, " label='Export FPS'"); MiniGL::mainLoop(); @@ -125,23 +193,6 @@ int main( int argc, char **argv ) return 0; } -void initParameters() -{ - TwRemoveAllVars(MiniGL::getTweakBar()); - TweakBarParameters::cleanup(); - - MiniGL::initTweakBarParameters(); - - TwAddVarRW(MiniGL::getTweakBar(), "ExportOBJ", TW_TYPE_BOOL32, &enableExportOBJ, " label='Export OBJ'"); - TwAddVarRW(MiniGL::getTweakBar(), "ExportFPS", TW_TYPE_UINT32, &exportFPS, " label='Export FPS'"); - - TweakBarParameters::createParameterGUI(); - TweakBarParameters::createParameterObjectGUI(base); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getModel()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getTimeStep()); -} - void reset() { const Real h = TimeManager::getCurrent()->getTimeStepSize(); @@ -217,113 +268,16 @@ void initTriangleModelConstraints() SimulationModel *model = Simulation::getCurrent()->getModel(); for (unsigned int cm = 0; cm < model->getTriangleModels().size(); cm++) { - model->setValue(SimulationModel::CLOTH_STIFFNESS, 1.0); - model->setValue(SimulationModel::CLOTH_BENDING_STIFFNESS, 0.01); - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - if (clothSimulationMethod == 1) - { - const unsigned int nEdges = model->getTriangleModels()[cm]->getParticleMesh().numEdges(); - const IndexedFaceMesh::Edge *edges = model->getTriangleModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint(v1, v2); - } - } - else if (clothSimulationMethod == 2) - { - - TriangleModel::ParticleMesh &mesh = model->getTriangleModels()[cm]->getParticleMesh(); - const unsigned int *tris = mesh.getFaces().data(); - const unsigned int nFaces = mesh.numFaces(); - for (unsigned int i = 0; i < nFaces; i++) - { - const unsigned int v1 = tris[3 * i] + offset; - const unsigned int v2 = tris[3 * i + 1] + offset; - const unsigned int v3 = tris[3 * i + 2] + offset; - model->addFEMTriangleConstraint(v1, v2, v3); - } - } - else if (clothSimulationMethod == 3) - { - TriangleModel::ParticleMesh &mesh = model->getTriangleModels()[cm]->getParticleMesh(); - const unsigned int *tris = mesh.getFaces().data(); - const unsigned int nFaces = mesh.numFaces(); - for (unsigned int i = 0; i < nFaces; i++) - { - const unsigned int v1 = tris[3 * i] + offset; - const unsigned int v2 = tris[3 * i + 1] + offset; - const unsigned int v3 = tris[3 * i + 2] + offset; - model->addStrainTriangleConstraint(v1, v2, v3); - } - } - else if (clothSimulationMethod == 4) - { - model->setValue(SimulationModel::CLOTH_STIFFNESS, 100000); - const unsigned int nEdges = model->getTriangleModels()[cm]->getParticleMesh().numEdges(); - const IndexedFaceMesh::Edge* edges = model->getTriangleModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint_XPBD(v1, v2); - } - } - if (bendingMethod != 0) - { - TriangleModel::ParticleMesh &mesh = model->getTriangleModels()[cm]->getParticleMesh(); - unsigned int nEdges = mesh.numEdges(); - const TriangleModel::ParticleMesh::Edge *edges = mesh.getEdges().data(); - const unsigned int *tris = mesh.getFaces().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const int tri1 = edges[i].m_face[0]; - const int tri2 = edges[i].m_face[1]; - if ((tri1 != 0xffffffff) && (tri2 != 0xffffffff)) - { - // Find the triangle points which do not lie on the axis - const int axisPoint1 = edges[i].m_vert[0]; - const int axisPoint2 = edges[i].m_vert[1]; - int point1 = -1; - int point2 = -1; - for (int j = 0; j < 3; j++) - { - if ((tris[3 * tri1 + j] != axisPoint1) && (tris[3 * tri1 + j] != axisPoint2)) - { - point1 = tris[3 * tri1 + j]; - break; - } - } - for (int j = 0; j < 3; j++) - { - if ((tris[3 * tri2 + j] != axisPoint1) && (tris[3 * tri2 + j] != axisPoint2)) - { - point2 = tris[3 * tri2 + j]; - break; - } - } - if ((point1 != -1) && (point2 != -1)) - { - const unsigned int vertex1 = point1 + offset; - const unsigned int vertex2 = point2 + offset; - const unsigned int vertex3 = edges[i].m_vert[0] + offset; - const unsigned int vertex4 = edges[i].m_vert[1] + offset; - if (bendingMethod == 1) - model->addDihedralConstraint(vertex1, vertex2, vertex3, vertex4); - else if (bendingMethod == 2) - model->addIsometricBendingConstraint(vertex1, vertex2, vertex3, vertex4); - else if (bendingMethod == 3) - { - model->setValue(SimulationModel::CLOTH_BENDING_STIFFNESS, 100.0); - model->addIsometricBendingConstraint_XPBD(vertex1, vertex2, vertex3, vertex4); - } - } - } - } - } + distanceStiffness = 1.0; + if (clothSimulationMethod == 4) + distanceStiffness = 100000; + model->addClothConstraints(model->getTriangleModels()[cm], clothSimulationMethod, distanceStiffness, xxStiffness, + yyStiffness, xyStiffness, xyPoissonRatio, yxPoissonRatio, normalizeStretch, normalizeShear); + + bendingStiffness = 0.01; + if (bendingMethod == 3) + bendingStiffness = 100.0; + model->addBendingConstraints(model->getTriangleModels()[cm], bendingMethod, bendingStiffness); } } @@ -331,103 +285,17 @@ void initTetModelConstraints() { // init constraints SimulationModel *model = Simulation::getCurrent()->getModel(); - model->setValue(SimulationModel::CLOTH_STIFFNESS, 1.0); - model->setValue(SimulationModel::SOLID_STIFFNESS, 1.0); + solidStiffness = 1.0; + if (solidSimulationMethod == 5) + solidStiffness = 100000; + + volumeStiffness = 1.0; + if (solidSimulationMethod == 5) + volumeStiffness = 100000; for (unsigned int cm = 0; cm < model->getTetModels().size(); cm++) { - const unsigned int offset = model->getTetModels()[cm]->getIndexOffset(); - const unsigned int nTets = model->getTetModels()[cm]->getParticleMesh().numTets(); - const unsigned int *tets = model->getTetModels()[cm]->getParticleMesh().getTets().data(); - const IndexedTetMesh::VertexTets *vTets = model->getTetModels()[cm]->getParticleMesh().getVertexTets().data(); - if (solidSimulationMethod == 1) - { - const unsigned int offset = model->getTetModels()[cm]->getIndexOffset(); - const unsigned int nEdges = model->getTetModels()[cm]->getParticleMesh().numEdges(); - const IndexedTetMesh::Edge *edges = model->getTetModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint(v1, v2); - } - - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i] + offset; - const unsigned int v2 = tets[4 * i + 1] + offset; - const unsigned int v3 = tets[4 * i + 2] + offset; - const unsigned int v4 = tets[4 * i + 3] + offset; - - model->addVolumeConstraint(v1, v2, v3, v4); - } - } - else if (solidSimulationMethod == 2) - { - TetModel::ParticleMesh &mesh = model->getTetModels()[cm]->getParticleMesh(); - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i] + offset; - const unsigned int v2 = tets[4 * i + 1] + offset; - const unsigned int v3 = tets[4 * i + 2] + offset; - const unsigned int v4 = tets[4 * i + 3] + offset; - - model->addFEMTetConstraint(v1, v2, v3, v4); - } - } - else if (solidSimulationMethod == 3) - { - TetModel::ParticleMesh &mesh = model->getTetModels()[cm]->getParticleMesh(); - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i] + offset; - const unsigned int v2 = tets[4 * i + 1] + offset; - const unsigned int v3 = tets[4 * i + 2] + offset; - const unsigned int v4 = tets[4 * i + 3] + offset; - - model->addStrainTetConstraint(v1, v2, v3, v4); - } - } - else if (solidSimulationMethod == 4) - { - TetModel::ParticleMesh &mesh = model->getTetModels()[cm]->getParticleMesh(); - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v[4] = { tets[4 * i] + offset, - tets[4 * i + 1] + offset, - tets[4 * i + 2] + offset, - tets[4 * i + 3] + offset }; - // Important: Divide position correction by the number of clusters - // which contain the vertex. - const unsigned int nc[4] = { vTets[v[0]].m_numTets, vTets[v[1]].m_numTets, vTets[v[2]].m_numTets, vTets[v[3]].m_numTets }; - model->addShapeMatchingConstraint(4, v, nc); - } - } - else if (solidSimulationMethod == 5) - { - model->setValue(SimulationModel::CLOTH_STIFFNESS, 100000); - model->setValue(SimulationModel::SOLID_STIFFNESS, 100000); - const unsigned int offset = model->getTetModels()[cm]->getIndexOffset(); - const unsigned int nEdges = model->getTetModels()[cm]->getParticleMesh().numEdges(); - const IndexedTetMesh::Edge* edges = model->getTetModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint_XPBD(v1, v2); - } - - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i]; - const unsigned int v2 = tets[4 * i + 1]; - const unsigned int v3 = tets[4 * i + 2]; - const unsigned int v4 = tets[4 * i + 3]; - - model->addVolumeConstraint_XPBD(v1, v2, v3, v4); - } - } + model->addSolidConstraints(model->getTetModels()[cm], solidSimulationMethod, solidStiffness, + solidPoissonRatio, volumeStiffness, solidNormalizeStretch, solidNormalizeShear); } } @@ -592,19 +460,20 @@ void readScene(const bool readFile) model->setContactStiffnessParticleRigidBody(data.m_contactStiffnessParticleRigidBody); - model->setValue(SimulationModel::CLOTH_BENDING_STIFFNESS, data.m_cloth_bendingStiffness); - model->setValue(SimulationModel::CLOTH_STIFFNESS_XX, data.m_cloth_xxStiffness); - model->setValue(SimulationModel::CLOTH_STIFFNESS_YY, data.m_cloth_yyStiffness); - model->setValue(SimulationModel::CLOTH_STIFFNESS_XY, data.m_cloth_xyStiffness); - model->setValue(SimulationModel::CLOTH_POISSON_RATIO_XY, data.m_cloth_xyPoissonRatio); - model->setValue(SimulationModel::CLOTH_POISSON_RATIO_YX, data.m_cloth_yxPoissonRatio); - model->setValue(SimulationModel::CLOTH_NORMALIZE_STRETCH, data.m_cloth_normalizeStretch); - model->setValue(SimulationModel::CLOTH_NORMALIZE_SHEAR, data.m_cloth_normalizeShear); + bendingStiffness = data.m_cloth_bendingStiffness; + xxStiffness = data.m_cloth_xxStiffness; + yyStiffness = data.m_cloth_yyStiffness; + xyStiffness = data.m_cloth_xyStiffness; + xyPoissonRatio = data.m_cloth_xyPoissonRatio; + yxPoissonRatio = data.m_cloth_yxPoissonRatio; + normalizeStretch = data.m_cloth_normalizeStretch; + normalizeShear = data.m_cloth_normalizeShear; - model->setValue(SimulationModel::SOLID_STIFFNESS, data.m_solid_stiffness); - model->setValue(SimulationModel::SOLID_POISSON_RATIO, data.m_solid_poissonRatio); - model->setValue(SimulationModel::SOLID_NORMALIZE_STRETCH, data.m_solid_normalizeStretch); - model->setValue(SimulationModel::SOLID_NORMALIZE_SHEAR, data.m_solid_normalizeShear); + solidStiffness = data.m_solid_stiffness; + solidPoissonRatio = data.m_solid_poissonRatio; + solidNormalizeStretch = data.m_solid_normalizeStretch; + solidNormalizeShear = data.m_solid_normalizeShear; + volumeStiffness = data.m_volume_stiffness; ////////////////////////////////////////////////////////////////////////// // rigid bodies @@ -1306,7 +1175,6 @@ void TW_CALL setSolidSimulationMethod(const void *value, void *clientData) { const short val = *(const short *)(value); *((short*)clientData) = val; - reset(); } @@ -1315,3 +1183,169 @@ void TW_CALL getSolidSimulationMethod(void *value, void *clientData) *(short *)(value) = *((short*)clientData); } +void TW_CALL setBendingStiffness(const void* value, void* clientData) +{ + bendingStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); +} + +void TW_CALL getBendingStiffness(void* value, void* clientData) +{ + *(Real*)(value) = bendingStiffness; +} + +void TW_CALL setDistanceStiffness(const void* value, void* clientData) +{ + distanceStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(distanceStiffness); + ((SimulationModel*)clientData)->setConstraintValue(distanceStiffness); +} + +void TW_CALL getDistanceStiffness(void* value, void* clientData) +{ + *(Real*)(value) = distanceStiffness; +} + +void TW_CALL setXXStiffness(const void* value, void* clientData) +{ + xxStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(xxStiffness); + ((SimulationModel*)clientData)->setConstraintValue(xxStiffness); +} + +void TW_CALL getXXStiffness(void* value, void* clientData) +{ + *(Real*)(value) = xxStiffness; +} + +void TW_CALL getYYStiffness(void* value, void* clientData) +{ + *(Real*)(value) = yyStiffness; +} + +void TW_CALL setYYStiffness(const void* value, void* clientData) +{ + yyStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(yyStiffness); + ((SimulationModel*)clientData)->setConstraintValue(yyStiffness); +} + +void TW_CALL getXYStiffness(void* value, void* clientData) +{ + *(Real*)(value) = xyStiffness; +} + +void TW_CALL setXYStiffness(const void* value, void* clientData) +{ + xyStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(xyStiffness); + ((SimulationModel*)clientData)->setConstraintValue(xyStiffness); +} + +void TW_CALL getXYPoissonRatio(void* value, void* clientData) +{ + *(Real*)(value) = xyPoissonRatio; +} + +void TW_CALL setXYPoissonRatio(const void* value, void* clientData) +{ + xyPoissonRatio = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(xyPoissonRatio); +} + +void TW_CALL getYXPoissonRatio(void* value, void* clientData) +{ + *(Real*)(value) = yxPoissonRatio; +} + +void TW_CALL setYXPoissonRatio(const void* value, void* clientData) +{ + yxPoissonRatio = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(yxPoissonRatio); +} + +void TW_CALL getNormalizeStretch(void* value, void* clientData) +{ + *(bool*)(value) = normalizeStretch; +} + +void TW_CALL setNormalizeStretch(const void* value, void* clientData) +{ + normalizeStretch = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(normalizeStretch); +} + +void TW_CALL getNormalizeShear(void* value, void* clientData) +{ + *(bool*)(value) = normalizeShear; +} + +void TW_CALL setNormalizeShear(const void* value, void* clientData) +{ + normalizeShear = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(normalizeShear); +} + + +void TW_CALL setSolidStiffness(const void* value, void* clientData) +{ + solidStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(solidStiffness); + ((SimulationModel*)clientData)->setConstraintValue(solidStiffness); + ((SimulationModel*)clientData)->setConstraintValue(solidStiffness); + ((SimulationModel*)clientData)->setConstraintValue(solidStiffness); + ((SimulationModel*)clientData)->setConstraintValue(solidStiffness); + ((SimulationModel*)clientData)->setConstraintValue(solidStiffness); +} + +void TW_CALL getSolidStiffness(void* value, void* clientData) +{ + *(Real*)(value) = solidStiffness; +} + +void TW_CALL setVolumeStiffness(const void* value, void* clientData) +{ + volumeStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(volumeStiffness); + ((SimulationModel*)clientData)->setConstraintValue(volumeStiffness); +} + +void TW_CALL getVolumeStiffness(void* value, void* clientData) +{ + *(Real*)(value) = volumeStiffness; +} + +void TW_CALL getSolidPoissonRatio(void* value, void* clientData) +{ + *(Real*)(value) = solidPoissonRatio; +} + +void TW_CALL setSolidPoissonRatio(const void* value, void* clientData) +{ + solidPoissonRatio = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(solidPoissonRatio); +} + +void TW_CALL getSolidNormalizeStretch(void* value, void* clientData) +{ + *(bool*)(value) = solidNormalizeStretch; +} + +void TW_CALL setSolidNormalizeStretch(const void* value, void* clientData) +{ + solidNormalizeStretch = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(solidNormalizeStretch); +} + +void TW_CALL getSolidNormalizeShear(void* value, void* clientData) +{ + *(bool*)(value) = solidNormalizeShear; +} + +void TW_CALL setSolidNormalizeShear(const void* value, void* clientData) +{ + solidNormalizeShear = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(solidNormalizeShear); +} \ No newline at end of file diff --git a/Demos/StiffRodsDemos/CMakeLists.txt b/Demos/StiffRodsDemos/CMakeLists.txt index ac0e0264..5be076e4 100644 --- a/Demos/StiffRodsDemos/CMakeLists.txt +++ b/Demos/StiffRodsDemos/CMakeLists.txt @@ -1,5 +1,5 @@ set(SIMULATION_LINK_LIBRARIES AntTweakBar glfw MD5 PositionBasedDynamics Simulation Utils) -set(SIMULATION_DEPENDENCIES AntTweakBar glfw MD5 PositionBasedDynamics Simulation Utils) +set(SIMULATION_DEPENDENCIES AntTweakBar glfw MD5 PositionBasedDynamics Simulation Utils CopyPBDShaders CopyPBDModels CopyPBDScenes) if(WIN32) set(SIMULATION_LINK_LIBRARIES opengl32.lib glu32.lib ${SIMULATION_LINK_LIBRARIES}) diff --git a/Demos/StiffRodsDemos/DirectPositionBasedSolverForStiffRodsDemo.cpp b/Demos/StiffRodsDemos/DirectPositionBasedSolverForStiffRodsDemo.cpp index 2664a289..536a9304 100644 --- a/Demos/StiffRodsDemos/DirectPositionBasedSolverForStiffRodsDemo.cpp +++ b/Demos/StiffRodsDemos/DirectPositionBasedSolverForStiffRodsDemo.cpp @@ -34,7 +34,6 @@ using namespace Eigen; using namespace std; using namespace Utilities; -void initParameters(); void timeStep(); void buildModel(); void readScene(); @@ -53,7 +52,34 @@ void TW_CALL setClothSimulationMethod(const void *value, void *clientData); void TW_CALL getClothSimulationMethod(void *value, void *clientData); void TW_CALL setSolidSimulationMethod(const void *value, void *clientData); void TW_CALL getSolidSimulationMethod(void *value, void *clientData); - +void TW_CALL setBendingStiffness(const void* value, void* clientData); +void TW_CALL getBendingStiffness(void* value, void* clientData); +void TW_CALL setDistanceStiffness(const void* value, void* clientData); +void TW_CALL getDistanceStiffness(void* value, void* clientData); +void TW_CALL setXXStiffness(const void* value, void* clientData); +void TW_CALL getXXStiffness(void* value, void* clientData); +void TW_CALL setYYStiffness(const void* value, void* clientData); +void TW_CALL getYYStiffness(void* value, void* clientData); +void TW_CALL setXYStiffness(const void* value, void* clientData); +void TW_CALL getXYStiffness(void* value, void* clientData); +void TW_CALL setXYPoissonRatio(const void* value, void* clientData); +void TW_CALL getXYPoissonRatio(void* value, void* clientData); +void TW_CALL setYXPoissonRatio(const void* value, void* clientData); +void TW_CALL getYXPoissonRatio(void* value, void* clientData); +void TW_CALL setNormalizeStretch(const void* value, void* clientData); +void TW_CALL getNormalizeStretch(void* value, void* clientData); +void TW_CALL setNormalizeShear(const void* value, void* clientData); +void TW_CALL getNormalizeShear(void* value, void* clientData); +void TW_CALL setSolidStiffness(const void* value, void* clientData); +void TW_CALL getSolidStiffness(void* value, void* clientData); +void TW_CALL setSolidPoissonRatio(const void* value, void* clientData); +void TW_CALL getSolidPoissonRatio(void* value, void* clientData); +void TW_CALL setVolumeStiffness(const void* value, void* clientData); +void TW_CALL getVolumeStiffness(void* value, void* clientData); +void TW_CALL setSolidNormalizeStretch(const void* value, void* clientData); +void TW_CALL getSolidNormalizeStretch(void* value, void* clientData); +void TW_CALL setSolidNormalizeShear(const void* value, void* clientData); +void TW_CALL getSolidNormalizeShear(void* value, void* clientData); void singleStep(); @@ -64,6 +90,20 @@ CubicSDFCollisionDetection cd; short clothSimulationMethod = 2; short solidSimulationMethod = 2; short bendingMethod = 2; +Real distanceStiffness = 1.0; +Real xxStiffness = 1.0; +Real yyStiffness = 1.0; +Real xyStiffness = 1.0; +Real xyPoissonRatio = 0.3; +Real yxPoissonRatio = 0.3; +bool normalizeStretch = false; +bool normalizeShear = false; +Real bendingStiffness = 0.01; +Real solidStiffness = 1.0; +Real solidPoissonRatio = 0.3; +bool solidNormalizeStretch = false; +bool solidNormalizeShear = false; +Real volumeStiffness = 1.0; string sceneFileName = "/scenes/Wilberforce_scene.json"; string sceneName; @@ -77,12 +117,11 @@ int main(int argc, char **argv) REPORT_MEMORY_LEAKS; std::string exePath = FileSystem::getProgramPath(); - std::string dataPath = exePath + "/" + std::string(PBD_DATA_PATH); if (argc > 1) sceneFileName = string(argv[1]); else - sceneFileName = FileSystem::normalizePath(dataPath + sceneFileName); + sceneFileName = FileSystem::normalizePath(exePath + "/resources" + sceneFileName); base = new DemoBase(); base->init(argc, argv, sceneFileName.c_str()); @@ -94,7 +133,7 @@ int main(int argc, char **argv) buildModel(); - initParameters(); + base->createParameterGUI(); base->readParameters(); // OpenGL @@ -114,17 +153,31 @@ int main(int argc, char **argv) TwType enumType2 = TwDefineEnum("ClothSimulationMethodType", NULL, 0); TwAddVarCB(MiniGL::getTweakBar(), "ClothSimulationMethod", enumType2, setClothSimulationMethod, getClothSimulationMethod, &clothSimulationMethod, " label='Cloth sim. method' enum='0 {None}, 1 {Distance constraints}, 2 {FEM based PBD}, 3 {Strain based dynamics}, 4 {XPBD distance constraints}' group=Simulation"); + TwAddVarCB(MiniGL::getTweakBar(), "DistanceStiffness", TW_TYPE_REAL, setDistanceStiffness, getDistanceStiffness, model, " label='Distance constraint stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "xxStiffness", TW_TYPE_REAL, setXXStiffness, getXXStiffness, model, " label='xx stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "yyStiffness", TW_TYPE_REAL, setYYStiffness, getYYStiffness, model, " label='yy stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "xyStiffness", TW_TYPE_REAL, setXYStiffness, getXYStiffness, model, " label='xy stiffness' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "xyPoissonRatio", TW_TYPE_REAL, setXYPoissonRatio, getXYPoissonRatio, model, " label='xy Poisson ratio' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "yxPoissonRatio", TW_TYPE_REAL, setYXPoissonRatio, getYXPoissonRatio, model, " label='yx Poisson ratio' min=0.0 step=0.1 precision=4 group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeStretch", TW_TYPE_BOOL32, setNormalizeStretch, getNormalizeStretch, model, " label='Normalize stretch' group='Cloth' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeShear", TW_TYPE_BOOL32, setNormalizeShear, getNormalizeShear, model, " label='Normalize shear' group='Cloth' "); } if (tetModels.size() > 0) { - TwType enumType3 = TwDefineEnum("SolidSimulationMethodType", NULL, 0); - TwAddVarCB(MiniGL::getTweakBar(), "SolidSimulationMethod", enumType3, setSolidSimulationMethod, getSolidSimulationMethod, &solidSimulationMethod, - " label='Solid sim. method' enum='0 {None}, 1 {Volume constraints}, 2 {FEM based PBD}, 3 {Strain based dynamics (no inversion handling)}, 4 {Shape matching (no inversion handling)}, 5 {XPBD volume constraints}' group=Simulation"); + TwType enumType3 = TwDefineEnum("SolidSimulationMethodType", NULL, 0); + TwAddVarCB(MiniGL::getTweakBar(), "SolidSimulationMethod", enumType3, setSolidSimulationMethod, getSolidSimulationMethod, &solidSimulationMethod, + " label='Solid sim. method' enum='0 {None}, 1 {Volume constraints}, 2 {FEM based PBD}, 3 {Strain based dynamics (no inversion handling)}, 4 {Shape matching (no inversion handling)}, 5 {XPBD volume constraints}' group=Simulation"); + TwAddVarCB(MiniGL::getTweakBar(), "stiffness", TW_TYPE_REAL, setSolidStiffness, getSolidStiffness, model, " label='Stiffness' min=0.0 step=0.1 precision=4 group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "poissonRatio", TW_TYPE_REAL, setSolidPoissonRatio, getSolidPoissonRatio, model, " label='Poisson ratio' min=0.0 step=0.1 precision=4 group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeStretch", TW_TYPE_BOOL32, setSolidNormalizeStretch, getSolidNormalizeStretch, model, " label='Normalize stretch' group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "normalizeShear", TW_TYPE_BOOL32, setSolidNormalizeShear, getSolidNormalizeShear, model, " label='Normalize shear' group='Solid' "); + TwAddVarCB(MiniGL::getTweakBar(), "volumeStiffness", TW_TYPE_REAL, setVolumeStiffness, getVolumeStiffness, model, " label='Volume stiffness' min=0.0 step=0.1 precision=4 group='Solid' "); } if (triModels.size() > 0) { TwType enumType4 = TwDefineEnum("BendingMethodType", NULL, 0); TwAddVarCB(MiniGL::getTweakBar(), "BendingMethod", enumType4, setBendingMethod, getBendingMethod, &bendingMethod, " label='Bending method' enum='0 {None}, 1 {Dihedral angle}, 2 {Isometric bending}, 3 {XPBD isometric bending}' group=Bending"); + TwAddVarCB(MiniGL::getTweakBar(), "BendingStiffness", TW_TYPE_REAL, setBendingStiffness, getBendingStiffness, model, " label='Bending stiffness' min=0.0 step=0.1 precision=4 group='Bending' "); } MiniGL::mainLoop(); @@ -141,20 +194,6 @@ int main(int argc, char **argv) return 0; } -void initParameters() -{ - TwRemoveAllVars(MiniGL::getTweakBar()); - TweakBarParameters::cleanup(); - - MiniGL::initTweakBarParameters(); - - TweakBarParameters::createParameterGUI(); - TweakBarParameters::createParameterObjectGUI(base); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getModel()); - TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getTimeStep()); -} - void reset() { Utilities::Timing::printAverageTimes(); @@ -241,216 +280,34 @@ void initTriangleModelConstraints() SimulationModel *model = Simulation::getCurrent()->getModel(); for (unsigned int cm = 0; cm < model->getTriangleModels().size(); cm++) { - model->setValue(SimulationModel::CLOTH_STIFFNESS, 1.0); - model->setValue(SimulationModel::CLOTH_BENDING_STIFFNESS, 0.01); - const unsigned int offset = model->getTriangleModels()[cm]->getIndexOffset(); - if (clothSimulationMethod == 1) - { - const unsigned int nEdges = model->getTriangleModels()[cm]->getParticleMesh().numEdges(); - const IndexedFaceMesh::Edge *edges = model->getTriangleModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint(v1, v2); - } - } - else if (clothSimulationMethod == 2) - { - - TriangleModel::ParticleMesh &mesh = model->getTriangleModels()[cm]->getParticleMesh(); - const unsigned int *tris = mesh.getFaces().data(); - const unsigned int nFaces = mesh.numFaces(); - for (unsigned int i = 0; i < nFaces; i++) - { - const unsigned int v1 = tris[3 * i] + offset; - const unsigned int v2 = tris[3 * i + 1] + offset; - const unsigned int v3 = tris[3 * i + 2] + offset; - model->addFEMTriangleConstraint(v1, v2, v3); - } - } - else if (clothSimulationMethod == 3) - { - TriangleModel::ParticleMesh &mesh = model->getTriangleModels()[cm]->getParticleMesh(); - const unsigned int *tris = mesh.getFaces().data(); - const unsigned int nFaces = mesh.numFaces(); - for (unsigned int i = 0; i < nFaces; i++) - { - const unsigned int v1 = tris[3 * i] + offset; - const unsigned int v2 = tris[3 * i + 1] + offset; - const unsigned int v3 = tris[3 * i + 2] + offset; - model->addStrainTriangleConstraint(v1, v2, v3); - } - } - else if (clothSimulationMethod == 4) - { - const unsigned int nEdges = model->getTriangleModels()[cm]->getParticleMesh().numEdges(); - const IndexedFaceMesh::Edge* edges = model->getTriangleModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint_XPBD(v1, v2); - } - } - if (bendingMethod != 0) - { - TriangleModel::ParticleMesh &mesh = model->getTriangleModels()[cm]->getParticleMesh(); - unsigned int nEdges = mesh.numEdges(); - const TriangleModel::ParticleMesh::Edge *edges = mesh.getEdges().data(); - const unsigned int *tris = mesh.getFaces().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const int tri1 = edges[i].m_face[0]; - const int tri2 = edges[i].m_face[1]; - if ((tri1 != 0xffffffff) && (tri2 != 0xffffffff)) - { - // Find the triangle points which do not lie on the axis - const int axisPoint1 = edges[i].m_vert[0]; - const int axisPoint2 = edges[i].m_vert[1]; - int point1 = -1; - int point2 = -1; - for (int j = 0; j < 3; j++) - { - if ((tris[3 * tri1 + j] != axisPoint1) && (tris[3 * tri1 + j] != axisPoint2)) - { - point1 = tris[3 * tri1 + j]; - break; - } - } - for (int j = 0; j < 3; j++) - { - if ((tris[3 * tri2 + j] != axisPoint1) && (tris[3 * tri2 + j] != axisPoint2)) - { - point2 = tris[3 * tri2 + j]; - break; - } - } - if ((point1 != -1) && (point2 != -1)) - { - const unsigned int vertex1 = point1 + offset; - const unsigned int vertex2 = point2 + offset; - const unsigned int vertex3 = edges[i].m_vert[0] + offset; - const unsigned int vertex4 = edges[i].m_vert[1] + offset; - if (bendingMethod == 1) - model->addDihedralConstraint(vertex1, vertex2, vertex3, vertex4); - else if (bendingMethod == 2) - model->addIsometricBendingConstraint(vertex1, vertex2, vertex3, vertex4); - else if (bendingMethod == 3) - { - model->setValue(SimulationModel::CLOTH_BENDING_STIFFNESS, 100.0); - model->addIsometricBendingConstraint_XPBD(vertex1, vertex2, vertex3, vertex4); - } - } - } - } - } + distanceStiffness = 1.0; + if (clothSimulationMethod == 4) + distanceStiffness = 100000; + model->addClothConstraints(model->getTriangleModels()[cm], clothSimulationMethod, distanceStiffness, xxStiffness, + yyStiffness, xyStiffness, xyPoissonRatio, yxPoissonRatio, normalizeStretch, normalizeShear); + + bendingStiffness = 0.01; + if (bendingMethod == 3) + bendingStiffness = 100.0; + model->addBendingConstraints(model->getTriangleModels()[cm], bendingMethod, bendingStiffness); } } void initTetModelConstraints() { // init constraints - SimulationModel *model = Simulation::getCurrent()->getModel(); - model->setValue(SimulationModel::CLOTH_STIFFNESS, 1.0); - model->setValue(SimulationModel::SOLID_STIFFNESS, 1.0); + SimulationModel* model = Simulation::getCurrent()->getModel(); + solidStiffness = 1.0; + if (solidSimulationMethod == 5) + solidStiffness = 100000; + + volumeStiffness = 1.0; + if (solidSimulationMethod == 5) + volumeStiffness = 100000; for (unsigned int cm = 0; cm < model->getTetModels().size(); cm++) { - const unsigned int offset = model->getTetModels()[cm]->getIndexOffset(); - const unsigned int nTets = model->getTetModels()[cm]->getParticleMesh().numTets(); - const unsigned int *tets = model->getTetModels()[cm]->getParticleMesh().getTets().data(); - const IndexedTetMesh::VertexTets *vTets = model->getTetModels()[cm]->getParticleMesh().getVertexTets().data(); - if (solidSimulationMethod == 1) - { - const unsigned int offset = model->getTetModels()[cm]->getIndexOffset(); - const unsigned int nEdges = model->getTetModels()[cm]->getParticleMesh().numEdges(); - const IndexedTetMesh::Edge *edges = model->getTetModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint(v1, v2); - } - - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i] + offset; - const unsigned int v2 = tets[4 * i + 1] + offset; - const unsigned int v3 = tets[4 * i + 2] + offset; - const unsigned int v4 = tets[4 * i + 3] + offset; - - model->addVolumeConstraint(v1, v2, v3, v4); - } - } - else if (solidSimulationMethod == 2) - { - TetModel::ParticleMesh &mesh = model->getTetModels()[cm]->getParticleMesh(); - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i] + offset; - const unsigned int v2 = tets[4 * i + 1] + offset; - const unsigned int v3 = tets[4 * i + 2] + offset; - const unsigned int v4 = tets[4 * i + 3] + offset; - - model->addFEMTetConstraint(v1, v2, v3, v4); - } - } - else if (solidSimulationMethod == 3) - { - TetModel::ParticleMesh &mesh = model->getTetModels()[cm]->getParticleMesh(); - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i] + offset; - const unsigned int v2 = tets[4 * i + 1] + offset; - const unsigned int v3 = tets[4 * i + 2] + offset; - const unsigned int v4 = tets[4 * i + 3] + offset; - - model->addStrainTetConstraint(v1, v2, v3, v4); - } - } - else if (solidSimulationMethod == 4) - { - TetModel::ParticleMesh &mesh = model->getTetModels()[cm]->getParticleMesh(); - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v[4] = { tets[4 * i] + offset, - tets[4 * i + 1] + offset, - tets[4 * i + 2] + offset, - tets[4 * i + 3] + offset }; - // Important: Divide position correction by the number of clusters - // which contain the vertex. - const unsigned int nc[4] = { vTets[v[0]].m_numTets, vTets[v[1]].m_numTets, vTets[v[2]].m_numTets, vTets[v[3]].m_numTets }; - model->addShapeMatchingConstraint(4, v, nc); - } - } - else if (solidSimulationMethod == 5) - { - model->setValue(SimulationModel::CLOTH_STIFFNESS, 100000); - model->setValue(SimulationModel::SOLID_STIFFNESS, 100000); - const unsigned int offset = model->getTetModels()[cm]->getIndexOffset(); - const unsigned int nEdges = model->getTetModels()[cm]->getParticleMesh().numEdges(); - const IndexedTetMesh::Edge* edges = model->getTetModels()[cm]->getParticleMesh().getEdges().data(); - for (unsigned int i = 0; i < nEdges; i++) - { - const unsigned int v1 = edges[i].m_vert[0] + offset; - const unsigned int v2 = edges[i].m_vert[1] + offset; - - model->addDistanceConstraint_XPBD(v1, v2); - } - - for (unsigned int i = 0; i < nTets; i++) - { - const unsigned int v1 = tets[4 * i]; - const unsigned int v2 = tets[4 * i + 1]; - const unsigned int v3 = tets[4 * i + 2]; - const unsigned int v4 = tets[4 * i + 3]; - - model->addVolumeConstraint_XPBD(v1, v2, v3, v4); - } - } + model->addSolidConstraints(model->getTetModels()[cm], solidSimulationMethod, solidStiffness, + solidPoissonRatio, volumeStiffness, solidNormalizeStretch, solidNormalizeShear); } } @@ -613,21 +470,21 @@ void readScene() model->setContactStiffnessRigidBody(data.m_contactStiffnessRigidBody); model->setContactStiffnessParticleRigidBody(data.m_contactStiffnessParticleRigidBody); - model->setValue(SimulationModel::CLOTH_BENDING_STIFFNESS, data.m_cloth_bendingStiffness); - model->setValue(SimulationModel::CLOTH_STIFFNESS_XX, data.m_cloth_xxStiffness); - model->setValue(SimulationModel::CLOTH_STIFFNESS_YY, data.m_cloth_yyStiffness); - model->setValue(SimulationModel::CLOTH_STIFFNESS_XY, data.m_cloth_xyStiffness); - model->setValue(SimulationModel::CLOTH_POISSON_RATIO_XY, data.m_cloth_xyPoissonRatio); - model->setValue(SimulationModel::CLOTH_POISSON_RATIO_YX, data.m_cloth_yxPoissonRatio); - model->setValue(SimulationModel::CLOTH_NORMALIZE_STRETCH, data.m_cloth_normalizeStretch); - model->setValue(SimulationModel::CLOTH_NORMALIZE_SHEAR, data.m_cloth_normalizeShear); - - model->setValue(SimulationModel::SOLID_STIFFNESS, data.m_solid_stiffness); - model->setValue(SimulationModel::SOLID_POISSON_RATIO, data.m_solid_poissonRatio); - model->setValue(SimulationModel::SOLID_NORMALIZE_STRETCH, data.m_solid_normalizeStretch); - model->setValue(SimulationModel::SOLID_NORMALIZE_SHEAR, data.m_solid_normalizeShear); + bendingStiffness = data.m_cloth_bendingStiffness; + xxStiffness = data.m_cloth_xxStiffness; + yyStiffness = data.m_cloth_yyStiffness; + xyStiffness = data.m_cloth_xyStiffness; + xyPoissonRatio = data.m_cloth_xyPoissonRatio; + yxPoissonRatio = data.m_cloth_yxPoissonRatio; + normalizeStretch = data.m_cloth_normalizeStretch; + normalizeShear = data.m_cloth_normalizeShear; + solidStiffness = data.m_solid_stiffness; + solidPoissonRatio = data.m_solid_poissonRatio; + solidNormalizeStretch = data.m_solid_normalizeStretch; + solidNormalizeShear = data.m_solid_normalizeShear; + volumeStiffness = data.m_volume_stiffness; ////////////////////////////////////////////////////////////////////////// // rigid bodies @@ -1409,3 +1266,168 @@ void TW_CALL getSolidSimulationMethod(void *value, void *clientData) *(short *)(value) = *((short*)clientData); } +void TW_CALL setBendingStiffness(const void* value, void* clientData) +{ + bendingStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); + ((SimulationModel*)clientData)->setConstraintValue(bendingStiffness); +} + +void TW_CALL getBendingStiffness(void* value, void* clientData) +{ + *(Real*)(value) = bendingStiffness; +} + +void TW_CALL setDistanceStiffness(const void* value, void* clientData) +{ + distanceStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(distanceStiffness); + ((SimulationModel*)clientData)->setConstraintValue(distanceStiffness); +} + +void TW_CALL getDistanceStiffness(void* value, void* clientData) +{ + *(Real*)(value) = distanceStiffness; +} + +void TW_CALL setXXStiffness(const void* value, void* clientData) +{ + xxStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(xxStiffness); + ((SimulationModel*)clientData)->setConstraintValue(xxStiffness); +} + +void TW_CALL getXXStiffness(void* value, void* clientData) +{ + *(Real*)(value) = xxStiffness; +} + +void TW_CALL getYYStiffness(void* value, void* clientData) +{ + *(Real*)(value) = yyStiffness; +} + +void TW_CALL setYYStiffness(const void* value, void* clientData) +{ + yyStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(yyStiffness); + ((SimulationModel*)clientData)->setConstraintValue(yyStiffness); +} + +void TW_CALL getXYStiffness(void* value, void* clientData) +{ + *(Real*)(value) = xyStiffness; +} + +void TW_CALL setXYStiffness(const void* value, void* clientData) +{ + xyStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(xyStiffness); + ((SimulationModel*)clientData)->setConstraintValue(xyStiffness); +} + +void TW_CALL getXYPoissonRatio(void* value, void* clientData) +{ + *(Real*)(value) = xyPoissonRatio; +} + +void TW_CALL setXYPoissonRatio(const void* value, void* clientData) +{ + xyPoissonRatio = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(xyPoissonRatio); +} + +void TW_CALL getYXPoissonRatio(void* value, void* clientData) +{ + *(Real*)(value) = yxPoissonRatio; +} + +void TW_CALL setYXPoissonRatio(const void* value, void* clientData) +{ + yxPoissonRatio = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(yxPoissonRatio); +} + +void TW_CALL getNormalizeStretch(void* value, void* clientData) +{ + *(bool*)(value) = normalizeStretch; +} + +void TW_CALL setNormalizeStretch(const void* value, void* clientData) +{ + normalizeStretch = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(normalizeStretch); +} + +void TW_CALL getNormalizeShear(void* value, void* clientData) +{ + *(bool*)(value) = normalizeShear; +} + +void TW_CALL setNormalizeShear(const void* value, void* clientData) +{ + normalizeShear = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(normalizeShear); +} + +void TW_CALL setSolidStiffness(const void* value, void* clientData) +{ + solidStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(solidStiffness); + ((SimulationModel*)clientData)->setConstraintValue(solidStiffness); + ((SimulationModel*)clientData)->setConstraintValue(solidStiffness); + ((SimulationModel*)clientData)->setConstraintValue(solidStiffness); + ((SimulationModel*)clientData)->setConstraintValue(solidStiffness); + ((SimulationModel*)clientData)->setConstraintValue(solidStiffness); +} + +void TW_CALL getSolidStiffness(void* value, void* clientData) +{ + *(Real*)(value) = solidStiffness; +} + +void TW_CALL setVolumeStiffness(const void* value, void* clientData) +{ + volumeStiffness = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(volumeStiffness); + ((SimulationModel*)clientData)->setConstraintValue(volumeStiffness); +} + +void TW_CALL getVolumeStiffness(void* value, void* clientData) +{ + *(Real*)(value) = volumeStiffness; +} + +void TW_CALL getSolidPoissonRatio(void* value, void* clientData) +{ + *(Real*)(value) = solidPoissonRatio; +} + +void TW_CALL setSolidPoissonRatio(const void* value, void* clientData) +{ + solidPoissonRatio = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(solidPoissonRatio); +} + +void TW_CALL getSolidNormalizeStretch(void* value, void* clientData) +{ + *(bool*)(value) = solidNormalizeStretch; +} + +void TW_CALL setSolidNormalizeStretch(const void* value, void* clientData) +{ + solidNormalizeStretch = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(solidNormalizeStretch); +} + +void TW_CALL getSolidNormalizeShear(void* value, void* clientData) +{ + *(bool*)(value) = solidNormalizeShear; +} + +void TW_CALL setSolidNormalizeShear(const void* value, void* clientData) +{ + solidNormalizeShear = *(const Real*)(value); + ((SimulationModel*)clientData)->setConstraintValue(solidNormalizeShear); +} \ No newline at end of file diff --git a/Demos/StiffRodsDemos/StretchBendingTwistingDemo.cpp b/Demos/StiffRodsDemos/StretchBendingTwistingDemo.cpp index f31bae2a..496cfe32 100644 --- a/Demos/StiffRodsDemos/StretchBendingTwistingDemo.cpp +++ b/Demos/StiffRodsDemos/StretchBendingTwistingDemo.cpp @@ -198,13 +198,13 @@ void createBunnyRodModel() SimulationModel::RigidBodyVector &rb = model->getRigidBodies(); SimulationModel::ConstraintVector &constraints = model->getConstraints(); - string fileName = FileSystem::normalizePath(base->getDataPath() + "/models/cube.obj"); + string fileName = FileSystem::normalizePath(base->getExePath() + "/resources/models/cube.obj"); IndexedFaceMesh mesh; VertexData vd; loadObj(fileName, vd, mesh, Vector3r(height, width, depth)); mesh.setFlatShading(true); - string fileName2 = FileSystem::normalizePath(base->getDataPath() + "/models/bunny_10k.obj"); + string fileName2 = FileSystem::normalizePath(base->getExePath() + "/resources/models/bunny_10k.obj"); IndexedFaceMesh mesh2; VertexData vd2; loadObj(fileName2, vd2, mesh2, Vector3r(2.0, 2.0, 2.0)); diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..bec3e4d2 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +recursive-include data * diff --git a/PositionBasedDynamics/PositionBasedDynamics.cpp b/PositionBasedDynamics/PositionBasedDynamics.cpp index 1aef6a0b..bf3c0e0b 100644 --- a/PositionBasedDynamics/PositionBasedDynamics.cpp +++ b/PositionBasedDynamics/PositionBasedDynamics.cpp @@ -14,8 +14,7 @@ bool PositionBasedDynamics::solve_DistanceConstraint( const Vector3r &p0, Real invMass0, const Vector3r &p1, Real invMass1, const Real restLength, - const Real compressionStiffness, - const Real stretchStiffness, + const Real stiffness, Vector3r &corr0, Vector3r &corr1) { Real wSum = invMass0 + invMass1; @@ -27,10 +26,7 @@ bool PositionBasedDynamics::solve_DistanceConstraint( n.normalize(); Vector3r corr; - if (d < restLength) - corr = compressionStiffness * n * (d - restLength) / wSum; - else - corr = stretchStiffness * n * (d - restLength) / wSum; + corr = stiffness * n * (d - restLength) / wSum; corr0 = invMass0 * corr; corr1 = -invMass1 * corr; @@ -110,22 +106,17 @@ bool PositionBasedDynamics::solve_VolumeConstraint( const Vector3r &p1, Real invMass1, const Vector3r &p2, Real invMass2, const Vector3r &p3, Real invMass3, - const Real restVolume, - const Real negVolumeStiffness, - const Real posVolumeStiffness, + const Real restVolume, + const Real stiffness, Vector3r &corr0, Vector3r &corr1, Vector3r &corr2, Vector3r &corr3) { Real volume = static_cast(1.0 / 6.0) * (p1 - p0).cross(p2 - p0).dot(p3 - p0); corr0.setZero(); corr1.setZero(); corr2.setZero(); corr3.setZero(); - if (posVolumeStiffness == 0.0 && volume > 0.0) + if (stiffness == 0.0) return false; - if (negVolumeStiffness == 0.0 && volume < 0.0) - return false; - - Vector3r grad0 = (p1 - p2).cross(p3 - p2); Vector3r grad1 = (p2 - p0).cross(p3 - p0); Vector3r grad2 = (p0 - p1).cross(p3 - p1); @@ -140,10 +131,7 @@ bool PositionBasedDynamics::solve_VolumeConstraint( if (fabs(lambda) < eps) return false; - if (volume < 0.0) - lambda = negVolumeStiffness * (volume - restVolume) / lambda; - else - lambda = posVolumeStiffness * (volume - restVolume) / lambda; + lambda = stiffness * (volume - restVolume) / lambda; corr0 = -lambda * invMass0 * grad0; corr1 = -lambda * invMass1 * grad1; @@ -487,13 +475,13 @@ bool PositionBasedDynamics::solve_EdgeEdgeDistanceConstraint( return true; } +#include + // ---------------------------------------------------------------------------------------------- bool PositionBasedDynamics::init_ShapeMatchingConstraint( const Vector3r x0[], const Real invMasses[], int numPoints, - Vector3r &restCm, Matrix3r &invRestMat) + Vector3r &restCm) { - invRestMat.setIdentity(); - // center of mass restCm.setZero(); Real wsum = 0.0; @@ -506,36 +494,13 @@ bool PositionBasedDynamics::init_ShapeMatchingConstraint( return false; restCm /= wsum; - // A - Matrix3r A; - A.setZero(); - for (int i = 0; i < numPoints; i++) { - const Vector3r qi = x0[i] - restCm; - Real wi = static_cast(1.0) / (invMasses[i] + eps); - Real x2 = wi * qi[0] * qi[0]; - Real y2 = wi * qi[1] * qi[1]; - Real z2 = wi * qi[2] * qi[2]; - Real xy = wi * qi[0] * qi[1]; - Real xz = wi * qi[0] * qi[2]; - Real yz = wi * qi[1] * qi[2]; - A(0, 0) += x2; A(0, 1) += xy; A(0, 2) += xz; - A(1, 0) += xy; A(1, 1) += y2; A(1, 2) += yz; - A(2, 0) += xz; A(2, 1) += yz; A(2, 2) += z2; - } - Real det = A.determinant(); - if (fabs(det) > eps) - { - invRestMat = A.inverse(); - return true; - } - return false; + return true; } // ---------------------------------------------------------------------------------------------- bool PositionBasedDynamics::solve_ShapeMatchingConstraint( const Vector3r x0[], const Vector3r x[], const Real invMasses[], int numPoints, const Vector3r &restCm, - const Matrix3r &invRestMat, const Real stiffness, const bool allowStretch, Vector3r corr[], Matrix3r *rot) @@ -571,7 +536,7 @@ bool PositionBasedDynamics::solve_ShapeMatchingConstraint( mat(2, 0) += p[2] * q[0]; mat(2, 1) += p[2] * q[1]; mat(2, 2) += p[2] * q[2]; } - mat = mat * invRestMat; + //mat = mat * invRestMat; Matrix3r R, U, D; R = mat; diff --git a/PositionBasedDynamics/PositionBasedDynamics.h b/PositionBasedDynamics/PositionBasedDynamics.h index 7f93a443..4fa3d4e1 100644 --- a/PositionBasedDynamics/PositionBasedDynamics.h +++ b/PositionBasedDynamics/PositionBasedDynamics.h @@ -21,8 +21,7 @@ namespace PBD * @param p1 position of second particle * @param invMass1 inverse mass of second particle * @param restLength rest length of distance constraint - * @param compressionStiffness stiffness coefficient for compression - * @param stretchStiffness stiffness coefficient for stretching + * @param stiffness stiffness coefficient * @param corr0 position correction of first particle * @param corr1 position correction of second particle */ @@ -30,8 +29,7 @@ namespace PBD const Vector3r &p0, Real invMass0, const Vector3r &p1, Real invMass1, const Real restLength, - const Real compressionStiffness, - const Real stretchStiffness, + const Real stiffness, Vector3r &corr0, Vector3r &corr1); @@ -99,8 +97,7 @@ namespace PBD * @param p3 position of fourth particle * @param invMass3 inverse mass of fourth particle * @param restVolume rest angle \f$V_0\f$ - * @param negVolumeStiffness stiffness coefficient for compression - * @param posVolumeStiffness stiffness coefficient for stretching + * @param stiffness stiffness coefficient * @param corr0 position correction of first particle * @param corr1 position correction of second particle * @param corr2 position correction of third particle @@ -112,8 +109,7 @@ namespace PBD const Vector3r &p2, Real invMass2, const Vector3r &p3, Real invMass3, const Real restVolume, - const Real negVolumeStiffness, - const Real posVolumeStiffness, + const Real stiffness, Vector3r &corr0, Vector3r &corr1, Vector3r &corr2, Vector3r &corr3); /** Determine the position corrections for a constraint that preserves a @@ -261,11 +257,10 @@ namespace PBD * @param invMasses inverse masses of all particles in the cluster * @param numPoints number of particles in the cluster * @param restCm returns the center of mass of the rest configuration - * @param invRestMat returns a matrix required by the solver */ static bool init_ShapeMatchingConstraint( const Vector3r x0[], const Real invMasses[], const int numPoints, - Vector3r &restCm, Matrix3r &invRestMat); + Vector3r &restCm); /** Determine the position corrections for a shape matching constraint.\n\n * More information can be found in: \cite BMM2015, \cite BMOT2013, \cite BMOTM2014, @@ -276,7 +271,6 @@ namespace PBD * @param invMasses invMasses inverse masses of all particles in the cluster * @param numPoints number of particles in the cluster * @param restCm center of mass of the rest configuration - * @param invRestMat matrix precomputed by init_ShapeMatchingConstraint() * @param stiffness stiffness coefficient * @param allowStretch allow stretching * @param corr position corrections for all particles in the cluster @@ -285,7 +279,6 @@ namespace PBD static bool solve_ShapeMatchingConstraint( const Vector3r x0[], const Vector3r x[], const Real invMasses[], const int numPoints, const Vector3r &restCm, - const Matrix3r &invRestMat, const Real stiffness, const bool allowStretch, // default false Vector3r corr[], Matrix3r *rot = NULL); diff --git a/README.md b/README.md index eac7ffd2..490de499 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,8 @@ http://www.interactive-graphics.de/PositionBasedDynamics/doc/html ## Latest Important Changes +* added Python binding +* added some XPBD constraints * added OBJ export * added substepping * added DamperJoint diff --git a/Simulation/CMakeLists.txt b/Simulation/CMakeLists.txt index 32f27f0b..41d0f62f 100644 --- a/Simulation/CMakeLists.txt +++ b/Simulation/CMakeLists.txt @@ -45,7 +45,7 @@ add_library(Simulation ############################################################ # Discregrid ############################################################ -include_directories(${Discregrid_INCLUDE_DIR}) +target_include_directories(Simulation PUBLIC ${Discregrid_INCLUDE_DIR}) if (TARGET Ext_Discregrid) add_dependencies(Simulation Ext_Discregrid) endif() @@ -54,15 +54,15 @@ endif() ############################################################ # GenericParameters ############################################################ -include_directories(${GenericParameters_INCLUDE_DIR}) +target_include_directories(Simulation PUBLIC ${GenericParameters_INCLUDE_DIR}) if(TARGET Ext_GenericParameters) add_dependencies(Simulation Ext_GenericParameters) endif() find_package( Eigen3 REQUIRED ) -include_directories( ${EIGEN3_INCLUDE_DIR} ) +target_include_directories(Simulation PUBLIC ${EIGEN3_INCLUDE_DIR} ) -target_link_libraries(Simulation PositionBasedDynamics) +target_link_libraries(Simulation PUBLIC PositionBasedDynamics) install(TARGETS Simulation diff --git a/Simulation/Constraints.cpp b/Simulation/Constraints.cpp index 63268716..c8b291d5 100644 --- a/Simulation/Constraints.cpp +++ b/Simulation/Constraints.cpp @@ -1162,8 +1162,9 @@ bool DistanceJoint::solvePositionConstraint(SimulationModel &model, const unsign ////////////////////////////////////////////////////////////////////////// // DistanceConstraint ////////////////////////////////////////////////////////////////////////// -bool DistanceConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2) +bool DistanceConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, const Real stiffness) { + m_stiffness = stiffness; m_bodies[0] = particle1; m_bodies[1] = particle2; ParticleData &pd = model.getParticles(); @@ -1191,7 +1192,7 @@ bool DistanceConstraint::solvePositionConstraint(SimulationModel &model, const u Vector3r corr1, corr2; const bool res = PositionBasedDynamics::solve_DistanceConstraint( x1, invMass1, x2, invMass2, - m_restLength, model.getValue(SimulationModel::CLOTH_STIFFNESS), model.getValue(SimulationModel::CLOTH_STIFFNESS), corr1, corr2); + m_restLength, m_stiffness, corr1, corr2); if (res) { @@ -1206,8 +1207,9 @@ bool DistanceConstraint::solvePositionConstraint(SimulationModel &model, const u ////////////////////////////////////////////////////////////////////////// // DistanceConstraint_XPBD ////////////////////////////////////////////////////////////////////////// -bool DistanceConstraint_XPBD::initConstraint(SimulationModel& model, const unsigned int particle1, const unsigned int particle2) +bool DistanceConstraint_XPBD::initConstraint(SimulationModel& model, const unsigned int particle1, const unsigned int particle2, const Real stiffness) { + m_stiffness = stiffness; m_lambda = 0.0; m_bodies[0] = particle1; m_bodies[1] = particle2; @@ -1241,7 +1243,7 @@ bool DistanceConstraint_XPBD::solvePositionConstraint(SimulationModel& model, co Vector3r corr1, corr2; const bool res = XPBD::solve_DistanceConstraint( x1, invMass1, x2, invMass2, - m_restLength, model.getValue(SimulationModel::CLOTH_STIFFNESS), dt, m_lambda, + m_restLength, m_stiffness, dt, m_lambda, corr1, corr2); if (res) @@ -1259,8 +1261,9 @@ bool DistanceConstraint_XPBD::solvePositionConstraint(SimulationModel& model, co ////////////////////////////////////////////////////////////////////////// bool DihedralConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) + const unsigned int particle3, const unsigned int particle4, const Real stiffness) { + m_stiffness = stiffness; m_bodies[0] = particle1; m_bodies[1] = particle2; m_bodies[2] = particle3; @@ -1317,7 +1320,7 @@ bool DihedralConstraint::solvePositionConstraint(SimulationModel &model, const u const bool res = PositionBasedDynamics::solve_DihedralConstraint( x1, invMass1, x2, invMass2, x3, invMass3, x4, invMass4, m_restAngle, - model.getValue(SimulationModel::CLOTH_BENDING_STIFFNESS), + m_stiffness, corr1, corr2, corr3, corr4); if (res) @@ -1339,8 +1342,9 @@ bool DihedralConstraint::solvePositionConstraint(SimulationModel &model, const u // IsometricBendingConstraint ////////////////////////////////////////////////////////////////////////// bool IsometricBendingConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) + const unsigned int particle3, const unsigned int particle4, const Real stiffness) { + m_stiffness = stiffness; m_bodies[0] = particle1; m_bodies[1] = particle2; m_bodies[2] = particle3; @@ -1379,7 +1383,7 @@ bool IsometricBendingConstraint::solvePositionConstraint(SimulationModel &model, const bool res = PositionBasedDynamics::solve_IsometricBendingConstraint( x1, invMass1, x2, invMass2, x3, invMass3, x4, invMass4, m_Q, - model.getValue(SimulationModel::CLOTH_BENDING_STIFFNESS), + m_stiffness, corr1, corr2, corr3, corr4); if (res) @@ -1400,9 +1404,10 @@ bool IsometricBendingConstraint::solvePositionConstraint(SimulationModel &model, // IsometricBendingConstraint_XPBD ////////////////////////////////////////////////////////////////////////// bool IsometricBendingConstraint_XPBD::initConstraint(SimulationModel& model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) + const unsigned int particle3, const unsigned int particle4, const Real stiffness) { m_lambda = 0.0; + m_stiffness = stiffness; m_bodies[0] = particle1; m_bodies[1] = particle2; m_bodies[2] = particle3; @@ -1446,7 +1451,7 @@ bool IsometricBendingConstraint_XPBD::solvePositionConstraint(SimulationModel& m const bool res = XPBD::solve_IsometricBendingConstraint( x1, invMass1, x2, invMass2, x3, invMass3, x4, invMass4, m_Q, - model.getValue(SimulationModel::CLOTH_BENDING_STIFFNESS), + m_stiffness, dt, m_lambda, corr1, corr2, corr3, corr4); @@ -1468,8 +1473,14 @@ bool IsometricBendingConstraint_XPBD::solvePositionConstraint(SimulationModel& m // FEMTriangleConstraint ////////////////////////////////////////////////////////////////////////// bool FEMTriangleConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3) -{ + const unsigned int particle3, const Real xxStiffness, const Real yyStiffness, const Real xyStiffness, + const Real xyPoissonRatio, const Real yxPoissonRatio) +{ + m_xxStiffness = xxStiffness; + m_yyStiffness = yyStiffness; + m_xyStiffness = xyStiffness; + m_xyPoissonRatio = xyPoissonRatio; + m_yxPoissonRatio = yxPoissonRatio; m_bodies[0] = particle1; m_bodies[1] = particle2; m_bodies[2] = particle3; @@ -1506,11 +1517,11 @@ bool FEMTriangleConstraint::solvePositionConstraint(SimulationModel &model, cons x3, invMass3, m_area, m_invRestMat, - model.getValue(SimulationModel::CLOTH_STIFFNESS_XX), - model.getValue(SimulationModel::CLOTH_STIFFNESS_YY), - model.getValue(SimulationModel::CLOTH_STIFFNESS_XY), - model.getValue(SimulationModel::CLOTH_POISSON_RATIO_XY), - model.getValue(SimulationModel::CLOTH_POISSON_RATIO_YX), + m_xxStiffness, + m_yyStiffness, + m_xyStiffness, + m_xyPoissonRatio, + m_yxPoissonRatio, corr1, corr2, corr3); if (res) @@ -1530,8 +1541,14 @@ bool FEMTriangleConstraint::solvePositionConstraint(SimulationModel &model, cons // StrainTriangleConstraint ////////////////////////////////////////////////////////////////////////// bool StrainTriangleConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3) -{ + const unsigned int particle3, const Real xxStiffness, const Real yyStiffness, const Real xyStiffness, + const bool normalizeStretch, const bool normalizeShear) +{ + m_xxStiffness = xxStiffness; + m_yyStiffness = yyStiffness; + m_xyStiffness = xyStiffness; + m_normalizeStretch = normalizeStretch; + m_normalizeShear = normalizeShear; m_bodies[0] = particle1; m_bodies[1] = particle2; m_bodies[2] = particle3; @@ -1572,11 +1589,11 @@ bool StrainTriangleConstraint::solvePositionConstraint(SimulationModel &model, c x2, invMass2, x3, invMass3, m_invRestMat, - model.getValue(SimulationModel::CLOTH_STIFFNESS_XX), - model.getValue(SimulationModel::CLOTH_STIFFNESS_YY), - model.getValue(SimulationModel::CLOTH_STIFFNESS_XY), - model.getValue(SimulationModel::CLOTH_NORMALIZE_STRETCH), - model.getValue(SimulationModel::CLOTH_NORMALIZE_SHEAR), + m_xxStiffness, + m_yyStiffness, + m_xyStiffness, + m_normalizeStretch, + m_normalizeShear, corr1, corr2, corr3); if (res) @@ -1597,8 +1614,9 @@ bool StrainTriangleConstraint::solvePositionConstraint(SimulationModel &model, c ////////////////////////////////////////////////////////////////////////// bool VolumeConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) + const unsigned int particle3, const unsigned int particle4, const Real stiffness) { + m_stiffness = stiffness; m_bodies[0] = particle1; m_bodies[1] = particle2; m_bodies[2] = particle3; @@ -1640,8 +1658,7 @@ bool VolumeConstraint::solvePositionConstraint(SimulationModel &model, const uns x3, invMass3, x4, invMass4, m_restVolume, - model.getValue(SimulationModel::SOLID_STIFFNESS), - model.getValue(SimulationModel::SOLID_STIFFNESS), + m_stiffness, corr1, corr2, corr3, corr4); if (res) @@ -1663,8 +1680,9 @@ bool VolumeConstraint::solvePositionConstraint(SimulationModel &model, const uns ////////////////////////////////////////////////////////////////////////// bool VolumeConstraint_XPBD::initConstraint(SimulationModel& model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) + const unsigned int particle3, const unsigned int particle4, const Real stiffness) { + m_stiffness = stiffness; m_lambda = 0.0; m_bodies[0] = particle1; m_bodies[1] = particle2; @@ -1712,7 +1730,7 @@ bool VolumeConstraint_XPBD::solvePositionConstraint(SimulationModel& model, cons x3, invMass3, x4, invMass4, m_restVolume, - model.getValue(SimulationModel::SOLID_STIFFNESS), + m_stiffness, dt, m_lambda, corr1, corr2, corr3, corr4); @@ -1734,8 +1752,11 @@ bool VolumeConstraint_XPBD::solvePositionConstraint(SimulationModel& model, cons // FEMTetConstraint ////////////////////////////////////////////////////////////////////////// bool FEMTetConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) + const unsigned int particle3, const unsigned int particle4, + const Real stiffness, const Real poissonRatio) { + m_stiffness = stiffness; + m_poissonRatio = poissonRatio; m_bodies[0] = particle1; m_bodies[1] = particle2; m_bodies[2] = particle3; @@ -1784,8 +1805,8 @@ bool FEMTetConstraint::solvePositionConstraint(SimulationModel &model, const uns x4, invMass4, m_volume, m_invRestMat, - model.getValue(SimulationModel::SOLID_STIFFNESS), - model.getValue(SimulationModel::SOLID_POISSON_RATIO), handleInversion, + m_stiffness, + m_poissonRatio, handleInversion, corr1, corr2, corr3, corr4); if (res) @@ -1807,8 +1828,14 @@ bool FEMTetConstraint::solvePositionConstraint(SimulationModel &model, const uns // StrainTetConstraint ////////////////////////////////////////////////////////////////////////// bool StrainTetConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) -{ + const unsigned int particle3, const unsigned int particle4, + const Real stretchStiffness, const Real shearStiffness, + const bool normalizeStretch, const bool normalizeShear) +{ + m_stretchStiffness = stretchStiffness; + m_shearStiffness = shearStiffness; + m_normalizeStretch = normalizeStretch; + m_normalizeShear = normalizeShear; m_bodies[0] = particle1; m_bodies[1] = particle2; m_bodies[2] = particle3; @@ -1843,9 +1870,6 @@ bool StrainTetConstraint::solvePositionConstraint(SimulationModel &model, const const Real invMass3 = pd.getInvMass(i3); const Real invMass4 = pd.getInvMass(i4); - const Real stiff = model.getValue(SimulationModel::SOLID_STIFFNESS); - Vector3r stiffness(stiff, stiff, stiff); - Vector3r corr1, corr2, corr3, corr4; const bool res = PositionBasedDynamics::solve_StrainTetraConstraint( x1, invMass1, @@ -1853,10 +1877,10 @@ bool StrainTetConstraint::solvePositionConstraint(SimulationModel &model, const x3, invMass3, x4, invMass4, m_invRestMat, - stiffness, - stiffness, - model.getValue(SimulationModel::SOLID_NORMALIZE_STRETCH), - model.getValue(SimulationModel::SOLID_NORMALIZE_SHEAR), + m_stretchStiffness * Vector3r::Ones(), + m_shearStiffness * Vector3r::Ones(), + m_normalizeStretch, + m_normalizeShear, corr1, corr2, corr3, corr4); if (res) @@ -1877,10 +1901,12 @@ bool StrainTetConstraint::solvePositionConstraint(SimulationModel &model, const // ShapeMatchingConstraint ////////////////////////////////////////////////////////////////////////// bool ShapeMatchingConstraint::initConstraint(SimulationModel &model, - const unsigned int particleIndices[], const unsigned int numClusters[]) + const unsigned int particleIndices[], const unsigned int numClusters[], + const Real stiffness) { + m_stiffness = stiffness; ParticleData &pd = model.getParticles(); - for (unsigned int i = 0; i < m_numberOfBodies; i++) + for (unsigned int i = 0; i < numberOfBodies(); i++) { m_bodies[i] = particleIndices[i]; m_x0[i] = pd.getPosition0(m_bodies[i]); @@ -1888,27 +1914,27 @@ bool ShapeMatchingConstraint::initConstraint(SimulationModel &model, m_numClusters[i] = numClusters[i]; } - const bool res = PositionBasedDynamics::init_ShapeMatchingConstraint(m_x0, m_w, m_numberOfBodies, m_restCm, m_invRestMat); + const bool res = PositionBasedDynamics::init_ShapeMatchingConstraint(m_x0, m_w, numberOfBodies(), m_restCm); return res; } bool ShapeMatchingConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter) { ParticleData &pd = model.getParticles(); - for (unsigned int i = 0; i < m_numberOfBodies; i++) + for (unsigned int i = 0; i < numberOfBodies(); i++) { m_x[i] = pd.getPosition(m_bodies[i]); } const bool res = PositionBasedDynamics::solve_ShapeMatchingConstraint( - m_x0, m_x, m_w, m_numberOfBodies, - m_restCm, m_invRestMat, - model.getValue(SimulationModel::SOLID_STIFFNESS), false, + m_x0, m_x, m_w, numberOfBodies(), + m_restCm, + m_stiffness, false, m_corr); if (res) { - for (unsigned int i = 0; i < m_numberOfBodies; i++) + for (unsigned int i = 0; i < numberOfBodies(); i++) { // Important: Divide position correction by the number of clusters // which contain the vertex. @@ -2118,8 +2144,8 @@ bool ParticleTetContactConstraint::initConstraint(SimulationModel &model, pd.getPosition(particleIndex), pd.getVelocity(particleIndex), m_invMasses, - m_x, - m_v, + m_x.data(), + m_v.data(), bary, normal, m_constraintInfo); } @@ -2143,7 +2169,7 @@ bool ParticleTetContactConstraint::solvePositionConstraint(SimulationModel &mode pd.getInvMass(m_bodies[0]), pd.getPosition(m_bodies[0]), m_invMasses, - m_x, + m_x.data(), m_bary, m_constraintInfo, m_lambda, @@ -2191,8 +2217,8 @@ bool ParticleTetContactConstraint::solveVelocityConstraint(SimulationModel &mode pd.getPosition(m_bodies[0]), pd.getVelocity(m_bodies[0]), m_invMasses, - m_x, - m_v, + m_x.data(), + m_v.data(), m_bary, m_lambda, m_frictionCoeff, @@ -2219,8 +2245,13 @@ bool ParticleTetContactConstraint::solveVelocityConstraint(SimulationModel &mode ////////////////////////////////////////////////////////////////////////// // StretchShearConstraint ////////////////////////////////////////////////////////////////////////// -bool StretchShearConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, const unsigned int quaternion1) +bool StretchShearConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, + const unsigned int quaternion1, const Real stretchingStiffness, + const Real shearingStiffness1, const Real shearingStiffness2) { + m_stretchingStiffness = stretchingStiffness; + m_shearingStiffness1 = shearingStiffness1; + m_shearingStiffness2 = shearingStiffness2; m_bodies[0] = particle1; m_bodies[1] = particle2; m_bodies[2] = quaternion1; @@ -2249,9 +2280,9 @@ bool StretchShearConstraint::solvePositionConstraint(SimulationModel &model, con const Real invMass1 = pd.getInvMass(i1); const Real invMass2 = pd.getInvMass(i2); const Real invMassq1 = od.getInvMass(iq1); - Vector3r stiffness(model.getRodShearingStiffness1(), - model.getRodShearingStiffness2(), - model.getRodStretchingStiffness()); + Vector3r stiffness(m_shearingStiffness1, + m_shearingStiffness2, + m_stretchingStiffness); Vector3r corr1, corr2; Quaternionr corrq1; @@ -2278,8 +2309,13 @@ bool StretchShearConstraint::solvePositionConstraint(SimulationModel &model, con ////////////////////////////////////////////////////////////////////////// // BendTwistConstraint ////////////////////////////////////////////////////////////////////////// -bool BendTwistConstraint::initConstraint(SimulationModel &model, const unsigned int quaternion1, const unsigned int quaternion2) +bool BendTwistConstraint::initConstraint(SimulationModel &model, const unsigned int quaternion1, + const unsigned int quaternion2, const Real twistingStiffness, + const Real bendingStiffness1, const Real bendingStiffness2) { + m_twistingStiffness = twistingStiffness; + m_bendingStiffness1 = bendingStiffness1; + m_bendingStiffness2 = bendingStiffness2; m_bodies[0] = quaternion1; m_bodies[1] = quaternion2; OrientationData &od = model.getOrientations(); @@ -2308,9 +2344,9 @@ bool BendTwistConstraint::solvePositionConstraint(SimulationModel &model, const Quaternionr &q2 = od.getQuaternion(i2); const Real invMass1 = od.getInvMass(i1); const Real invMass2 = od.getInvMass(i2); - Vector3r stiffness(model.getRodBendingStiffness1(), - model.getRodBendingStiffness2(), - model.getRodTwistingStiffness()); + Vector3r stiffness(m_bendingStiffness1, + m_bendingStiffness2, + m_twistingStiffness); Quaternionr corr1, corr2; const bool res = PositionBasedCosseratRods::solve_BendTwistConstraint( @@ -2506,9 +2542,7 @@ bool PBD::DirectPositionBasedSolverForStiffRodsConstraint::initConstraint( uniqueSegmentIndices.insert(idxPair.second); } - delete[] m_bodies; - m_numberOfBodies = (unsigned int)uniqueSegmentIndices.size(); - m_bodies = new unsigned int[m_numberOfBodies]; + m_bodies.resize(uniqueSegmentIndices.size()); // initialize m_bodies for constraint colouring algorithm of multi threading implementation diff --git a/Simulation/Constraints.h b/Simulation/Constraints.h index 42981b39..69fea27e 100644 --- a/Simulation/Constraints.h +++ b/Simulation/Constraints.h @@ -5,6 +5,7 @@ #define _USE_MATH_DEFINES #include "math.h" #include +#include #include #include #include "PositionBasedDynamics/DirectPositionBasedSolverForStiffRodsInterface.h" @@ -16,17 +17,16 @@ namespace PBD class Constraint { public: - unsigned int m_numberOfBodies; /** indices of the linked bodies */ - unsigned int *m_bodies; + std::vector m_bodies; Constraint(const unsigned int numberOfBodies) { - m_numberOfBodies = numberOfBodies; - m_bodies = new unsigned int[numberOfBodies]; + m_bodies.resize(numberOfBodies); } - virtual ~Constraint() { delete[] m_bodies; }; + unsigned int numberOfBodies() const { return static_cast(m_bodies.size()); } + virtual ~Constraint() {}; virtual int &getTypeId() const = 0; virtual bool initConstraintBeforeProjection(SimulationModel &model) { return true; }; @@ -257,11 +257,12 @@ namespace PBD public: static int TYPE_ID; Real m_restLength; + Real m_stiffness; DistanceConstraint() : Constraint(2) {} virtual int &getTypeId() const { return TYPE_ID; } - virtual bool initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2); + virtual bool initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, const Real stiffness); virtual bool solvePositionConstraint(SimulationModel &model, const unsigned int iter); }; @@ -271,11 +272,12 @@ namespace PBD static int TYPE_ID; Real m_restLength; Real m_lambda; + Real m_stiffness; DistanceConstraint_XPBD() : Constraint(2) {} virtual int& getTypeId() const { return TYPE_ID; } - virtual bool initConstraint(SimulationModel& model, const unsigned int particle1, const unsigned int particle2); + virtual bool initConstraint(SimulationModel& model, const unsigned int particle1, const unsigned int particle2, const Real stiffness); virtual bool solvePositionConstraint(SimulationModel& model, const unsigned int iter); }; @@ -284,12 +286,13 @@ namespace PBD public: static int TYPE_ID; Real m_restAngle; + Real m_stiffness; DihedralConstraint() : Constraint(4) {} virtual int &getTypeId() const { return TYPE_ID; } virtual bool initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); + const unsigned int particle3, const unsigned int particle4, const Real stiffness); virtual bool solvePositionConstraint(SimulationModel &model, const unsigned int iter); }; @@ -297,13 +300,14 @@ namespace PBD { public: static int TYPE_ID; + Real m_stiffness; Matrix4r m_Q; IsometricBendingConstraint() : Constraint(4) {} virtual int &getTypeId() const { return TYPE_ID; } virtual bool initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); + const unsigned int particle3, const unsigned int particle4, const Real stiffness); virtual bool solvePositionConstraint(SimulationModel &model, const unsigned int iter); }; @@ -311,6 +315,7 @@ namespace PBD { public: static int TYPE_ID; + Real m_stiffness; Matrix4r m_Q; Real m_lambda; @@ -318,7 +323,7 @@ namespace PBD virtual int& getTypeId() const { return TYPE_ID; } virtual bool initConstraint(SimulationModel& model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); + const unsigned int particle3, const unsigned int particle4, const Real stiffness); virtual bool solvePositionConstraint(SimulationModel& model, const unsigned int iter); }; @@ -328,12 +333,18 @@ namespace PBD static int TYPE_ID; Real m_area; Matrix2r m_invRestMat; + Real m_xxStiffness; + Real m_xyStiffness; + Real m_yyStiffness; + Real m_xyPoissonRatio; + Real m_yxPoissonRatio; FEMTriangleConstraint() : Constraint(3) {} virtual int &getTypeId() const { return TYPE_ID; } virtual bool initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3); + const unsigned int particle3, const Real xxStiffness, const Real yyStiffness, const Real xyStiffness, + const Real xyPoissonRatio, const Real yxPoissonRatio); virtual bool solvePositionConstraint(SimulationModel &model, const unsigned int iter); }; @@ -342,12 +353,18 @@ namespace PBD public: static int TYPE_ID; Matrix2r m_invRestMat; + Real m_xxStiffness; + Real m_xyStiffness; + Real m_yyStiffness; + bool m_normalizeStretch; + bool m_normalizeShear; StrainTriangleConstraint() : Constraint(3) {} virtual int &getTypeId() const { return TYPE_ID; } virtual bool initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3); + const unsigned int particle3, const Real xxStiffness, const Real yyStiffness, const Real xyStiffness, + const bool normalizeStretch, const bool normalizeShear); virtual bool solvePositionConstraint(SimulationModel &model, const unsigned int iter); }; @@ -355,13 +372,14 @@ namespace PBD { public: static int TYPE_ID; + Real m_stiffness; Real m_restVolume; VolumeConstraint() : Constraint(4) {} virtual int &getTypeId() const { return TYPE_ID; } virtual bool initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); + const unsigned int particle3, const unsigned int particle4, const Real stiffness); virtual bool solvePositionConstraint(SimulationModel &model, const unsigned int iter); }; @@ -369,6 +387,7 @@ namespace PBD { public: static int TYPE_ID; + Real m_stiffness; Real m_restVolume; Real m_lambda; @@ -376,7 +395,7 @@ namespace PBD virtual int& getTypeId() const { return TYPE_ID; } virtual bool initConstraint(SimulationModel& model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); + const unsigned int particle3, const unsigned int particle4, const Real stiffness); virtual bool solvePositionConstraint(SimulationModel& model, const unsigned int iter); }; @@ -384,6 +403,8 @@ namespace PBD { public: static int TYPE_ID; + Real m_stiffness; + Real m_poissonRatio; Real m_volume; Matrix3r m_invRestMat; @@ -391,7 +412,8 @@ namespace PBD virtual int &getTypeId() const { return TYPE_ID; } virtual bool initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); + const unsigned int particle3, const unsigned int particle4, + const Real stiffness, const Real poissonRatio); virtual bool solvePositionConstraint(SimulationModel &model, const unsigned int iter); }; @@ -399,13 +421,19 @@ namespace PBD { public: static int TYPE_ID; + Real m_stretchStiffness; + Real m_shearStiffness; Matrix3r m_invRestMat; + bool m_normalizeStretch; + bool m_normalizeShear; StrainTetConstraint() : Constraint(4) {} virtual int &getTypeId() const { return TYPE_ID; } virtual bool initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); + const unsigned int particle3, const unsigned int particle4, + const Real stretchStiffness, const Real shearStiffness, + const bool normalizeStretch, const bool normalizeShear); virtual bool solvePositionConstraint(SimulationModel &model, const unsigned int iter); }; @@ -413,8 +441,8 @@ namespace PBD { public: static int TYPE_ID; + Real m_stiffness; Vector3r m_restCm; - Matrix3r m_invRestMat; Real *m_w; Vector3r *m_x0; Vector3r *m_x; @@ -439,7 +467,7 @@ namespace PBD } virtual int &getTypeId() const { return TYPE_ID; } - virtual bool initConstraint(SimulationModel &model, const unsigned int particleIndices[], const unsigned int numClusters[]); + virtual bool initConstraint(SimulationModel &model, const unsigned int particleIndices[], const unsigned int numClusters[], const Real stiffness); virtual bool solvePositionConstraint(SimulationModel &model, const unsigned int iter); }; @@ -448,7 +476,7 @@ namespace PBD public: static int TYPE_ID; /** indices of the linked bodies */ - unsigned int m_bodies[2]; + std::array m_bodies; Real m_stiffness; Real m_frictionCoeff; Real m_sum_impulses; @@ -470,7 +498,7 @@ namespace PBD public: static int TYPE_ID; /** indices of the linked bodies */ - unsigned int m_bodies[2]; + std::array m_bodies; Real m_stiffness; Real m_frictionCoeff; Real m_sum_impulses; @@ -492,7 +520,7 @@ namespace PBD public: static int TYPE_ID; /** indices of the linked bodies */ - unsigned int m_bodies[2]; + std::array m_bodies; unsigned int m_solidIndex; unsigned int m_tetIndex; Vector3r m_bary; @@ -500,8 +528,8 @@ namespace PBD Real m_frictionCoeff; Eigen::Matrix m_constraintInfo; Real m_invMasses[4]; - Vector3r m_x[4]; - Vector3r m_v[4]; + std::array m_x; + std::array m_v; ParticleTetContactConstraint() { } ~ParticleTetContactConstraint() {} @@ -521,11 +549,16 @@ namespace PBD public: static int TYPE_ID; Real m_restLength; + Real m_shearingStiffness1; + Real m_shearingStiffness2; + Real m_stretchingStiffness; StretchShearConstraint() : Constraint(3) {} virtual int &getTypeId() const { return TYPE_ID; } - virtual bool initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, const unsigned int quaternion1); + virtual bool initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, + const unsigned int quaternion1, const Real stretchingStiffness, + const Real shearingStiffness1, const Real shearingStiffness2); virtual bool solvePositionConstraint(SimulationModel &model, const unsigned int iter); }; @@ -534,11 +567,16 @@ namespace PBD public: static int TYPE_ID; Quaternionr m_restDarbouxVector; + Real m_bendingStiffness1; + Real m_bendingStiffness2; + Real m_twistingStiffness; BendTwistConstraint() : Constraint(2) {} virtual int &getTypeId() const { return TYPE_ID; } - virtual bool initConstraint(SimulationModel &model, const unsigned int quaternion1, const unsigned int quaternion2); + virtual bool initConstraint(SimulationModel &model, const unsigned int quaternion1, + const unsigned int quaternion2, const Real twistingStiffness, + const Real bendingStiffness1, const Real bendingStiffness2); virtual bool solvePositionConstraint(SimulationModel &model, const unsigned int iter); }; diff --git a/Simulation/Simulation.h b/Simulation/Simulation.h index 834820de..2dc1c705 100644 --- a/Simulation/Simulation.h +++ b/Simulation/Simulation.h @@ -30,6 +30,8 @@ namespace PBD public: Simulation (); + Simulation(const Simulation&) = delete; + Simulation& operator=(const Simulation&) = delete; ~Simulation (); void init(); diff --git a/Simulation/SimulationModel.cpp b/Simulation/SimulationModel.cpp index 2089361e..978e5bf6 100644 --- a/Simulation/SimulationModel.cpp +++ b/Simulation/SimulationModel.cpp @@ -5,48 +5,11 @@ using namespace PBD; using namespace GenParam; -int SimulationModel::CLOTH_STIFFNESS = -1; -int SimulationModel::CLOTH_BENDING_STIFFNESS = -1; -int SimulationModel::CLOTH_STIFFNESS_XX = -1; -int SimulationModel::CLOTH_STIFFNESS_YY = -1; -int SimulationModel::CLOTH_STIFFNESS_XY = -1; -int SimulationModel::CLOTH_POISSON_RATIO_XY = -1; -int SimulationModel::CLOTH_POISSON_RATIO_YX = -1; -int SimulationModel::CLOTH_NORMALIZE_STRETCH = -1; -int SimulationModel::CLOTH_NORMALIZE_SHEAR = -1; -int SimulationModel::SOLID_STIFFNESS = -1; -int SimulationModel::SOLID_POISSON_RATIO = -1; -int SimulationModel::SOLID_NORMALIZE_STRETCH = -1; -int SimulationModel::SOLID_NORMALIZE_SHEAR = -1; - - SimulationModel::SimulationModel() { - m_cloth_stiffness = static_cast(1.0); - m_cloth_bendingStiffness = static_cast(0.01); - m_cloth_xxStiffness = static_cast(1.0); - m_cloth_yyStiffness = static_cast(1.0); - m_cloth_xyStiffness = static_cast(1.0); - m_cloth_xyPoissonRatio = static_cast(0.3); - m_cloth_yxPoissonRatio = static_cast(0.3); - m_cloth_normalizeShear = false; - m_cloth_normalizeStretch = false; - - m_solid_stiffness = static_cast(1.0); - m_solid_poissonRatio = static_cast(0.3); - m_solid_normalizeShear = false; - m_solid_normalizeStretch = false; - m_contactStiffnessRigidBody = 1.0; m_contactStiffnessParticleRigidBody = 100.0; - m_rod_shearingStiffness1 = 1.0; - m_rod_shearingStiffness2 = 1.0; - m_rod_stretchingStiffness = 1.0; - m_rod_bendingStiffness1 = 0.5; - m_rod_bendingStiffness2 = 0.5; - m_rod_twistingStiffness = 0.5; - m_groupsInitialized = false; m_rigidBodyContactConstraints.reserve(10000); @@ -64,73 +27,6 @@ void SimulationModel::init() initParameters(); } -void SimulationModel::initParameters() -{ - ParameterObject::initParameters(); - - CLOTH_STIFFNESS = createNumericParameter("cloth_stiffness", "Stiffness", &m_cloth_stiffness); - setGroup(CLOTH_STIFFNESS, "Cloth"); - setDescription(CLOTH_STIFFNESS, "Stiffness of cloth models."); - static_cast*>(getParameter(CLOTH_STIFFNESS))->setMinValue(0.0); - - CLOTH_STIFFNESS_XX = createNumericParameter("cloth_xxStiffness", "Youngs modulus XX", &m_cloth_xxStiffness); - setGroup(CLOTH_STIFFNESS_XX, "Cloth"); - setDescription(CLOTH_STIFFNESS_XX, "XX stiffness of orthotropic cloth models."); - static_cast*>(getParameter(CLOTH_STIFFNESS_XX))->setMinValue(0.0); - - CLOTH_STIFFNESS_YY = createNumericParameter("cloth_yyStiffness", "Youngs modulus YY", &m_cloth_yyStiffness); - setGroup(CLOTH_STIFFNESS_YY, "Cloth"); - setDescription(CLOTH_STIFFNESS_YY, "YY stiffness of orthotropic cloth models."); - static_cast*>(getParameter(CLOTH_STIFFNESS_YY))->setMinValue(0.0); - - CLOTH_STIFFNESS_XY = createNumericParameter("cloth_xyStiffness", "Youngs modulus XY", &m_cloth_xyStiffness); - setGroup(CLOTH_STIFFNESS_XY, "Cloth"); - setDescription(CLOTH_STIFFNESS_XY, "XY stiffness of orthotropic cloth models."); - static_cast*>(getParameter(CLOTH_STIFFNESS_XY))->setMinValue(0.0); - - CLOTH_POISSON_RATIO_XY = createNumericParameter("cloth_xyPoissonRatio", "Poisson ratio XY", &m_cloth_xyPoissonRatio); - setGroup(CLOTH_POISSON_RATIO_XY, "Cloth"); - setDescription(CLOTH_POISSON_RATIO_XY, "XY Poisson ratio of orthotropic cloth models."); - static_cast*>(getParameter(CLOTH_POISSON_RATIO_XY))->setMinValue(0.0); - - CLOTH_POISSON_RATIO_YX = createNumericParameter("cloth_yxPoissonRatio", "Poisson ratio YX", &m_cloth_yxPoissonRatio); - setGroup(CLOTH_POISSON_RATIO_YX, "Cloth"); - setDescription(CLOTH_POISSON_RATIO_YX, "YX Poisson ratio of orthotropic cloth models."); - static_cast*>(getParameter(CLOTH_POISSON_RATIO_YX))->setMinValue(0.0); - - CLOTH_BENDING_STIFFNESS = createNumericParameter("cloth_bendingStiffness", "Bending stiffness", &m_cloth_bendingStiffness); - setGroup(CLOTH_BENDING_STIFFNESS, "Cloth"); - setDescription(CLOTH_BENDING_STIFFNESS, "Bending stiffness of cloth models."); - static_cast*>(getParameter(CLOTH_BENDING_STIFFNESS))->setMinValue(0.0); - - CLOTH_NORMALIZE_STRETCH = createBoolParameter("cloth_normalizeStretch", "Normalize stretch", &m_cloth_normalizeStretch); - setGroup(CLOTH_NORMALIZE_STRETCH, "Cloth"); - setDescription(CLOTH_NORMALIZE_STRETCH, "Normalize stretch (strain based dynamics)"); - - CLOTH_NORMALIZE_SHEAR = createBoolParameter("cloth_normalizeShear", "Normalize shear", &m_cloth_normalizeShear); - setGroup(CLOTH_NORMALIZE_SHEAR, "Cloth"); - setDescription(CLOTH_NORMALIZE_SHEAR, "Normalize shear (strain based dynamics)"); - - SOLID_STIFFNESS = createNumericParameter("solid_stiffness", "Stiffness", &m_solid_stiffness); - setGroup(SOLID_STIFFNESS, "Solids"); - setDescription(SOLID_STIFFNESS, "Stiffness of solid models."); - static_cast*>(getParameter(SOLID_STIFFNESS))->setMinValue(0.0); - - SOLID_POISSON_RATIO = createNumericParameter("solid_poissonRatio", "Poisson ratio", &m_solid_poissonRatio); - setGroup(SOLID_POISSON_RATIO, "Solids"); - setDescription(SOLID_POISSON_RATIO, "XY Poisson ratio of solid models."); - static_cast*>(getParameter(SOLID_POISSON_RATIO))->setMinValue(0.0); - - SOLID_NORMALIZE_STRETCH = createBoolParameter("solid_normalizeStretch", "Normalize stretch", &m_solid_normalizeStretch); - setGroup(SOLID_NORMALIZE_STRETCH, "Solids"); - setDescription(SOLID_NORMALIZE_STRETCH, "Normalize stretch (strain based dynamics)"); - - SOLID_NORMALIZE_SHEAR = createBoolParameter("solid_normalizeShear", "Normalize shear", &m_solid_normalizeShear); - setGroup(SOLID_NORMALIZE_SHEAR, "Solids"); - setDescription(SOLID_NORMALIZE_SHEAR, "Normalize shear (strain based dynamics)"); - -} - void SimulationModel::cleanup() { resetContacts(); @@ -449,10 +345,10 @@ bool SimulationModel::addParticleSolidContactConstraint(const unsigned int parti return res; } -bool SimulationModel::addDistanceConstraint(const unsigned int particle1, const unsigned int particle2) +bool SimulationModel::addDistanceConstraint(const unsigned int particle1, const unsigned int particle2, const Real stiffness) { DistanceConstraint *c = new DistanceConstraint(); - const bool res = c->initConstraint(*this, particle1, particle2); + const bool res = c->initConstraint(*this, particle1, particle2, stiffness); if (res) { m_constraints.push_back(c); @@ -461,10 +357,10 @@ bool SimulationModel::addDistanceConstraint(const unsigned int particle1, const return res; } -bool SimulationModel::addDistanceConstraint_XPBD(const unsigned int particle1, const unsigned int particle2) +bool SimulationModel::addDistanceConstraint_XPBD(const unsigned int particle1, const unsigned int particle2, const Real stiffness) { DistanceConstraint_XPBD* c = new DistanceConstraint_XPBD(); - const bool res = c->initConstraint(*this, particle1, particle2); + const bool res = c->initConstraint(*this, particle1, particle2, stiffness); if (res) { m_constraints.push_back(c); @@ -474,10 +370,10 @@ bool SimulationModel::addDistanceConstraint_XPBD(const unsigned int particle1, c } bool SimulationModel::addDihedralConstraint(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) + const unsigned int particle3, const unsigned int particle4, const Real stiffness) { DihedralConstraint *c = new DihedralConstraint(); - const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4); + const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4, stiffness); if (res) { m_constraints.push_back(c); @@ -487,10 +383,10 @@ bool SimulationModel::addDihedralConstraint(const unsigned int particle1, const } bool SimulationModel::addIsometricBendingConstraint(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) + const unsigned int particle3, const unsigned int particle4, const Real stiffness) { IsometricBendingConstraint *c = new IsometricBendingConstraint(); - const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4); + const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4, stiffness); if (res) { m_constraints.push_back(c); @@ -500,10 +396,10 @@ bool SimulationModel::addIsometricBendingConstraint(const unsigned int particle1 } bool SimulationModel::addIsometricBendingConstraint_XPBD(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) + const unsigned int particle3, const unsigned int particle4, const Real stiffness) { IsometricBendingConstraint_XPBD* c = new IsometricBendingConstraint_XPBD(); - const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4); + const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4, stiffness); if (res) { m_constraints.push_back(c); @@ -513,10 +409,12 @@ bool SimulationModel::addIsometricBendingConstraint_XPBD(const unsigned int part } bool SimulationModel::addFEMTriangleConstraint(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3) + const unsigned int particle3, const Real xxStiffness, const Real yyStiffness, const Real xyStiffness, + const Real xyPoissonRatio, const Real yxPoissonRatio) { FEMTriangleConstraint *c = new FEMTriangleConstraint(); - const bool res = c->initConstraint(*this, particle1, particle2, particle3); + const bool res = c->initConstraint(*this, particle1, particle2, particle3, xxStiffness, + yyStiffness, xyStiffness, xyPoissonRatio, yxPoissonRatio); if (res) { m_constraints.push_back(c); @@ -526,10 +424,12 @@ bool SimulationModel::addFEMTriangleConstraint(const unsigned int particle1, con } bool SimulationModel::addStrainTriangleConstraint(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3) + const unsigned int particle3, const Real xxStiffness, const Real yyStiffness, const Real xyStiffness, + const bool normalizeStretch, const bool normalizeShear) { StrainTriangleConstraint *c = new StrainTriangleConstraint(); - const bool res = c->initConstraint(*this, particle1, particle2, particle3); + const bool res = c->initConstraint(*this, particle1, particle2, particle3, xxStiffness, + yyStiffness, xyStiffness, normalizeStretch, normalizeShear); if (res) { m_constraints.push_back(c); @@ -539,10 +439,10 @@ bool SimulationModel::addStrainTriangleConstraint(const unsigned int particle1, } bool SimulationModel::addVolumeConstraint(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) + const unsigned int particle3, const unsigned int particle4, const Real stiffness) { VolumeConstraint *c = new VolumeConstraint(); - const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4); + const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4, stiffness); if (res) { m_constraints.push_back(c); @@ -552,10 +452,10 @@ bool SimulationModel::addVolumeConstraint(const unsigned int particle1, const un } bool SimulationModel::addVolumeConstraint_XPBD(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) + const unsigned int particle3, const unsigned int particle4, const Real stiffness) { VolumeConstraint_XPBD* c = new VolumeConstraint_XPBD(); - const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4); + const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4, stiffness); if (res) { m_constraints.push_back(c); @@ -565,10 +465,11 @@ bool SimulationModel::addVolumeConstraint_XPBD(const unsigned int particle1, con } bool SimulationModel::addFEMTetConstraint(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) + const unsigned int particle3, const unsigned int particle4, + const Real stiffness, const Real poissonRatio) { FEMTetConstraint *c = new FEMTetConstraint(); - const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4); + const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4, stiffness, poissonRatio); if (res) { m_constraints.push_back(c); @@ -578,10 +479,13 @@ bool SimulationModel::addFEMTetConstraint(const unsigned int particle1, const un } bool SimulationModel::addStrainTetConstraint(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4) + const unsigned int particle3, const unsigned int particle4, + const Real stretchStiffness, const Real shearStiffness, + const bool normalizeStretch, const bool normalizeShear) { StrainTetConstraint *c = new StrainTetConstraint(); - const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4); + const bool res = c->initConstraint(*this, particle1, particle2, particle3, particle4, stretchStiffness, shearStiffness, + normalizeStretch, normalizeShear); if (res) { m_constraints.push_back(c); @@ -590,10 +494,10 @@ bool SimulationModel::addStrainTetConstraint(const unsigned int particle1, const return res; } -bool SimulationModel::addShapeMatchingConstraint(const unsigned int numberOfParticles, const unsigned int particleIndices[], const unsigned int numClusters[]) +bool SimulationModel::addShapeMatchingConstraint(const unsigned int numberOfParticles, const unsigned int particleIndices[], const unsigned int numClusters[], const Real stiffness) { ShapeMatchingConstraint *c = new ShapeMatchingConstraint(numberOfParticles); - const bool res = c->initConstraint(*this, particleIndices, numClusters); + const bool res = c->initConstraint(*this, particleIndices, numClusters, stiffness); if (res) { m_constraints.push_back(c); @@ -602,10 +506,12 @@ bool SimulationModel::addShapeMatchingConstraint(const unsigned int numberOfPart return res; } -bool SimulationModel::addStretchShearConstraint(const unsigned int particle1, const unsigned int particle2, const unsigned int quaternion1) +bool SimulationModel::addStretchShearConstraint(const unsigned int particle1, const unsigned int particle2, + const unsigned int quaternion1, const Real stretchingStiffness, + const Real shearingStiffness1, const Real shearingStiffness2) { StretchShearConstraint *c = new StretchShearConstraint(); - const bool res = c->initConstraint(*this, particle1, particle2, quaternion1); + const bool res = c->initConstraint(*this, particle1, particle2, quaternion1, stretchingStiffness, shearingStiffness1, shearingStiffness2); if (res) { m_constraints.push_back(c); @@ -614,10 +520,12 @@ bool SimulationModel::addStretchShearConstraint(const unsigned int particle1, co return res; } -bool SimulationModel::addBendTwistConstraint(const unsigned int quaternion1, const unsigned int quaternion2) +bool SimulationModel::addBendTwistConstraint(const unsigned int quaternion1, + const unsigned int quaternion2, const Real twistingStiffness, + const Real bendingStiffness1, const Real bendingStiffness2) { BendTwistConstraint *c = new BendTwistConstraint(); - const bool res = c->initConstraint(*this, quaternion1, quaternion2); + const bool res = c->initConstraint(*this, quaternion1, quaternion2, twistingStiffness, bendingStiffness1, bendingStiffness2); if (res) { m_constraints.push_back(c); @@ -689,6 +597,76 @@ void SimulationModel::addTriangleModel( triModel->updateMeshNormals(m_particles); } +void SimulationModel::addRegularTriangleModel(const int width, const int height, + const Vector3r& translation, + const Matrix3r& rotation, + const Vector2r& scale) +{ + TriangleModel::ParticleMesh::UVs uvs; + uvs.resize(width * height); + + const Real dy = scale[1] / (Real)(height - 1); + const Real dx = scale[0] / (Real)(width - 1); + + std::vector points; + points.resize(width * height); + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + const Real y = (Real)dy * i; + const Real x = (Real)dx * j; + points[i * width + j] = rotation * Vector3r(x, y, 0.0) + translation; + + uvs[i * width + j][0] = x / scale[0]; + uvs[i * width + j][1] = y / scale[1]; + } + } + const int nIndices = 6 * (height - 1) * (width - 1); + + TriangleModel::ParticleMesh::UVIndices uvIndices; + uvIndices.resize(nIndices); + + std::vector indices; + indices.resize(nIndices); + int index = 0; + for (int i = 0; i < height - 1; i++) + { + for (int j = 0; j < width - 1; j++) + { + int helper = 0; + if (i % 2 == j % 2) + helper = 1; + + indices[index] = i * width + j; + indices[index + 1] = i * width + j + 1; + indices[index + 2] = (i + 1) * width + j + helper; + + uvIndices[index] = i * width + j; + uvIndices[index + 1] = i * width + j + 1; + uvIndices[index + 2] = (i + 1) * width + j + helper; + index += 3; + + indices[index] = (i + 1) * width + j + 1; + indices[index + 1] = (i + 1) * width + j; + indices[index + 2] = i * width + j + 1 - helper; + + uvIndices[index] = (i + 1) * width + j + 1; + uvIndices[index + 1] = (i + 1) * width + j; + uvIndices[index + 2] = i * width + j + 1 - helper; + index += 3; + } + } + + const unsigned int nPoints = height * width; + const unsigned int nFaces = nIndices / 3; + addTriangleModel(nPoints, nFaces, points.data(), indices.data(), uvIndices, uvs); + + ParticleData& pd = getParticles(); + for (unsigned int i = 0; i < pd.getNumberOfParticles(); i++) + pd.setMass(i, 1.0); +} + void SimulationModel::addTetModel( const unsigned int nPoints, const unsigned int nTets, @@ -707,6 +685,90 @@ void SimulationModel::addTetModel( tetModel->initMesh(nPoints, nTets, startIndex, indices); } +void SimulationModel::addRegularTetModel(const int width, const int height, const int depth, + const Vector3r& translation, + const Matrix3r& rotation, + const Vector3r& scale) +{ + std::vector points; + points.resize(width * height * depth); + + const Real dx = scale[0] / (Real)(width - 1); + const Real dy = scale[1] / (Real)(height - 1); + const Real dz = scale[2] / (Real)(depth - 1); + + // center in origin + const Vector3r t = translation - 0.5*scale; + for (int i = 0; i < width; i++) + { + for (int j = 0; j < height; j++) + { + for (int k = 0; k < depth; k++) + { + const Real x = (Real)dx * i; + const Real y = (Real)dy * j; + const Real z = (Real)dz * k; + + points[i * height * depth + j * depth + k] = rotation * Vector3r(x, y, z) + t; + } + } + } + + std::vector indices; + indices.reserve(width * height * depth * 5); + for (int i = 0; i < width - 1; i++) + { + for (int j = 0; j < height - 1; j++) + { + for (int k = 0; k < depth - 1; k++) + { + // For each block, the 8 corners are numerated as: + // 4*-----*7 + // /| /| + // / | / | + // 5*-----*6 | + // | 0*--|--*3 + // | / | / + // |/ |/ + // 1*-----*2 + unsigned int p0 = i * height * depth + j * depth + k; + unsigned int p1 = p0 + 1; + unsigned int p3 = (i + 1) * height * depth + j * depth + k; + unsigned int p2 = p3 + 1; + unsigned int p7 = (i + 1) * height * depth + (j + 1) * depth + k; + unsigned int p6 = p7 + 1; + unsigned int p4 = i * height * depth + (j + 1) * depth + k; + unsigned int p5 = p4 + 1; + + // Ensure that neighboring tetras are sharing faces + if ((i + j + k) % 2 == 1) + { + indices.push_back(p2); indices.push_back(p1); indices.push_back(p6); indices.push_back(p3); + indices.push_back(p6); indices.push_back(p3); indices.push_back(p4); indices.push_back(p7); + indices.push_back(p4); indices.push_back(p1); indices.push_back(p6); indices.push_back(p5); + indices.push_back(p3); indices.push_back(p1); indices.push_back(p4); indices.push_back(p0); + indices.push_back(p6); indices.push_back(p1); indices.push_back(p4); indices.push_back(p3); + } + else + { + indices.push_back(p0); indices.push_back(p2); indices.push_back(p5); indices.push_back(p1); + indices.push_back(p7); indices.push_back(p2); indices.push_back(p0); indices.push_back(p3); + indices.push_back(p5); indices.push_back(p2); indices.push_back(p7); indices.push_back(p6); + indices.push_back(p7); indices.push_back(p0); indices.push_back(p5); indices.push_back(p4); + indices.push_back(p0); indices.push_back(p2); indices.push_back(p7); indices.push_back(p5); + } + } + } + } + addTetModel(width * height * depth, (unsigned int)indices.size() / 4u, points.data(), indices.data()); + + ParticleData& pd = getParticles(); + for (unsigned int i = 0; i < pd.getNumberOfParticles(); i++) + { + pd.setMass(i, 1.0); + } +} + void SimulationModel::addLineModel( const unsigned int nPoints, const unsigned int nQuaternions, @@ -756,7 +818,7 @@ void SimulationModel::initConstraintGroups() { bool addToThisGroup = true; - for (unsigned int k = 0; k < constraint->m_numberOfBodies; k++) + for (unsigned int k = 0; k < constraint->numberOfBodies(); k++) { if (mapping[j][constraint->m_bodies[k]] != 0) { @@ -769,7 +831,7 @@ void SimulationModel::initConstraintGroups() { m_constraintGroups[j].push_back(i); - for (unsigned int k = 0; k < constraint->m_numberOfBodies; k++) + for (unsigned int k = 0; k < constraint->numberOfBodies(); k++) mapping[j][constraint->m_bodies[k]] = 1; addToNewGroup = false; @@ -782,7 +844,7 @@ void SimulationModel::initConstraintGroups() memset(mapping[mapping.size() - 1], 0, sizeof(unsigned char)*numBodies); m_constraintGroups.resize(m_constraintGroups.size() + 1); m_constraintGroups[m_constraintGroups.size()-1].push_back(i); - for (unsigned int k = 0; k < constraint->m_numberOfBodies; k++) + for (unsigned int k = 0; k < constraint->numberOfBodies(); k++) mapping[m_constraintGroups.size() - 1][constraint->m_bodies[k]] = 1; } } @@ -803,3 +865,225 @@ void SimulationModel::resetContacts() m_particleSolidContactConstraints.clear(); } +void SimulationModel::addClothConstraints(const TriangleModel* tm, const unsigned int clothMethod, + const Real distanceStiffness, const Real xxStiffness, const Real yyStiffness, + const Real xyStiffness, const Real xyPoissonRatio, const Real yxPoissonRatio, + const bool normalizeStretch, const bool normalizeShear) +{ + if (clothMethod == 1) + { + const unsigned int offset = tm->getIndexOffset(); + const unsigned int nEdges = tm->getParticleMesh().numEdges(); + const Utilities::IndexedFaceMesh::Edge* edges = tm->getParticleMesh().getEdges().data(); + for (unsigned int i = 0; i < nEdges; i++) + { + const unsigned int v1 = edges[i].m_vert[0] + offset; + const unsigned int v2 = edges[i].m_vert[1] + offset; + + addDistanceConstraint(v1, v2, distanceStiffness); + } + } + else if (clothMethod == 2) + { + const unsigned int offset = tm->getIndexOffset(); + const TriangleModel::ParticleMesh& mesh = tm->getParticleMesh(); + const unsigned int* tris = mesh.getFaces().data(); + const unsigned int nFaces = mesh.numFaces(); + for (unsigned int i = 0; i < nFaces; i++) + { + const unsigned int v1 = tris[3 * i] + offset; + const unsigned int v2 = tris[3 * i + 1] + offset; + const unsigned int v3 = tris[3 * i + 2] + offset; + addFEMTriangleConstraint(v1, v2, v3, xxStiffness, yyStiffness, xyStiffness, xyPoissonRatio, yxPoissonRatio); + } + } + else if (clothMethod == 3) + { + const unsigned int offset = tm->getIndexOffset(); + const TriangleModel::ParticleMesh& mesh = tm->getParticleMesh(); + const unsigned int* tris = mesh.getFaces().data(); + const unsigned int nFaces = mesh.numFaces(); + for (unsigned int i = 0; i < nFaces; i++) + { + const unsigned int v1 = tris[3 * i] + offset; + const unsigned int v2 = tris[3 * i + 1] + offset; + const unsigned int v3 = tris[3 * i + 2] + offset; + addStrainTriangleConstraint(v1, v2, v3, xxStiffness, yyStiffness, xyStiffness, normalizeStretch, normalizeShear); + } + } + else if (clothMethod == 4) + { + const unsigned int offset = tm->getIndexOffset(); + const unsigned int nEdges = tm->getParticleMesh().numEdges(); + const Utilities::IndexedFaceMesh::Edge* edges = tm->getParticleMesh().getEdges().data(); + for (unsigned int i = 0; i < nEdges; i++) + { + const unsigned int v1 = edges[i].m_vert[0] + offset; + const unsigned int v2 = edges[i].m_vert[1] + offset; + + addDistanceConstraint_XPBD(v1, v2, distanceStiffness); + } + } +} + +void SimulationModel::addBendingConstraints(const TriangleModel *tm, const unsigned int bendingMethod, const Real stiffness) +{ + if ((bendingMethod < 1) || (bendingMethod > 3)) + return; + + const unsigned int offset = tm->getIndexOffset(); + const TriangleModel::ParticleMesh& mesh = tm->getParticleMesh(); + unsigned int nEdges = mesh.numEdges(); + const TriangleModel::ParticleMesh::Edge* edges = mesh.getEdges().data(); + const unsigned int* tris = mesh.getFaces().data(); + for (unsigned int i = 0; i < nEdges; i++) + { + const int tri1 = edges[i].m_face[0]; + const int tri2 = edges[i].m_face[1]; + if ((tri1 != 0xffffffff) && (tri2 != 0xffffffff)) + { + // Find the triangle points which do not lie on the axis + const int axisPoint1 = edges[i].m_vert[0]; + const int axisPoint2 = edges[i].m_vert[1]; + int point1 = -1; + int point2 = -1; + for (int j = 0; j < 3; j++) + { + if ((tris[3 * tri1 + j] != axisPoint1) && (tris[3 * tri1 + j] != axisPoint2)) + { + point1 = tris[3 * tri1 + j]; + break; + } + } + for (int j = 0; j < 3; j++) + { + if ((tris[3 * tri2 + j] != axisPoint1) && (tris[3 * tri2 + j] != axisPoint2)) + { + point2 = tris[3 * tri2 + j]; + break; + } + } + if ((point1 != -1) && (point2 != -1)) + { + const unsigned int vertex1 = point1 + offset; + const unsigned int vertex2 = point2 + offset; + const unsigned int vertex3 = edges[i].m_vert[0] + offset; + const unsigned int vertex4 = edges[i].m_vert[1] + offset; + if (bendingMethod == 1) + addDihedralConstraint(vertex1, vertex2, vertex3, vertex4, stiffness); + else if (bendingMethod == 2) + addIsometricBendingConstraint(vertex1, vertex2, vertex3, vertex4, stiffness); + else if (bendingMethod == 3) + { + addIsometricBendingConstraint_XPBD(vertex1, vertex2, vertex3, vertex4, stiffness); + } + } + } + } +} + +void SimulationModel::addSolidConstraints(const TetModel* tm, const unsigned int solidMethod, const Real stiffness, + const Real poissonRatio, const Real volumeStiffness, + const bool normalizeStretch, const bool normalizeShear) +{ + const unsigned int nTets = tm->getParticleMesh().numTets(); + const unsigned int* tets = tm->getParticleMesh().getTets().data(); + const Utilities::IndexedTetMesh::VerticesTets& vTets = tm->getParticleMesh().getVertexTets(); + const unsigned int offset = tm->getIndexOffset(); + if (solidMethod == 1) + { + const unsigned int nEdges = tm->getParticleMesh().numEdges(); + const Utilities::IndexedTetMesh::Edge* edges = tm->getParticleMesh().getEdges().data(); + for (unsigned int i = 0; i < nEdges; i++) + { + const unsigned int v1 = edges[i].m_vert[0] + offset; + const unsigned int v2 = edges[i].m_vert[1] + offset; + + addDistanceConstraint(v1, v2, stiffness); + } + + for (unsigned int i = 0; i < nTets; i++) + { + const unsigned int v1 = tets[4 * i] + offset; + const unsigned int v2 = tets[4 * i + 1] + offset; + const unsigned int v3 = tets[4 * i + 2] + offset; + const unsigned int v4 = tets[4 * i + 3] + offset; + + addVolumeConstraint(v1, v2, v3, v4, volumeStiffness); + } + } + else if (solidMethod == 2) + { + const TetModel::ParticleMesh& mesh = tm->getParticleMesh(); + for (unsigned int i = 0; i < nTets; i++) + { + const unsigned int v1 = tets[4 * i] + offset; + const unsigned int v2 = tets[4 * i + 1] + offset; + const unsigned int v3 = tets[4 * i + 2] + offset; + const unsigned int v4 = tets[4 * i + 3] + offset; + + addFEMTetConstraint(v1, v2, v3, v4, stiffness, poissonRatio); + } + } + else if (solidMethod == 3) + { + const TetModel::ParticleMesh& mesh = tm->getParticleMesh(); + for (unsigned int i = 0; i < nTets; i++) + { + const unsigned int v1 = tets[4 * i] + offset; + const unsigned int v2 = tets[4 * i + 1] + offset; + const unsigned int v3 = tets[4 * i + 2] + offset; + const unsigned int v4 = tets[4 * i + 3] + offset; + + addStrainTetConstraint(v1, v2, v3, v4, stiffness, stiffness, normalizeStretch, normalizeStretch); + } + } + else if (solidMethod == 4) + { + const TetModel::ParticleMesh& mesh = tm->getParticleMesh(); + for (unsigned int i = 0; i < nTets; i++) + { + const unsigned int v[4] = { tets[4 * i] + offset, + tets[4 * i + 1] + offset, + tets[4 * i + 2] + offset, + tets[4 * i + 3] + offset }; + // Important: Divide position correction by the number of clusters + // which contain the vertex. + const unsigned int nc[4] = { (unsigned int)vTets[v[0]-offset].size(), (unsigned int)vTets[v[1] - offset].size(), (unsigned int)vTets[v[2] - offset].size(), (unsigned int)vTets[v[3] - offset].size() }; + addShapeMatchingConstraint(4, v, nc, stiffness); + + if (v[0] == 1005) + std::cout << v[0] << ", " << v[1] << ", " << v[2] << ", " << v[3] << "chk\n"; + if (v[1] == 1005) + std::cout << v[0] << ", " << v[1] << ", " << v[2] << ", " << v[3] << "chk\n"; + if (v[2] == 1005) + std::cout << v[0] << ", " << v[1] << ", " << v[2] << ", " << v[3] << "chk\n"; + if (v[3] == 1005) + std::cout << v[0] << ", " << v[1] << ", " << v[2] << ", " << v[3] << "chk\n"; + } + } + else if (solidMethod == 5) + { + const unsigned int offset = tm->getIndexOffset(); + const unsigned int nEdges = tm->getParticleMesh().numEdges(); + const Utilities::IndexedTetMesh::Edge* edges = tm->getParticleMesh().getEdges().data(); + for (unsigned int i = 0; i < nEdges; i++) + { + const unsigned int v1 = edges[i].m_vert[0] + offset; + const unsigned int v2 = edges[i].m_vert[1] + offset; + + addDistanceConstraint_XPBD(v1, v2, stiffness); + } + + for (unsigned int i = 0; i < nTets; i++) + { + const unsigned int v1 = tets[4 * i] + offset; + const unsigned int v2 = tets[4 * i + 1] + offset; + const unsigned int v3 = tets[4 * i + 2] + offset; + const unsigned int v4 = tets[4 * i + 3] + offset; + + addVolumeConstraint_XPBD(v1, v2, v3, v4, volumeStiffness); + } + } +} + diff --git a/Simulation/SimulationModel.h b/Simulation/SimulationModel.h index 4c6e7641..6a3a620e 100644 --- a/Simulation/SimulationModel.h +++ b/Simulation/SimulationModel.h @@ -16,24 +16,10 @@ namespace PBD class SimulationModel : public GenParam::ParameterObject { - public: - static int CLOTH_STIFFNESS; - static int CLOTH_BENDING_STIFFNESS; - static int CLOTH_STIFFNESS_XX; - static int CLOTH_STIFFNESS_YY; - static int CLOTH_STIFFNESS_XY; - static int CLOTH_POISSON_RATIO_XY; - static int CLOTH_POISSON_RATIO_YX; - static int CLOTH_NORMALIZE_STRETCH; - static int CLOTH_NORMALIZE_SHEAR; - - static int SOLID_STIFFNESS; - static int SOLID_POISSON_RATIO; - static int SOLID_NORMALIZE_STRETCH; - static int SOLID_NORMALIZE_SHEAR; - public: SimulationModel(); + SimulationModel(const SimulationModel&) = delete; + SimulationModel& operator=(const SimulationModel&) = delete; virtual ~SimulationModel(); void init(); @@ -63,33 +49,9 @@ namespace PBD ParticleSolidContactConstraintVector m_particleSolidContactConstraints; ConstraintGroupVector m_constraintGroups; - Real m_cloth_stiffness; - Real m_cloth_bendingStiffness; - Real m_cloth_xxStiffness; - Real m_cloth_yyStiffness; - Real m_cloth_xyStiffness; - Real m_cloth_xyPoissonRatio; - Real m_cloth_yxPoissonRatio; - bool m_cloth_normalizeStretch; - bool m_cloth_normalizeShear; - - Real m_solid_stiffness; - Real m_solid_poissonRatio; - bool m_solid_normalizeStretch; - bool m_solid_normalizeShear; - Real m_contactStiffnessRigidBody; Real m_contactStiffnessParticleRigidBody; - Real m_rod_stretchingStiffness; - Real m_rod_shearingStiffness1; - Real m_rod_shearingStiffness2; - Real m_rod_bendingStiffness1; - Real m_rod_bendingStiffness2; - Real m_rod_twistingStiffness; - - virtual void initParameters(); - public: void reset(); void cleanup(); @@ -116,12 +78,20 @@ namespace PBD unsigned int* indices, const TriangleModel::ParticleMesh::UVIndices& uvIndices, const TriangleModel::ParticleMesh::UVs& uvs); + void addRegularTriangleModel(const int width, const int height, + const Vector3r& translation = Vector3r::Zero(), + const Matrix3r& rotation = Matrix3r::Identity(), + const Vector2r& scale = Vector2r::Ones()); void addTetModel( const unsigned int nPoints, const unsigned int nTets, Vector3r *points, unsigned int* indices); + void addRegularTetModel(const int width, const int height, const int depth, + const Vector3r& translation = Vector3r::Zero(), + const Matrix3r& rotation = Matrix3r::Identity(), + const Vector3r& scale = Vector3r::Ones()); void addLineModel( const unsigned int nPoints, @@ -162,27 +132,37 @@ namespace PBD const Vector3r &normal, const Real dist, const Real restitutionCoeff, const Real frictionCoeff); - bool addDistanceConstraint(const unsigned int particle1, const unsigned int particle2); - bool addDistanceConstraint_XPBD(const unsigned int particle1, const unsigned int particle2); + bool addDistanceConstraint(const unsigned int particle1, const unsigned int particle2, const Real stiffness); + bool addDistanceConstraint_XPBD(const unsigned int particle1, const unsigned int particle2, const Real stiffness); bool addDihedralConstraint( const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); + const unsigned int particle3, const unsigned int particle4, const Real stiffness); bool addIsometricBendingConstraint(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); + const unsigned int particle3, const unsigned int particle4, const Real stiffness); bool addIsometricBendingConstraint_XPBD(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); - bool addFEMTriangleConstraint(const unsigned int particle1, const unsigned int particle2, const unsigned int particle3); - bool addStrainTriangleConstraint(const unsigned int particle1, const unsigned int particle2, const unsigned int particle3); + const unsigned int particle3, const unsigned int particle4, const Real stiffness); + bool addFEMTriangleConstraint(const unsigned int particle1, const unsigned int particle2, const unsigned int particle3, + const Real xxStiffness, const Real yyStiffness, const Real xyStiffness, + const Real xyPoissonRatio, const Real yxPoissonRatio); + bool addStrainTriangleConstraint(const unsigned int particle1, const unsigned int particle2, + const unsigned int particle3, const Real xxStiffness, const Real yyStiffness, const Real xyStiffness, + const bool normalizeStretch, const bool normalizeShear); bool addVolumeConstraint(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); + const unsigned int particle3, const unsigned int particle4, const Real stiffness); bool addVolumeConstraint_XPBD(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); + const unsigned int particle3, const unsigned int particle4, const Real stiffness); bool addFEMTetConstraint(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); + const unsigned int particle3, const unsigned int particle4, + const Real stiffness, const Real poissonRatio); bool addStrainTetConstraint(const unsigned int particle1, const unsigned int particle2, - const unsigned int particle3, const unsigned int particle4); - bool addShapeMatchingConstraint(const unsigned int numberOfParticles, const unsigned int particleIndices[], const unsigned int numClusters[]); - bool addStretchShearConstraint(const unsigned int particle1, const unsigned int particle2, const unsigned int quaternion1); - bool addBendTwistConstraint(const unsigned int quaternion1, const unsigned int quaternion2); + const unsigned int particle3, const unsigned int particle4, + const Real stretchStiffness, const Real shearStiffness, + const bool normalizeStretch, const bool normalizeShear); + bool addShapeMatchingConstraint(const unsigned int numberOfParticles, const unsigned int particleIndices[], const unsigned int numClusters[], const Real stiffness); + bool addStretchShearConstraint(const unsigned int particle1, const unsigned int particle2, + const unsigned int quaternion1, const Real stretchingStiffness, + const Real shearingStiffness1, const Real shearingStiffness2); + bool addBendTwistConstraint(const unsigned int quaternion1, const unsigned int quaternion2, + const Real twistingStiffness, const Real bendingStiffness1, const Real bendingStiffness2); bool addStretchBendingTwistingConstraint(const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &pos, const Real averageRadius, const Real averageSegmentLength, const Real youngsModulus, const Real torsionModulus); bool addDirectPositionBasedSolverForStiffRodsConstraint(const std::vector> & jointSegmentIndices, const std::vector &jointPositions, const std::vector &averageRadii, const std::vector &averageSegmentLengths, const std::vector &youngsModuli, const std::vector &torsionModuli); @@ -191,18 +171,25 @@ namespace PBD Real getContactStiffnessParticleRigidBody() const { return m_contactStiffnessParticleRigidBody; } void setContactStiffnessParticleRigidBody(Real val) { m_contactStiffnessParticleRigidBody = val; } - Real getRodStretchingStiffness() const { return m_rod_stretchingStiffness; } - void setRodStretchingStiffness(Real val) { m_rod_stretchingStiffness = val; } - Real getRodShearingStiffness1() const { return m_rod_shearingStiffness1; } - void setRodShearingStiffness1(Real val) { m_rod_shearingStiffness1 = val; } - Real getRodShearingStiffness2() const { return m_rod_shearingStiffness2; } - void setRodShearingStiffness2(Real val) { m_rod_shearingStiffness2 = val; } - Real getRodBendingStiffness1() const { return m_rod_bendingStiffness1; } - void setRodBendingStiffness1(Real val) { m_rod_bendingStiffness1 = val; } - Real getRodBendingStiffness2() const { return m_rod_bendingStiffness2; } - void setRodBendingStiffness2(Real val) { m_rod_bendingStiffness2 = val; } - Real getRodTwistingStiffness() const { return m_rod_twistingStiffness; } - void setRodTwistingStiffness(Real val) { m_rod_twistingStiffness = val; } + void addClothConstraints(const TriangleModel* tm, const unsigned int clothMethod, + const Real distanceStiffness, const Real xxStiffness, const Real yyStiffness, + const Real xyStiffness, const Real xyPoissonRatio, const Real yxPoissonRatio, + const bool normalizeStretch, const bool normalizeShear); + void addBendingConstraints(const TriangleModel* tm, const unsigned int bendingMethod, const Real stiffness); + void addSolidConstraints(const TetModel* tm, const unsigned int solidMethod, const Real stiffness, + const Real poissonRatio, const Real volumeStiffness, + const bool normalizeStretch, const bool normalizeShear); + + template + void setConstraintValue(const T v) + { + for (auto i = 0; i < m_constraints.size(); i++) + { + ConstraintType* c = dynamic_cast(m_constraints[i]); + if (c != nullptr) + c->*MemPtr = v; + } + } }; } diff --git a/Simulation/TetModel.cpp b/Simulation/TetModel.cpp index 92e70c58..47e5c354 100644 --- a/Simulation/TetModel.cpp +++ b/Simulation/TetModel.cpp @@ -26,11 +26,6 @@ void TetModel::cleanupModel() m_particleMesh.release(); } -TetModel::ParticleMesh &TetModel::getParticleMesh() -{ - return m_particleMesh; -} - TetModel::SurfaceMesh &TetModel::getSurfaceMesh() { return m_surfaceMesh; diff --git a/Simulation/TetModel.h b/Simulation/TetModel.h index af64dca1..a79d122c 100644 --- a/Simulation/TetModel.h +++ b/Simulation/TetModel.h @@ -59,12 +59,11 @@ namespace PBD public: - void updateConstraints(); - SurfaceMesh &getSurfaceMesh(); VertexData &getVisVertices(); SurfaceMesh &getVisMesh(); - ParticleMesh &getParticleMesh(); + ParticleMesh& getParticleMesh() { return m_particleMesh; } + const ParticleMesh& getParticleMesh() const { return m_particleMesh; } void cleanupModel(); unsigned int getIndexOffset() const; diff --git a/Simulation/TimeStepController.cpp b/Simulation/TimeStepController.cpp index 3c43a02e..32dabde5 100644 --- a/Simulation/TimeStepController.cpp +++ b/Simulation/TimeStepController.cpp @@ -175,9 +175,8 @@ void TimeStepController::step(SimulationModel &model) h = hOld; tm->setTimeStepSize(hOld); - #pragma omp parallel if(numBodies > MIN_PARALLEL_SIZE) default(shared) + #pragma omp parallel default(shared) { - // Update velocities #pragma omp for schedule(static) nowait for (int i = 0; i < numBodies; i++) { diff --git a/Simulation/TriangleModel.cpp b/Simulation/TriangleModel.cpp index cc66bc28..09d35ad8 100644 --- a/Simulation/TriangleModel.cpp +++ b/Simulation/TriangleModel.cpp @@ -27,11 +27,6 @@ void TriangleModel::updateMeshNormals(const ParticleData &pd) m_particleMesh.updateVertexNormals(pd); } -TriangleModel::ParticleMesh &TriangleModel::getParticleMesh() -{ - return m_particleMesh; -} - void TriangleModel::initMesh(const unsigned int nPoints, const unsigned int nFaces, const unsigned int indexOffset, unsigned int* indices, const ParticleMesh::UVIndices& uvIndices, const ParticleMesh::UVs& uvs) { m_indexOffset = indexOffset; diff --git a/Simulation/TriangleModel.h b/Simulation/TriangleModel.h index d3d1f3f8..8acbe411 100644 --- a/Simulation/TriangleModel.h +++ b/Simulation/TriangleModel.h @@ -27,9 +27,9 @@ namespace PBD Real m_frictionCoeff; public: - void updateConstraints(); + ParticleMesh &getParticleMesh() { return m_particleMesh; } + const ParticleMesh& getParticleMesh() const { return m_particleMesh; } - ParticleMesh &getParticleMesh(); void cleanupModel(); unsigned int getIndexOffset() const; diff --git a/Utils/CMakeLists.txt b/Utils/CMakeLists.txt index 2358afcd..b24386ec 100644 --- a/Utils/CMakeLists.txt +++ b/Utils/CMakeLists.txt @@ -4,7 +4,10 @@ git_local_changes(GIT_LOCAL_CHANGES) if (${GIT_LOCAL_CHANGES} STREQUAL "DIRTY") set(COMPILER_MESSAGE "#pragma WARNING(Local changes not committed.)") endif() -configure_file("${PROJECT_PATH}/Utils/Version.h.in" "${PROJECT_PATH}/Utils/Version.h" @ONLY) + +file (STRINGS "../version" PBD_VERSION) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/Version.h @ONLY) add_library(Utils FileSystem.h diff --git a/Utils/IndexedFaceMesh.cpp b/Utils/IndexedFaceMesh.cpp index 5f01d9e8..526bf185 100644 --- a/Utils/IndexedFaceMesh.cpp +++ b/Utils/IndexedFaceMesh.cpp @@ -7,22 +7,21 @@ IndexedFaceMesh& IndexedFaceMesh::operator=(IndexedFaceMesh const& other) m_numPoints = other.m_numPoints; m_indices = other.m_indices; m_edges = other.m_edges; - m_faces = other.m_faces; + m_facesEdges = other.m_facesEdges; m_closed = other.m_closed; m_uvIndices = other.m_uvIndices; m_uvs = other.m_uvs; - m_verticesPerFace = other.m_verticesPerFace; m_normals = other.m_normals; m_vertexNormals = other.m_vertexNormals; - for (size_t i(0u); i < m_faces.size(); ++i) + for (size_t i(0u); i < m_facesEdges.size(); ++i) { - m_faces[i].m_edges = new unsigned int[m_verticesPerFace]; + m_facesEdges[i].resize(m_verticesPerFace); #if defined(_MSC_VER) - std::copy(other.m_faces[i].m_edges, other.m_faces[i].m_edges + m_verticesPerFace, - stdext::unchecked_array_iterator(m_faces[i].m_edges)); + std::copy(other.m_facesEdges[i].data(), other.m_facesEdges[i].data() + m_verticesPerFace, + stdext::unchecked_array_iterator(m_facesEdges[i].data())); #else - std::copy(other.m_faces[i].m_edges, other.m_faces[i].m_edges + m_verticesPerFace, m_faces[i].m_edges); + std::copy(other.m_facesEdges[i].data(), other.m_facesEdges[i].data() + m_verticesPerFace, m_facesEdges[i].data()); #endif } @@ -42,9 +41,8 @@ IndexedFaceMesh::IndexedFaceMesh(IndexedFaceMesh const& other) *this = other; } -IndexedFaceMesh::IndexedFaceMesh(const unsigned int verticesPerFace) +IndexedFaceMesh::IndexedFaceMesh() { - m_verticesPerFace = verticesPerFace; m_closed=false; m_flatShading = false; } @@ -64,7 +62,7 @@ void IndexedFaceMesh::initMesh(const unsigned int nPoints, const unsigned int nE m_numPoints = nPoints; m_indices.reserve(nFaces*m_verticesPerFace); m_edges.reserve(nEdges); - m_faces.reserve(nFaces); + m_facesEdges.reserve(nFaces); m_uvIndices.reserve(nFaces); m_uvs.reserve(nPoints); m_verticesFaces.reserve(nPoints); @@ -77,9 +75,8 @@ void IndexedFaceMesh::release() { m_indices.clear(); m_edges.clear(); - for(unsigned int i=0; i < m_faces.size(); i++) - delete [] m_faces[i].m_edges; - m_faces.clear(); + m_facesEdges.clear(); + m_facesEdges.clear(); m_uvIndices.clear(); m_uvs.clear(); m_verticesFaces.clear(); @@ -120,22 +117,24 @@ void IndexedFaceMesh::addUVIndex(const unsigned int index) void IndexedFaceMesh::buildNeighbors() { typedef std::vector PEdges; - typedef std::vector VertexFE; PEdges* pEdges = new PEdges[numVertices()]; - VertexFE* vFaces = new VertexFE[numVertices()]; - VertexFE* vEdges = new VertexFE[numVertices()]; - for(unsigned int i=0; i < m_faces.size(); i++) - delete [] m_faces[i].m_edges; + // build vertex-face structure + m_verticesFaces.clear(); // to delete old pointers + m_verticesFaces.resize(numVertices()); + m_verticesEdges.clear(); // to delete old pointers + m_verticesEdges.resize(numVertices()); + m_facesEdges.clear(); + m_facesEdges.resize(numFaces()); + m_edges.clear(); - m_faces.resize(numFaces()); unsigned int *v = new unsigned int[m_verticesPerFace]; unsigned int *edges = new unsigned int[m_verticesPerFace*2]; for(unsigned int i=0; i < numFaces(); i++) { - m_faces[i].m_edges = new unsigned int[m_verticesPerFace]; + m_facesEdges[i].resize(m_verticesPerFace); for (unsigned int j=0u; j < m_verticesPerFace; j++) v[j] = m_indices[m_verticesPerFace*i+j]; @@ -152,9 +151,9 @@ void IndexedFaceMesh::buildNeighbors() // add vertex-face connection const unsigned int vIndex = m_indices[m_verticesPerFace*i+j]; bool found = false; - for(unsigned int k=0; k < vFaces[vIndex].size(); k++) + for(unsigned int k=0; k < m_verticesFaces[vIndex].size(); k++) { - if (vFaces[vIndex][k] == i) + if (m_verticesFaces[vIndex][k] == i) { found = true; break; @@ -162,7 +161,7 @@ void IndexedFaceMesh::buildNeighbors() } if (!found) { - vFaces[vIndex].push_back(i); + m_verticesFaces[vIndex].push_back(i); } // add edge information @@ -192,8 +191,8 @@ void IndexedFaceMesh::buildNeighbors() edge = (unsigned int) m_edges.size() - 1u; // add vertex-edge connection - vEdges[a].push_back(edge); - vEdges[b].push_back(edge); + m_verticesEdges[a].push_back(edge); + m_verticesEdges[b].push_back(edge); } else { @@ -204,28 +203,12 @@ void IndexedFaceMesh::buildNeighbors() pEdges[a].push_back(edge); pEdges[b].push_back(edge); // append face - m_faces[i].m_edges[j] = edge; + m_facesEdges[i][j] = edge; } } delete [] v; delete [] edges; - // build vertex-face structure - m_verticesFaces.clear(); // to delete old pointers - m_verticesFaces.resize(numVertices()); - m_verticesEdges.clear(); // to delete old pointers - m_verticesEdges.resize(numVertices()); - for(unsigned int i=0; i < numVertices(); i++) - { - m_verticesFaces[i].m_numFaces = (unsigned int) vFaces[i].size(); - m_verticesFaces[i].m_fIndices = new unsigned int[m_verticesFaces[i].m_numFaces]; - memcpy(m_verticesFaces[i].m_fIndices, vFaces[i].data(), sizeof(unsigned int)*m_verticesFaces[i].m_numFaces); - - m_verticesEdges[i].m_numEdges = (unsigned int) vEdges[i].size(); - m_verticesEdges[i].m_eIndices = new unsigned int[m_verticesEdges[i].m_numEdges]; - memcpy(m_verticesEdges[i].m_eIndices, vEdges[i].data(), sizeof(unsigned int)*m_verticesEdges[i].m_numEdges); - } - // check for boundary m_closed = true; for (unsigned int i = 0; i < (unsigned int)m_edges.size(); i++) @@ -239,8 +222,6 @@ void IndexedFaceMesh::buildNeighbors() } delete [] pEdges; - delete [] vFaces; - delete [] vEdges; } void IndexedFaceMesh::copyUVs(const UVIndices& uvIndices, const UVs& uvs) diff --git a/Utils/IndexedFaceMesh.h b/Utils/IndexedFaceMesh.h index 84805c93..3d35c3e3 100644 --- a/Utils/IndexedFaceMesh.h +++ b/Utils/IndexedFaceMesh.h @@ -2,6 +2,7 @@ #define __INDEXEDFACEMESH_H__ #include +#include #include "Common/Common.h" #include @@ -12,95 +13,18 @@ namespace Utilities public: struct Edge { - unsigned int m_face[2]; - unsigned int m_vert[2]; - }; - - struct Face - { - unsigned int *m_edges; - }; - - // Stores the indices of each face connected to a specific vertex - struct VertexFaces - { - VertexFaces() - { - m_fIndices = 0; - m_numFaces = 0; - } - - VertexFaces(VertexFaces const& other) - { - *this = other; - } - - VertexFaces& operator=(VertexFaces const& other) - { - m_numFaces = other.m_numFaces; - m_fIndices = new unsigned int[m_numFaces]; -#if defined(_MSC_VER) - std::copy(other.m_fIndices, other.m_fIndices + m_numFaces, - stdext::unchecked_array_iterator(m_fIndices)); -#else - std::copy(other.m_fIndices, other.m_fIndices + m_numFaces, m_fIndices); -#endif - return *this; - } - - ~VertexFaces() - { - delete[] m_fIndices; - } - - unsigned int m_numFaces; - unsigned int* m_fIndices; - }; - - // Stores the indices of each edge connected to a specific vertex - struct VertexEdges - { - VertexEdges() - { - m_eIndices = 0; - m_numEdges = 0; - } - - VertexEdges(VertexEdges const& other) - { - *this = other; - } - - VertexEdges& operator=(VertexEdges const& other) - { - m_numEdges = other.m_numEdges; - m_eIndices = new unsigned int[m_numEdges]; -#if defined(_MSC_VER) - std::copy(other.m_eIndices, other.m_eIndices + m_numEdges, - stdext::unchecked_array_iterator(m_eIndices)); -#else - std::copy(other.m_eIndices, other.m_eIndices + m_numEdges, m_eIndices); -#endif - return *this; - } - - ~VertexEdges() - { - delete[] m_eIndices; - } - - unsigned int m_numEdges; - unsigned int* m_eIndices; + std::array m_face; + std::array m_vert; }; public: typedef std::vector Faces; typedef std::vector FaceNormals; typedef std::vector VertexNormals; - typedef std::vector FaceData; + typedef std::vector> FacesEdges; typedef std::vector Edges; - typedef std::vector VerticesFaces; - typedef std::vector VerticesEdges; + typedef std::vector> VerticesEdges; + typedef std::vector> VerticesFaces; typedef std::vector UVIndices; typedef std::vector UVs; @@ -108,19 +32,19 @@ namespace Utilities unsigned int m_numPoints; Faces m_indices; Edges m_edges; - FaceData m_faces; + FacesEdges m_facesEdges; bool m_closed; UVIndices m_uvIndices; UVs m_uvs; VerticesFaces m_verticesFaces; VerticesEdges m_verticesEdges; - unsigned int m_verticesPerFace; + const unsigned int m_verticesPerFace = 3u; FaceNormals m_normals; VertexNormals m_vertexNormals; bool m_flatShading; public: - IndexedFaceMesh(const unsigned int verticesPerFace = 3); + IndexedFaceMesh(); IndexedFaceMesh(IndexedFaceMesh const& other); IndexedFaceMesh& operator=(IndexedFaceMesh const& other); ~IndexedFaceMesh(); @@ -143,7 +67,7 @@ namespace Utilities VertexNormals& getVertexNormals(){ return m_vertexNormals; } Edges& getEdges() { return m_edges; } const Edges& getEdges() const { return m_edges; } - const FaceData& getFaceData() const { return m_faces; } + const FacesEdges& getFacesEdges() const { return m_facesEdges; } const UVIndices& getUVIndices() const { return m_uvIndices; } const UVs& getUVs() const { return m_uvs; } const VerticesFaces& getVertexFaces() const { return m_verticesFaces; } diff --git a/Utils/IndexedTetMesh.cpp b/Utils/IndexedTetMesh.cpp index 36ec9bfa..e80ed9dd 100644 --- a/Utils/IndexedTetMesh.cpp +++ b/Utils/IndexedTetMesh.cpp @@ -55,12 +55,15 @@ void IndexedTetMesh::addTet(const int * const indices) void IndexedTetMesh::buildNeighbors() { typedef std::vector VertexEdges; - typedef std::vector VertexFaces; - typedef std::vector VertexTets; - VertexEdges* vEdges = new VertexEdges[numVertices()]; - VertexFaces* vFaces = new VertexFaces[numVertices()]; - VertexTets* vTets = new VertexTets[numVertices()]; + m_verticesEdges.clear(); // to delete old pointers + m_verticesEdges.resize(numVertices()); + + m_verticesFaces.clear(); // to delete old pointers + m_verticesFaces.resize(numVertices()); + + m_verticesTets.clear(); // to delete old pointers + m_verticesTets.resize(numVertices()); m_faces.clear(); m_edges.clear(); @@ -92,7 +95,7 @@ void IndexedTetMesh::buildNeighbors() { // add vertex-tet connection const unsigned int vIndex = m_tetIndices[4*i+j]; - vTets[vIndex].push_back(i); + m_verticesTets[vIndex].push_back(i); } for(unsigned int j=0u; j < 4; j++) @@ -103,15 +106,15 @@ void IndexedTetMesh::buildNeighbors() const unsigned int c = faces[j*3+2]; unsigned int face = 0xffffffff; // find face - for(unsigned int k=0; k < vFaces[a].size(); k++) + for(unsigned int k=0; k < m_verticesFaces[a].size(); k++) { // Check if we already have this face in the list - const unsigned int& faceIndex = vFaces[a][k]; + const unsigned int& faceIndex = m_verticesFaces[a][k]; if(((m_faceIndices[3*faceIndex] == a) || (m_faceIndices[3*faceIndex] == b) || (m_faceIndices[3*faceIndex] == c)) && ((m_faceIndices[3*faceIndex+1] == a) || (m_faceIndices[3*faceIndex+1] == b) || (m_faceIndices[3*faceIndex+1] == c)) && ((m_faceIndices[3*faceIndex+2] == a) || (m_faceIndices[3*faceIndex+2] == b) || (m_faceIndices[3*faceIndex+2] == c))) { - face = vFaces[a][k]; + face = m_verticesFaces[a][k]; break; } } @@ -128,9 +131,9 @@ void IndexedTetMesh::buildNeighbors() m_faces.push_back(f); // add vertex-face connection - vFaces[a].push_back(face); - vFaces[b].push_back(face); - vFaces[c].push_back(face); + m_verticesFaces[a].push_back(face); + m_verticesFaces[b].push_back(face); + m_verticesFaces[c].push_back(face); } else { @@ -148,14 +151,14 @@ void IndexedTetMesh::buildNeighbors() const unsigned int b = edges[j*2+1]; unsigned int edge = 0xffffffff; // find edge - for(unsigned int k=0; k < vEdges[a].size(); k++) + for(unsigned int k=0; k < m_verticesEdges[a].size(); k++) { // Check if we already have this edge in the list - const Edge& e = m_edges[vEdges[a][k]]; + const Edge& e = m_edges[m_verticesEdges[a][k]]; if(((e.m_vert[0] == a) || (e.m_vert[0] == b)) && ((e.m_vert[1] == a) || (e.m_vert[1] == b))) { - edge = vEdges[a][k]; + edge = m_verticesEdges[a][k]; break; } } @@ -169,37 +172,11 @@ void IndexedTetMesh::buildNeighbors() edge = (unsigned int) m_edges.size() - 1u; // add vertex-edge connection - vEdges[a].push_back(edge); - vEdges[b].push_back(edge); + m_verticesEdges[a].push_back(edge); + m_verticesEdges[b].push_back(edge); } // append edge m_tets[i].m_edges[j] = edge; } } - - m_verticesEdges.clear(); // to delete old pointers - m_verticesEdges.resize(numVertices()); - m_verticesFaces.clear(); // to delete old pointers - m_verticesFaces.resize(numVertices()); - m_verticesTets.clear(); // to delete old pointers - m_verticesTets.resize(numVertices()); - for(unsigned int i=0; i < numVertices(); i++) - { - m_verticesEdges[i].m_numEdges = (unsigned int) vEdges[i].size(); - m_verticesEdges[i].m_eIndices = new unsigned int[m_verticesEdges[i].m_numEdges]; - memcpy(m_verticesEdges[i].m_eIndices, vEdges[i].data(), sizeof(unsigned int)*m_verticesEdges[i].m_numEdges); - - m_verticesFaces[i].m_numFaces = (unsigned int) vFaces[i].size(); - m_verticesFaces[i].m_fIndices = new unsigned int[m_verticesFaces[i].m_numFaces]; - memcpy(m_verticesFaces[i].m_fIndices, vFaces[i].data(), sizeof(unsigned int)*m_verticesFaces[i].m_numFaces); - - m_verticesTets[i].m_numTets = (unsigned int) vTets[i].size(); - m_verticesTets[i].m_tIndices = new unsigned int[m_verticesTets[i].m_numTets]; - memcpy(m_verticesTets[i].m_tIndices, vTets[i].data(), sizeof(unsigned int)*m_verticesTets[i].m_numTets); - } - - delete [] vEdges; - delete [] vFaces; - delete [] vTets; - } diff --git a/Utils/IndexedTetMesh.h b/Utils/IndexedTetMesh.h index 5c6596f0..d6706949 100644 --- a/Utils/IndexedTetMesh.h +++ b/Utils/IndexedTetMesh.h @@ -2,6 +2,7 @@ #define __INDEXEDTETMESH_H__ #include +#include #include "Common/Common.h" namespace Utilities @@ -11,75 +12,21 @@ namespace Utilities public: struct Edge { - unsigned int m_vert[2]; + std::array m_vert; }; struct Face { // edge indices - unsigned int m_edges[3]; + std::array m_edges; // tet indices - unsigned int m_tets[2]; + std::array m_tets; }; struct Tet { - unsigned int m_edges[6]; - unsigned int m_faces[4]; - }; - - // Stores the indices of each tet connected to a specific vertex - struct VertexTets - { - VertexTets() - { - m_tIndices = 0; - m_numTets = 0; - } - - ~VertexTets() - { - delete [] m_tIndices; - } - - unsigned int m_numTets; - unsigned int* m_tIndices; - }; - - // Stores the indices of each face connected to a specific vertex - struct VertexFaces - { - VertexFaces() - { - m_fIndices = 0; - m_numFaces = 0; - } - - ~VertexFaces() - { - delete [] m_fIndices; - } - - unsigned int m_numFaces; - unsigned int* m_fIndices; - }; - - // Stores the indices of each edge connected to a specific vertex - struct VertexEdges - { - VertexEdges() - { - m_eIndices = 0; - m_numEdges = 0; - } - - ~VertexEdges() - { - delete [] m_eIndices; - } - - unsigned int m_numEdges; - unsigned int* m_eIndices; + std::array m_edges; + std::array m_faces; }; public: @@ -88,9 +35,9 @@ namespace Utilities typedef std::vector TetData; typedef std::vector FaceData; typedef std::vector Edges; - typedef std::vector VerticesTets; - typedef std::vector VerticesFaces; - typedef std::vector VerticesEdges; + typedef std::vector> VerticesTets; + typedef std::vector> VerticesFaces; + typedef std::vector> VerticesEdges; protected: unsigned int m_numPoints; diff --git a/Utils/OBJLoader.h b/Utils/OBJLoader.h index 92886d4d..73cabddc 100644 --- a/Utils/OBJLoader.h +++ b/Utils/OBJLoader.h @@ -12,9 +12,9 @@ namespace Utilities */ struct MeshFaceIndices { - int posIndices[3]; - int texIndices[3]; - int normalIndices[3]; + std::array posIndices; + std::array texIndices; + std::array normalIndices; }; /** \brief Read for OBJ files. diff --git a/Utils/SceneLoader.cpp b/Utils/SceneLoader.cpp index 6e1290be..ff8c6078 100644 --- a/Utils/SceneLoader.cpp +++ b/Utils/SceneLoader.cpp @@ -203,6 +203,9 @@ void SceneLoader::readSimulation(const nlohmann::json &j, const std::string &key // normalize readValue(child, "solid_normalizeStretch", sceneData.m_solid_normalizeStretch); readValue(child, "solid_normalizeShear", sceneData.m_solid_normalizeShear); + + // volume stiffness + readValue(child, "volume_stiffness", sceneData.m_volume_stiffness); } void SceneLoader::readRigidBodies(const nlohmann::json &j, const std::string &key, const std::string &basePath, SceneData &sceneData) diff --git a/Utils/SceneLoader.h b/Utils/SceneLoader.h index 1b5e85bd..f662296a 100644 --- a/Utils/SceneLoader.h +++ b/Utils/SceneLoader.h @@ -213,6 +213,8 @@ namespace Utilities bool m_solid_normalizeStretch; bool m_solid_normalizeShear; + bool m_volume_stiffness; + std::vector m_rigidBodyData; std::vector m_triangleModelData; std::vector m_tetModelData; diff --git a/Utils/Version.h.in b/Utils/Version.h.in index 12e9eca9..7c3a4db6 100644 --- a/Utils/Version.h.in +++ b/Utils/Version.h.in @@ -9,6 +9,8 @@ #define GIT_REFSPEC "@GIT_REFSPEC@" #define GIT_LOCAL_STATUS "@GIT_LOCAL_CHANGES@" +#define PBD_VERSION "@PBD_VERSION@" + #ifdef DL_OUTPUT @COMPILER_MESSAGE@ #endif diff --git a/doc/bibliography.bib b/doc/bibliography.bib index b32fb7a8..15317b53 100644 --- a/doc/bibliography.bib +++ b/doc/bibliography.bib @@ -155,4 +155,21 @@ @article{Akinci:2012 publisher = {ACM}, address = {New York, NY, USA}, keywords = {fluid simulation, fluid-solid coupling, physically-based animation, smoothed particle hydrodynamics}, -} \ No newline at end of file +} + +@InProceedings{MMC16, + author = {Macklin, Miles and M\"{u}ller, Matthias and Chentanez, Nuttapong}, + title = {XPBD: Position-based Simulation of Compliant Constrained Dynamics}, + booktitle = MIG, + year = {2016}, + series = {MIG '16}, + pages = {49--54}, + publisher = {ACM}, + acmid = {2994272}, + doi = {10.1145/2994258.2994272}, + isbn = {978-1-4503-4592-7}, + keywords = {constrained dynamics, physics simulation, position based dynamics}, + location = {Burlingame, California}, + numpages = {6}, + url = {http://doi.acm.org/10.1145/2994258.2994272}, +} \ No newline at end of file diff --git a/extern/pybind/CMakeLists.txt b/extern/pybind/CMakeLists.txt new file mode 100644 index 00000000..2c08ff0b --- /dev/null +++ b/extern/pybind/CMakeLists.txt @@ -0,0 +1,270 @@ +# CMakeLists.txt -- Build system for the pybind11 modules +# +# Copyright (c) 2015 Wenzel Jakob +# +# All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +cmake_minimum_required(VERSION 3.4) + +# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with +# some versions of VS that have a patched CMake 3.11. This forces us to emulate +# the behavior using the following workaround: +if(${CMAKE_VERSION} VERSION_LESS 3.18) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +else() + cmake_policy(VERSION 3.18) +endif() + +# Extract project version from source +file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/pybind11/detail/common.h" + pybind11_version_defines REGEX "#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) ") + +foreach(ver ${pybind11_version_defines}) + if(ver MATCHES [[#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$]]) + set(PYBIND11_VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}") + endif() +endforeach() + +if(PYBIND11_VERSION_PATCH MATCHES [[\.([a-zA-Z0-9]+)$]]) + set(pybind11_VERSION_TYPE "${CMAKE_MATCH_1}") +endif() +string(REGEX MATCH "^[0-9]+" PYBIND11_VERSION_PATCH "${PYBIND11_VERSION_PATCH}") + +project( + pybind11 + LANGUAGES CXX + VERSION "${PYBIND11_VERSION_MAJOR}.${PYBIND11_VERSION_MINOR}.${PYBIND11_VERSION_PATCH}") + +# Standard includes +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) +include(CMakeDependentOption) + +if(NOT pybind11_FIND_QUIETLY) + message(STATUS "pybind11 v${pybind11_VERSION} ${pybind11_VERSION_TYPE}") +endif() + +# Check if pybind11 is being used directly or via add_subdirectory +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + ### Warn if not an out-of-source builds + if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) + set(lines + "You are building in-place. If that is not what you intended to " + "do, you can clean the source directory with:\n" + "rm -r CMakeCache.txt CMakeFiles/ cmake_uninstall.cmake pybind11Config.cmake " + "pybind11ConfigVersion.cmake tests/CMakeFiles/\n") + message(AUTHOR_WARNING ${lines}) + endif() + + set(PYBIND11_MASTER_PROJECT ON) + + if(OSX AND CMAKE_VERSION VERSION_LESS 3.7) + # Bug in macOS CMake < 3.7 is unable to download catch + message(WARNING "CMAKE 3.7+ needed on macOS to download catch, and newer HIGHLY recommended") + elseif(WINDOWS AND CMAKE_VERSION VERSION_LESS 3.8) + # Only tested with 3.8+ in CI. + message(WARNING "CMAKE 3.8+ tested on Windows, previous versions untested") + endif() + + message(STATUS "CMake ${CMAKE_VERSION}") + + if(CMAKE_CXX_STANDARD) + set(CMAKE_CXX_EXTENSIONS OFF) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + endif() +else() + set(PYBIND11_MASTER_PROJECT OFF) + set(pybind11_system SYSTEM) +endif() + +# Options +option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT}) +option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT}) +option(PYBIND11_NOPYTHON "Disable search for Python" OFF) + +cmake_dependent_option( + USE_PYTHON_INCLUDE_DIR + "Install pybind11 headers in Python include directory instead of default installation prefix" + OFF "PYBIND11_INSTALL" OFF) + +cmake_dependent_option(PYBIND11_FINDPYTHON "Force new FindPython" OFF + "NOT CMAKE_VERSION VERSION_LESS 3.12" OFF) + +# NB: when adding a header don't forget to also add it to setup.py +set(PYBIND11_HEADERS + include/pybind11/detail/class.h + include/pybind11/detail/common.h + include/pybind11/detail/descr.h + include/pybind11/detail/init.h + include/pybind11/detail/internals.h + include/pybind11/detail/typeid.h + include/pybind11/attr.h + include/pybind11/buffer_info.h + include/pybind11/cast.h + include/pybind11/chrono.h + include/pybind11/common.h + include/pybind11/complex.h + include/pybind11/options.h + include/pybind11/eigen.h + include/pybind11/embed.h + include/pybind11/eval.h + include/pybind11/iostream.h + include/pybind11/functional.h + include/pybind11/numpy.h + include/pybind11/operators.h + include/pybind11/pybind11.h + include/pybind11/pytypes.h + include/pybind11/stl.h + include/pybind11/stl_bind.h) + +# Compare with grep and warn if mismatched +if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12) + file( + GLOB_RECURSE _pybind11_header_check + LIST_DIRECTORIES false + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + CONFIGURE_DEPENDS "include/pybind11/*.h") + set(_pybind11_here_only ${PYBIND11_HEADERS}) + set(_pybind11_disk_only ${_pybind11_header_check}) + list(REMOVE_ITEM _pybind11_here_only ${_pybind11_header_check}) + list(REMOVE_ITEM _pybind11_disk_only ${PYBIND11_HEADERS}) + if(_pybind11_here_only) + message(AUTHOR_WARNING "PYBIND11_HEADERS has extra files:" ${_pybind11_here_only}) + endif() + if(_pybind11_disk_only) + message(AUTHOR_WARNING "PYBIND11_HEADERS is missing files:" ${_pybind11_disk_only}) + endif() +endif() + +# CMake 3.12 added list(TRANSFORM PREPEND +# But we can't use it yet +string(REPLACE "include/" "${CMAKE_CURRENT_SOURCE_DIR}/include/" PYBIND11_HEADERS + "${PYBIND11_HEADERS}") + +# Cache variable so this can be used in parent projects +set(pybind11_INCLUDE_DIR + "${CMAKE_CURRENT_LIST_DIR}/include" + CACHE INTERNAL "Directory where pybind11 headers are located") + +# Backward compatible variable for add_subdirectory mode +if(NOT PYBIND11_MASTER_PROJECT) + set(PYBIND11_INCLUDE_DIR + "${pybind11_INCLUDE_DIR}" + CACHE INTERNAL "") +endif() + +# Note: when creating targets, you cannot use if statements at configure time - +# you need generator expressions, because those will be placed in the target file. +# You can also place ifs *in* the Config.in, but not here. + +# This section builds targets, but does *not* touch Python + +# Build the headers-only target (no Python included): +# (long name used here to keep this from clashing in subdirectory mode) +add_library(pybind11_headers INTERFACE) +add_library(pybind11::pybind11_headers ALIAS pybind11_headers) # to match exported target +add_library(pybind11::headers ALIAS pybind11_headers) # easier to use/remember + +include("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11Common.cmake") + +# Relative directory setting +if(USE_PYTHON_INCLUDE_DIR AND DEFINED Python_INCLUDE_DIRS) + file(RELATIVE_PATH CMAKE_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX} ${Python_INCLUDE_DIRS}) +elseif(USE_PYTHON_INCLUDE_DIR AND DEFINED PYTHON_INCLUDE_DIR) + file(RELATIVE_PATH CMAKE_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX} ${PYTHON_INCLUDE_DIRS}) +endif() + +# Fill in headers target +target_include_directories( + pybind11_headers ${pybind11_system} INTERFACE $ + $) + +target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals + cxx_right_angle_brackets) + +if(PYBIND11_INSTALL) + install(DIRECTORY ${pybind11_INCLUDE_DIR}/pybind11 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + set(PYBIND11_CMAKECONFIG_INSTALL_DIR + "${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME}" + CACHE STRING "install path for pybind11Config.cmake") + + configure_package_config_file( + tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + + if(CMAKE_VERSION VERSION_LESS 3.14) + # Remove CMAKE_SIZEOF_VOID_P from ConfigVersion.cmake since the library does + # not depend on architecture specific settings or libraries. + set(_PYBIND11_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) + unset(CMAKE_SIZEOF_VOID_P) + + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion) + + set(CMAKE_SIZEOF_VOID_P ${_PYBIND11_CMAKE_SIZEOF_VOID_P}) + else() + # CMake 3.14+ natively supports header-only libraries + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion ARCH_INDEPENDENT) + endif() + + install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + tools/FindPythonLibsNew.cmake + tools/pybind11Common.cmake + tools/pybind11Tools.cmake + tools/pybind11NewTools.cmake + DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + + if(NOT PYBIND11_EXPORT_NAME) + set(PYBIND11_EXPORT_NAME "${PROJECT_NAME}Targets") + endif() + + install(TARGETS pybind11_headers EXPORT "${PYBIND11_EXPORT_NAME}") + + install( + EXPORT "${PYBIND11_EXPORT_NAME}" + NAMESPACE "pybind11::" + DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + + # Uninstall target + if(PYBIND11_MASTER_PROJECT) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) + + add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P + ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) + endif() +endif() + +# BUILD_TESTING takes priority, but only if this is the master project +if(PYBIND11_MASTER_PROJECT AND DEFINED BUILD_TESTING) + if(BUILD_TESTING) + if(_pybind11_nopython) + message(FATAL_ERROR "Cannot activate tests in NOPYTHON mode") + else() + add_subdirectory(tests) + endif() + endif() +else() + if(PYBIND11_TEST) + if(_pybind11_nopython) + message(FATAL_ERROR "Cannot activate tests in NOPYTHON mode") + else() + add_subdirectory(tests) + endif() + endif() +endif() + +# Better symmetry with find_package(pybind11 CONFIG) mode. +if(NOT PYBIND11_MASTER_PROJECT) + set(pybind11_FOUND + TRUE + CACHE INTERNAL "True if pybind11 and all required components found on the system") +endif() diff --git a/extern/pybind/LICENSE b/extern/pybind/LICENSE new file mode 100644 index 00000000..e466b0df --- /dev/null +++ b/extern/pybind/LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2016 Wenzel Jakob , All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Please also refer to the file .github/CONTRIBUTING.md, which clarifies licensing of +external contributions to this project including patches, pull requests, etc. diff --git a/extern/pybind/MANIFEST.in b/extern/pybind/MANIFEST.in new file mode 100644 index 00000000..aed183e8 --- /dev/null +++ b/extern/pybind/MANIFEST.in @@ -0,0 +1,6 @@ +recursive-include pybind11/include/pybind11 *.h +recursive-include pybind11 *.py +recursive-include pybind11 py.typed +recursive-include pybind11 *.pyi +include pybind11/share/cmake/pybind11/*.cmake +include LICENSE README.rst pyproject.toml setup.py setup.cfg diff --git a/extern/pybind/README.rst b/extern/pybind/README.rst new file mode 100644 index 00000000..1474cb95 --- /dev/null +++ b/extern/pybind/README.rst @@ -0,0 +1,175 @@ +.. figure:: https://github.com/pybind/pybind11/raw/master/docs/pybind11-logo.png + :alt: pybind11 logo + +**pybind11 — Seamless operability between C++11 and Python** + +|Latest Documentation Status| |Stable Documentation Status| |Gitter chat| |CI| |Build status| + +.. warning:: + + Combining older versions of pybind11 (< 2.6.0) with the brand-new Python + 3.9.0 will trigger undefined behavior that typically manifests as crashes + during interpreter shutdown (but could also destroy your data. **You have been + warned.**) + + We recommend that you wait for Python 3.9.1 slated for release in December, + which will include a `fix `_ + that resolves this problem. In the meantime, please update to the latest + version of pybind11 (2.6.0 or newer), which includes a temporary workaround + specifically when Python 3.9.0 is detected at runtime. + +**pybind11** is a lightweight header-only library that exposes C++ types +in Python and vice versa, mainly to create Python bindings of existing +C++ code. Its goals and syntax are similar to the excellent +`Boost.Python `_ +library by David Abrahams: to minimize boilerplate code in traditional +extension modules by inferring type information using compile-time +introspection. + +The main issue with Boost.Python—and the reason for creating such a +similar project—is Boost. Boost is an enormously large and complex suite +of utility libraries that works with almost every C++ compiler in +existence. This compatibility has its cost: arcane template tricks and +workarounds are necessary to support the oldest and buggiest of compiler +specimens. Now that C++11-compatible compilers are widely available, +this heavy machinery has become an excessively large and unnecessary +dependency. + +Think of this library as a tiny self-contained version of Boost.Python +with everything stripped away that isn’t relevant for binding +generation. Without comments, the core header files only require ~4K +lines of code and depend on Python (2.7 or 3.5+, or PyPy) and the C++ +standard library. This compact implementation was possible thanks to +some of the new C++11 language features (specifically: tuples, lambda +functions and variadic templates). Since its creation, this library has +grown beyond Boost.Python in many ways, leading to dramatically simpler +binding code in many common situations. + +Tutorial and reference documentation is provided at +`pybind11.readthedocs.io `_. +A PDF version of the manual is available +`here `_. +And the source code is always available at +`github.com/pybind/pybind11 `_. + + +Core features +------------- + + +pybind11 can map the following core C++ features to Python: + +- Functions accepting and returning custom data structures per value, + reference, or pointer +- Instance methods and static methods +- Overloaded functions +- Instance attributes and static attributes +- Arbitrary exception types +- Enumerations +- Callbacks +- Iterators and ranges +- Custom operators +- Single and multiple inheritance +- STL data structures +- Smart pointers with reference counting like ``std::shared_ptr`` +- Internal references with correct reference counting +- C++ classes with virtual (and pure virtual) methods can be extended + in Python + +Goodies +------- + +In addition to the core functionality, pybind11 provides some extra +goodies: + +- Python 2.7, 3.5+, and PyPy/PyPy3 7.3 are supported with an + implementation-agnostic interface. + +- It is possible to bind C++11 lambda functions with captured + variables. The lambda capture data is stored inside the resulting + Python function object. + +- pybind11 uses C++11 move constructors and move assignment operators + whenever possible to efficiently transfer custom data types. + +- It’s easy to expose the internal storage of custom data types through + Pythons’ buffer protocols. This is handy e.g. for fast conversion + between C++ matrix classes like Eigen and NumPy without expensive + copy operations. + +- pybind11 can automatically vectorize functions so that they are + transparently applied to all entries of one or more NumPy array + arguments. + +- Python’s slice-based access and assignment operations can be + supported with just a few lines of code. + +- Everything is contained in just a few header files; there is no need + to link against any additional libraries. + +- Binaries are generally smaller by a factor of at least 2 compared to + equivalent bindings generated by Boost.Python. A recent pybind11 + conversion of PyRosetta, an enormous Boost.Python binding project, + `reported `_ + a binary size reduction of **5.4x** and compile time reduction by + **5.8x**. + +- Function signatures are precomputed at compile time (using + ``constexpr``), leading to smaller binaries. + +- With little extra effort, C++ types can be pickled and unpickled + similar to regular Python objects. + +Supported compilers +------------------- + +1. Clang/LLVM 3.3 or newer (for Apple Xcode’s clang, this is 5.0.0 or + newer) +2. GCC 4.8 or newer +3. Microsoft Visual Studio 2015 Update 3 or newer +4. Intel C++ compiler 18 or newer + (`possible issue `_ on 20.2) +5. Cygwin/GCC (tested on 2.5.1) +6. NVCC (CUDA 11.0 tested) +7. NVIDIA PGI (20.7 and 20.9 tested) + +About +----- + +This project was created by `Wenzel +Jakob `_. Significant features and/or +improvements to the code were contributed by Jonas Adler, Lori A. Burns, +Sylvain Corlay, Eric Cousineau, Ralf Grosse-Kunstleve, Trent Houliston, Axel +Huebl, @hulucc, Yannick Jadoul, Sergey Lyskov Johan Mabille, Tomasz MiÄ…sko, +Dean Moldovan, Ben Pritchard, Jason Rhinelander, Boris Schäling, Pim +Schellart, Henry Schreiner, Ivan Smirnov, Boris Staletic, and Patrick Stewart. + +We thank Google for a generous financial contribution to the continuous +integration infrastructure used by this project. + + +Contributing +~~~~~~~~~~~~ + +See the `contributing +guide `_ +for information on building and contributing to pybind11. + +License +~~~~~~~ + +pybind11 is provided under a BSD-style license that can be found in the +`LICENSE `_ +file. By using, distributing, or contributing to this project, you agree +to the terms and conditions of this license. + +.. |Latest Documentation Status| image:: https://readthedocs.org/projects/pybind11/badge?version=latest + :target: http://pybind11.readthedocs.org/en/latest +.. |Stable Documentation Status| image:: https://img.shields.io/badge/docs-stable-blue + :target: http://pybind11.readthedocs.org/en/stable +.. |Gitter chat| image:: https://img.shields.io/gitter/room/gitterHQ/gitter.svg + :target: https://gitter.im/pybind/Lobby +.. |CI| image:: https://github.com/pybind/pybind11/workflows/CI/badge.svg + :target: https://github.com/pybind/pybind11/actions +.. |Build status| image:: https://ci.appveyor.com/api/projects/status/riaj54pn4h08xy40?svg=true + :target: https://ci.appveyor.com/project/wjakob/pybind11 diff --git a/extern/pybind/include/pybind11/attr.h b/extern/pybind/include/pybind11/attr.h new file mode 100644 index 00000000..0c416709 --- /dev/null +++ b/extern/pybind/include/pybind11/attr.h @@ -0,0 +1,551 @@ +/* + pybind11/attr.h: Infrastructure for processing custom + type and function attributes + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "cast.h" + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/// \addtogroup annotations +/// @{ + +/// Annotation for methods +struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; + +/// Annotation for operators +struct is_operator { }; + +/// Annotation for classes that cannot be subclassed +struct is_final { }; + +/// Annotation for parent scope +struct scope { handle value; scope(const handle &s) : value(s) { } }; + +/// Annotation for documentation +struct doc { const char *value; doc(const char *value) : value(value) { } }; + +/// Annotation for function names +struct name { const char *value; name(const char *value) : value(value) { } }; + +/// Annotation indicating that a function is an overload associated with a given "sibling" +struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } }; + +/// Annotation indicating that a class derives from another given type +template struct base { + + PYBIND11_DEPRECATED("base() was deprecated in favor of specifying 'T' as a template argument to class_") + base() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute +}; + +/// Keep patient alive while nurse lives +template struct keep_alive { }; + +/// Annotation indicating that a class is involved in a multiple inheritance relationship +struct multiple_inheritance { }; + +/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class +struct dynamic_attr { }; + +/// Annotation which enables the buffer protocol for a type +struct buffer_protocol { }; + +/// Annotation which requests that a special metaclass is created for a type +struct metaclass { + handle value; + + PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") + metaclass() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute + + /// Override pybind11's default metaclass + explicit metaclass(handle value) : value(value) { } +}; + +/// Annotation that marks a class as local to the module: +struct module_local { const bool value; constexpr module_local(bool v = true) : value(v) { } }; + +/// Annotation to mark enums as an arithmetic type +struct arithmetic { }; + +/// Mark a function for addition at the beginning of the existing overload chain instead of the end +struct prepend { }; + +/** \rst + A call policy which places one or more guard variables (``Ts...``) around the function call. + + For example, this definition: + + .. code-block:: cpp + + m.def("foo", foo, py::call_guard()); + + is equivalent to the following pseudocode: + + .. code-block:: cpp + + m.def("foo", [](args...) { + T scope_guard; + return foo(args...); // forwarded arguments + }); + \endrst */ +template struct call_guard; + +template <> struct call_guard<> { using type = detail::void_type; }; + +template +struct call_guard { + static_assert(std::is_default_constructible::value, + "The guard type must be default constructible"); + + using type = T; +}; + +template +struct call_guard { + struct type { + T guard{}; // Compose multiple guard types with left-to-right default-constructor order + typename call_guard::type next{}; + }; +}; + +/// @} annotations + +PYBIND11_NAMESPACE_BEGIN(detail) +/* Forward declarations */ +enum op_id : int; +enum op_type : int; +struct undefined_t; +template struct op_; +inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); + +/// Internal data structure which holds metadata about a keyword argument +struct argument_record { + const char *name; ///< Argument name + const char *descr; ///< Human-readable version of the argument value + handle value; ///< Associated Python object + bool convert : 1; ///< True if the argument is allowed to convert when loading + bool none : 1; ///< True if None is allowed when loading + + argument_record(const char *name, const char *descr, handle value, bool convert, bool none) + : name(name), descr(descr), value(value), convert(convert), none(none) { } +}; + +/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) +struct function_record { + function_record() + : is_constructor(false), is_new_style_constructor(false), is_stateless(false), + is_operator(false), is_method(false), has_args(false), + has_kwargs(false), has_kw_only_args(false), prepend(false) { } + + /// Function name + char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ + + // User-specified documentation string + char *doc = nullptr; + + /// Human-readable version of the function signature + char *signature = nullptr; + + /// List of registered keyword arguments + std::vector args; + + /// Pointer to lambda function which converts arguments and performs the actual call + handle (*impl) (function_call &) = nullptr; + + /// Storage for the wrapped function pointer and captured data, if any + void *data[3] = { }; + + /// Pointer to custom destructor for 'data' (if needed) + void (*free_data) (function_record *ptr) = nullptr; + + /// Return value policy associated with this function + return_value_policy policy = return_value_policy::automatic; + + /// True if name == '__init__' + bool is_constructor : 1; + + /// True if this is a new-style `__init__` defined in `detail/init.h` + bool is_new_style_constructor : 1; + + /// True if this is a stateless function pointer + bool is_stateless : 1; + + /// True if this is an operator (__add__), etc. + bool is_operator : 1; + + /// True if this is a method + bool is_method : 1; + + /// True if the function has a '*args' argument + bool has_args : 1; + + /// True if the function has a '**kwargs' argument + bool has_kwargs : 1; + + /// True once a 'py::kw_only' is encountered (any following args are keyword-only) + bool has_kw_only_args : 1; + + /// True if this function is to be inserted at the beginning of the overload resolution chain + bool prepend : 1; + + /// Number of arguments (including py::args and/or py::kwargs, if present) + std::uint16_t nargs; + + /// Number of trailing arguments (counted in `nargs`) that are keyword-only + std::uint16_t nargs_kw_only = 0; + + /// Number of leading arguments (counted in `nargs`) that are positional-only + std::uint16_t nargs_pos_only = 0; + + /// Python method object + PyMethodDef *def = nullptr; + + /// Python handle to the parent scope (a class or a module) + handle scope; + + /// Python handle to the sibling function representing an overload chain + handle sibling; + + /// Pointer to next overload + function_record *next = nullptr; +}; + +/// Special data structure which (temporarily) holds metadata about a bound class +struct type_record { + PYBIND11_NOINLINE type_record() + : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), + default_holder(true), module_local(false), is_final(false) { } + + /// Handle to the parent scope + handle scope; + + /// Name of the class + const char *name = nullptr; + + // Pointer to RTTI type_info data structure + const std::type_info *type = nullptr; + + /// How large is the underlying C++ type? + size_t type_size = 0; + + /// What is the alignment of the underlying C++ type? + size_t type_align = 0; + + /// How large is the type's holder? + size_t holder_size = 0; + + /// The global operator new can be overridden with a class-specific variant + void *(*operator_new)(size_t) = nullptr; + + /// Function pointer to class_<..>::init_instance + void (*init_instance)(instance *, const void *) = nullptr; + + /// Function pointer to class_<..>::dealloc + void (*dealloc)(detail::value_and_holder &) = nullptr; + + /// List of base classes of the newly created type + list bases; + + /// Optional docstring + const char *doc = nullptr; + + /// Custom metaclass (optional) + handle metaclass; + + /// Multiple inheritance marker + bool multiple_inheritance : 1; + + /// Does the class manage a __dict__? + bool dynamic_attr : 1; + + /// Does the class implement the buffer protocol? + bool buffer_protocol : 1; + + /// Is the default (unique_ptr) holder type used? + bool default_holder : 1; + + /// Is the class definition local to the module shared object? + bool module_local : 1; + + /// Is the class inheritable from python classes? + bool is_final : 1; + + PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { + auto base_info = detail::get_type_info(base, false); + if (!base_info) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + + "\" referenced unknown base type \"" + tname + "\""); + } + + if (default_holder != base_info->default_holder) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + + (default_holder ? "does not have" : "has") + + " a non-default holder type while its base \"" + tname + "\" " + + (base_info->default_holder ? "does not" : "does")); + } + + bases.append((PyObject *) base_info->type); + + if (base_info->type->tp_dictoffset != 0) + dynamic_attr = true; + + if (caster) + base_info->implicit_casts.emplace_back(type, caster); + } +}; + +inline function_call::function_call(const function_record &f, handle p) : + func(f), parent(p) { + args.reserve(f.nargs); + args_convert.reserve(f.nargs); +} + +/// Tag for a new-style `__init__` defined in `detail/init.h` +struct is_new_style_constructor { }; + +/** + * Partial template specializations to process custom attributes provided to + * cpp_function_ and class_. These are either used to initialize the respective + * fields in the type_record and function_record data structures or executed at + * runtime to deal with custom call policies (e.g. keep_alive). + */ +template struct process_attribute; + +template struct process_attribute_default { + /// Default implementation: do nothing + static void init(const T &, function_record *) { } + static void init(const T &, type_record *) { } + static void precall(function_call &) { } + static void postcall(function_call &, handle) { } +}; + +/// Process an attribute specifying the function's name +template <> struct process_attribute : process_attribute_default { + static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring +template <> struct process_attribute : process_attribute_default { + static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring (provided as a C-style string) +template <> struct process_attribute : process_attribute_default { + static void init(const char *d, function_record *r) { r->doc = const_cast(d); } + static void init(const char *d, type_record *r) { r->doc = const_cast(d); } +}; +template <> struct process_attribute : process_attribute { }; + +/// Process an attribute indicating the function's return value policy +template <> struct process_attribute : process_attribute_default { + static void init(const return_value_policy &p, function_record *r) { r->policy = p; } +}; + +/// Process an attribute which indicates that this is an overloaded function associated with a given sibling +template <> struct process_attribute : process_attribute_default { + static void init(const sibling &s, function_record *r) { r->sibling = s.value; } +}; + +/// Process an attribute which indicates that this function is a method +template <> struct process_attribute : process_attribute_default { + static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } +}; + +/// Process an attribute which indicates the parent scope of a method +template <> struct process_attribute : process_attribute_default { + static void init(const scope &s, function_record *r) { r->scope = s.value; } +}; + +/// Process an attribute which indicates that this function is an operator +template <> struct process_attribute : process_attribute_default { + static void init(const is_operator &, function_record *r) { r->is_operator = true; } +}; + +template <> struct process_attribute : process_attribute_default { + static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } +}; + +inline void process_kw_only_arg(const arg &a, function_record *r) { + if (!a.name || strlen(a.name) == 0) + pybind11_fail("arg(): cannot specify an unnamed argument after an kw_only() annotation"); + ++r->nargs_kw_only; +} + +/// Process a keyword argument attribute (*without* a default value) +template <> struct process_attribute : process_attribute_default { + static void init(const arg &a, function_record *r) { + if (r->is_method && r->args.empty()) + r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); + r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); + + if (r->has_kw_only_args) process_kw_only_arg(a, r); + } +}; + +/// Process a keyword argument attribute (*with* a default value) +template <> struct process_attribute : process_attribute_default { + static void init(const arg_v &a, function_record *r) { + if (r->is_method && r->args.empty()) + r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/); + + if (!a.value) { +#if !defined(NDEBUG) + std::string descr("'"); + if (a.name) descr += std::string(a.name) + ": "; + descr += a.type + "'"; + if (r->is_method) { + if (r->name) + descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; + else + descr += " in method of '" + (std::string) str(r->scope) + "'"; + } else if (r->name) { + descr += " in function '" + (std::string) r->name + "'"; + } + pybind11_fail("arg(): could not convert default argument " + + descr + " into a Python object (type not registered yet?)"); +#else + pybind11_fail("arg(): could not convert default argument " + "into a Python object (type not registered yet?). " + "Compile in debug mode for more information."); +#endif + } + r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); + + if (r->has_kw_only_args) process_kw_only_arg(a, r); + } +}; + +/// Process a keyword-only-arguments-follow pseudo argument +template <> struct process_attribute : process_attribute_default { + static void init(const kw_only &, function_record *r) { + r->has_kw_only_args = true; + } +}; + +/// Process a positional-only-argument maker +template <> struct process_attribute : process_attribute_default { + static void init(const pos_only &, function_record *r) { + r->nargs_pos_only = static_cast(r->args.size()); + } +}; + +/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that) +template +struct process_attribute::value>> : process_attribute_default { + static void init(const handle &h, type_record *r) { r->bases.append(h); } +}; + +/// Process a parent class attribute (deprecated, does not support multiple inheritance) +template +struct process_attribute> : process_attribute_default> { + static void init(const base &, type_record *r) { r->add_base(typeid(T), nullptr); } +}; + +/// Process a multiple inheritance attribute +template <> +struct process_attribute : process_attribute_default { + static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const is_final &, type_record *r) { r->is_final = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const module_local &l, type_record *r) { r->module_local = l.value; } +}; + +/// Process a 'prepend' attribute, putting this at the beginning of the overload chain +template <> +struct process_attribute : process_attribute_default { + static void init(const prepend &, function_record *r) { r->prepend = true; } +}; + +/// Process an 'arithmetic' attribute for enums (does nothing here) +template <> +struct process_attribute : process_attribute_default {}; + +template +struct process_attribute> : process_attribute_default> { }; + +/** + * Process a keep_alive call policy -- invokes keep_alive_impl during the + * pre-call handler if both Nurse, Patient != 0 and use the post-call handler + * otherwise + */ +template struct process_attribute> : public process_attribute_default> { + template = 0> + static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } + template = 0> + static void postcall(function_call &, handle) { } + template = 0> + static void precall(function_call &) { } + template = 0> + static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } +}; + +/// Recursively iterate over variadic template arguments +template struct process_attributes { + static void init(const Args&... args, function_record *r) { + int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; + ignore_unused(unused); + } + static void init(const Args&... args, type_record *r) { + int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; + ignore_unused(unused); + } + static void precall(function_call &call) { + int unused[] = { 0, (process_attribute::type>::precall(call), 0) ... }; + ignore_unused(unused); + } + static void postcall(function_call &call, handle fn_ret) { + int unused[] = { 0, (process_attribute::type>::postcall(call, fn_ret), 0) ... }; + ignore_unused(unused); + } +}; + +template +using is_call_guard = is_instantiation; + +/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found) +template +using extract_guard_t = typename exactly_one_t, Extra...>::type; + +/// Check the number of named arguments at compile time +template ::value...), + size_t self = constexpr_sum(std::is_same::value...)> +constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { + return named == 0 || (self + named + has_args + has_kwargs) == nargs; +} + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/buffer_info.h b/extern/pybind/include/pybind11/buffer_info.h new file mode 100644 index 00000000..d803004a --- /dev/null +++ b/extern/pybind/include/pybind11/buffer_info.h @@ -0,0 +1,146 @@ +/* + pybind11/buffer_info.h: Python buffer object interface + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +PYBIND11_NAMESPACE_BEGIN(detail) + +// Default, C-style strides +inline std::vector c_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + if (ndim > 0) + for (size_t i = ndim - 1; i > 0; --i) + strides[i - 1] = strides[i] * shape[i]; + return strides; +} + +// F-style strides; default when constructing an array_t with `ExtraFlags & f_style` +inline std::vector f_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + for (size_t i = 1; i < ndim; ++i) + strides[i] = strides[i - 1] * shape[i - 1]; + return strides; +} + +PYBIND11_NAMESPACE_END(detail) + +/// Information record describing a Python buffer object +struct buffer_info { + void *ptr = nullptr; // Pointer to the underlying storage + ssize_t itemsize = 0; // Size of individual items in bytes + ssize_t size = 0; // Total number of entries + std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() + ssize_t ndim = 0; // Number of dimensions + std::vector shape; // Shape of the tensor (1 entry per dimension) + std::vector strides; // Number of bytes between adjacent entries (for each per dimension) + bool readonly = false; // flag to indicate if the underlying storage may be written to + + buffer_info() = default; + + buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, + detail::any_container shape_in, detail::any_container strides_in, bool readonly=false) + : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), + shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { + if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) + pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); + for (size_t i = 0; i < (size_t) ndim; ++i) + size *= shape[i]; + } + + template + buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in, bool readonly=false) + : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(shape_in->size()), std::move(shape_in), std::move(strides_in), readonly) { } + + buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size, bool readonly=false) + : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) { } + + template + buffer_info(T *ptr, ssize_t size, bool readonly=false) + : buffer_info(ptr, sizeof(T), format_descriptor::format(), size, readonly) { } + + template + buffer_info(const T *ptr, ssize_t size, bool readonly=true) + : buffer_info(const_cast(ptr), sizeof(T), format_descriptor::format(), size, readonly) { } + + explicit buffer_info(Py_buffer *view, bool ownview = true) + : buffer_info(view->buf, view->itemsize, view->format, view->ndim, + {view->shape, view->shape + view->ndim}, + /* Though buffer::request() requests PyBUF_STRIDES, ctypes objects + * ignore this flag and return a view with NULL strides. + * When strides are NULL, build them manually. */ + view->strides + ? std::vector(view->strides, view->strides + view->ndim) + : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), + view->readonly) { + this->m_view = view; + this->ownview = ownview; + } + + buffer_info(const buffer_info &) = delete; + buffer_info& operator=(const buffer_info &) = delete; + + buffer_info(buffer_info &&other) { + (*this) = std::move(other); + } + + buffer_info& operator=(buffer_info &&rhs) { + ptr = rhs.ptr; + itemsize = rhs.itemsize; + size = rhs.size; + format = std::move(rhs.format); + ndim = rhs.ndim; + shape = std::move(rhs.shape); + strides = std::move(rhs.strides); + std::swap(m_view, rhs.m_view); + std::swap(ownview, rhs.ownview); + readonly = rhs.readonly; + return *this; + } + + ~buffer_info() { + if (m_view && ownview) { PyBuffer_Release(m_view); delete m_view; } + } + + Py_buffer *view() const { return m_view; } + Py_buffer *&view() { return m_view; } +private: + struct private_ctr_tag { }; + + buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, + detail::any_container &&shape_in, detail::any_container &&strides_in, bool readonly) + : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) { } + + Py_buffer *m_view = nullptr; + bool ownview = false; +}; + +PYBIND11_NAMESPACE_BEGIN(detail) + +template struct compare_buffer_info { + static bool compare(const buffer_info& b) { + return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); + } +}; + +template struct compare_buffer_info::value>> { + static bool compare(const buffer_info& b) { + return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor::value || + ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || + ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); + } +}; + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/cast.h b/extern/pybind/include/pybind11/cast.h new file mode 100644 index 00000000..d6e440f7 --- /dev/null +++ b/extern/pybind/include/pybind11/cast.h @@ -0,0 +1,2219 @@ +/* + pybind11/cast.h: Partial template specializations to cast between + C++ and Python types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pytypes.h" +#include "detail/typeid.h" +#include "detail/descr.h" +#include "detail/internals.h" +#include +#include +#include +#include + +#if defined(PYBIND11_CPP17) +# if defined(__has_include) +# if __has_include() +# define PYBIND11_HAS_STRING_VIEW +# endif +# elif defined(_MSC_VER) +# define PYBIND11_HAS_STRING_VIEW +# endif +#endif +#ifdef PYBIND11_HAS_STRING_VIEW +#include +#endif + +#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L +# define PYBIND11_HAS_U8STRING +#endif + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +/// A life support system for temporary objects created by `type_caster::load()`. +/// Adding a patient will keep it alive up until the enclosing function returns. +class loader_life_support { +public: + /// A new patient frame is created when a function is entered + loader_life_support() { + get_internals().loader_patient_stack.push_back(nullptr); + } + + /// ... and destroyed after it returns + ~loader_life_support() { + auto &stack = get_internals().loader_patient_stack; + if (stack.empty()) + pybind11_fail("loader_life_support: internal error"); + + auto ptr = stack.back(); + stack.pop_back(); + Py_CLEAR(ptr); + + // A heuristic to reduce the stack's capacity (e.g. after long recursive calls) + if (stack.capacity() > 16 && !stack.empty() && stack.capacity() / stack.size() > 2) + stack.shrink_to_fit(); + } + + /// This can only be used inside a pybind11-bound function, either by `argument_loader` + /// at argument preparation time or by `py::cast()` at execution time. + PYBIND11_NOINLINE static void add_patient(handle h) { + auto &stack = get_internals().loader_patient_stack; + if (stack.empty()) + throw cast_error("When called outside a bound function, py::cast() cannot " + "do Python -> C++ conversions which require the creation " + "of temporary values"); + + auto &list_ptr = stack.back(); + if (list_ptr == nullptr) { + list_ptr = PyList_New(1); + if (!list_ptr) + pybind11_fail("loader_life_support: error allocating list"); + PyList_SET_ITEM(list_ptr, 0, h.inc_ref().ptr()); + } else { + auto result = PyList_Append(list_ptr, h.ptr()); + if (result == -1) + pybind11_fail("loader_life_support: error adding patient"); + } + } +}; + +// Gets the cache entry for the given type, creating it if necessary. The return value is the pair +// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was +// just created. +inline std::pair all_type_info_get_cache(PyTypeObject *type); + +// Populates a just-created cache entry. +PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vector &bases) { + std::vector check; + for (handle parent : reinterpret_borrow(t->tp_bases)) + check.push_back((PyTypeObject *) parent.ptr()); + + auto const &type_dict = get_internals().registered_types_py; + for (size_t i = 0; i < check.size(); i++) { + auto type = check[i]; + // Ignore Python2 old-style class super type: + if (!PyType_Check((PyObject *) type)) continue; + + // Check `type` in the current set of registered python types: + auto it = type_dict.find(type); + if (it != type_dict.end()) { + // We found a cache entry for it, so it's either pybind-registered or has pre-computed + // pybind bases, but we have to make sure we haven't already seen the type(s) before: we + // want to follow Python/virtual C++ rules that there should only be one instance of a + // common base. + for (auto *tinfo : it->second) { + // NB: Could use a second set here, rather than doing a linear search, but since + // having a large number of immediate pybind11-registered types seems fairly + // unlikely, that probably isn't worthwhile. + bool found = false; + for (auto *known : bases) { + if (known == tinfo) { found = true; break; } + } + if (!found) bases.push_back(tinfo); + } + } + else if (type->tp_bases) { + // It's some python type, so keep follow its bases classes to look for one or more + // registered types + if (i + 1 == check.size()) { + // When we're at the end, we can pop off the current element to avoid growing + // `check` when adding just one base (which is typical--i.e. when there is no + // multiple inheritance) + check.pop_back(); + i--; + } + for (handle parent : reinterpret_borrow(type->tp_bases)) + check.push_back((PyTypeObject *) parent.ptr()); + } + } +} + +/** + * Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will + * be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side + * derived class that uses single inheritance. Will contain as many types as required for a Python + * class that uses multiple inheritance to inherit (directly or indirectly) from multiple + * pybind-registered classes. Will be empty if neither the type nor any base classes are + * pybind-registered. + * + * The value is cached for the lifetime of the Python type. + */ +inline const std::vector &all_type_info(PyTypeObject *type) { + auto ins = all_type_info_get_cache(type); + if (ins.second) + // New cache entry: populate it + all_type_info_populate(type, ins.first->second); + + return ins.first->second; +} + +/** + * Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any + * ancestors are pybind11-registered. Throws an exception if there are multiple bases--use + * `all_type_info` instead if you want to support multiple bases. + */ +PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) { + auto &bases = all_type_info(type); + if (bases.empty()) + return nullptr; + if (bases.size() > 1) + pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases"); + return bases.front(); +} + +inline detail::type_info *get_local_type_info(const std::type_index &tp) { + auto &locals = registered_local_types_cpp(); + auto it = locals.find(tp); + if (it != locals.end()) + return it->second; + return nullptr; +} + +inline detail::type_info *get_global_type_info(const std::type_index &tp) { + auto &types = get_internals().registered_types_cpp; + auto it = types.find(tp); + if (it != types.end()) + return it->second; + return nullptr; +} + +/// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr. +PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_index &tp, + bool throw_if_missing = false) { + if (auto ltype = get_local_type_info(tp)) + return ltype; + if (auto gtype = get_global_type_info(tp)) + return gtype; + + if (throw_if_missing) { + std::string tname = tp.name(); + detail::clean_type_id(tname); + pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\""); + } + return nullptr; +} + +PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool throw_if_missing) { + detail::type_info *type_info = get_type_info(tp, throw_if_missing); + return handle(type_info ? ((PyObject *) type_info->type) : nullptr); +} + +struct value_and_holder { + instance *inst = nullptr; + size_t index = 0u; + const detail::type_info *type = nullptr; + void **vh = nullptr; + + // Main constructor for a found value/holder: + value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) : + inst{i}, index{index}, type{type}, + vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]} + {} + + // Default constructor (used to signal a value-and-holder not found by get_value_and_holder()) + value_and_holder() = default; + + // Used for past-the-end iterator + value_and_holder(size_t index) : index{index} {} + + template V *&value_ptr() const { + return reinterpret_cast(vh[0]); + } + // True if this `value_and_holder` has a non-null value pointer + explicit operator bool() const { return value_ptr(); } + + template H &holder() const { + return reinterpret_cast(vh[1]); + } + bool holder_constructed() const { + return inst->simple_layout + ? inst->simple_holder_constructed + : inst->nonsimple.status[index] & instance::status_holder_constructed; + } + void set_holder_constructed(bool v = true) { + if (inst->simple_layout) + inst->simple_holder_constructed = v; + else if (v) + inst->nonsimple.status[index] |= instance::status_holder_constructed; + else + inst->nonsimple.status[index] &= (uint8_t) ~instance::status_holder_constructed; + } + bool instance_registered() const { + return inst->simple_layout + ? inst->simple_instance_registered + : inst->nonsimple.status[index] & instance::status_instance_registered; + } + void set_instance_registered(bool v = true) { + if (inst->simple_layout) + inst->simple_instance_registered = v; + else if (v) + inst->nonsimple.status[index] |= instance::status_instance_registered; + else + inst->nonsimple.status[index] &= (uint8_t) ~instance::status_instance_registered; + } +}; + +// Container for accessing and iterating over an instance's values/holders +struct values_and_holders { +private: + instance *inst; + using type_vec = std::vector; + const type_vec &tinfo; + +public: + values_and_holders(instance *inst) : inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {} + + struct iterator { + private: + instance *inst = nullptr; + const type_vec *types = nullptr; + value_and_holder curr; + friend struct values_and_holders; + iterator(instance *inst, const type_vec *tinfo) + : inst{inst}, types{tinfo}, + curr(inst /* instance */, + types->empty() ? nullptr : (*types)[0] /* type info */, + 0, /* vpos: (non-simple types only): the first vptr comes first */ + 0 /* index */) + {} + // Past-the-end iterator: + iterator(size_t end) : curr(end) {} + public: + bool operator==(const iterator &other) const { return curr.index == other.curr.index; } + bool operator!=(const iterator &other) const { return curr.index != other.curr.index; } + iterator &operator++() { + if (!inst->simple_layout) + curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs; + ++curr.index; + curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr; + return *this; + } + value_and_holder &operator*() { return curr; } + value_and_holder *operator->() { return &curr; } + }; + + iterator begin() { return iterator(inst, &tinfo); } + iterator end() { return iterator(tinfo.size()); } + + iterator find(const type_info *find_type) { + auto it = begin(), endit = end(); + while (it != endit && it->type != find_type) ++it; + return it; + } + + size_t size() { return tinfo.size(); } +}; + +/** + * Extracts C++ value and holder pointer references from an instance (which may contain multiple + * values/holders for python-side multiple inheritance) that match the given type. Throws an error + * if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If + * `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned, + * regardless of type (and the resulting .type will be nullptr). + * + * The returned object should be short-lived: in particular, it must not outlive the called-upon + * instance. + */ +PYBIND11_NOINLINE inline value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, bool throw_if_missing /*= true in common.h*/) { + // Optimize common case: + if (!find_type || Py_TYPE(this) == find_type->type) + return value_and_holder(this, find_type, 0, 0); + + detail::values_and_holders vhs(this); + auto it = vhs.find(find_type); + if (it != vhs.end()) + return *it; + + if (!throw_if_missing) + return value_and_holder(); + +#if defined(NDEBUG) + pybind11_fail("pybind11::detail::instance::get_value_and_holder: " + "type is not a pybind11 base of the given instance " + "(compile in debug mode for type details)"); +#else + pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" + + get_fully_qualified_tp_name(find_type->type) + "' is not a pybind11 base of the given `" + + get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance"); +#endif +} + +PYBIND11_NOINLINE inline void instance::allocate_layout() { + auto &tinfo = all_type_info(Py_TYPE(this)); + + const size_t n_types = tinfo.size(); + + if (n_types == 0) + pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types"); + + simple_layout = + n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); + + // Simple path: no python-side multiple inheritance, and a small-enough holder + if (simple_layout) { + simple_value_holder[0] = nullptr; + simple_holder_constructed = false; + simple_instance_registered = false; + } + else { // multiple base types or a too-large holder + // Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer, + // [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool + // values that tracks whether each associated holder has been initialized. Each [block] is + // padded, if necessary, to an integer multiple of sizeof(void *). + size_t space = 0; + for (auto t : tinfo) { + space += 1; // value pointer + space += t->holder_size_in_ptrs; // holder instance + } + size_t flags_at = space; + space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered) + + // Allocate space for flags, values, and holders, and initialize it to 0 (flags and values, + // in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6 + // they default to using pymalloc, which is designed to be efficient for small allocations + // like the one we're doing here; in earlier versions (and for larger allocations) they are + // just wrappers around malloc. +#if PY_VERSION_HEX >= 0x03050000 + nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); + if (!nonsimple.values_and_holders) throw std::bad_alloc(); +#else + nonsimple.values_and_holders = (void **) PyMem_New(void *, space); + if (!nonsimple.values_and_holders) throw std::bad_alloc(); + std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *)); +#endif + nonsimple.status = reinterpret_cast(&nonsimple.values_and_holders[flags_at]); + } + owned = true; +} + +PYBIND11_NOINLINE inline void instance::deallocate_layout() { + if (!simple_layout) + PyMem_Free(nonsimple.values_and_holders); +} + +PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) { + handle type = detail::get_type_handle(tp, false); + if (!type) + return false; + return isinstance(obj, type); +} + +PYBIND11_NOINLINE inline std::string error_string() { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred"); + return "Unknown internal error occurred"; + } + + error_scope scope; // Preserve error state + + std::string errorString; + if (scope.type) { + errorString += handle(scope.type).attr("__name__").cast(); + errorString += ": "; + } + if (scope.value) + errorString += (std::string) str(scope.value); + + PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); + +#if PY_MAJOR_VERSION >= 3 + if (scope.trace != nullptr) + PyException_SetTraceback(scope.value, scope.trace); +#endif + +#if !defined(PYPY_VERSION) + if (scope.trace) { + auto *trace = (PyTracebackObject *) scope.trace; + + /* Get the deepest trace possible */ + while (trace->tb_next) + trace = trace->tb_next; + + PyFrameObject *frame = trace->tb_frame; + errorString += "\n\nAt:\n"; + while (frame) { + int lineno = PyFrame_GetLineNumber(frame); + errorString += + " " + handle(frame->f_code->co_filename).cast() + + "(" + std::to_string(lineno) + "): " + + handle(frame->f_code->co_name).cast() + "\n"; + frame = frame->f_back; + } + } +#endif + + return errorString; +} + +PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail::type_info *type ) { + auto &instances = get_internals().registered_instances; + auto range = instances.equal_range(ptr); + for (auto it = range.first; it != range.second; ++it) { + for (const auto &vh : values_and_holders(it->second)) { + if (vh.type == type) + return handle((PyObject *) it->second); + } + } + return handle(); +} + +inline PyThreadState *get_thread_state_unchecked() { +#if defined(PYPY_VERSION) + return PyThreadState_GET(); +#elif PY_VERSION_HEX < 0x03000000 + return _PyThreadState_Current; +#elif PY_VERSION_HEX < 0x03050000 + return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current); +#elif PY_VERSION_HEX < 0x03050200 + return (PyThreadState*) _PyThreadState_Current.value; +#else + return _PyThreadState_UncheckedGet(); +#endif +} + +// Forward declarations +inline void keep_alive_impl(handle nurse, handle patient); +inline PyObject *make_new_instance(PyTypeObject *type); + +class type_caster_generic { +public: + PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info) + : typeinfo(get_type_info(type_info)), cpptype(&type_info) { } + + type_caster_generic(const type_info *typeinfo) + : typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) { } + + bool load(handle src, bool convert) { + return load_impl(src, convert); + } + + PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent, + const detail::type_info *tinfo, + void *(*copy_constructor)(const void *), + void *(*move_constructor)(const void *), + const void *existing_holder = nullptr) { + if (!tinfo) // no type info: error will be set already + return handle(); + + void *src = const_cast(_src); + if (src == nullptr) + return none().release(); + + auto it_instances = get_internals().registered_instances.equal_range(src); + for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) { + for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) { + if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) + return handle((PyObject *) it_i->second).inc_ref(); + } + } + + auto inst = reinterpret_steal(make_new_instance(tinfo->type)); + auto wrapper = reinterpret_cast(inst.ptr()); + wrapper->owned = false; + void *&valueptr = values_and_holders(wrapper).begin()->value_ptr(); + + switch (policy) { + case return_value_policy::automatic: + case return_value_policy::take_ownership: + valueptr = src; + wrapper->owned = true; + break; + + case return_value_policy::automatic_reference: + case return_value_policy::reference: + valueptr = src; + wrapper->owned = false; + break; + + case return_value_policy::copy: + if (copy_constructor) + valueptr = copy_constructor(src); + else { +#if defined(NDEBUG) + throw cast_error("return_value_policy = copy, but type is " + "non-copyable! (compile in debug mode for details)"); +#else + std::string type_name(tinfo->cpptype->name()); + detail::clean_type_id(type_name); + throw cast_error("return_value_policy = copy, but type " + + type_name + " is non-copyable!"); +#endif + } + wrapper->owned = true; + break; + + case return_value_policy::move: + if (move_constructor) + valueptr = move_constructor(src); + else if (copy_constructor) + valueptr = copy_constructor(src); + else { +#if defined(NDEBUG) + throw cast_error("return_value_policy = move, but type is neither " + "movable nor copyable! " + "(compile in debug mode for details)"); +#else + std::string type_name(tinfo->cpptype->name()); + detail::clean_type_id(type_name); + throw cast_error("return_value_policy = move, but type " + + type_name + " is neither movable nor copyable!"); +#endif + } + wrapper->owned = true; + break; + + case return_value_policy::reference_internal: + valueptr = src; + wrapper->owned = false; + keep_alive_impl(inst, parent); + break; + + default: + throw cast_error("unhandled return_value_policy: should not happen!"); + } + + tinfo->init_instance(wrapper, existing_holder); + + return inst.release(); + } + + // Base methods for generic caster; there are overridden in copyable_holder_caster + void load_value(value_and_holder &&v_h) { + auto *&vptr = v_h.value_ptr(); + // Lazy allocation for unallocated values: + if (vptr == nullptr) { + auto *type = v_h.type ? v_h.type : typeinfo; + if (type->operator_new) { + vptr = type->operator_new(type->type_size); + } else { + #if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912) + if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + vptr = ::operator new(type->type_size, + std::align_val_t(type->type_align)); + else + #endif + vptr = ::operator new(type->type_size); + } + } + value = vptr; + } + bool try_implicit_casts(handle src, bool convert) { + for (auto &cast : typeinfo->implicit_casts) { + type_caster_generic sub_caster(*cast.first); + if (sub_caster.load(src, convert)) { + value = cast.second(sub_caster.value); + return true; + } + } + return false; + } + bool try_direct_conversions(handle src) { + for (auto &converter : *typeinfo->direct_conversions) { + if (converter(src.ptr(), value)) + return true; + } + return false; + } + void check_holder_compat() {} + + PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) { + auto caster = type_caster_generic(ti); + if (caster.load(src, false)) + return caster.value; + return nullptr; + } + + /// Try to load with foreign typeinfo, if available. Used when there is no + /// native typeinfo, or when the native one wasn't able to produce a value. + PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) { + constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID; + const auto pytype = type::handle_of(src); + if (!hasattr(pytype, local_key)) + return false; + + type_info *foreign_typeinfo = reinterpret_borrow(getattr(pytype, local_key)); + // Only consider this foreign loader if actually foreign and is a loader of the correct cpp type + if (foreign_typeinfo->module_local_load == &local_load + || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) + return false; + + if (auto result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) { + value = result; + return true; + } + return false; + } + + // Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant + // bits of code between here and copyable_holder_caster where the two classes need different + // logic (without having to resort to virtual inheritance). + template + PYBIND11_NOINLINE bool load_impl(handle src, bool convert) { + if (!src) return false; + if (!typeinfo) return try_load_foreign_module_local(src); + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + value = nullptr; + return true; + } + + auto &this_ = static_cast(*this); + this_.check_holder_compat(); + + PyTypeObject *srctype = Py_TYPE(src.ptr()); + + // Case 1: If src is an exact type match for the target type then we can reinterpret_cast + // the instance's value pointer to the target type: + if (srctype == typeinfo->type) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); + return true; + } + // Case 2: We have a derived class + else if (PyType_IsSubtype(srctype, typeinfo->type)) { + auto &bases = all_type_info(srctype); + bool no_cpp_mi = typeinfo->simple_type; + + // Case 2a: the python type is a Python-inherited derived class that inherits from just + // one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of + // the right type and we can use reinterpret_cast. + // (This is essentially the same as case 2b, but because not using multiple inheritance + // is extremely common, we handle it specially to avoid the loop iterator and type + // pointer lookup overhead) + if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); + return true; + } + // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if + // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we + // can safely reinterpret_cast to the relevant pointer. + else if (bases.size() > 1) { + for (auto base : bases) { + if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder(base)); + return true; + } + } + } + + // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match + // in the registered bases, above, so try implicit casting (needed for proper C++ casting + // when MI is involved). + if (this_.try_implicit_casts(src, convert)) + return true; + } + + // Perform an implicit conversion + if (convert) { + for (auto &converter : typeinfo->implicit_conversions) { + auto temp = reinterpret_steal(converter(src.ptr(), typeinfo->type)); + if (load_impl(temp, false)) { + loader_life_support::add_patient(temp); + return true; + } + } + if (this_.try_direct_conversions(src)) + return true; + } + + // Failed to match local typeinfo. Try again with global. + if (typeinfo->module_local) { + if (auto gtype = get_global_type_info(*typeinfo->cpptype)) { + typeinfo = gtype; + return load(src, false); + } + } + + // Global typeinfo has precedence over foreign module_local + return try_load_foreign_module_local(src); + } + + + // Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast + // isn't needed or can't be used. If the type is unknown, sets the error and returns a pair + // with .second = nullptr. (p.first = nullptr is not an error: it becomes None). + PYBIND11_NOINLINE static std::pair src_and_type( + const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) { + if (auto *tpi = get_type_info(cast_type)) + return {src, const_cast(tpi)}; + + // Not found, set error: + std::string tname = rtti_type ? rtti_type->name() : cast_type.name(); + detail::clean_type_id(tname); + std::string msg = "Unregistered type : " + tname; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return {nullptr, nullptr}; + } + + const type_info *typeinfo = nullptr; + const std::type_info *cpptype = nullptr; + void *value = nullptr; +}; + +/** + * Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster + * needs to provide `operator T*()` and `operator T&()` operators. + * + * If the type supports moving the value away via an `operator T&&() &&` method, it should use + * `movable_cast_op_type` instead. + */ +template +using cast_op_type = + conditional_t>::value, + typename std::add_pointer>::type, + typename std::add_lvalue_reference>::type>; + +/** + * Determine suitable casting operator for a type caster with a movable value. Such a type caster + * needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be + * called in appropriate contexts where the value can be moved rather than copied. + * + * These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro. + */ +template +using movable_cast_op_type = + conditional_t::type>::value, + typename std::add_pointer>::type, + conditional_t::value, + typename std::add_rvalue_reference>::type, + typename std::add_lvalue_reference>::type>>; + +// std::is_copy_constructible isn't quite enough: it lets std::vector (and similar) through when +// T is non-copyable, but code containing such a copy constructor fails to actually compile. +template struct is_copy_constructible : std::is_copy_constructible {}; + +// Specialization for types that appear to be copy constructible but also look like stl containers +// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if +// so, copy constructability depends on whether the value_type is copy constructible. +template struct is_copy_constructible, + std::is_same, + // Avoid infinite recursion + negation> + >::value>> : is_copy_constructible {}; + +// Likewise for std::pair +// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't themselves +// copy constructible, but this can not be relied upon when T1 or T2 are themselves containers). +template struct is_copy_constructible> + : all_of, is_copy_constructible> {}; + +// The same problems arise with std::is_copy_assignable, so we use the same workaround. +template struct is_copy_assignable : std::is_copy_assignable {}; +template struct is_copy_assignable, + std::is_same + >::value>> : is_copy_assignable {}; +template struct is_copy_assignable> + : all_of, is_copy_assignable> {}; + +PYBIND11_NAMESPACE_END(detail) + +// polymorphic_type_hook::get(src, tinfo) determines whether the object pointed +// to by `src` actually is an instance of some class derived from `itype`. +// If so, it sets `tinfo` to point to the std::type_info representing that derived +// type, and returns a pointer to the start of the most-derived object of that type +// (in which `src` is a subobject; this will be the same address as `src` in most +// single inheritance cases). If not, or if `src` is nullptr, it simply returns `src` +// and leaves `tinfo` at its default value of nullptr. +// +// The default polymorphic_type_hook just returns src. A specialization for polymorphic +// types determines the runtime type of the passed object and adjusts the this-pointer +// appropriately via dynamic_cast. This is what enables a C++ Animal* to appear +// to Python as a Dog (if Dog inherits from Animal, Animal is polymorphic, Dog is +// registered with pybind11, and this Animal is in fact a Dog). +// +// You may specialize polymorphic_type_hook yourself for types that want to appear +// polymorphic to Python but do not use C++ RTTI. (This is a not uncommon pattern +// in performance-sensitive applications, used most notably in LLVM.) +// +// polymorphic_type_hook_base allows users to specialize polymorphic_type_hook with +// std::enable_if. User provided specializations will always have higher priority than +// the default implementation and specialization provided in polymorphic_type_hook_base. +template +struct polymorphic_type_hook_base +{ + static const void *get(const itype *src, const std::type_info*&) { return src; } +}; +template +struct polymorphic_type_hook_base::value>> +{ + static const void *get(const itype *src, const std::type_info*& type) { + type = src ? &typeid(*src) : nullptr; + return dynamic_cast(src); + } +}; +template +struct polymorphic_type_hook : public polymorphic_type_hook_base {}; + +PYBIND11_NAMESPACE_BEGIN(detail) + +/// Generic type caster for objects stored on the heap +template class type_caster_base : public type_caster_generic { + using itype = intrinsic_t; + +public: + static constexpr auto name = _(); + + type_caster_base() : type_caster_base(typeid(type)) { } + explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { } + + static handle cast(const itype &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast(&src, policy, parent); + } + + static handle cast(itype &&src, return_value_policy, handle parent) { + return cast(&src, return_value_policy::move, parent); + } + + // Returns a (pointer, type_info) pair taking care of necessary type lookup for a + // polymorphic type (using RTTI by default, but can be overridden by specializing + // polymorphic_type_hook). If the instance isn't derived, returns the base version. + static std::pair src_and_type(const itype *src) { + auto &cast_type = typeid(itype); + const std::type_info *instance_type = nullptr; + const void *vsrc = polymorphic_type_hook::get(src, instance_type); + if (instance_type && !same_type(cast_type, *instance_type)) { + // This is a base pointer to a derived type. If the derived type is registered + // with pybind11, we want to make the full derived object available. + // In the typical case where itype is polymorphic, we get the correct + // derived pointer (which may be != base pointer) by a dynamic_cast to + // most derived type. If itype is not polymorphic, we won't get here + // except via a user-provided specialization of polymorphic_type_hook, + // and the user has promised that no this-pointer adjustment is + // required in that case, so it's OK to use static_cast. + if (const auto *tpi = get_type_info(*instance_type)) + return {vsrc, tpi}; + } + // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so + // don't do a cast + return type_caster_generic::src_and_type(src, cast_type, instance_type); + } + + static handle cast(const itype *src, return_value_policy policy, handle parent) { + auto st = src_and_type(src); + return type_caster_generic::cast( + st.first, policy, parent, st.second, + make_copy_constructor(src), make_move_constructor(src)); + } + + static handle cast_holder(const itype *src, const void *holder) { + auto st = src_and_type(src); + return type_caster_generic::cast( + st.first, return_value_policy::take_ownership, {}, st.second, + nullptr, nullptr, holder); + } + + template using cast_op_type = detail::cast_op_type; + + operator itype*() { return (type *) value; } + operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); } + +protected: + using Constructor = void *(*)(const void *); + + /* Only enabled when the types are {copy,move}-constructible *and* when the type + does not have a private operator new implementation. */ + template ::value>> + static auto make_copy_constructor(const T *x) -> decltype(new T(*x), Constructor{}) { + return [](const void *arg) -> void * { + return new T(*reinterpret_cast(arg)); + }; + } + + template ::value>> + static auto make_move_constructor(const T *x) -> decltype(new T(std::move(*const_cast(x))), Constructor{}) { + return [](const void *arg) -> void * { + return new T(std::move(*const_cast(reinterpret_cast(arg)))); + }; + } + + static Constructor make_copy_constructor(...) { return nullptr; } + static Constructor make_move_constructor(...) { return nullptr; } +}; + +template class type_caster : public type_caster_base { }; +template using make_caster = type_caster>; + +// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T +template typename make_caster::template cast_op_type cast_op(make_caster &caster) { + return caster.operator typename make_caster::template cast_op_type(); +} +template typename make_caster::template cast_op_type::type> +cast_op(make_caster &&caster) { + return std::move(caster).operator + typename make_caster::template cast_op_type::type>(); +} + +template class type_caster> { +private: + using caster_t = make_caster; + caster_t subcaster; + using subcaster_cast_op_type = typename caster_t::template cast_op_type; + static_assert(std::is_same::type &, subcaster_cast_op_type>::value, + "std::reference_wrapper caster requires T to have a caster with an `T &` operator"); +public: + bool load(handle src, bool convert) { return subcaster.load(src, convert); } + static constexpr auto name = caster_t::name; + static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { + // It is definitely wrong to take ownership of this pointer, so mask that rvp + if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic) + policy = return_value_policy::automatic_reference; + return caster_t::cast(&src.get(), policy, parent); + } + template using cast_op_type = std::reference_wrapper; + operator std::reference_wrapper() { return subcaster.operator subcaster_cast_op_type&(); } +}; + +#define PYBIND11_TYPE_CASTER(type, py_name) \ + protected: \ + type value; \ + public: \ + static constexpr auto name = py_name; \ + template >::value, int> = 0> \ + static handle cast(T_ *src, return_value_policy policy, handle parent) { \ + if (!src) return none().release(); \ + if (policy == return_value_policy::take_ownership) { \ + auto h = cast(std::move(*src), policy, parent); delete src; return h; \ + } else { \ + return cast(*src, policy, parent); \ + } \ + } \ + operator type*() { return &value; } \ + operator type&() { return value; } \ + operator type&&() && { return std::move(value); } \ + template using cast_op_type = pybind11::detail::movable_cast_op_type + + +template using is_std_char_type = any_of< + std::is_same, /* std::string */ +#if defined(PYBIND11_HAS_U8STRING) + std::is_same, /* std::u8string */ +#endif + std::is_same, /* std::u16string */ + std::is_same, /* std::u32string */ + std::is_same /* std::wstring */ +>; + + +template +struct type_caster::value && !is_std_char_type::value>> { + using _py_type_0 = conditional_t; + using _py_type_1 = conditional_t::value, _py_type_0, typename std::make_unsigned<_py_type_0>::type>; + using py_type = conditional_t::value, double, _py_type_1>; +public: + + bool load(handle src, bool convert) { + py_type py_value; + + if (!src) + return false; + + if (std::is_floating_point::value) { + if (convert || PyFloat_Check(src.ptr())) + py_value = (py_type) PyFloat_AsDouble(src.ptr()); + else + return false; + } else if (PyFloat_Check(src.ptr())) { + return false; + } else if (std::is_unsigned::value) { + py_value = as_unsigned(src.ptr()); + } else { // signed integer: + py_value = sizeof(T) <= sizeof(long) + ? (py_type) PyLong_AsLong(src.ptr()) + : (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr()); + } + + // Python API reported an error + bool py_err = py_value == (py_type) -1 && PyErr_Occurred(); + + // Check to see if the conversion is valid (integers should match exactly) + // Signed/unsigned checks happen elsewhere + if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && py_value != (py_type) (T) py_value)) { + bool type_error = py_err && PyErr_ExceptionMatches( +#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION) + PyExc_SystemError +#else + PyExc_TypeError +#endif + ); + PyErr_Clear(); + if (type_error && convert && PyNumber_Check(src.ptr())) { + auto tmp = reinterpret_steal(std::is_floating_point::value + ? PyNumber_Float(src.ptr()) + : PyNumber_Long(src.ptr())); + PyErr_Clear(); + return load(tmp, false); + } + return false; + } + + value = (T) py_value; + return true; + } + + template + static typename std::enable_if::value, handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyFloat_FromDouble((double) src); + } + + template + static typename std::enable_if::value && std::is_signed::value && (sizeof(U) <= sizeof(long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_SIGNED((long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) <= sizeof(unsigned long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src); + } + + template + static typename std::enable_if::value && std::is_signed::value && (sizeof(U) > sizeof(long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromLongLong((long long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) > sizeof(unsigned long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromUnsignedLongLong((unsigned long long) src); + } + + PYBIND11_TYPE_CASTER(T, _::value>("int", "float")); +}; + +template struct void_caster { +public: + bool load(handle src, bool) { + if (src && src.is_none()) + return true; + return false; + } + static handle cast(T, return_value_policy /* policy */, handle /* parent */) { + return none().inc_ref(); + } + PYBIND11_TYPE_CASTER(T, _("None")); +}; + +template <> class type_caster : public void_caster {}; + +template <> class type_caster : public type_caster { +public: + using type_caster::cast; + + bool load(handle h, bool) { + if (!h) { + return false; + } else if (h.is_none()) { + value = nullptr; + return true; + } + + /* Check if this is a capsule */ + if (isinstance(h)) { + value = reinterpret_borrow(h); + return true; + } + + /* Check if this is a C++ type */ + auto &bases = all_type_info((PyTypeObject *) type::handle_of(h).ptr()); + if (bases.size() == 1) { // Only allowing loading from a single-value type + value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); + return true; + } + + /* Fail */ + return false; + } + + static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { + if (ptr) + return capsule(ptr).release(); + else + return none().inc_ref(); + } + + template using cast_op_type = void*&; + operator void *&() { return value; } + static constexpr auto name = _("capsule"); +private: + void *value = nullptr; +}; + +template <> class type_caster : public void_caster { }; + +template <> class type_caster { +public: + bool load(handle src, bool convert) { + if (!src) return false; + else if (src.ptr() == Py_True) { value = true; return true; } + else if (src.ptr() == Py_False) { value = false; return true; } + else if (convert || !strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) { + // (allow non-implicit conversion for numpy booleans) + + Py_ssize_t res = -1; + if (src.is_none()) { + res = 0; // None is implicitly converted to False + } + #if defined(PYPY_VERSION) + // On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists + else if (hasattr(src, PYBIND11_BOOL_ATTR)) { + res = PyObject_IsTrue(src.ptr()); + } + #else + // Alternate approach for CPython: this does the same as the above, but optimized + // using the CPython API so as to avoid an unneeded attribute lookup. + else if (auto tp_as_number = src.ptr()->ob_type->tp_as_number) { + if (PYBIND11_NB_BOOL(tp_as_number)) { + res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); + } + } + #endif + if (res == 0 || res == 1) { + value = (bool) res; + return true; + } else { + PyErr_Clear(); + } + } + return false; + } + static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { + return handle(src ? Py_True : Py_False).inc_ref(); + } + PYBIND11_TYPE_CASTER(bool, _("bool")); +}; + +// Helper class for UTF-{8,16,32} C++ stl strings: +template struct string_caster { + using CharT = typename StringType::value_type; + + // Simplify life by being able to assume standard char sizes (the standard only guarantees + // minimums, but Python requires exact sizes) + static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char size != 1"); +#if defined(PYBIND11_HAS_U8STRING) + static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char8_t size != 1"); +#endif + static_assert(!std::is_same::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2"); + static_assert(!std::is_same::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4"); + // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) + static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, + "Unsupported wchar_t size != 2/4"); + static constexpr size_t UTF_N = 8 * sizeof(CharT); + + bool load(handle src, bool) { +#if PY_MAJOR_VERSION < 3 + object temp; +#endif + handle load_src = src; + if (!src) { + return false; + } else if (!PyUnicode_Check(load_src.ptr())) { +#if PY_MAJOR_VERSION >= 3 + return load_bytes(load_src); +#else + if (std::is_same::value) { + return load_bytes(load_src); + } + + // The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false + if (!PYBIND11_BYTES_CHECK(load_src.ptr())) + return false; + + temp = reinterpret_steal(PyUnicode_FromObject(load_src.ptr())); + if (!temp) { PyErr_Clear(); return false; } + load_src = temp; +#endif + } + + object utfNbytes = reinterpret_steal(PyUnicode_AsEncodedString( + load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr)); + if (!utfNbytes) { PyErr_Clear(); return false; } + + const auto *buffer = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); + size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); + if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32 + value = StringType(buffer, length); + + // If we're loading a string_view we need to keep the encoded Python object alive: + if (IsView) + loader_life_support::add_patient(utfNbytes); + + return true; + } + + static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { + const char *buffer = reinterpret_cast(src.data()); + auto nbytes = ssize_t(src.size() * sizeof(CharT)); + handle s = decode_utfN(buffer, nbytes); + if (!s) throw error_already_set(); + return s; + } + + PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME)); + +private: + static handle decode_utfN(const char *buffer, ssize_t nbytes) { +#if !defined(PYPY_VERSION) + return + UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) : + UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) : + PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); +#else + // PyPy segfaults when on PyUnicode_DecodeUTF16 (and possibly on PyUnicode_DecodeUTF32 as well), + // so bypass the whole thing by just passing the encoding as a string value, which works properly: + return PyUnicode_Decode(buffer, nbytes, UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr); +#endif + } + + // When loading into a std::string or char*, accept a bytes object as-is (i.e. + // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. + // which supports loading a unicode from a str, doesn't take this path. + template + bool load_bytes(enable_if_t::value, handle> src) { + if (PYBIND11_BYTES_CHECK(src.ptr())) { + // We were passed a Python 3 raw bytes; accept it into a std::string or char* + // without any encoding attempt. + const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); + if (bytes) { + value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); + return true; + } + } + + return false; + } + + template + bool load_bytes(enable_if_t::value, handle>) { return false; } +}; + +template +struct type_caster, enable_if_t::value>> + : string_caster> {}; + +#ifdef PYBIND11_HAS_STRING_VIEW +template +struct type_caster, enable_if_t::value>> + : string_caster, true> {}; +#endif + +// Type caster for C-style strings. We basically use a std::string type caster, but also add the +// ability to use None as a nullptr char* (which the string caster doesn't allow). +template struct type_caster::value>> { + using StringType = std::basic_string; + using StringCaster = type_caster; + StringCaster str_caster; + bool none = false; + CharT one_char = 0; +public: + bool load(handle src, bool convert) { + if (!src) return false; + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + none = true; + return true; + } + return str_caster.load(src, convert); + } + + static handle cast(const CharT *src, return_value_policy policy, handle parent) { + if (src == nullptr) return pybind11::none().inc_ref(); + return StringCaster::cast(StringType(src), policy, parent); + } + + static handle cast(CharT src, return_value_policy policy, handle parent) { + if (std::is_same::value) { + handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); + if (!s) throw error_already_set(); + return s; + } + return StringCaster::cast(StringType(1, src), policy, parent); + } + + operator CharT*() { return none ? nullptr : const_cast(static_cast(str_caster).c_str()); } + operator CharT&() { + if (none) + throw value_error("Cannot convert None to a character"); + + auto &value = static_cast(str_caster); + size_t str_len = value.size(); + if (str_len == 0) + throw value_error("Cannot convert empty string to a character"); + + // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that + // is too high, and one for multiple unicode characters (caught later), so we need to figure + // out how long the first encoded character is in bytes to distinguish between these two + // errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those + // can fit into a single char value. + if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { + auto v0 = static_cast(value[0]); + size_t char0_bytes = !(v0 & 0x80) ? 1 : // low bits only: 0-127 + (v0 & 0xE0) == 0xC0 ? 2 : // 0b110xxxxx - start of 2-byte sequence + (v0 & 0xF0) == 0xE0 ? 3 : // 0b1110xxxx - start of 3-byte sequence + 4; // 0b11110xxx - start of 4-byte sequence + + if (char0_bytes == str_len) { + // If we have a 128-255 value, we can decode it into a single char: + if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx + one_char = static_cast(((v0 & 3) << 6) + (static_cast(value[1]) & 0x3F)); + return one_char; + } + // Otherwise we have a single character, but it's > U+00FF + throw value_error("Character code point not in range(0x100)"); + } + } + + // UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a + // surrogate pair with total length 2 instantly indicates a range error (but not a "your + // string was too long" error). + else if (StringCaster::UTF_N == 16 && str_len == 2) { + one_char = static_cast(value[0]); + if (one_char >= 0xD800 && one_char < 0xE000) + throw value_error("Character code point not in range(0x10000)"); + } + + if (str_len != 1) + throw value_error("Expected a character, but multi-character string found"); + + one_char = value[0]; + return one_char; + } + + static constexpr auto name = _(PYBIND11_STRING_NAME); + template using cast_op_type = pybind11::detail::cast_op_type<_T>; +}; + +// Base implementation for std::tuple and std::pair +template class Tuple, typename... Ts> class tuple_caster { + using type = Tuple; + static constexpr auto size = sizeof...(Ts); + using indices = make_index_sequence; +public: + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + const auto seq = reinterpret_borrow(src); + if (seq.size() != size) + return false; + return load_impl(seq, convert, indices{}); + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + return cast_impl(std::forward(src), policy, parent, indices{}); + } + + // copied from the PYBIND11_TYPE_CASTER macro + template + static handle cast(T *src, return_value_policy policy, handle parent) { + if (!src) return none().release(); + if (policy == return_value_policy::take_ownership) { + auto h = cast(std::move(*src), policy, parent); delete src; return h; + } else { + return cast(*src, policy, parent); + } + } + + static constexpr auto name = _("Tuple[") + concat(make_caster::name...) + _("]"); + + template using cast_op_type = type; + + operator type() & { return implicit_cast(indices{}); } + operator type() && { return std::move(*this).implicit_cast(indices{}); } + +protected: + template + type implicit_cast(index_sequence) & { return type(cast_op(std::get(subcasters))...); } + template + type implicit_cast(index_sequence) && { return type(cast_op(std::move(std::get(subcasters)))...); } + + static constexpr bool load_impl(const sequence &, bool, index_sequence<>) { return true; } + + template + bool load_impl(const sequence &seq, bool convert, index_sequence) { +#ifdef __cpp_fold_expressions + if ((... || !std::get(subcasters).load(seq[Is], convert))) + return false; +#else + for (bool r : {std::get(subcasters).load(seq[Is], convert)...}) + if (!r) + return false; +#endif + return true; + } + + /* Implementation: Convert a C++ tuple into a Python tuple */ + template + static handle cast_impl(T &&src, return_value_policy policy, handle parent, index_sequence) { + std::array entries{{ + reinterpret_steal(make_caster::cast(std::get(std::forward(src)), policy, parent))... + }}; + for (const auto &entry: entries) + if (!entry) + return handle(); + tuple result(size); + int counter = 0; + for (auto & entry: entries) + PyTuple_SET_ITEM(result.ptr(), counter++, entry.release().ptr()); + return result.release(); + } + + Tuple...> subcasters; +}; + +template class type_caster> + : public tuple_caster {}; + +template class type_caster> + : public tuple_caster {}; + +/// Helper class which abstracts away certain actions. Users can provide specializations for +/// custom holders, but it's only necessary if the type has a non-standard interface. +template +struct holder_helper { + static auto get(const T &p) -> decltype(p.get()) { return p.get(); } +}; + +/// Type caster for holder types like std::shared_ptr, etc. +template +struct copyable_holder_caster : public type_caster_base { +public: + using base = type_caster_base; + static_assert(std::is_base_of>::value, + "Holder classes are only supported for custom types"); + using base::base; + using base::cast; + using base::typeinfo; + using base::value; + + bool load(handle src, bool convert) { + return base::template load_impl>(src, convert); + } + + explicit operator type*() { return this->value; } + // static_cast works around compiler error with MSVC 17 and CUDA 10.2 + // see issue #2180 + explicit operator type&() { return *(static_cast(this->value)); } + explicit operator holder_type*() { return std::addressof(holder); } + explicit operator holder_type&() { return holder; } + + static handle cast(const holder_type &src, return_value_policy, handle) { + const auto *ptr = holder_helper::get(src); + return type_caster_base::cast_holder(ptr, &src); + } + +protected: + friend class type_caster_generic; + void check_holder_compat() { + if (typeinfo->default_holder) + throw cast_error("Unable to load a custom holder type from a default-holder instance"); + } + + bool load_value(value_and_holder &&v_h) { + if (v_h.holder_constructed()) { + value = v_h.value_ptr(); + holder = v_h.template holder(); + return true; + } else { + throw cast_error("Unable to cast from non-held to held instance (T& to Holder) " +#if defined(NDEBUG) + "(compile in debug mode for type information)"); +#else + "of type '" + type_id() + "''"); +#endif + } + } + + template ::value, int> = 0> + bool try_implicit_casts(handle, bool) { return false; } + + template ::value, int> = 0> + bool try_implicit_casts(handle src, bool convert) { + for (auto &cast : typeinfo->implicit_casts) { + copyable_holder_caster sub_caster(*cast.first); + if (sub_caster.load(src, convert)) { + value = cast.second(sub_caster.value); + holder = holder_type(sub_caster.holder, (type *) value); + return true; + } + } + return false; + } + + static bool try_direct_conversions(handle) { return false; } + + + holder_type holder; +}; + +/// Specialize for the common std::shared_ptr, so users don't need to +template +class type_caster> : public copyable_holder_caster> { }; + +template +struct move_only_holder_caster { + static_assert(std::is_base_of, type_caster>::value, + "Holder classes are only supported for custom types"); + + static handle cast(holder_type &&src, return_value_policy, handle) { + auto *ptr = holder_helper::get(src); + return type_caster_base::cast_holder(ptr, std::addressof(src)); + } + static constexpr auto name = type_caster_base::name; +}; + +template +class type_caster> + : public move_only_holder_caster> { }; + +template +using type_caster_holder = conditional_t::value, + copyable_holder_caster, + move_only_holder_caster>; + +template struct always_construct_holder { static constexpr bool value = Value; }; + +/// Create a specialization for custom holder types (silently ignores std::shared_ptr) +#define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type, ...) \ + namespace pybind11 { namespace detail { \ + template \ + struct always_construct_holder : always_construct_holder { }; \ + template \ + class type_caster::value>> \ + : public type_caster_holder { }; \ + }} + +// PYBIND11_DECLARE_HOLDER_TYPE holder types: +template struct is_holder_type : + std::is_base_of, detail::type_caster> {}; +// Specialization for always-supported unique_ptr holders: +template struct is_holder_type> : + std::true_type {}; + +template struct handle_type_name { static constexpr auto name = _(); }; +template <> struct handle_type_name { static constexpr auto name = _(PYBIND11_BYTES_NAME); }; +template <> struct handle_type_name { static constexpr auto name = _("int"); }; +template <> struct handle_type_name { static constexpr auto name = _("Iterable"); }; +template <> struct handle_type_name { static constexpr auto name = _("Iterator"); }; +template <> struct handle_type_name { static constexpr auto name = _("None"); }; +template <> struct handle_type_name { static constexpr auto name = _("*args"); }; +template <> struct handle_type_name { static constexpr auto name = _("**kwargs"); }; + +template +struct pyobject_caster { + template ::value, int> = 0> + bool load(handle src, bool /* convert */) { value = src; return static_cast(value); } + + template ::value, int> = 0> + bool load(handle src, bool /* convert */) { + if (!isinstance(src)) + return false; + value = reinterpret_borrow(src); + return true; + } + + static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { + return src.inc_ref(); + } + PYBIND11_TYPE_CASTER(type, handle_type_name::name); +}; + +template +class type_caster::value>> : public pyobject_caster { }; + +// Our conditions for enabling moving are quite restrictive: +// At compile time: +// - T needs to be a non-const, non-pointer, non-reference type +// - type_caster::operator T&() must exist +// - the type must be move constructible (obviously) +// At run-time: +// - if the type is non-copy-constructible, the object must be the sole owner of the type (i.e. it +// must have ref_count() == 1)h +// If any of the above are not satisfied, we fall back to copying. +template using move_is_plain_type = satisfies_none_of; +template struct move_always : std::false_type {}; +template struct move_always, + negation>, + std::is_move_constructible, + std::is_same>().operator T&()), T&> +>::value>> : std::true_type {}; +template struct move_if_unreferenced : std::false_type {}; +template struct move_if_unreferenced, + negation>, + std::is_move_constructible, + std::is_same>().operator T&()), T&> +>::value>> : std::true_type {}; +template using move_never = none_of, move_if_unreferenced>; + +// Detect whether returning a `type` from a cast on type's type_caster is going to result in a +// reference or pointer to a local variable of the type_caster. Basically, only +// non-reference/pointer `type`s and reference/pointers from a type_caster_generic are safe; +// everything else returns a reference/pointer to a local variable. +template using cast_is_temporary_value_reference = bool_constant< + (std::is_reference::value || std::is_pointer::value) && + !std::is_base_of>::value && + !std::is_same, void>::value +>; + +// When a value returned from a C++ function is being cast back to Python, we almost always want to +// force `policy = move`, regardless of the return value policy the function/method was declared +// with. +template struct return_value_policy_override { + static return_value_policy policy(return_value_policy p) { return p; } +}; + +template struct return_value_policy_override>::value, void>> { + static return_value_policy policy(return_value_policy p) { + return !std::is_lvalue_reference::value && + !std::is_pointer::value + ? return_value_policy::move : p; + } +}; + +// Basic python -> C++ casting; throws if casting fails +template type_caster &load_type(type_caster &conv, const handle &handle) { + if (!conv.load(handle, true)) { +#if defined(NDEBUG) + throw cast_error("Unable to cast Python instance to C++ type (compile in debug mode for details)"); +#else + throw cast_error("Unable to cast Python instance of type " + + (std::string) str(type::handle_of(handle)) + " to C++ type '" + type_id() + "'"); +#endif + } + return conv; +} +// Wrapper around the above that also constructs and returns a type_caster +template make_caster load_type(const handle &handle) { + make_caster conv; + load_type(conv, handle); + return conv; +} + +PYBIND11_NAMESPACE_END(detail) + +// pytype -> C++ type +template ::value, int> = 0> +T cast(const handle &handle) { + using namespace detail; + static_assert(!cast_is_temporary_value_reference::value, + "Unable to cast type to reference: value is local to type caster"); + return cast_op(load_type(handle)); +} + +// pytype -> pytype (calls converting constructor) +template ::value, int> = 0> +T cast(const handle &handle) { return T(reinterpret_borrow(handle)); } + +// C++ type -> py::object +template ::value, int> = 0> +object cast(T &&value, return_value_policy policy = return_value_policy::automatic_reference, + handle parent = handle()) { + using no_ref_T = typename std::remove_reference::type; + if (policy == return_value_policy::automatic) + policy = std::is_pointer::value ? return_value_policy::take_ownership : + std::is_lvalue_reference::value ? return_value_policy::copy : return_value_policy::move; + else if (policy == return_value_policy::automatic_reference) + policy = std::is_pointer::value ? return_value_policy::reference : + std::is_lvalue_reference::value ? return_value_policy::copy : return_value_policy::move; + return reinterpret_steal(detail::make_caster::cast(std::forward(value), policy, parent)); +} + +template T handle::cast() const { return pybind11::cast(*this); } +template <> inline void handle::cast() const { return; } + +template +detail::enable_if_t::value, T> move(object &&obj) { + if (obj.ref_count() > 1) +#if defined(NDEBUG) + throw cast_error("Unable to cast Python instance to C++ rvalue: instance has multiple references" + " (compile in debug mode for details)"); +#else + throw cast_error("Unable to move from Python " + (std::string) str(type::handle_of(obj)) + + " instance to C++ " + type_id() + " instance: instance has multiple references"); +#endif + + // Move into a temporary and return that, because the reference may be a local value of `conv` + T ret = std::move(detail::load_type(obj).operator T&()); + return ret; +} + +// Calling cast() on an rvalue calls pybind11::cast with the object rvalue, which does: +// - If we have to move (because T has no copy constructor), do it. This will fail if the moved +// object has multiple references, but trying to copy will fail to compile. +// - If both movable and copyable, check ref count: if 1, move; otherwise copy +// - Otherwise (not movable), copy. +template detail::enable_if_t::value, T> cast(object &&object) { + return move(std::move(object)); +} +template detail::enable_if_t::value, T> cast(object &&object) { + if (object.ref_count() > 1) + return cast(object); + else + return move(std::move(object)); +} +template detail::enable_if_t::value, T> cast(object &&object) { + return cast(object); +} + +template T object::cast() const & { return pybind11::cast(*this); } +template T object::cast() && { return pybind11::cast(std::move(*this)); } +template <> inline void object::cast() const & { return; } +template <> inline void object::cast() && { return; } + +PYBIND11_NAMESPACE_BEGIN(detail) + +// Declared in pytypes.h: +template ::value, int>> +object object_or_cast(T &&o) { return pybind11::cast(std::forward(o)); } + +struct override_unused {}; // Placeholder type for the unneeded (and dead code) static variable in the PYBIND11_OVERRIDE_OVERRIDE macro +template using override_caster_t = conditional_t< + cast_is_temporary_value_reference::value, make_caster, override_unused>; + +// Trampoline use: for reference/pointer types to value-converted values, we do a value cast, then +// store the result in the given variable. For other types, this is a no-op. +template enable_if_t::value, T> cast_ref(object &&o, make_caster &caster) { + return cast_op(load_type(caster, o)); +} +template enable_if_t::value, T> cast_ref(object &&, override_unused &) { + pybind11_fail("Internal error: cast_ref fallback invoked"); } + +// Trampoline use: Having a pybind11::cast with an invalid reference type is going to static_assert, even +// though if it's in dead code, so we provide a "trampoline" to pybind11::cast that only does anything in +// cases where pybind11::cast is valid. +template enable_if_t::value, T> cast_safe(object &&o) { + return pybind11::cast(std::move(o)); } +template enable_if_t::value, T> cast_safe(object &&) { + pybind11_fail("Internal error: cast_safe fallback invoked"); } +template <> inline void cast_safe(object &&) {} + +PYBIND11_NAMESPACE_END(detail) + +template +tuple make_tuple() { return tuple(0); } + +template tuple make_tuple(Args&&... args_) { + constexpr size_t size = sizeof...(Args); + std::array args { + { reinterpret_steal(detail::make_caster::cast( + std::forward(args_), policy, nullptr))... } + }; + for (size_t i = 0; i < args.size(); i++) { + if (!args[i]) { +#if defined(NDEBUG) + throw cast_error("make_tuple(): unable to convert arguments to Python object (compile in debug mode for details)"); +#else + std::array argtypes { {type_id()...} }; + throw cast_error("make_tuple(): unable to convert argument of type '" + + argtypes[i] + "' to Python object"); +#endif + } + } + tuple result(size); + int counter = 0; + for (auto &arg_value : args) + PyTuple_SET_ITEM(result.ptr(), counter++, arg_value.release().ptr()); + return result; +} + +/// \ingroup annotations +/// Annotation for arguments +struct arg { + /// Constructs an argument with the name of the argument; if null or omitted, this is a positional argument. + constexpr explicit arg(const char *name = nullptr) : name(name), flag_noconvert(false), flag_none(true) { } + /// Assign a value to this argument + template arg_v operator=(T &&value) const; + /// Indicate that the type should not be converted in the type caster + arg &noconvert(bool flag = true) { flag_noconvert = flag; return *this; } + /// Indicates that the argument should/shouldn't allow None (e.g. for nullable pointer args) + arg &none(bool flag = true) { flag_none = flag; return *this; } + + const char *name; ///< If non-null, this is a named kwargs argument + bool flag_noconvert : 1; ///< If set, do not allow conversion (requires a supporting type caster!) + bool flag_none : 1; ///< If set (the default), allow None to be passed to this argument +}; + +/// \ingroup annotations +/// Annotation for arguments with values +struct arg_v : arg { +private: + template + arg_v(arg &&base, T &&x, const char *descr = nullptr) + : arg(base), + value(reinterpret_steal( + detail::make_caster::cast(x, return_value_policy::automatic, {}) + )), + descr(descr) +#if !defined(NDEBUG) + , type(type_id()) +#endif + { } + +public: + /// Direct construction with name, default, and description + template + arg_v(const char *name, T &&x, const char *descr = nullptr) + : arg_v(arg(name), std::forward(x), descr) { } + + /// Called internally when invoking `py::arg("a") = value` + template + arg_v(const arg &base, T &&x, const char *descr = nullptr) + : arg_v(arg(base), std::forward(x), descr) { } + + /// Same as `arg::noconvert()`, but returns *this as arg_v&, not arg& + arg_v &noconvert(bool flag = true) { arg::noconvert(flag); return *this; } + + /// Same as `arg::nonone()`, but returns *this as arg_v&, not arg& + arg_v &none(bool flag = true) { arg::none(flag); return *this; } + + /// The default value + object value; + /// The (optional) description of the default value + const char *descr; +#if !defined(NDEBUG) + /// The C++ type name of the default value (only available when compiled in debug mode) + std::string type; +#endif +}; + +/// \ingroup annotations +/// Annotation indicating that all following arguments are keyword-only; the is the equivalent of an +/// unnamed '*' argument (in Python 3) +struct kw_only {}; + +/// \ingroup annotations +/// Annotation indicating that all previous arguments are positional-only; the is the equivalent of an +/// unnamed '/' argument (in Python 3.8) +struct pos_only {}; + +template +arg_v arg::operator=(T &&value) const { return {std::move(*this), std::forward(value)}; } + +/// Alias for backward compatibility -- to be removed in version 2.0 +template using arg_t = arg_v; + +inline namespace literals { +/** \rst + String literal version of `arg` + \endrst */ +constexpr arg operator"" _a(const char *name, size_t) { return arg(name); } +} // namespace literals + +PYBIND11_NAMESPACE_BEGIN(detail) + +// forward declaration (definition in attr.h) +struct function_record; + +/// Internal data associated with a single function call +struct function_call { + function_call(const function_record &f, handle p); // Implementation in attr.h + + /// The function data: + const function_record &func; + + /// Arguments passed to the function: + std::vector args; + + /// The `convert` value the arguments should be loaded with + std::vector args_convert; + + /// Extra references for the optional `py::args` and/or `py::kwargs` arguments (which, if + /// present, are also in `args` but without a reference). + object args_ref, kwargs_ref; + + /// The parent, if any + handle parent; + + /// If this is a call to an initializer, this argument contains `self` + handle init_self; +}; + + +/// Helper class which loads arguments for C++ functions called from Python +template +class argument_loader { + using indices = make_index_sequence; + + template using argument_is_args = std::is_same, args>; + template using argument_is_kwargs = std::is_same, kwargs>; + // Get args/kwargs argument positions relative to the end of the argument list: + static constexpr auto args_pos = constexpr_first() - (int) sizeof...(Args), + kwargs_pos = constexpr_first() - (int) sizeof...(Args); + + static constexpr bool args_kwargs_are_last = kwargs_pos >= - 1 && args_pos >= kwargs_pos - 1; + + static_assert(args_kwargs_are_last, "py::args/py::kwargs are only permitted as the last argument(s) of a function"); + +public: + static constexpr bool has_kwargs = kwargs_pos < 0; + static constexpr bool has_args = args_pos < 0; + + static constexpr auto arg_names = concat(type_descr(make_caster::name)...); + + bool load_args(function_call &call) { + return load_impl_sequence(call, indices{}); + } + + template + enable_if_t::value, Return> call(Func &&f) && { + return std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); + } + + template + enable_if_t::value, void_type> call(Func &&f) && { + std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); + return void_type(); + } + +private: + + static bool load_impl_sequence(function_call &, index_sequence<>) { return true; } + + template + bool load_impl_sequence(function_call &call, index_sequence) { +#ifdef __cpp_fold_expressions + if ((... || !std::get(argcasters).load(call.args[Is], call.args_convert[Is]))) + return false; +#else + for (bool r : {std::get(argcasters).load(call.args[Is], call.args_convert[Is])...}) + if (!r) + return false; +#endif + return true; + } + + template + Return call_impl(Func &&f, index_sequence, Guard &&) && { + return std::forward(f)(cast_op(std::move(std::get(argcasters)))...); + } + + std::tuple...> argcasters; +}; + +/// Helper class which collects only positional arguments for a Python function call. +/// A fancier version below can collect any argument, but this one is optimal for simple calls. +template +class simple_collector { +public: + template + explicit simple_collector(Ts &&...values) + : m_args(pybind11::make_tuple(std::forward(values)...)) { } + + const tuple &args() const & { return m_args; } + dict kwargs() const { return {}; } + + tuple args() && { return std::move(m_args); } + + /// Call a Python function and pass the collected arguments + object call(PyObject *ptr) const { + PyObject *result = PyObject_CallObject(ptr, m_args.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); + } + +private: + tuple m_args; +}; + +/// Helper class which collects positional, keyword, * and ** arguments for a Python function call +template +class unpacking_collector { +public: + template + explicit unpacking_collector(Ts &&...values) { + // Tuples aren't (easily) resizable so a list is needed for collection, + // but the actual function call strictly requires a tuple. + auto args_list = list(); + int _[] = { 0, (process(args_list, std::forward(values)), 0)... }; + ignore_unused(_); + + m_args = std::move(args_list); + } + + const tuple &args() const & { return m_args; } + const dict &kwargs() const & { return m_kwargs; } + + tuple args() && { return std::move(m_args); } + dict kwargs() && { return std::move(m_kwargs); } + + /// Call a Python function and pass the collected arguments + object call(PyObject *ptr) const { + PyObject *result = PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); + } + +private: + template + void process(list &args_list, T &&x) { + auto o = reinterpret_steal(detail::make_caster::cast(std::forward(x), policy, {})); + if (!o) { +#if defined(NDEBUG) + argument_cast_error(); +#else + argument_cast_error(std::to_string(args_list.size()), type_id()); +#endif + } + args_list.append(o); + } + + void process(list &args_list, detail::args_proxy ap) { + for (auto a : ap) + args_list.append(a); + } + + void process(list &/*args_list*/, arg_v a) { + if (!a.name) +#if defined(NDEBUG) + nameless_argument_error(); +#else + nameless_argument_error(a.type); +#endif + + if (m_kwargs.contains(a.name)) { +#if defined(NDEBUG) + multiple_values_error(); +#else + multiple_values_error(a.name); +#endif + } + if (!a.value) { +#if defined(NDEBUG) + argument_cast_error(); +#else + argument_cast_error(a.name, a.type); +#endif + } + m_kwargs[a.name] = a.value; + } + + void process(list &/*args_list*/, detail::kwargs_proxy kp) { + if (!kp) + return; + for (auto k : reinterpret_borrow(kp)) { + if (m_kwargs.contains(k.first)) { +#if defined(NDEBUG) + multiple_values_error(); +#else + multiple_values_error(str(k.first)); +#endif + } + m_kwargs[k.first] = k.second; + } + } + + [[noreturn]] static void nameless_argument_error() { + throw type_error("Got kwargs without a name; only named arguments " + "may be passed via py::arg() to a python function call. " + "(compile in debug mode for details)"); + } + [[noreturn]] static void nameless_argument_error(std::string type) { + throw type_error("Got kwargs without a name of type '" + type + "'; only named " + "arguments may be passed via py::arg() to a python function call. "); + } + [[noreturn]] static void multiple_values_error() { + throw type_error("Got multiple values for keyword argument " + "(compile in debug mode for details)"); + } + + [[noreturn]] static void multiple_values_error(std::string name) { + throw type_error("Got multiple values for keyword argument '" + name + "'"); + } + + [[noreturn]] static void argument_cast_error() { + throw cast_error("Unable to convert call argument to Python object " + "(compile in debug mode for details)"); + } + + [[noreturn]] static void argument_cast_error(std::string name, std::string type) { + throw cast_error("Unable to convert call argument '" + name + + "' of type '" + type + "' to Python object"); + } + +private: + tuple m_args; + dict m_kwargs; +}; + +/// Collect only positional arguments for a Python function call +template ...>::value>> +simple_collector collect_arguments(Args &&...args) { + return simple_collector(std::forward(args)...); +} + +/// Collect all arguments, including keywords and unpacking (only instantiated when needed) +template ...>::value>> +unpacking_collector collect_arguments(Args &&...args) { + // Following argument order rules for generalized unpacking according to PEP 448 + static_assert( + constexpr_last() < constexpr_first() + && constexpr_last() < constexpr_first(), + "Invalid function call: positional args must precede keywords and ** unpacking; " + "* unpacking must precede ** unpacking" + ); + return unpacking_collector(std::forward(args)...); +} + +template +template +object object_api::operator()(Args &&...args) const { + return detail::collect_arguments(std::forward(args)...).call(derived().ptr()); +} + +template +template +object object_api::call(Args &&...args) const { + return operator()(std::forward(args)...); +} + +PYBIND11_NAMESPACE_END(detail) + + +template +handle type::handle_of() { + static_assert( + std::is_base_of>::value, + "py::type::of only supports the case where T is a registered C++ types." + ); + + return detail::get_type_handle(typeid(T), true); +} + + +#define PYBIND11_MAKE_OPAQUE(...) \ + namespace pybind11 { namespace detail { \ + template<> class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> { }; \ + }} + +/// Lets you pass a type containing a `,` through a macro parameter without needing a separate +/// typedef, e.g.: `PYBIND11_OVERRIDE(PYBIND11_TYPE(ReturnType), PYBIND11_TYPE(Parent), f, arg)` +#define PYBIND11_TYPE(...) __VA_ARGS__ + +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/chrono.h b/extern/pybind/include/pybind11/chrono.h new file mode 100644 index 00000000..cbe9acec --- /dev/null +++ b/extern/pybind/include/pybind11/chrono.h @@ -0,0 +1,191 @@ +/* + pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime + + Copyright (c) 2016 Trent Houliston and + Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include +#include +#include +#include + +// Backport the PyDateTime_DELTA functions from Python3.3 if required +#ifndef PyDateTime_DELTA_GET_DAYS +#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) +#endif +#ifndef PyDateTime_DELTA_GET_SECONDS +#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) +#endif +#ifndef PyDateTime_DELTA_GET_MICROSECONDS +#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) +#endif + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +template class duration_caster { +public: + typedef typename type::rep rep; + using period = typename type::period; + + using days = std::chrono::duration>; + + bool load(handle src, bool) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + if (!src) return false; + // If invoked with datetime.delta object + if (PyDelta_Check(src.ptr())) { + value = type(duration_cast>( + days(PyDateTime_DELTA_GET_DAYS(src.ptr())) + + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr())) + + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr())))); + return true; + } + // If invoked with a float we assume it is seconds and convert + else if (PyFloat_Check(src.ptr())) { + value = type(duration_cast>(duration(PyFloat_AsDouble(src.ptr())))); + return true; + } + else return false; + } + + // If this is a duration just return it back + static const std::chrono::duration& get_duration(const std::chrono::duration &src) { + return src; + } + + // If this is a time_point get the time_since_epoch + template static std::chrono::duration get_duration(const std::chrono::time_point> &src) { + return src.time_since_epoch(); + } + + static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) { + using namespace std::chrono; + + // Use overloaded function to get our duration from our source + // Works out if it is a duration or time_point and get the duration + auto d = get_duration(src); + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + // Declare these special duration types so the conversions happen with the correct primitive types (int) + using dd_t = duration>; + using ss_t = duration>; + using us_t = duration; + + auto dd = duration_cast(d); + auto subd = d - dd; + auto ss = duration_cast(subd); + auto us = duration_cast(subd - ss); + return PyDelta_FromDSU(dd.count(), ss.count(), us.count()); + } + + PYBIND11_TYPE_CASTER(type, _("datetime.timedelta")); +}; + +// This is for casting times on the system clock into datetime.datetime instances +template class type_caster> { +public: + using type = std::chrono::time_point; + bool load(handle src, bool) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + if (!src) return false; + + std::tm cal; + microseconds msecs; + + if (PyDateTime_Check(src.ptr())) { + cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr()); + cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr()); + cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr()); + cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); + cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; + cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; + cal.tm_isdst = -1; + msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr())); + } else if (PyDate_Check(src.ptr())) { + cal.tm_sec = 0; + cal.tm_min = 0; + cal.tm_hour = 0; + cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); + cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; + cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; + cal.tm_isdst = -1; + msecs = microseconds(0); + } else if (PyTime_Check(src.ptr())) { + cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr()); + cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr()); + cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr()); + cal.tm_mday = 1; // This date (day, month, year) = (1, 0, 70) + cal.tm_mon = 0; // represents 1-Jan-1970, which is the first + cal.tm_year = 70; // earliest available date for Python's datetime + cal.tm_isdst = -1; + msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr())); + } + else return false; + + value = time_point_cast(system_clock::from_time_t(std::mktime(&cal)) + msecs); + return true; + } + + static handle cast(const std::chrono::time_point &src, return_value_policy /* policy */, handle /* parent */) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + // Get out microseconds, and make sure they are positive, to avoid bug in eastern hemisphere time zones + // (cfr. https://github.com/pybind/pybind11/issues/2417) + using us_t = duration; + auto us = duration_cast(src.time_since_epoch() % seconds(1)); + if (us.count() < 0) + us += seconds(1); + + // Subtract microseconds BEFORE `system_clock::to_time_t`, because: + // > If std::time_t has lower precision, it is implementation-defined whether the value is rounded or truncated. + // (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t) + std::time_t tt = system_clock::to_time_t(time_point_cast(src - us)); + // this function uses static memory so it's best to copy it out asap just in case + // otherwise other code that is using localtime may break this (not just python code) + std::tm localtime = *std::localtime(&tt); + + return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, + localtime.tm_mon + 1, + localtime.tm_mday, + localtime.tm_hour, + localtime.tm_min, + localtime.tm_sec, + us.count()); + } + PYBIND11_TYPE_CASTER(type, _("datetime.datetime")); +}; + +// Other clocks that are not the system clock are not measured as datetime.datetime objects +// since they are not measured on calendar time. So instead we just make them timedeltas +// Or if they have passed us a time as a float we convert that +template class type_caster> +: public duration_caster> { +}; + +template class type_caster> +: public duration_caster> { +}; + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/common.h b/extern/pybind/include/pybind11/common.h new file mode 100644 index 00000000..6c8a4f1e --- /dev/null +++ b/extern/pybind/include/pybind11/common.h @@ -0,0 +1,2 @@ +#include "detail/common.h" +#warning "Including 'common.h' is deprecated. It will be removed in v3.0. Use 'pybind11.h'." diff --git a/extern/pybind/include/pybind11/complex.h b/extern/pybind/include/pybind11/complex.h new file mode 100644 index 00000000..f8327eb3 --- /dev/null +++ b/extern/pybind/include/pybind11/complex.h @@ -0,0 +1,65 @@ +/* + pybind11/complex.h: Complex number support + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include + +/// glibc defines I as a macro which breaks things, e.g., boost template names +#ifdef I +# undef I +#endif + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +template struct format_descriptor, detail::enable_if_t::value>> { + static constexpr const char c = format_descriptor::c; + static constexpr const char value[3] = { 'Z', c, '\0' }; + static std::string format() { return std::string(value); } +}; + +#ifndef PYBIND11_CPP17 + +template constexpr const char format_descriptor< + std::complex, detail::enable_if_t::value>>::value[3]; + +#endif + +PYBIND11_NAMESPACE_BEGIN(detail) + +template struct is_fmt_numeric, detail::enable_if_t::value>> { + static constexpr bool value = true; + static constexpr int index = is_fmt_numeric::index + 3; +}; + +template class type_caster> { +public: + bool load(handle src, bool convert) { + if (!src) + return false; + if (!convert && !PyComplex_Check(src.ptr())) + return false; + Py_complex result = PyComplex_AsCComplex(src.ptr()); + if (result.real == -1.0 && PyErr_Occurred()) { + PyErr_Clear(); + return false; + } + value = std::complex((T) result.real, (T) result.imag); + return true; + } + + static handle cast(const std::complex &src, return_value_policy /* policy */, handle /* parent */) { + return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); + } + + PYBIND11_TYPE_CASTER(std::complex, _("complex")); +}; +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/detail/class.h b/extern/pybind/include/pybind11/detail/class.h new file mode 100644 index 00000000..65dad5a5 --- /dev/null +++ b/extern/pybind/include/pybind11/detail/class.h @@ -0,0 +1,710 @@ +/* + pybind11/detail/class.h: Python C API implementation details for py::class_ + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "../attr.h" +#include "../options.h" + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +#if PY_VERSION_HEX >= 0x03030000 && !defined(PYPY_VERSION) +# define PYBIND11_BUILTIN_QUALNAME +# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) +#else +// In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type +// signatures; in 3.3+ this macro expands to nothing: +# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) setattr((PyObject *) obj, "__qualname__", nameobj) +#endif + +inline std::string get_fully_qualified_tp_name(PyTypeObject *type) { +#if !defined(PYPY_VERSION) + return type->tp_name; +#else + auto module_name = handle((PyObject *) type).attr("__module__").cast(); + if (module_name == PYBIND11_BUILTINS_MODULE) + return type->tp_name; + else + return std::move(module_name) + "." + type->tp_name; +#endif +} + +inline PyTypeObject *type_incref(PyTypeObject *type) { + Py_INCREF(type); + return type; +} + +#if !defined(PYPY_VERSION) + +/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance. +extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) { + return PyProperty_Type.tp_descr_get(self, cls, cls); +} + +/// `pybind11_static_property.__set__()`: Just like the above `__get__()`. +extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) { + PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj); + return PyProperty_Type.tp_descr_set(self, cls, value); +} + +/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()` + methods are modified to always use the object type instead of a concrete instance. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + constexpr auto *name = "pybind11_static_property"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_static_property_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyProperty_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + type->tp_descr_get = pybind11_static_get; + type->tp_descr_set = pybind11_static_set; + + if (PyType_Ready(type) < 0) + pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); + + return type; +} + +#else // PYPY + +/** PyPy has some issues with the above C API, so we evaluate Python code instead. + This function will only be called once so performance isn't really a concern. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + auto d = dict(); + PyObject *result = PyRun_String(R"(\ + class pybind11_static_property(property): + def __get__(self, obj, cls): + return property.__get__(self, cls, cls) + + def __set__(self, obj, value): + cls = obj if isinstance(obj, type) else type(obj) + property.__set__(self, cls, value) + )", Py_file_input, d.ptr(), d.ptr() + ); + if (result == nullptr) + throw error_already_set(); + Py_DECREF(result); + return (PyTypeObject *) d["pybind11_static_property"].cast().release().ptr(); +} + +#endif // PYPY + +/** Types with static properties need to handle `Type.static_prop = x` in a specific way. + By default, Python replaces the `static_property` itself, but for wrapped C++ types + we need to call `static_property.__set__()` in order to propagate the new value to + the underlying C++ data structure. */ +extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) { + // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw + // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + + // The following assignment combinations are possible: + // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` + // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` + // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment + const auto static_prop = (PyObject *) get_internals().static_property_type; + const auto call_descr_set = descr && value && PyObject_IsInstance(descr, static_prop) + && !PyObject_IsInstance(value, static_prop); + if (call_descr_set) { + // Call `static_property.__set__()` instead of replacing the `static_property`. +#if !defined(PYPY_VERSION) + return Py_TYPE(descr)->tp_descr_set(descr, obj, value); +#else + if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) { + Py_DECREF(result); + return 0; + } else { + return -1; + } +#endif + } else { + // Replace existing attribute. + return PyType_Type.tp_setattro(obj, name, value); + } +} + +#if PY_MAJOR_VERSION >= 3 +/** + * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing + * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, + * when called on a class, or a PyMethod, when called on an instance. Override that behaviour here + * to do a special case bypass for PyInstanceMethod_Types. + */ +extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + if (descr && PyInstanceMethod_Check(descr)) { + Py_INCREF(descr); + return descr; + } + else { + return PyType_Type.tp_getattro(obj, name); + } +} +#endif + +/// metaclass `__call__` function that is used to create all pybind11 objects. +extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, PyObject *kwargs) { + + // use the default metaclass call to create/initialize the object + PyObject *self = PyType_Type.tp_call(type, args, kwargs); + if (self == nullptr) { + return nullptr; + } + + // This must be a pybind11 instance + auto instance = reinterpret_cast(self); + + // Ensure that the base __init__ function(s) were called + for (const auto &vh : values_and_holders(instance)) { + if (!vh.holder_constructed()) { + PyErr_Format(PyExc_TypeError, "%.200s.__init__() must be called when overriding __init__", + get_fully_qualified_tp_name(vh.type->type).c_str()); + Py_DECREF(self); + return nullptr; + } + } + + return self; +} + +/// Cleanup the type-info for a pybind11-registered type. +extern "C" inline void pybind11_meta_dealloc(PyObject *obj) { + auto *type = (PyTypeObject *) obj; + auto &internals = get_internals(); + + // A pybind11-registered type will: + // 1) be found in internals.registered_types_py + // 2) have exactly one associated `detail::type_info` + auto found_type = internals.registered_types_py.find(type); + if (found_type != internals.registered_types_py.end() && + found_type->second.size() == 1 && + found_type->second[0]->type == type) { + + auto *tinfo = found_type->second[0]; + auto tindex = std::type_index(*tinfo->cpptype); + internals.direct_conversions.erase(tindex); + + if (tinfo->module_local) + registered_local_types_cpp().erase(tindex); + else + internals.registered_types_cpp.erase(tindex); + internals.registered_types_py.erase(tinfo->type); + + // Actually just `std::erase_if`, but that's only available in C++20 + auto &cache = internals.inactive_override_cache; + for (auto it = cache.begin(), last = cache.end(); it != last; ) { + if (it->first == (PyObject *) tinfo->type) + it = cache.erase(it); + else + ++it; + } + + delete tinfo; + } + + PyType_Type.tp_dealloc(obj); +} + +/** This metaclass is assigned by default to all pybind11 types and is required in order + for static properties to function correctly. Users may override this using `py::metaclass`. + Return value: New reference. */ +inline PyTypeObject* make_default_metaclass() { + constexpr auto *name = "pybind11_type"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_default_metaclass(): error allocating metaclass!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyType_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_call = pybind11_meta_call; + + type->tp_setattro = pybind11_meta_setattro; +#if PY_MAJOR_VERSION >= 3 + type->tp_getattro = pybind11_meta_getattro; +#endif + + type->tp_dealloc = pybind11_meta_dealloc; + + if (PyType_Ready(type) < 0) + pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); + + return type; +} + +/// For multiple inheritance types we need to recursively register/deregister base pointers for any +/// base classes with pointers that are difference from the instance value pointer so that we can +/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs. +inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, + bool (*f)(void * /*parentptr*/, instance * /*self*/)) { + for (handle h : reinterpret_borrow(tinfo->type->tp_bases)) { + if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { + for (auto &c : parent_tinfo->implicit_casts) { + if (c.first == tinfo->cpptype) { + auto *parentptr = c.second(valueptr); + if (parentptr != valueptr) + f(parentptr, self); + traverse_offset_bases(parentptr, parent_tinfo, self, f); + break; + } + } + } + } +} + +inline bool register_instance_impl(void *ptr, instance *self) { + get_internals().registered_instances.emplace(ptr, self); + return true; // unused, but gives the same signature as the deregister func +} +inline bool deregister_instance_impl(void *ptr, instance *self) { + auto ®istered_instances = get_internals().registered_instances; + auto range = registered_instances.equal_range(ptr); + for (auto it = range.first; it != range.second; ++it) { + if (self == it->second) { + registered_instances.erase(it); + return true; + } + } + return false; +} + +inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { + register_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, register_instance_impl); +} + +inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { + bool ret = deregister_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); + return ret; +} + +/// Instance creation function for all pybind11 types. It allocates the internal instance layout for +/// holding C++ objects and holders. Allocation is done lazily (the first time the instance is cast +/// to a reference or pointer), and initialization is done by an `__init__` function. +inline PyObject *make_new_instance(PyTypeObject *type) { +#if defined(PYPY_VERSION) + // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited + // object is a a plain Python type (i.e. not derived from an extension type). Fix it. + ssize_t instance_size = static_cast(sizeof(instance)); + if (type->tp_basicsize < instance_size) { + type->tp_basicsize = instance_size; + } +#endif + PyObject *self = type->tp_alloc(type, 0); + auto inst = reinterpret_cast(self); + // Allocate the value/holder internals: + inst->allocate_layout(); + + inst->owned = true; + + return self; +} + +/// Instance creation function for all pybind11 types. It only allocates space for the +/// C++ object, but doesn't call the constructor -- an `__init__` function must do that. +extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) { + return make_new_instance(type); +} + +/// An `__init__` function constructs the C++ object. Users should provide at least one +/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the +/// following default function will be used which simply throws an exception. +extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { + PyTypeObject *type = Py_TYPE(self); + std::string msg = get_fully_qualified_tp_name(type) + ": No constructor defined!"; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return -1; +} + +inline void add_patient(PyObject *nurse, PyObject *patient) { + auto &internals = get_internals(); + auto instance = reinterpret_cast(nurse); + instance->has_patients = true; + Py_INCREF(patient); + internals.patients[nurse].push_back(patient); +} + +inline void clear_patients(PyObject *self) { + auto instance = reinterpret_cast(self); + auto &internals = get_internals(); + auto pos = internals.patients.find(self); + assert(pos != internals.patients.end()); + // Clearing the patients can cause more Python code to run, which + // can invalidate the iterator. Extract the vector of patients + // from the unordered_map first. + auto patients = std::move(pos->second); + internals.patients.erase(pos); + instance->has_patients = false; + for (PyObject *&patient : patients) + Py_CLEAR(patient); +} + +/// Clears all internal data from the instance and removes it from registered instances in +/// preparation for deallocation. +inline void clear_instance(PyObject *self) { + auto instance = reinterpret_cast(self); + + // Deallocate any values/holders, if present: + for (auto &v_h : values_and_holders(instance)) { + if (v_h) { + + // We have to deregister before we call dealloc because, for virtual MI types, we still + // need to be able to get the parent pointers. + if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) + pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); + + if (instance->owned || v_h.holder_constructed()) + v_h.type->dealloc(v_h); + } + } + // Deallocate the value/holder layout internals: + instance->deallocate_layout(); + + if (instance->weakrefs) + PyObject_ClearWeakRefs(self); + + PyObject **dict_ptr = _PyObject_GetDictPtr(self); + if (dict_ptr) + Py_CLEAR(*dict_ptr); + + if (instance->has_patients) + clear_patients(self); +} + +/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` +/// to destroy the C++ object itself, while the rest is Python bookkeeping. +extern "C" inline void pybind11_object_dealloc(PyObject *self) { + clear_instance(self); + + auto type = Py_TYPE(self); + type->tp_free(self); + +#if PY_VERSION_HEX < 0x03080000 + // `type->tp_dealloc != pybind11_object_dealloc` means that we're being called + // as part of a derived type's dealloc, in which case we're not allowed to decref + // the type here. For cross-module compatibility, we shouldn't compare directly + // with `pybind11_object_dealloc`, but with the common one stashed in internals. + auto pybind11_object_type = (PyTypeObject *) get_internals().instance_base; + if (type->tp_dealloc == pybind11_object_type->tp_dealloc) + Py_DECREF(type); +#else + // This was not needed before Python 3.8 (Python issue 35810) + // https://github.com/pybind/pybind11/issues/1946 + Py_DECREF(type); +#endif +} + +/** Create the type which can be used as a common base for all classes. This is + needed in order to satisfy Python's requirements for multiple inheritance. + Return value: New reference. */ +inline PyObject *make_object_base_type(PyTypeObject *metaclass) { + constexpr auto *name = "pybind11_object"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail("make_object_base_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyBaseObject_Type); + type->tp_basicsize = static_cast(sizeof(instance)); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_new = pybind11_object_new; + type->tp_init = pybind11_object_init; + type->tp_dealloc = pybind11_object_dealloc; + + /* Support weak references (needed for the keep_alive feature) */ + type->tp_weaklistoffset = offsetof(instance, weakrefs); + + if (PyType_Ready(type) < 0) + pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string()); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); + + assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + return (PyObject *) heap_type; +} + +/// dynamic_attr: Support for `d = instance.__dict__`. +extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + if (!dict) + dict = PyDict_New(); + Py_XINCREF(dict); + return dict; +} + +/// dynamic_attr: Support for `instance.__dict__ = dict()`. +extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { + if (!PyDict_Check(new_dict)) { + PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'", + get_fully_qualified_tp_name(Py_TYPE(new_dict)).c_str()); + return -1; + } + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_INCREF(new_dict); + Py_CLEAR(dict); + dict = new_dict; + return 0; +} + +/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`. +extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_VISIT(dict); + return 0; +} + +/// dynamic_attr: Allow the GC to clear the dictionary. +extern "C" inline int pybind11_clear(PyObject *self) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_CLEAR(dict); + return 0; +} + +/// Give instances of this type a `__dict__` and opt into garbage collection. +inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { + auto type = &heap_type->ht_type; + type->tp_flags |= Py_TPFLAGS_HAVE_GC; + type->tp_dictoffset = type->tp_basicsize; // place dict at the end + type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it + type->tp_traverse = pybind11_traverse; + type->tp_clear = pybind11_clear; + + static PyGetSetDef getset[] = { + {const_cast("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, + {nullptr, nullptr, nullptr, nullptr, nullptr} + }; + type->tp_getset = getset; +} + +/// buffer_protocol: Fill in the view as specified by flags. +extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) { + // Look for a `get_buffer` implementation in this type's info or any bases (following MRO). + type_info *tinfo = nullptr; + for (auto type : reinterpret_borrow(Py_TYPE(obj)->tp_mro)) { + tinfo = get_type_info((PyTypeObject *) type.ptr()); + if (tinfo && tinfo->get_buffer) + break; + } + if (view == nullptr || !tinfo || !tinfo->get_buffer) { + if (view) + view->obj = nullptr; + PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); + return -1; + } + std::memset(view, 0, sizeof(Py_buffer)); + buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); + view->obj = obj; + view->ndim = 1; + view->internal = info; + view->buf = info->ptr; + view->itemsize = info->itemsize; + view->len = view->itemsize; + for (auto s : info->shape) + view->len *= s; + view->readonly = info->readonly; + if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) { + if (view) + view->obj = nullptr; + PyErr_SetString(PyExc_BufferError, "Writable buffer requested for readonly storage"); + return -1; + } + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) + view->format = const_cast(info->format.c_str()); + if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { + view->ndim = (int) info->ndim; + view->strides = &info->strides[0]; + view->shape = &info->shape[0]; + } + Py_INCREF(view->obj); + return 0; +} + +/// buffer_protocol: Release the resources of the buffer. +extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { + delete (buffer_info *) view->internal; +} + +/// Give this type a buffer interface. +inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { + heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; +#if PY_MAJOR_VERSION < 3 + heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; +#endif + + heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; + heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; +} + +/** Create a brand new Python type according to the `type_record` specification. + Return value: New reference. */ +inline PyObject* make_new_python_type(const type_record &rec) { + auto name = reinterpret_steal(PYBIND11_FROM_STRING(rec.name)); + + auto qualname = name; + if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) { +#if PY_MAJOR_VERSION >= 3 + qualname = reinterpret_steal( + PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); +#else + qualname = str(rec.scope.attr("__qualname__").cast() + "." + rec.name); +#endif + } + + object module_; + if (rec.scope) { + if (hasattr(rec.scope, "__module__")) + module_ = rec.scope.attr("__module__"); + else if (hasattr(rec.scope, "__name__")) + module_ = rec.scope.attr("__name__"); + } + + auto full_name = c_str( +#if !defined(PYPY_VERSION) + module_ ? str(module_).cast() + "." + rec.name : +#endif + rec.name); + + char *tp_doc = nullptr; + if (rec.doc && options::show_user_defined_docstrings()) { + /* Allocate memory for docstring (using PyObject_MALLOC, since + Python will free this later on) */ + size_t size = strlen(rec.doc) + 1; + tp_doc = (char *) PyObject_MALLOC(size); + memcpy((void *) tp_doc, rec.doc, size); + } + + auto &internals = get_internals(); + auto bases = tuple(rec.bases); + auto base = (bases.empty()) ? internals.instance_base + : bases[0].ptr(); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() + : internals.default_metaclass; + + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); + + heap_type->ht_name = name.release().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = qualname.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = full_name; + type->tp_doc = tp_doc; + type->tp_base = type_incref((PyTypeObject *)base); + type->tp_basicsize = static_cast(sizeof(instance)); + if (!bases.empty()) + type->tp_bases = bases.release().ptr(); + + /* Don't inherit base __init__ */ + type->tp_init = pybind11_object_init; + + /* Supported protocols */ + type->tp_as_number = &heap_type->as_number; + type->tp_as_sequence = &heap_type->as_sequence; + type->tp_as_mapping = &heap_type->as_mapping; +#if PY_VERSION_HEX >= 0x03050000 + type->tp_as_async = &heap_type->as_async; +#endif + + /* Flags */ + type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE; +#if PY_MAJOR_VERSION < 3 + type->tp_flags |= Py_TPFLAGS_CHECKTYPES; +#endif + if (!rec.is_final) + type->tp_flags |= Py_TPFLAGS_BASETYPE; + + if (rec.dynamic_attr) + enable_dynamic_attributes(heap_type); + + if (rec.buffer_protocol) + enable_buffer_protocol(heap_type); + + if (PyType_Ready(type) < 0) + pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); + + assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) + : !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + + /* Register type with the parent scope */ + if (rec.scope) + setattr(rec.scope, rec.name, (PyObject *) type); + else + Py_INCREF(type); // Keep it alive forever (reference leak) + + if (module_) // Needed by pydoc + setattr((PyObject *) type, "__module__", module_); + + PYBIND11_SET_OLDPY_QUALNAME(type, qualname); + + return (PyObject *) type; +} + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/detail/common.h b/extern/pybind/include/pybind11/detail/common.h new file mode 100644 index 00000000..9ee4dd2d --- /dev/null +++ b/extern/pybind/include/pybind11/detail/common.h @@ -0,0 +1,853 @@ +/* + pybind11/detail/common.h -- Basic macros + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#define PYBIND11_VERSION_MAJOR 2 +#define PYBIND11_VERSION_MINOR 6 +#define PYBIND11_VERSION_PATCH 1.dev1 + +#define PYBIND11_NAMESPACE_BEGIN(name) namespace name { +#define PYBIND11_NAMESPACE_END(name) } + +// Robust support for some features and loading modules compiled against different pybind versions +// requires forcing hidden visibility on pybind code, so we enforce this by setting the attribute on +// the main `pybind11` namespace. +#if !defined(PYBIND11_NAMESPACE) +# ifdef __GNUG__ +# define PYBIND11_NAMESPACE pybind11 __attribute__((visibility("hidden"))) +# else +# define PYBIND11_NAMESPACE pybind11 +# endif +#endif + +#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) +# if __cplusplus >= 201402L +# define PYBIND11_CPP14 +# if __cplusplus >= 201703L +# define PYBIND11_CPP17 +# endif +# endif +#elif defined(_MSC_VER) && __cplusplus == 199711L +// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented) +// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer +# if _MSVC_LANG >= 201402L +# define PYBIND11_CPP14 +# if _MSVC_LANG > 201402L && _MSC_VER >= 1910 +# define PYBIND11_CPP17 +# endif +# endif +#endif + +// Compiler version assertions +#if defined(__INTEL_COMPILER) +# if __INTEL_COMPILER < 1800 +# error pybind11 requires Intel C++ compiler v18 or newer +# endif +#elif defined(__clang__) && !defined(__apple_build_version__) +# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3) +# error pybind11 requires clang 3.3 or newer +# endif +#elif defined(__clang__) +// Apple changes clang version macros to its Xcode version; the first Xcode release based on +// (upstream) clang 3.3 was Xcode 5: +# if __clang_major__ < 5 +# error pybind11 requires Xcode/clang 5.0 or newer +# endif +#elif defined(__GNUG__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) +# error pybind11 requires gcc 4.8 or newer +# endif +#elif defined(_MSC_VER) +// Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features +// (e.g. std::negation) added in 2015u3: +# if _MSC_FULL_VER < 190024210 +# error pybind11 requires MSVC 2015 update 3 or newer +# endif +#endif + +#if !defined(PYBIND11_EXPORT) +# if defined(WIN32) || defined(_WIN32) +# define PYBIND11_EXPORT __declspec(dllexport) +# else +# define PYBIND11_EXPORT __attribute__ ((visibility("default"))) +# endif +#endif + +#if defined(_MSC_VER) +# define PYBIND11_NOINLINE __declspec(noinline) +#else +# define PYBIND11_NOINLINE __attribute__ ((noinline)) +#endif + +#if defined(PYBIND11_CPP14) +# define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]] +#else +# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason))) +#endif + +#if defined(PYBIND11_CPP17) +# define PYBIND11_MAYBE_UNUSED [[maybe_unused]] +#elif defined(_MSC_VER) && !defined(__clang__) +# define PYBIND11_MAYBE_UNUSED +#else +# define PYBIND11_MAYBE_UNUSED __attribute__ ((__unused__)) +#endif + +/* Don't let Python.h #define (v)snprintf as macro because they are implemented + properly in Visual Studio since 2015. */ +#if defined(_MSC_VER) && _MSC_VER >= 1900 +# define HAVE_SNPRINTF 1 +#endif + +/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode +#if defined(_MSC_VER) +# if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4) +# define HAVE_ROUND 1 +# endif +# pragma warning(push) +# pragma warning(disable: 4510 4610 4512 4005) +# if defined(_DEBUG) && !defined(Py_DEBUG) +# define PYBIND11_DEBUG_MARKER +# undef _DEBUG +# endif +#endif + +#include +#include +#include + +/* Python #defines overrides on all sorts of core functions, which + tends to weak havok in C++ codebases that expect these to work + like regular functions (potentially with several overloads) */ +#if defined(isalnum) +# undef isalnum +# undef isalpha +# undef islower +# undef isspace +# undef isupper +# undef tolower +# undef toupper +#endif + +#if defined(copysign) +# undef copysign +#endif + +#if defined(_MSC_VER) +# if defined(PYBIND11_DEBUG_MARKER) +# define _DEBUG +# undef PYBIND11_DEBUG_MARKER +# endif +# pragma warning(pop) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions +#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) +#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check +#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION +#define PYBIND11_BYTES_CHECK PyBytes_Check +#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString +#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize +#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize +#define PYBIND11_BYTES_AS_STRING PyBytes_AsString +#define PYBIND11_BYTES_SIZE PyBytes_Size +#define PYBIND11_LONG_CHECK(o) PyLong_Check(o) +#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o) +#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) o) +#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) o) +#define PYBIND11_BYTES_NAME "bytes" +#define PYBIND11_STRING_NAME "str" +#define PYBIND11_SLICE_OBJECT PyObject +#define PYBIND11_FROM_STRING PyUnicode_FromString +#define PYBIND11_STR_TYPE ::pybind11::str +#define PYBIND11_BOOL_ATTR "__bool__" +#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool) +#define PYBIND11_BUILTINS_MODULE "builtins" +// Providing a separate declaration to make Clang's -Wmissing-prototypes happy. +// See comment for PYBIND11_MODULE below for why this is marked "maybe unused". +#define PYBIND11_PLUGIN_IMPL(name) \ + extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT PyObject *PyInit_##name(); \ + extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() + +#else +#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_) +#define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check +#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION +#define PYBIND11_BYTES_CHECK PyString_Check +#define PYBIND11_BYTES_FROM_STRING PyString_FromString +#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize +#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize +#define PYBIND11_BYTES_AS_STRING PyString_AsString +#define PYBIND11_BYTES_SIZE PyString_Size +#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o)) +#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o)) +#define PYBIND11_LONG_FROM_SIGNED(o) PyInt_FromSsize_t((ssize_t) o) // Returns long if needed. +#define PYBIND11_LONG_FROM_UNSIGNED(o) PyInt_FromSize_t((size_t) o) // Returns long if needed. +#define PYBIND11_BYTES_NAME "str" +#define PYBIND11_STRING_NAME "unicode" +#define PYBIND11_SLICE_OBJECT PySliceObject +#define PYBIND11_FROM_STRING PyString_FromString +#define PYBIND11_STR_TYPE ::pybind11::bytes +#define PYBIND11_BOOL_ATTR "__nonzero__" +#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero) +#define PYBIND11_BUILTINS_MODULE "__builtin__" +// Providing a separate PyInit decl to make Clang's -Wmissing-prototypes happy. +// See comment for PYBIND11_MODULE below for why this is marked "maybe unused". +#define PYBIND11_PLUGIN_IMPL(name) \ + static PyObject *pybind11_init_wrapper(); \ + extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT void init##name(); \ + extern "C" PYBIND11_EXPORT void init##name() { \ + (void)pybind11_init_wrapper(); \ + } \ + PyObject *pybind11_init_wrapper() +#endif + +#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200 +extern "C" { + struct _Py_atomic_address { void *value; }; + PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current; +} +#endif + +#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code +#define PYBIND11_STRINGIFY(x) #x +#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) +#define PYBIND11_CONCAT(first, second) first##second +#define PYBIND11_ENSURE_INTERNALS_READY \ + pybind11::detail::get_internals(); + +#define PYBIND11_CHECK_PYTHON_VERSION \ + { \ + const char *compiled_ver = PYBIND11_TOSTRING(PY_MAJOR_VERSION) \ + "." PYBIND11_TOSTRING(PY_MINOR_VERSION); \ + const char *runtime_ver = Py_GetVersion(); \ + size_t len = std::strlen(compiled_ver); \ + if (std::strncmp(runtime_ver, compiled_ver, len) != 0 \ + || (runtime_ver[len] >= '0' && runtime_ver[len] <= '9')) { \ + PyErr_Format(PyExc_ImportError, \ + "Python version mismatch: module was compiled for Python %s, " \ + "but the interpreter version is incompatible: %s.", \ + compiled_ver, runtime_ver); \ + return nullptr; \ + } \ + } + +#define PYBIND11_CATCH_INIT_EXCEPTIONS \ + catch (pybind11::error_already_set &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } \ + +/** \rst + ***Deprecated in favor of PYBIND11_MODULE*** + + This macro creates the entry point that will be invoked when the Python interpreter + imports a plugin library. Please create a `module_` in the function body and return + the pointer to its underlying Python object at the end. + + .. code-block:: cpp + + PYBIND11_PLUGIN(example) { + pybind11::module_ m("example", "pybind11 example plugin"); + /// Set up bindings here + return m.ptr(); + } +\endrst */ +#define PYBIND11_PLUGIN(name) \ + PYBIND11_DEPRECATED("PYBIND11_PLUGIN is deprecated, use PYBIND11_MODULE") \ + static PyObject *pybind11_init(); \ + PYBIND11_PLUGIN_IMPL(name) { \ + PYBIND11_CHECK_PYTHON_VERSION \ + PYBIND11_ENSURE_INTERNALS_READY \ + try { \ + return pybind11_init(); \ + } PYBIND11_CATCH_INIT_EXCEPTIONS \ + } \ + PyObject *pybind11_init() + +/** \rst + This macro creates the entry point that will be invoked when the Python interpreter + imports an extension module. The module name is given as the fist argument and it + should not be in quotes. The second macro argument defines a variable of type + `py::module_` which can be used to initialize the module. + + The entry point is marked as "maybe unused" to aid dead-code detection analysis: + since the entry point is typically only looked up at runtime and not referenced + during translation, it would otherwise appear as unused ("dead") code. + + .. code-block:: cpp + + PYBIND11_MODULE(example, m) { + m.doc() = "pybind11 example module"; + + // Add bindings here + m.def("foo", []() { + return "Hello, World!"; + }); + } +\endrst */ +#define PYBIND11_MODULE(name, variable) \ + static ::pybind11::module_::module_def \ + PYBIND11_CONCAT(pybind11_module_def_, name) PYBIND11_MAYBE_UNUSED; \ + PYBIND11_MAYBE_UNUSED \ + static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \ + PYBIND11_PLUGIN_IMPL(name) { \ + PYBIND11_CHECK_PYTHON_VERSION \ + PYBIND11_ENSURE_INTERNALS_READY \ + auto m = ::pybind11::module_::create_extension_module( \ + PYBIND11_TOSTRING(name), nullptr, \ + &PYBIND11_CONCAT(pybind11_module_def_, name)); \ + try { \ + PYBIND11_CONCAT(pybind11_init_, name)(m); \ + return m.ptr(); \ + } PYBIND11_CATCH_INIT_EXCEPTIONS \ + } \ + void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &variable) + + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +using ssize_t = Py_ssize_t; +using size_t = std::size_t; + +/// Approach used to cast a previously unknown C++ instance into a Python object +enum class return_value_policy : uint8_t { + /** This is the default return value policy, which falls back to the policy + return_value_policy::take_ownership when the return value is a pointer. + Otherwise, it uses return_value::move or return_value::copy for rvalue + and lvalue references, respectively. See below for a description of what + all of these different policies do. */ + automatic = 0, + + /** As above, but use policy return_value_policy::reference when the return + value is a pointer. This is the default conversion policy for function + arguments when calling Python functions manually from C++ code (i.e. via + handle::operator()). You probably won't need to use this. */ + automatic_reference, + + /** Reference an existing object (i.e. do not create a new copy) and take + ownership. Python will call the destructor and delete operator when the + object’s reference count reaches zero. Undefined behavior ensues when + the C++ side does the same.. */ + take_ownership, + + /** Create a new copy of the returned object, which will be owned by + Python. This policy is comparably safe because the lifetimes of the two + instances are decoupled. */ + copy, + + /** Use std::move to move the return value contents into a new instance + that will be owned by Python. This policy is comparably safe because the + lifetimes of the two instances (move source and destination) are + decoupled. */ + move, + + /** Reference an existing object, but do not take ownership. The C++ side + is responsible for managing the object’s lifetime and deallocating it + when it is no longer used. Warning: undefined behavior will ensue when + the C++ side deletes an object that is still referenced and used by + Python. */ + reference, + + /** This policy only applies to methods and properties. It references the + object without taking ownership similar to the above + return_value_policy::reference policy. In contrast to that policy, the + function or property’s implicit this argument (called the parent) is + considered to be the the owner of the return value (the child). + pybind11 then couples the lifetime of the parent to the child via a + reference relationship that ensures that the parent cannot be garbage + collected while Python is still using the child. More advanced + variations of this scheme are also possible using combinations of + return_value_policy::reference and the keep_alive call policy */ + reference_internal +}; + +PYBIND11_NAMESPACE_BEGIN(detail) + +inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); } + +// Returns the size as a multiple of sizeof(void *), rounded up. +inline static constexpr size_t size_in_ptrs(size_t s) { return 1 + ((s - 1) >> log2(sizeof(void *))); } + +/** + * The space to allocate for simple layout instance holders (see below) in multiple of the size of + * a pointer (e.g. 2 means 16 bytes on 64-bit architectures). The default is the minimum required + * to holder either a std::unique_ptr or std::shared_ptr (which is almost always + * sizeof(std::shared_ptr)). + */ +constexpr size_t instance_simple_holder_in_ptrs() { + static_assert(sizeof(std::shared_ptr) >= sizeof(std::unique_ptr), + "pybind assumes std::shared_ptrs are at least as big as std::unique_ptrs"); + return size_in_ptrs(sizeof(std::shared_ptr)); +} + +// Forward declarations +struct type_info; +struct value_and_holder; + +struct nonsimple_values_and_holders { + void **values_and_holders; + uint8_t *status; +}; + +/// The 'instance' type which needs to be standard layout (need to be able to use 'offsetof') +struct instance { + PyObject_HEAD + /// Storage for pointers and holder; see simple_layout, below, for a description + union { + void *simple_value_holder[1 + instance_simple_holder_in_ptrs()]; + nonsimple_values_and_holders nonsimple; + }; + /// Weak references + PyObject *weakrefs; + /// If true, the pointer is owned which means we're free to manage it with a holder. + bool owned : 1; + /** + * An instance has two possible value/holder layouts. + * + * Simple layout (when this flag is true), means the `simple_value_holder` is set with a pointer + * and the holder object governing that pointer, i.e. [val1*][holder]. This layout is applied + * whenever there is no python-side multiple inheritance of bound C++ types *and* the type's + * holder will fit in the default space (which is large enough to hold either a std::unique_ptr + * or std::shared_ptr). + * + * Non-simple layout applies when using custom holders that require more space than `shared_ptr` + * (which is typically the size of two pointers), or when multiple inheritance is used on the + * python side. Non-simple layout allocates the required amount of memory to have multiple + * bound C++ classes as parents. Under this layout, `nonsimple.values_and_holders` is set to a + * pointer to allocated space of the required space to hold a sequence of value pointers and + * holders followed `status`, a set of bit flags (1 byte each), i.e. + * [val1*][holder1][val2*][holder2]...[bb...] where each [block] is rounded up to a multiple of + * `sizeof(void *)`. `nonsimple.status` is, for convenience, a pointer to the + * beginning of the [bb...] block (but not independently allocated). + * + * Status bits indicate whether the associated holder is constructed (& + * status_holder_constructed) and whether the value pointer is registered (& + * status_instance_registered) in `registered_instances`. + */ + bool simple_layout : 1; + /// For simple layout, tracks whether the holder has been constructed + bool simple_holder_constructed : 1; + /// For simple layout, tracks whether the instance is registered in `registered_instances` + bool simple_instance_registered : 1; + /// If true, get_internals().patients has an entry for this object + bool has_patients : 1; + + /// Initializes all of the above type/values/holders data (but not the instance values themselves) + void allocate_layout(); + + /// Destroys/deallocates all of the above + void deallocate_layout(); + + /// Returns the value_and_holder wrapper for the given type (or the first, if `find_type` + /// omitted). Returns a default-constructed (with `.inst = nullptr`) object on failure if + /// `throw_if_missing` is false. + value_and_holder get_value_and_holder(const type_info *find_type = nullptr, bool throw_if_missing = true); + + /// Bit values for the non-simple status flags + static constexpr uint8_t status_holder_constructed = 1; + static constexpr uint8_t status_instance_registered = 2; +}; + +static_assert(std::is_standard_layout::value, "Internal error: `pybind11::detail::instance` is not standard layout!"); + +/// from __cpp_future__ import (convenient aliases from C++14/17) +#if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910) +using std::enable_if_t; +using std::conditional_t; +using std::remove_cv_t; +using std::remove_reference_t; +#else +template using enable_if_t = typename std::enable_if::type; +template using conditional_t = typename std::conditional::type; +template using remove_cv_t = typename std::remove_cv::type; +template using remove_reference_t = typename std::remove_reference::type; +#endif + +/// Index sequences +#if defined(PYBIND11_CPP14) +using std::index_sequence; +using std::make_index_sequence; +#else +template struct index_sequence { }; +template struct make_index_sequence_impl : make_index_sequence_impl { }; +template struct make_index_sequence_impl <0, S...> { typedef index_sequence type; }; +template using make_index_sequence = typename make_index_sequence_impl::type; +#endif + +/// Make an index sequence of the indices of true arguments +template struct select_indices_impl { using type = ISeq; }; +template struct select_indices_impl, I, B, Bs...> + : select_indices_impl, index_sequence>, I + 1, Bs...> {}; +template using select_indices = typename select_indices_impl, 0, Bs...>::type; + +/// Backports of std::bool_constant and std::negation to accommodate older compilers +template using bool_constant = std::integral_constant; +template struct negation : bool_constant { }; + +// PGI/Intel cannot detect operator delete with the "compatible" void_t impl, so +// using the new one (C++14 defect, so generally works on newer compilers, even +// if not in C++17 mode) +#if defined(__PGIC__) || defined(__INTEL_COMPILER) +template using void_t = void; +#else +template struct void_t_impl { using type = void; }; +template using void_t = typename void_t_impl::type; +#endif + + +/// Compile-time all/any/none of that check the boolean value of all template types +#if defined(__cpp_fold_expressions) && !(defined(_MSC_VER) && (_MSC_VER < 1916)) +template using all_of = bool_constant<(Ts::value && ...)>; +template using any_of = bool_constant<(Ts::value || ...)>; +#elif !defined(_MSC_VER) +template struct bools {}; +template using all_of = std::is_same< + bools, + bools>; +template using any_of = negation...>>; +#else +// MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit +// at a slight loss of compilation efficiency). +template using all_of = std::conjunction; +template using any_of = std::disjunction; +#endif +template using none_of = negation>; + +template class... Predicates> using satisfies_all_of = all_of...>; +template class... Predicates> using satisfies_any_of = any_of...>; +template class... Predicates> using satisfies_none_of = none_of...>; + +/// Strip the class from a method type +template struct remove_class { }; +template struct remove_class { using type = R (A...); }; +template struct remove_class { using type = R (A...); }; + +/// Helper template to strip away type modifiers +template struct intrinsic_type { using type = T; }; +template struct intrinsic_type { using type = typename intrinsic_type::type; }; +template struct intrinsic_type { using type = typename intrinsic_type::type; }; +template struct intrinsic_type { using type = typename intrinsic_type::type; }; +template struct intrinsic_type { using type = typename intrinsic_type::type; }; +template struct intrinsic_type { using type = typename intrinsic_type::type; }; +template struct intrinsic_type { using type = typename intrinsic_type::type; }; +template using intrinsic_t = typename intrinsic_type::type; + +/// Helper type to replace 'void' in some expressions +struct void_type { }; + +/// Helper template which holds a list of types +template struct type_list { }; + +/// Compile-time integer sum +#ifdef __cpp_fold_expressions +template constexpr size_t constexpr_sum(Ts... ns) { return (0 + ... + size_t{ns}); } +#else +constexpr size_t constexpr_sum() { return 0; } +template +constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); } +#endif + +PYBIND11_NAMESPACE_BEGIN(constexpr_impl) +/// Implementation details for constexpr functions +constexpr int first(int i) { return i; } +template +constexpr int first(int i, T v, Ts... vs) { return v ? i : first(i + 1, vs...); } + +constexpr int last(int /*i*/, int result) { return result; } +template +constexpr int last(int i, int result, T v, Ts... vs) { return last(i + 1, v ? i : result, vs...); } +PYBIND11_NAMESPACE_END(constexpr_impl) + +/// Return the index of the first type in Ts which satisfies Predicate. Returns sizeof...(Ts) if +/// none match. +template class Predicate, typename... Ts> +constexpr int constexpr_first() { return constexpr_impl::first(0, Predicate::value...); } + +/// Return the index of the last type in Ts which satisfies Predicate, or -1 if none match. +template class Predicate, typename... Ts> +constexpr int constexpr_last() { return constexpr_impl::last(0, -1, Predicate::value...); } + +/// Return the Nth element from the parameter pack +template +struct pack_element { using type = typename pack_element::type; }; +template +struct pack_element<0, T, Ts...> { using type = T; }; + +/// Return the one and only type which matches the predicate, or Default if none match. +/// If more than one type matches the predicate, fail at compile-time. +template class Predicate, typename Default, typename... Ts> +struct exactly_one { + static constexpr auto found = constexpr_sum(Predicate::value...); + static_assert(found <= 1, "Found more than one type matching the predicate"); + + static constexpr auto index = found ? constexpr_first() : 0; + using type = conditional_t::type, Default>; +}; +template class P, typename Default> +struct exactly_one { using type = Default; }; + +template class Predicate, typename Default, typename... Ts> +using exactly_one_t = typename exactly_one::type; + +/// Defer the evaluation of type T until types Us are instantiated +template struct deferred_type { using type = T; }; +template using deferred_t = typename deferred_type::type; + +/// Like is_base_of, but requires a strict base (i.e. `is_strict_base_of::value == false`, +/// unlike `std::is_base_of`) +template using is_strict_base_of = bool_constant< + std::is_base_of::value && !std::is_same::value>; + +/// Like is_base_of, but also requires that the base type is accessible (i.e. that a Derived pointer +/// can be converted to a Base pointer) +/// For unions, `is_base_of::value` is False, so we need to check `is_same` as well. +template using is_accessible_base_of = bool_constant< + (std::is_same::value || std::is_base_of::value) && std::is_convertible::value>; + +template class Base> +struct is_template_base_of_impl { + template static std::true_type check(Base *); + static std::false_type check(...); +}; + +/// Check if a template is the base of a type. For example: +/// `is_template_base_of` is true if `struct T : Base {}` where U can be anything +template class Base, typename T> +#if !defined(_MSC_VER) +using is_template_base_of = decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)); +#else // MSVC2015 has trouble with decltype in template aliases +struct is_template_base_of : decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)) { }; +#endif + +/// Check if T is an instantiation of the template `Class`. For example: +/// `is_instantiation` is true if `T == shared_ptr` where U can be anything. +template class Class, typename T> +struct is_instantiation : std::false_type { }; +template class Class, typename... Us> +struct is_instantiation> : std::true_type { }; + +/// Check if T is std::shared_ptr where U can be anything +template using is_shared_ptr = is_instantiation; + +/// Check if T looks like an input iterator +template struct is_input_iterator : std::false_type {}; +template +struct is_input_iterator()), decltype(++std::declval())>> + : std::true_type {}; + +template using is_function_pointer = bool_constant< + std::is_pointer::value && std::is_function::type>::value>; + +template struct strip_function_object { + using type = typename remove_class::type; +}; + +// Extracts the function signature from a function, function pointer or lambda. +template > +using function_signature_t = conditional_t< + std::is_function::value, + F, + typename conditional_t< + std::is_pointer::value || std::is_member_pointer::value, + std::remove_pointer, + strip_function_object + >::type +>; + +/// Returns true if the type looks like a lambda: that is, isn't a function, pointer or member +/// pointer. Note that this can catch all sorts of other things, too; this is intended to be used +/// in a place where passing a lambda makes sense. +template using is_lambda = satisfies_none_of, + std::is_function, std::is_pointer, std::is_member_pointer>; + +/// Ignore that a variable is unused in compiler warnings +inline void ignore_unused(const int *) { } + +/// Apply a function over each element of a parameter pack +#ifdef __cpp_fold_expressions +#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...) +#else +using expand_side_effects = bool[]; +#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (void)pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false } +#endif + +PYBIND11_NAMESPACE_END(detail) + +/// C++ bindings of builtin Python exceptions +class builtin_exception : public std::runtime_error { +public: + using std::runtime_error::runtime_error; + /// Set the error using the Python C API + virtual void set_error() const = 0; +}; + +#define PYBIND11_RUNTIME_EXCEPTION(name, type) \ + class name : public builtin_exception { public: \ + using builtin_exception::builtin_exception; \ + name() : name("") { } \ + void set_error() const override { PyErr_SetString(type, what()); } \ + }; + +PYBIND11_RUNTIME_EXCEPTION(stop_iteration, PyExc_StopIteration) +PYBIND11_RUNTIME_EXCEPTION(index_error, PyExc_IndexError) +PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError) +PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError) +PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError) +PYBIND11_RUNTIME_EXCEPTION(buffer_error, PyExc_BufferError) +PYBIND11_RUNTIME_EXCEPTION(import_error, PyExc_ImportError) +PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error +PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally + +[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); } +[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); } + +template struct format_descriptor { }; + +PYBIND11_NAMESPACE_BEGIN(detail) +// Returns the index of the given type in the type char array below, and in the list in numpy.h +// The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double; +// complex float,double,long double. Note that the long double types only participate when long +// double is actually longer than double (it isn't under MSVC). +// NB: not only the string below but also complex.h and numpy.h rely on this order. +template struct is_fmt_numeric { static constexpr bool value = false; }; +template struct is_fmt_numeric::value>> { + static constexpr bool value = true; + static constexpr int index = std::is_same::value ? 0 : 1 + ( + std::is_integral::value ? detail::log2(sizeof(T))*2 + std::is_unsigned::value : 8 + ( + std::is_same::value ? 1 : std::is_same::value ? 2 : 0)); +}; +PYBIND11_NAMESPACE_END(detail) + +template struct format_descriptor::value>> { + static constexpr const char c = "?bBhHiIqQfdg"[detail::is_fmt_numeric::index]; + static constexpr const char value[2] = { c, '\0' }; + static std::string format() { return std::string(1, c); } +}; + +#if !defined(PYBIND11_CPP17) + +template constexpr const char format_descriptor< + T, detail::enable_if_t::value>>::value[2]; + +#endif + +/// RAII wrapper that temporarily clears any Python error state +struct error_scope { + PyObject *type, *value, *trace; + error_scope() { PyErr_Fetch(&type, &value, &trace); } + ~error_scope() { PyErr_Restore(type, value, trace); } +}; + +/// Dummy destructor wrapper that can be used to expose classes with a private destructor +struct nodelete { template void operator()(T*) { } }; + +PYBIND11_NAMESPACE_BEGIN(detail) +template +struct overload_cast_impl { + constexpr overload_cast_impl() {}; // NOLINT(modernize-use-equals-default): MSVC 2015 needs this + + template + constexpr auto operator()(Return (*pf)(Args...)) const noexcept + -> decltype(pf) { return pf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept + -> decltype(pmf) { return pmf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept + -> decltype(pmf) { return pmf; } +}; +PYBIND11_NAMESPACE_END(detail) + +// overload_cast requires variable templates: C++14 +#if defined(PYBIND11_CPP14) +#define PYBIND11_OVERLOAD_CAST 1 +/// Syntax sugar for resolving overloaded function pointers: +/// - regular: static_cast(&Class::func) +/// - sweet: overload_cast(&Class::func) +template +static constexpr detail::overload_cast_impl overload_cast = {}; +// MSVC 2015 only accepts this particular initialization syntax for this variable template. +#endif + +/// Const member function selector for overload_cast +/// - regular: static_cast(&Class::func) +/// - sweet: overload_cast(&Class::func, const_) +static constexpr auto const_ = std::true_type{}; + +#if !defined(PYBIND11_CPP14) // no overload_cast: providing something that static_assert-fails: +template struct overload_cast { + static_assert(detail::deferred_t::value, + "pybind11::overload_cast<...> requires compiling in C++14 mode"); +}; +#endif // overload_cast + +PYBIND11_NAMESPACE_BEGIN(detail) + +// Adaptor for converting arbitrary container arguments into a vector; implicitly convertible from +// any standard container (or C-style array) supporting std::begin/std::end, any singleton +// arithmetic type (if T is arithmetic), or explicitly constructible from an iterator pair. +template +class any_container { + std::vector v; +public: + any_container() = default; + + // Can construct from a pair of iterators + template ::value>> + any_container(It first, It last) : v(first, last) { } + + // Implicit conversion constructor from any arbitrary container type with values convertible to T + template ())), T>::value>> + any_container(const Container &c) : any_container(std::begin(c), std::end(c)) { } + + // initializer_list's aren't deducible, so don't get matched by the above template; we need this + // to explicitly allow implicit conversion from one: + template ::value>> + any_container(const std::initializer_list &c) : any_container(c.begin(), c.end()) { } + + // Avoid copying if given an rvalue vector of the correct type. + any_container(std::vector &&v) : v(std::move(v)) { } + + // Moves the vector out of an rvalue any_container + operator std::vector &&() && { return std::move(v); } + + // Dereferencing obtains a reference to the underlying vector + std::vector &operator*() { return v; } + const std::vector &operator*() const { return v; } + + // -> lets you call methods on the underlying vector + std::vector *operator->() { return &v; } + const std::vector *operator->() const { return &v; } +}; + +// Forward-declaration; see detail/class.h +std::string get_fully_qualified_tp_name(PyTypeObject*); + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/detail/descr.h b/extern/pybind/include/pybind11/detail/descr.h new file mode 100644 index 00000000..92720cd5 --- /dev/null +++ b/extern/pybind/include/pybind11/detail/descr.h @@ -0,0 +1,100 @@ +/* + pybind11/detail/descr.h: Helper type for concatenating type signatures at compile time + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "common.h" + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +#if !defined(_MSC_VER) +# define PYBIND11_DESCR_CONSTEXPR static constexpr +#else +# define PYBIND11_DESCR_CONSTEXPR const +#endif + +/* Concatenate type signatures at compile time */ +template +struct descr { + char text[N + 1]; + + constexpr descr() : text{'\0'} { } + constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence()) { } + + template + constexpr descr(char const (&s)[N+1], index_sequence) : text{s[Is]..., '\0'} { } + + template + constexpr descr(char c, Chars... cs) : text{c, static_cast(cs)..., '\0'} { } + + static constexpr std::array types() { + return {{&typeid(Ts)..., nullptr}}; + } +}; + +template +constexpr descr plus_impl(const descr &a, const descr &b, + index_sequence, index_sequence) { + return {a.text[Is1]..., b.text[Is2]...}; +} + +template +constexpr descr operator+(const descr &a, const descr &b) { + return plus_impl(a, b, make_index_sequence(), make_index_sequence()); +} + +template +constexpr descr _(char const(&text)[N]) { return descr(text); } +constexpr descr<0> _(char const(&)[1]) { return {}; } + +template struct int_to_str : int_to_str { }; +template struct int_to_str<0, Digits...> { + static constexpr auto digits = descr(('0' + Digits)...); +}; + +// Ternary description (like std::conditional) +template +constexpr enable_if_t> _(char const(&text1)[N1], char const(&)[N2]) { + return _(text1); +} +template +constexpr enable_if_t> _(char const(&)[N1], char const(&text2)[N2]) { + return _(text2); +} + +template +constexpr enable_if_t _(const T1 &d, const T2 &) { return d; } +template +constexpr enable_if_t _(const T1 &, const T2 &d) { return d; } + +template auto constexpr _() -> decltype(int_to_str::digits) { + return int_to_str::digits; +} + +template constexpr descr<1, Type> _() { return {'%'}; } + +constexpr descr<0> concat() { return {}; } + +template +constexpr descr concat(const descr &descr) { return descr; } + +template +constexpr auto concat(const descr &d, const Args &...args) + -> decltype(std::declval>() + concat(args...)) { + return d + _(", ") + concat(args...); +} + +template +constexpr descr type_descr(const descr &descr) { + return _("{") + descr + _("}"); +} + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/detail/init.h b/extern/pybind/include/pybind11/detail/init.h new file mode 100644 index 00000000..3ef78c11 --- /dev/null +++ b/extern/pybind/include/pybind11/detail/init.h @@ -0,0 +1,336 @@ +/* + pybind11/detail/init.h: init factory function implementation and support code. + + Copyright (c) 2017 Jason Rhinelander + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "class.h" + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +template <> +class type_caster { +public: + bool load(handle h, bool) { + value = reinterpret_cast(h.ptr()); + return true; + } + + template using cast_op_type = value_and_holder &; + operator value_and_holder &() { return *value; } + static constexpr auto name = _(); + +private: + value_and_holder *value = nullptr; +}; + +PYBIND11_NAMESPACE_BEGIN(initimpl) + +inline void no_nullptr(void *ptr) { + if (!ptr) throw type_error("pybind11::init(): factory function returned nullptr"); +} + +// Implementing functions for all forms of py::init<...> and py::init(...) +template using Cpp = typename Class::type; +template using Alias = typename Class::type_alias; +template using Holder = typename Class::holder_type; + +template using is_alias_constructible = std::is_constructible, Cpp &&>; + +// Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance. +template = 0> +bool is_alias(Cpp *ptr) { + return dynamic_cast *>(ptr) != nullptr; +} +// Failing fallback version of the above for a no-alias class (always returns false) +template +constexpr bool is_alias(void *) { return false; } + +// Constructs and returns a new object; if the given arguments don't map to a constructor, we fall +// back to brace aggregate initiailization so that for aggregate initialization can be used with +// py::init, e.g. `py::init` to initialize a `struct T { int a; int b; }`. For +// non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually +// works, but will not do the expected thing when `T` has an `initializer_list` constructor). +template ::value, int> = 0> +inline Class *construct_or_initialize(Args &&...args) { return new Class(std::forward(args)...); } +template ::value, int> = 0> +inline Class *construct_or_initialize(Args &&...args) { return new Class{std::forward(args)...}; } + +// Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with +// an alias to provide only a single Cpp factory function as long as the Alias can be +// constructed from an rvalue reference of the base Cpp type. This means that Alias classes +// can, when appropriate, simply define a `Alias(Cpp &&)` constructor rather than needing to +// inherit all the base class constructors. +template +void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/, + value_and_holder &v_h, Cpp &&base) { + v_h.value_ptr() = new Alias(std::move(base)); +} +template +[[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/, + value_and_holder &, Cpp &&) { + throw type_error("pybind11::init(): unable to convert returned instance to required " + "alias class: no `Alias(Class &&)` constructor available"); +} + +// Error-generating fallback for factories that don't match one of the below construction +// mechanisms. +template +void construct(...) { + static_assert(!std::is_same::value /* always false */, + "pybind11::init(): init function must return a compatible pointer, " + "holder, or value"); +} + +// Pointer return v1: the factory function returns a class pointer for a registered class. +// If we don't need an alias (because this class doesn't have one, or because the final type is +// inherited on the Python side) we can simply take over ownership. Otherwise we need to try to +// construct an Alias from the returned base instance. +template +void construct(value_and_holder &v_h, Cpp *ptr, bool need_alias) { + no_nullptr(ptr); + if (Class::has_alias && need_alias && !is_alias(ptr)) { + // We're going to try to construct an alias by moving the cpp type. Whether or not + // that succeeds, we still need to destroy the original cpp pointer (either the + // moved away leftover, if the alias construction works, or the value itself if we + // throw an error), but we can't just call `delete ptr`: it might have a special + // deleter, or might be shared_from_this. So we construct a holder around it as if + // it was a normal instance, then steal the holder away into a local variable; thus + // the holder and destruction happens when we leave the C++ scope, and the holder + // class gets to handle the destruction however it likes. + v_h.value_ptr() = ptr; + v_h.set_instance_registered(true); // To prevent init_instance from registering it + v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder + Holder temp_holder(std::move(v_h.holder>())); // Steal the holder + v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null + v_h.set_instance_registered(false); + + construct_alias_from_cpp(is_alias_constructible{}, v_h, std::move(*ptr)); + } else { + // Otherwise the type isn't inherited, so we don't need an Alias + v_h.value_ptr() = ptr; + } +} + +// Pointer return v2: a factory that always returns an alias instance ptr. We simply take over +// ownership of the pointer. +template = 0> +void construct(value_and_holder &v_h, Alias *alias_ptr, bool) { + no_nullptr(alias_ptr); + v_h.value_ptr() = static_cast *>(alias_ptr); +} + +// Holder return: copy its pointer, and move or copy the returned holder into the new instance's +// holder. This also handles types like std::shared_ptr and std::unique_ptr where T is a +// derived type (through those holder's implicit conversion from derived class holder constructors). +template +void construct(value_and_holder &v_h, Holder holder, bool need_alias) { + auto *ptr = holder_helper>::get(holder); + no_nullptr(ptr); + // If we need an alias, check that the held pointer is actually an alias instance + if (Class::has_alias && need_alias && !is_alias(ptr)) + throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance " + "is not an alias instance"); + + v_h.value_ptr() = ptr; + v_h.type->init_instance(v_h.inst, &holder); +} + +// return-by-value version 1: returning a cpp class by value. If the class has an alias and an +// alias is required the alias must have an `Alias(Cpp &&)` constructor so that we can construct +// the alias from the base when needed (i.e. because of Python-side inheritance). When we don't +// need it, we simply move-construct the cpp value into a new instance. +template +void construct(value_and_holder &v_h, Cpp &&result, bool need_alias) { + static_assert(std::is_move_constructible>::value, + "pybind11::init() return-by-value factory function requires a movable class"); + if (Class::has_alias && need_alias) + construct_alias_from_cpp(is_alias_constructible{}, v_h, std::move(result)); + else + v_h.value_ptr() = new Cpp(std::move(result)); +} + +// return-by-value version 2: returning a value of the alias type itself. We move-construct an +// Alias instance (even if no the python-side inheritance is involved). The is intended for +// cases where Alias initialization is always desired. +template +void construct(value_and_holder &v_h, Alias &&result, bool) { + static_assert(std::is_move_constructible>::value, + "pybind11::init() return-by-alias-value factory function requires a movable alias class"); + v_h.value_ptr() = new Alias(std::move(result)); +} + +// Implementing class for py::init<...>() +template +struct constructor { + template = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } + + template , Args...>::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + if (Py_TYPE(v_h.inst) == v_h.type->type) + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + else + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } + + template , Args...>::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } +}; + +// Implementing class for py::init_alias<...>() +template struct alias_constructor { + template , Args...>::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } +}; + +// Implementation class for py::init(Func) and py::init(Func, AliasFunc) +template , typename = function_signature_t> +struct factory; + +// Specialization for py::init(Func) +template +struct factory { + remove_reference_t class_factory; + + factory(Func &&f) : class_factory(std::forward(f)) { } + + // The given class either has no alias or has no separate alias factory; + // this always constructs the class itself. If the class is registered with an alias + // type and an alias instance is needed (i.e. because the final type is a Python class + // inheriting from the C++ type) the returned value needs to either already be an alias + // instance, or the alias needs to be constructible from a `Class &&` argument. + template + void execute(Class &cl, const Extra &...extra) && { + #if defined(PYBIND11_CPP14) + cl.def("__init__", [func = std::move(class_factory)] + #else + auto &func = class_factory; + cl.def("__init__", [func] + #endif + (value_and_holder &v_h, Args... args) { + construct(v_h, func(std::forward(args)...), + Py_TYPE(v_h.inst) != v_h.type->type); + }, is_new_style_constructor(), extra...); + } +}; + +// Specialization for py::init(Func, AliasFunc) +template +struct factory { + static_assert(sizeof...(CArgs) == sizeof...(AArgs), + "pybind11::init(class_factory, alias_factory): class and alias factories " + "must have identical argument signatures"); + static_assert(all_of...>::value, + "pybind11::init(class_factory, alias_factory): class and alias factories " + "must have identical argument signatures"); + + remove_reference_t class_factory; + remove_reference_t alias_factory; + + factory(CFunc &&c, AFunc &&a) + : class_factory(std::forward(c)), alias_factory(std::forward(a)) { } + + // The class factory is called when the `self` type passed to `__init__` is the direct + // class (i.e. not inherited), the alias factory when `self` is a Python-side subtype. + template + void execute(Class &cl, const Extra&... extra) && { + static_assert(Class::has_alias, "The two-argument version of `py::init()` can " + "only be used if the class has an alias"); + #if defined(PYBIND11_CPP14) + cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)] + #else + auto &class_func = class_factory; + auto &alias_func = alias_factory; + cl.def("__init__", [class_func, alias_func] + #endif + (value_and_holder &v_h, CArgs... args) { + if (Py_TYPE(v_h.inst) == v_h.type->type) + // If the instance type equals the registered type we don't have inheritance, so + // don't need the alias and can construct using the class function: + construct(v_h, class_func(std::forward(args)...), false); + else + construct(v_h, alias_func(std::forward(args)...), true); + }, is_new_style_constructor(), extra...); + } +}; + +/// Set just the C++ state. Same as `__init__`. +template +void setstate(value_and_holder &v_h, T &&result, bool need_alias) { + construct(v_h, std::forward(result), need_alias); +} + +/// Set both the C++ and Python states +template ::value, int> = 0> +void setstate(value_and_holder &v_h, std::pair &&result, bool need_alias) { + construct(v_h, std::move(result.first), need_alias); + setattr((PyObject *) v_h.inst, "__dict__", result.second); +} + +/// Implementation for py::pickle(GetState, SetState) +template , typename = function_signature_t> +struct pickle_factory; + +template +struct pickle_factory { + static_assert(std::is_same, intrinsic_t>::value, + "The type returned by `__getstate__` must be the same " + "as the argument accepted by `__setstate__`"); + + remove_reference_t get; + remove_reference_t set; + + pickle_factory(Get get, Set set) + : get(std::forward(get)), set(std::forward(set)) { } + + template + void execute(Class &cl, const Extra &...extra) && { + cl.def("__getstate__", std::move(get)); + +#if defined(PYBIND11_CPP14) + cl.def("__setstate__", [func = std::move(set)] +#else + auto &func = set; + cl.def("__setstate__", [func] +#endif + (value_and_holder &v_h, ArgState state) { + setstate(v_h, func(std::forward(state)), + Py_TYPE(v_h.inst) != v_h.type->type); + }, is_new_style_constructor(), extra...); + } +}; + +PYBIND11_NAMESPACE_END(initimpl) +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(pybind11) diff --git a/extern/pybind/include/pybind11/detail/internals.h b/extern/pybind/include/pybind11/detail/internals.h new file mode 100644 index 00000000..a455715b --- /dev/null +++ b/extern/pybind/include/pybind11/detail/internals.h @@ -0,0 +1,363 @@ +/* + pybind11/detail/internals.h: Internal data structure and related functions + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "../pytypes.h" + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) +// Forward declarations +inline PyTypeObject *make_static_property_type(); +inline PyTypeObject *make_default_metaclass(); +inline PyObject *make_object_base_type(PyTypeObject *metaclass); + +// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new +// Thread Specific Storage (TSS) API. +#if PY_VERSION_HEX >= 0x03070000 +# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr +# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key)) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value)) +# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr) +# define PYBIND11_TLS_FREE(key) PyThread_tss_free(key) +#else + // Usually an int but a long on Cygwin64 with Python 3.x +# define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0 +# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) +# if PY_MAJOR_VERSION < 3 +# define PYBIND11_TLS_DELETE_VALUE(key) \ + PyThread_delete_key_value(key) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ + do { \ + PyThread_delete_key_value((key)); \ + PyThread_set_key_value((key), (value)); \ + } while (false) +# else +# define PYBIND11_TLS_DELETE_VALUE(key) \ + PyThread_set_key_value((key), nullptr) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ + PyThread_set_key_value((key), (value)) +# endif +# define PYBIND11_TLS_FREE(key) (void)key +#endif + +// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly +// other STLs, this means `typeid(A)` from one module won't equal `typeid(A)` from another module +// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under +// libstdc++, this doesn't happen: equality and the type_index hash are based on the type name, +// which works. If not under a known-good stl, provide our own name-based hash and equality +// functions that use the type name. +#if defined(__GLIBCXX__) +inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; } +using type_hash = std::hash; +using type_equal_to = std::equal_to; +#else +inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { + return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; +} + +struct type_hash { + size_t operator()(const std::type_index &t) const { + size_t hash = 5381; + const char *ptr = t.name(); + while (auto c = static_cast(*ptr++)) + hash = (hash * 33) ^ c; + return hash; + } +}; + +struct type_equal_to { + bool operator()(const std::type_index &lhs, const std::type_index &rhs) const { + return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; + } +}; +#endif + +template +using type_map = std::unordered_map; + +struct override_hash { + inline size_t operator()(const std::pair& v) const { + size_t value = std::hash()(v.first); + value ^= std::hash()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2); + return value; + } +}; + +/// Internal data structure used to track registered instances and types. +/// Whenever binary incompatible changes are made to this structure, +/// `PYBIND11_INTERNALS_VERSION` must be incremented. +struct internals { + type_map registered_types_cpp; // std::type_index -> pybind11's type information + std::unordered_map> registered_types_py; // PyTypeObject* -> base type_info(s) + std::unordered_multimap registered_instances; // void * -> instance* + std::unordered_set, override_hash> inactive_override_cache; + type_map> direct_conversions; + std::unordered_map> patients; + std::forward_list registered_exception_translators; + std::unordered_map shared_data; // Custom data to be shared across extensions + std::vector loader_patient_stack; // Used by `loader_life_support` + std::forward_list static_strings; // Stores the std::strings backing detail::c_str() + PyTypeObject *static_property_type; + PyTypeObject *default_metaclass; + PyObject *instance_base; +#if defined(WITH_THREAD) + PYBIND11_TLS_KEY_INIT(tstate); + PyInterpreterState *istate = nullptr; + ~internals() { + // This destructor is called *after* Py_Finalize() in finalize_interpreter(). + // That *SHOULD BE* fine. The following details what happens whe PyThread_tss_free is called. + // PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does nothing. + // PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree. + // PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX). Neither + // of those have anything to do with CPython internals. + // PyMem_RawFree *requires* that the `tstate` be allocated with the CPython allocator. + PYBIND11_TLS_FREE(tstate); + } +#endif +}; + +/// Additional type information which does not fit into the PyTypeObject. +/// Changes to this struct also require bumping `PYBIND11_INTERNALS_VERSION`. +struct type_info { + PyTypeObject *type; + const std::type_info *cpptype; + size_t type_size, type_align, holder_size_in_ptrs; + void *(*operator_new)(size_t); + void (*init_instance)(instance *, const void *); + void (*dealloc)(value_and_holder &v_h); + std::vector implicit_conversions; + std::vector> implicit_casts; + std::vector *direct_conversions; + buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; + void *get_buffer_data = nullptr; + void *(*module_local_load)(PyObject *, const type_info *) = nullptr; + /* A simple type never occurs as a (direct or indirect) parent + * of a class that makes use of multiple inheritance */ + bool simple_type : 1; + /* True if there is no multiple inheritance in this type's inheritance tree */ + bool simple_ancestors : 1; + /* for base vs derived holder_type checks */ + bool default_holder : 1; + /* true if this is a type registered with py::module_local */ + bool module_local : 1; +}; + +/// Tracks the `internals` and `type_info` ABI version independent of the main library version +#define PYBIND11_INTERNALS_VERSION 4 + +/// On MSVC, debug and release builds are not ABI-compatible! +#if defined(_MSC_VER) && defined(_DEBUG) +# define PYBIND11_BUILD_TYPE "_debug" +#else +# define PYBIND11_BUILD_TYPE "" +#endif + +/// Let's assume that different compilers are ABI-incompatible. +/// A user can manually set this string if they know their +/// compiler is compatible. +#ifndef PYBIND11_COMPILER_TYPE +# if defined(_MSC_VER) +# define PYBIND11_COMPILER_TYPE "_msvc" +# elif defined(__INTEL_COMPILER) +# define PYBIND11_COMPILER_TYPE "_icc" +# elif defined(__clang__) +# define PYBIND11_COMPILER_TYPE "_clang" +# elif defined(__PGI) +# define PYBIND11_COMPILER_TYPE "_pgi" +# elif defined(__MINGW32__) +# define PYBIND11_COMPILER_TYPE "_mingw" +# elif defined(__CYGWIN__) +# define PYBIND11_COMPILER_TYPE "_gcc_cygwin" +# elif defined(__GNUC__) +# define PYBIND11_COMPILER_TYPE "_gcc" +# else +# define PYBIND11_COMPILER_TYPE "_unknown" +# endif +#endif + +/// Also standard libs +#ifndef PYBIND11_STDLIB +# if defined(_LIBCPP_VERSION) +# define PYBIND11_STDLIB "_libcpp" +# elif defined(__GLIBCXX__) || defined(__GLIBCPP__) +# define PYBIND11_STDLIB "_libstdcpp" +# else +# define PYBIND11_STDLIB "" +# endif +#endif + +/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility. +#ifndef PYBIND11_BUILD_ABI +# if defined(__GXX_ABI_VERSION) +# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION) +# else +# define PYBIND11_BUILD_ABI "" +# endif +#endif + +#ifndef PYBIND11_INTERNALS_KIND +# if defined(WITH_THREAD) +# define PYBIND11_INTERNALS_KIND "" +# else +# define PYBIND11_INTERNALS_KIND "_without_thread" +# endif +#endif + +#define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \ + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" + +#define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \ + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" + +/// Each module locally stores a pointer to the `internals` data. The data +/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. +inline internals **&get_internals_pp() { + static internals **internals_pp = nullptr; + return internals_pp; +} + +inline void translate_exception(std::exception_ptr p) { + try { + if (p) std::rethrow_exception(p); + } catch (error_already_set &e) { e.restore(); return; + } catch (const builtin_exception &e) { e.set_error(); return; + } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return; + } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return; + } catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::overflow_error &e) { PyErr_SetString(PyExc_OverflowError, e.what()); return; + } catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return; + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); + return; + } +} + +#if !defined(__GLIBCXX__) +inline void translate_local_exception(std::exception_ptr p) { + try { + if (p) std::rethrow_exception(p); + } catch (error_already_set &e) { e.restore(); return; + } catch (const builtin_exception &e) { e.set_error(); return; + } +} +#endif + +/// Return a reference to the current `internals` data +PYBIND11_NOINLINE inline internals &get_internals() { + auto **&internals_pp = get_internals_pp(); + if (internals_pp && *internals_pp) + return **internals_pp; + + // Ensure that the GIL is held since we will need to make Python calls. + // Cannot use py::gil_scoped_acquire here since that constructor calls get_internals. + struct gil_scoped_acquire_local { + gil_scoped_acquire_local() : state (PyGILState_Ensure()) {} + ~gil_scoped_acquire_local() { PyGILState_Release(state); } + const PyGILState_STATE state; + } gil; + + constexpr auto *id = PYBIND11_INTERNALS_ID; + auto builtins = handle(PyEval_GetBuiltins()); + if (builtins.contains(id) && isinstance(builtins[id])) { + internals_pp = static_cast(capsule(builtins[id])); + + // We loaded builtins through python's builtins, which means that our `error_already_set` + // and `builtin_exception` may be different local classes than the ones set up in the + // initial exception translator, below, so add another for our local exception classes. + // + // libstdc++ doesn't require this (types there are identified only by name) +#if !defined(__GLIBCXX__) + (*internals_pp)->registered_exception_translators.push_front(&translate_local_exception); +#endif + } else { + if (!internals_pp) internals_pp = new internals*(); + auto *&internals_ptr = *internals_pp; + internals_ptr = new internals(); +#if defined(WITH_THREAD) + + #if PY_VERSION_HEX < 0x03090000 + PyEval_InitThreads(); + #endif + PyThreadState *tstate = PyThreadState_Get(); + #if PY_VERSION_HEX >= 0x03070000 + internals_ptr->tstate = PyThread_tss_alloc(); + if (!internals_ptr->tstate || PyThread_tss_create(internals_ptr->tstate)) + pybind11_fail("get_internals: could not successfully initialize the TSS key!"); + PyThread_tss_set(internals_ptr->tstate, tstate); + #else + internals_ptr->tstate = PyThread_create_key(); + if (internals_ptr->tstate == -1) + pybind11_fail("get_internals: could not successfully initialize the TLS key!"); + PyThread_set_key_value(internals_ptr->tstate, tstate); + #endif + internals_ptr->istate = tstate->interp; +#endif + builtins[id] = capsule(internals_pp); + internals_ptr->registered_exception_translators.push_front(&translate_exception); + internals_ptr->static_property_type = make_static_property_type(); + internals_ptr->default_metaclass = make_default_metaclass(); + internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass); + } + return **internals_pp; +} + +/// Works like `internals.registered_types_cpp`, but for module-local registered types: +inline type_map ®istered_local_types_cpp() { + static type_map locals{}; + return locals; +} + +/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its +/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only +/// cleared when the program exits or after interpreter shutdown (when embedding), and so are +/// suitable for c-style strings needed by Python internals (such as PyTypeObject's tp_name). +template +const char *c_str(Args &&...args) { + auto &strings = get_internals().static_strings; + strings.emplace_front(std::forward(args)...); + return strings.front().c_str(); +} + +PYBIND11_NAMESPACE_END(detail) + +/// Returns a named pointer that is shared among all extension modules (using the same +/// pybind11 version) running in the current interpreter. Names starting with underscores +/// are reserved for internal usage. Returns `nullptr` if no matching entry was found. +inline PYBIND11_NOINLINE void *get_shared_data(const std::string &name) { + auto &internals = detail::get_internals(); + auto it = internals.shared_data.find(name); + return it != internals.shared_data.end() ? it->second : nullptr; +} + +/// Set the shared data that can be later recovered by `get_shared_data()`. +inline PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) { + detail::get_internals().shared_data[name] = data; + return data; +} + +/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if +/// such entry exists. Otherwise, a new object of default-constructible type `T` is +/// added to the shared data under the given name and a reference to it is returned. +template +T &get_or_create_shared_data(const std::string &name) { + auto &internals = detail::get_internals(); + auto it = internals.shared_data.find(name); + T *ptr = (T *) (it != internals.shared_data.end() ? it->second : nullptr); + if (!ptr) { + ptr = new T(); + internals.shared_data[name] = ptr; + } + return *ptr; +} + +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/detail/typeid.h b/extern/pybind/include/pybind11/detail/typeid.h new file mode 100644 index 00000000..148889ff --- /dev/null +++ b/extern/pybind/include/pybind11/detail/typeid.h @@ -0,0 +1,55 @@ +/* + pybind11/detail/typeid.h: Compiler-independent access to type identifiers + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include +#include + +#if defined(__GNUG__) +#include +#endif + +#include "common.h" + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) +/// Erase all occurrences of a substring +inline void erase_all(std::string &string, const std::string &search) { + for (size_t pos = 0;;) { + pos = string.find(search, pos); + if (pos == std::string::npos) break; + string.erase(pos, search.length()); + } +} + +PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { +#if defined(__GNUG__) + int status = 0; + std::unique_ptr res { + abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; + if (status == 0) + name = res.get(); +#else + detail::erase_all(name, "class "); + detail::erase_all(name, "struct "); + detail::erase_all(name, "enum "); +#endif + detail::erase_all(name, "pybind11::"); +} +PYBIND11_NAMESPACE_END(detail) + +/// Return a string representation of a C++ type +template static std::string type_id() { + std::string name(typeid(T).name()); + detail::clean_type_id(name); + return name; +} + +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/eigen.h b/extern/pybind/include/pybind11/eigen.h new file mode 100644 index 00000000..58582b2c --- /dev/null +++ b/extern/pybind/include/pybind11/eigen.h @@ -0,0 +1,607 @@ +/* + pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "numpy.h" + +#if defined(__INTEL_COMPILER) +# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) +#elif defined(__GNUG__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# ifdef __clang__ +// Eigen generates a bunch of implicit-copy-constructor-is-deprecated warnings with -Wdeprecated +// under Clang, so disable that warning here: +# pragma GCC diagnostic ignored "-Wdeprecated" +# endif +# if __GNUC__ >= 7 +# pragma GCC diagnostic ignored "-Wint-in-bool-context" +# endif +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +# pragma warning(disable: 4996) // warning C4996: std::unary_negate is deprecated in C++17 +#endif + +#include +#include + +// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit +// move constructors that break things. We could detect this an explicitly copy, but an extra copy +// of matrices seems highly undesirable. +static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7"); + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides: +using EigenDStride = Eigen::Stride; +template using EigenDRef = Eigen::Ref; +template using EigenDMap = Eigen::Map; + +PYBIND11_NAMESPACE_BEGIN(detail) + +#if EIGEN_VERSION_AT_LEAST(3,3,0) +using EigenIndex = Eigen::Index; +#else +using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE; +#endif + +// Matches Eigen::Map, Eigen::Ref, blocks, etc: +template using is_eigen_dense_map = all_of, std::is_base_of, T>>; +template using is_eigen_mutable_map = std::is_base_of, T>; +template using is_eigen_dense_plain = all_of>, is_template_base_of>; +template using is_eigen_sparse = is_template_base_of; +// Test for objects inheriting from EigenBase that aren't captured by the above. This +// basically covers anything that can be assigned to a dense matrix but that don't have a typical +// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and +// SelfAdjointView fall into this category. +template using is_eigen_other = all_of< + is_template_base_of, + negation, is_eigen_dense_plain, is_eigen_sparse>> +>; + +// Captures numpy/eigen conformability status (returned by EigenProps::conformable()): +template struct EigenConformable { + bool conformable = false; + EigenIndex rows = 0, cols = 0; + EigenDStride stride{0, 0}; // Only valid if negativestrides is false! + bool negativestrides = false; // If true, do not use stride! + + EigenConformable(bool fits = false) : conformable{fits} {} + // Matrix type: + EigenConformable(EigenIndex r, EigenIndex c, + EigenIndex rstride, EigenIndex cstride) : + conformable{true}, rows{r}, cols{c} { + // TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747 + if (rstride < 0 || cstride < 0) { + negativestrides = true; + } else { + stride = {EigenRowMajor ? rstride : cstride /* outer stride */, + EigenRowMajor ? cstride : rstride /* inner stride */ }; + } + } + // Vector type: + EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride) + : EigenConformable(r, c, r == 1 ? c*stride : stride, c == 1 ? r : r*stride) {} + + template bool stride_compatible() const { + // To have compatible strides, we need (on both dimensions) one of fully dynamic strides, + // matching strides, or a dimension size of 1 (in which case the stride value is irrelevant) + return + !negativestrides && + (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() || + (EigenRowMajor ? cols : rows) == 1) && + (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || + (EigenRowMajor ? rows : cols) == 1); + } + operator bool() const { return conformable; } +}; + +template struct eigen_extract_stride { using type = Type; }; +template +struct eigen_extract_stride> { using type = StrideType; }; +template +struct eigen_extract_stride> { using type = StrideType; }; + +// Helper struct for extracting information from an Eigen type +template struct EigenProps { + using Type = Type_; + using Scalar = typename Type::Scalar; + using StrideType = typename eigen_extract_stride::type; + static constexpr EigenIndex + rows = Type::RowsAtCompileTime, + cols = Type::ColsAtCompileTime, + size = Type::SizeAtCompileTime; + static constexpr bool + row_major = Type::IsRowMajor, + vector = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1 + fixed_rows = rows != Eigen::Dynamic, + fixed_cols = cols != Eigen::Dynamic, + fixed = size != Eigen::Dynamic, // Fully-fixed size + dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size + + template using if_zero = std::integral_constant; + static constexpr EigenIndex inner_stride = if_zero::value, + outer_stride = if_zero::value; + static constexpr bool dynamic_stride = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic; + static constexpr bool requires_row_major = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1; + static constexpr bool requires_col_major = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1; + + // Takes an input array and determines whether we can make it fit into the Eigen type. If + // the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector + // (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type). + static EigenConformable conformable(const array &a) { + const auto dims = a.ndim(); + if (dims < 1 || dims > 2) + return false; + + if (dims == 2) { // Matrix type: require exact match (or dynamic) + + EigenIndex + np_rows = a.shape(0), + np_cols = a.shape(1), + np_rstride = a.strides(0) / static_cast(sizeof(Scalar)), + np_cstride = a.strides(1) / static_cast(sizeof(Scalar)); + if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols)) + return false; + + return {np_rows, np_cols, np_rstride, np_cstride}; + } + + // Otherwise we're storing an n-vector. Only one of the strides will be used, but whichever + // is used, we want the (single) numpy stride value. + const EigenIndex n = a.shape(0), + stride = a.strides(0) / static_cast(sizeof(Scalar)); + + if (vector) { // Eigen type is a compile-time vector + if (fixed && size != n) + return false; // Vector size mismatch + return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; + } + else if (fixed) { + // The type has a fixed size, but is not a vector: abort + return false; + } + else if (fixed_cols) { + // Since this isn't a vector, cols must be != 1. We allow this only if it exactly + // equals the number of elements (rows is Dynamic, and so 1 row is allowed). + if (cols != n) return false; + return {1, n, stride}; + } + else { + // Otherwise it's either fully dynamic, or column dynamic; both become a column vector + if (fixed_rows && rows != n) return false; + return {n, 1, stride}; + } + } + + static constexpr bool show_writeable = is_eigen_dense_map::value && is_eigen_mutable_map::value; + static constexpr bool show_order = is_eigen_dense_map::value; + static constexpr bool show_c_contiguous = show_order && requires_row_major; + static constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; + + static constexpr auto descriptor = + _("numpy.ndarray[") + npy_format_descriptor::name + + _("[") + _(_<(size_t) rows>(), _("m")) + + _(", ") + _(_<(size_t) cols>(), _("n")) + + _("]") + + // For a reference type (e.g. Ref) we have other constraints that might need to be + // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride + // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output + // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to + // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you + // *gave* a numpy.ndarray of the right type and dimensions. + _(", flags.writeable", "") + + _(", flags.c_contiguous", "") + + _(", flags.f_contiguous", "") + + _("]"); +}; + +// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data, +// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array. +template handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) { + constexpr ssize_t elem_size = sizeof(typename props::Scalar); + array a; + if (props::vector) + a = array({ src.size() }, { elem_size * src.innerStride() }, src.data(), base); + else + a = array({ src.rows(), src.cols() }, { elem_size * src.rowStride(), elem_size * src.colStride() }, + src.data(), base); + + if (!writeable) + array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_; + + return a.release(); +} + +// Takes an lvalue ref to some Eigen type and a (python) base object, creating a numpy array that +// reference the Eigen object's data with `base` as the python-registered base class (if omitted, +// the base will be set to None, and lifetime management is up to the caller). The numpy array is +// non-writeable if the given type is const. +template +handle eigen_ref_array(Type &src, handle parent = none()) { + // none here is to get past array's should-we-copy detection, which currently always + // copies when there is no base. Setting the base to None should be harmless. + return eigen_array_cast(src, parent, !std::is_const::value); +} + +// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a numpy +// array that references the encapsulated data with a python-side reference to the capsule to tie +// its destruction to that of any dependent python objects. Const-ness is determined by whether or +// not the Type of the pointer given is const. +template ::value>> +handle eigen_encapsulate(Type *src) { + capsule base(src, [](void *o) { delete static_cast(o); }); + return eigen_ref_array(*src, base); +} + +// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense +// types. +template +struct type_caster::value>> { + using Scalar = typename Type::Scalar; + using props = EigenProps; + + bool load(handle src, bool convert) { + // If we're in no-convert mode, only load if given an array of the correct type + if (!convert && !isinstance>(src)) + return false; + + // Coerce into an array, but don't do type conversion yet; the copy below handles it. + auto buf = array::ensure(src); + + if (!buf) + return false; + + auto dims = buf.ndim(); + if (dims < 1 || dims > 2) + return false; + + auto fits = props::conformable(buf); + if (!fits) + return false; + + // Allocate the new type, then build a numpy reference into it + value = Type(fits.rows, fits.cols); + auto ref = reinterpret_steal(eigen_ref_array(value)); + if (dims == 1) ref = ref.squeeze(); + else if (ref.ndim() == 1) buf = buf.squeeze(); + + int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr()); + + if (result < 0) { // Copy failed! + PyErr_Clear(); + return false; + } + + return true; + } + +private: + + // Cast implementation + template + static handle cast_impl(CType *src, return_value_policy policy, handle parent) { + switch (policy) { + case return_value_policy::take_ownership: + case return_value_policy::automatic: + return eigen_encapsulate(src); + case return_value_policy::move: + return eigen_encapsulate(new CType(std::move(*src))); + case return_value_policy::copy: + return eigen_array_cast(*src); + case return_value_policy::reference: + case return_value_policy::automatic_reference: + return eigen_ref_array(*src); + case return_value_policy::reference_internal: + return eigen_ref_array(*src, parent); + default: + throw cast_error("unhandled return_value_policy: should not happen!"); + }; + } + +public: + + // Normal returned non-reference, non-const value: + static handle cast(Type &&src, return_value_policy /* policy */, handle parent) { + return cast_impl(&src, return_value_policy::move, parent); + } + // If you return a non-reference const, we mark the numpy array readonly: + static handle cast(const Type &&src, return_value_policy /* policy */, handle parent) { + return cast_impl(&src, return_value_policy::move, parent); + } + // lvalue reference return; default (automatic) becomes copy + static handle cast(Type &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast_impl(&src, policy, parent); + } + // const lvalue reference return; default (automatic) becomes copy + static handle cast(const Type &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast(&src, policy, parent); + } + // non-const pointer return + static handle cast(Type *src, return_value_policy policy, handle parent) { + return cast_impl(src, policy, parent); + } + // const pointer return + static handle cast(const Type *src, return_value_policy policy, handle parent) { + return cast_impl(src, policy, parent); + } + + static constexpr auto name = props::descriptor; + + operator Type*() { return &value; } + operator Type&() { return value; } + operator Type&&() && { return std::move(value); } + template using cast_op_type = movable_cast_op_type; + +private: + Type value; +}; + +// Base class for casting reference/map/block/etc. objects back to python. +template struct eigen_map_caster { +private: + using props = EigenProps; + +public: + + // Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has + // to stay around), but we'll allow it under the assumption that you know what you're doing (and + // have an appropriate keep_alive in place). We return a numpy array pointing directly at the + // ref's data (The numpy array ends up read-only if the ref was to a const matrix type.) Note + // that this means you need to ensure you don't destroy the object in some other way (e.g. with + // an appropriate keep_alive, or with a reference to a statically allocated matrix). + static handle cast(const MapType &src, return_value_policy policy, handle parent) { + switch (policy) { + case return_value_policy::copy: + return eigen_array_cast(src); + case return_value_policy::reference_internal: + return eigen_array_cast(src, parent, is_eigen_mutable_map::value); + case return_value_policy::reference: + case return_value_policy::automatic: + case return_value_policy::automatic_reference: + return eigen_array_cast(src, none(), is_eigen_mutable_map::value); + default: + // move, take_ownership don't make any sense for a ref/map: + pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type"); + } + } + + static constexpr auto name = props::descriptor; + + // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return + // types but not bound arguments). We still provide them (with an explicitly delete) so that + // you end up here if you try anyway. + bool load(handle, bool) = delete; + operator MapType() = delete; + template using cast_op_type = MapType; +}; + +// We can return any map-like object (but can only load Refs, specialized next): +template struct type_caster::value>> + : eigen_map_caster {}; + +// Loader for Ref<...> arguments. See the documentation for info on how to make this work without +// copying (it requires some extra effort in many cases). +template +struct type_caster< + Eigen::Ref, + enable_if_t>::value> +> : public eigen_map_caster> { +private: + using Type = Eigen::Ref; + using props = EigenProps; + using Scalar = typename props::Scalar; + using MapType = Eigen::Map; + using Array = array_t; + static constexpr bool need_writeable = is_eigen_mutable_map::value; + // Delay construction (these have no default constructor) + std::unique_ptr map; + std::unique_ptr ref; + // Our array. When possible, this is just a numpy array pointing to the source data, but + // sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an incompatible + // layout, or is an array of a type that needs to be converted). Using a numpy temporary + // (rather than an Eigen temporary) saves an extra copy when we need both type conversion and + // storage order conversion. (Note that we refuse to use this temporary copy when loading an + // argument for a Ref with M non-const, i.e. a read-write reference). + Array copy_or_ref; +public: + bool load(handle src, bool convert) { + // First check whether what we have is already an array of the right type. If not, we can't + // avoid a copy (because the copy is also going to do type conversion). + bool need_copy = !isinstance(src); + + EigenConformable fits; + if (!need_copy) { + // We don't need a converting copy, but we also need to check whether the strides are + // compatible with the Ref's stride requirements + Array aref = reinterpret_borrow(src); + + if (aref && (!need_writeable || aref.writeable())) { + fits = props::conformable(aref); + if (!fits) return false; // Incompatible dimensions + if (!fits.template stride_compatible()) + need_copy = true; + else + copy_or_ref = std::move(aref); + } + else { + need_copy = true; + } + } + + if (need_copy) { + // We need to copy: If we need a mutable reference, or we're not supposed to convert + // (either because we're in the no-convert overload pass, or because we're explicitly + // instructed not to copy (via `py::arg().noconvert()`) we have to fail loading. + if (!convert || need_writeable) return false; + + Array copy = Array::ensure(src); + if (!copy) return false; + fits = props::conformable(copy); + if (!fits || !fits.template stride_compatible()) + return false; + copy_or_ref = std::move(copy); + loader_life_support::add_patient(copy_or_ref); + } + + ref.reset(); + map.reset(new MapType(data(copy_or_ref), fits.rows, fits.cols, make_stride(fits.stride.outer(), fits.stride.inner()))); + ref.reset(new Type(*map)); + + return true; + } + + operator Type*() { return ref.get(); } + operator Type&() { return *ref; } + template using cast_op_type = pybind11::detail::cast_op_type<_T>; + +private: + template ::value, int> = 0> + Scalar *data(Array &a) { return a.mutable_data(); } + + template ::value, int> = 0> + const Scalar *data(Array &a) { return a.data(); } + + // Attempt to figure out a constructor of `Stride` that will work. + // If both strides are fixed, use a default constructor: + template using stride_ctor_default = bool_constant< + S::InnerStrideAtCompileTime != Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && + std::is_default_constructible::value>; + // Otherwise, if there is a two-index constructor, assume it is (outer,inner) like + // Eigen::Stride, and use it: + template using stride_ctor_dual = bool_constant< + !stride_ctor_default::value && std::is_constructible::value>; + // Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use + // it (passing whichever stride is dynamic). + template using stride_ctor_outer = bool_constant< + !any_of, stride_ctor_dual>::value && + S::OuterStrideAtCompileTime == Eigen::Dynamic && S::InnerStrideAtCompileTime != Eigen::Dynamic && + std::is_constructible::value>; + template using stride_ctor_inner = bool_constant< + !any_of, stride_ctor_dual>::value && + S::InnerStrideAtCompileTime == Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && + std::is_constructible::value>; + + template ::value, int> = 0> + static S make_stride(EigenIndex, EigenIndex) { return S(); } + template ::value, int> = 0> + static S make_stride(EigenIndex outer, EigenIndex inner) { return S(outer, inner); } + template ::value, int> = 0> + static S make_stride(EigenIndex outer, EigenIndex) { return S(outer); } + template ::value, int> = 0> + static S make_stride(EigenIndex, EigenIndex inner) { return S(inner); } + +}; + +// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not +// EigenDense (i.e. they don't have a data(), at least not with the usual matrix layout). +// load() is not supported, but we can cast them into the python domain by first copying to a +// regular Eigen::Matrix, then casting that. +template +struct type_caster::value>> { +protected: + using Matrix = Eigen::Matrix; + using props = EigenProps; +public: + static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { + handle h = eigen_encapsulate(new Matrix(src)); + return h; + } + static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); } + + static constexpr auto name = props::descriptor; + + // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return + // types but not bound arguments). We still provide them (with an explicitly delete) so that + // you end up here if you try anyway. + bool load(handle, bool) = delete; + operator Type() = delete; + template using cast_op_type = Type; +}; + +template +struct type_caster::value>> { + typedef typename Type::Scalar Scalar; + typedef remove_reference_t().outerIndexPtr())> StorageIndex; + typedef typename Type::Index Index; + static constexpr bool rowMajor = Type::IsRowMajor; + + bool load(handle src, bool) { + if (!src) + return false; + + auto obj = reinterpret_borrow(src); + object sparse_module = module_::import("scipy.sparse"); + object matrix_type = sparse_module.attr( + rowMajor ? "csr_matrix" : "csc_matrix"); + + if (!type::handle_of(obj).is(matrix_type)) { + try { + obj = matrix_type(obj); + } catch (const error_already_set &) { + return false; + } + } + + auto values = array_t((object) obj.attr("data")); + auto innerIndices = array_t((object) obj.attr("indices")); + auto outerIndices = array_t((object) obj.attr("indptr")); + auto shape = pybind11::tuple((pybind11::object) obj.attr("shape")); + auto nnz = obj.attr("nnz").cast(); + + if (!values || !innerIndices || !outerIndices) + return false; + + value = Eigen::MappedSparseMatrix( + shape[0].cast(), shape[1].cast(), nnz, + outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); + + return true; + } + + static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { + const_cast(src).makeCompressed(); + + object matrix_type = module_::import("scipy.sparse").attr( + rowMajor ? "csr_matrix" : "csc_matrix"); + + array data(src.nonZeros(), src.valuePtr()); + array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr()); + array innerIndices(src.nonZeros(), src.innerIndexPtr()); + + return matrix_type( + std::make_tuple(data, innerIndices, outerIndices), + std::make_pair(src.rows(), src.cols()) + ).release(); + } + + PYBIND11_TYPE_CASTER(Type, _<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[") + + npy_format_descriptor::name + _("]")); +}; + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(__GNUG__) || defined(__clang__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/extern/pybind/include/pybind11/embed.h b/extern/pybind/include/pybind11/embed.h new file mode 100644 index 00000000..204aaf98 --- /dev/null +++ b/extern/pybind/include/pybind11/embed.h @@ -0,0 +1,201 @@ +/* + pybind11/embed.h: Support for embedding the interpreter + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include "eval.h" + +#if defined(PYPY_VERSION) +# error Embedding the interpreter is not supported with PyPy +#endif + +#if PY_MAJOR_VERSION >= 3 +# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + extern "C" PyObject *pybind11_init_impl_##name(); \ + extern "C" PyObject *pybind11_init_impl_##name() { \ + return pybind11_init_wrapper_##name(); \ + } +#else +# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + extern "C" void pybind11_init_impl_##name(); \ + extern "C" void pybind11_init_impl_##name() { \ + pybind11_init_wrapper_##name(); \ + } +#endif + +/** \rst + Add a new module to the table of builtins for the interpreter. Must be + defined in global scope. The first macro parameter is the name of the + module (without quotes). The second parameter is the variable which will + be used as the interface to add functions and classes to the module. + + .. code-block:: cpp + + PYBIND11_EMBEDDED_MODULE(example, m) { + // ... initialize functions and classes here + m.def("foo", []() { + return "Hello, World!"; + }); + } + \endrst */ +#define PYBIND11_EMBEDDED_MODULE(name, variable) \ + static ::pybind11::module_::module_def \ + PYBIND11_CONCAT(pybind11_module_def_, name); \ + static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \ + static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \ + auto m = ::pybind11::module_::create_extension_module( \ + PYBIND11_TOSTRING(name), nullptr, \ + &PYBIND11_CONCAT(pybind11_module_def_, name)); \ + try { \ + PYBIND11_CONCAT(pybind11_init_, name)(m); \ + return m.ptr(); \ + } PYBIND11_CATCH_INIT_EXCEPTIONS \ + } \ + PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + ::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name) \ + (PYBIND11_TOSTRING(name), \ + PYBIND11_CONCAT(pybind11_init_impl_, name)); \ + void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &variable) + + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. +struct embedded_module { +#if PY_MAJOR_VERSION >= 3 + using init_t = PyObject *(*)(); +#else + using init_t = void (*)(); +#endif + embedded_module(const char *name, init_t init) { + if (Py_IsInitialized()) + pybind11_fail("Can't add new modules after the interpreter has been initialized"); + + auto result = PyImport_AppendInittab(name, init); + if (result == -1) + pybind11_fail("Insufficient memory to add a new module"); + } +}; + +PYBIND11_NAMESPACE_END(detail) + +/** \rst + Initialize the Python interpreter. No other pybind11 or CPython API functions can be + called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The + optional parameter can be used to skip the registration of signal handlers (see the + `Python documentation`_ for details). Calling this function again after the interpreter + has already been initialized is a fatal error. + + If initializing the Python interpreter fails, then the program is terminated. (This + is controlled by the CPython runtime and is an exception to pybind11's normal behavior + of throwing exceptions on errors.) + + .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx + \endrst */ +inline void initialize_interpreter(bool init_signal_handlers = true) { + if (Py_IsInitialized()) + pybind11_fail("The interpreter is already running"); + + Py_InitializeEx(init_signal_handlers ? 1 : 0); + + // Make .py files in the working directory available by default + module_::import("sys").attr("path").cast().append("."); +} + +/** \rst + Shut down the Python interpreter. No pybind11 or CPython API functions can be called + after this. In addition, pybind11 objects must not outlive the interpreter: + + .. code-block:: cpp + + { // BAD + py::initialize_interpreter(); + auto hello = py::str("Hello, World!"); + py::finalize_interpreter(); + } // <-- BOOM, hello's destructor is called after interpreter shutdown + + { // GOOD + py::initialize_interpreter(); + { // scoped + auto hello = py::str("Hello, World!"); + } // <-- OK, hello is cleaned up properly + py::finalize_interpreter(); + } + + { // BETTER + py::scoped_interpreter guard{}; + auto hello = py::str("Hello, World!"); + } + + .. warning:: + + The interpreter can be restarted by calling `initialize_interpreter` again. + Modules created using pybind11 can be safely re-initialized. However, Python + itself cannot completely unload binary extension modules and there are several + caveats with regard to interpreter restarting. All the details can be found + in the CPython documentation. In short, not all interpreter memory may be + freed, either due to reference cycles or user-created global data. + + \endrst */ +inline void finalize_interpreter() { + handle builtins(PyEval_GetBuiltins()); + const char *id = PYBIND11_INTERNALS_ID; + + // Get the internals pointer (without creating it if it doesn't exist). It's possible for the + // internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()` + // during destruction), so we get the pointer-pointer here and check it after Py_Finalize(). + detail::internals **internals_ptr_ptr = detail::get_internals_pp(); + // It could also be stashed in builtins, so look there too: + if (builtins.contains(id) && isinstance(builtins[id])) + internals_ptr_ptr = capsule(builtins[id]); + + Py_Finalize(); + + if (internals_ptr_ptr) { + delete *internals_ptr_ptr; + *internals_ptr_ptr = nullptr; + } +} + +/** \rst + Scope guard version of `initialize_interpreter` and `finalize_interpreter`. + This a move-only guard and only a single instance can exist. + + .. code-block:: cpp + + #include + + int main() { + py::scoped_interpreter guard{}; + py::print(Hello, World!); + } // <-- interpreter shutdown + \endrst */ +class scoped_interpreter { +public: + scoped_interpreter(bool init_signal_handlers = true) { + initialize_interpreter(init_signal_handlers); + } + + scoped_interpreter(const scoped_interpreter &) = delete; + scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; } + scoped_interpreter &operator=(const scoped_interpreter &) = delete; + scoped_interpreter &operator=(scoped_interpreter &&) = delete; + + ~scoped_interpreter() { + if (is_valid) + finalize_interpreter(); + } + +private: + bool is_valid = true; +}; + +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/eval.h b/extern/pybind/include/pybind11/eval.h new file mode 100644 index 00000000..fa6b8af4 --- /dev/null +++ b/extern/pybind/include/pybind11/eval.h @@ -0,0 +1,152 @@ +/* + pybind11/exec.h: Support for evaluating Python expressions and statements + from strings and files + + Copyright (c) 2016 Klemens Morgenstern and + Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +inline void ensure_builtins_in_globals(object &global) { + #if PY_VERSION_HEX < 0x03080000 + // Running exec and eval on Python 2 and 3 adds `builtins` module under + // `__builtins__` key to globals if not yet present. + // Python 3.8 made PyRun_String behave similarly. Let's also do that for + // older versions, for consistency. + if (!global.contains("__builtins__")) + global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE); + #else + (void) global; + #endif +} + +PYBIND11_NAMESPACE_END(detail) + +enum eval_mode { + /// Evaluate a string containing an isolated expression + eval_expr, + + /// Evaluate a string containing a single statement. Returns \c none + eval_single_statement, + + /// Evaluate a string containing a sequence of statement. Returns \c none + eval_statements +}; + +template +object eval(str expr, object global = globals(), object local = object()) { + if (!local) + local = global; + + detail::ensure_builtins_in_globals(global); + + /* PyRun_String does not accept a PyObject / encoding specifier, + this seems to be the only alternative */ + std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr; + + int start; + switch (mode) { + case eval_expr: start = Py_eval_input; break; + case eval_single_statement: start = Py_single_input; break; + case eval_statements: start = Py_file_input; break; + default: pybind11_fail("invalid evaluation mode"); + } + + PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); +} + +template +object eval(const char (&s)[N], object global = globals(), object local = object()) { + /* Support raw string literals by removing common leading whitespace */ + auto expr = (s[0] == '\n') ? str(module_::import("textwrap").attr("dedent")(s)) + : str(s); + return eval(expr, global, local); +} + +inline void exec(str expr, object global = globals(), object local = object()) { + eval(expr, global, local); +} + +template +void exec(const char (&s)[N], object global = globals(), object local = object()) { + eval(s, global, local); +} + +#if defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03000000 +template +object eval_file(str, object, object) { + pybind11_fail("eval_file not supported in PyPy3. Use eval"); +} +template +object eval_file(str, object) { + pybind11_fail("eval_file not supported in PyPy3. Use eval"); +} +template +object eval_file(str) { + pybind11_fail("eval_file not supported in PyPy3. Use eval"); +} +#else +template +object eval_file(str fname, object global = globals(), object local = object()) { + if (!local) + local = global; + + detail::ensure_builtins_in_globals(global); + + int start; + switch (mode) { + case eval_expr: start = Py_eval_input; break; + case eval_single_statement: start = Py_single_input; break; + case eval_statements: start = Py_file_input; break; + default: pybind11_fail("invalid evaluation mode"); + } + + int closeFile = 1; + std::string fname_str = (std::string) fname; +#if PY_VERSION_HEX >= 0x03040000 + FILE *f = _Py_fopen_obj(fname.ptr(), "r"); +#elif PY_VERSION_HEX >= 0x03000000 + FILE *f = _Py_fopen(fname.ptr(), "r"); +#else + /* No unicode support in open() :( */ + auto fobj = reinterpret_steal(PyFile_FromString( + const_cast(fname_str.c_str()), + const_cast("r"))); + FILE *f = nullptr; + if (fobj) + f = PyFile_AsFile(fobj.ptr()); + closeFile = 0; +#endif + if (!f) { + PyErr_Clear(); + pybind11_fail("File \"" + fname_str + "\" could not be opened!"); + } + +#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) + PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), + local.ptr()); + (void) closeFile; +#else + PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), + local.ptr(), closeFile); +#endif + + if (!result) + throw error_already_set(); + return reinterpret_steal(result); +} +#endif + +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/functional.h b/extern/pybind/include/pybind11/functional.h new file mode 100644 index 00000000..92c17dc2 --- /dev/null +++ b/extern/pybind/include/pybind11/functional.h @@ -0,0 +1,104 @@ +/* + pybind11/functional.h: std::function<> support + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +template +struct type_caster> { + using type = std::function; + using retval_type = conditional_t::value, void_type, Return>; + using function_type = Return (*) (Args...); + +public: + bool load(handle src, bool convert) { + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + return true; + } + + if (!isinstance(src)) + return false; + + auto func = reinterpret_borrow(src); + + /* + When passing a C++ function as an argument to another C++ + function via Python, every function call would normally involve + a full C++ -> Python -> C++ roundtrip, which can be prohibitive. + Here, we try to at least detect the case where the function is + stateless (i.e. function pointer or lambda function without + captured variables), in which case the roundtrip can be avoided. + */ + if (auto cfunc = func.cpp_function()) { + auto c = reinterpret_borrow(PyCFunction_GET_SELF(cfunc.ptr())); + auto rec = (function_record *) c; + + if (rec && rec->is_stateless && + same_type(typeid(function_type), *reinterpret_cast(rec->data[1]))) { + struct capture { function_type f; }; + value = ((capture *) &rec->data)->f; + return true; + } + } + + // ensure GIL is held during functor destruction + struct func_handle { + function f; + func_handle(function&& f_) : f(std::move(f_)) {} + func_handle(const func_handle& f_) { + gil_scoped_acquire acq; + f = f_.f; + } + ~func_handle() { + gil_scoped_acquire acq; + function kill_f(std::move(f)); + } + }; + + // to emulate 'move initialization capture' in C++11 + struct func_wrapper { + func_handle hfunc; + func_wrapper(func_handle&& hf): hfunc(std::move(hf)) {} + Return operator()(Args... args) const { + gil_scoped_acquire acq; + object retval(hfunc.f(std::forward(args)...)); + /* Visual studio 2015 parser issue: need parentheses around this expression */ + return (retval.template cast()); + } + }; + + value = func_wrapper(func_handle(std::move(func))); + return true; + } + + template + static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { + if (!f_) + return none().inc_ref(); + + auto result = f_.template target(); + if (result) + return cpp_function(*result, policy).release(); + else + return cpp_function(std::forward(f_), policy).release(); + } + + PYBIND11_TYPE_CASTER(type, _("Callable[[") + concat(make_caster::name...) + _("], ") + + make_caster::name + _("]")); +}; + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/iostream.h b/extern/pybind/include/pybind11/iostream.h new file mode 100644 index 00000000..5e9a8143 --- /dev/null +++ b/extern/pybind/include/pybind11/iostream.h @@ -0,0 +1,216 @@ +/* + pybind11/iostream.h -- Tools to assist with redirecting cout and cerr to Python + + Copyright (c) 2017 Henry F. Schreiner + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +#include +#include +#include +#include +#include + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +// Buffer that writes to Python instead of C++ +class pythonbuf : public std::streambuf { +private: + using traits_type = std::streambuf::traits_type; + + const size_t buf_size; + std::unique_ptr d_buffer; + object pywrite; + object pyflush; + + int overflow(int c) override { + if (!traits_type::eq_int_type(c, traits_type::eof())) { + *pptr() = traits_type::to_char_type(c); + pbump(1); + } + return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof(); + } + + // This function must be non-virtual to be called in a destructor. If the + // rare MSVC test failure shows up with this version, then this should be + // simplified to a fully qualified call. + int _sync() { + if (pbase() != pptr()) { + // This subtraction cannot be negative, so dropping the sign + str line(pbase(), static_cast(pptr() - pbase())); + + { + gil_scoped_acquire tmp; + pywrite(line); + pyflush(); + } + + setp(pbase(), epptr()); + } + return 0; + } + + int sync() override { + return _sync(); + } + +public: + + pythonbuf(object pyostream, size_t buffer_size = 1024) + : buf_size(buffer_size), + d_buffer(new char[buf_size]), + pywrite(pyostream.attr("write")), + pyflush(pyostream.attr("flush")) { + setp(d_buffer.get(), d_buffer.get() + buf_size - 1); + } + + pythonbuf(pythonbuf&&) = default; + + /// Sync before destroy + ~pythonbuf() override { + _sync(); + } +}; + +PYBIND11_NAMESPACE_END(detail) + + +/** \rst + This a move-only guard that redirects output. + + .. code-block:: cpp + + #include + + ... + + { + py::scoped_ostream_redirect output; + std::cout << "Hello, World!"; // Python stdout + } // <-- return std::cout to normal + + You can explicitly pass the c++ stream and the python object, + for example to guard stderr instead. + + .. code-block:: cpp + + { + py::scoped_ostream_redirect output{std::cerr, py::module_::import("sys").attr("stderr")}; + std::cerr << "Hello, World!"; + } + \endrst */ +class scoped_ostream_redirect { +protected: + std::streambuf *old; + std::ostream &costream; + detail::pythonbuf buffer; + +public: + scoped_ostream_redirect( + std::ostream &costream = std::cout, + object pyostream = module_::import("sys").attr("stdout")) + : costream(costream), buffer(pyostream) { + old = costream.rdbuf(&buffer); + } + + ~scoped_ostream_redirect() { + costream.rdbuf(old); + } + + scoped_ostream_redirect(const scoped_ostream_redirect &) = delete; + scoped_ostream_redirect(scoped_ostream_redirect &&other) = default; + scoped_ostream_redirect &operator=(const scoped_ostream_redirect &) = delete; + scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete; +}; + + +/** \rst + Like `scoped_ostream_redirect`, but redirects cerr by default. This class + is provided primary to make ``py::call_guard`` easier to make. + + .. code-block:: cpp + + m.def("noisy_func", &noisy_func, + py::call_guard()); + +\endrst */ +class scoped_estream_redirect : public scoped_ostream_redirect { +public: + scoped_estream_redirect( + std::ostream &costream = std::cerr, + object pyostream = module_::import("sys").attr("stderr")) + : scoped_ostream_redirect(costream,pyostream) {} +}; + + +PYBIND11_NAMESPACE_BEGIN(detail) + +// Class to redirect output as a context manager. C++ backend. +class OstreamRedirect { + bool do_stdout_; + bool do_stderr_; + std::unique_ptr redirect_stdout; + std::unique_ptr redirect_stderr; + +public: + OstreamRedirect(bool do_stdout = true, bool do_stderr = true) + : do_stdout_(do_stdout), do_stderr_(do_stderr) {} + + void enter() { + if (do_stdout_) + redirect_stdout.reset(new scoped_ostream_redirect()); + if (do_stderr_) + redirect_stderr.reset(new scoped_estream_redirect()); + } + + void exit() { + redirect_stdout.reset(); + redirect_stderr.reset(); + } +}; + +PYBIND11_NAMESPACE_END(detail) + +/** \rst + This is a helper function to add a C++ redirect context manager to Python + instead of using a C++ guard. To use it, add the following to your binding code: + + .. code-block:: cpp + + #include + + ... + + py::add_ostream_redirect(m, "ostream_redirect"); + + You now have a Python context manager that redirects your output: + + .. code-block:: python + + with m.ostream_redirect(): + m.print_to_cout_function() + + This manager can optionally be told which streams to operate on: + + .. code-block:: python + + with m.ostream_redirect(stdout=true, stderr=true): + m.noisy_function_with_error_printing() + + \endrst */ +inline class_ add_ostream_redirect(module_ m, std::string name = "ostream_redirect") { + return class_(m, name.c_str(), module_local()) + .def(init(), arg("stdout")=true, arg("stderr")=true) + .def("__enter__", &detail::OstreamRedirect::enter) + .def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); }); +} + +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/numpy.h b/extern/pybind/include/pybind11/numpy.h new file mode 100644 index 00000000..2c2ab788 --- /dev/null +++ b/extern/pybind/include/pybind11/numpy.h @@ -0,0 +1,1693 @@ +/* + pybind11/numpy.h: Basic NumPy support, vectorize() wrapper + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include "complex.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +/* This will be true on all flat address space platforms and allows us to reduce the + whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size + and dimension types (e.g. shape, strides, indexing), instead of inflicting this + upon the library user. */ +static_assert(sizeof(::pybind11::ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t"); +static_assert(std::is_signed::value, "Py_intptr_t must be signed"); +// We now can reinterpret_cast between py::ssize_t and Py_intptr_t (MSVC + PyPy cares) + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +class array; // Forward declaration + +PYBIND11_NAMESPACE_BEGIN(detail) + +template <> struct handle_type_name { static constexpr auto name = _("numpy.ndarray"); }; + +template struct npy_format_descriptor; + +struct PyArrayDescr_Proxy { + PyObject_HEAD + PyObject *typeobj; + char kind; + char type; + char byteorder; + char flags; + int type_num; + int elsize; + int alignment; + char *subarray; + PyObject *fields; + PyObject *names; +}; + +struct PyArray_Proxy { + PyObject_HEAD + char *data; + int nd; + ssize_t *dimensions; + ssize_t *strides; + PyObject *base; + PyObject *descr; + int flags; +}; + +struct PyVoidScalarObject_Proxy { + PyObject_VAR_HEAD + char *obval; + PyArrayDescr_Proxy *descr; + int flags; + PyObject *base; +}; + +struct numpy_type_info { + PyObject* dtype_ptr; + std::string format_str; +}; + +struct numpy_internals { + std::unordered_map registered_dtypes; + + numpy_type_info *get_type_info(const std::type_info& tinfo, bool throw_if_missing = true) { + auto it = registered_dtypes.find(std::type_index(tinfo)); + if (it != registered_dtypes.end()) + return &(it->second); + if (throw_if_missing) + pybind11_fail(std::string("NumPy type info missing for ") + tinfo.name()); + return nullptr; + } + + template numpy_type_info *get_type_info(bool throw_if_missing = true) { + return get_type_info(typeid(typename std::remove_cv::type), throw_if_missing); + } +}; + +inline PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) { + ptr = &get_or_create_shared_data("_numpy_internals"); +} + +inline numpy_internals& get_numpy_internals() { + static numpy_internals* ptr = nullptr; + if (!ptr) + load_numpy_internals(ptr); + return *ptr; +} + +template struct same_size { + template using as = bool_constant; +}; + +template constexpr int platform_lookup() { return -1; } + +// Lookup a type according to its size, and return a value corresponding to the NumPy typenum. +template +constexpr int platform_lookup(int I, Ints... Is) { + return sizeof(Concrete) == sizeof(T) ? I : platform_lookup(Is...); +} + +struct npy_api { + enum constants { + NPY_ARRAY_C_CONTIGUOUS_ = 0x0001, + NPY_ARRAY_F_CONTIGUOUS_ = 0x0002, + NPY_ARRAY_OWNDATA_ = 0x0004, + NPY_ARRAY_FORCECAST_ = 0x0010, + NPY_ARRAY_ENSUREARRAY_ = 0x0040, + NPY_ARRAY_ALIGNED_ = 0x0100, + NPY_ARRAY_WRITEABLE_ = 0x0400, + NPY_BOOL_ = 0, + NPY_BYTE_, NPY_UBYTE_, + NPY_SHORT_, NPY_USHORT_, + NPY_INT_, NPY_UINT_, + NPY_LONG_, NPY_ULONG_, + NPY_LONGLONG_, NPY_ULONGLONG_, + NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_, + NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_, + NPY_OBJECT_ = 17, + NPY_STRING_, NPY_UNICODE_, NPY_VOID_, + // Platform-dependent normalization + NPY_INT8_ = NPY_BYTE_, + NPY_UINT8_ = NPY_UBYTE_, + NPY_INT16_ = NPY_SHORT_, + NPY_UINT16_ = NPY_USHORT_, + // `npy_common.h` defines the integer aliases. In order, it checks: + // NPY_BITSOF_LONG, NPY_BITSOF_LONGLONG, NPY_BITSOF_INT, NPY_BITSOF_SHORT, NPY_BITSOF_CHAR + // and assigns the alias to the first matching size, so we should check in this order. + NPY_INT32_ = platform_lookup( + NPY_LONG_, NPY_INT_, NPY_SHORT_), + NPY_UINT32_ = platform_lookup( + NPY_ULONG_, NPY_UINT_, NPY_USHORT_), + NPY_INT64_ = platform_lookup( + NPY_LONG_, NPY_LONGLONG_, NPY_INT_), + NPY_UINT64_ = platform_lookup( + NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_), + }; + + typedef struct { + Py_intptr_t *ptr; + int len; + } PyArray_Dims; + + static npy_api& get() { + static npy_api api = lookup(); + return api; + } + + bool PyArray_Check_(PyObject *obj) const { + return (bool) PyObject_TypeCheck(obj, PyArray_Type_); + } + bool PyArrayDescr_Check_(PyObject *obj) const { + return (bool) PyObject_TypeCheck(obj, PyArrayDescr_Type_); + } + + unsigned int (*PyArray_GetNDArrayCFeatureVersion_)(); + PyObject *(*PyArray_DescrFromType_)(int); + PyObject *(*PyArray_NewFromDescr_) + (PyTypeObject *, PyObject *, int, Py_intptr_t const *, + Py_intptr_t const *, void *, int, PyObject *); + // Unused. Not removed because that affects ABI of the class. + PyObject *(*PyArray_DescrNewFromType_)(int); + int (*PyArray_CopyInto_)(PyObject *, PyObject *); + PyObject *(*PyArray_NewCopy_)(PyObject *, int); + PyTypeObject *PyArray_Type_; + PyTypeObject *PyVoidArrType_Type_; + PyTypeObject *PyArrayDescr_Type_; + PyObject *(*PyArray_DescrFromScalar_)(PyObject *); + PyObject *(*PyArray_FromAny_) (PyObject *, PyObject *, int, int, int, PyObject *); + int (*PyArray_DescrConverter_) (PyObject *, PyObject **); + bool (*PyArray_EquivTypes_) (PyObject *, PyObject *); + int (*PyArray_GetArrayParamsFromObject_)(PyObject *, PyObject *, unsigned char, PyObject **, int *, + Py_intptr_t *, PyObject **, PyObject *); + PyObject *(*PyArray_Squeeze_)(PyObject *); + // Unused. Not removed because that affects ABI of the class. + int (*PyArray_SetBaseObject_)(PyObject *, PyObject *); + PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int); +private: + enum functions { + API_PyArray_GetNDArrayCFeatureVersion = 211, + API_PyArray_Type = 2, + API_PyArrayDescr_Type = 3, + API_PyVoidArrType_Type = 39, + API_PyArray_DescrFromType = 45, + API_PyArray_DescrFromScalar = 57, + API_PyArray_FromAny = 69, + API_PyArray_Resize = 80, + API_PyArray_CopyInto = 82, + API_PyArray_NewCopy = 85, + API_PyArray_NewFromDescr = 94, + API_PyArray_DescrNewFromType = 96, + API_PyArray_DescrConverter = 174, + API_PyArray_EquivTypes = 182, + API_PyArray_GetArrayParamsFromObject = 278, + API_PyArray_Squeeze = 136, + API_PyArray_SetBaseObject = 282 + }; + + static npy_api lookup() { + module_ m = module_::import("numpy.core.multiarray"); + auto c = m.attr("_ARRAY_API"); +#if PY_MAJOR_VERSION >= 3 + void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL); +#else + void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr()); +#endif + npy_api api; +#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; + DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion); + if (api.PyArray_GetNDArrayCFeatureVersion_() < 0x7) + pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0"); + DECL_NPY_API(PyArray_Type); + DECL_NPY_API(PyVoidArrType_Type); + DECL_NPY_API(PyArrayDescr_Type); + DECL_NPY_API(PyArray_DescrFromType); + DECL_NPY_API(PyArray_DescrFromScalar); + DECL_NPY_API(PyArray_FromAny); + DECL_NPY_API(PyArray_Resize); + DECL_NPY_API(PyArray_CopyInto); + DECL_NPY_API(PyArray_NewCopy); + DECL_NPY_API(PyArray_NewFromDescr); + DECL_NPY_API(PyArray_DescrNewFromType); + DECL_NPY_API(PyArray_DescrConverter); + DECL_NPY_API(PyArray_EquivTypes); + DECL_NPY_API(PyArray_GetArrayParamsFromObject); + DECL_NPY_API(PyArray_Squeeze); + DECL_NPY_API(PyArray_SetBaseObject); +#undef DECL_NPY_API + return api; + } +}; + +inline PyArray_Proxy* array_proxy(void* ptr) { + return reinterpret_cast(ptr); +} + +inline const PyArray_Proxy* array_proxy(const void* ptr) { + return reinterpret_cast(ptr); +} + +inline PyArrayDescr_Proxy* array_descriptor_proxy(PyObject* ptr) { + return reinterpret_cast(ptr); +} + +inline const PyArrayDescr_Proxy* array_descriptor_proxy(const PyObject* ptr) { + return reinterpret_cast(ptr); +} + +inline bool check_flags(const void* ptr, int flag) { + return (flag == (array_proxy(ptr)->flags & flag)); +} + +template struct is_std_array : std::false_type { }; +template struct is_std_array> : std::true_type { }; +template struct is_complex : std::false_type { }; +template struct is_complex> : std::true_type { }; + +template struct array_info_scalar { + using type = T; + static constexpr bool is_array = false; + static constexpr bool is_empty = false; + static constexpr auto extents = _(""); + static void append_extents(list& /* shape */) { } +}; +// Computes underlying type and a comma-separated list of extents for array +// types (any mix of std::array and built-in arrays). An array of char is +// treated as scalar because it gets special handling. +template struct array_info : array_info_scalar { }; +template struct array_info> { + using type = typename array_info::type; + static constexpr bool is_array = true; + static constexpr bool is_empty = (N == 0) || array_info::is_empty; + static constexpr size_t extent = N; + + // appends the extents to shape + static void append_extents(list& shape) { + shape.append(N); + array_info::append_extents(shape); + } + + static constexpr auto extents = _::is_array>( + concat(_(), array_info::extents), _() + ); +}; +// For numpy we have special handling for arrays of characters, so we don't include +// the size in the array extents. +template struct array_info : array_info_scalar { }; +template struct array_info> : array_info_scalar> { }; +template struct array_info : array_info> { }; +template using remove_all_extents_t = typename array_info::type; + +template using is_pod_struct = all_of< + std::is_standard_layout, // since we're accessing directly in memory we need a standard layout type +#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || defined(_GLIBCXX_USE_CXX11_ABI) + // _GLIBCXX_USE_CXX11_ABI indicates that we're using libstdc++ from GCC 5 or newer, independent + // of the actual compiler (Clang can also use libstdc++, but it always defines __GNUC__ == 4). + std::is_trivially_copyable, +#else + // GCC 4 doesn't implement is_trivially_copyable, so approximate it + std::is_trivially_destructible, + satisfies_any_of, +#endif + satisfies_none_of +>; + +// Replacement for std::is_pod (deprecated in C++20) +template using is_pod = all_of< + std::is_standard_layout, + std::is_trivial +>; + +template ssize_t byte_offset_unsafe(const Strides &) { return 0; } +template +ssize_t byte_offset_unsafe(const Strides &strides, ssize_t i, Ix... index) { + return i * strides[Dim] + byte_offset_unsafe(strides, index...); +} + +/** + * Proxy class providing unsafe, unchecked const access to array data. This is constructed through + * the `unchecked()` method of `array` or the `unchecked()` method of `array_t`. `Dims` + * will be -1 for dimensions determined at runtime. + */ +template +class unchecked_reference { +protected: + static constexpr bool Dynamic = Dims < 0; + const unsigned char *data_; + // Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to + // make large performance gains on big, nested loops, but requires compile-time dimensions + conditional_t> + shape_, strides_; + const ssize_t dims_; + + friend class pybind11::array; + // Constructor for compile-time dimensions: + template + unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t) + : data_{reinterpret_cast(data)}, dims_{Dims} { + for (size_t i = 0; i < (size_t) dims_; i++) { + shape_[i] = shape[i]; + strides_[i] = strides[i]; + } + } + // Constructor for runtime dimensions: + template + unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t dims) + : data_{reinterpret_cast(data)}, shape_{shape}, strides_{strides}, dims_{dims} {} + +public: + /** + * Unchecked const reference access to data at the given indices. For a compile-time known + * number of dimensions, this requires the correct number of arguments; for run-time + * dimensionality, this is not checked (and so is up to the caller to use safely). + */ + template const T &operator()(Ix... index) const { + static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, + "Invalid number of indices for unchecked array reference"); + return *reinterpret_cast(data_ + byte_offset_unsafe(strides_, ssize_t(index)...)); + } + /** + * Unchecked const reference access to data; this operator only participates if the reference + * is to a 1-dimensional array. When present, this is exactly equivalent to `obj(index)`. + */ + template > + const T &operator[](ssize_t index) const { return operator()(index); } + + /// Pointer access to the data at the given indices. + template const T *data(Ix... ix) const { return &operator()(ssize_t(ix)...); } + + /// Returns the item size, i.e. sizeof(T) + constexpr static ssize_t itemsize() { return sizeof(T); } + + /// Returns the shape (i.e. size) of dimension `dim` + ssize_t shape(ssize_t dim) const { return shape_[(size_t) dim]; } + + /// Returns the number of dimensions of the array + ssize_t ndim() const { return dims_; } + + /// Returns the total number of elements in the referenced array, i.e. the product of the shapes + template + enable_if_t size() const { + return std::accumulate(shape_.begin(), shape_.end(), (ssize_t) 1, std::multiplies()); + } + template + enable_if_t size() const { + return std::accumulate(shape_, shape_ + ndim(), (ssize_t) 1, std::multiplies()); + } + + /// Returns the total number of bytes used by the referenced data. Note that the actual span in + /// memory may be larger if the referenced array has non-contiguous strides (e.g. for a slice). + ssize_t nbytes() const { + return size() * itemsize(); + } +}; + +template +class unchecked_mutable_reference : public unchecked_reference { + friend class pybind11::array; + using ConstBase = unchecked_reference; + using ConstBase::ConstBase; + using ConstBase::Dynamic; +public: + // Bring in const-qualified versions from base class + using ConstBase::operator(); + using ConstBase::operator[]; + + /// Mutable, unchecked access to data at the given indices. + template T& operator()(Ix... index) { + static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, + "Invalid number of indices for unchecked array reference"); + return const_cast(ConstBase::operator()(index...)); + } + /** + * Mutable, unchecked access data at the given index; this operator only participates if the + * reference is to a 1-dimensional array (or has runtime dimensions). When present, this is + * exactly equivalent to `obj(index)`. + */ + template > + T &operator[](ssize_t index) { return operator()(index); } + + /// Mutable pointer access to the data at the given indices. + template T *mutable_data(Ix... ix) { return &operator()(ssize_t(ix)...); } +}; + +template +struct type_caster> { + static_assert(Dim == 0 && Dim > 0 /* always fail */, "unchecked array proxy object is not castable"); +}; +template +struct type_caster> : type_caster> {}; + +PYBIND11_NAMESPACE_END(detail) + +class dtype : public object { +public: + PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_); + + explicit dtype(const buffer_info &info) { + dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format))); + // If info.itemsize == 0, use the value calculated from the format string + m_ptr = descr.strip_padding(info.itemsize ? info.itemsize : descr.itemsize()).release().ptr(); + } + + explicit dtype(const std::string &format) { + m_ptr = from_args(pybind11::str(format)).release().ptr(); + } + + dtype(const char *format) : dtype(std::string(format)) { } + + dtype(list names, list formats, list offsets, ssize_t itemsize) { + dict args; + args["names"] = names; + args["formats"] = formats; + args["offsets"] = offsets; + args["itemsize"] = pybind11::int_(itemsize); + m_ptr = from_args(args).release().ptr(); + } + + /// This is essentially the same as calling numpy.dtype(args) in Python. + static dtype from_args(object args) { + PyObject *ptr = nullptr; + if (!detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) || !ptr) + throw error_already_set(); + return reinterpret_steal(ptr); + } + + /// Return dtype associated with a C++ type. + template static dtype of() { + return detail::npy_format_descriptor::type>::dtype(); + } + + /// Size of the data type in bytes. + ssize_t itemsize() const { + return detail::array_descriptor_proxy(m_ptr)->elsize; + } + + /// Returns true for structured data types. + bool has_fields() const { + return detail::array_descriptor_proxy(m_ptr)->names != nullptr; + } + + /// Single-character type code. + char kind() const { + return detail::array_descriptor_proxy(m_ptr)->kind; + } + +private: + static object _dtype_from_pep3118() { + static PyObject *obj = module_::import("numpy.core._internal") + .attr("_dtype_from_pep3118").cast().release().ptr(); + return reinterpret_borrow(obj); + } + + dtype strip_padding(ssize_t itemsize) { + // Recursively strip all void fields with empty names that are generated for + // padding fields (as of NumPy v1.11). + if (!has_fields()) + return *this; + + struct field_descr { PYBIND11_STR_TYPE name; object format; pybind11::int_ offset; }; + std::vector field_descriptors; + + for (auto field : attr("fields").attr("items")()) { + auto spec = field.cast(); + auto name = spec[0].cast(); + auto format = spec[1].cast()[0].cast(); + auto offset = spec[1].cast()[1].cast(); + if (!len(name) && format.kind() == 'V') + continue; + field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset}); + } + + std::sort(field_descriptors.begin(), field_descriptors.end(), + [](const field_descr& a, const field_descr& b) { + return a.offset.cast() < b.offset.cast(); + }); + + list names, formats, offsets; + for (auto& descr : field_descriptors) { + names.append(descr.name); + formats.append(descr.format); + offsets.append(descr.offset); + } + return dtype(names, formats, offsets, itemsize); + } +}; + +class array : public buffer { +public: + PYBIND11_OBJECT_CVT(array, buffer, detail::npy_api::get().PyArray_Check_, raw_array) + + enum { + c_style = detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_, + f_style = detail::npy_api::NPY_ARRAY_F_CONTIGUOUS_, + forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_ + }; + + array() : array(0, static_cast(nullptr)) {} + + using ShapeContainer = detail::any_container; + using StridesContainer = detail::any_container; + + // Constructs an array taking shape/strides from arbitrary container types + array(const pybind11::dtype &dt, ShapeContainer shape, StridesContainer strides, + const void *ptr = nullptr, handle base = handle()) { + + if (strides->empty()) + *strides = detail::c_strides(*shape, dt.itemsize()); + + auto ndim = shape->size(); + if (ndim != strides->size()) + pybind11_fail("NumPy: shape ndim doesn't match strides ndim"); + auto descr = dt; + + int flags = 0; + if (base && ptr) { + if (isinstance(base)) + /* Copy flags from base (except ownership bit) */ + flags = reinterpret_borrow(base).flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_; + else + /* Writable by default, easy to downgrade later on if needed */ + flags = detail::npy_api::NPY_ARRAY_WRITEABLE_; + } + + auto &api = detail::npy_api::get(); + auto tmp = reinterpret_steal(api.PyArray_NewFromDescr_( + api.PyArray_Type_, descr.release().ptr(), (int) ndim, + // Use reinterpret_cast for PyPy on Windows (remove if fixed, checked on 7.3.1) + reinterpret_cast(shape->data()), + reinterpret_cast(strides->data()), + const_cast(ptr), flags, nullptr)); + if (!tmp) + throw error_already_set(); + if (ptr) { + if (base) { + api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr()); + } else { + tmp = reinterpret_steal(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */)); + } + } + m_ptr = tmp.release().ptr(); + } + + array(const pybind11::dtype &dt, ShapeContainer shape, const void *ptr = nullptr, handle base = handle()) + : array(dt, std::move(shape), {}, ptr, base) { } + + template ::value && !std::is_same::value>> + array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle()) + : array(dt, {{count}}, ptr, base) { } + + template + array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle()) + : array(pybind11::dtype::of(), std::move(shape), std::move(strides), ptr, base) { } + + template + array(ShapeContainer shape, const T *ptr, handle base = handle()) + : array(std::move(shape), {}, ptr, base) { } + + template + explicit array(ssize_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { } + + explicit array(const buffer_info &info, handle base = handle()) + : array(pybind11::dtype(info), info.shape, info.strides, info.ptr, base) { } + + /// Array descriptor (dtype) + pybind11::dtype dtype() const { + return reinterpret_borrow(detail::array_proxy(m_ptr)->descr); + } + + /// Total number of elements + ssize_t size() const { + return std::accumulate(shape(), shape() + ndim(), (ssize_t) 1, std::multiplies()); + } + + /// Byte size of a single element + ssize_t itemsize() const { + return detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize; + } + + /// Total number of bytes + ssize_t nbytes() const { + return size() * itemsize(); + } + + /// Number of dimensions + ssize_t ndim() const { + return detail::array_proxy(m_ptr)->nd; + } + + /// Base object + object base() const { + return reinterpret_borrow(detail::array_proxy(m_ptr)->base); + } + + /// Dimensions of the array + const ssize_t* shape() const { + return detail::array_proxy(m_ptr)->dimensions; + } + + /// Dimension along a given axis + ssize_t shape(ssize_t dim) const { + if (dim >= ndim()) + fail_dim_check(dim, "invalid axis"); + return shape()[dim]; + } + + /// Strides of the array + const ssize_t* strides() const { + return detail::array_proxy(m_ptr)->strides; + } + + /// Stride along a given axis + ssize_t strides(ssize_t dim) const { + if (dim >= ndim()) + fail_dim_check(dim, "invalid axis"); + return strides()[dim]; + } + + /// Return the NumPy array flags + int flags() const { + return detail::array_proxy(m_ptr)->flags; + } + + /// If set, the array is writeable (otherwise the buffer is read-only) + bool writeable() const { + return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_WRITEABLE_); + } + + /// If set, the array owns the data (will be freed when the array is deleted) + bool owndata() const { + return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_OWNDATA_); + } + + /// Pointer to the contained data. If index is not provided, points to the + /// beginning of the buffer. May throw if the index would lead to out of bounds access. + template const void* data(Ix... index) const { + return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); + } + + /// Mutable pointer to the contained data. If index is not provided, points to the + /// beginning of the buffer. May throw if the index would lead to out of bounds access. + /// May throw if the array is not writeable. + template void* mutable_data(Ix... index) { + check_writeable(); + return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); + } + + /// Byte offset from beginning of the array to a given index (full or partial). + /// May throw if the index would lead to out of bounds access. + template ssize_t offset_at(Ix... index) const { + if ((ssize_t) sizeof...(index) > ndim()) + fail_dim_check(sizeof...(index), "too many indices for an array"); + return byte_offset(ssize_t(index)...); + } + + ssize_t offset_at() const { return 0; } + + /// Item count from beginning of the array to a given index (full or partial). + /// May throw if the index would lead to out of bounds access. + template ssize_t index_at(Ix... index) const { + return offset_at(index...) / itemsize(); + } + + /** + * Returns a proxy object that provides access to the array's data without bounds or + * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with + * care: the array must not be destroyed or reshaped for the duration of the returned object, + * and the caller must take care not to access invalid dimensions or dimension indices. + */ + template detail::unchecked_mutable_reference mutable_unchecked() & { + if (Dims >= 0 && ndim() != Dims) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + + "; expected " + std::to_string(Dims)); + return detail::unchecked_mutable_reference(mutable_data(), shape(), strides(), ndim()); + } + + /** + * Returns a proxy object that provides const access to the array's data without bounds or + * dimensionality checking. Unlike `mutable_unchecked()`, this does not require that the + * underlying array have the `writable` flag. Use with care: the array must not be destroyed or + * reshaped for the duration of the returned object, and the caller must take care not to access + * invalid dimensions or dimension indices. + */ + template detail::unchecked_reference unchecked() const & { + if (Dims >= 0 && ndim() != Dims) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + + "; expected " + std::to_string(Dims)); + return detail::unchecked_reference(data(), shape(), strides(), ndim()); + } + + /// Return a new view with all of the dimensions of length 1 removed + array squeeze() { + auto& api = detail::npy_api::get(); + return reinterpret_steal(api.PyArray_Squeeze_(m_ptr)); + } + + /// Resize array to given shape + /// If refcheck is true and more that one reference exist to this array + /// then resize will succeed only if it makes a reshape, i.e. original size doesn't change + void resize(ShapeContainer new_shape, bool refcheck = true) { + detail::npy_api::PyArray_Dims d = { + // Use reinterpret_cast for PyPy on Windows (remove if fixed, checked on 7.3.1) + reinterpret_cast(new_shape->data()), + int(new_shape->size()) + }; + // try to resize, set ordering param to -1 cause it's not used anyway + object new_array = reinterpret_steal( + detail::npy_api::get().PyArray_Resize_(m_ptr, &d, int(refcheck), -1) + ); + if (!new_array) throw error_already_set(); + if (isinstance(new_array)) { *this = std::move(new_array); } + } + + /// Ensure that the argument is a NumPy array + /// In case of an error, nullptr is returned and the Python error is cleared. + static array ensure(handle h, int ExtraFlags = 0) { + auto result = reinterpret_steal(raw_array(h.ptr(), ExtraFlags)); + if (!result) + PyErr_Clear(); + return result; + } + +protected: + template friend struct detail::npy_format_descriptor; + + void fail_dim_check(ssize_t dim, const std::string& msg) const { + throw index_error(msg + ": " + std::to_string(dim) + + " (ndim = " + std::to_string(ndim()) + ")"); + } + + template ssize_t byte_offset(Ix... index) const { + check_dimensions(index...); + return detail::byte_offset_unsafe(strides(), ssize_t(index)...); + } + + void check_writeable() const { + if (!writeable()) + throw std::domain_error("array is not writeable"); + } + + template void check_dimensions(Ix... index) const { + check_dimensions_impl(ssize_t(0), shape(), ssize_t(index)...); + } + + void check_dimensions_impl(ssize_t, const ssize_t*) const { } + + template void check_dimensions_impl(ssize_t axis, const ssize_t* shape, ssize_t i, Ix... index) const { + if (i >= *shape) { + throw index_error(std::string("index ") + std::to_string(i) + + " is out of bounds for axis " + std::to_string(axis) + + " with size " + std::to_string(*shape)); + } + check_dimensions_impl(axis + 1, shape + 1, index...); + } + + /// Create array from any object -- always returns a new reference + static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) { + if (ptr == nullptr) { + PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array from a nullptr"); + return nullptr; + } + return detail::npy_api::get().PyArray_FromAny_( + ptr, nullptr, 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); + } +}; + +template class array_t : public array { +private: + struct private_ctor {}; + // Delegating constructor needed when both moving and accessing in the same constructor + array_t(private_ctor, ShapeContainer &&shape, StridesContainer &&strides, const T *ptr, handle base) + : array(std::move(shape), std::move(strides), ptr, base) {} +public: + static_assert(!detail::array_info::is_array, "Array types cannot be used with array_t"); + + using value_type = T; + + array_t() : array(0, static_cast(nullptr)) {} + array_t(handle h, borrowed_t) : array(h, borrowed_t{}) { } + array_t(handle h, stolen_t) : array(h, stolen_t{}) { } + + PYBIND11_DEPRECATED("Use array_t::ensure() instead") + array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen_t{}) { + if (!m_ptr) PyErr_Clear(); + if (!is_borrowed) Py_XDECREF(h.ptr()); + } + + array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) { + if (!m_ptr) throw error_already_set(); + } + + explicit array_t(const buffer_info& info, handle base = handle()) : array(info, base) { } + + array_t(ShapeContainer shape, StridesContainer strides, const T *ptr = nullptr, handle base = handle()) + : array(std::move(shape), std::move(strides), ptr, base) { } + + explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle()) + : array_t(private_ctor{}, std::move(shape), + ExtraFlags & f_style + ? detail::f_strides(*shape, itemsize()) + : detail::c_strides(*shape, itemsize()), + ptr, base) { } + + explicit array_t(ssize_t count, const T *ptr = nullptr, handle base = handle()) + : array({count}, {}, ptr, base) { } + + constexpr ssize_t itemsize() const { + return sizeof(T); + } + + template ssize_t index_at(Ix... index) const { + return offset_at(index...) / itemsize(); + } + + template const T* data(Ix... index) const { + return static_cast(array::data(index...)); + } + + template T* mutable_data(Ix... index) { + return static_cast(array::mutable_data(index...)); + } + + // Reference to element at a given index + template const T& at(Ix... index) const { + if ((ssize_t) sizeof...(index) != ndim()) + fail_dim_check(sizeof...(index), "index dimension mismatch"); + return *(static_cast(array::data()) + byte_offset(ssize_t(index)...) / itemsize()); + } + + // Mutable reference to element at a given index + template T& mutable_at(Ix... index) { + if ((ssize_t) sizeof...(index) != ndim()) + fail_dim_check(sizeof...(index), "index dimension mismatch"); + return *(static_cast(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize()); + } + + /** + * Returns a proxy object that provides access to the array's data without bounds or + * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with + * care: the array must not be destroyed or reshaped for the duration of the returned object, + * and the caller must take care not to access invalid dimensions or dimension indices. + */ + template detail::unchecked_mutable_reference mutable_unchecked() & { + return array::mutable_unchecked(); + } + + /** + * Returns a proxy object that provides const access to the array's data without bounds or + * dimensionality checking. Unlike `unchecked()`, this does not require that the underlying + * array have the `writable` flag. Use with care: the array must not be destroyed or reshaped + * for the duration of the returned object, and the caller must take care not to access invalid + * dimensions or dimension indices. + */ + template detail::unchecked_reference unchecked() const & { + return array::unchecked(); + } + + /// Ensure that the argument is a NumPy array of the correct dtype (and if not, try to convert + /// it). In case of an error, nullptr is returned and the Python error is cleared. + static array_t ensure(handle h) { + auto result = reinterpret_steal(raw_array_t(h.ptr())); + if (!result) + PyErr_Clear(); + return result; + } + + static bool check_(handle h) { + const auto &api = detail::npy_api::get(); + return api.PyArray_Check_(h.ptr()) + && api.PyArray_EquivTypes_(detail::array_proxy(h.ptr())->descr, dtype::of().ptr()) + && detail::check_flags(h.ptr(), ExtraFlags & (array::c_style | array::f_style)); + } + +protected: + /// Create array from any object -- always returns a new reference + static PyObject *raw_array_t(PyObject *ptr) { + if (ptr == nullptr) { + PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr"); + return nullptr; + } + return detail::npy_api::get().PyArray_FromAny_( + ptr, dtype::of().release().ptr(), 0, 0, + detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); + } +}; + +template +struct format_descriptor::value>> { + static std::string format() { + return detail::npy_format_descriptor::type>::format(); + } +}; + +template struct format_descriptor { + static std::string format() { return std::to_string(N) + "s"; } +}; +template struct format_descriptor> { + static std::string format() { return std::to_string(N) + "s"; } +}; + +template +struct format_descriptor::value>> { + static std::string format() { + return format_descriptor< + typename std::remove_cv::type>::type>::format(); + } +}; + +template +struct format_descriptor::is_array>> { + static std::string format() { + using namespace detail; + static constexpr auto extents = _("(") + array_info::extents + _(")"); + return extents.text + format_descriptor>::format(); + } +}; + +PYBIND11_NAMESPACE_BEGIN(detail) +template +struct pyobject_caster> { + using type = array_t; + + bool load(handle src, bool convert) { + if (!convert && !type::check_(src)) + return false; + value = type::ensure(src); + return static_cast(value); + } + + static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { + return src.inc_ref(); + } + PYBIND11_TYPE_CASTER(type, handle_type_name::name); +}; + +template +struct compare_buffer_info::value>> { + static bool compare(const buffer_info& b) { + return npy_api::get().PyArray_EquivTypes_(dtype::of().ptr(), dtype(b).ptr()); + } +}; + +template +struct npy_format_descriptor_name; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value>( + _("bool"), _::value>("numpy.int", "numpy.uint") + _() + ); +}; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value || std::is_same::value>( + _("numpy.float") + _(), _("numpy.longdouble") + ); +}; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value + || std::is_same::value>( + _("numpy.complex") + _(), _("numpy.longcomplex") + ); +}; + +template +struct npy_format_descriptor::value>> + : npy_format_descriptor_name { +private: + // NB: the order here must match the one in common.h + constexpr static const int values[15] = { + npy_api::NPY_BOOL_, + npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_INT16_, npy_api::NPY_UINT16_, + npy_api::NPY_INT32_, npy_api::NPY_UINT32_, npy_api::NPY_INT64_, npy_api::NPY_UINT64_, + npy_api::NPY_FLOAT_, npy_api::NPY_DOUBLE_, npy_api::NPY_LONGDOUBLE_, + npy_api::NPY_CFLOAT_, npy_api::NPY_CDOUBLE_, npy_api::NPY_CLONGDOUBLE_ + }; + +public: + static constexpr int value = values[detail::is_fmt_numeric::index]; + + static pybind11::dtype dtype() { + if (auto ptr = npy_api::get().PyArray_DescrFromType_(value)) + return reinterpret_steal(ptr); + pybind11_fail("Unsupported buffer format!"); + } +}; + +#define PYBIND11_DECL_CHAR_FMT \ + static constexpr auto name = _("S") + _(); \ + static pybind11::dtype dtype() { return pybind11::dtype(std::string("S") + std::to_string(N)); } +template struct npy_format_descriptor { PYBIND11_DECL_CHAR_FMT }; +template struct npy_format_descriptor> { PYBIND11_DECL_CHAR_FMT }; +#undef PYBIND11_DECL_CHAR_FMT + +template struct npy_format_descriptor::is_array>> { +private: + using base_descr = npy_format_descriptor::type>; +public: + static_assert(!array_info::is_empty, "Zero-sized arrays are not supported"); + + static constexpr auto name = _("(") + array_info::extents + _(")") + base_descr::name; + static pybind11::dtype dtype() { + list shape; + array_info::append_extents(shape); + return pybind11::dtype::from_args(pybind11::make_tuple(base_descr::dtype(), shape)); + } +}; + +template struct npy_format_descriptor::value>> { +private: + using base_descr = npy_format_descriptor::type>; +public: + static constexpr auto name = base_descr::name; + static pybind11::dtype dtype() { return base_descr::dtype(); } +}; + +struct field_descriptor { + const char *name; + ssize_t offset; + ssize_t size; + std::string format; + dtype descr; +}; + +inline PYBIND11_NOINLINE void register_structured_dtype( + any_container fields, + const std::type_info& tinfo, ssize_t itemsize, + bool (*direct_converter)(PyObject *, void *&)) { + + auto& numpy_internals = get_numpy_internals(); + if (numpy_internals.get_type_info(tinfo, false)) + pybind11_fail("NumPy: dtype is already registered"); + + // Use ordered fields because order matters as of NumPy 1.14: + // https://docs.scipy.org/doc/numpy/release.html#multiple-field-indexing-assignment-of-structured-arrays + std::vector ordered_fields(std::move(fields)); + std::sort(ordered_fields.begin(), ordered_fields.end(), + [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; }); + + list names, formats, offsets; + for (auto& field : ordered_fields) { + if (!field.descr) + pybind11_fail(std::string("NumPy: unsupported field dtype: `") + + field.name + "` @ " + tinfo.name()); + names.append(PYBIND11_STR_TYPE(field.name)); + formats.append(field.descr); + offsets.append(pybind11::int_(field.offset)); + } + auto dtype_ptr = pybind11::dtype(names, formats, offsets, itemsize).release().ptr(); + + // There is an existing bug in NumPy (as of v1.11): trailing bytes are + // not encoded explicitly into the format string. This will supposedly + // get fixed in v1.12; for further details, see these: + // - https://github.com/numpy/numpy/issues/7797 + // - https://github.com/numpy/numpy/pull/7798 + // Because of this, we won't use numpy's logic to generate buffer format + // strings and will just do it ourselves. + ssize_t offset = 0; + std::ostringstream oss; + // mark the structure as unaligned with '^', because numpy and C++ don't + // always agree about alignment (particularly for complex), and we're + // explicitly listing all our padding. This depends on none of the fields + // overriding the endianness. Putting the ^ in front of individual fields + // isn't guaranteed to work due to https://github.com/numpy/numpy/issues/9049 + oss << "^T{"; + for (auto& field : ordered_fields) { + if (field.offset > offset) + oss << (field.offset - offset) << 'x'; + oss << field.format << ':' << field.name << ':'; + offset = field.offset + field.size; + } + if (itemsize > offset) + oss << (itemsize - offset) << 'x'; + oss << '}'; + auto format_str = oss.str(); + + // Sanity check: verify that NumPy properly parses our buffer format string + auto& api = npy_api::get(); + auto arr = array(buffer_info(nullptr, itemsize, format_str, 1)); + if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) + pybind11_fail("NumPy: invalid buffer descriptor!"); + + auto tindex = std::type_index(tinfo); + numpy_internals.registered_dtypes[tindex] = { dtype_ptr, format_str }; + get_internals().direct_conversions[tindex].push_back(direct_converter); +} + +template struct npy_format_descriptor { + static_assert(is_pod_struct::value, "Attempt to use a non-POD or unimplemented POD type as a numpy dtype"); + + static constexpr auto name = make_caster::name; + + static pybind11::dtype dtype() { + return reinterpret_borrow(dtype_ptr()); + } + + static std::string format() { + static auto format_str = get_numpy_internals().get_type_info(true)->format_str; + return format_str; + } + + static void register_dtype(any_container fields) { + register_structured_dtype(std::move(fields), typeid(typename std::remove_cv::type), + sizeof(T), &direct_converter); + } + +private: + static PyObject* dtype_ptr() { + static PyObject* ptr = get_numpy_internals().get_type_info(true)->dtype_ptr; + return ptr; + } + + static bool direct_converter(PyObject *obj, void*& value) { + auto& api = npy_api::get(); + if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_)) + return false; + if (auto descr = reinterpret_steal(api.PyArray_DescrFromScalar_(obj))) { + if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) { + value = ((PyVoidScalarObject_Proxy *) obj)->obval; + return true; + } + } + return false; + } +}; + +#ifdef __CLION_IDE__ // replace heavy macro with dummy code for the IDE (doesn't affect code) +# define PYBIND11_NUMPY_DTYPE(Type, ...) ((void)0) +# define PYBIND11_NUMPY_DTYPE_EX(Type, ...) ((void)0) +#else + +#define PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, Name) \ + ::pybind11::detail::field_descriptor { \ + Name, offsetof(T, Field), sizeof(decltype(std::declval().Field)), \ + ::pybind11::format_descriptor().Field)>::format(), \ + ::pybind11::detail::npy_format_descriptor().Field)>::dtype() \ + } + +// Extract name, offset and format descriptor for a struct field +#define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field) + +// The main idea of this macro is borrowed from https://github.com/swansontec/map-macro +// (C) William Swanson, Paul Fultz +#define PYBIND11_EVAL0(...) __VA_ARGS__ +#define PYBIND11_EVAL1(...) PYBIND11_EVAL0 (PYBIND11_EVAL0 (PYBIND11_EVAL0 (__VA_ARGS__))) +#define PYBIND11_EVAL2(...) PYBIND11_EVAL1 (PYBIND11_EVAL1 (PYBIND11_EVAL1 (__VA_ARGS__))) +#define PYBIND11_EVAL3(...) PYBIND11_EVAL2 (PYBIND11_EVAL2 (PYBIND11_EVAL2 (__VA_ARGS__))) +#define PYBIND11_EVAL4(...) PYBIND11_EVAL3 (PYBIND11_EVAL3 (PYBIND11_EVAL3 (__VA_ARGS__))) +#define PYBIND11_EVAL(...) PYBIND11_EVAL4 (PYBIND11_EVAL4 (PYBIND11_EVAL4 (__VA_ARGS__))) +#define PYBIND11_MAP_END(...) +#define PYBIND11_MAP_OUT +#define PYBIND11_MAP_COMMA , +#define PYBIND11_MAP_GET_END() 0, PYBIND11_MAP_END +#define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT +#define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0 (test, next, 0) +#define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1 (PYBIND11_MAP_GET_END test, next) +#if defined(_MSC_VER) && !defined(__clang__) // MSVC is not as eager to expand macros, hence this workaround +#define PYBIND11_MAP_LIST_NEXT1(test, next) \ + PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) +#else +#define PYBIND11_MAP_LIST_NEXT1(test, next) \ + PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) +#endif +#define PYBIND11_MAP_LIST_NEXT(test, next) \ + PYBIND11_MAP_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) +#define PYBIND11_MAP_LIST0(f, t, x, peek, ...) \ + f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST1) (f, t, peek, __VA_ARGS__) +#define PYBIND11_MAP_LIST1(f, t, x, peek, ...) \ + f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST0) (f, t, peek, __VA_ARGS__) +// PYBIND11_MAP_LIST(f, t, a1, a2, ...) expands to f(t, a1), f(t, a2), ... +#define PYBIND11_MAP_LIST(f, t, ...) \ + PYBIND11_EVAL (PYBIND11_MAP_LIST1 (f, t, __VA_ARGS__, (), 0)) + +#define PYBIND11_NUMPY_DTYPE(Type, ...) \ + ::pybind11::detail::npy_format_descriptor::register_dtype \ + (::std::vector<::pybind11::detail::field_descriptor> \ + {PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)}) + +#if defined(_MSC_VER) && !defined(__clang__) +#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ + PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) +#else +#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ + PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) +#endif +#define PYBIND11_MAP2_LIST_NEXT(test, next) \ + PYBIND11_MAP2_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) +#define PYBIND11_MAP2_LIST0(f, t, x1, x2, peek, ...) \ + f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST1) (f, t, peek, __VA_ARGS__) +#define PYBIND11_MAP2_LIST1(f, t, x1, x2, peek, ...) \ + f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST0) (f, t, peek, __VA_ARGS__) +// PYBIND11_MAP2_LIST(f, t, a1, a2, ...) expands to f(t, a1, a2), f(t, a3, a4), ... +#define PYBIND11_MAP2_LIST(f, t, ...) \ + PYBIND11_EVAL (PYBIND11_MAP2_LIST1 (f, t, __VA_ARGS__, (), 0)) + +#define PYBIND11_NUMPY_DTYPE_EX(Type, ...) \ + ::pybind11::detail::npy_format_descriptor::register_dtype \ + (::std::vector<::pybind11::detail::field_descriptor> \ + {PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)}) + +#endif // __CLION_IDE__ + +class common_iterator { +public: + using container_type = std::vector; + using value_type = container_type::value_type; + using size_type = container_type::size_type; + + common_iterator() : p_ptr(0), m_strides() {} + + common_iterator(void* ptr, const container_type& strides, const container_type& shape) + : p_ptr(reinterpret_cast(ptr)), m_strides(strides.size()) { + m_strides.back() = static_cast(strides.back()); + for (size_type i = m_strides.size() - 1; i != 0; --i) { + size_type j = i - 1; + auto s = static_cast(shape[i]); + m_strides[j] = strides[j] + m_strides[i] - strides[i] * s; + } + } + + void increment(size_type dim) { + p_ptr += m_strides[dim]; + } + + void* data() const { + return p_ptr; + } + +private: + char* p_ptr; + container_type m_strides; +}; + +template class multi_array_iterator { +public: + using container_type = std::vector; + + multi_array_iterator(const std::array &buffers, + const container_type &shape) + : m_shape(shape.size()), m_index(shape.size(), 0), + m_common_iterator() { + + // Manual copy to avoid conversion warning if using std::copy + for (size_t i = 0; i < shape.size(); ++i) + m_shape[i] = shape[i]; + + container_type strides(shape.size()); + for (size_t i = 0; i < N; ++i) + init_common_iterator(buffers[i], shape, m_common_iterator[i], strides); + } + + multi_array_iterator& operator++() { + for (size_t j = m_index.size(); j != 0; --j) { + size_t i = j - 1; + if (++m_index[i] != m_shape[i]) { + increment_common_iterator(i); + break; + } else { + m_index[i] = 0; + } + } + return *this; + } + + template T* data() const { + return reinterpret_cast(m_common_iterator[K].data()); + } + +private: + + using common_iter = common_iterator; + + void init_common_iterator(const buffer_info &buffer, + const container_type &shape, + common_iter &iterator, + container_type &strides) { + auto buffer_shape_iter = buffer.shape.rbegin(); + auto buffer_strides_iter = buffer.strides.rbegin(); + auto shape_iter = shape.rbegin(); + auto strides_iter = strides.rbegin(); + + while (buffer_shape_iter != buffer.shape.rend()) { + if (*shape_iter == *buffer_shape_iter) + *strides_iter = *buffer_strides_iter; + else + *strides_iter = 0; + + ++buffer_shape_iter; + ++buffer_strides_iter; + ++shape_iter; + ++strides_iter; + } + + std::fill(strides_iter, strides.rend(), 0); + iterator = common_iter(buffer.ptr, strides, shape); + } + + void increment_common_iterator(size_t dim) { + for (auto &iter : m_common_iterator) + iter.increment(dim); + } + + container_type m_shape; + container_type m_index; + std::array m_common_iterator; +}; + +enum class broadcast_trivial { non_trivial, c_trivial, f_trivial }; + +// Populates the shape and number of dimensions for the set of buffers. Returns a broadcast_trivial +// enum value indicating whether the broadcast is "trivial"--that is, has each buffer being either a +// singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous (`f_trivial`) storage +// buffer; returns `non_trivial` otherwise. +template +broadcast_trivial broadcast(const std::array &buffers, ssize_t &ndim, std::vector &shape) { + ndim = std::accumulate(buffers.begin(), buffers.end(), ssize_t(0), [](ssize_t res, const buffer_info &buf) { + return std::max(res, buf.ndim); + }); + + shape.clear(); + shape.resize((size_t) ndim, 1); + + // Figure out the output size, and make sure all input arrays conform (i.e. are either size 1 or + // the full size). + for (size_t i = 0; i < N; ++i) { + auto res_iter = shape.rbegin(); + auto end = buffers[i].shape.rend(); + for (auto shape_iter = buffers[i].shape.rbegin(); shape_iter != end; ++shape_iter, ++res_iter) { + const auto &dim_size_in = *shape_iter; + auto &dim_size_out = *res_iter; + + // Each input dimension can either be 1 or `n`, but `n` values must match across buffers + if (dim_size_out == 1) + dim_size_out = dim_size_in; + else if (dim_size_in != 1 && dim_size_in != dim_size_out) + pybind11_fail("pybind11::vectorize: incompatible size/dimension of inputs!"); + } + } + + bool trivial_broadcast_c = true; + bool trivial_broadcast_f = true; + for (size_t i = 0; i < N && (trivial_broadcast_c || trivial_broadcast_f); ++i) { + if (buffers[i].size == 1) + continue; + + // Require the same number of dimensions: + if (buffers[i].ndim != ndim) + return broadcast_trivial::non_trivial; + + // Require all dimensions be full-size: + if (!std::equal(buffers[i].shape.cbegin(), buffers[i].shape.cend(), shape.cbegin())) + return broadcast_trivial::non_trivial; + + // Check for C contiguity (but only if previous inputs were also C contiguous) + if (trivial_broadcast_c) { + ssize_t expect_stride = buffers[i].itemsize; + auto end = buffers[i].shape.crend(); + for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin(); + trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) { + if (expect_stride == *stride_iter) + expect_stride *= *shape_iter; + else + trivial_broadcast_c = false; + } + } + + // Check for Fortran contiguity (if previous inputs were also F contiguous) + if (trivial_broadcast_f) { + ssize_t expect_stride = buffers[i].itemsize; + auto end = buffers[i].shape.cend(); + for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin(); + trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) { + if (expect_stride == *stride_iter) + expect_stride *= *shape_iter; + else + trivial_broadcast_f = false; + } + } + } + + return + trivial_broadcast_c ? broadcast_trivial::c_trivial : + trivial_broadcast_f ? broadcast_trivial::f_trivial : + broadcast_trivial::non_trivial; +} + +template +struct vectorize_arg { + static_assert(!std::is_rvalue_reference::value, "Functions with rvalue reference arguments cannot be vectorized"); + // The wrapped function gets called with this type: + using call_type = remove_reference_t; + // Is this a vectorized argument? + static constexpr bool vectorize = + satisfies_any_of::value && + satisfies_none_of::value && + (!std::is_reference::value || + (std::is_lvalue_reference::value && std::is_const::value)); + // Accept this type: an array for vectorized types, otherwise the type as-is: + using type = conditional_t, array::forcecast>, T>; +}; + + +// py::vectorize when a return type is present +template +struct vectorize_returned_array { + using Type = array_t; + + static Type create(broadcast_trivial trivial, const std::vector &shape) { + if (trivial == broadcast_trivial::f_trivial) + return array_t(shape); + else + return array_t(shape); + } + + static Return *mutable_data(Type &array) { + return array.mutable_data(); + } + + static Return call(Func &f, Args &... args) { + return f(args...); + } + + static void call(Return *out, size_t i, Func &f, Args &... args) { + out[i] = f(args...); + } +}; + +// py::vectorize when a return type is not present +template +struct vectorize_returned_array { + using Type = none; + + static Type create(broadcast_trivial, const std::vector &) { + return none(); + } + + static void *mutable_data(Type &) { + return nullptr; + } + + static detail::void_type call(Func &f, Args &... args) { + f(args...); + return {}; + } + + static void call(void *, size_t, Func &f, Args &... args) { + f(args...); + } +}; + + +template +struct vectorize_helper { + +// NVCC for some reason breaks if NVectorized is private +#ifdef __CUDACC__ +public: +#else +private: +#endif + + static constexpr size_t N = sizeof...(Args); + static constexpr size_t NVectorized = constexpr_sum(vectorize_arg::vectorize...); + static_assert(NVectorized >= 1, + "pybind11::vectorize(...) requires a function with at least one vectorizable argument"); + +public: + template + explicit vectorize_helper(T &&f) : f(std::forward(f)) { } + + object operator()(typename vectorize_arg::type... args) { + return run(args..., + make_index_sequence(), + select_indices::vectorize...>(), + make_index_sequence()); + } + +private: + remove_reference_t f; + + // Internal compiler error in MSVC 19.16.27025.1 (Visual Studio 2017 15.9.4), when compiling with "/permissive-" flag + // when arg_call_types is manually inlined. + using arg_call_types = std::tuple::call_type...>; + template using param_n_t = typename std::tuple_element::type; + + using returned_array = vectorize_returned_array; + + // Runs a vectorized function given arguments tuple and three index sequences: + // - Index is the full set of 0 ... (N-1) argument indices; + // - VIndex is the subset of argument indices with vectorized parameters, letting us access + // vectorized arguments (anything not in this sequence is passed through) + // - BIndex is a incremental sequence (beginning at 0) of the same size as VIndex, so that + // we can store vectorized buffer_infos in an array (argument VIndex has its buffer at + // index BIndex in the array). + template object run( + typename vectorize_arg::type &...args, + index_sequence i_seq, index_sequence vi_seq, index_sequence bi_seq) { + + // Pointers to values the function was called with; the vectorized ones set here will start + // out as array_t pointers, but they will be changed them to T pointers before we make + // call the wrapped function. Non-vectorized pointers are left as-is. + std::array params{{ &args... }}; + + // The array of `buffer_info`s of vectorized arguments: + std::array buffers{{ reinterpret_cast(params[VIndex])->request()... }}; + + /* Determine dimensions parameters of output array */ + ssize_t nd = 0; + std::vector shape(0); + auto trivial = broadcast(buffers, nd, shape); + auto ndim = (size_t) nd; + + size_t size = std::accumulate(shape.begin(), shape.end(), (size_t) 1, std::multiplies()); + + // If all arguments are 0-dimension arrays (i.e. single values) return a plain value (i.e. + // not wrapped in an array). + if (size == 1 && ndim == 0) { + PYBIND11_EXPAND_SIDE_EFFECTS(params[VIndex] = buffers[BIndex].ptr); + return cast(returned_array::call(f, *reinterpret_cast *>(params[Index])...)); + } + + auto result = returned_array::create(trivial, shape); + + if (size == 0) return std::move(result); + + /* Call the function */ + auto mutable_data = returned_array::mutable_data(result); + if (trivial == broadcast_trivial::non_trivial) + apply_broadcast(buffers, params, mutable_data, size, shape, i_seq, vi_seq, bi_seq); + else + apply_trivial(buffers, params, mutable_data, size, i_seq, vi_seq, bi_seq); + + return std::move(result); + } + + template + void apply_trivial(std::array &buffers, + std::array ¶ms, + Return *out, + size_t size, + index_sequence, index_sequence, index_sequence) { + + // Initialize an array of mutable byte references and sizes with references set to the + // appropriate pointer in `params`; as we iterate, we'll increment each pointer by its size + // (except for singletons, which get an increment of 0). + std::array, NVectorized> vecparams{{ + std::pair( + reinterpret_cast(params[VIndex] = buffers[BIndex].ptr), + buffers[BIndex].size == 1 ? 0 : sizeof(param_n_t) + )... + }}; + + for (size_t i = 0; i < size; ++i) { + returned_array::call(out, i, f, *reinterpret_cast *>(params[Index])...); + for (auto &x : vecparams) x.first += x.second; + } + } + + template + void apply_broadcast(std::array &buffers, + std::array ¶ms, + Return *out, + size_t size, + const std::vector &output_shape, + index_sequence, index_sequence, index_sequence) { + + multi_array_iterator input_iter(buffers, output_shape); + + for (size_t i = 0; i < size; ++i, ++input_iter) { + PYBIND11_EXPAND_SIDE_EFFECTS(( + params[VIndex] = input_iter.template data() + )); + returned_array::call(out, i, f, *reinterpret_cast *>(std::get(params))...); + } + } +}; + +template +vectorize_helper +vectorize_extractor(const Func &f, Return (*) (Args ...)) { + return detail::vectorize_helper(f); +} + +template struct handle_type_name> { + static constexpr auto name = _("numpy.ndarray[") + npy_format_descriptor::name + _("]"); +}; + +PYBIND11_NAMESPACE_END(detail) + +// Vanilla pointer vectorizer: +template +detail::vectorize_helper +vectorize(Return (*f) (Args ...)) { + return detail::vectorize_helper(f); +} + +// lambda vectorizer: +template ::value, int> = 0> +auto vectorize(Func &&f) -> decltype( + detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr)) { + return detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr); +} + +// Vectorize a class method (non-const): +template ())), Return, Class *, Args...>> +Helper vectorize(Return (Class::*f)(Args...)) { + return Helper(std::mem_fn(f)); +} + +// Vectorize a class method (const): +template ())), Return, const Class *, Args...>> +Helper vectorize(Return (Class::*f)(Args...) const) { + return Helper(std::mem_fn(f)); +} + +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/extern/pybind/include/pybind11/operators.h b/extern/pybind/include/pybind11/operators.h new file mode 100644 index 00000000..086cb4cf --- /dev/null +++ b/extern/pybind/include/pybind11/operators.h @@ -0,0 +1,173 @@ +/* + pybind11/operator.h: Metatemplates for operator overloading + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +#if defined(__clang__) && !defined(__INTEL_COMPILER) +# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type())) +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +/// Enumeration with all supported operator types +enum op_id : int { + op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift, + op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert, + op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le, + op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift, + op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero, + op_repr, op_truediv, op_itruediv, op_hash +}; + +enum op_type : int { + op_l, /* base type on left */ + op_r, /* base type on right */ + op_u /* unary operator */ +}; + +struct self_t { }; +static const self_t self = self_t(); + +/// Type for an unused type slot +struct undefined_t { }; + +/// Don't warn about an unused variable +inline self_t __self() { return self; } + +/// base template of operator implementations +template struct op_impl { }; + +/// Operator implementation generator +template struct op_ { + template void execute(Class &cl, const Extra&... extra) const { + using Base = typename Class::type; + using L_type = conditional_t::value, Base, L>; + using R_type = conditional_t::value, Base, R>; + using op = op_impl; + cl.def(op::name(), &op::execute, is_operator(), extra...); + #if PY_MAJOR_VERSION < 3 + if (id == op_truediv || id == op_itruediv) + cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", + &op::execute, is_operator(), extra...); + #endif + } + template void execute_cast(Class &cl, const Extra&... extra) const { + using Base = typename Class::type; + using L_type = conditional_t::value, Base, L>; + using R_type = conditional_t::value, Base, R>; + using op = op_impl; + cl.def(op::name(), &op::execute_cast, is_operator(), extra...); + #if PY_MAJOR_VERSION < 3 + if (id == op_truediv || id == op_itruediv) + cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", + &op::execute, is_operator(), extra...); + #endif + } +}; + +#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \ + static B execute_cast(const L &l, const R &r) { return B(expr); } \ +}; \ +template struct op_impl { \ + static char const* name() { return "__" #rid "__"; } \ + static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \ + static B execute_cast(const R &r, const L &l) { return B(expr); } \ +}; \ +inline op_ op(const self_t &, const self_t &) { \ + return op_(); \ +} \ +template op_ op(const self_t &, const T &) { \ + return op_(); \ +} \ +template op_ op(const T &, const self_t &) { \ + return op_(); \ +} + +#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \ + static B execute_cast(L &l, const R &r) { return B(expr); } \ +}; \ +template op_ op(const self_t &, const T &) { \ + return op_(); \ +} + +#define PYBIND11_UNARY_OPERATOR(id, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(const L &l) -> decltype(expr) { return expr; } \ + static B execute_cast(const L &l) { return B(expr); } \ +}; \ +inline op_ op(const self_t &) { \ + return op_(); \ +} + +PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r) +PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r) +PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r) +PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) +PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r) +PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r) +PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r) +PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r) +PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r) +PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r) +PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r) +PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r) +PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r) +PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r) +PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r) +PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r) +//PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r)) +PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r) +PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r) +PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r) +PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r) +PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r) +PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r) +PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r) +PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r) +PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) +PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) +PYBIND11_UNARY_OPERATOR(neg, operator-, -l) +PYBIND11_UNARY_OPERATOR(pos, operator+, +l) +// WARNING: This usage of `abs` should only be done for existing STL overloads. +// Adding overloads directly in to the `std::` namespace is advised against: +// https://en.cppreference.com/w/cpp/language/extending_std +PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) +PYBIND11_UNARY_OPERATOR(hash, hash, std::hash()(l)) +PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) +PYBIND11_UNARY_OPERATOR(bool, operator!, !!l) +PYBIND11_UNARY_OPERATOR(int, int_, (int) l) +PYBIND11_UNARY_OPERATOR(float, float_, (double) l) + +#undef PYBIND11_BINARY_OPERATOR +#undef PYBIND11_INPLACE_OPERATOR +#undef PYBIND11_UNARY_OPERATOR +PYBIND11_NAMESPACE_END(detail) + +using detail::self; +// Add named operators so that they are accessible via `py::`. +using detail::hash; + +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/extern/pybind/include/pybind11/options.h b/extern/pybind/include/pybind11/options.h new file mode 100644 index 00000000..d74db1c6 --- /dev/null +++ b/extern/pybind/include/pybind11/options.h @@ -0,0 +1,65 @@ +/* + pybind11/options.h: global settings that are configurable at runtime. + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +class options { +public: + + // Default RAII constructor, which leaves settings as they currently are. + options() : previous_state(global_state()) {} + + // Class is non-copyable. + options(const options&) = delete; + options& operator=(const options&) = delete; + + // Destructor, which restores settings that were in effect before. + ~options() { + global_state() = previous_state; + } + + // Setter methods (affect the global state): + + options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; } + + options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; } + + options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; } + + options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; } + + // Getter methods (return the global state): + + static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; } + + static bool show_function_signatures() { return global_state().show_function_signatures; } + + // This type is not meant to be allocated on the heap. + void* operator new(size_t) = delete; + +private: + + struct state { + bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. + bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings. + }; + + static state &global_state() { + static state instance; + return instance; + } + + state previous_state; +}; + +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/pybind11.h b/extern/pybind/include/pybind11/pybind11.h new file mode 100644 index 00000000..761f2a71 --- /dev/null +++ b/extern/pybind/include/pybind11/pybind11.h @@ -0,0 +1,2378 @@ +/* + pybind11/pybind11.h: Main header file of the C++11 python + binding generator library + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#if defined(__INTEL_COMPILER) +# pragma warning push +# pragma warning disable 68 // integer conversion resulted in a change of sign +# pragma warning disable 186 // pointless comparison of unsigned integer with zero +# pragma warning disable 878 // incompatible exception specifications +# pragma warning disable 1334 // the "template" keyword used for syntactic disambiguation may only be used within a template +# pragma warning disable 1682 // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) +# pragma warning disable 1786 // function "strdup" was declared deprecated +# pragma warning disable 1875 // offsetof applied to non-POD (Plain Old Data) types is nonstandard +# pragma warning disable 2196 // warning #2196: routine is both "inline" and "noinline" +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +# pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted +# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning) +# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name +# pragma warning(disable: 4702) // warning C4702: unreachable code +# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified +# pragma warning(disable: 4505) // warning C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed (PyPy only) +#elif defined(__GNUG__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-but-set-parameter" +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# pragma GCC diagnostic ignored "-Wstrict-aliasing" +# pragma GCC diagnostic ignored "-Wattributes" +# if __GNUC__ >= 7 +# pragma GCC diagnostic ignored "-Wnoexcept-type" +# endif +#endif + +#include "attr.h" +#include "options.h" +#include "detail/class.h" +#include "detail/init.h" + +#include +#include +#include +#include + +#if defined(__GNUG__) && !defined(__clang__) +# include +#endif + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object +class cpp_function : public function { +public: + cpp_function() = default; + cpp_function(std::nullptr_t) { } + + /// Construct a cpp_function from a vanilla function pointer + template + cpp_function(Return (*f)(Args...), const Extra&... extra) { + initialize(f, f, extra...); + } + + /// Construct a cpp_function from a lambda function (possibly with internal state) + template ::value>> + cpp_function(Func &&f, const Extra&... extra) { + initialize(std::forward(f), + (detail::function_signature_t *) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (non-const, no ref-qualifier) + template + cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { + initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(std::forward(args)...); }, + (Return (*) (Class *, Arg...)) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (non-const, lvalue ref-qualifier) + /// A copy of the overload for non-const functions without explicit ref-qualifier + /// but with an added `&`. + template + cpp_function(Return (Class::*f)(Arg...)&, const Extra&... extra) { + initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*) (Class *, Arg...)) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (const, no ref-qualifier) + template + cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { + initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(std::forward(args)...); }, + (Return (*)(const Class *, Arg ...)) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (const, lvalue ref-qualifier) + /// A copy of the overload for const functions without explicit ref-qualifier + /// but with an added `&`. + template + cpp_function(Return (Class::*f)(Arg...) const&, const Extra&... extra) { + initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*)(const Class *, Arg ...)) nullptr, extra...); + } + + /// Return the function name + object name() const { return attr("__name__"); } + +protected: + /// Space optimization: don't inline this frequently instantiated fragment + PYBIND11_NOINLINE detail::function_record *make_function_record() { + return new detail::function_record(); + } + + /// Special internal constructor for functors, lambda functions, etc. + template + void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) { + using namespace detail; + struct capture { remove_reference_t f; }; + + /* Store the function including any extra state it might have (e.g. a lambda capture object) */ + auto rec = make_function_record(); + + /* Store the capture object directly in the function record if there is enough space */ + if (sizeof(capture) <= sizeof(rec->data)) { + /* Without these pragmas, GCC warns that there might not be + enough space to use the placement new operator. However, the + 'if' statement above ensures that this is the case. */ +#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wplacement-new" +#endif + new ((capture *) &rec->data) capture { std::forward(f) }; +#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 +# pragma GCC diagnostic pop +#endif + if (!std::is_trivially_destructible::value) + rec->free_data = [](function_record *r) { ((capture *) &r->data)->~capture(); }; + } else { + rec->data[0] = new capture { std::forward(f) }; + rec->free_data = [](function_record *r) { delete ((capture *) r->data[0]); }; + } + + /* Type casters for the function arguments and return value */ + using cast_in = argument_loader; + using cast_out = make_caster< + conditional_t::value, void_type, Return> + >; + + static_assert(expected_num_args(sizeof...(Args), cast_in::has_args, cast_in::has_kwargs), + "The number of argument annotations does not match the number of function arguments"); + + /* Dispatch code which converts function arguments and performs the actual function call */ + rec->impl = [](function_call &call) -> handle { + cast_in args_converter; + + /* Try to cast the function arguments into the C++ domain */ + if (!args_converter.load_args(call)) + return PYBIND11_TRY_NEXT_OVERLOAD; + + /* Invoke call policy pre-call hook */ + process_attributes::precall(call); + + /* Get a pointer to the capture object */ + auto data = (sizeof(capture) <= sizeof(call.func.data) + ? &call.func.data : call.func.data[0]); + auto *cap = const_cast(reinterpret_cast(data)); + + /* Override policy for rvalues -- usually to enforce rvp::move on an rvalue */ + return_value_policy policy = return_value_policy_override::policy(call.func.policy); + + /* Function scope guard -- defaults to the compile-to-nothing `void_type` */ + using Guard = extract_guard_t; + + /* Perform the function call */ + handle result = cast_out::cast( + std::move(args_converter).template call(cap->f), policy, call.parent); + + /* Invoke call policy post-call hook */ + process_attributes::postcall(call, result); + + return result; + }; + + /* Process any user-provided function attributes */ + process_attributes::init(extra..., rec); + + { + constexpr bool has_kw_only_args = any_of...>::value, + has_pos_only_args = any_of...>::value, + has_args = any_of...>::value, + has_arg_annotations = any_of...>::value; + static_assert(has_arg_annotations || !has_kw_only_args, "py::kw_only requires the use of argument annotations"); + static_assert(has_arg_annotations || !has_pos_only_args, "py::pos_only requires the use of argument annotations (for docstrings and aligning the annotations to the argument)"); + static_assert(!(has_args && has_kw_only_args), "py::kw_only cannot be combined with a py::args argument"); + } + + /* Generate a readable signature describing the function's arguments and return value types */ + static constexpr auto signature = _("(") + cast_in::arg_names + _(") -> ") + cast_out::name; + PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types(); + + /* Register the function with Python from generic (non-templated) code */ + initialize_generic(rec, signature.text, types.data(), sizeof...(Args)); + + if (cast_in::has_args) rec->has_args = true; + if (cast_in::has_kwargs) rec->has_kwargs = true; + + /* Stash some additional information used by an important optimization in 'functional.h' */ + using FunctionType = Return (*)(Args...); + constexpr bool is_function_ptr = + std::is_convertible::value && + sizeof(capture) == sizeof(void *); + if (is_function_ptr) { + rec->is_stateless = true; + rec->data[1] = const_cast(reinterpret_cast(&typeid(FunctionType))); + } + } + + /// Register a function call with Python (generic non-templated code goes here) + void initialize_generic(detail::function_record *rec, const char *text, + const std::type_info *const *types, size_t args) { + + /* Create copies of all referenced C-style strings */ + rec->name = strdup(rec->name ? rec->name : ""); + if (rec->doc) rec->doc = strdup(rec->doc); + for (auto &a: rec->args) { + if (a.name) + a.name = strdup(a.name); + if (a.descr) + a.descr = strdup(a.descr); + else if (a.value) + a.descr = strdup(repr(a.value).cast().c_str()); + } + + rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__"); + +#if !defined(NDEBUG) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING) + if (rec->is_constructor && !rec->is_new_style_constructor) { + const auto class_name = detail::get_fully_qualified_tp_name((PyTypeObject *) rec->scope.ptr()); + const auto func_name = std::string(rec->name); + PyErr_WarnEx( + PyExc_FutureWarning, + ("pybind11-bound class '" + class_name + "' is using an old-style " + "placement-new '" + func_name + "' which has been deprecated. See " + "the upgrade guide in pybind11's docs. This message is only visible " + "when compiled in debug mode.").c_str(), 0 + ); + } +#endif + + /* Generate a proper function signature */ + std::string signature; + size_t type_index = 0, arg_index = 0; + for (auto *pc = text; *pc != '\0'; ++pc) { + const auto c = *pc; + + if (c == '{') { + // Write arg name for everything except *args and **kwargs. + if (*(pc + 1) == '*') + continue; + // Separator for keyword-only arguments, placed before the kw + // arguments start + if (rec->nargs_kw_only > 0 && arg_index + rec->nargs_kw_only == args) + signature += "*, "; + if (arg_index < rec->args.size() && rec->args[arg_index].name) { + signature += rec->args[arg_index].name; + } else if (arg_index == 0 && rec->is_method) { + signature += "self"; + } else { + signature += "arg" + std::to_string(arg_index - (rec->is_method ? 1 : 0)); + } + signature += ": "; + } else if (c == '}') { + // Write default value if available. + if (arg_index < rec->args.size() && rec->args[arg_index].descr) { + signature += " = "; + signature += rec->args[arg_index].descr; + } + // Separator for positional-only arguments (placed after the + // argument, rather than before like * + if (rec->nargs_pos_only > 0 && (arg_index + 1) == rec->nargs_pos_only) + signature += ", /"; + arg_index++; + } else if (c == '%') { + const std::type_info *t = types[type_index++]; + if (!t) + pybind11_fail("Internal error while parsing type signature (1)"); + if (auto tinfo = detail::get_type_info(*t)) { + handle th((PyObject *) tinfo->type); + signature += + th.attr("__module__").cast() + "." + + th.attr("__qualname__").cast(); // Python 3.3+, but we backport it to earlier versions + } else if (rec->is_new_style_constructor && arg_index == 0) { + // A new-style `__init__` takes `self` as `value_and_holder`. + // Rewrite it to the proper class type. + signature += + rec->scope.attr("__module__").cast() + "." + + rec->scope.attr("__qualname__").cast(); + } else { + std::string tname(t->name()); + detail::clean_type_id(tname); + signature += tname; + } + } else { + signature += c; + } + } + + if (arg_index != args || types[type_index] != nullptr) + pybind11_fail("Internal error while parsing type signature (2)"); + +#if PY_MAJOR_VERSION < 3 + if (strcmp(rec->name, "__next__") == 0) { + std::free(rec->name); + rec->name = strdup("next"); + } else if (strcmp(rec->name, "__bool__") == 0) { + std::free(rec->name); + rec->name = strdup("__nonzero__"); + } +#endif + rec->signature = strdup(signature.c_str()); + rec->args.shrink_to_fit(); + rec->nargs = (std::uint16_t) args; + + if (rec->sibling && PYBIND11_INSTANCE_METHOD_CHECK(rec->sibling.ptr())) + rec->sibling = PYBIND11_INSTANCE_METHOD_GET_FUNCTION(rec->sibling.ptr()); + + detail::function_record *chain = nullptr, *chain_start = rec; + if (rec->sibling) { + if (PyCFunction_Check(rec->sibling.ptr())) { + auto rec_capsule = reinterpret_borrow(PyCFunction_GET_SELF(rec->sibling.ptr())); + chain = (detail::function_record *) rec_capsule; + /* Never append a method to an overload chain of a parent class; + instead, hide the parent's overloads in this case */ + if (!chain->scope.is(rec->scope)) + chain = nullptr; + } + // Don't trigger for things like the default __init__, which are wrapper_descriptors that we are intentionally replacing + else if (!rec->sibling.is_none() && rec->name[0] != '_') + pybind11_fail("Cannot overload existing non-function object \"" + std::string(rec->name) + + "\" with a function of the same name"); + } + + if (!chain) { + /* No existing overload was found, create a new function object */ + rec->def = new PyMethodDef(); + std::memset(rec->def, 0, sizeof(PyMethodDef)); + rec->def->ml_name = rec->name; + rec->def->ml_meth = reinterpret_cast(reinterpret_cast(*dispatcher)); + rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; + + capsule rec_capsule(rec, [](void *ptr) { + destruct((detail::function_record *) ptr); + }); + + object scope_module; + if (rec->scope) { + if (hasattr(rec->scope, "__module__")) { + scope_module = rec->scope.attr("__module__"); + } else if (hasattr(rec->scope, "__name__")) { + scope_module = rec->scope.attr("__name__"); + } + } + + m_ptr = PyCFunction_NewEx(rec->def, rec_capsule.ptr(), scope_module.ptr()); + if (!m_ptr) + pybind11_fail("cpp_function::cpp_function(): Could not allocate function object"); + } else { + /* Append at the beginning or end of the overload chain */ + m_ptr = rec->sibling.ptr(); + inc_ref(); + if (chain->is_method != rec->is_method) + pybind11_fail("overloading a method with both static and instance methods is not supported; " + #if defined(NDEBUG) + "compile in debug mode for more details" + #else + "error while attempting to bind " + std::string(rec->is_method ? "instance" : "static") + " method " + + std::string(pybind11::str(rec->scope.attr("__name__"))) + "." + std::string(rec->name) + signature + #endif + ); + + if (rec->prepend) { + // Beginning of chain; we need to replace the capsule's current head-of-the-chain + // pointer with this one, then make this one point to the previous head of the + // chain. + chain_start = rec; + rec->next = chain; + auto rec_capsule = reinterpret_borrow(((PyCFunctionObject *) m_ptr)->m_self); + rec_capsule.set_pointer(rec); + } else { + // Or end of chain (normal behavior) + chain_start = chain; + while (chain->next) + chain = chain->next; + chain->next = rec; + } + } + + std::string signatures; + int index = 0; + /* Create a nice pydoc rec including all signatures and + docstrings of the functions in the overload chain */ + if (chain && options::show_function_signatures()) { + // First a generic signature + signatures += rec->name; + signatures += "(*args, **kwargs)\n"; + signatures += "Overloaded function.\n\n"; + } + // Then specific overload signatures + bool first_user_def = true; + for (auto it = chain_start; it != nullptr; it = it->next) { + if (options::show_function_signatures()) { + if (index > 0) signatures += "\n"; + if (chain) + signatures += std::to_string(++index) + ". "; + signatures += rec->name; + signatures += it->signature; + signatures += "\n"; + } + if (it->doc && strlen(it->doc) > 0 && options::show_user_defined_docstrings()) { + // If we're appending another docstring, and aren't printing function signatures, we + // need to append a newline first: + if (!options::show_function_signatures()) { + if (first_user_def) first_user_def = false; + else signatures += "\n"; + } + if (options::show_function_signatures()) signatures += "\n"; + signatures += it->doc; + if (options::show_function_signatures()) signatures += "\n"; + } + } + + /* Install docstring */ + auto *func = (PyCFunctionObject *) m_ptr; + if (func->m_ml->ml_doc) + std::free(const_cast(func->m_ml->ml_doc)); + func->m_ml->ml_doc = strdup(signatures.c_str()); + + if (rec->is_method) { + m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr()); + if (!m_ptr) + pybind11_fail("cpp_function::cpp_function(): Could not allocate instance method object"); + Py_DECREF(func); + } + } + + /// When a cpp_function is GCed, release any memory allocated by pybind11 + static void destruct(detail::function_record *rec) { + // If on Python 3.9, check the interpreter "MICRO" (patch) version. + // If this is running on 3.9.0, we have to work around a bug. + #if !defined(PYPY_VERSION) && PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 9 + static bool is_zero = Py_GetVersion()[4] == '0'; + #endif + + while (rec) { + detail::function_record *next = rec->next; + if (rec->free_data) + rec->free_data(rec); + std::free((char *) rec->name); + std::free((char *) rec->doc); + std::free((char *) rec->signature); + for (auto &arg: rec->args) { + std::free(const_cast(arg.name)); + std::free(const_cast(arg.descr)); + arg.value.dec_ref(); + } + if (rec->def) { + std::free(const_cast(rec->def->ml_doc)); + // Python 3.9.0 decref's these in the wrong order; rec->def + // If loaded on 3.9.0, let these leak (use Python 3.9.1 at runtime to fix) + // See https://github.com/python/cpython/pull/22670 + #if !defined(PYPY_VERSION) && PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 9 + if (!is_zero) + delete rec->def; + #else + delete rec->def; + #endif + } + delete rec; + rec = next; + } + } + + /// Main dispatch logic for calls to functions bound using pybind11 + static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) { + using namespace detail; + + /* Iterator over the list of potentially admissible overloads */ + const function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr), + *it = overloads; + + /* Need to know how many arguments + keyword arguments there are to pick the right overload */ + const auto n_args_in = (size_t) PyTuple_GET_SIZE(args_in); + + handle parent = n_args_in > 0 ? PyTuple_GET_ITEM(args_in, 0) : nullptr, + result = PYBIND11_TRY_NEXT_OVERLOAD; + + auto self_value_and_holder = value_and_holder(); + if (overloads->is_constructor) { + const auto tinfo = get_type_info((PyTypeObject *) overloads->scope.ptr()); + const auto pi = reinterpret_cast(parent.ptr()); + self_value_and_holder = pi->get_value_and_holder(tinfo, false); + + if (!self_value_and_holder.type || !self_value_and_holder.inst) { + PyErr_SetString(PyExc_TypeError, "__init__(self, ...) called with invalid `self` argument"); + return nullptr; + } + + // If this value is already registered it must mean __init__ is invoked multiple times; + // we really can't support that in C++, so just ignore the second __init__. + if (self_value_and_holder.instance_registered()) + return none().release().ptr(); + } + + try { + // We do this in two passes: in the first pass, we load arguments with `convert=false`; + // in the second, we allow conversion (except for arguments with an explicit + // py::arg().noconvert()). This lets us prefer calls without conversion, with + // conversion as a fallback. + std::vector second_pass; + + // However, if there are no overloads, we can just skip the no-convert pass entirely + const bool overloaded = it != nullptr && it->next != nullptr; + + for (; it != nullptr; it = it->next) { + + /* For each overload: + 1. Copy all positional arguments we were given, also checking to make sure that + named positional arguments weren't *also* specified via kwarg. + 2. If we weren't given enough, try to make up the omitted ones by checking + whether they were provided by a kwarg matching the `py::arg("name")` name. If + so, use it (and remove it from kwargs; if not, see if the function binding + provided a default that we can use. + 3. Ensure that either all keyword arguments were "consumed", or that the function + takes a kwargs argument to accept unconsumed kwargs. + 4. Any positional arguments still left get put into a tuple (for args), and any + leftover kwargs get put into a dict. + 5. Pack everything into a vector; if we have py::args or py::kwargs, they are an + extra tuple or dict at the end of the positional arguments. + 6. Call the function call dispatcher (function_record::impl) + + If one of these fail, move on to the next overload and keep trying until we get a + result other than PYBIND11_TRY_NEXT_OVERLOAD. + */ + + const function_record &func = *it; + size_t num_args = func.nargs; // Number of positional arguments that we need + if (func.has_args) --num_args; // (but don't count py::args + if (func.has_kwargs) --num_args; // or py::kwargs) + size_t pos_args = num_args - func.nargs_kw_only; + + if (!func.has_args && n_args_in > pos_args) + continue; // Too many positional arguments for this overload + + if (n_args_in < pos_args && func.args.size() < pos_args) + continue; // Not enough positional arguments given, and not enough defaults to fill in the blanks + + function_call call(func, parent); + + size_t args_to_copy = (std::min)(pos_args, n_args_in); // Protect std::min with parentheses + size_t args_copied = 0; + + // 0. Inject new-style `self` argument + if (func.is_new_style_constructor) { + // The `value` may have been preallocated by an old-style `__init__` + // if it was a preceding candidate for overload resolution. + if (self_value_and_holder) + self_value_and_holder.type->dealloc(self_value_and_holder); + + call.init_self = PyTuple_GET_ITEM(args_in, 0); + call.args.emplace_back(reinterpret_cast(&self_value_and_holder)); + call.args_convert.push_back(false); + ++args_copied; + } + + // 1. Copy any position arguments given. + bool bad_arg = false; + for (; args_copied < args_to_copy; ++args_copied) { + const argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr; + if (kwargs_in && arg_rec && arg_rec->name && PyDict_GetItemString(kwargs_in, arg_rec->name)) { + bad_arg = true; + break; + } + + handle arg(PyTuple_GET_ITEM(args_in, args_copied)); + if (arg_rec && !arg_rec->none && arg.is_none()) { + bad_arg = true; + break; + } + call.args.push_back(arg); + call.args_convert.push_back(arg_rec ? arg_rec->convert : true); + } + if (bad_arg) + continue; // Maybe it was meant for another overload (issue #688) + + // We'll need to copy this if we steal some kwargs for defaults + dict kwargs = reinterpret_borrow(kwargs_in); + + // 1.5. Fill in any missing pos_only args from defaults if they exist + if (args_copied < func.nargs_pos_only) { + for (; args_copied < func.nargs_pos_only; ++args_copied) { + const auto &arg_rec = func.args[args_copied]; + handle value; + + if (arg_rec.value) { + value = arg_rec.value; + } + if (value) { + call.args.push_back(value); + call.args_convert.push_back(arg_rec.convert); + } else + break; + } + + if (args_copied < func.nargs_pos_only) + continue; // Not enough defaults to fill the positional arguments + } + + // 2. Check kwargs and, failing that, defaults that may help complete the list + if (args_copied < num_args) { + bool copied_kwargs = false; + + for (; args_copied < num_args; ++args_copied) { + const auto &arg_rec = func.args[args_copied]; + + handle value; + if (kwargs_in && arg_rec.name) + value = PyDict_GetItemString(kwargs.ptr(), arg_rec.name); + + if (value) { + // Consume a kwargs value + if (!copied_kwargs) { + kwargs = reinterpret_steal(PyDict_Copy(kwargs.ptr())); + copied_kwargs = true; + } + PyDict_DelItemString(kwargs.ptr(), arg_rec.name); + } else if (arg_rec.value) { + value = arg_rec.value; + } + + if (!arg_rec.none && value.is_none()) { + break; + } + + if (value) { + call.args.push_back(value); + call.args_convert.push_back(arg_rec.convert); + } + else + break; + } + + if (args_copied < num_args) + continue; // Not enough arguments, defaults, or kwargs to fill the positional arguments + } + + // 3. Check everything was consumed (unless we have a kwargs arg) + if (kwargs && !kwargs.empty() && !func.has_kwargs) + continue; // Unconsumed kwargs, but no py::kwargs argument to accept them + + // 4a. If we have a py::args argument, create a new tuple with leftovers + if (func.has_args) { + tuple extra_args; + if (args_to_copy == 0) { + // We didn't copy out any position arguments from the args_in tuple, so we + // can reuse it directly without copying: + extra_args = reinterpret_borrow(args_in); + } else if (args_copied >= n_args_in) { + extra_args = tuple(0); + } else { + size_t args_size = n_args_in - args_copied; + extra_args = tuple(args_size); + for (size_t i = 0; i < args_size; ++i) { + extra_args[i] = PyTuple_GET_ITEM(args_in, args_copied + i); + } + } + call.args.push_back(extra_args); + call.args_convert.push_back(false); + call.args_ref = std::move(extra_args); + } + + // 4b. If we have a py::kwargs, pass on any remaining kwargs + if (func.has_kwargs) { + if (!kwargs.ptr()) + kwargs = dict(); // If we didn't get one, send an empty one + call.args.push_back(kwargs); + call.args_convert.push_back(false); + call.kwargs_ref = std::move(kwargs); + } + + // 5. Put everything in a vector. Not technically step 5, we've been building it + // in `call.args` all along. + #if !defined(NDEBUG) + if (call.args.size() != func.nargs || call.args_convert.size() != func.nargs) + pybind11_fail("Internal error: function call dispatcher inserted wrong number of arguments!"); + #endif + + std::vector second_pass_convert; + if (overloaded) { + // We're in the first no-convert pass, so swap out the conversion flags for a + // set of all-false flags. If the call fails, we'll swap the flags back in for + // the conversion-allowed call below. + second_pass_convert.resize(func.nargs, false); + call.args_convert.swap(second_pass_convert); + } + + // 6. Call the function. + try { + loader_life_support guard{}; + result = func.impl(call); + } catch (reference_cast_error &) { + result = PYBIND11_TRY_NEXT_OVERLOAD; + } + + if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) + break; + + if (overloaded) { + // The (overloaded) call failed; if the call has at least one argument that + // permits conversion (i.e. it hasn't been explicitly specified `.noconvert()`) + // then add this call to the list of second pass overloads to try. + for (size_t i = func.is_method ? 1 : 0; i < pos_args; i++) { + if (second_pass_convert[i]) { + // Found one: swap the converting flags back in and store the call for + // the second pass. + call.args_convert.swap(second_pass_convert); + second_pass.push_back(std::move(call)); + break; + } + } + } + } + + if (overloaded && !second_pass.empty() && result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { + // The no-conversion pass finished without success, try again with conversion allowed + for (auto &call : second_pass) { + try { + loader_life_support guard{}; + result = call.func.impl(call); + } catch (reference_cast_error &) { + result = PYBIND11_TRY_NEXT_OVERLOAD; + } + + if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) { + // The error reporting logic below expects 'it' to be valid, as it would be + // if we'd encountered this failure in the first-pass loop. + if (!result) + it = &call.func; + break; + } + } + } + } catch (error_already_set &e) { + e.restore(); + return nullptr; +#ifdef __GLIBCXX__ + } catch ( abi::__forced_unwind& ) { + throw; +#endif + } catch (...) { + /* When an exception is caught, give each registered exception + translator a chance to translate it to a Python exception + in reverse order of registration. + + A translator may choose to do one of the following: + + - catch the exception and call PyErr_SetString or PyErr_SetObject + to set a standard (or custom) Python exception, or + - do nothing and let the exception fall through to the next translator, or + - delegate translation to the next translator by throwing a new type of exception. */ + + auto last_exception = std::current_exception(); + auto ®istered_exception_translators = get_internals().registered_exception_translators; + for (auto& translator : registered_exception_translators) { + try { + translator(last_exception); + } catch (...) { + last_exception = std::current_exception(); + continue; + } + return nullptr; + } + PyErr_SetString(PyExc_SystemError, "Exception escaped from default exception translator!"); + return nullptr; + } + + auto append_note_if_missing_header_is_suspected = [](std::string &msg) { + if (msg.find("std::") != std::string::npos) { + msg += "\n\n" + "Did you forget to `#include `? Or ,\n" + ", , etc. Some automatic\n" + "conversions are optional and require extra headers to be included\n" + "when compiling your pybind11 module."; + } + }; + + if (result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { + if (overloads->is_operator) + return handle(Py_NotImplemented).inc_ref().ptr(); + + std::string msg = std::string(overloads->name) + "(): incompatible " + + std::string(overloads->is_constructor ? "constructor" : "function") + + " arguments. The following argument types are supported:\n"; + + int ctr = 0; + for (const function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) { + msg += " "+ std::to_string(++ctr) + ". "; + + bool wrote_sig = false; + if (overloads->is_constructor) { + // For a constructor, rewrite `(self: Object, arg0, ...) -> NoneType` as `Object(arg0, ...)` + std::string sig = it2->signature; + size_t start = sig.find('(') + 7; // skip "(self: " + if (start < sig.size()) { + // End at the , for the next argument + size_t end = sig.find(", "), next = end + 2; + size_t ret = sig.rfind(" -> "); + // Or the ), if there is no comma: + if (end >= sig.size()) next = end = sig.find(')'); + if (start < end && next < sig.size()) { + msg.append(sig, start, end - start); + msg += '('; + msg.append(sig, next, ret - next); + wrote_sig = true; + } + } + } + if (!wrote_sig) msg += it2->signature; + + msg += "\n"; + } + msg += "\nInvoked with: "; + auto args_ = reinterpret_borrow(args_in); + bool some_args = false; + for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) { + if (!some_args) some_args = true; + else msg += ", "; + try { + msg += pybind11::repr(args_[ti]); + } catch (const error_already_set&) { + msg += ""; + } + } + if (kwargs_in) { + auto kwargs = reinterpret_borrow(kwargs_in); + if (!kwargs.empty()) { + if (some_args) msg += "; "; + msg += "kwargs: "; + bool first = true; + for (auto kwarg : kwargs) { + if (first) first = false; + else msg += ", "; + msg += pybind11::str("{}=").format(kwarg.first); + try { + msg += pybind11::repr(kwarg.second); + } catch (const error_already_set&) { + msg += ""; + } + } + } + } + + append_note_if_missing_header_is_suspected(msg); + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return nullptr; + } else if (!result) { + std::string msg = "Unable to convert function return value to a " + "Python type! The signature was\n\t"; + msg += it->signature; + append_note_if_missing_header_is_suspected(msg); + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return nullptr; + } else { + if (overloads->is_constructor && !self_value_and_holder.holder_constructed()) { + auto *pi = reinterpret_cast(parent.ptr()); + self_value_and_holder.type->init_instance(pi, nullptr); + } + return result.ptr(); + } + } +}; + +/// Wrapper for Python extension modules +class module_ : public object { +public: + PYBIND11_OBJECT_DEFAULT(module_, object, PyModule_Check) + + /// Create a new top-level Python module with the given name and docstring + PYBIND11_DEPRECATED("Use PYBIND11_MODULE or module_::create_extension_module instead") + explicit module_(const char *name, const char *doc = nullptr) { +#if PY_MAJOR_VERSION >= 3 + *this = create_extension_module(name, doc, new PyModuleDef()); +#else + *this = create_extension_module(name, doc, nullptr); +#endif + } + + /** \rst + Create Python binding for a new function within the module scope. ``Func`` + can be a plain C++ function, a function pointer, or a lambda function. For + details on the ``Extra&& ... extra`` argument, see section :ref:`extras`. + \endrst */ + template + module_ &def(const char *name_, Func &&f, const Extra& ... extra) { + cpp_function func(std::forward(f), name(name_), scope(*this), + sibling(getattr(*this, name_, none())), extra...); + // NB: allow overwriting here because cpp_function sets up a chain with the intention of + // overwriting (and has already checked internally that it isn't overwriting non-functions). + add_object(name_, func, true /* overwrite */); + return *this; + } + + /** \rst + Create and return a new Python submodule with the given name and docstring. + This also works recursively, i.e. + + .. code-block:: cpp + + py::module_ m("example", "pybind11 example plugin"); + py::module_ m2 = m.def_submodule("sub", "A submodule of 'example'"); + py::module_ m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'"); + \endrst */ + module_ def_submodule(const char *name, const char *doc = nullptr) { + std::string full_name = std::string(PyModule_GetName(m_ptr)) + + std::string(".") + std::string(name); + auto result = reinterpret_borrow(PyImport_AddModule(full_name.c_str())); + if (doc && options::show_user_defined_docstrings()) + result.attr("__doc__") = pybind11::str(doc); + attr(name) = result; + return result; + } + + /// Import and return a module or throws `error_already_set`. + static module_ import(const char *name) { + PyObject *obj = PyImport_ImportModule(name); + if (!obj) + throw error_already_set(); + return reinterpret_steal(obj); + } + + /// Reload the module or throws `error_already_set`. + void reload() { + PyObject *obj = PyImport_ReloadModule(ptr()); + if (!obj) + throw error_already_set(); + *this = reinterpret_steal(obj); + } + + /** \rst + Adds an object to the module using the given name. Throws if an object with the given name + already exists. + + ``overwrite`` should almost always be false: attempting to overwrite objects that pybind11 has + established will, in most cases, break things. + \endrst */ + PYBIND11_NOINLINE void add_object(const char *name, handle obj, bool overwrite = false) { + if (!overwrite && hasattr(*this, name)) + pybind11_fail("Error during initialization: multiple incompatible definitions with name \"" + + std::string(name) + "\""); + + PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); + } + +#if PY_MAJOR_VERSION >= 3 + using module_def = PyModuleDef; +#else + struct module_def {}; +#endif + + /** \rst + Create a new top-level module that can be used as the main module of a C extension. + + For Python 3, ``def`` should point to a staticly allocated module_def. + For Python 2, ``def`` can be a nullptr and is completely ignored. + \endrst */ + static module_ create_extension_module(const char *name, const char *doc, module_def *def) { +#if PY_MAJOR_VERSION >= 3 + // module_def is PyModuleDef + def = new (def) PyModuleDef { // Placement new (not an allocation). + /* m_base */ PyModuleDef_HEAD_INIT, + /* m_name */ name, + /* m_doc */ options::show_user_defined_docstrings() ? doc : nullptr, + /* m_size */ -1, + /* m_methods */ nullptr, + /* m_slots */ nullptr, + /* m_traverse */ nullptr, + /* m_clear */ nullptr, + /* m_free */ nullptr + }; + auto m = PyModule_Create(def); +#else + // Ignore module_def *def; only necessary for Python 3 + (void) def; + auto m = Py_InitModule3(name, nullptr, options::show_user_defined_docstrings() ? doc : nullptr); +#endif + if (m == nullptr) { + if (PyErr_Occurred()) + throw error_already_set(); + pybind11_fail("Internal error in module_::create_extension_module()"); + } + // TODO: Sould be reinterpret_steal for Python 3, but Python also steals it again when returned from PyInit_... + // For Python 2, reinterpret_borrow is correct. + return reinterpret_borrow(m); + } +}; + +// When inside a namespace (or anywhere as long as it's not the first item on a line), +// C++20 allows "module" to be used. This is provided for backward compatibility, and for +// simplicity, if someone wants to use py::module for example, that is perfectly safe. +using module = module_; + +/// \ingroup python_builtins +/// Return a dictionary representing the global variables in the current execution frame, +/// or ``__main__.__dict__`` if there is no frame (usually when the interpreter is embedded). +inline dict globals() { + PyObject *p = PyEval_GetGlobals(); + return reinterpret_borrow(p ? p : module_::import("__main__").attr("__dict__").ptr()); +} + +PYBIND11_NAMESPACE_BEGIN(detail) +/// Generic support for creating new Python heap types +class generic_type : public object { +public: + PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check) +protected: + void initialize(const type_record &rec) { + if (rec.scope && hasattr(rec.scope, "__dict__") && rec.scope.attr("__dict__").contains(rec.name)) + pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) + + "\": an object with that name is already defined"); + + if (rec.module_local ? get_local_type_info(*rec.type) : get_global_type_info(*rec.type)) + pybind11_fail("generic_type: type \"" + std::string(rec.name) + + "\" is already registered!"); + + m_ptr = make_new_python_type(rec); + + /* Register supplemental type information in C++ dict */ + auto *tinfo = new detail::type_info(); + tinfo->type = (PyTypeObject *) m_ptr; + tinfo->cpptype = rec.type; + tinfo->type_size = rec.type_size; + tinfo->type_align = rec.type_align; + tinfo->operator_new = rec.operator_new; + tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size); + tinfo->init_instance = rec.init_instance; + tinfo->dealloc = rec.dealloc; + tinfo->simple_type = true; + tinfo->simple_ancestors = true; + tinfo->default_holder = rec.default_holder; + tinfo->module_local = rec.module_local; + + auto &internals = get_internals(); + auto tindex = std::type_index(*rec.type); + tinfo->direct_conversions = &internals.direct_conversions[tindex]; + if (rec.module_local) + registered_local_types_cpp()[tindex] = tinfo; + else + internals.registered_types_cpp[tindex] = tinfo; + internals.registered_types_py[(PyTypeObject *) m_ptr] = { tinfo }; + + if (rec.bases.size() > 1 || rec.multiple_inheritance) { + mark_parents_nonsimple(tinfo->type); + tinfo->simple_ancestors = false; + } + else if (rec.bases.size() == 1) { + auto parent_tinfo = get_type_info((PyTypeObject *) rec.bases[0].ptr()); + tinfo->simple_ancestors = parent_tinfo->simple_ancestors; + } + + if (rec.module_local) { + // Stash the local typeinfo and loader so that external modules can access it. + tinfo->module_local_load = &type_caster_generic::local_load; + setattr(m_ptr, PYBIND11_MODULE_LOCAL_ID, capsule(tinfo)); + } + } + + /// Helper function which tags all parents of a type using mult. inheritance + void mark_parents_nonsimple(PyTypeObject *value) { + auto t = reinterpret_borrow(value->tp_bases); + for (handle h : t) { + auto tinfo2 = get_type_info((PyTypeObject *) h.ptr()); + if (tinfo2) + tinfo2->simple_type = false; + mark_parents_nonsimple((PyTypeObject *) h.ptr()); + } + } + + void install_buffer_funcs( + buffer_info *(*get_buffer)(PyObject *, void *), + void *get_buffer_data) { + auto *type = (PyHeapTypeObject*) m_ptr; + auto tinfo = detail::get_type_info(&type->ht_type); + + if (!type->ht_type.tp_as_buffer) + pybind11_fail( + "To be able to register buffer protocol support for the type '" + + get_fully_qualified_tp_name(tinfo->type) + + "' the associated class<>(..) invocation must " + "include the pybind11::buffer_protocol() annotation!"); + + tinfo->get_buffer = get_buffer; + tinfo->get_buffer_data = get_buffer_data; + } + + // rec_func must be set for either fget or fset. + void def_property_static_impl(const char *name, + handle fget, handle fset, + detail::function_record *rec_func) { + const auto is_static = rec_func && !(rec_func->is_method && rec_func->scope); + const auto has_doc = rec_func && rec_func->doc && pybind11::options::show_user_defined_docstrings(); + auto property = handle((PyObject *) (is_static ? get_internals().static_property_type + : &PyProperty_Type)); + attr(name) = property(fget.ptr() ? fget : none(), + fset.ptr() ? fset : none(), + /*deleter*/none(), + pybind11::str(has_doc ? rec_func->doc : "")); + } +}; + +/// Set the pointer to operator new if it exists. The cast is needed because it can be overloaded. +template (T::operator new))>> +void set_operator_new(type_record *r) { r->operator_new = &T::operator new; } + +template void set_operator_new(...) { } + +template struct has_operator_delete : std::false_type { }; +template struct has_operator_delete(T::operator delete))>> + : std::true_type { }; +template struct has_operator_delete_size : std::false_type { }; +template struct has_operator_delete_size(T::operator delete))>> + : std::true_type { }; +/// Call class-specific delete if it exists or global otherwise. Can also be an overload set. +template ::value, int> = 0> +void call_operator_delete(T *p, size_t, size_t) { T::operator delete(p); } +template ::value && has_operator_delete_size::value, int> = 0> +void call_operator_delete(T *p, size_t s, size_t) { T::operator delete(p, s); } + +inline void call_operator_delete(void *p, size_t s, size_t a) { + (void)s; (void)a; + #if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912) + if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { + #ifdef __cpp_sized_deallocation + ::operator delete(p, s, std::align_val_t(a)); + #else + ::operator delete(p, std::align_val_t(a)); + #endif + return; + } + #endif + #ifdef __cpp_sized_deallocation + ::operator delete(p, s); + #else + ::operator delete(p); + #endif +} + +inline void add_class_method(object& cls, const char *name_, const cpp_function &cf) { + cls.attr(cf.name()) = cf; + if (strcmp(name_, "__eq__") == 0 && !cls.attr("__dict__").contains("__hash__")) { + cls.attr("__hash__") = none(); + } +} + +PYBIND11_NAMESPACE_END(detail) + +/// Given a pointer to a member function, cast it to its `Derived` version. +/// Forward everything else unchanged. +template +auto method_adaptor(F &&f) -> decltype(std::forward(f)) { return std::forward(f); } + +template +auto method_adaptor(Return (Class::*pmf)(Args...)) -> Return (Derived::*)(Args...) { + static_assert(detail::is_accessible_base_of::value, + "Cannot bind an inaccessible base class method; use a lambda definition instead"); + return pmf; +} + +template +auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(Args...) const { + static_assert(detail::is_accessible_base_of::value, + "Cannot bind an inaccessible base class method; use a lambda definition instead"); + return pmf; +} + +template +class class_ : public detail::generic_type { + template using is_holder = detail::is_holder_type; + template using is_subtype = detail::is_strict_base_of; + template using is_base = detail::is_strict_base_of; + // struct instead of using here to help MSVC: + template struct is_valid_class_option : + detail::any_of, is_subtype, is_base> {}; + +public: + using type = type_; + using type_alias = detail::exactly_one_t; + constexpr static bool has_alias = !std::is_void::value; + using holder_type = detail::exactly_one_t, options...>; + + static_assert(detail::all_of...>::value, + "Unknown/invalid class_ template parameters provided"); + + static_assert(!has_alias || std::is_polymorphic::value, + "Cannot use an alias class with a non-polymorphic type"); + + PYBIND11_OBJECT(class_, generic_type, PyType_Check) + + template + class_(handle scope, const char *name, const Extra &... extra) { + using namespace detail; + + // MI can only be specified via class_ template options, not constructor parameters + static_assert( + none_of...>::value || // no base class arguments, or: + ( constexpr_sum(is_pyobject::value...) == 1 && // Exactly one base + constexpr_sum(is_base::value...) == 0 && // no template option bases + none_of...>::value), // no multiple_inheritance attr + "Error: multiple inheritance bases must be specified via class_ template options"); + + type_record record; + record.scope = scope; + record.name = name; + record.type = &typeid(type); + record.type_size = sizeof(conditional_t); + record.type_align = alignof(conditional_t&); + record.holder_size = sizeof(holder_type); + record.init_instance = init_instance; + record.dealloc = dealloc; + record.default_holder = detail::is_instantiation::value; + + set_operator_new(&record); + + /* Register base classes specified via template arguments to class_, if any */ + PYBIND11_EXPAND_SIDE_EFFECTS(add_base(record)); + + /* Process optional arguments, if any */ + process_attributes::init(extra..., &record); + + generic_type::initialize(record); + + if (has_alias) { + auto &instances = record.module_local ? registered_local_types_cpp() : get_internals().registered_types_cpp; + instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))]; + } + } + + template ::value, int> = 0> + static void add_base(detail::type_record &rec) { + rec.add_base(typeid(Base), [](void *src) -> void * { + return static_cast(reinterpret_cast(src)); + }); + } + + template ::value, int> = 0> + static void add_base(detail::type_record &) { } + + template + class_ &def(const char *name_, Func&& f, const Extra&... extra) { + cpp_function cf(method_adaptor(std::forward(f)), name(name_), is_method(*this), + sibling(getattr(*this, name_, none())), extra...); + add_class_method(*this, name_, cf); + return *this; + } + + template class_ & + def_static(const char *name_, Func &&f, const Extra&... extra) { + static_assert(!std::is_member_function_pointer::value, + "def_static(...) called with a non-static member function pointer"); + cpp_function cf(std::forward(f), name(name_), scope(*this), + sibling(getattr(*this, name_, none())), extra...); + attr(cf.name()) = staticmethod(cf); + return *this; + } + + template + class_ &def(const detail::op_ &op, const Extra&... extra) { + op.execute(*this, extra...); + return *this; + } + + template + class_ & def_cast(const detail::op_ &op, const Extra&... extra) { + op.execute_cast(*this, extra...); + return *this; + } + + template + class_ &def(const detail::initimpl::constructor &init, const Extra&... extra) { + init.execute(*this, extra...); + return *this; + } + + template + class_ &def(const detail::initimpl::alias_constructor &init, const Extra&... extra) { + init.execute(*this, extra...); + return *this; + } + + template + class_ &def(detail::initimpl::factory &&init, const Extra&... extra) { + std::move(init).execute(*this, extra...); + return *this; + } + + template + class_ &def(detail::initimpl::pickle_factory &&pf, const Extra &...extra) { + std::move(pf).execute(*this, extra...); + return *this; + } + + template + class_& def_buffer(Func &&func) { + struct capture { Func func; }; + auto *ptr = new capture { std::forward(func) }; + install_buffer_funcs([](PyObject *obj, void *ptr) -> buffer_info* { + detail::make_caster caster; + if (!caster.load(obj, false)) + return nullptr; + return new buffer_info(((capture *) ptr)->func(caster)); + }, ptr); + weakref(m_ptr, cpp_function([ptr](handle wr) { + delete ptr; + wr.dec_ref(); + })).release(); + return *this; + } + + template + class_ &def_buffer(Return (Class::*func)(Args...)) { + return def_buffer([func] (type &obj) { return (obj.*func)(); }); + } + + template + class_ &def_buffer(Return (Class::*func)(Args...) const) { + return def_buffer([func] (const type &obj) { return (obj.*func)(); }); + } + + template + class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) { + static_assert(std::is_same::value || std::is_base_of::value, "def_readwrite() requires a class member (or base class member)"); + cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)), + fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this)); + def_property(name, fget, fset, return_value_policy::reference_internal, extra...); + return *this; + } + + template + class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) { + static_assert(std::is_same::value || std::is_base_of::value, "def_readonly() requires a class member (or base class member)"); + cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)); + def_property_readonly(name, fget, return_value_policy::reference_internal, extra...); + return *this; + } + + template + class_ &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) { + cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)), + fset([pm](object, const D &value) { *pm = value; }, scope(*this)); + def_property_static(name, fget, fset, return_value_policy::reference, extra...); + return *this; + } + + template + class_ &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) { + cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)); + def_property_readonly_static(name, fget, return_value_policy::reference, extra...); + return *this; + } + + /// Uses return_value_policy::reference_internal by default + template + class_ &def_property_readonly(const char *name, const Getter &fget, const Extra& ...extra) { + return def_property_readonly(name, cpp_function(method_adaptor(fget)), + return_value_policy::reference_internal, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) { + return def_property(name, fget, nullptr, extra...); + } + + /// Uses return_value_policy::reference by default + template + class_ &def_property_readonly_static(const char *name, const Getter &fget, const Extra& ...extra) { + return def_property_readonly_static(name, cpp_function(fget), return_value_policy::reference, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) { + return def_property_static(name, fget, nullptr, extra...); + } + + /// Uses return_value_policy::reference_internal by default + template + class_ &def_property(const char *name, const Getter &fget, const Setter &fset, const Extra& ...extra) { + return def_property(name, fget, cpp_function(method_adaptor(fset)), extra...); + } + template + class_ &def_property(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property(name, cpp_function(method_adaptor(fget)), fset, + return_value_policy::reference_internal, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property_static(name, fget, fset, is_method(*this), extra...); + } + + /// Uses return_value_policy::reference by default + template + class_ &def_property_static(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property_static(name, cpp_function(fget), fset, return_value_policy::reference, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { + static_assert( 0 == detail::constexpr_sum(std::is_base_of::value...), + "Argument annotations are not allowed for properties"); + auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset); + auto *rec_active = rec_fget; + if (rec_fget) { + char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */ + detail::process_attributes::init(extra..., rec_fget); + if (rec_fget->doc && rec_fget->doc != doc_prev) { + free(doc_prev); + rec_fget->doc = strdup(rec_fget->doc); + } + } + if (rec_fset) { + char *doc_prev = rec_fset->doc; + detail::process_attributes::init(extra..., rec_fset); + if (rec_fset->doc && rec_fset->doc != doc_prev) { + free(doc_prev); + rec_fset->doc = strdup(rec_fset->doc); + } + if (! rec_active) rec_active = rec_fset; + } + def_property_static_impl(name, fget, fset, rec_active); + return *this; + } + +private: + /// Initialize holder object, variant 1: object derives from enable_shared_from_this + template + static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, + const holder_type * /* unused */, const std::enable_shared_from_this * /* dummy */) { + try { + auto sh = std::dynamic_pointer_cast( + v_h.value_ptr()->shared_from_this()); + if (sh) { + new (std::addressof(v_h.holder())) holder_type(std::move(sh)); + v_h.set_holder_constructed(); + } + } catch (const std::bad_weak_ptr &) {} + + if (!v_h.holder_constructed() && inst->owned) { + new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); + v_h.set_holder_constructed(); + } + } + + static void init_holder_from_existing(const detail::value_and_holder &v_h, + const holder_type *holder_ptr, std::true_type /*is_copy_constructible*/) { + new (std::addressof(v_h.holder())) holder_type(*reinterpret_cast(holder_ptr)); + } + + static void init_holder_from_existing(const detail::value_and_holder &v_h, + const holder_type *holder_ptr, std::false_type /*is_copy_constructible*/) { + new (std::addressof(v_h.holder())) holder_type(std::move(*const_cast(holder_ptr))); + } + + /// Initialize holder object, variant 2: try to construct from existing holder object, if possible + static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, + const holder_type *holder_ptr, const void * /* dummy -- not enable_shared_from_this) */) { + if (holder_ptr) { + init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible()); + v_h.set_holder_constructed(); + } else if (inst->owned || detail::always_construct_holder::value) { + new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); + v_h.set_holder_constructed(); + } + } + + /// Performs instance initialization including constructing a holder and registering the known + /// instance. Should be called as soon as the `type` value_ptr is set for an instance. Takes an + /// optional pointer to an existing holder to use; if not specified and the instance is + /// `.owned`, a new holder will be constructed to manage the value pointer. + static void init_instance(detail::instance *inst, const void *holder_ptr) { + auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type))); + if (!v_h.instance_registered()) { + register_instance(inst, v_h.value_ptr(), v_h.type); + v_h.set_instance_registered(); + } + init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr()); + } + + /// Deallocates an instance; via holder, if constructed; otherwise via operator delete. + static void dealloc(detail::value_and_holder &v_h) { + // We could be deallocating because we are cleaning up after a Python exception. + // If so, the Python error indicator will be set. We need to clear that before + // running the destructor, in case the destructor code calls more Python. + // If we don't, the Python API will exit with an exception, and pybind11 will + // throw error_already_set from the C++ destructor which is forbidden and triggers + // std::terminate(). + error_scope scope; + if (v_h.holder_constructed()) { + v_h.holder().~holder_type(); + v_h.set_holder_constructed(false); + } + else { + detail::call_operator_delete(v_h.value_ptr(), + v_h.type->type_size, + v_h.type->type_align + ); + } + v_h.value_ptr() = nullptr; + } + + static detail::function_record *get_function_record(handle h) { + h = detail::get_function(h); + return h ? (detail::function_record *) reinterpret_borrow(PyCFunction_GET_SELF(h.ptr())) + : nullptr; + } +}; + +/// Binds an existing constructor taking arguments Args... +template detail::initimpl::constructor init() { return {}; } +/// Like `init()`, but the instance is always constructed through the alias class (even +/// when not inheriting on the Python side). +template detail::initimpl::alias_constructor init_alias() { return {}; } + +/// Binds a factory function as a constructor +template > +Ret init(Func &&f) { return {std::forward(f)}; } + +/// Dual-argument factory function: the first function is called when no alias is needed, the second +/// when an alias is needed (i.e. due to python-side inheritance). Arguments must be identical. +template > +Ret init(CFunc &&c, AFunc &&a) { + return {std::forward(c), std::forward(a)}; +} + +/// Binds pickling functions `__getstate__` and `__setstate__` and ensures that the type +/// returned by `__getstate__` is the same as the argument accepted by `__setstate__`. +template +detail::initimpl::pickle_factory pickle(GetState &&g, SetState &&s) { + return {std::forward(g), std::forward(s)}; +} + +PYBIND11_NAMESPACE_BEGIN(detail) + +inline str enum_name(handle arg) { + dict entries = arg.get_type().attr("__entries"); + for (auto kv : entries) { + if (handle(kv.second[int_(0)]).equal(arg)) + return pybind11::str(kv.first); + } + return "???"; +} + +struct enum_base { + enum_base(handle base, handle parent) : m_base(base), m_parent(parent) { } + + PYBIND11_NOINLINE void init(bool is_arithmetic, bool is_convertible) { + m_base.attr("__entries") = dict(); + auto property = handle((PyObject *) &PyProperty_Type); + auto static_property = handle((PyObject *) get_internals().static_property_type); + + m_base.attr("__repr__") = cpp_function( + [](object arg) -> str { + handle type = type::handle_of(arg); + object type_name = type.attr("__name__"); + return pybind11::str("<{}.{}: {}>").format(type_name, enum_name(arg), int_(arg)); + }, name("__repr__"), is_method(m_base) + ); + + m_base.attr("name") = property(cpp_function(&enum_name, name("name"), is_method(m_base))); + + m_base.attr("__str__") = cpp_function( + [](handle arg) -> str { + object type_name = type::handle_of(arg).attr("__name__"); + return pybind11::str("{}.{}").format(type_name, enum_name(arg)); + }, name("name"), is_method(m_base) + ); + + m_base.attr("__doc__") = static_property(cpp_function( + [](handle arg) -> std::string { + std::string docstring; + dict entries = arg.attr("__entries"); + if (((PyTypeObject *) arg.ptr())->tp_doc) + docstring += std::string(((PyTypeObject *) arg.ptr())->tp_doc) + "\n\n"; + docstring += "Members:"; + for (auto kv : entries) { + auto key = std::string(pybind11::str(kv.first)); + auto comment = kv.second[int_(1)]; + docstring += "\n\n " + key; + if (!comment.is_none()) + docstring += " : " + (std::string) pybind11::str(comment); + } + return docstring; + }, name("__doc__") + ), none(), none(), ""); + + m_base.attr("__members__") = static_property(cpp_function( + [](handle arg) -> dict { + dict entries = arg.attr("__entries"), m; + for (auto kv : entries) + m[kv.first] = kv.second[int_(0)]; + return m; + }, name("__members__")), none(), none(), "" + ); + + #define PYBIND11_ENUM_OP_STRICT(op, expr, strict_behavior) \ + m_base.attr(op) = cpp_function( \ + [](object a, object b) { \ + if (!type::handle_of(a).is(type::handle_of(b))) \ + strict_behavior; \ + return expr; \ + }, \ + name(op), is_method(m_base)) + + #define PYBIND11_ENUM_OP_CONV(op, expr) \ + m_base.attr(op) = cpp_function( \ + [](object a_, object b_) { \ + int_ a(a_), b(b_); \ + return expr; \ + }, \ + name(op), is_method(m_base)) + + #define PYBIND11_ENUM_OP_CONV_LHS(op, expr) \ + m_base.attr(op) = cpp_function( \ + [](object a_, object b) { \ + int_ a(a_); \ + return expr; \ + }, \ + name(op), is_method(m_base)) + + if (is_convertible) { + PYBIND11_ENUM_OP_CONV_LHS("__eq__", !b.is_none() && a.equal(b)); + PYBIND11_ENUM_OP_CONV_LHS("__ne__", b.is_none() || !a.equal(b)); + + if (is_arithmetic) { + PYBIND11_ENUM_OP_CONV("__lt__", a < b); + PYBIND11_ENUM_OP_CONV("__gt__", a > b); + PYBIND11_ENUM_OP_CONV("__le__", a <= b); + PYBIND11_ENUM_OP_CONV("__ge__", a >= b); + PYBIND11_ENUM_OP_CONV("__and__", a & b); + PYBIND11_ENUM_OP_CONV("__rand__", a & b); + PYBIND11_ENUM_OP_CONV("__or__", a | b); + PYBIND11_ENUM_OP_CONV("__ror__", a | b); + PYBIND11_ENUM_OP_CONV("__xor__", a ^ b); + PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b); + m_base.attr("__invert__") = cpp_function( + [](object arg) { return ~(int_(arg)); }, name("__invert__"), is_method(m_base)); + } + } else { + PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false); + PYBIND11_ENUM_OP_STRICT("__ne__", !int_(a).equal(int_(b)), return true); + + if (is_arithmetic) { + #define PYBIND11_THROW throw type_error("Expected an enumeration of matching type!"); + PYBIND11_ENUM_OP_STRICT("__lt__", int_(a) < int_(b), PYBIND11_THROW); + PYBIND11_ENUM_OP_STRICT("__gt__", int_(a) > int_(b), PYBIND11_THROW); + PYBIND11_ENUM_OP_STRICT("__le__", int_(a) <= int_(b), PYBIND11_THROW); + PYBIND11_ENUM_OP_STRICT("__ge__", int_(a) >= int_(b), PYBIND11_THROW); + #undef PYBIND11_THROW + } + } + + #undef PYBIND11_ENUM_OP_CONV_LHS + #undef PYBIND11_ENUM_OP_CONV + #undef PYBIND11_ENUM_OP_STRICT + + m_base.attr("__getstate__") = cpp_function( + [](object arg) { return int_(arg); }, name("__getstate__"), is_method(m_base)); + + m_base.attr("__hash__") = cpp_function( + [](object arg) { return int_(arg); }, name("__hash__"), is_method(m_base)); + } + + PYBIND11_NOINLINE void value(char const* name_, object value, const char *doc = nullptr) { + dict entries = m_base.attr("__entries"); + str name(name_); + if (entries.contains(name)) { + std::string type_name = (std::string) str(m_base.attr("__name__")); + throw value_error(type_name + ": element \"" + std::string(name_) + "\" already exists!"); + } + + entries[name] = std::make_pair(value, doc); + m_base.attr(name) = value; + } + + PYBIND11_NOINLINE void export_values() { + dict entries = m_base.attr("__entries"); + for (auto kv : entries) + m_parent.attr(kv.first) = kv.second[int_(0)]; + } + + handle m_base; + handle m_parent; +}; + +PYBIND11_NAMESPACE_END(detail) + +/// Binds C++ enumerations and enumeration classes to Python +template class enum_ : public class_ { +public: + using Base = class_; + using Base::def; + using Base::attr; + using Base::def_property_readonly; + using Base::def_property_readonly_static; + using Scalar = typename std::underlying_type::type; + + template + enum_(const handle &scope, const char *name, const Extra&... extra) + : class_(scope, name, extra...), m_base(*this, scope) { + constexpr bool is_arithmetic = detail::any_of...>::value; + constexpr bool is_convertible = std::is_convertible::value; + m_base.init(is_arithmetic, is_convertible); + + def(init([](Scalar i) { return static_cast(i); })); + def("__int__", [](Type value) { return (Scalar) value; }); + #if PY_MAJOR_VERSION < 3 + def("__long__", [](Type value) { return (Scalar) value; }); + #endif + #if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 8) + def("__index__", [](Type value) { return (Scalar) value; }); + #endif + + attr("__setstate__") = cpp_function( + [](detail::value_and_holder &v_h, Scalar arg) { + detail::initimpl::setstate(v_h, static_cast(arg), + Py_TYPE(v_h.inst) != v_h.type->type); }, + detail::is_new_style_constructor(), + pybind11::name("__setstate__"), is_method(*this)); + } + + /// Export enumeration entries into the parent scope + enum_& export_values() { + m_base.export_values(); + return *this; + } + + /// Add an enumeration entry + enum_& value(char const* name, Type value, const char *doc = nullptr) { + m_base.value(name, pybind11::cast(value, return_value_policy::copy), doc); + return *this; + } + +private: + detail::enum_base m_base; +}; + +PYBIND11_NAMESPACE_BEGIN(detail) + + +inline void keep_alive_impl(handle nurse, handle patient) { + if (!nurse || !patient) + pybind11_fail("Could not activate keep_alive!"); + + if (patient.is_none() || nurse.is_none()) + return; /* Nothing to keep alive or nothing to be kept alive by */ + + auto tinfo = all_type_info(Py_TYPE(nurse.ptr())); + if (!tinfo.empty()) { + /* It's a pybind-registered type, so we can store the patient in the + * internal list. */ + add_patient(nurse.ptr(), patient.ptr()); + } + else { + /* Fall back to clever approach based on weak references taken from + * Boost.Python. This is not used for pybind-registered types because + * the objects can be destroyed out-of-order in a GC pass. */ + cpp_function disable_lifesupport( + [patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); }); + + weakref wr(nurse, disable_lifesupport); + + patient.inc_ref(); /* reference patient and leak the weak reference */ + (void) wr.release(); + } +} + +PYBIND11_NOINLINE inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) { + auto get_arg = [&](size_t n) { + if (n == 0) + return ret; + else if (n == 1 && call.init_self) + return call.init_self; + else if (n <= call.args.size()) + return call.args[n - 1]; + return handle(); + }; + + keep_alive_impl(get_arg(Nurse), get_arg(Patient)); +} + +inline std::pair all_type_info_get_cache(PyTypeObject *type) { + auto res = get_internals().registered_types_py +#ifdef __cpp_lib_unordered_map_try_emplace + .try_emplace(type); +#else + .emplace(type, std::vector()); +#endif + if (res.second) { + // New cache entry created; set up a weak reference to automatically remove it if the type + // gets destroyed: + weakref((PyObject *) type, cpp_function([type](handle wr) { + get_internals().registered_types_py.erase(type); + wr.dec_ref(); + })).release(); + } + + return res; +} + +template +struct iterator_state { + Iterator it; + Sentinel end; + bool first_or_done; +}; + +PYBIND11_NAMESPACE_END(detail) + +/// Makes a python iterator from a first and past-the-end C++ InputIterator. +template ()), + typename... Extra> +iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { + typedef detail::iterator_state state; + + if (!detail::get_type_info(typeid(state), false)) { + class_(handle(), "iterator", pybind11::module_local()) + .def("__iter__", [](state &s) -> state& { return s; }) + .def("__next__", [](state &s) -> ValueType { + if (!s.first_or_done) + ++s.it; + else + s.first_or_done = false; + if (s.it == s.end) { + s.first_or_done = true; + throw stop_iteration(); + } + return *s.it; + }, std::forward(extra)..., Policy); + } + + return cast(state{first, last, true}); +} + +/// Makes an python iterator over the keys (`.first`) of a iterator over pairs from a +/// first and past-the-end InputIterator. +template ()).first), + typename... Extra> +iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { + using state = detail::iterator_state; + + if (!detail::get_type_info(typeid(state), false)) { + class_(handle(), "iterator", pybind11::module_local()) + .def("__iter__", [](state &s) -> state& { return s; }) + .def("__next__", [](state &s) -> KeyType { + if (!s.first_or_done) + ++s.it; + else + s.first_or_done = false; + if (s.it == s.end) { + s.first_or_done = true; + throw stop_iteration(); + } + return (*s.it).first; + }, std::forward(extra)..., Policy); + } + + return cast(state{first, last, true}); +} + +/// Makes an iterator over values of an stl container or other container supporting +/// `std::begin()`/`std::end()` +template iterator make_iterator(Type &value, Extra&&... extra) { + return make_iterator(std::begin(value), std::end(value), extra...); +} + +/// Makes an iterator over the keys (`.first`) of a stl map-like container supporting +/// `std::begin()`/`std::end()` +template iterator make_key_iterator(Type &value, Extra&&... extra) { + return make_key_iterator(std::begin(value), std::end(value), extra...); +} + +template void implicitly_convertible() { + struct set_flag { + bool &flag; + set_flag(bool &flag) : flag(flag) { flag = true; } + ~set_flag() { flag = false; } + }; + auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * { + static bool currently_used = false; + if (currently_used) // implicit conversions are non-reentrant + return nullptr; + set_flag flag_helper(currently_used); + if (!detail::make_caster().load(obj, false)) + return nullptr; + tuple args(1); + args[0] = obj; + PyObject *result = PyObject_Call((PyObject *) type, args.ptr(), nullptr); + if (result == nullptr) + PyErr_Clear(); + return result; + }; + + if (auto tinfo = detail::get_type_info(typeid(OutputType))) + tinfo->implicit_conversions.push_back(implicit_caster); + else + pybind11_fail("implicitly_convertible: Unable to find type " + type_id()); +} + +template +void register_exception_translator(ExceptionTranslator&& translator) { + detail::get_internals().registered_exception_translators.push_front( + std::forward(translator)); +} + +/** + * Wrapper to generate a new Python exception type. + * + * This should only be used with PyErr_SetString for now. + * It is not (yet) possible to use as a py::base. + * Template type argument is reserved for future use. + */ +template +class exception : public object { +public: + exception() = default; + exception(handle scope, const char *name, handle base = PyExc_Exception) { + std::string full_name = scope.attr("__name__").cast() + + std::string(".") + name; + m_ptr = PyErr_NewException(const_cast(full_name.c_str()), base.ptr(), NULL); + if (hasattr(scope, "__dict__") && scope.attr("__dict__").contains(name)) + pybind11_fail("Error during initialization: multiple incompatible " + "definitions with name \"" + std::string(name) + "\""); + scope.attr(name) = *this; + } + + // Sets the current python exception to this exception object with the given message + void operator()(const char *message) { + PyErr_SetString(m_ptr, message); + } +}; + +PYBIND11_NAMESPACE_BEGIN(detail) +// Returns a reference to a function-local static exception object used in the simple +// register_exception approach below. (It would be simpler to have the static local variable +// directly in register_exception, but that makes clang <3.5 segfault - issue #1349). +template +exception &get_exception_object() { static exception ex; return ex; } +PYBIND11_NAMESPACE_END(detail) + +/** + * Registers a Python exception in `m` of the given `name` and installs an exception translator to + * translate the C++ exception to the created Python exception using the exceptions what() method. + * This is intended for simple exception translations; for more complex translation, register the + * exception object and translator directly. + */ +template +exception ®ister_exception(handle scope, + const char *name, + handle base = PyExc_Exception) { + auto &ex = detail::get_exception_object(); + if (!ex) ex = exception(scope, name, base); + + register_exception_translator([](std::exception_ptr p) { + if (!p) return; + try { + std::rethrow_exception(p); + } catch (const CppException &e) { + detail::get_exception_object()(e.what()); + } + }); + return ex; +} + +PYBIND11_NAMESPACE_BEGIN(detail) +PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { + auto strings = tuple(args.size()); + for (size_t i = 0; i < args.size(); ++i) { + strings[i] = str(args[i]); + } + auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" "); + auto line = sep.attr("join")(strings); + + object file; + if (kwargs.contains("file")) { + file = kwargs["file"].cast(); + } else { + try { + file = module_::import("sys").attr("stdout"); + } catch (const error_already_set &) { + /* If print() is called from code that is executed as + part of garbage collection during interpreter shutdown, + importing 'sys' can fail. Give up rather than crashing the + interpreter in this case. */ + return; + } + } + + auto write = file.attr("write"); + write(line); + write(kwargs.contains("end") ? kwargs["end"] : cast("\n")); + + if (kwargs.contains("flush") && kwargs["flush"].cast()) + file.attr("flush")(); +} +PYBIND11_NAMESPACE_END(detail) + +template +void print(Args &&...args) { + auto c = detail::collect_arguments(std::forward(args)...); + detail::print(c.args(), c.kwargs()); +} + +#if defined(WITH_THREAD) && !defined(PYPY_VERSION) + +/* The functions below essentially reproduce the PyGILState_* API using a RAII + * pattern, but there are a few important differences: + * + * 1. When acquiring the GIL from an non-main thread during the finalization + * phase, the GILState API blindly terminates the calling thread, which + * is often not what is wanted. This API does not do this. + * + * 2. The gil_scoped_release function can optionally cut the relationship + * of a PyThreadState and its associated thread, which allows moving it to + * another thread (this is a fairly rare/advanced use case). + * + * 3. The reference count of an acquired thread state can be controlled. This + * can be handy to prevent cases where callbacks issued from an external + * thread would otherwise constantly construct and destroy thread state data + * structures. + * + * See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an + * example which uses features 2 and 3 to migrate the Python thread of + * execution to another thread (to run the event loop on the original thread, + * in this case). + */ + +class gil_scoped_acquire { +public: + PYBIND11_NOINLINE gil_scoped_acquire() { + auto const &internals = detail::get_internals(); + tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate); + + if (!tstate) { + /* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if + calling from a Python thread). Since we use a different key, this ensures + we don't create a new thread state and deadlock in PyEval_AcquireThread + below. Note we don't save this state with internals.tstate, since we don't + create it we would fail to clear it (its reference count should be > 0). */ + tstate = PyGILState_GetThisThreadState(); + } + + if (!tstate) { + tstate = PyThreadState_New(internals.istate); + #if !defined(NDEBUG) + if (!tstate) + pybind11_fail("scoped_acquire: could not create thread state!"); + #endif + tstate->gilstate_counter = 0; + PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate); + } else { + release = detail::get_thread_state_unchecked() != tstate; + } + + if (release) { + /* Work around an annoying assertion in PyThreadState_Swap */ + #if defined(Py_DEBUG) + PyInterpreterState *interp = tstate->interp; + tstate->interp = nullptr; + #endif + PyEval_AcquireThread(tstate); + #if defined(Py_DEBUG) + tstate->interp = interp; + #endif + } + + inc_ref(); + } + + void inc_ref() { + ++tstate->gilstate_counter; + } + + PYBIND11_NOINLINE void dec_ref() { + --tstate->gilstate_counter; + #if !defined(NDEBUG) + if (detail::get_thread_state_unchecked() != tstate) + pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); + if (tstate->gilstate_counter < 0) + pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); + #endif + if (tstate->gilstate_counter == 0) { + #if !defined(NDEBUG) + if (!release) + pybind11_fail("scoped_acquire::dec_ref(): internal error!"); + #endif + PyThreadState_Clear(tstate); + PyThreadState_DeleteCurrent(); + PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate); + release = false; + } + } + + PYBIND11_NOINLINE ~gil_scoped_acquire() { + dec_ref(); + if (release) + PyEval_SaveThread(); + } +private: + PyThreadState *tstate = nullptr; + bool release = true; +}; + +class gil_scoped_release { +public: + explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) { + // `get_internals()` must be called here unconditionally in order to initialize + // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an + // initialization race could occur as multiple threads try `gil_scoped_acquire`. + const auto &internals = detail::get_internals(); + tstate = PyEval_SaveThread(); + if (disassoc) { + auto key = internals.tstate; + PYBIND11_TLS_DELETE_VALUE(key); + } + } + ~gil_scoped_release() { + if (!tstate) + return; + PyEval_RestoreThread(tstate); + if (disassoc) { + auto key = detail::get_internals().tstate; + PYBIND11_TLS_REPLACE_VALUE(key, tstate); + } + } +private: + PyThreadState *tstate; + bool disassoc; +}; +#elif defined(PYPY_VERSION) +class gil_scoped_acquire { + PyGILState_STATE state; +public: + gil_scoped_acquire() { state = PyGILState_Ensure(); } + ~gil_scoped_acquire() { PyGILState_Release(state); } +}; + +class gil_scoped_release { + PyThreadState *state; +public: + gil_scoped_release() { state = PyEval_SaveThread(); } + ~gil_scoped_release() { PyEval_RestoreThread(state); } +}; +#else +class gil_scoped_acquire { }; +class gil_scoped_release { }; +#endif + +error_already_set::~error_already_set() { + if (m_type) { + gil_scoped_acquire gil; + error_scope scope; + m_type.release().dec_ref(); + m_value.release().dec_ref(); + m_trace.release().dec_ref(); + } +} + +PYBIND11_NAMESPACE_BEGIN(detail) +inline function get_type_override(const void *this_ptr, const type_info *this_type, const char *name) { + handle self = get_object_handle(this_ptr, this_type); + if (!self) + return function(); + handle type = type::handle_of(self); + auto key = std::make_pair(type.ptr(), name); + + /* Cache functions that aren't overridden in Python to avoid + many costly Python dictionary lookups below */ + auto &cache = get_internals().inactive_override_cache; + if (cache.find(key) != cache.end()) + return function(); + + function override = getattr(self, name, function()); + if (override.is_cpp_function()) { + cache.insert(key); + return function(); + } + + /* Don't call dispatch code if invoked from overridden function. + Unfortunately this doesn't work on PyPy. */ +#if !defined(PYPY_VERSION) + PyFrameObject *frame = PyThreadState_Get()->frame; + if (frame && (std::string) str(frame->f_code->co_name) == name && + frame->f_code->co_argcount > 0) { + PyFrame_FastToLocals(frame); + PyObject *self_caller = PyDict_GetItem( + frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0)); + if (self_caller == self.ptr()) + return function(); + } +#else + /* PyPy currently doesn't provide a detailed cpyext emulation of + frame objects, so we have to emulate this using Python. This + is going to be slow..*/ + dict d; d["self"] = self; d["name"] = pybind11::str(name); + PyObject *result = PyRun_String( + "import inspect\n" + "frame = inspect.currentframe()\n" + "if frame is not None:\n" + " frame = frame.f_back\n" + " if frame is not None and str(frame.f_code.co_name) == name and " + "frame.f_code.co_argcount > 0:\n" + " self_caller = frame.f_locals[frame.f_code.co_varnames[0]]\n" + " if self_caller == self:\n" + " self = None\n", + Py_file_input, d.ptr(), d.ptr()); + if (result == nullptr) + throw error_already_set(); + if (d["self"].is_none()) + return function(); + Py_DECREF(result); +#endif + + return override; +} +PYBIND11_NAMESPACE_END(detail) + +/** \rst + Try to retrieve a python method by the provided name from the instance pointed to by the this_ptr. + + :this_ptr: The pointer to the object the overriden method should be retrieved for. This should be + the first non-trampoline class encountered in the inheritance chain. + :name: The name of the overridden Python method to retrieve. + :return: The Python method by this name from the object or an empty function wrapper. + \endrst */ +template function get_override(const T *this_ptr, const char *name) { + auto tinfo = detail::get_type_info(typeid(T)); + return tinfo ? detail::get_type_override(this_ptr, tinfo, name) : function(); +} + +#define PYBIND11_OVERRIDE_IMPL(ret_type, cname, name, ...) \ + do { \ + pybind11::gil_scoped_acquire gil; \ + pybind11::function override = pybind11::get_override(static_cast(this), name); \ + if (override) { \ + auto o = override(__VA_ARGS__); \ + if (pybind11::detail::cast_is_temporary_value_reference::value) { \ + static pybind11::detail::override_caster_t caster; \ + return pybind11::detail::cast_ref(std::move(o), caster); \ + } \ + else return pybind11::detail::cast_safe(std::move(o)); \ + } \ + } while (false) + +/** \rst + Macro to populate the virtual method in the trampoline class. This macro tries to look up a method named 'fn' + from the Python side, deals with the :ref:`gil` and necessary argument conversions to call this method and return + the appropriate type. See :ref:`overriding_virtuals` for more information. This macro should be used when the method + name in C is not the same as the method name in Python. For example with `__str__`. + + .. code-block:: cpp + + std::string toString() override { + PYBIND11_OVERRIDE_NAME( + std::string, // Return type (ret_type) + Animal, // Parent class (cname) + "__str__", // Name of method in Python (name) + toString, // Name of function in C++ (fn) + ); + } +\endrst */ +#define PYBIND11_OVERRIDE_NAME(ret_type, cname, name, fn, ...) \ + do { \ + PYBIND11_OVERRIDE_IMPL(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__); \ + return cname::fn(__VA_ARGS__); \ + } while (false) + +/** \rst + Macro for pure virtual functions, this function is identical to :c:macro:`PYBIND11_OVERRIDE_NAME`, except that it + throws if no override can be found. +\endrst */ +#define PYBIND11_OVERRIDE_PURE_NAME(ret_type, cname, name, fn, ...) \ + do { \ + PYBIND11_OVERRIDE_IMPL(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__); \ + pybind11::pybind11_fail("Tried to call pure virtual function \"" PYBIND11_STRINGIFY(cname) "::" name "\""); \ + } while (false) + +/** \rst + Macro to populate the virtual method in the trampoline class. This macro tries to look up the method + from the Python side, deals with the :ref:`gil` and necessary argument conversions to call this method and return + the appropriate type. This macro should be used if the method name in C and in Python are identical. + See :ref:`overriding_virtuals` for more information. + + .. code-block:: cpp + + class PyAnimal : public Animal { + public: + // Inherit the constructors + using Animal::Animal; + + // Trampoline (need one for each virtual function) + std::string go(int n_times) override { + PYBIND11_OVERRIDE_PURE( + std::string, // Return type (ret_type) + Animal, // Parent class (cname) + go, // Name of function in C++ (must match Python name) (fn) + n_times // Argument(s) (...) + ); + } + }; +\endrst */ +#define PYBIND11_OVERRIDE(ret_type, cname, fn, ...) \ + PYBIND11_OVERRIDE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) + +/** \rst + Macro for pure virtual functions, this function is identical to :c:macro:`PYBIND11_OVERRIDE`, except that it throws + if no override can be found. +\endrst */ +#define PYBIND11_OVERRIDE_PURE(ret_type, cname, fn, ...) \ + PYBIND11_OVERRIDE_PURE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) + + +// Deprecated versions + +PYBIND11_DEPRECATED("get_type_overload has been deprecated") +inline function get_type_overload(const void *this_ptr, const detail::type_info *this_type, const char *name) { + return detail::get_type_override(this_ptr, this_type, name); +} + +template +inline function get_overload(const T *this_ptr, const char *name) { + return get_override(this_ptr, name); +} + +#define PYBIND11_OVERLOAD_INT(ret_type, cname, name, ...) \ + PYBIND11_OVERRIDE_IMPL(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) +#define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \ + PYBIND11_OVERRIDE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, fn, __VA_ARGS__) +#define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \ + PYBIND11_OVERRIDE_PURE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, fn, __VA_ARGS__); +#define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \ + PYBIND11_OVERRIDE(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), fn, __VA_ARGS__) +#define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \ + PYBIND11_OVERRIDE_PURE(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), fn, __VA_ARGS__); + +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) +# pragma warning(pop) +#elif defined(__GNUG__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif diff --git a/extern/pybind/include/pybind11/pytypes.h b/extern/pybind/include/pybind11/pytypes.h new file mode 100644 index 00000000..1010ad71 --- /dev/null +++ b/extern/pybind/include/pybind11/pytypes.h @@ -0,0 +1,1659 @@ +/* + pybind11/pytypes.h: Convenience wrapper classes for basic Python types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" +#include "buffer_info.h" +#include +#include + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/* A few forward declarations */ +class handle; class object; +class str; class iterator; +class type; +struct arg; struct arg_v; + +PYBIND11_NAMESPACE_BEGIN(detail) +class args_proxy; +inline bool isinstance_generic(handle obj, const std::type_info &tp); + +// Accessor forward declarations +template class accessor; +namespace accessor_policies { + struct obj_attr; + struct str_attr; + struct generic_item; + struct sequence_item; + struct list_item; + struct tuple_item; +} // namespace accessor_policies +using obj_attr_accessor = accessor; +using str_attr_accessor = accessor; +using item_accessor = accessor; +using sequence_accessor = accessor; +using list_accessor = accessor; +using tuple_accessor = accessor; + +/// Tag and check to identify a class which implements the Python object API +class pyobject_tag { }; +template using is_pyobject = std::is_base_of>; + +/** \rst + A mixin class which adds common functions to `handle`, `object` and various accessors. + The only requirement for `Derived` is to implement ``PyObject *Derived::ptr() const``. +\endrst */ +template +class object_api : public pyobject_tag { + const Derived &derived() const { return static_cast(*this); } + +public: + /** \rst + Return an iterator equivalent to calling ``iter()`` in Python. The object + must be a collection which supports the iteration protocol. + \endrst */ + iterator begin() const; + /// Return a sentinel which ends iteration. + iterator end() const; + + /** \rst + Return an internal functor to invoke the object's sequence protocol. Casting + the returned ``detail::item_accessor`` instance to a `handle` or `object` + subclass causes a corresponding call to ``__getitem__``. Assigning a `handle` + or `object` subclass causes a call to ``__setitem__``. + \endrst */ + item_accessor operator[](handle key) const; + /// See above (the only difference is that they key is provided as a string literal) + item_accessor operator[](const char *key) const; + + /** \rst + Return an internal functor to access the object's attributes. Casting the + returned ``detail::obj_attr_accessor`` instance to a `handle` or `object` + subclass causes a corresponding call to ``getattr``. Assigning a `handle` + or `object` subclass causes a call to ``setattr``. + \endrst */ + obj_attr_accessor attr(handle key) const; + /// See above (the only difference is that they key is provided as a string literal) + str_attr_accessor attr(const char *key) const; + + /** \rst + Matches * unpacking in Python, e.g. to unpack arguments out of a ``tuple`` + or ``list`` for a function call. Applying another * to the result yields + ** unpacking, e.g. to unpack a dict as function keyword arguments. + See :ref:`calling_python_functions`. + \endrst */ + args_proxy operator*() const; + + /// Check if the given item is contained within this object, i.e. ``item in obj``. + template bool contains(T &&item) const; + + /** \rst + Assuming the Python object is a function or implements the ``__call__`` + protocol, ``operator()`` invokes the underlying function, passing an + arbitrary set of parameters. The result is returned as a `object` and + may need to be converted back into a Python object using `handle::cast()`. + + When some of the arguments cannot be converted to Python objects, the + function will throw a `cast_error` exception. When the Python function + call fails, a `error_already_set` exception is thrown. + \endrst */ + template + object operator()(Args &&...args) const; + template + PYBIND11_DEPRECATED("call(...) was deprecated in favor of operator()(...)") + object call(Args&&... args) const; + + /// Equivalent to ``obj is other`` in Python. + bool is(object_api const& other) const { return derived().ptr() == other.derived().ptr(); } + /// Equivalent to ``obj is None`` in Python. + bool is_none() const { return derived().ptr() == Py_None; } + /// Equivalent to obj == other in Python + bool equal(object_api const &other) const { return rich_compare(other, Py_EQ); } + bool not_equal(object_api const &other) const { return rich_compare(other, Py_NE); } + bool operator<(object_api const &other) const { return rich_compare(other, Py_LT); } + bool operator<=(object_api const &other) const { return rich_compare(other, Py_LE); } + bool operator>(object_api const &other) const { return rich_compare(other, Py_GT); } + bool operator>=(object_api const &other) const { return rich_compare(other, Py_GE); } + + object operator-() const; + object operator~() const; + object operator+(object_api const &other) const; + object operator+=(object_api const &other) const; + object operator-(object_api const &other) const; + object operator-=(object_api const &other) const; + object operator*(object_api const &other) const; + object operator*=(object_api const &other) const; + object operator/(object_api const &other) const; + object operator/=(object_api const &other) const; + object operator|(object_api const &other) const; + object operator|=(object_api const &other) const; + object operator&(object_api const &other) const; + object operator&=(object_api const &other) const; + object operator^(object_api const &other) const; + object operator^=(object_api const &other) const; + object operator<<(object_api const &other) const; + object operator<<=(object_api const &other) const; + object operator>>(object_api const &other) const; + object operator>>=(object_api const &other) const; + + PYBIND11_DEPRECATED("Use py::str(obj) instead") + pybind11::str str() const; + + /// Get or set the object's docstring, i.e. ``obj.__doc__``. + str_attr_accessor doc() const; + + /// Return the object's current reference count + int ref_count() const { return static_cast(Py_REFCNT(derived().ptr())); } + + // TODO PYBIND11_DEPRECATED("Call py::type::handle_of(h) or py::type::of(h) instead of h.get_type()") + handle get_type() const; + +private: + bool rich_compare(object_api const &other, int value) const; +}; + +PYBIND11_NAMESPACE_END(detail) + +/** \rst + Holds a reference to a Python object (no reference counting) + + The `handle` class is a thin wrapper around an arbitrary Python object (i.e. a + ``PyObject *`` in Python's C API). It does not perform any automatic reference + counting and merely provides a basic C++ interface to various Python API functions. + + .. seealso:: + The `object` class inherits from `handle` and adds automatic reference + counting features. +\endrst */ +class handle : public detail::object_api { +public: + /// The default constructor creates a handle with a ``nullptr``-valued pointer + handle() = default; + /// Creates a ``handle`` from the given raw Python object pointer + handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject* + + /// Return the underlying ``PyObject *`` pointer + PyObject *ptr() const { return m_ptr; } + PyObject *&ptr() { return m_ptr; } + + /** \rst + Manually increase the reference count of the Python object. Usually, it is + preferable to use the `object` class which derives from `handle` and calls + this function automatically. Returns a reference to itself. + \endrst */ + const handle& inc_ref() const & { Py_XINCREF(m_ptr); return *this; } + + /** \rst + Manually decrease the reference count of the Python object. Usually, it is + preferable to use the `object` class which derives from `handle` and calls + this function automatically. Returns a reference to itself. + \endrst */ + const handle& dec_ref() const & { Py_XDECREF(m_ptr); return *this; } + + /** \rst + Attempt to cast the Python object into the given C++ type. A `cast_error` + will be throw upon failure. + \endrst */ + template T cast() const; + /// Return ``true`` when the `handle` wraps a valid Python object + explicit operator bool() const { return m_ptr != nullptr; } + /** \rst + Deprecated: Check that the underlying pointers are the same. + Equivalent to ``obj1 is obj2`` in Python. + \endrst */ + PYBIND11_DEPRECATED("Use obj1.is(obj2) instead") + bool operator==(const handle &h) const { return m_ptr == h.m_ptr; } + PYBIND11_DEPRECATED("Use !obj1.is(obj2) instead") + bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; } + PYBIND11_DEPRECATED("Use handle::operator bool() instead") + bool check() const { return m_ptr != nullptr; } +protected: + PyObject *m_ptr = nullptr; +}; + +/** \rst + Holds a reference to a Python object (with reference counting) + + Like `handle`, the `object` class is a thin wrapper around an arbitrary Python + object (i.e. a ``PyObject *`` in Python's C API). In contrast to `handle`, it + optionally increases the object's reference count upon construction, and it + *always* decreases the reference count when the `object` instance goes out of + scope and is destructed. When using `object` instances consistently, it is much + easier to get reference counting right at the first attempt. +\endrst */ +class object : public handle { +public: + object() = default; + PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") + object(handle h, bool is_borrowed) : handle(h) { if (is_borrowed) inc_ref(); } + /// Copy constructor; always increases the reference count + object(const object &o) : handle(o) { inc_ref(); } + /// Move constructor; steals the object from ``other`` and preserves its reference count + object(object &&other) noexcept { m_ptr = other.m_ptr; other.m_ptr = nullptr; } + /// Destructor; automatically calls `handle::dec_ref()` + ~object() { dec_ref(); } + + /** \rst + Resets the internal pointer to ``nullptr`` without decreasing the + object's reference count. The function returns a raw handle to the original + Python object. + \endrst */ + handle release() { + PyObject *tmp = m_ptr; + m_ptr = nullptr; + return handle(tmp); + } + + object& operator=(const object &other) { + other.inc_ref(); + dec_ref(); + m_ptr = other.m_ptr; + return *this; + } + + object& operator=(object &&other) noexcept { + if (this != &other) { + handle temp(m_ptr); + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + temp.dec_ref(); + } + return *this; + } + + // Calling cast() on an object lvalue just copies (via handle::cast) + template T cast() const &; + // Calling on an object rvalue does a move, if needed and/or possible + template T cast() &&; + +protected: + // Tags for choosing constructors from raw PyObject * + struct borrowed_t { }; + struct stolen_t { }; + + template friend T reinterpret_borrow(handle); + template friend T reinterpret_steal(handle); + +public: + // Only accessible from derived classes and the reinterpret_* functions + object(handle h, borrowed_t) : handle(h) { inc_ref(); } + object(handle h, stolen_t) : handle(h) { } +}; + +/** \rst + Declare that a `handle` or ``PyObject *`` is a certain type and borrow the reference. + The target type ``T`` must be `object` or one of its derived classes. The function + doesn't do any conversions or checks. It's up to the user to make sure that the + target type is correct. + + .. code-block:: cpp + + PyObject *p = PyList_GetItem(obj, index); + py::object o = reinterpret_borrow(p); + // or + py::tuple t = reinterpret_borrow(p); // <-- `p` must be already be a `tuple` +\endrst */ +template T reinterpret_borrow(handle h) { return {h, object::borrowed_t{}}; } + +/** \rst + Like `reinterpret_borrow`, but steals the reference. + + .. code-block:: cpp + + PyObject *p = PyObject_Str(obj); + py::str s = reinterpret_steal(p); // <-- `p` must be already be a `str` +\endrst */ +template T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; } + +PYBIND11_NAMESPACE_BEGIN(detail) +inline std::string error_string(); +PYBIND11_NAMESPACE_END(detail) + +/// Fetch and hold an error which was already set in Python. An instance of this is typically +/// thrown to propagate python-side errors back through C++ which can either be caught manually or +/// else falls back to the function dispatcher (which then raises the captured error back to +/// python). +class error_already_set : public std::runtime_error { +public: + /// Constructs a new exception from the current Python error indicator, if any. The current + /// Python error indicator will be cleared. + error_already_set() : std::runtime_error(detail::error_string()) { + PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr()); + } + + error_already_set(const error_already_set &) = default; + error_already_set(error_already_set &&) = default; + + inline ~error_already_set() override; + + /// Give the currently-held error back to Python, if any. If there is currently a Python error + /// already set it is cleared first. After this call, the current object no longer stores the + /// error variables (but the `.what()` string is still available). + void restore() { PyErr_Restore(m_type.release().ptr(), m_value.release().ptr(), m_trace.release().ptr()); } + + /// If it is impossible to raise the currently-held error, such as in destructor, we can write + /// it out using Python's unraisable hook (sys.unraisablehook). The error context should be + /// some object whose repr() helps identify the location of the error. Python already knows the + /// type and value of the error, so there is no need to repeat that. For example, __func__ could + /// be helpful. After this call, the current object no longer stores the error variables, + /// and neither does Python. + void discard_as_unraisable(object err_context) { + restore(); + PyErr_WriteUnraisable(err_context.ptr()); + } + void discard_as_unraisable(const char *err_context) { + discard_as_unraisable(reinterpret_steal(PYBIND11_FROM_STRING(err_context))); + } + + // Does nothing; provided for backwards compatibility. + PYBIND11_DEPRECATED("Use of error_already_set.clear() is deprecated") + void clear() {} + + /// Check if the currently trapped error type matches the given Python exception class (or a + /// subclass thereof). May also be passed a tuple to search for any exception class matches in + /// the given tuple. + bool matches(handle exc) const { return PyErr_GivenExceptionMatches(m_type.ptr(), exc.ptr()); } + + const object& type() const { return m_type; } + const object& value() const { return m_value; } + const object& trace() const { return m_trace; } + +private: + object m_type, m_value, m_trace; +}; + +/** \defgroup python_builtins _ + Unless stated otherwise, the following C++ functions behave the same + as their Python counterparts. + */ + +/** \ingroup python_builtins + \rst + Return true if ``obj`` is an instance of ``T``. Type ``T`` must be a subclass of + `object` or a class which was exposed to Python as ``py::class_``. +\endrst */ +template ::value, int> = 0> +bool isinstance(handle obj) { return T::check_(obj); } + +template ::value, int> = 0> +bool isinstance(handle obj) { return detail::isinstance_generic(obj, typeid(T)); } + +template <> inline bool isinstance(handle) = delete; +template <> inline bool isinstance(handle obj) { return obj.ptr() != nullptr; } + +/// \ingroup python_builtins +/// Return true if ``obj`` is an instance of the ``type``. +inline bool isinstance(handle obj, handle type) { + const auto result = PyObject_IsInstance(obj.ptr(), type.ptr()); + if (result == -1) + throw error_already_set(); + return result != 0; +} + +/// \addtogroup python_builtins +/// @{ +inline bool hasattr(handle obj, handle name) { + return PyObject_HasAttr(obj.ptr(), name.ptr()) == 1; +} + +inline bool hasattr(handle obj, const char *name) { + return PyObject_HasAttrString(obj.ptr(), name) == 1; +} + +inline void delattr(handle obj, handle name) { + if (PyObject_DelAttr(obj.ptr(), name.ptr()) != 0) { throw error_already_set(); } +} + +inline void delattr(handle obj, const char *name) { + if (PyObject_DelAttrString(obj.ptr(), name) != 0) { throw error_already_set(); } +} + +inline object getattr(handle obj, handle name) { + PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} + +inline object getattr(handle obj, const char *name) { + PyObject *result = PyObject_GetAttrString(obj.ptr(), name); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} + +inline object getattr(handle obj, handle name, handle default_) { + if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) { + return reinterpret_steal(result); + } else { + PyErr_Clear(); + return reinterpret_borrow(default_); + } +} + +inline object getattr(handle obj, const char *name, handle default_) { + if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) { + return reinterpret_steal(result); + } else { + PyErr_Clear(); + return reinterpret_borrow(default_); + } +} + +inline void setattr(handle obj, handle name, handle value) { + if (PyObject_SetAttr(obj.ptr(), name.ptr(), value.ptr()) != 0) { throw error_already_set(); } +} + +inline void setattr(handle obj, const char *name, handle value) { + if (PyObject_SetAttrString(obj.ptr(), name, value.ptr()) != 0) { throw error_already_set(); } +} + +inline ssize_t hash(handle obj) { + auto h = PyObject_Hash(obj.ptr()); + if (h == -1) { throw error_already_set(); } + return h; +} + +/// @} python_builtins + +PYBIND11_NAMESPACE_BEGIN(detail) +inline handle get_function(handle value) { + if (value) { +#if PY_MAJOR_VERSION >= 3 + if (PyInstanceMethod_Check(value.ptr())) + value = PyInstanceMethod_GET_FUNCTION(value.ptr()); + else +#endif + if (PyMethod_Check(value.ptr())) + value = PyMethod_GET_FUNCTION(value.ptr()); + } + return value; +} + +// Helper aliases/functions to support implicit casting of values given to python accessors/methods. +// When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes +// through pybind11::cast(obj) to convert it to an `object`. +template ::value, int> = 0> +auto object_or_cast(T &&o) -> decltype(std::forward(o)) { return std::forward(o); } +// The following casting version is implemented in cast.h: +template ::value, int> = 0> +object object_or_cast(T &&o); +// Match a PyObject*, which we want to convert directly to handle via its converting constructor +inline handle object_or_cast(PyObject *ptr) { return ptr; } + +template +class accessor : public object_api> { + using key_type = typename Policy::key_type; + +public: + accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { } + accessor(const accessor &) = default; + accessor(accessor &&) = default; + + // accessor overload required to override default assignment operator (templates are not allowed + // to replace default compiler-generated assignments). + void operator=(const accessor &a) && { std::move(*this).operator=(handle(a)); } + void operator=(const accessor &a) & { operator=(handle(a)); } + + template void operator=(T &&value) && { + Policy::set(obj, key, object_or_cast(std::forward(value))); + } + template void operator=(T &&value) & { + get_cache() = reinterpret_borrow(object_or_cast(std::forward(value))); + } + + template + PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)") + explicit operator enable_if_t::value || + std::is_same::value, bool>() const { + return hasattr(obj, key); + } + template + PYBIND11_DEPRECATED("Use of obj[key] as bool is deprecated in favor of obj.contains(key)") + explicit operator enable_if_t::value, bool>() const { + return obj.contains(key); + } + + operator object() const { return get_cache(); } + PyObject *ptr() const { return get_cache().ptr(); } + template T cast() const { return get_cache().template cast(); } + +private: + object &get_cache() const { + if (!cache) { cache = Policy::get(obj, key); } + return cache; + } + +private: + handle obj; + key_type key; + mutable object cache; +}; + +PYBIND11_NAMESPACE_BEGIN(accessor_policies) +struct obj_attr { + using key_type = object; + static object get(handle obj, handle key) { return getattr(obj, key); } + static void set(handle obj, handle key, handle val) { setattr(obj, key, val); } +}; + +struct str_attr { + using key_type = const char *; + static object get(handle obj, const char *key) { return getattr(obj, key); } + static void set(handle obj, const char *key, handle val) { setattr(obj, key, val); } +}; + +struct generic_item { + using key_type = object; + + static object get(handle obj, handle key) { + PyObject *result = PyObject_GetItem(obj.ptr(), key.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); + } + + static void set(handle obj, handle key, handle val) { + if (PyObject_SetItem(obj.ptr(), key.ptr(), val.ptr()) != 0) { throw error_already_set(); } + } +}; + +struct sequence_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PySequence_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); + } + + static void set(handle obj, size_t index, handle val) { + // PySequence_SetItem does not steal a reference to 'val' + if (PySequence_SetItem(obj.ptr(), static_cast(index), val.ptr()) != 0) { + throw error_already_set(); + } + } +}; + +struct list_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PyList_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_borrow(result); + } + + static void set(handle obj, size_t index, handle val) { + // PyList_SetItem steals a reference to 'val' + if (PyList_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { + throw error_already_set(); + } + } +}; + +struct tuple_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_borrow(result); + } + + static void set(handle obj, size_t index, handle val) { + // PyTuple_SetItem steals a reference to 'val' + if (PyTuple_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { + throw error_already_set(); + } + } +}; +PYBIND11_NAMESPACE_END(accessor_policies) + +/// STL iterator template used for tuple, list, sequence and dict +template +class generic_iterator : public Policy { + using It = generic_iterator; + +public: + using difference_type = ssize_t; + using iterator_category = typename Policy::iterator_category; + using value_type = typename Policy::value_type; + using reference = typename Policy::reference; + using pointer = typename Policy::pointer; + + generic_iterator() = default; + generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { } + + reference operator*() const { return Policy::dereference(); } + reference operator[](difference_type n) const { return *(*this + n); } + pointer operator->() const { return **this; } + + It &operator++() { Policy::increment(); return *this; } + It operator++(int) { auto copy = *this; Policy::increment(); return copy; } + It &operator--() { Policy::decrement(); return *this; } + It operator--(int) { auto copy = *this; Policy::decrement(); return copy; } + It &operator+=(difference_type n) { Policy::advance(n); return *this; } + It &operator-=(difference_type n) { Policy::advance(-n); return *this; } + + friend It operator+(const It &a, difference_type n) { auto copy = a; return copy += n; } + friend It operator+(difference_type n, const It &b) { return b + n; } + friend It operator-(const It &a, difference_type n) { auto copy = a; return copy -= n; } + friend difference_type operator-(const It &a, const It &b) { return a.distance_to(b); } + + friend bool operator==(const It &a, const It &b) { return a.equal(b); } + friend bool operator!=(const It &a, const It &b) { return !(a == b); } + friend bool operator< (const It &a, const It &b) { return b - a > 0; } + friend bool operator> (const It &a, const It &b) { return b < a; } + friend bool operator>=(const It &a, const It &b) { return !(a < b); } + friend bool operator<=(const It &a, const It &b) { return !(a > b); } +}; + +PYBIND11_NAMESPACE_BEGIN(iterator_policies) +/// Quick proxy class needed to implement ``operator->`` for iterators which can't return pointers +template +struct arrow_proxy { + T value; + + arrow_proxy(T &&value) : value(std::move(value)) { } + T *operator->() const { return &value; } +}; + +/// Lightweight iterator policy using just a simple pointer: see ``PySequence_Fast_ITEMS`` +class sequence_fast_readonly { +protected: + using iterator_category = std::random_access_iterator_tag; + using value_type = handle; + using reference = const handle; + using pointer = arrow_proxy; + + sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { } + + reference dereference() const { return *ptr; } + void increment() { ++ptr; } + void decrement() { --ptr; } + void advance(ssize_t n) { ptr += n; } + bool equal(const sequence_fast_readonly &b) const { return ptr == b.ptr; } + ssize_t distance_to(const sequence_fast_readonly &b) const { return ptr - b.ptr; } + +private: + PyObject **ptr; +}; + +/// Full read and write access using the sequence protocol: see ``detail::sequence_accessor`` +class sequence_slow_readwrite { +protected: + using iterator_category = std::random_access_iterator_tag; + using value_type = object; + using reference = sequence_accessor; + using pointer = arrow_proxy; + + sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) { } + + reference dereference() const { return {obj, static_cast(index)}; } + void increment() { ++index; } + void decrement() { --index; } + void advance(ssize_t n) { index += n; } + bool equal(const sequence_slow_readwrite &b) const { return index == b.index; } + ssize_t distance_to(const sequence_slow_readwrite &b) const { return index - b.index; } + +private: + handle obj; + ssize_t index; +}; + +/// Python's dictionary protocol permits this to be a forward iterator +class dict_readonly { +protected: + using iterator_category = std::forward_iterator_tag; + using value_type = std::pair; + using reference = const value_type; + using pointer = arrow_proxy; + + dict_readonly() = default; + dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); } + + reference dereference() const { return {key, value}; } + void increment() { if (!PyDict_Next(obj.ptr(), &pos, &key, &value)) { pos = -1; } } + bool equal(const dict_readonly &b) const { return pos == b.pos; } + +private: + handle obj; + PyObject *key = nullptr, *value = nullptr; + ssize_t pos = -1; +}; +PYBIND11_NAMESPACE_END(iterator_policies) + +#if !defined(PYPY_VERSION) +using tuple_iterator = generic_iterator; +using list_iterator = generic_iterator; +#else +using tuple_iterator = generic_iterator; +using list_iterator = generic_iterator; +#endif + +using sequence_iterator = generic_iterator; +using dict_iterator = generic_iterator; + +inline bool PyIterable_Check(PyObject *obj) { + PyObject *iter = PyObject_GetIter(obj); + if (iter) { + Py_DECREF(iter); + return true; + } else { + PyErr_Clear(); + return false; + } +} + +inline bool PyNone_Check(PyObject *o) { return o == Py_None; } +inline bool PyEllipsis_Check(PyObject *o) { return o == Py_Ellipsis; } + +inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); } + +inline bool PyStaticMethod_Check(PyObject *o) { return o->ob_type == &PyStaticMethod_Type; } + +class kwargs_proxy : public handle { +public: + explicit kwargs_proxy(handle h) : handle(h) { } +}; + +class args_proxy : public handle { +public: + explicit args_proxy(handle h) : handle(h) { } + kwargs_proxy operator*() const { return kwargs_proxy(*this); } +}; + +/// Python argument categories (using PEP 448 terms) +template using is_keyword = std::is_base_of; +template using is_s_unpacking = std::is_same; // * unpacking +template using is_ds_unpacking = std::is_same; // ** unpacking +template using is_positional = satisfies_none_of; +template using is_keyword_or_ds = satisfies_any_of; + +// Call argument collector forward declarations +template +class simple_collector; +template +class unpacking_collector; + +PYBIND11_NAMESPACE_END(detail) + +// TODO: After the deprecated constructors are removed, this macro can be simplified by +// inheriting ctors: `using Parent::Parent`. It's not an option right now because +// the `using` statement triggers the parent deprecation warning even if the ctor +// isn't even used. +#define PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + public: \ + PYBIND11_DEPRECATED("Use reinterpret_borrow<"#Name">() or reinterpret_steal<"#Name">()") \ + Name(handle h, bool is_borrowed) : Parent(is_borrowed ? Parent(h, borrowed_t{}) : Parent(h, stolen_t{})) { } \ + Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) { } \ + Name(handle h, stolen_t) : Parent(h, stolen_t{}) { } \ + PYBIND11_DEPRECATED("Use py::isinstance(obj) instead") \ + bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \ + static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } \ + template \ + Name(const ::pybind11::detail::accessor &a) : Name(object(a)) { } + +#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \ + PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ + Name(const object &o) \ + : Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ + { if (!m_ptr) throw error_already_set(); } \ + Name(object &&o) \ + : Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ + { if (!m_ptr) throw error_already_set(); } + +#define PYBIND11_OBJECT_CHECK_FAILED(Name, o) \ + ::pybind11::type_error("Object of type '" + \ + ::pybind11::detail::get_fully_qualified_tp_name(Py_TYPE(o.ptr())) + \ + "' is not an instance of '" #Name "'") + +#define PYBIND11_OBJECT(Name, Parent, CheckFun) \ + PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ + Name(const object &o) : Parent(o) \ + { if (o && !check_(o)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, o); } \ + Name(object &&o) : Parent(std::move(o)) \ + { if (o && !check_(o)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, o); } + +#define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \ + PYBIND11_OBJECT(Name, Parent, CheckFun) \ + Name() : Parent() { } + +/// \addtogroup pytypes +/// @{ + +/** \rst + Wraps a Python iterator so that it can also be used as a C++ input iterator + + Caveat: copying an iterator does not (and cannot) clone the internal + state of the Python iterable. This also applies to the post-increment + operator. This iterator should only be used to retrieve the current + value using ``operator*()``. +\endrst */ +class iterator : public object { +public: + using iterator_category = std::input_iterator_tag; + using difference_type = ssize_t; + using value_type = handle; + using reference = const handle; + using pointer = const handle *; + + PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check) + + iterator& operator++() { + advance(); + return *this; + } + + iterator operator++(int) { + auto rv = *this; + advance(); + return rv; + } + + reference operator*() const { + if (m_ptr && !value.ptr()) { + auto& self = const_cast(*this); + self.advance(); + } + return value; + } + + pointer operator->() const { operator*(); return &value; } + + /** \rst + The value which marks the end of the iteration. ``it == iterator::sentinel()`` + is equivalent to catching ``StopIteration`` in Python. + + .. code-block:: cpp + + void foo(py::iterator it) { + while (it != py::iterator::sentinel()) { + // use `*it` + ++it; + } + } + \endrst */ + static iterator sentinel() { return {}; } + + friend bool operator==(const iterator &a, const iterator &b) { return a->ptr() == b->ptr(); } + friend bool operator!=(const iterator &a, const iterator &b) { return a->ptr() != b->ptr(); } + +private: + void advance() { + value = reinterpret_steal(PyIter_Next(m_ptr)); + if (PyErr_Occurred()) { throw error_already_set(); } + } + +private: + object value = {}; +}; + + + +class type : public object { +public: + PYBIND11_OBJECT(type, object, PyType_Check) + + /// Return a type handle from a handle or an object + static handle handle_of(handle h) { return handle((PyObject*) Py_TYPE(h.ptr())); } + + /// Return a type object from a handle or an object + static type of(handle h) { return type(type::handle_of(h), borrowed_t{}); } + + // Defined in pybind11/cast.h + /// Convert C++ type to handle if previously registered. Does not convert + /// standard types, like int, float. etc. yet. + /// See https://github.com/pybind/pybind11/issues/2486 + template + static handle handle_of(); + + /// Convert C++ type to type if previously registered. Does not convert + /// standard types, like int, float. etc. yet. + /// See https://github.com/pybind/pybind11/issues/2486 + template + static type of() {return type(type::handle_of(), borrowed_t{}); } +}; + +class iterable : public object { +public: + PYBIND11_OBJECT_DEFAULT(iterable, object, detail::PyIterable_Check) +}; + +class bytes; + +class str : public object { +public: + PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str) + + str(const char *c, size_t n) + : object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate string object!"); + } + + // 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects + str(const char *c = "") + : object(PyUnicode_FromString(c), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate string object!"); + } + + str(const std::string &s) : str(s.data(), s.size()) { } + + explicit str(const bytes &b); + + /** \rst + Return a string representation of the object. This is analogous to + the ``str()`` function in Python. + \endrst */ + explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { if (!m_ptr) throw error_already_set(); } + + operator std::string() const { + object temp = *this; + if (PyUnicode_Check(m_ptr)) { + temp = reinterpret_steal(PyUnicode_AsUTF8String(m_ptr)); + if (!temp) + pybind11_fail("Unable to extract string contents! (encoding issue)"); + } + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract string contents! (invalid type)"); + return std::string(buffer, (size_t) length); + } + + template + str format(Args &&...args) const { + return attr("format")(std::forward(args)...); + } + +private: + /// Return string representation -- always returns a new reference, even if already a str + static PyObject *raw_str(PyObject *op) { + PyObject *str_value = PyObject_Str(op); +#if PY_MAJOR_VERSION < 3 + if (!str_value) throw error_already_set(); + PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); + Py_XDECREF(str_value); str_value = unicode; +#endif + return str_value; + } +}; +/// @} pytypes + +inline namespace literals { +/** \rst + String literal version of `str` + \endrst */ +inline str operator"" _s(const char *s, size_t size) { return {s, size}; } +} // namespace literals + +/// \addtogroup pytypes +/// @{ +class bytes : public object { +public: + PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK) + + // Allow implicit conversion: + bytes(const char *c = "") + : object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); + } + + bytes(const char *c, size_t n) + : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); + } + + // Allow implicit conversion: + bytes(const std::string &s) : bytes(s.data(), s.size()) { } + + explicit bytes(const pybind11::str &s); + + operator std::string() const { + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length)) + pybind11_fail("Unable to extract bytes contents!"); + return std::string(buffer, (size_t) length); + } +}; +// Note: breathe >= 4.17.0 will fail to build docs if the below two constructors +// are included in the doxygen group; close here and reopen after as a workaround +/// @} pytypes + +inline bytes::bytes(const pybind11::str &s) { + object temp = s; + if (PyUnicode_Check(s.ptr())) { + temp = reinterpret_steal(PyUnicode_AsUTF8String(s.ptr())); + if (!temp) + pybind11_fail("Unable to extract string contents! (encoding issue)"); + } + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract string contents! (invalid type)"); + auto obj = reinterpret_steal(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length)); + if (!obj) + pybind11_fail("Could not allocate bytes object!"); + m_ptr = obj.release().ptr(); +} + +inline str::str(const bytes& b) { + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract bytes contents!"); + auto obj = reinterpret_steal(PyUnicode_FromStringAndSize(buffer, (ssize_t) length)); + if (!obj) + pybind11_fail("Could not allocate string object!"); + m_ptr = obj.release().ptr(); +} + +/// \addtogroup pytypes +/// @{ +class none : public object { +public: + PYBIND11_OBJECT(none, object, detail::PyNone_Check) + none() : object(Py_None, borrowed_t{}) { } +}; + +class ellipsis : public object { +public: + PYBIND11_OBJECT(ellipsis, object, detail::PyEllipsis_Check) + ellipsis() : object(Py_Ellipsis, borrowed_t{}) { } +}; + +class bool_ : public object { +public: + PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool) + bool_() : object(Py_False, borrowed_t{}) { } + // Allow implicit conversion from and to `bool`: + bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { } + operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; } + +private: + /// Return the truth value of an object -- always returns a new reference + static PyObject *raw_bool(PyObject *op) { + const auto value = PyObject_IsTrue(op); + if (value == -1) return nullptr; + return handle(value ? Py_True : Py_False).inc_ref().ptr(); + } +}; + +PYBIND11_NAMESPACE_BEGIN(detail) +// Converts a value to the given unsigned type. If an error occurs, you get back (Unsigned) -1; +// otherwise you get back the unsigned long or unsigned long long value cast to (Unsigned). +// (The distinction is critically important when casting a returned -1 error value to some other +// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes). +template +Unsigned as_unsigned(PyObject *o) { + if (sizeof(Unsigned) <= sizeof(unsigned long) +#if PY_VERSION_HEX < 0x03000000 + || PyInt_Check(o) +#endif + ) { + unsigned long v = PyLong_AsUnsignedLong(o); + return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; + } + else { + unsigned long long v = PyLong_AsUnsignedLongLong(o); + return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; + } +} +PYBIND11_NAMESPACE_END(detail) + +class int_ : public object { +public: + PYBIND11_OBJECT_CVT(int_, object, PYBIND11_LONG_CHECK, PyNumber_Long) + int_() : object(PyLong_FromLong(0), stolen_t{}) { } + // Allow implicit conversion from C++ integral types: + template ::value, int> = 0> + int_(T value) { + if (sizeof(T) <= sizeof(long)) { + if (std::is_signed::value) + m_ptr = PyLong_FromLong((long) value); + else + m_ptr = PyLong_FromUnsignedLong((unsigned long) value); + } else { + if (std::is_signed::value) + m_ptr = PyLong_FromLongLong((long long) value); + else + m_ptr = PyLong_FromUnsignedLongLong((unsigned long long) value); + } + if (!m_ptr) pybind11_fail("Could not allocate int object!"); + } + + template ::value, int> = 0> + operator T() const { + return std::is_unsigned::value + ? detail::as_unsigned(m_ptr) + : sizeof(T) <= sizeof(long) + ? (T) PyLong_AsLong(m_ptr) + : (T) PYBIND11_LONG_AS_LONGLONG(m_ptr); + } +}; + +class float_ : public object { +public: + PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float) + // Allow implicit conversion from float/double: + float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate float object!"); + } + float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate float object!"); + } + operator float() const { return (float) PyFloat_AsDouble(m_ptr); } + operator double() const { return (double) PyFloat_AsDouble(m_ptr); } +}; + +class weakref : public object { +public: + PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check) + explicit weakref(handle obj, handle callback = {}) + : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate weak reference!"); + } +}; + +class slice : public object { +public: + PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check) + slice(ssize_t start_, ssize_t stop_, ssize_t step_) { + int_ start(start_), stop(stop_), step(step_); + m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr()); + if (!m_ptr) pybind11_fail("Could not allocate slice object!"); + } + bool compute(size_t length, size_t *start, size_t *stop, size_t *step, + size_t *slicelength) const { + return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, + (ssize_t) length, (ssize_t *) start, + (ssize_t *) stop, (ssize_t *) step, + (ssize_t *) slicelength) == 0; + } + bool compute(ssize_t length, ssize_t *start, ssize_t *stop, ssize_t *step, + ssize_t *slicelength) const { + return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, + length, start, + stop, step, + slicelength) == 0; + } +}; + +class capsule : public object { +public: + PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact) + PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") + capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed_t{}) : object(ptr, stolen_t{})) { } + + explicit capsule(const void *value, const char *name = nullptr, void (*destructor)(PyObject *) = nullptr) + : object(PyCapsule_New(const_cast(value), name, destructor), stolen_t{}) { + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input") + capsule(const void *value, void (*destruct)(PyObject *)) + : object(PyCapsule_New(const_cast(value), nullptr, destruct), stolen_t{}) { + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + capsule(const void *value, void (*destructor)(void *)) { + m_ptr = PyCapsule_New(const_cast(value), nullptr, [](PyObject *o) { + auto destructor = reinterpret_cast(PyCapsule_GetContext(o)); + void *ptr = PyCapsule_GetPointer(o, nullptr); + destructor(ptr); + }); + + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + + if (PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) + pybind11_fail("Could not set capsule context!"); + } + + capsule(void (*destructor)()) { + m_ptr = PyCapsule_New(reinterpret_cast(destructor), nullptr, [](PyObject *o) { + auto destructor = reinterpret_cast(PyCapsule_GetPointer(o, nullptr)); + destructor(); + }); + + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + template operator T *() const { + return get_pointer(); + } + + /// Get the pointer the capsule holds. + template + T* get_pointer() const { + auto name = this->name(); + T *result = static_cast(PyCapsule_GetPointer(m_ptr, name)); + if (!result) pybind11_fail("Unable to extract capsule contents!"); + return result; + } + + /// Replaces a capsule's pointer *without* calling the destructor on the existing one. + void set_pointer(const void *value) { + if (PyCapsule_SetPointer(m_ptr, const_cast(value)) != 0) + pybind11_fail("Could not set capsule pointer"); + } + + const char *name() const { return PyCapsule_GetName(m_ptr); } +}; + +class tuple : public object { +public: + PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple) + explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); + } + size_t size() const { return (size_t) PyTuple_Size(m_ptr); } + bool empty() const { return size() == 0; } + detail::tuple_accessor operator[](size_t index) const { return {*this, index}; } + detail::item_accessor operator[](handle h) const { return object::operator[](h); } + detail::tuple_iterator begin() const { return {*this, 0}; } + detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; } +}; + +class dict : public object { +public: + PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict) + dict() : object(PyDict_New(), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate dict object!"); + } + template ...>::value>, + // MSVC workaround: it can't compile an out-of-line definition, so defer the collector + typename collector = detail::deferred_t, Args...>> + explicit dict(Args &&...args) : dict(collector(std::forward(args)...).kwargs()) { } + + size_t size() const { return (size_t) PyDict_Size(m_ptr); } + bool empty() const { return size() == 0; } + detail::dict_iterator begin() const { return {*this, 0}; } + detail::dict_iterator end() const { return {}; } + void clear() const { PyDict_Clear(ptr()); } + template bool contains(T &&key) const { + return PyDict_Contains(m_ptr, detail::object_or_cast(std::forward(key)).ptr()) == 1; + } + +private: + /// Call the `dict` Python type -- always returns a new reference + static PyObject *raw_dict(PyObject *op) { + if (PyDict_Check(op)) + return handle(op).inc_ref().ptr(); + return PyObject_CallFunctionObjArgs((PyObject *) &PyDict_Type, op, nullptr); + } +}; + +class sequence : public object { +public: + PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check) + size_t size() const { + ssize_t result = PySequence_Size(m_ptr); + if (result == -1) + throw error_already_set(); + return (size_t) result; + } + bool empty() const { return size() == 0; } + detail::sequence_accessor operator[](size_t index) const { return {*this, index}; } + detail::item_accessor operator[](handle h) const { return object::operator[](h); } + detail::sequence_iterator begin() const { return {*this, 0}; } + detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; } +}; + +class list : public object { +public: + PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List) + explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate list object!"); + } + size_t size() const { return (size_t) PyList_Size(m_ptr); } + bool empty() const { return size() == 0; } + detail::list_accessor operator[](size_t index) const { return {*this, index}; } + detail::item_accessor operator[](handle h) const { return object::operator[](h); } + detail::list_iterator begin() const { return {*this, 0}; } + detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; } + template void append(T &&val) const { + PyList_Append(m_ptr, detail::object_or_cast(std::forward(val)).ptr()); + } + template void insert(size_t index, T &&val) const { + PyList_Insert(m_ptr, static_cast(index), + detail::object_or_cast(std::forward(val)).ptr()); + } +}; + +class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) }; +class kwargs : public dict { PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check) }; + +class set : public object { +public: + PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New) + set() : object(PySet_New(nullptr), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate set object!"); + } + size_t size() const { return (size_t) PySet_Size(m_ptr); } + bool empty() const { return size() == 0; } + template bool add(T &&val) const { + return PySet_Add(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) == 0; + } + void clear() const { PySet_Clear(m_ptr); } + template bool contains(T &&val) const { + return PySet_Contains(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) == 1; + } +}; + +class function : public object { +public: + PYBIND11_OBJECT_DEFAULT(function, object, PyCallable_Check) + handle cpp_function() const { + handle fun = detail::get_function(m_ptr); + if (fun && PyCFunction_Check(fun.ptr())) + return fun; + return handle(); + } + bool is_cpp_function() const { return (bool) cpp_function(); } +}; + +class staticmethod : public object { +public: + PYBIND11_OBJECT_CVT(staticmethod, object, detail::PyStaticMethod_Check, PyStaticMethod_New) +}; + +class buffer : public object { +public: + PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer) + + buffer_info request(bool writable = false) const { + int flags = PyBUF_STRIDES | PyBUF_FORMAT; + if (writable) flags |= PyBUF_WRITABLE; + auto *view = new Py_buffer(); + if (PyObject_GetBuffer(m_ptr, view, flags) != 0) { + delete view; + throw error_already_set(); + } + return buffer_info(view); + } +}; + +class memoryview : public object { +public: + PYBIND11_OBJECT_CVT(memoryview, object, PyMemoryView_Check, PyMemoryView_FromObject) + + /** \rst + Creates ``memoryview`` from ``buffer_info``. + + ``buffer_info`` must be created from ``buffer::request()``. Otherwise + throws an exception. + + For creating a ``memoryview`` from objects that support buffer protocol, + use ``memoryview(const object& obj)`` instead of this constructor. + \endrst */ + explicit memoryview(const buffer_info& info) { + if (!info.view()) + pybind11_fail("Prohibited to create memoryview without Py_buffer"); + // Note: PyMemoryView_FromBuffer never increments obj reference. + m_ptr = (info.view()->obj) ? + PyMemoryView_FromObject(info.view()->obj) : + PyMemoryView_FromBuffer(info.view()); + if (!m_ptr) + pybind11_fail("Unable to create memoryview from buffer descriptor"); + } + + /** \rst + Creates ``memoryview`` from static buffer. + + This method is meant for providing a ``memoryview`` for C/C++ buffer not + managed by Python. The caller is responsible for managing the lifetime + of ``ptr`` and ``format``, which MUST outlive the memoryview constructed + here. + + See also: Python C API documentation for `PyMemoryView_FromBuffer`_. + + .. _PyMemoryView_FromBuffer: https://docs.python.org/c-api/memoryview.html#c.PyMemoryView_FromBuffer + + :param ptr: Pointer to the buffer. + :param itemsize: Byte size of an element. + :param format: Pointer to the null-terminated format string. For + homogeneous Buffers, this should be set to + ``format_descriptor::value``. + :param shape: Shape of the tensor (1 entry per dimension). + :param strides: Number of bytes between adjacent entries (for each + per dimension). + :param readonly: Flag to indicate if the underlying storage may be + written to. + \endrst */ + static memoryview from_buffer( + void *ptr, ssize_t itemsize, const char *format, + detail::any_container shape, + detail::any_container strides, bool readonly = false); + + static memoryview from_buffer( + const void *ptr, ssize_t itemsize, const char *format, + detail::any_container shape, + detail::any_container strides) { + return memoryview::from_buffer( + const_cast(ptr), itemsize, format, shape, strides, true); + } + + template + static memoryview from_buffer( + T *ptr, detail::any_container shape, + detail::any_container strides, bool readonly = false) { + return memoryview::from_buffer( + reinterpret_cast(ptr), sizeof(T), + format_descriptor::value, shape, strides, readonly); + } + + template + static memoryview from_buffer( + const T *ptr, detail::any_container shape, + detail::any_container strides) { + return memoryview::from_buffer( + const_cast(ptr), shape, strides, true); + } + +#if PY_MAJOR_VERSION >= 3 + /** \rst + Creates ``memoryview`` from static memory. + + This method is meant for providing a ``memoryview`` for C/C++ buffer not + managed by Python. The caller is responsible for managing the lifetime + of ``mem``, which MUST outlive the memoryview constructed here. + + This method is not available in Python 2. + + See also: Python C API documentation for `PyMemoryView_FromBuffer`_. + + .. _PyMemoryView_FromMemory: https://docs.python.org/c-api/memoryview.html#c.PyMemoryView_FromMemory + \endrst */ + static memoryview from_memory(void *mem, ssize_t size, bool readonly = false) { + PyObject* ptr = PyMemoryView_FromMemory( + reinterpret_cast(mem), size, + (readonly) ? PyBUF_READ : PyBUF_WRITE); + if (!ptr) + pybind11_fail("Could not allocate memoryview object!"); + return memoryview(object(ptr, stolen_t{})); + } + + static memoryview from_memory(const void *mem, ssize_t size) { + return memoryview::from_memory(const_cast(mem), size, true); + } +#endif +}; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +inline memoryview memoryview::from_buffer( + void *ptr, ssize_t itemsize, const char* format, + detail::any_container shape, + detail::any_container strides, bool readonly) { + size_t ndim = shape->size(); + if (ndim != strides->size()) + pybind11_fail("memoryview: shape length doesn't match strides length"); + ssize_t size = ndim ? 1 : 0; + for (size_t i = 0; i < ndim; ++i) + size *= (*shape)[i]; + Py_buffer view; + view.buf = ptr; + view.obj = nullptr; + view.len = size * itemsize; + view.readonly = static_cast(readonly); + view.itemsize = itemsize; + view.format = const_cast(format); + view.ndim = static_cast(ndim); + view.shape = shape->data(); + view.strides = strides->data(); + view.suboffsets = nullptr; + view.internal = nullptr; + PyObject* obj = PyMemoryView_FromBuffer(&view); + if (!obj) + throw error_already_set(); + return memoryview(object(obj, stolen_t{})); +} +#endif // DOXYGEN_SHOULD_SKIP_THIS +/// @} pytypes + +/// \addtogroup python_builtins +/// @{ + +/// Get the length of a Python object. +inline size_t len(handle h) { + ssize_t result = PyObject_Length(h.ptr()); + if (result < 0) + throw error_already_set(); + return (size_t) result; +} + +/// Get the length hint of a Python object. +/// Returns 0 when this cannot be determined. +inline size_t len_hint(handle h) { +#if PY_VERSION_HEX >= 0x03040000 + ssize_t result = PyObject_LengthHint(h.ptr(), 0); +#else + ssize_t result = PyObject_Length(h.ptr()); +#endif + if (result < 0) { + // Sometimes a length can't be determined at all (eg generators) + // In which case simply return 0 + PyErr_Clear(); + return 0; + } + return (size_t) result; +} + +inline str repr(handle h) { + PyObject *str_value = PyObject_Repr(h.ptr()); + if (!str_value) throw error_already_set(); +#if PY_MAJOR_VERSION < 3 + PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); + Py_XDECREF(str_value); str_value = unicode; + if (!str_value) throw error_already_set(); +#endif + return reinterpret_steal(str_value); +} + +inline iterator iter(handle obj) { + PyObject *result = PyObject_GetIter(obj.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} +/// @} python_builtins + +PYBIND11_NAMESPACE_BEGIN(detail) +template iterator object_api::begin() const { return iter(derived()); } +template iterator object_api::end() const { return iterator::sentinel(); } +template item_accessor object_api::operator[](handle key) const { + return {derived(), reinterpret_borrow(key)}; +} +template item_accessor object_api::operator[](const char *key) const { + return {derived(), pybind11::str(key)}; +} +template obj_attr_accessor object_api::attr(handle key) const { + return {derived(), reinterpret_borrow(key)}; +} +template str_attr_accessor object_api::attr(const char *key) const { + return {derived(), key}; +} +template args_proxy object_api::operator*() const { + return args_proxy(derived().ptr()); +} +template template bool object_api::contains(T &&item) const { + return attr("__contains__")(std::forward(item)).template cast(); +} + +template +pybind11::str object_api::str() const { return pybind11::str(derived()); } + +template +str_attr_accessor object_api::doc() const { return attr("__doc__"); } + +template +handle object_api::get_type() const { return type::handle_of(derived()); } + +template +bool object_api::rich_compare(object_api const &other, int value) const { + int rv = PyObject_RichCompareBool(derived().ptr(), other.derived().ptr(), value); + if (rv == -1) + throw error_already_set(); + return rv == 1; +} + +#define PYBIND11_MATH_OPERATOR_UNARY(op, fn) \ + template object object_api::op() const { \ + object result = reinterpret_steal(fn(derived().ptr())); \ + if (!result.ptr()) \ + throw error_already_set(); \ + return result; \ + } + +#define PYBIND11_MATH_OPERATOR_BINARY(op, fn) \ + template \ + object object_api::op(object_api const &other) const { \ + object result = reinterpret_steal( \ + fn(derived().ptr(), other.derived().ptr())); \ + if (!result.ptr()) \ + throw error_already_set(); \ + return result; \ + } + +PYBIND11_MATH_OPERATOR_UNARY (operator~, PyNumber_Invert) +PYBIND11_MATH_OPERATOR_UNARY (operator-, PyNumber_Negative) +PYBIND11_MATH_OPERATOR_BINARY(operator+, PyNumber_Add) +PYBIND11_MATH_OPERATOR_BINARY(operator+=, PyNumber_InPlaceAdd) +PYBIND11_MATH_OPERATOR_BINARY(operator-, PyNumber_Subtract) +PYBIND11_MATH_OPERATOR_BINARY(operator-=, PyNumber_InPlaceSubtract) +PYBIND11_MATH_OPERATOR_BINARY(operator*, PyNumber_Multiply) +PYBIND11_MATH_OPERATOR_BINARY(operator*=, PyNumber_InPlaceMultiply) +PYBIND11_MATH_OPERATOR_BINARY(operator/, PyNumber_TrueDivide) +PYBIND11_MATH_OPERATOR_BINARY(operator/=, PyNumber_InPlaceTrueDivide) +PYBIND11_MATH_OPERATOR_BINARY(operator|, PyNumber_Or) +PYBIND11_MATH_OPERATOR_BINARY(operator|=, PyNumber_InPlaceOr) +PYBIND11_MATH_OPERATOR_BINARY(operator&, PyNumber_And) +PYBIND11_MATH_OPERATOR_BINARY(operator&=, PyNumber_InPlaceAnd) +PYBIND11_MATH_OPERATOR_BINARY(operator^, PyNumber_Xor) +PYBIND11_MATH_OPERATOR_BINARY(operator^=, PyNumber_InPlaceXor) +PYBIND11_MATH_OPERATOR_BINARY(operator<<, PyNumber_Lshift) +PYBIND11_MATH_OPERATOR_BINARY(operator<<=, PyNumber_InPlaceLshift) +PYBIND11_MATH_OPERATOR_BINARY(operator>>, PyNumber_Rshift) +PYBIND11_MATH_OPERATOR_BINARY(operator>>=, PyNumber_InPlaceRshift) + +#undef PYBIND11_MATH_OPERATOR_UNARY +#undef PYBIND11_MATH_OPERATOR_BINARY + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/include/pybind11/stl.h b/extern/pybind/include/pybind11/stl.h new file mode 100644 index 00000000..721bb669 --- /dev/null +++ b/extern/pybind/include/pybind11/stl.h @@ -0,0 +1,388 @@ +/* + pybind11/stl.h: Transparent conversion for STL data types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +#ifdef __has_include +// std::optional (but including it in c++14 mode isn't allowed) +# if defined(PYBIND11_CPP17) && __has_include() +# include +# define PYBIND11_HAS_OPTIONAL 1 +# endif +// std::experimental::optional (but not allowed in c++11 mode) +# if defined(PYBIND11_CPP14) && (__has_include() && \ + !__has_include()) +# include +# define PYBIND11_HAS_EXP_OPTIONAL 1 +# endif +// std::variant +# if defined(PYBIND11_CPP17) && __has_include() +# include +# define PYBIND11_HAS_VARIANT 1 +# endif +#elif defined(_MSC_VER) && defined(PYBIND11_CPP17) +# include +# include +# define PYBIND11_HAS_OPTIONAL 1 +# define PYBIND11_HAS_VARIANT 1 +#endif + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for +/// forwarding a container element). Typically used indirect via forwarded_type(), below. +template +using forwarded_type = conditional_t< + std::is_lvalue_reference::value, remove_reference_t &, remove_reference_t &&>; + +/// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically +/// used for forwarding a container's elements. +template +forwarded_type forward_like(U &&u) { + return std::forward>(std::forward(u)); +} + +template struct set_caster { + using type = Type; + using key_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto s = reinterpret_borrow(src); + value.clear(); + for (auto entry : s) { + key_conv conv; + if (!conv.load(entry, convert)) + return false; + value.insert(cast_op(std::move(conv))); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + if (!std::is_lvalue_reference::value) + policy = return_value_policy_override::policy(policy); + pybind11::set s; + for (auto &&value : src) { + auto value_ = reinterpret_steal(key_conv::cast(forward_like(value), policy, parent)); + if (!value_ || !s.add(value_)) + return handle(); + } + return s.release(); + } + + PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name + _("]")); +}; + +template struct map_caster { + using key_conv = make_caster; + using value_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto d = reinterpret_borrow(src); + value.clear(); + for (auto it : d) { + key_conv kconv; + value_conv vconv; + if (!kconv.load(it.first.ptr(), convert) || + !vconv.load(it.second.ptr(), convert)) + return false; + value.emplace(cast_op(std::move(kconv)), cast_op(std::move(vconv))); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + dict d; + return_value_policy policy_key = policy; + return_value_policy policy_value = policy; + if (!std::is_lvalue_reference::value) { + policy_key = return_value_policy_override::policy(policy_key); + policy_value = return_value_policy_override::policy(policy_value); + } + for (auto &&kv : src) { + auto key = reinterpret_steal(key_conv::cast(forward_like(kv.first), policy_key, parent)); + auto value = reinterpret_steal(value_conv::cast(forward_like(kv.second), policy_value, parent)); + if (!key || !value) + return handle(); + d[key] = value; + } + return d.release(); + } + + PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name + _(", ") + value_conv::name + _("]")); +}; + +template struct list_caster { + using value_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src) || isinstance(src)) + return false; + auto s = reinterpret_borrow(src); + value.clear(); + reserve_maybe(s, &value); + for (auto it : s) { + value_conv conv; + if (!conv.load(it, convert)) + return false; + value.push_back(cast_op(std::move(conv))); + } + return true; + } + +private: + template ().reserve(0)), void>::value, int> = 0> + void reserve_maybe(sequence s, Type *) { value.reserve(s.size()); } + void reserve_maybe(sequence, void *) { } + +public: + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + if (!std::is_lvalue_reference::value) + policy = return_value_policy_override::policy(policy); + list l(src.size()); + size_t index = 0; + for (auto &&value : src) { + auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); + if (!value_) + return handle(); + PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference + } + return l.release(); + } + + PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name + _("]")); +}; + +template struct type_caster> + : list_caster, Type> { }; + +template struct type_caster> + : list_caster, Type> { }; + +template struct type_caster> + : list_caster, Type> { }; + +template struct array_caster { + using value_conv = make_caster; + +private: + template + bool require_size(enable_if_t size) { + if (value.size() != size) + value.resize(size); + return true; + } + template + bool require_size(enable_if_t size) { + return size == Size; + } + +public: + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto l = reinterpret_borrow(src); + if (!require_size(l.size())) + return false; + size_t ctr = 0; + for (auto it : l) { + value_conv conv; + if (!conv.load(it, convert)) + return false; + value[ctr++] = cast_op(std::move(conv)); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + list l(src.size()); + size_t index = 0; + for (auto &&value : src) { + auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); + if (!value_) + return handle(); + PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference + } + return l.release(); + } + + PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name + _(_(""), _("[") + _() + _("]")) + _("]")); +}; + +template struct type_caster> + : array_caster, Type, false, Size> { }; + +template struct type_caster> + : array_caster, Type, true> { }; + +template struct type_caster> + : set_caster, Key> { }; + +template struct type_caster> + : set_caster, Key> { }; + +template struct type_caster> + : map_caster, Key, Value> { }; + +template struct type_caster> + : map_caster, Key, Value> { }; + +// This type caster is intended to be used for std::optional and std::experimental::optional +template struct optional_caster { + using value_conv = make_caster; + + template + static handle cast(T_ &&src, return_value_policy policy, handle parent) { + if (!src) + return none().inc_ref(); + if (!std::is_lvalue_reference::value) { + policy = return_value_policy_override::policy(policy); + } + return value_conv::cast(*std::forward(src), policy, parent); + } + + bool load(handle src, bool convert) { + if (!src) { + return false; + } else if (src.is_none()) { + return true; // default-constructed value is already empty + } + value_conv inner_caster; + if (!inner_caster.load(src, convert)) + return false; + + value.emplace(cast_op(std::move(inner_caster))); + return true; + } + + PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name + _("]")); +}; + +#if defined(PYBIND11_HAS_OPTIONAL) +template struct type_caster> + : public optional_caster> {}; + +template<> struct type_caster + : public void_caster {}; +#endif + +#if defined(PYBIND11_HAS_EXP_OPTIONAL) +template struct type_caster> + : public optional_caster> {}; + +template<> struct type_caster + : public void_caster {}; +#endif + +/// Visit a variant and cast any found type to Python +struct variant_caster_visitor { + return_value_policy policy; + handle parent; + + using result_type = handle; // required by boost::variant in C++11 + + template + result_type operator()(T &&src) const { + return make_caster::cast(std::forward(src), policy, parent); + } +}; + +/// Helper class which abstracts away variant's `visit` function. `std::variant` and similar +/// `namespace::variant` types which provide a `namespace::visit()` function are handled here +/// automatically using argument-dependent lookup. Users can provide specializations for other +/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`. +template class Variant> +struct visit_helper { + template + static auto call(Args &&...args) -> decltype(visit(std::forward(args)...)) { + return visit(std::forward(args)...); + } +}; + +/// Generic variant caster +template struct variant_caster; + +template class V, typename... Ts> +struct variant_caster> { + static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative."); + + template + bool load_alternative(handle src, bool convert, type_list) { + auto caster = make_caster(); + if (caster.load(src, convert)) { + value = cast_op(caster); + return true; + } + return load_alternative(src, convert, type_list{}); + } + + bool load_alternative(handle, bool, type_list<>) { return false; } + + bool load(handle src, bool convert) { + // Do a first pass without conversions to improve constructor resolution. + // E.g. `py::int_(1).cast>()` needs to fill the `int` + // slot of the variant. Without two-pass loading `double` would be filled + // because it appears first and a conversion is possible. + if (convert && load_alternative(src, false, type_list{})) + return true; + return load_alternative(src, convert, type_list{}); + } + + template + static handle cast(Variant &&src, return_value_policy policy, handle parent) { + return visit_helper::call(variant_caster_visitor{policy, parent}, + std::forward(src)); + } + + using Type = V; + PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster::name...) + _("]")); +}; + +#if defined(PYBIND11_HAS_VARIANT) +template +struct type_caster> : variant_caster> { }; +#endif + +PYBIND11_NAMESPACE_END(detail) + +inline std::ostream &operator<<(std::ostream &os, const handle &obj) { + os << (std::string) str(obj); + return os; +} + +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/extern/pybind/include/pybind11/stl_bind.h b/extern/pybind/include/pybind11/stl_bind.h new file mode 100644 index 00000000..9d8ed0c8 --- /dev/null +++ b/extern/pybind/include/pybind11/stl_bind.h @@ -0,0 +1,661 @@ +/* + pybind11/std_bind.h: Binding generators for STL data types + + Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" +#include "operators.h" + +#include +#include + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +/* SFINAE helper class used by 'is_comparable */ +template struct container_traits { + template static std::true_type test_comparable(decltype(std::declval() == std::declval())*); + template static std::false_type test_comparable(...); + template static std::true_type test_value(typename T2::value_type *); + template static std::false_type test_value(...); + template static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); + template static std::false_type test_pair(...); + + static constexpr const bool is_comparable = std::is_same(nullptr))>::value; + static constexpr const bool is_pair = std::is_same(nullptr, nullptr))>::value; + static constexpr const bool is_vector = std::is_same(nullptr))>::value; + static constexpr const bool is_element = !is_pair && !is_vector; +}; + +/* Default: is_comparable -> std::false_type */ +template +struct is_comparable : std::false_type { }; + +/* For non-map data structures, check whether operator== can be instantiated */ +template +struct is_comparable< + T, enable_if_t::is_element && + container_traits::is_comparable>> + : std::true_type { }; + +/* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */ +template +struct is_comparable::is_vector>> { + static constexpr const bool value = + is_comparable::value; +}; + +/* For pairs, recursively check the two data types */ +template +struct is_comparable::is_pair>> { + static constexpr const bool value = + is_comparable::value && + is_comparable::value; +}; + +/* Fallback functions */ +template void vector_if_copy_constructible(const Args &...) { } +template void vector_if_equal_operator(const Args &...) { } +template void vector_if_insertion_operator(const Args &...) { } +template void vector_modifiers(const Args &...) { } + +template +void vector_if_copy_constructible(enable_if_t::value, Class_> &cl) { + cl.def(init(), "Copy constructor"); +} + +template +void vector_if_equal_operator(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + + cl.def(self == self); + cl.def(self != self); + + cl.def("count", + [](const Vector &v, const T &x) { + return std::count(v.begin(), v.end(), x); + }, + arg("x"), + "Return the number of times ``x`` appears in the list" + ); + + cl.def("remove", [](Vector &v, const T &x) { + auto p = std::find(v.begin(), v.end(), x); + if (p != v.end()) + v.erase(p); + else + throw value_error(); + }, + arg("x"), + "Remove the first item from the list whose value is x. " + "It is an error if there is no such item." + ); + + cl.def("__contains__", + [](const Vector &v, const T &x) { + return std::find(v.begin(), v.end(), x) != v.end(); + }, + arg("x"), + "Return true the container contains ``x``" + ); +} + +// Vector modifiers -- requires a copyable vector_type: +// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems +// silly to allow deletion but not insertion, so include them here too.) +template +void vector_modifiers(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using DiffType = typename Vector::difference_type; + + auto wrap_i = [](DiffType i, SizeType n) { + if (i < 0) + i += n; + if (i < 0 || (SizeType)i >= n) + throw index_error(); + return i; + }; + + cl.def("append", + [](Vector &v, const T &value) { v.push_back(value); }, + arg("x"), + "Add an item to the end of the list"); + + cl.def(init([](iterable it) { + auto v = std::unique_ptr(new Vector()); + v->reserve(len_hint(it)); + for (handle h : it) + v->push_back(h.cast()); + return v.release(); + })); + + cl.def("clear", + [](Vector &v) { + v.clear(); + }, + "Clear the contents" + ); + + cl.def("extend", + [](Vector &v, const Vector &src) { + v.insert(v.end(), src.begin(), src.end()); + }, + arg("L"), + "Extend the list by appending all the items in the given list" + ); + + cl.def("extend", + [](Vector &v, iterable it) { + const size_t old_size = v.size(); + v.reserve(old_size + len_hint(it)); + try { + for (handle h : it) { + v.push_back(h.cast()); + } + } catch (const cast_error &) { + v.erase(v.begin() + static_cast(old_size), v.end()); + try { + v.shrink_to_fit(); + } catch (const std::exception &) { + // Do nothing + } + throw; + } + }, + arg("L"), + "Extend the list by appending all the items in the given list" + ); + + cl.def("insert", + [](Vector &v, DiffType i, const T &x) { + // Can't use wrap_i; i == v.size() is OK + if (i < 0) + i += v.size(); + if (i < 0 || (SizeType)i > v.size()) + throw index_error(); + v.insert(v.begin() + i, x); + }, + arg("i") , arg("x"), + "Insert an item at a given position." + ); + + cl.def("pop", + [](Vector &v) { + if (v.empty()) + throw index_error(); + T t = v.back(); + v.pop_back(); + return t; + }, + "Remove and return the last item" + ); + + cl.def("pop", + [wrap_i](Vector &v, DiffType i) { + i = wrap_i(i, v.size()); + T t = v[(SizeType) i]; + v.erase(v.begin() + i); + return t; + }, + arg("i"), + "Remove and return the item at index ``i``" + ); + + cl.def("__setitem__", + [wrap_i](Vector &v, DiffType i, const T &t) { + i = wrap_i(i, v.size()); + v[(SizeType)i] = t; + } + ); + + /// Slicing protocol + cl.def("__getitem__", + [](const Vector &v, slice slice) -> Vector * { + size_t start, stop, step, slicelength; + + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + auto *seq = new Vector(); + seq->reserve((size_t) slicelength); + + for (size_t i=0; ipush_back(v[start]); + start += step; + } + return seq; + }, + arg("s"), + "Retrieve list elements using a slice object" + ); + + cl.def("__setitem__", + [](Vector &v, slice slice, const Vector &value) { + size_t start, stop, step, slicelength; + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + if (slicelength != value.size()) + throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); + + for (size_t i=0; i), +// we have to access by copying; otherwise we return by reference. +template using vector_needs_copy = negation< + std::is_same()[typename Vector::size_type()]), typename Vector::value_type &>>; + +// The usual case: access and iterate by reference +template +void vector_accessor(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using DiffType = typename Vector::difference_type; + using ItType = typename Vector::iterator; + + auto wrap_i = [](DiffType i, SizeType n) { + if (i < 0) + i += n; + if (i < 0 || (SizeType)i >= n) + throw index_error(); + return i; + }; + + cl.def("__getitem__", + [wrap_i](Vector &v, DiffType i) -> T & { + i = wrap_i(i, v.size()); + return v[(SizeType)i]; + }, + return_value_policy::reference_internal // ref + keepalive + ); + + cl.def("__iter__", + [](Vector &v) { + return make_iterator< + return_value_policy::reference_internal, ItType, ItType, T&>( + v.begin(), v.end()); + }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); +} + +// The case for special objects, like std::vector, that have to be returned-by-copy: +template +void vector_accessor(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using DiffType = typename Vector::difference_type; + using ItType = typename Vector::iterator; + cl.def("__getitem__", + [](const Vector &v, DiffType i) -> T { + if (i < 0 && (i += v.size()) < 0) + throw index_error(); + if ((SizeType)i >= v.size()) + throw index_error(); + return v[(SizeType)i]; + } + ); + + cl.def("__iter__", + [](Vector &v) { + return make_iterator< + return_value_policy::copy, ItType, ItType, T>( + v.begin(), v.end()); + }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); +} + +template auto vector_if_insertion_operator(Class_ &cl, std::string const &name) + -> decltype(std::declval() << std::declval(), void()) { + using size_type = typename Vector::size_type; + + cl.def("__repr__", + [name](Vector &v) { + std::ostringstream s; + s << name << '['; + for (size_type i=0; i < v.size(); ++i) { + s << v[i]; + if (i != v.size() - 1) + s << ", "; + } + s << ']'; + return s.str(); + }, + "Return the canonical string representation of this list." + ); +} + +// Provide the buffer interface for vectors if we have data() and we have a format for it +// GCC seems to have "void std::vector::data()" - doing SFINAE on the existence of data() is insufficient, we need to check it returns an appropriate pointer +template +struct vector_has_data_and_format : std::false_type {}; +template +struct vector_has_data_and_format::format(), std::declval().data()), typename Vector::value_type*>::value>> : std::true_type {}; + +// Add the buffer interface to a vector +template +enable_if_t...>::value> +vector_buffer(Class_& cl) { + using T = typename Vector::value_type; + + static_assert(vector_has_data_and_format::value, "There is not an appropriate format descriptor for this vector"); + + // numpy.h declares this for arbitrary types, but it may raise an exception and crash hard at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here + format_descriptor::format(); + + cl.def_buffer([](Vector& v) -> buffer_info { + return buffer_info(v.data(), static_cast(sizeof(T)), format_descriptor::format(), 1, {v.size()}, {sizeof(T)}); + }); + + cl.def(init([](buffer buf) { + auto info = buf.request(); + if (info.ndim != 1 || info.strides[0] % static_cast(sizeof(T))) + throw type_error("Only valid 1D buffers can be copied to a vector"); + if (!detail::compare_buffer_info::compare(info) || (ssize_t) sizeof(T) != info.itemsize) + throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor::format() + ")"); + + T *p = static_cast(info.ptr); + ssize_t step = info.strides[0] / static_cast(sizeof(T)); + T *end = p + info.shape[0] * step; + if (step == 1) { + return Vector(p, end); + } + else { + Vector vec; + vec.reserve((size_t) info.shape[0]); + for (; p != end; p += step) + vec.push_back(*p); + return vec; + } + })); + + return; +} + +template +enable_if_t...>::value> vector_buffer(Class_&) {} + +PYBIND11_NAMESPACE_END(detail) + +// +// std::vector +// +template , typename... Args> +class_ bind_vector(handle scope, std::string const &name, Args&&... args) { + using Class_ = class_; + + // If the value_type is unregistered (e.g. a converting type) or is itself registered + // module-local then make the vector binding module-local as well: + using vtype = typename Vector::value_type; + auto vtype_info = detail::get_type_info(typeid(vtype)); + bool local = !vtype_info || vtype_info->module_local; + + Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); + + // Declare the buffer interface if a buffer_protocol() is passed in + detail::vector_buffer(cl); + + cl.def(init<>()); + + // Register copy constructor (if possible) + detail::vector_if_copy_constructible(cl); + + // Register comparison-related operators and functions (if possible) + detail::vector_if_equal_operator(cl); + + // Register stream insertion operator (if possible) + detail::vector_if_insertion_operator(cl, name); + + // Modifiers require copyable vector value type + detail::vector_modifiers(cl); + + // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive + detail::vector_accessor(cl); + + cl.def("__bool__", + [](const Vector &v) -> bool { + return !v.empty(); + }, + "Check whether the list is nonempty" + ); + + cl.def("__len__", &Vector::size); + + + + +#if 0 + // C++ style functions deprecated, leaving it here as an example + cl.def(init()); + + cl.def("resize", + (void (Vector::*) (size_type count)) & Vector::resize, + "changes the number of elements stored"); + + cl.def("erase", + [](Vector &v, SizeType i) { + if (i >= v.size()) + throw index_error(); + v.erase(v.begin() + i); + }, "erases element at index ``i``"); + + cl.def("empty", &Vector::empty, "checks whether the container is empty"); + cl.def("size", &Vector::size, "returns the number of elements"); + cl.def("push_back", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end"); + cl.def("pop_back", &Vector::pop_back, "removes the last element"); + + cl.def("max_size", &Vector::max_size, "returns the maximum possible number of elements"); + cl.def("reserve", &Vector::reserve, "reserves storage"); + cl.def("capacity", &Vector::capacity, "returns the number of elements that can be held in currently allocated storage"); + cl.def("shrink_to_fit", &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory"); + + cl.def("clear", &Vector::clear, "clears the contents"); + cl.def("swap", &Vector::swap, "swaps the contents"); + + cl.def("front", [](Vector &v) { + if (v.size()) return v.front(); + else throw index_error(); + }, "access the first element"); + + cl.def("back", [](Vector &v) { + if (v.size()) return v.back(); + else throw index_error(); + }, "access the last element "); + +#endif + + return cl; +} + + + +// +// std::map, std::unordered_map +// + +PYBIND11_NAMESPACE_BEGIN(detail) + +/* Fallback functions */ +template void map_if_insertion_operator(const Args &...) { } +template void map_assignment(const Args &...) { } + +// Map assignment when copy-assignable: just copy the value +template +void map_assignment(enable_if_t::value, Class_> &cl) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + + cl.def("__setitem__", + [](Map &m, const KeyType &k, const MappedType &v) { + auto it = m.find(k); + if (it != m.end()) it->second = v; + else m.emplace(k, v); + } + ); +} + +// Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting +template +void map_assignment(enable_if_t< + !is_copy_assignable::value && + is_copy_constructible::value, + Class_> &cl) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + + cl.def("__setitem__", + [](Map &m, const KeyType &k, const MappedType &v) { + // We can't use m[k] = v; because value type might not be default constructable + auto r = m.emplace(k, v); + if (!r.second) { + // value type is not copy assignable so the only way to insert it is to erase it first... + m.erase(r.first); + m.emplace(k, v); + } + } + ); +} + + +template auto map_if_insertion_operator(Class_ &cl, std::string const &name) +-> decltype(std::declval() << std::declval() << std::declval(), void()) { + + cl.def("__repr__", + [name](Map &m) { + std::ostringstream s; + s << name << '{'; + bool f = false; + for (auto const &kv : m) { + if (f) + s << ", "; + s << kv.first << ": " << kv.second; + f = true; + } + s << '}'; + return s.str(); + }, + "Return the canonical string representation of this map." + ); +} + + +PYBIND11_NAMESPACE_END(detail) + +template , typename... Args> +class_ bind_map(handle scope, const std::string &name, Args&&... args) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + using Class_ = class_; + + // If either type is a non-module-local bound type then make the map binding non-local as well; + // otherwise (e.g. both types are either module-local or converting) the map will be + // module-local. + auto tinfo = detail::get_type_info(typeid(MappedType)); + bool local = !tinfo || tinfo->module_local; + if (local) { + tinfo = detail::get_type_info(typeid(KeyType)); + local = !tinfo || tinfo->module_local; + } + + Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); + + cl.def(init<>()); + + // Register stream insertion operator (if possible) + detail::map_if_insertion_operator(cl, name); + + cl.def("__bool__", + [](const Map &m) -> bool { return !m.empty(); }, + "Check whether the map is nonempty" + ); + + cl.def("__iter__", + [](Map &m) { return make_key_iterator(m.begin(), m.end()); }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); + + cl.def("items", + [](Map &m) { return make_iterator(m.begin(), m.end()); }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); + + cl.def("__getitem__", + [](Map &m, const KeyType &k) -> MappedType & { + auto it = m.find(k); + if (it == m.end()) + throw key_error(); + return it->second; + }, + return_value_policy::reference_internal // ref + keepalive + ); + + cl.def("__contains__", + [](Map &m, const KeyType &k) -> bool { + auto it = m.find(k); + if (it == m.end()) + return false; + return true; + } + ); + + // Assignment provided only if the type is copyable + detail::map_assignment(cl); + + cl.def("__delitem__", + [](Map &m, const KeyType &k) { + auto it = m.find(k); + if (it == m.end()) + throw key_error(); + m.erase(it); + } + ); + + cl.def("__len__", &Map::size); + + return cl; +} + +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/extern/pybind/pybind11/__init__.py b/extern/pybind/pybind11/__init__.py new file mode 100644 index 00000000..ad654208 --- /dev/null +++ b/extern/pybind/pybind11/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +from ._version import version_info, __version__ +from .commands import get_include, get_cmake_dir + + +__all__ = ( + "version_info", + "__version__", + "get_include", + "get_cmake_dir", +) diff --git a/extern/pybind/pybind11/__main__.py b/extern/pybind/pybind11/__main__.py new file mode 100644 index 00000000..020988c6 --- /dev/null +++ b/extern/pybind/pybind11/__main__.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +from __future__ import print_function + +import argparse +import sys +import sysconfig + +from .commands import get_include, get_cmake_dir + + +def print_includes(): + # type: () -> None + dirs = [ + sysconfig.get_path("include"), + sysconfig.get_path("platinclude"), + get_include(), + ] + + # Make unique but preserve order + unique_dirs = [] + for d in dirs: + if d and d not in unique_dirs: + unique_dirs.append(d) + + print(" ".join("-I" + d for d in unique_dirs)) + + +def main(): + # type: () -> None + + parser = argparse.ArgumentParser() + parser.add_argument( + "--includes", + action="store_true", + help="Include flags for both pybind11 and Python headers.", + ) + parser.add_argument( + "--cmakedir", + action="store_true", + help="Print the CMake module directory, ideal for setting -Dpybind11_ROOT in CMake.", + ) + args = parser.parse_args() + if not sys.argv[1:]: + parser.print_help() + if args.includes: + print_includes() + if args.cmakedir: + print(get_cmake_dir()) + + +if __name__ == "__main__": + main() diff --git a/extern/pybind/pybind11/_version.py b/extern/pybind/pybind11/_version.py new file mode 100644 index 00000000..950a500e --- /dev/null +++ b/extern/pybind/pybind11/_version.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + + +def _to_int(s): + try: + return int(s) + except ValueError: + return s + + +__version__ = "2.6.1.dev1" +version_info = tuple(_to_int(s) for s in __version__.split(".")) diff --git a/extern/pybind/pybind11/_version.pyi b/extern/pybind/pybind11/_version.pyi new file mode 100644 index 00000000..970184c7 --- /dev/null +++ b/extern/pybind/pybind11/_version.pyi @@ -0,0 +1,6 @@ +from typing import Union, Tuple + +def _to_int(s: str) -> Union[int, str]: ... + +__version__: str +version_info: Tuple[Union[int, str], ...] diff --git a/extern/pybind/pybind11/commands.py b/extern/pybind/pybind11/commands.py new file mode 100644 index 00000000..34dbaf8a --- /dev/null +++ b/extern/pybind/pybind11/commands.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +import os + + +DIR = os.path.abspath(os.path.dirname(__file__)) + + +def get_include(user=False): + # type: (bool) -> str + installed_path = os.path.join(DIR, "include") + source_path = os.path.join(os.path.dirname(DIR), "include") + return installed_path if os.path.exists(installed_path) else source_path + + +def get_cmake_dir(): + # type: () -> str + cmake_installed_path = os.path.join(DIR, "share", "cmake", "pybind11") + if os.path.exists(cmake_installed_path): + return cmake_installed_path + else: + msg = "pybind11 not installed, installation required to access the CMake files" + raise ImportError(msg) diff --git a/extern/pybind/pybind11/py.typed b/extern/pybind/pybind11/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/extern/pybind/pybind11/setup_helpers.py b/extern/pybind/pybind11/setup_helpers.py new file mode 100644 index 00000000..2dcbab4e --- /dev/null +++ b/extern/pybind/pybind11/setup_helpers.py @@ -0,0 +1,398 @@ +# -*- coding: utf-8 -*- + +""" +This module provides helpers for C++11+ projects using pybind11. + +LICENSE: + +Copyright (c) 2016 Wenzel Jakob , All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" + +# IMPORTANT: If you change this file in the pybind11 repo, also review +# setup_helpers.pyi for matching changes. +# +# If you copy this file in, you don't +# need the .pyi file; it's just an interface file for static type checkers. + +import contextlib +import os +import shutil +import sys +import tempfile +import threading +import platform +import warnings + +try: + from setuptools.command.build_ext import build_ext as _build_ext + from setuptools import Extension as _Extension +except ImportError: + from distutils.command.build_ext import build_ext as _build_ext + from distutils.extension import Extension as _Extension + +import distutils.errors +import distutils.ccompiler + + +WIN = sys.platform.startswith("win32") +PY2 = sys.version_info[0] < 3 +MACOS = sys.platform.startswith("darwin") +STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}" + + +# It is recommended to use PEP 518 builds if using this module. However, this +# file explicitly supports being copied into a user's project directory +# standalone, and pulling pybind11 with the deprecated setup_requires feature. +# If you copy the file, remember to add it to your MANIFEST.in, and add the current +# directory into your path if it sits beside your setup.py. + + +class Pybind11Extension(_Extension): + """ + Build a C++11+ Extension module with pybind11. This automatically adds the + recommended flags when you init the extension and assumes C++ sources - you + can further modify the options yourself. + + The customizations are: + + * ``/EHsc`` and ``/bigobj`` on Windows + * ``stdlib=libc++`` on macOS + * ``visibility=hidden`` and ``-g0`` on Unix + + Finally, you can set ``cxx_std`` via constructor or afterwords to enable + flags for C++ std, and a few extra helper flags related to the C++ standard + level. It is _highly_ recommended you either set this, or use the provided + ``build_ext``, which will search for the highest supported extension for + you if the ``cxx_std`` property is not set. Do not set the ``cxx_std`` + property more than once, as flags are added when you set it. Set the + property to None to disable the addition of C++ standard flags. + + If you want to add pybind11 headers manually, for example for an exact + git checkout, then set ``include_pybind11=False``. + + Warning: do not use property-based access to the instance on Python 2 - + this is an ugly old-style class due to Distutils. + """ + + def _add_cflags(self, *flags): + for flag in flags: + if flag not in self.extra_compile_args: + self.extra_compile_args.append(flag) + + def _add_lflags(self, *flags): + for flag in flags: + if flag not in self.extra_link_args: + self.extra_link_args.append(flag) + + def __init__(self, *args, **kwargs): + + self._cxx_level = 0 + cxx_std = kwargs.pop("cxx_std", 0) + + if "language" not in kwargs: + kwargs["language"] = "c++" + + include_pybind11 = kwargs.pop("include_pybind11", True) + + # Can't use super here because distutils has old-style classes in + # Python 2! + _Extension.__init__(self, *args, **kwargs) + + # Include the installed package pybind11 headers + if include_pybind11: + # If using setup_requires, this fails the first time - that's okay + try: + import pybind11 + + pyinc = pybind11.get_include() + + if pyinc not in self.include_dirs: + self.include_dirs.append(pyinc) + except ImportError: + pass + + # Have to use the accessor manually to support Python 2 distutils + Pybind11Extension.cxx_std.__set__(self, cxx_std) + + if WIN: + self._add_cflags("/EHsc", "/bigobj") + else: + self._add_cflags("-fvisibility=hidden", "-g0") + if MACOS: + self._add_cflags("-stdlib=libc++") + self._add_lflags("-stdlib=libc++") + + @property + def cxx_std(self): + """ + The CXX standard level. If set, will add the required flags. If left + at 0, it will trigger an automatic search when pybind11's build_ext + is used. If None, will have no effect. Besides just the flags, this + may add a register warning/error fix for Python 2 or macos-min 10.9 + or 10.14. + """ + return self._cxx_level + + @cxx_std.setter + def cxx_std(self, level): + + if self._cxx_level: + warnings.warn("You cannot safely change the cxx_level after setting it!") + + # MSVC 2015 Update 3 and later only have 14 (and later 17) modes, so + # force a valid flag here. + if WIN and level == 11: + level = 14 + + self._cxx_level = level + + if not level: + return + + self.extra_compile_args.append(STD_TMPL.format(level)) + + if MACOS and "MACOSX_DEPLOYMENT_TARGET" not in os.environ: + # C++17 requires a higher min version of macOS. An earlier version + # (10.12 or 10.13) can be set manually via environment variable if + # you are careful in your feature usage, but 10.14 is the safest + # setting for general use. However, never set higher than the + # current macOS version! + current_macos = tuple(int(x) for x in platform.mac_ver()[0].split(".")[:2]) + desired_macos = (10, 9) if level < 17 else (10, 14) + macos_string = ".".join(str(x) for x in min(current_macos, desired_macos)) + macosx_min = "-mmacosx-version-min=" + macos_string + self.extra_compile_args.append(macosx_min) + self.extra_link_args.append(macosx_min) + + if PY2: + if WIN: + # Will be ignored on MSVC 2015, where C++17 is not supported so + # this flag is not valid. + self.extra_compile_args.append("/wd5033") + elif level >= 17: + self.extra_compile_args.append("-Wno-register") + elif level >= 14: + self.extra_compile_args.append("-Wno-deprecated-register") + + +# Just in case someone clever tries to multithread +tmp_chdir_lock = threading.Lock() +cpp_cache_lock = threading.Lock() + + +@contextlib.contextmanager +def tmp_chdir(): + "Prepare and enter a temporary directory, cleanup when done" + + # Threadsafe + with tmp_chdir_lock: + olddir = os.getcwd() + try: + tmpdir = tempfile.mkdtemp() + os.chdir(tmpdir) + yield tmpdir + finally: + os.chdir(olddir) + shutil.rmtree(tmpdir) + + +# cf http://bugs.python.org/issue26689 +def has_flag(compiler, flag): + """ + Return the flag if a flag name is supported on the + specified compiler, otherwise None (can be used as a boolean). + If multiple flags are passed, return the first that matches. + """ + + with tmp_chdir(): + fname = "flagcheck.cpp" + with open(fname, "w") as f: + f.write("int main (int argc, char **argv) { return 0; }") + + try: + compiler.compile([fname], extra_postargs=[flag]) + except distutils.errors.CompileError: + return False + return True + + +# Every call will cache the result +cpp_flag_cache = None + + +def auto_cpp_level(compiler): + """ + Return the max supported C++ std level (17, 14, or 11). Returns latest on Windows. + """ + + if WIN: + return "latest" + + global cpp_flag_cache + + # If this has been previously calculated with the same args, return that + with cpp_cache_lock: + if cpp_flag_cache: + return cpp_flag_cache + + levels = [17, 14, 11] + + for level in levels: + if has_flag(compiler, STD_TMPL.format(level)): + with cpp_cache_lock: + cpp_flag_cache = level + return level + + msg = "Unsupported compiler -- at least C++11 support is needed!" + raise RuntimeError(msg) + + +class build_ext(_build_ext): # noqa: N801 + """ + Customized build_ext that allows an auto-search for the highest supported + C++ level for Pybind11Extension. + """ + + def build_extensions(self): + """ + Build extensions, injecting C++ std for Pybind11Extension if needed. + """ + + for ext in self.extensions: + if hasattr(ext, "_cxx_level") and ext._cxx_level == 0: + # Python 2 syntax - old-style distutils class + ext.__class__.cxx_std.__set__(ext, auto_cpp_level(self.compiler)) + + # Python 2 doesn't allow super here, since distutils uses old-style + # classes! + _build_ext.build_extensions(self) + + +# Optional parallel compile utility +# inspired by: http://stackoverflow.com/questions/11013851/speeding-up-build-process-with-distutils +# and: https://github.com/tbenthompson/cppimport/blob/stable/cppimport/build_module.py +# and NumPy's parallel distutils module: +# https://github.com/numpy/numpy/blob/master/numpy/distutils/ccompiler.py +class ParallelCompile(object): + """ + Make a parallel compile function. Inspired by + numpy.distutils.ccompiler.CCompiler_compile and cppimport. + + This takes several arguments that allow you to customize the compile + function created: + + envvar: Set an environment variable to control the compilation threads, like NPY_NUM_BUILD_JOBS + default: 0 will automatically multithread, or 1 will only multithread if the envvar is set. + max: The limit for automatic multithreading if non-zero + + To use: + ParallelCompile("NPY_NUM_BUILD_JOBS").install() + or: + with ParallelCompile("NPY_NUM_BUILD_JOBS"): + setup(...) + """ + + __slots__ = ("envvar", "default", "max", "old") + + def __init__(self, envvar=None, default=0, max=0): + self.envvar = envvar + self.default = default + self.max = max + self.old = [] + + def function(self): + """ + Builds a function object usable as distutils.ccompiler.CCompiler.compile. + """ + + def compile_function( + compiler, + sources, + output_dir=None, + macros=None, + include_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + depends=None, + ): + + # These lines are directly from distutils.ccompiler.CCompiler + macros, objects, extra_postargs, pp_opts, build = compiler._setup_compile( + output_dir, macros, include_dirs, sources, depends, extra_postargs + ) + cc_args = compiler._get_cc_args(pp_opts, debug, extra_preargs) + + # The number of threads; start with default. + threads = self.default + + # Determine the number of compilation threads, unless set by an environment variable. + if self.envvar is not None: + threads = int(os.environ.get(self.envvar, self.default)) + + def _single_compile(obj): + try: + src, ext = build[obj] + except KeyError: + return + compiler._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) + + try: + import multiprocessing + from multiprocessing.pool import ThreadPool + except ImportError: + threads = 1 + + if threads == 0: + try: + threads = multiprocessing.cpu_count() + threads = self.max if self.max and self.max < threads else threads + except NotImplementedError: + threads = 1 + + if threads > 1: + for _ in ThreadPool(threads).imap_unordered(_single_compile, objects): + pass + else: + for ob in objects: + _single_compile(ob) + + return objects + + return compile_function + + def install(self): + distutils.ccompiler.CCompiler.compile = self.function() + return self + + def __enter__(self): + self.old.append(distutils.ccompiler.CCompiler.compile) + return self.install() + + def __exit__(self, *args): + distutils.ccompiler.CCompiler.compile = self.old.pop() diff --git a/extern/pybind/pybind11/setup_helpers.pyi b/extern/pybind/pybind11/setup_helpers.pyi new file mode 100644 index 00000000..19feb8b3 --- /dev/null +++ b/extern/pybind/pybind11/setup_helpers.pyi @@ -0,0 +1,50 @@ +# IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI / +# pre-commit). + +from typing import Any, Iterator, Optional, Type, TypeVar, Union +from types import TracebackType + +from distutils.command.build_ext import build_ext as _build_ext # type: ignore +from distutils.extension import Extension as _Extension +import distutils.ccompiler +import contextlib + +WIN: bool +PY2: bool +MACOS: bool +STD_TMPL: str + +class Pybind11Extension(_Extension): + def _add_cflags(self, *flags: str) -> None: ... + def _add_lflags(self, *flags: str) -> None: ... + def __init__( + self, *args: Any, cxx_std: int = 0, language: str = "c++", **kwargs: Any + ) -> None: ... + @property + def cxx_std(self) -> int: ... + @cxx_std.setter + def cxx_std(self, level: int) -> None: ... + +@contextlib.contextmanager +def tmp_chdir() -> Iterator[str]: ... +def has_flag(compiler: distutils.ccompiler.CCompiler, flag: str) -> bool: ... +def auto_cpp_level(compiler: distutils.ccompiler.CCompiler) -> Union[int, str]: ... + +class build_ext(_build_ext): # type: ignore + def build_extensions(self) -> None: ... + +T = TypeVar("T", bound="ParallelCompile") + +class ParallelCompile: + def __init__( + self, envvar: Optional[str] = None, default: int = 0, max: int = 0 + ): ... + def function(self) -> Any: ... + def install(self: T) -> T: ... + def __enter__(self: T) -> T: ... + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + ) -> None: ... diff --git a/extern/pybind/pyproject.toml b/extern/pybind/pyproject.toml new file mode 100644 index 00000000..5c9d1535 --- /dev/null +++ b/extern/pybind/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=42", "wheel", "cmake>=3.18", "ninja"] +build-backend = "setuptools.build_meta" diff --git a/extern/pybind/setup.cfg b/extern/pybind/setup.cfg new file mode 100644 index 00000000..e7fc8f4d --- /dev/null +++ b/extern/pybind/setup.cfg @@ -0,0 +1,71 @@ +[metadata] +long_description = file: README.rst +long_description_content_type = text/x-rst +description = Seamless operability between C++11 and Python +author = Wenzel Jakob +author_email = wenzel.jakob@epfl.ch +url = https://github.com/pybind/pybind11 +license = BSD + +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Developers + Topic :: Software Development :: Libraries :: Python Modules + Topic :: Utilities + Programming Language :: C++ + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + License :: OSI Approved :: BSD License + Programming Language :: Python :: Implementation :: PyPy + Programming Language :: Python :: Implementation :: CPython + Programming Language :: C++ + Topic :: Software Development :: Libraries :: Python Modules + +keywords = + C++11 + Python bindings + +[options] +python_requires = >=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4 +zip_safe = False + +[bdist_wheel] +universal=1 + +[check-manifest] +ignore = + tests/** + docs/** + tools/** + include/** + .appveyor.yml + .cmake-format.yaml + .gitmodules + .pre-commit-config.yaml + .readthedocs.yml + .clang-tidy + pybind11/include/** + pybind11/share/** + CMakeLists.txt + + +[flake8] +max-line-length = 99 +show_source = True +exclude = .git, __pycache__, build, dist, docs, tools, venv +ignore = + # required for pretty matrix formatting: multiple spaces after `,` and `[` + E201, E241, W504, + # camelcase 'cPickle' imported as lowercase 'pickle' + N813 + # Black conflict + W503, E203 + +[mypy] +files = pybind11 +strict = True diff --git a/extern/pybind/setup.py b/extern/pybind/setup.py new file mode 100644 index 00000000..3a032798 --- /dev/null +++ b/extern/pybind/setup.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Setup script for PyPI; use CMakeFile.txt to build extension modules + +import contextlib +import os +import re +import shutil +import string +import subprocess +import sys +import tempfile + +import setuptools.command.sdist + +DIR = os.path.abspath(os.path.dirname(__file__)) +VERSION_REGEX = re.compile( + r"^\s*#\s*define\s+PYBIND11_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE +) + +# PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers +# files, and the sys.prefix files (CMake and headers). + +global_sdist = os.environ.get("PYBIND11_GLOBAL_SDIST", False) + +setup_py = "tools/setup_global.py.in" if global_sdist else "tools/setup_main.py.in" +extra_cmd = 'cmdclass["sdist"] = SDist\n' + +to_src = ( + ("pyproject.toml", "tools/pyproject.toml"), + ("setup.py", setup_py), +) + +# Read the listed version +with open("pybind11/_version.py") as f: + code = compile(f.read(), "pybind11/_version.py", "exec") +loc = {} +exec(code, loc) +version = loc["__version__"] + +# Verify that the version matches the one in C++ +with open("include/pybind11/detail/common.h") as f: + matches = dict(VERSION_REGEX.findall(f.read())) +cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches) +if version != cpp_version: + msg = "Python version {} does not match C++ version {}!".format( + version, cpp_version + ) + raise RuntimeError(msg) + + +def get_and_replace(filename, binary=False, **opts): + with open(filename, "rb" if binary else "r") as f: + contents = f.read() + # Replacement has to be done on text in Python 3 (both work in Python 2) + if binary: + return string.Template(contents.decode()).substitute(opts).encode() + else: + return string.Template(contents).substitute(opts) + + +# Use our input files instead when making the SDist (and anything that depends +# on it, like a wheel) +class SDist(setuptools.command.sdist.sdist): + def make_release_tree(self, base_dir, files): + setuptools.command.sdist.sdist.make_release_tree(self, base_dir, files) + + for to, src in to_src: + txt = get_and_replace(src, binary=True, version=version, extra_cmd="") + + dest = os.path.join(base_dir, to) + + # This is normally linked, so unlink before writing! + os.unlink(dest) + with open(dest, "wb") as f: + f.write(txt) + + +# Backport from Python 3 +@contextlib.contextmanager +def TemporaryDirectory(): # noqa: N802 + "Prepare a temporary directory, cleanup when done" + try: + tmpdir = tempfile.mkdtemp() + yield tmpdir + finally: + shutil.rmtree(tmpdir) + + +# Remove the CMake install directory when done +@contextlib.contextmanager +def remove_output(*sources): + try: + yield + finally: + for src in sources: + shutil.rmtree(src) + + +with remove_output("pybind11/include", "pybind11/share"): + # Generate the files if they are not present. + with TemporaryDirectory() as tmpdir: + cmd = ["cmake", "-S", ".", "-B", tmpdir] + [ + "-DCMAKE_INSTALL_PREFIX=pybind11", + "-DBUILD_TESTING=OFF", + "-DPYBIND11_NOPYTHON=ON", + ] + cmake_opts = dict(cwd=DIR, stdout=sys.stdout, stderr=sys.stderr) + subprocess.check_call(cmd, **cmake_opts) + subprocess.check_call(["cmake", "--install", tmpdir], **cmake_opts) + + txt = get_and_replace(setup_py, version=version, extra_cmd=extra_cmd) + code = compile(txt, setup_py, "exec") + exec(code, {"SDist": SDist}) diff --git a/extern/pybind/tools/FindCatch.cmake b/extern/pybind/tools/FindCatch.cmake new file mode 100644 index 00000000..4d6bffcf --- /dev/null +++ b/extern/pybind/tools/FindCatch.cmake @@ -0,0 +1,70 @@ +# - Find the Catch test framework or download it (single header) +# +# This is a quick module for internal use. It assumes that Catch is +# REQUIRED and that a minimum version is provided (not EXACT). If +# a suitable version isn't found locally, the single header file +# will be downloaded and placed in the build dir: PROJECT_BINARY_DIR. +# +# This code sets the following variables: +# CATCH_INCLUDE_DIR - path to catch.hpp +# CATCH_VERSION - version number + +if(NOT Catch_FIND_VERSION) + message(FATAL_ERROR "A version number must be specified.") +elseif(Catch_FIND_REQUIRED) + message(FATAL_ERROR "This module assumes Catch is not required.") +elseif(Catch_FIND_VERSION_EXACT) + message(FATAL_ERROR "Exact version numbers are not supported, only minimum.") +endif() + +# Extract the version number from catch.hpp +function(_get_catch_version) + file( + STRINGS "${CATCH_INCLUDE_DIR}/catch.hpp" version_line + REGEX "Catch v.*" + LIMIT_COUNT 1) + if(version_line MATCHES "Catch v([0-9]+)\\.([0-9]+)\\.([0-9]+)") + set(CATCH_VERSION + "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}" + PARENT_SCOPE) + endif() +endfunction() + +# Download the single-header version of Catch +function(_download_catch version destination_dir) + message(STATUS "Downloading catch v${version}...") + set(url https://github.com/philsquared/Catch/releases/download/v${version}/catch.hpp) + file(DOWNLOAD ${url} "${destination_dir}/catch.hpp" STATUS status) + list(GET status 0 error) + if(error) + message(FATAL_ERROR "Could not download ${url}") + endif() + set(CATCH_INCLUDE_DIR + "${destination_dir}" + CACHE INTERNAL "") +endfunction() + +# Look for catch locally +find_path( + CATCH_INCLUDE_DIR + NAMES catch.hpp + PATH_SUFFIXES catch2) +if(CATCH_INCLUDE_DIR) + _get_catch_version() +endif() + +# Download the header if it wasn't found or if it's outdated +if(NOT CATCH_VERSION OR CATCH_VERSION VERSION_LESS ${Catch_FIND_VERSION}) + if(DOWNLOAD_CATCH) + _download_catch(${Catch_FIND_VERSION} "${PROJECT_BINARY_DIR}/catch/") + _get_catch_version() + else() + set(CATCH_FOUND FALSE) + return() + endif() +endif() + +add_library(Catch2::Catch2 IMPORTED INTERFACE) +set_property(TARGET Catch2::Catch2 PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CATCH_INCLUDE_DIR}") + +set(CATCH_FOUND TRUE) diff --git a/extern/pybind/tools/FindEigen3.cmake b/extern/pybind/tools/FindEigen3.cmake new file mode 100644 index 00000000..98ab43d9 --- /dev/null +++ b/extern/pybind/tools/FindEigen3.cmake @@ -0,0 +1,83 @@ +# - Try to find Eigen3 lib +# +# This module supports requiring a minimum version, e.g. you can do +# find_package(Eigen3 3.1.2) +# to require version 3.1.2 or newer of Eigen3. +# +# Once done this will define +# +# EIGEN3_FOUND - system has eigen lib with correct version +# EIGEN3_INCLUDE_DIR - the eigen include directory +# EIGEN3_VERSION - eigen version + +# Copyright (c) 2006, 2007 Montel Laurent, +# Copyright (c) 2008, 2009 Gael Guennebaud, +# Copyright (c) 2009 Benoit Jacob +# Redistribution and use is allowed according to the terms of the 2-clause BSD license. + +if(NOT Eigen3_FIND_VERSION) + if(NOT Eigen3_FIND_VERSION_MAJOR) + set(Eigen3_FIND_VERSION_MAJOR 2) + endif(NOT Eigen3_FIND_VERSION_MAJOR) + if(NOT Eigen3_FIND_VERSION_MINOR) + set(Eigen3_FIND_VERSION_MINOR 91) + endif(NOT Eigen3_FIND_VERSION_MINOR) + if(NOT Eigen3_FIND_VERSION_PATCH) + set(Eigen3_FIND_VERSION_PATCH 0) + endif(NOT Eigen3_FIND_VERSION_PATCH) + + set(Eigen3_FIND_VERSION + "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}") +endif(NOT Eigen3_FIND_VERSION) + +macro(_eigen3_check_version) + file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header) + + string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match + "${_eigen3_version_header}") + set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match + "${_eigen3_version_header}") + set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match + "${_eigen3_version_header}") + set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}") + + set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION}) + if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) + set(EIGEN3_VERSION_OK FALSE) + else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) + set(EIGEN3_VERSION_OK TRUE) + endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) + + if(NOT EIGEN3_VERSION_OK) + + message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, " + "but at least version ${Eigen3_FIND_VERSION} is required") + endif(NOT EIGEN3_VERSION_OK) +endmacro(_eigen3_check_version) + +if(EIGEN3_INCLUDE_DIR) + + # in cache already + _eigen3_check_version() + set(EIGEN3_FOUND ${EIGEN3_VERSION_OK}) + +else(EIGEN3_INCLUDE_DIR) + + find_path( + EIGEN3_INCLUDE_DIR + NAMES signature_of_eigen3_matrix_library + PATHS ${CMAKE_INSTALL_PREFIX}/include ${KDE4_INCLUDE_DIR} + PATH_SUFFIXES eigen3 eigen) + + if(EIGEN3_INCLUDE_DIR) + _eigen3_check_version() + endif(EIGEN3_INCLUDE_DIR) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK) + + mark_as_advanced(EIGEN3_INCLUDE_DIR) + +endif(EIGEN3_INCLUDE_DIR) diff --git a/extern/pybind/tools/FindPythonLibsNew.cmake b/extern/pybind/tools/FindPythonLibsNew.cmake new file mode 100644 index 00000000..b7125912 --- /dev/null +++ b/extern/pybind/tools/FindPythonLibsNew.cmake @@ -0,0 +1,255 @@ +# - Find python libraries +# This module finds the libraries corresponding to the Python interpreter +# FindPythonInterp provides. +# This code sets the following variables: +# +# PYTHONLIBS_FOUND - have the Python libs been found +# PYTHON_PREFIX - path to the Python installation +# PYTHON_LIBRARIES - path to the python library +# PYTHON_INCLUDE_DIRS - path to where Python.h is found +# PYTHON_MODULE_EXTENSION - lib extension, e.g. '.so' or '.pyd' +# PYTHON_MODULE_PREFIX - lib name prefix: usually an empty string +# PYTHON_SITE_PACKAGES - path to installation site-packages +# PYTHON_IS_DEBUG - whether the Python interpreter is a debug build +# +# Thanks to talljimbo for the patch adding the 'LDVERSION' config +# variable usage. + +#============================================================================= +# Copyright 2001-2009 Kitware, Inc. +# Copyright 2012 Continuum Analytics, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +# Checking for the extension makes sure that `LibsNew` was found and not just `Libs`. +if(PYTHONLIBS_FOUND AND PYTHON_MODULE_EXTENSION) + return() +endif() + +if(PythonLibsNew_FIND_QUIETLY) + set(_pythonlibs_quiet QUIET) +endif() + +if(PythonLibsNew_FIND_REQUIRED) + set(_pythonlibs_required REQUIRED) +endif() + +# Check to see if the `python` command is present and from a virtual +# environment, conda, or GHA activation - if it is, try to use that. + +if(NOT DEFINED PYTHON_EXECUTABLE) + if(DEFINED ENV{VIRTUAL_ENV}) + find_program( + PYTHON_EXECUTABLE python + PATHS "$ENV{VIRTUAL_ENV}" "$ENV{VIRTUAL_ENV}/bin" + NO_DEFAULT_PATH) + elseif(DEFINED ENV{CONDA_PREFIX}) + find_program( + PYTHON_EXECUTABLE python + PATHS "$ENV{CONDA_PREFIX}" "$ENV{CONDA_PREFIX}/bin" + NO_DEFAULT_PATH) + elseif(DEFINED ENV{pythonLocation}) + find_program( + PYTHON_EXECUTABLE python + PATHS "$ENV{pythonLocation}" "$ENV{pythonLocation}/bin" + NO_DEFAULT_PATH) + endif() + if(NOT PYTHON_EXECUTABLE) + unset(PYTHON_EXECUTABLE) + endif() +endif() + +# Use the Python interpreter to find the libs. +if(NOT PythonLibsNew_FIND_VERSION) + set(PythonLibsNew_FIND_VERSION "") +endif() + +find_package(PythonInterp ${PythonLibsNew_FIND_VERSION} ${_pythonlibs_required} + ${_pythonlibs_quiet}) + +if(NOT PYTHONINTERP_FOUND) + set(PYTHONLIBS_FOUND FALSE) + set(PythonLibsNew_FOUND FALSE) + return() +endif() + +# According to https://stackoverflow.com/questions/646518/python-how-to-detect-debug-interpreter +# testing whether sys has the gettotalrefcount function is a reliable, cross-platform +# way to detect a CPython debug interpreter. +# +# The library suffix is from the config var LDVERSION sometimes, otherwise +# VERSION. VERSION will typically be like "2.7" on unix, and "27" on windows. +execute_process( + COMMAND + "${PYTHON_EXECUTABLE}" "-c" "from distutils import sysconfig as s;import sys;import struct; +print('.'.join(str(v) for v in sys.version_info)); +print(sys.prefix); +print(s.get_python_inc(plat_specific=True)); +print(s.get_python_lib(plat_specific=True)); +print(s.get_config_var('EXT_SUFFIX') or s.get_config_var('SO')); +print(hasattr(sys, 'gettotalrefcount')+0); +print(struct.calcsize('@P')); +print(s.get_config_var('LDVERSION') or s.get_config_var('VERSION')); +print(s.get_config_var('LIBDIR') or ''); +print(s.get_config_var('MULTIARCH') or ''); +" + RESULT_VARIABLE _PYTHON_SUCCESS + OUTPUT_VARIABLE _PYTHON_VALUES + ERROR_VARIABLE _PYTHON_ERROR_VALUE) + +if(NOT _PYTHON_SUCCESS MATCHES 0) + if(PythonLibsNew_FIND_REQUIRED) + message(FATAL_ERROR "Python config failure:\n${_PYTHON_ERROR_VALUE}") + endif() + set(PYTHONLIBS_FOUND FALSE) + set(PythonLibsNew_FOUND FALSE) + return() +endif() + +# Convert the process output into a list +if(WIN32) + string(REGEX REPLACE "\\\\" "/" _PYTHON_VALUES ${_PYTHON_VALUES}) +endif() +string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES}) +string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES}) +list(GET _PYTHON_VALUES 0 _PYTHON_VERSION_LIST) +list(GET _PYTHON_VALUES 1 PYTHON_PREFIX) +list(GET _PYTHON_VALUES 2 PYTHON_INCLUDE_DIR) +list(GET _PYTHON_VALUES 3 PYTHON_SITE_PACKAGES) +list(GET _PYTHON_VALUES 4 PYTHON_MODULE_EXTENSION) +list(GET _PYTHON_VALUES 5 PYTHON_IS_DEBUG) +list(GET _PYTHON_VALUES 6 PYTHON_SIZEOF_VOID_P) +list(GET _PYTHON_VALUES 7 PYTHON_LIBRARY_SUFFIX) +list(GET _PYTHON_VALUES 8 PYTHON_LIBDIR) +list(GET _PYTHON_VALUES 9 PYTHON_MULTIARCH) + +# Make sure the Python has the same pointer-size as the chosen compiler +# Skip if CMAKE_SIZEOF_VOID_P is not defined +if(CMAKE_SIZEOF_VOID_P AND (NOT "${PYTHON_SIZEOF_VOID_P}" STREQUAL "${CMAKE_SIZEOF_VOID_P}")) + if(PythonLibsNew_FIND_REQUIRED) + math(EXPR _PYTHON_BITS "${PYTHON_SIZEOF_VOID_P} * 8") + math(EXPR _CMAKE_BITS "${CMAKE_SIZEOF_VOID_P} * 8") + message(FATAL_ERROR "Python config failure: Python is ${_PYTHON_BITS}-bit, " + "chosen compiler is ${_CMAKE_BITS}-bit") + endif() + set(PYTHONLIBS_FOUND FALSE) + set(PythonLibsNew_FOUND FALSE) + return() +endif() + +# The built-in FindPython didn't always give the version numbers +string(REGEX REPLACE "\\." ";" _PYTHON_VERSION_LIST ${_PYTHON_VERSION_LIST}) +list(GET _PYTHON_VERSION_LIST 0 PYTHON_VERSION_MAJOR) +list(GET _PYTHON_VERSION_LIST 1 PYTHON_VERSION_MINOR) +list(GET _PYTHON_VERSION_LIST 2 PYTHON_VERSION_PATCH) +set(PYTHON_VERSION "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH}") + +# Make sure all directory separators are '/' +string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX "${PYTHON_PREFIX}") +string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}") +string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES "${PYTHON_SITE_PACKAGES}") + +if(CMAKE_HOST_WIN32) + set(PYTHON_LIBRARY "${PYTHON_PREFIX}/libs/python${PYTHON_LIBRARY_SUFFIX}.lib") + + # when run in a venv, PYTHON_PREFIX points to it. But the libraries remain in the + # original python installation. They may be found relative to PYTHON_INCLUDE_DIR. + if(NOT EXISTS "${PYTHON_LIBRARY}") + get_filename_component(_PYTHON_ROOT ${PYTHON_INCLUDE_DIR} DIRECTORY) + set(PYTHON_LIBRARY "${_PYTHON_ROOT}/libs/python${PYTHON_LIBRARY_SUFFIX}.lib") + endif() + + # if we are in MSYS & MINGW, and we didn't find windows python lib, look for system python lib + if(DEFINED ENV{MSYSTEM} + AND MINGW + AND NOT EXISTS "${PYTHON_LIBRARY}") + if(PYTHON_MULTIARCH) + set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}/${PYTHON_MULTIARCH}" "${PYTHON_LIBDIR}") + else() + set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}") + endif() + unset(PYTHON_LIBRARY) + find_library( + PYTHON_LIBRARY + NAMES "python${PYTHON_LIBRARY_SUFFIX}" + PATHS ${_PYTHON_LIBS_SEARCH} + NO_DEFAULT_PATH) + endif() + + # raise an error if the python libs are still not found. + if(NOT EXISTS "${PYTHON_LIBRARY}") + message(FATAL_ERROR "Python libraries not found") + endif() + +else() + if(PYTHON_MULTIARCH) + set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}/${PYTHON_MULTIARCH}" "${PYTHON_LIBDIR}") + else() + set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}") + endif() + #message(STATUS "Searching for Python libs in ${_PYTHON_LIBS_SEARCH}") + # Probably this needs to be more involved. It would be nice if the config + # information the python interpreter itself gave us were more complete. + find_library( + PYTHON_LIBRARY + NAMES "python${PYTHON_LIBRARY_SUFFIX}" + PATHS ${_PYTHON_LIBS_SEARCH} + NO_DEFAULT_PATH) + + # If all else fails, just set the name/version and let the linker figure out the path. + if(NOT PYTHON_LIBRARY) + set(PYTHON_LIBRARY python${PYTHON_LIBRARY_SUFFIX}) + endif() +endif() + +mark_as_advanced(PYTHON_LIBRARY PYTHON_INCLUDE_DIR) + +# We use PYTHON_INCLUDE_DIR, PYTHON_LIBRARY and PYTHON_DEBUG_LIBRARY for the +# cache entries because they are meant to specify the location of a single +# library. We now set the variables listed by the documentation for this +# module. +set(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}") +set(PYTHON_LIBRARIES "${PYTHON_LIBRARY}") +if(NOT PYTHON_DEBUG_LIBRARY) + set(PYTHON_DEBUG_LIBRARY "") +endif() +set(PYTHON_DEBUG_LIBRARIES "${PYTHON_DEBUG_LIBRARY}") + +find_package_message(PYTHON "Found PythonLibs: ${PYTHON_LIBRARY}" + "${PYTHON_EXECUTABLE}${PYTHON_VERSION_STRING}") + +set(PYTHONLIBS_FOUND TRUE) +set(PythonLibsNew_FOUND TRUE) + +if(NOT PYTHON_MODULE_PREFIX) + set(PYTHON_MODULE_PREFIX "") +endif() diff --git a/extern/pybind/tools/check-style.sh b/extern/pybind/tools/check-style.sh new file mode 100644 index 00000000..f7af2a41 --- /dev/null +++ b/extern/pybind/tools/check-style.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# +# Script to check include/test code for common pybind11 code style errors. +# +# This script currently checks for +# +# 1. missing space between keyword and parenthesis, e.g.: for(, if(, while( +# 2. Missing space between right parenthesis and brace, e.g. 'for (...){' +# 3. opening brace on its own line. It should always be on the same line as the +# if/while/for/do statement. +# +# Invoke as: tools/check-style.sh +# + +check_style_errors=0 +IFS=$'\n' + + +found="$(grep '\<\(if\|for\|while\|catch\)(\|){' $@ -rn --color=always)" +if [ -n "$found" ]; then + echo -e '\033[31;01mError: found the following coding style problems:\033[0m' + check_style_errors=1 + echo "$found" | sed -e 's/^/ /' +fi + +found="$(awk ' +function prefix(filename, lineno) { + return " \033[35m" filename "\033[36m:\033[32m" lineno "\033[36m:\033[0m" +} +function mark(pattern, string) { sub(pattern, "\033[01;31m&\033[0m", string); return string } +last && /^\s*{/ { + print prefix(FILENAME, FNR-1) mark("\\)\\s*$", last) + print prefix(FILENAME, FNR) mark("^\\s*{", $0) + last="" +} +{ last = /(if|for|while|catch|switch)\s*\(.*\)\s*$/ ? $0 : "" } +' $(find include -type f) $@)" +if [ -n "$found" ]; then + check_style_errors=1 + echo -e '\033[31;01mError: braces should occur on the same line as the if/while/.. statement. Found issues in the following files:\033[0m' + echo "$found" +fi + +exit $check_style_errors diff --git a/extern/pybind/tools/cmake_uninstall.cmake.in b/extern/pybind/tools/cmake_uninstall.cmake.in new file mode 100644 index 00000000..1e5d2bb8 --- /dev/null +++ b/extern/pybind/tools/cmake_uninstall.cmake.in @@ -0,0 +1,23 @@ +# Source: https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#can-i-do-make-uninstall-with-cmake + +if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") +endif() + +file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + exec_program( + "@CMAKE_COMMAND@" ARGS + "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + endif() + else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif() +endforeach() diff --git a/extern/pybind/tools/libsize.py b/extern/pybind/tools/libsize.py new file mode 100644 index 00000000..589c317f --- /dev/null +++ b/extern/pybind/tools/libsize.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +from __future__ import print_function, division +import os +import sys + +# Internal build script for generating debugging test .so size. +# Usage: +# python libsize.py file.so save.txt -- displays the size of file.so and, if save.txt exists, compares it to the +# size in it, then overwrites save.txt with the new size for future runs. + +if len(sys.argv) != 3: + sys.exit("Invalid arguments: usage: python libsize.py file.so save.txt") + +lib = sys.argv[1] +save = sys.argv[2] + +if not os.path.exists(lib): + sys.exit("Error: requested file ({}) does not exist".format(lib)) + +libsize = os.path.getsize(lib) + +print("------", os.path.basename(lib), "file size:", libsize, end="") + +if os.path.exists(save): + with open(save) as sf: + oldsize = int(sf.readline()) + + if oldsize > 0: + change = libsize - oldsize + if change == 0: + print(" (no change)") + else: + print(" (change of {:+} bytes = {:+.2%})".format(change, change / oldsize)) +else: + print() + +with open(save, "w") as sf: + sf.write(str(libsize)) diff --git a/extern/pybind/tools/mkdoc.py b/extern/pybind/tools/mkdoc.py new file mode 100644 index 00000000..44164af3 --- /dev/null +++ b/extern/pybind/tools/mkdoc.py @@ -0,0 +1,379 @@ +#!/usr/bin/env python3 +# +# Syntax: mkdoc.py [-I ..] [.. a list of header files ..] +# +# Extract documentation from C++ header files to use it in Python bindings +# + +import os +import sys +import platform +import re +import textwrap + +from clang import cindex +from clang.cindex import CursorKind +from collections import OrderedDict +from glob import glob +from threading import Thread, Semaphore +from multiprocessing import cpu_count + +RECURSE_LIST = [ + CursorKind.TRANSLATION_UNIT, + CursorKind.NAMESPACE, + CursorKind.CLASS_DECL, + CursorKind.STRUCT_DECL, + CursorKind.ENUM_DECL, + CursorKind.CLASS_TEMPLATE +] + +PRINT_LIST = [ + CursorKind.CLASS_DECL, + CursorKind.STRUCT_DECL, + CursorKind.ENUM_DECL, + CursorKind.ENUM_CONSTANT_DECL, + CursorKind.CLASS_TEMPLATE, + CursorKind.FUNCTION_DECL, + CursorKind.FUNCTION_TEMPLATE, + CursorKind.CONVERSION_FUNCTION, + CursorKind.CXX_METHOD, + CursorKind.CONSTRUCTOR, + CursorKind.FIELD_DECL +] + +PREFIX_BLACKLIST = [ + CursorKind.TRANSLATION_UNIT +] + +CPP_OPERATORS = { + '<=': 'le', '>=': 'ge', '==': 'eq', '!=': 'ne', '[]': 'array', + '+=': 'iadd', '-=': 'isub', '*=': 'imul', '/=': 'idiv', '%=': + 'imod', '&=': 'iand', '|=': 'ior', '^=': 'ixor', '<<=': 'ilshift', + '>>=': 'irshift', '++': 'inc', '--': 'dec', '<<': 'lshift', '>>': + 'rshift', '&&': 'land', '||': 'lor', '!': 'lnot', '~': 'bnot', + '&': 'band', '|': 'bor', '+': 'add', '-': 'sub', '*': 'mul', '/': + 'div', '%': 'mod', '<': 'lt', '>': 'gt', '=': 'assign', '()': 'call' +} + +CPP_OPERATORS = OrderedDict( + sorted(CPP_OPERATORS.items(), key=lambda t: -len(t[0]))) + +job_count = cpu_count() +job_semaphore = Semaphore(job_count) + + +class NoFilenamesError(ValueError): + pass + + +def d(s): + return s if isinstance(s, str) else s.decode('utf8') + + +def sanitize_name(name): + name = re.sub(r'type-parameter-0-([0-9]+)', r'T\1', name) + for k, v in CPP_OPERATORS.items(): + name = name.replace('operator%s' % k, 'operator_%s' % v) + name = re.sub('<.*>', '', name) + name = ''.join([ch if ch.isalnum() else '_' for ch in name]) + name = re.sub('_$', '', re.sub('_+', '_', name)) + return '__doc_' + name + + +def process_comment(comment): + result = '' + + # Remove C++ comment syntax + leading_spaces = float('inf') + for s in comment.expandtabs(tabsize=4).splitlines(): + s = s.strip() + if s.startswith('/*'): + s = s[2:].lstrip('*') + elif s.endswith('*/'): + s = s[:-2].rstrip('*') + elif s.startswith('///'): + s = s[3:] + if s.startswith('*'): + s = s[1:] + if len(s) > 0: + leading_spaces = min(leading_spaces, len(s) - len(s.lstrip())) + result += s + '\n' + + if leading_spaces != float('inf'): + result2 = "" + for s in result.splitlines(): + result2 += s[leading_spaces:] + '\n' + result = result2 + + # Doxygen tags + cpp_group = '([\w:]+)' + param_group = '([\[\w:\]]+)' + + s = result + s = re.sub(r'\\c\s+%s' % cpp_group, r'``\1``', s) + s = re.sub(r'\\a\s+%s' % cpp_group, r'*\1*', s) + s = re.sub(r'\\e\s+%s' % cpp_group, r'*\1*', s) + s = re.sub(r'\\em\s+%s' % cpp_group, r'*\1*', s) + s = re.sub(r'\\b\s+%s' % cpp_group, r'**\1**', s) + s = re.sub(r'\\ingroup\s+%s' % cpp_group, r'', s) + s = re.sub(r'\\param%s?\s+%s' % (param_group, cpp_group), + r'\n\n$Parameter ``\2``:\n\n', s) + s = re.sub(r'\\tparam%s?\s+%s' % (param_group, cpp_group), + r'\n\n$Template parameter ``\2``:\n\n', s) + + for in_, out_ in { + 'return': 'Returns', + 'author': 'Author', + 'authors': 'Authors', + 'copyright': 'Copyright', + 'date': 'Date', + 'remark': 'Remark', + 'sa': 'See also', + 'see': 'See also', + 'extends': 'Extends', + 'throw': 'Throws', + 'throws': 'Throws' + }.items(): + s = re.sub(r'\\%s\s*' % in_, r'\n\n$%s:\n\n' % out_, s) + + s = re.sub(r'\\details\s*', r'\n\n', s) + s = re.sub(r'\\brief\s*', r'', s) + s = re.sub(r'\\short\s*', r'', s) + s = re.sub(r'\\ref\s*', r'', s) + + s = re.sub(r'\\code\s?(.*?)\s?\\endcode', + r"```\n\1\n```\n", s, flags=re.DOTALL) + + # HTML/TeX tags + s = re.sub(r'(.*?)', r'``\1``', s, flags=re.DOTALL) + s = re.sub(r'
(.*?)
', r"```\n\1\n```\n", s, flags=re.DOTALL) + s = re.sub(r'(.*?)', r'*\1*', s, flags=re.DOTALL) + s = re.sub(r'(.*?)', r'**\1**', s, flags=re.DOTALL) + s = re.sub(r'\\f\$(.*?)\\f\$', r'$\1$', s, flags=re.DOTALL) + s = re.sub(r'
  • ', r'\n\n* ', s) + s = re.sub(r'', r'', s) + s = re.sub(r'
  • ', r'\n\n', s) + + s = s.replace('``true``', '``True``') + s = s.replace('``false``', '``False``') + + # Re-flow text + wrapper = textwrap.TextWrapper() + wrapper.expand_tabs = True + wrapper.replace_whitespace = True + wrapper.drop_whitespace = True + wrapper.width = 70 + wrapper.initial_indent = wrapper.subsequent_indent = '' + + result = '' + in_code_segment = False + for x in re.split(r'(```)', s): + if x == '```': + if not in_code_segment: + result += '```\n' + else: + result += '\n```\n\n' + in_code_segment = not in_code_segment + elif in_code_segment: + result += x.strip() + else: + for y in re.split(r'(?: *\n *){2,}', x): + wrapped = wrapper.fill(re.sub(r'\s+', ' ', y).strip()) + if len(wrapped) > 0 and wrapped[0] == '$': + result += wrapped[1:] + '\n' + wrapper.initial_indent = \ + wrapper.subsequent_indent = ' ' * 4 + else: + if len(wrapped) > 0: + result += wrapped + '\n\n' + wrapper.initial_indent = wrapper.subsequent_indent = '' + return result.rstrip().lstrip('\n') + + +def extract(filename, node, prefix, output): + if not (node.location.file is None or + os.path.samefile(d(node.location.file.name), filename)): + return 0 + if node.kind in RECURSE_LIST: + sub_prefix = prefix + if node.kind not in PREFIX_BLACKLIST: + if len(sub_prefix) > 0: + sub_prefix += '_' + sub_prefix += d(node.spelling) + for i in node.get_children(): + extract(filename, i, sub_prefix, output) + if node.kind in PRINT_LIST: + comment = d(node.raw_comment) if node.raw_comment is not None else '' + comment = process_comment(comment) + sub_prefix = prefix + if len(sub_prefix) > 0: + sub_prefix += '_' + if len(node.spelling) > 0: + name = sanitize_name(sub_prefix + d(node.spelling)) + output.append((name, filename, comment)) + + +class ExtractionThread(Thread): + def __init__(self, filename, parameters, output): + Thread.__init__(self) + self.filename = filename + self.parameters = parameters + self.output = output + job_semaphore.acquire() + + def run(self): + print('Processing "%s" ..' % self.filename, file=sys.stderr) + try: + index = cindex.Index( + cindex.conf.lib.clang_createIndex(False, True)) + tu = index.parse(self.filename, self.parameters) + extract(self.filename, tu.cursor, '', self.output) + finally: + job_semaphore.release() + + +def read_args(args): + parameters = [] + filenames = [] + if "-x" not in args: + parameters.extend(['-x', 'c++']) + if not any(it.startswith("-std=") for it in args): + parameters.append('-std=c++11') + + if platform.system() == 'Darwin': + dev_path = '/Applications/Xcode.app/Contents/Developer/' + lib_dir = dev_path + 'Toolchains/XcodeDefault.xctoolchain/usr/lib/' + sdk_dir = dev_path + 'Platforms/MacOSX.platform/Developer/SDKs' + libclang = lib_dir + 'libclang.dylib' + + if os.path.exists(libclang): + cindex.Config.set_library_path(os.path.dirname(libclang)) + + if os.path.exists(sdk_dir): + sysroot_dir = os.path.join(sdk_dir, next(os.walk(sdk_dir))[1][0]) + parameters.append('-isysroot') + parameters.append(sysroot_dir) + elif platform.system() == 'Linux': + # clang doesn't find its own base includes by default on Linux, + # but different distros install them in different paths. + # Try to autodetect, preferring the highest numbered version. + def clang_folder_version(d): + return [int(ver) for ver in re.findall(r'(?,$,3.9>>>") +set(no_register "$>") + +if(MSVC AND CMAKE_VERSION VERSION_LESS 3.11) + set(cxx_no_register "${no_register}") +else() + set(cxx_no_register "$,${no_register}>") +endif() + +set(msvc "$") + +set_property( + TARGET pybind11::python2_no_register + PROPERTY INTERFACE_COMPILE_OPTIONS + "$<${cxx_no_register}:-Wno-register;-Wno-deprecated-register>" "$<${msvc}:/wd5033>") + +# --------------------------- link helper --------------------------- + +add_library(pybind11::python_link_helper IMPORTED INTERFACE ${optional_global}) + +if(CMAKE_VERSION VERSION_LESS 3.13) + # In CMake 3.11+, you can set INTERFACE properties via the normal methods, and + # this would be simpler. + set_property( + TARGET pybind11::python_link_helper + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES "$<$:-undefined dynamic_lookup>") +else() + # link_options was added in 3.13+ + # This is safer, because you are ensured the deduplication pass in CMake will not consider + # these separate and remove one but not the other. + set_property( + TARGET pybind11::python_link_helper + APPEND + PROPERTY INTERFACE_LINK_OPTIONS "$<$:LINKER:-undefined,dynamic_lookup>") +endif() + +# ------------------------ Windows extras ------------------------- + +add_library(pybind11::windows_extras IMPORTED INTERFACE ${optional_global}) + +if(MSVC) + # /MP enables multithreaded builds (relevant when there are many files), /bigobj is + # needed for bigger binding projects due to the limit to 64k addressable sections + set_property( + TARGET pybind11::windows_extras + APPEND + PROPERTY INTERFACE_COMPILE_OPTIONS /bigobj) + + if(CMAKE_VERSION VERSION_LESS 3.11) + set_property( + TARGET pybind11::windows_extras + APPEND + PROPERTY INTERFACE_COMPILE_OPTIONS $<$>:/MP>) + else() + # Only set these options for C++ files. This is important so that, for + # instance, projects that include other types of source files like CUDA + # .cu files don't get these options propagated to nvcc since that would + # cause the build to fail. + set_property( + TARGET pybind11::windows_extras + APPEND + PROPERTY INTERFACE_COMPILE_OPTIONS $<$>:$<$:/MP>>) + endif() +endif() + +# ----------------------- Optimize binary size -------------------------- + +add_library(pybind11::opt_size IMPORTED INTERFACE ${optional_global}) + +if(MSVC) + set(PYBIND11_OPT_SIZE /Os) +else() + set(PYBIND11_OPT_SIZE -Os) +endif() + +set_property( + TARGET pybind11::opt_size + APPEND + PROPERTY INTERFACE_COMPILE_OPTIONS $<$:${PYBIND11_OPT_SIZE}> + $<$:${PYBIND11_OPT_SIZE}> + $<$:${PYBIND11_OPT_SIZE}>) + +# ----------------------- Legacy option -------------------------- + +# Warn or error if old variable name used +if(PYBIND11_CPP_STANDARD) + string(REGEX MATCH [[..$]] VAL "${PYBIND11_CPP_STANDARD}") + if(CMAKE_CXX_STANDARD) + if(NOT CMAKE_CXX_STANDARD STREQUAL VAL) + message(WARNING "CMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} does not match " + "PYBIND11_CPP_STANDARD=${PYBIND11_CPP_STANDARD}, " + "please remove PYBIND11_CPP_STANDARD from your cache") + endif() + else() + set(supported_standards 11 14 17 20) + if("${VAL}" IN_LIST supported_standards) + message(WARNING "USE -DCMAKE_CXX_STANDARD=${VAL} instead of PYBIND11_CPP_STANDARD") + set(CMAKE_CXX_STANDARD + ${VAL} + CACHE STRING "From PYBIND11_CPP_STANDARD") + else() + message(FATAL_ERROR "PYBIND11_CPP_STANDARD should be replaced with CMAKE_CXX_STANDARD " + "(last two chars: ${VAL} not understood as a valid CXX std)") + endif() + endif() +endif() + +# --------------------- Python specifics ------------------------- + +# Check to see which Python mode we are in, new, old, or no python +if(PYBIND11_NOPYTHON) + set(_pybind11_nopython ON) +elseif( + PYBIND11_FINDPYTHON + OR Python_FOUND + OR Python2_FOUND + OR Python3_FOUND) + # New mode + include("${CMAKE_CURRENT_LIST_DIR}/pybind11NewTools.cmake") + +else() + + # Classic mode + include("${CMAKE_CURRENT_LIST_DIR}/pybind11Tools.cmake") + +endif() + +# --------------------- pybind11_find_import ------------------------------- + +if(NOT _pybind11_nopython) + # Check to see if modules are importable. Use REQUIRED to force an error if + # one of the modules is not found. _FOUND will be set if the + # package was found (underscores replace dashes if present). QUIET will hide + # the found message, and VERSION will require a minimum version. A successful + # find will cache the result. + function(pybind11_find_import PYPI_NAME) + # CMake variables need underscores (PyPI doesn't care) + string(REPLACE "-" "_" NORM_PYPI_NAME "${PYPI_NAME}") + + # Return if found previously + if(${NORM_PYPI_NAME}_FOUND) + return() + endif() + + set(options "REQUIRED;QUIET") + set(oneValueArgs "VERSION") + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "" ${ARGN}) + + if(ARG_REQUIRED) + set(status_level FATAL_ERROR) + else() + set(status_level WARNING) + endif() + + execute_process( + COMMAND + ${${_Python}_EXECUTABLE} -c + "from pkg_resources import get_distribution; print(get_distribution('${PYPI_NAME}').version)" + RESULT_VARIABLE RESULT_PRESENT + OUTPUT_VARIABLE PKG_VERSION + ERROR_QUIET) + + string(STRIP "${PKG_VERSION}" PKG_VERSION) + + # If a result is present, this failed + if(RESULT_PRESENT) + set(${NORM_PYPI_NAME}_FOUND + ${NORM_PYPI_NAME}-NOTFOUND + CACHE INTERNAL "") + # Always warn or error + message( + ${status_level} + "Missing: ${PYPI_NAME} ${ARG_VERSION}\nTry: ${${_Python}_EXECUTABLE} -m pip install ${PYPI_NAME}" + ) + else() + if(ARG_VERSION AND PKG_VERSION VERSION_LESS ARG_VERSION) + message( + ${status_level} + "Version incorrect: ${PYPI_NAME} ${PKG_VERSION} found, ${ARG_VERSION} required - try upgrading" + ) + else() + set(${NORM_PYPI_NAME}_FOUND + YES + CACHE INTERNAL "") + set(${NORM_PYPI_NAME}_VERSION + ${PKG_VERSION} + CACHE INTERNAL "") + endif() + if(NOT ARG_QUIET) + message(STATUS "Found ${PYPI_NAME} ${PKG_VERSION}") + endif() + endif() + if(NOT ARG_VERSION OR (NOT PKG_VERSION VERSION_LESS ARG_VERSION)) + # We have successfully found a good version, cache to avoid calling again. + endif() + endfunction() +endif() + +# --------------------- LTO ------------------------------- + +include(CheckCXXCompilerFlag) + +# Checks whether the given CXX/linker flags can compile and link a cxx file. +# cxxflags and linkerflags are lists of flags to use. The result variable is a +# unique variable name for each set of flags: the compilation result will be +# cached base on the result variable. If the flags work, sets them in +# cxxflags_out/linkerflags_out internal cache variables (in addition to +# ${result}). +function(_pybind11_return_if_cxx_and_linker_flags_work result cxxflags linkerflags cxxflags_out + linkerflags_out) + set(CMAKE_REQUIRED_LIBRARIES ${linkerflags}) + check_cxx_compiler_flag("${cxxflags}" ${result}) + if(${result}) + set(${cxxflags_out} + "${cxxflags}" + PARENT_SCOPE) + set(${linkerflags_out} + "${linkerflags}" + PARENT_SCOPE) + endif() +endfunction() + +function(_pybind11_generate_lto target prefer_thin_lto) + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + set(cxx_append "") + set(linker_append "") + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT APPLE) + # Clang Gold plugin does not support -Os; append -O3 to MinSizeRel builds to override it + set(linker_append ";$<$:-O3>") + elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set(cxx_append ";-fno-fat-lto-objects") + endif() + + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND prefer_thin_lto) + _pybind11_return_if_cxx_and_linker_flags_work( + HAS_FLTO_THIN "-flto=thin${cxx_append}" "-flto=thin${linker_append}" + PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + + if(NOT HAS_FLTO_THIN) + _pybind11_return_if_cxx_and_linker_flags_work( + HAS_FLTO "-flto${cxx_append}" "-flto${linker_append}" PYBIND11_LTO_CXX_FLAGS + PYBIND11_LTO_LINKER_FLAGS) + endif() + elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") + # Intel equivalent to LTO is called IPO + _pybind11_return_if_cxx_and_linker_flags_work(HAS_INTEL_IPO "-ipo" "-ipo" + PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + elseif(MSVC) + # cmake only interprets libraries as linker flags when they start with a - (otherwise it + # converts /LTCG to \LTCG as if it was a Windows path). Luckily MSVC supports passing flags + # with - instead of /, even if it is a bit non-standard: + _pybind11_return_if_cxx_and_linker_flags_work(HAS_MSVC_GL_LTCG "/GL" "-LTCG" + PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + + # Enable LTO flags if found, except for Debug builds + if(PYBIND11_LTO_CXX_FLAGS) + # CONFIG takes multiple values in CMake 3.19+, until then we have to use OR + set(is_debug "$,$>") + set(not_debug "$") + set(cxx_lang "$") + if(MSVC AND CMAKE_VERSION VERSION_LESS 3.11) + set(genex "${not_debug}") + else() + set(genex "$") + endif() + set_property( + TARGET ${target} + APPEND + PROPERTY INTERFACE_COMPILE_OPTIONS "$<${genex}:${PYBIND11_LTO_CXX_FLAGS}>") + if(CMAKE_PROJECT_NAME STREQUAL "pybind11") + message(STATUS "${target} enabled") + endif() + else() + if(CMAKE_PROJECT_NAME STREQUAL "pybind11") + message(STATUS "${target} disabled (not supported by the compiler and/or linker)") + endif() + endif() + + if(PYBIND11_LTO_LINKER_FLAGS) + if(CMAKE_VERSION VERSION_LESS 3.11) + set_property( + TARGET ${target} + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES "$<${not_debug}:${PYBIND11_LTO_LINKER_FLAGS}>") + else() + set_property( + TARGET ${target} + APPEND + PROPERTY INTERFACE_LINK_OPTIONS "$<${not_debug}:${PYBIND11_LTO_LINKER_FLAGS}>") + endif() + endif() +endfunction() + +add_library(pybind11::lto IMPORTED INTERFACE ${optional_global}) +_pybind11_generate_lto(pybind11::lto FALSE) + +add_library(pybind11::thin_lto IMPORTED INTERFACE ${optional_global}) +_pybind11_generate_lto(pybind11::thin_lto TRUE) + +# ---------------------- pybind11_strip ----------------------------- + +function(pybind11_strip target_name) + # Strip unnecessary sections of the binary on Linux/macOS + if(CMAKE_STRIP) + if(APPLE) + set(x_opt -x) + endif() + + add_custom_command( + TARGET ${target_name} + POST_BUILD + COMMAND ${CMAKE_STRIP} ${x_opt} $) + endif() +endfunction() diff --git a/extern/pybind/tools/pybind11Config.cmake.in b/extern/pybind/tools/pybind11Config.cmake.in new file mode 100644 index 00000000..9808f3d2 --- /dev/null +++ b/extern/pybind/tools/pybind11Config.cmake.in @@ -0,0 +1,232 @@ +#[=============================================================================[.rst: + +pybind11Config.cmake +#################### + +Exported variables +================== + +This module sets the following variables in your project: + +``pybind11_FOUND`` + true if pybind11 and all required components found on the system +``pybind11_VERSION`` + pybind11 version in format Major.Minor.Release +``pybind11_VERSION_TYPE`` + pybind11 version type (dev, release) +``pybind11_INCLUDE_DIRS`` + Directories where pybind11 and python headers are located. +``pybind11_INCLUDE_DIR`` + Directory where pybind11 headers are located. +``pybind11_DEFINITIONS`` + Definitions necessary to use pybind11, namely USING_pybind11. +``pybind11_LIBRARIES`` + Compile flags and python libraries (as needed) to link against. +``pybind11_LIBRARY`` + Empty. + +Available components: None + + +Exported targets +================ + +If pybind11 is found, this module defines the following ``IMPORTED`` +interface library targets: + +``pybind11::module`` + for extension modules. +``pybind11::embed`` + for embedding the Python interpreter. + +Python headers, libraries (as needed by platform), and the C++ standard +are attached to the target. + +Advanced targets are also supplied - these are primary for users building +complex applications, and they are available in all modes: + +``pybind11::headers`` + Just the pybind11 headers and minimum compile requirements. +``pybind11::pybind11`` + Python headers too. +``pybind11::python_link_helper`` + Just the "linking" part of ``pybind11:module``, for CMake < 3.15. +``pybind11::python2_no_register`` + Quiets the warning/error when mixing C++14+ and Python 2, also included in ``pybind11::module``. +``pybind11::thin_lto`` + An alternative to ``INTERPROCEDURAL_OPTIMIZATION``. +``pybind11::lto`` + An alternative to ``INTERPROCEDURAL_OPTIMIZATION`` (also avoids thin LTO on clang). +``pybind11::windows_extras`` + Adds bigobj and mp for MSVC. + +Modes +===== + +There are two modes provided; classic, which is built on the old Python +discovery packages in CMake, or the new FindPython mode, which uses FindPython +from 3.12+ forward (3.15+ _highly_ recommended). + +New FindPython mode +^^^^^^^^^^^^^^^^^^^ + +To activate this mode, either call ``find_package(Python COMPONENTS Interpreter Development)`` +before finding this package, or set the ``PYBIND11_FINDPYTHON`` variable to ON. In this mode, +you can either use the basic targets, or use the FindPython tools: + +.. code-block:: cmake + + find_package(Python COMPONENTS Interpreter Development) + find_package(pybind11 CONFIG) + + # pybind11 method: + pybind11_add_module(MyModule1 src1.cpp) + + # Python method: + Python_add_library(MyModule2 src2.cpp) + target_link_libraries(MyModule2 pybind11::headers) + set_target_properties(MyModule2 PROPERTIES + INTERPROCEDURAL_OPTIMIZATION ON + CXX__VISIBILITY_PRESET ON + VISIBLITY_INLINES_HIDDEN ON) + +If you build targets yourself, you may be interested in stripping the output +for reduced size; this is the one other feature that the helper function gives you. + +Classic mode +^^^^^^^^^^^^ + +Set PythonLibsNew variables to influence python detection and +CMAKE_CXX_STANDARD to influence standard setting. + +.. code-block:: cmake + + find_package(pybind11 CONFIG REQUIRED) + + # Create an extension module + add_library(mylib MODULE main.cpp) + target_link_libraries(mylib PUBLIC pybind11::module) + + # Or embed the Python interpreter into an executable + add_executable(myexe main.cpp) + target_link_libraries(myexe PUBLIC pybind11::embed) + + +Hints +===== + +The following variables can be set to guide the search for this package: + +``pybind11_DIR`` + CMake variable, set to directory containing this Config file. +``CMAKE_PREFIX_PATH`` + CMake variable, set to root directory of this package. +``PATH`` + Environment variable, set to bin directory of this package. +``CMAKE_DISABLE_FIND_PACKAGE_pybind11`` + CMake variable, disables ``find_package(pybind11)`` when not ``REQUIRED``, + perhaps to force internal build. + +Commands +======== + +pybind11_add_module +^^^^^^^^^^^^^^^^^^^ + +This module defines the following commands to assist with creating Python modules: + +.. code-block:: cmake + + pybind11_add_module( + [STATIC|SHARED|MODULE] + [THIN_LTO] [OPT_SIZE] [NO_EXTRAS] [WITHOUT_SOBAI] + ... + ) + +Add a module and setup all helpers. You can select the type of the library; the +default is ``MODULE``. There are several options: + +``OPT_SIZE`` + Optimize for size, even if the ``CMAKE_BUILD_TYPE`` is not ``RelSize``. +``THIN_LTO`` + Use thin TLO instead of regular if there's a choice (pybind11's selection + is disabled if ``CMAKE_INTERPROCEDURAL_OPTIMIZATIONS`` is set). +``WITHOUT_SOABI`` + Disable the SOABI component (``PYBIND11_NEWPYTHON`` mode only). +``NO_EXTRAS`` + Disable all extras, exit immediately after making the module. + +pybind11_strip +^^^^^^^^^^^^^^ + +.. code-block:: cmake + + pybind11_strip() + +Strip a target after building it (linux/macOS), called by ``pybind11_add_module``. + +pybind11_extension +^^^^^^^^^^^^^^^^^^ + +.. code-block:: cmake + + pybind11_extension() + +Sets the Python extension name correctly for Python on your platform, called by +``pybind11_add_module``. + +pybind11_find_import(module) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: cmake + + pybind11_find_import( [VERSION ] [REQUIRED] [QUIET]) + +See if a module is installed. Use the registered name (the one on PyPI). You +can specify a ``VERSION``, and you can specify ``REQUIRED`` or ``QUIET``. Only available if +``NOPYTHON`` mode is not active. Sets ``module_VERSION`` and ``module_FOUND``. Caches the +result once a valid install is found. + +Suggested usage +=============== + +Using ``find_package`` with version info is not recommended except for release versions. + +.. code-block:: cmake + + find_package(pybind11 CONFIG) + find_package(pybind11 2.0 EXACT CONFIG REQUIRED) + +#]=============================================================================] +@PACKAGE_INIT@ + +# Location of pybind11/pybind11.h +set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@") + +set(pybind11_LIBRARY "") +set(pybind11_DEFINITIONS USING_pybind11) +set(pybind11_VERSION_TYPE "@pybind11_VERSION_TYPE@") + +check_required_components(pybind11) + +if(TARGET pybind11::python_link_helper) + # This has already been setup elsewhere, such as with a previous call or + # add_subdirectory + return() +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/pybind11Targets.cmake") + +# Easier to use / remember +add_library(pybind11::headers IMPORTED INTERFACE) +set_target_properties(pybind11::headers PROPERTIES INTERFACE_LINK_LIBRARIES + pybind11::pybind11_headers) + +include("${CMAKE_CURRENT_LIST_DIR}/pybind11Common.cmake") + +if(NOT pybind11_FIND_QUIETLY) + message( + STATUS + "Found pybind11: ${pybind11_INCLUDE_DIR} (found version \"${pybind11_VERSION}\" ${pybind11_VERSION_TYPE})" + ) +endif() diff --git a/extern/pybind/tools/pybind11NewTools.cmake b/extern/pybind/tools/pybind11NewTools.cmake new file mode 100644 index 00000000..357cc61c --- /dev/null +++ b/extern/pybind/tools/pybind11NewTools.cmake @@ -0,0 +1,246 @@ +# tools/pybind11NewTools.cmake -- Build system for the pybind11 modules +# +# Copyright (c) 2020 Wenzel Jakob and Henry Schreiner +# +# All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +get_property( + is_config + TARGET pybind11::headers + PROPERTY IMPORTED) + +if(pybind11_FIND_QUIETLY) + set(_pybind11_quiet QUIET) +endif() + +if(CMAKE_VERSION VERSION_LESS 3.12) + message(FATAL_ERROR "You cannot use the new FindPython module with CMake < 3.12") +endif() + +if(NOT Python_FOUND + AND NOT Python3_FOUND + AND NOT Python2_FOUND) + if(NOT DEFINED Python_FIND_IMPLEMENTATIONS) + set(Python_FIND_IMPLEMENTATIONS CPython PyPy) + endif() + + # GitHub Actions like activation + if(NOT DEFINED Python_ROOT_DIR AND DEFINED ENV{pythonLocation}) + set(Python_ROOT_DIR "$ENV{pythonLocation}") + endif() + + find_package(Python REQUIRED COMPONENTS Interpreter Development ${_pybind11_quiet}) + + # If we are in submodule mode, export the Python targets to global targets. + # If this behavior is not desired, FindPython _before_ pybind11. + if(NOT is_config) + set_property(TARGET Python::Python PROPERTY IMPORTED_GLOBAL TRUE) + set_property(TARGET Python::Interpreter PROPERTY IMPORTED_GLOBAL TRUE) + if(TARGET Python::Module) + set_property(TARGET Python::Module PROPERTY IMPORTED_GLOBAL TRUE) + endif() + endif() +endif() + +if(Python_FOUND) + set(_Python + Python + CACHE INTERNAL "" FORCE) +elseif(Python3_FOUND AND NOT Python2_FOUND) + set(_Python + Python3 + CACHE INTERNAL "" FORCE) +elseif(Python2_FOUND AND NOT Python3_FOUND) + set(_Python + Python2 + CACHE INTERNAL "" FORCE) +else() + message(AUTHOR_WARNING "Python2 and Python3 both present, pybind11 in " + "PYBIND11_NOPYTHON mode (manually activate to silence warning)") + set(_pybind11_nopython ON) + return() +endif() + +if(PYBIND11_MASTER_PROJECT) + if(${_Python}_INTERPRETER_ID MATCHES "PyPy") + message(STATUS "PyPy ${${_Python}_PyPy_VERSION} (Py ${${_Python}_VERSION})") + else() + message(STATUS "${_Python} ${${_Python}_VERSION}") + endif() +endif() + +# If a user finds Python, they may forget to include the Interpreter component +# and the following two steps require it. It is highly recommended by CMake +# when finding development libraries anyway, so we will require it. +if(NOT DEFINED ${_Python}_EXECUTABLE) + message( + FATAL_ERROR + "${_Python} was found without the Interpreter component. Pybind11 requires this component.") + +endif() + +if(NOT DEFINED PYTHON_IS_DEBUG) + # Debug check - see https://stackoverflow.com/questions/646518/python-how-to-detect-debug-Interpreter + execute_process( + COMMAND "${${_Python}_EXECUTABLE}" "-c" + "import sys; sys.exit(hasattr(sys, 'gettotalrefcount'))" + RESULT_VARIABLE _PYTHON_IS_DEBUG) + set(PYTHON_IS_DEBUG + "${_PYTHON_IS_DEBUG}" + CACHE INTERNAL "Python debug status") +endif() + +# Get the suffix - SO is deprecated, should use EXT_SUFFIX, but this is +# required for PyPy3 (as of 7.3.1) +if(NOT DEFINED PYTHON_MODULE_EXTENSION) + execute_process( + COMMAND + "${${_Python}_EXECUTABLE}" "-c" + "from distutils import sysconfig as s;print(s.get_config_var('EXT_SUFFIX') or s.get_config_var('SO'))" + OUTPUT_VARIABLE _PYTHON_MODULE_EXTENSION + ERROR_VARIABLE _PYTHON_MODULE_EXTENSION_ERR + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(_PYTHON_MODULE_EXTENSION STREQUAL "") + message( + FATAL_ERROR "pybind11 could not query the module file extension, likely the 'distutils'" + "package is not installed. Full error message:\n${_PYTHON_MODULE_EXTENSION_ERR}") + endif() + + # This needs to be available for the pybind11_extension function + set(PYTHON_MODULE_EXTENSION + "${_PYTHON_MODULE_EXTENSION}" + CACHE INTERNAL "") +endif() + +# Python debug libraries expose slightly different objects before 3.8 +# https://docs.python.org/3.6/c-api/intro.html#debugging-builds +# https://stackoverflow.com/questions/39161202/how-to-work-around-missing-pymodule-create2-in-amd64-win-python35-d-lib +if(PYTHON_IS_DEBUG) + set_property( + TARGET pybind11::pybind11 + APPEND + PROPERTY INTERFACE_COMPILE_DEFINITIONS Py_DEBUG) +endif() + +# Check on every access - since Python2 and Python3 could have been used - do nothing in that case. + +if(DEFINED ${_Python}_INCLUDE_DIRS) + set_property( + TARGET pybind11::pybind11 + APPEND + PROPERTY INTERFACE_INCLUDE_DIRECTORIES $) + set(pybind11_INCLUDE_DIRS + "${pybind11_INCLUDE_DIR}" "${${_Python}_INCLUDE_DIRS}" + CACHE INTERNAL "Directories where pybind11 and possibly Python headers are located") +endif() + +if(DEFINED ${_Python}_VERSION AND ${_Python}_VERSION VERSION_LESS 3) + set_property( + TARGET pybind11::pybind11 + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES pybind11::python2_no_register) +endif() + +# In CMake 3.18+, you can find these separately, so include an if +if(TARGET ${_Python}::${_Python}) + set_property( + TARGET pybind11::embed + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES ${_Python}::${_Python}) +endif() + +# CMake 3.15+ has this +if(TARGET ${_Python}::Module) + set_property( + TARGET pybind11::module + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES ${_Python}::Module) +else() + set_property( + TARGET pybind11::module + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES pybind11::python_link_helper) +endif() + +# WITHOUT_SOABI and WITH_SOABI will disable the custom extension handling used by pybind11. +# WITH_SOABI is passed on to python_add_library. +function(pybind11_add_module target_name) + cmake_parse_arguments(PARSE_ARGV 1 ARG + "STATIC;SHARED;MODULE;THIN_LTO;OPT_SIZE;NO_EXTRAS;WITHOUT_SOABI" "" "") + + if(ARG_ADD_LIBRARY_STATIC) + set(type STATIC) + elseif(ARG_ADD_LIBRARY_SHARED) + set(type SHARED) + else() + set(type MODULE) + endif() + + if("${_Python}" STREQUAL "Python") + python_add_library(${target_name} ${type} ${ARG_UNPARSED_ARGUMENTS}) + elseif("${_Python}" STREQUAL "Python3") + python3_add_library(${target_name} ${type} ${ARG_UNPARSED_ARGUMENTS}) + elseif("${_Python}" STREQUAL "Python2") + python2_add_library(${target_name} ${type} ${ARG_UNPARSED_ARGUMENTS}) + else() + message(FATAL_ERROR "Cannot detect FindPython version: ${_Python}") + endif() + + target_link_libraries(${target_name} PRIVATE pybind11::headers) + + if(type STREQUAL "MODULE") + target_link_libraries(${target_name} PRIVATE pybind11::module) + else() + target_link_libraries(${target_name} PRIVATE pybind11::embed) + endif() + + if(MSVC) + target_link_libraries(${target_name} PRIVATE pybind11::windows_extras) + endif() + + if(DEFINED ${_Python}_VERSION AND ${_Python}_VERSION VERSION_LESS 3) + target_link_libraries(${target_name} PRIVATE pybind11::python2_no_register) + endif() + + set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden" + CUDA_VISIBILITY_PRESET "hidden") + + # If we don't pass a WITH_SOABI or WITHOUT_SOABI, use our own default handling of extensions + if("${type}" STREQUAL "MODULE" AND (NOT ARG_WITHOUT_SOABI OR NOT "WITH_SOABI" IN_LIST + ARG_UNPARSED_ARGUMENTS)) + pybind11_extension(${target_name}) + endif() + + if(ARG_NO_EXTRAS) + return() + endif() + + if(NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION) + if(ARG_THIN_LTO) + target_link_libraries(${target_name} PRIVATE pybind11::thin_lto) + else() + target_link_libraries(${target_name} PRIVATE pybind11::lto) + endif() + endif() + + if(NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo) + # Strip unnecessary sections of the binary on Linux/macOS + pybind11_strip(${target_name}) + endif() + + if(MSVC) + target_link_libraries(${target_name} PRIVATE pybind11::windows_extras) + endif() + + if(ARG_OPT_SIZE) + target_link_libraries(${target_name} PRIVATE pybind11::opt_size) + endif() +endfunction() + +function(pybind11_extension name) + # The extension is precomputed + set_target_properties(${name} PROPERTIES PREFIX "" SUFFIX "${PYTHON_MODULE_EXTENSION}") + +endfunction() diff --git a/extern/pybind/tools/pybind11Tools.cmake b/extern/pybind/tools/pybind11Tools.cmake new file mode 100644 index 00000000..23cff98e --- /dev/null +++ b/extern/pybind/tools/pybind11Tools.cmake @@ -0,0 +1,200 @@ +# tools/pybind11Tools.cmake -- Build system for the pybind11 modules +# +# Copyright (c) 2020 Wenzel Jakob +# +# All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +# Built-in in CMake 3.5+ +include(CMakeParseArguments) + +if(pybind11_FIND_QUIETLY) + set(_pybind11_quiet QUIET) +endif() + +# If this is the first run, PYTHON_VERSION can stand in for PYBIND11_PYTHON_VERSION +if(NOT DEFINED PYBIND11_PYTHON_VERSION AND DEFINED PYTHON_VERSION) + message(WARNING "Set PYBIND11_PYTHON_VERSION to search for a specific version, not " + "PYTHON_VERSION (which is an output). Assuming that is what you " + "meant to do and continuing anyway.") + set(PYBIND11_PYTHON_VERSION + "${PYTHON_VERSION}" + CACHE STRING "Python version to use for compiling modules") + unset(PYTHON_VERSION) + unset(PYTHON_VERSION CACHE) +else() + # If this is set as a normal variable, promote it, otherwise, make an empty cache variable. + set(PYBIND11_PYTHON_VERSION + "${PYBIND11_PYTHON_VERSION}" + CACHE STRING "Python version to use for compiling modules") +endif() + +# A user can set versions manually too +set(Python_ADDITIONAL_VERSIONS + "3.9;3.8;3.7;3.6;3.5;3.4" + CACHE INTERNAL "") + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") +find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} MODULE REQUIRED ${_pybind11_quiet}) +list(REMOVE_AT CMAKE_MODULE_PATH -1) + +# Cache variables so pybind11_add_module can be used in parent projects +set(PYTHON_INCLUDE_DIRS + ${PYTHON_INCLUDE_DIRS} + CACHE INTERNAL "") +set(PYTHON_LIBRARIES + ${PYTHON_LIBRARIES} + CACHE INTERNAL "") +set(PYTHON_MODULE_PREFIX + ${PYTHON_MODULE_PREFIX} + CACHE INTERNAL "") +set(PYTHON_MODULE_EXTENSION + ${PYTHON_MODULE_EXTENSION} + CACHE INTERNAL "") +set(PYTHON_VERSION_MAJOR + ${PYTHON_VERSION_MAJOR} + CACHE INTERNAL "") +set(PYTHON_VERSION_MINOR + ${PYTHON_VERSION_MINOR} + CACHE INTERNAL "") +set(PYTHON_VERSION + ${PYTHON_VERSION} + CACHE INTERNAL "") +set(PYTHON_IS_DEBUG + "${PYTHON_IS_DEBUG}" + CACHE INTERNAL "") + +if(PYBIND11_MASTER_PROJECT) + if(PYTHON_MODULE_EXTENSION MATCHES "pypy") + if(NOT DEFINED PYPY_VERSION) + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c + [=[import sys; sys.stdout.write(".".join(map(str, sys.pypy_version_info[:3])))]=] + OUTPUT_VARIABLE pypy_version) + set(PYPY_VERSION + ${pypy_version} + CACHE INTERNAL "") + endif() + message(STATUS "PYPY ${PYPY_VERSION} (Py ${PYTHON_VERSION})") + else() + message(STATUS "PYTHON ${PYTHON_VERSION}") + endif() +endif() + +# Only add Python for build - must be added during the import for config since it has to be re-discovered. +set_property( + TARGET pybind11::pybind11 + APPEND + PROPERTY INTERFACE_INCLUDE_DIRECTORIES $) + +set(pybind11_INCLUDE_DIRS + "${pybind11_INCLUDE_DIR}" "${PYTHON_INCLUDE_DIRS}" + CACHE INTERNAL "Directories where pybind11 and possibly Python headers are located") + +# Python debug libraries expose slightly different objects before 3.8 +# https://docs.python.org/3.6/c-api/intro.html#debugging-builds +# https://stackoverflow.com/questions/39161202/how-to-work-around-missing-pymodule-create2-in-amd64-win-python35-d-lib +if(PYTHON_IS_DEBUG) + set_property( + TARGET pybind11::pybind11 + APPEND + PROPERTY INTERFACE_COMPILE_DEFINITIONS Py_DEBUG) +endif() + +set_property( + TARGET pybind11::module + APPEND + PROPERTY + INTERFACE_LINK_LIBRARIES pybind11::python_link_helper + "$<$,$>:$>") + +if(PYTHON_VERSION VERSION_LESS 3) + set_property( + TARGET pybind11::pybind11 + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES pybind11::python2_no_register) +endif() + +set_property( + TARGET pybind11::embed + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11 $) + +function(pybind11_extension name) + # The prefix and extension are provided by FindPythonLibsNew.cmake + set_target_properties(${name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" + SUFFIX "${PYTHON_MODULE_EXTENSION}") +endfunction() + +# Build a Python extension module: +# pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] +# [NO_EXTRAS] [THIN_LTO] [OPT_SIZE] source1 [source2 ...]) +# +function(pybind11_add_module target_name) + set(options "MODULE;SHARED;EXCLUDE_FROM_ALL;NO_EXTRAS;SYSTEM;THIN_LTO;OPT_SIZE") + cmake_parse_arguments(ARG "${options}" "" "" ${ARGN}) + + if(ARG_MODULE AND ARG_SHARED) + message(FATAL_ERROR "Can't be both MODULE and SHARED") + elseif(ARG_SHARED) + set(lib_type SHARED) + else() + set(lib_type MODULE) + endif() + + if(ARG_EXCLUDE_FROM_ALL) + set(exclude_from_all EXCLUDE_FROM_ALL) + else() + set(exclude_from_all "") + endif() + + add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS}) + + target_link_libraries(${target_name} PRIVATE pybind11::module) + + if(ARG_SYSTEM) + message( + STATUS + "Warning: this does not have an effect - use NO_SYSTEM_FROM_IMPORTED if using imported targets" + ) + endif() + + pybind11_extension(${target_name}) + + # -fvisibility=hidden is required to allow multiple modules compiled against + # different pybind versions to work properly, and for some features (e.g. + # py::module_local). We force it on everything inside the `pybind11` + # namespace; also turning it on for a pybind module compilation here avoids + # potential warnings or issues from having mixed hidden/non-hidden types. + set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden" + CUDA_VISIBILITY_PRESET "hidden") + + if(ARG_NO_EXTRAS) + return() + endif() + + if(NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION) + if(ARG_THIN_LTO) + target_link_libraries(${target_name} PRIVATE pybind11::thin_lto) + else() + target_link_libraries(${target_name} PRIVATE pybind11::lto) + endif() + endif() + + if(NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo) + pybind11_strip(${target_name}) + endif() + + if(MSVC) + target_link_libraries(${target_name} PRIVATE pybind11::windows_extras) + endif() + + if(ARG_OPT_SIZE) + target_link_libraries(${target_name} PRIVATE pybind11::opt_size) + endif() +endfunction() + +# Provide general way to call common Python commands in "common" file. +set(_Python + PYTHON + CACHE INTERNAL "" FORCE) diff --git a/extern/pybind/tools/pyproject.toml b/extern/pybind/tools/pyproject.toml new file mode 100644 index 00000000..8fe2f47a --- /dev/null +++ b/extern/pybind/tools/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=42", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/extern/pybind/tools/setup_global.py.in b/extern/pybind/tools/setup_global.py.in new file mode 100644 index 00000000..4cf040b2 --- /dev/null +++ b/extern/pybind/tools/setup_global.py.in @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Setup script for pybind11-global (in the sdist or in tools/setup_global.py in the repository) +# This package is targeted for easy use from CMake. + +import contextlib +import glob +import os +import re +import shutil +import subprocess +import sys +import tempfile + +# Setuptools has to be before distutils +from setuptools import setup + +from distutils.command.install_headers import install_headers + +class InstallHeadersNested(install_headers): + def run(self): + headers = self.distribution.headers or [] + for header in headers: + # Remove pybind11/include/ + short_header = header.split("/", 2)[-1] + + dst = os.path.join(self.install_dir, os.path.dirname(short_header)) + self.mkpath(dst) + (out, _) = self.copy_file(header, dst) + self.outfiles.append(out) + + +main_headers = glob.glob("pybind11/include/pybind11/*.h") +detail_headers = glob.glob("pybind11/include/pybind11/detail/*.h") +cmake_files = glob.glob("pybind11/share/cmake/pybind11/*.cmake") +headers = main_headers + detail_headers + +cmdclass = {"install_headers": InstallHeadersNested} +$extra_cmd + +# This will _not_ affect installing from wheels, +# only building wheels or installing from SDist. +# Primarily intended on Windows, where this is sometimes +# customized (for example, conda-forge uses Library/) +base = os.environ.get("PYBIND11_GLOBAL_PREFIX", "") + +# Must have a separator +if base and not base.endswith("/"): + base += "/" + +setup( + name="pybind11_global", + version="$version", + packages=[], + headers=headers, + data_files=[ + (base + "share/cmake/pybind11", cmake_files), + (base + "include/pybind11", main_headers), + (base + "include/pybind11/detail", detail_headers), + ], + cmdclass=cmdclass, +) diff --git a/extern/pybind/tools/setup_main.py.in b/extern/pybind/tools/setup_main.py.in new file mode 100644 index 00000000..2231a08f --- /dev/null +++ b/extern/pybind/tools/setup_main.py.in @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Setup script (in the sdist or in tools/setup_main.py in the repository) + +from setuptools import setup + +cmdclass = {} +$extra_cmd + +setup( + name="pybind11", + version="$version", + download_url='https://github.com/pybind/pybind11/tarball/v$version', + packages=[ + "pybind11", + "pybind11.include.pybind11", + "pybind11.include.pybind11.detail", + "pybind11.share.cmake.pybind11", + ], + package_data={ + "pybind11": ["py.typed", "*.pyi"], + "pybind11.include.pybind11": ["*.h"], + "pybind11.include.pybind11.detail": ["*.h"], + "pybind11.share.cmake.pybind11": ["*.cmake"], + }, + extras_require={ + "global": ["pybind11_global==$version"] + }, + entry_points={ + "console_scripts": [ + "pybind11-config = pybind11.__main__:main", + ] + }, + cmdclass=cmdclass +) diff --git a/pyPBD/CMakeLists.txt b/pyPBD/CMakeLists.txt new file mode 100644 index 00000000..1e10b65b --- /dev/null +++ b/pyPBD/CMakeLists.txt @@ -0,0 +1,31 @@ +pybind11_add_module(pypbd + main.cpp + + bind_pointer_vector.h + common.h + CollisionDetectionModule.cpp + ConstraintsModule.cpp + ParameterObjectModule.cpp + ParticleDataModule.cpp + RigidBodyModule.cpp + SimulationModelModule.cpp + SimulationModule.cpp + TimeModule.cpp + TimeStepModule.cpp + UtilitiesModule.cpp +) + +# All the same postfix, otherwise the module name ist not the same as the exported name and python gets confused +set_target_properties(pypbd PROPERTIES + DEBUG_POSTFIX "" + RELWITHDEBINFO_POSTFIX "" + MINSIZEREL_POSTFIX "") + +target_link_libraries(pypbd PRIVATE Simulation PositionBasedDynamics Utils ${Discregrid_LIBRARIES}) +add_dependencies(pypbd Simulation PositionBasedDynamics Utils Ext_GenericParameters Ext_Discregrid) + +add_custom_target(pypbd_install + ${PYTHON_EXECUTABLE} setup.py bdist_wheel + COMMAND ${PYTHON_EXECUTABLE} -m pip install -I build/dist/py*.whl + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + diff --git a/pyPBD/CollisionDetectionModule.cpp b/pyPBD/CollisionDetectionModule.cpp new file mode 100644 index 00000000..3b6d6121 --- /dev/null +++ b/pyPBD/CollisionDetectionModule.cpp @@ -0,0 +1,190 @@ +#include "common.h" + +#include +#include +#include + +#include + +namespace py = pybind11; + +template +using overload_cast_ = pybind11::detail::overload_cast_impl; + +void CollisionDetectionModule(py::module m_sub) +{ + py::class_(m_sub, "CollisionObject") + .def_readonly_static("RigidBodyCollisionObjectType", &PBD::CollisionDetection::CollisionObject::RigidBodyCollisionObjectType) + .def_readonly_static("TriangleModelCollisionObjectType", &PBD::CollisionDetection::CollisionObject::TriangleModelCollisionObjectType) + .def_readonly_static("TetModelCollisionObjectType", &PBD::CollisionDetection::CollisionObject::TetModelCollisionObjectType) + .def_readwrite("aabb", &PBD::CollisionDetection::CollisionObject::m_aabb) + .def_readwrite("bodyIndex", &PBD::CollisionDetection::CollisionObject::m_bodyIndex) + .def_readwrite("bodyType", &PBD::CollisionDetection::CollisionObject::m_bodyType); + + py::class_(m_sub, "CollisionObjectWithoutGeometry") + .def(py::init<>()) + .def_readwrite_static("TYPE_ID", &PBD::CollisionDetection::CollisionObjectWithoutGeometry::TYPE_ID) + .def("getTypeId", &PBD::CollisionDetection::CollisionObjectWithoutGeometry::getTypeId); + + py::class_(m_sub, "CollisionDetection") + .def_readonly_static("RigidBodyContactType", &PBD::CollisionDetection::RigidBodyContactType) + .def_readonly_static("ParticleContactType", &PBD::CollisionDetection::ParticleContactType) + .def_readonly_static("ParticleRigidBodyContactType", &PBD::CollisionDetection::ParticleRigidBodyContactType) + .def_readonly_static("ParticleSolidContactType", &PBD::CollisionDetection::ParticleSolidContactType) + + .def("cleanup", &PBD::CollisionDetection::cleanup) + .def("getTolerance", &PBD::CollisionDetection::getTolerance) + .def("setTolerance", &PBD::CollisionDetection::setTolerance) + .def("addRigidBodyContact", &PBD::CollisionDetection::addRigidBodyContact) + .def("addParticleRigidBodyContact", &PBD::CollisionDetection::addParticleRigidBodyContact) + .def("addParticleSolidContact", &PBD::CollisionDetection::addParticleSolidContact) + .def("addCollisionObject", &PBD::CollisionDetection::addCollisionObject) + .def("getCollisionObjects", &PBD::CollisionDetection::getCollisionObjects) + .def("collisionDetection", &PBD::CollisionDetection::collisionDetection) + //.def("setContactCallback", &PBD::CollisionDetection::setContactCallback) + //.def("setSolidContactCallback", &PBD::CollisionDetection::setSolidContactCallback) + .def("updateAABBs", &PBD::CollisionDetection::updateAABBs) + .def("updateAABB", overload_cast_()(&PBD::CollisionDetection::updateAABB)); + + py::class_(m_sub, "DistanceFieldCollisionDetection") + .def(py::init<>()) + .def("addCollisionBox", [](PBD::DistanceFieldCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::ParticleData& pd, const unsigned int offset, const unsigned int numVertices, const Vector3r& box, const bool testMesh, const bool invertSDF) + { + cd.addCollisionBox(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, box, testMesh, invertSDF); + }) + .def("addCollisionBox", [](PBD::DistanceFieldCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::VertexData& pd, const unsigned int offset, const unsigned int numVertices, const Vector3r& box, const bool testMesh, const bool invertSDF) + { + cd.addCollisionBox(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, box, testMesh, invertSDF); + }) + + .def("addCollisionSphere", [](PBD::DistanceFieldCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::ParticleData& pd, const unsigned int offset, const unsigned int numVertices, const Real radius, const bool testMesh, const bool invertSDF) + { + cd.addCollisionSphere(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, radius, testMesh, invertSDF); + }) + .def("addCollisionSphere", [](PBD::DistanceFieldCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::VertexData& pd, const unsigned int offset, const unsigned int numVertices, const Real radius, const bool testMesh, const bool invertSDF) + { + cd.addCollisionSphere(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, radius, testMesh, invertSDF); + }) + + .def("addCollisionTorus", [](PBD::DistanceFieldCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::ParticleData& pd, const unsigned int offset, const unsigned int numVertices, const Vector2r& radii, const bool testMesh, const bool invertSDF) + { + cd.addCollisionTorus(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, radii, testMesh, invertSDF); + }) + .def("addCollisionTorus", [](PBD::DistanceFieldCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::VertexData& pd, const unsigned int offset, const unsigned int numVertices, const Vector2r& radii, const bool testMesh, const bool invertSDF) + { + cd.addCollisionTorus(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, radii, testMesh, invertSDF); + }) + + .def("addCollisionCylinder", [](PBD::DistanceFieldCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::ParticleData& pd, const unsigned int offset, const unsigned int numVertices, const Vector2r& dim, const bool testMesh, const bool invertSDF) + { + cd.addCollisionCylinder(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, dim, testMesh, invertSDF); + }) + .def("addCollisionCylinder", [](PBD::DistanceFieldCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::VertexData& pd, const unsigned int offset, const unsigned int numVertices, const Vector2r& dim, const bool testMesh, const bool invertSDF) + { + cd.addCollisionCylinder(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, dim, testMesh, invertSDF); + }) + + .def("addCollisionHollowSphere", [](PBD::DistanceFieldCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::ParticleData& pd, const unsigned int offset, const unsigned int numVertices, const Real radius, const Real thickness, const bool testMesh, const bool invertSDF) + { + cd.addCollisionHollowSphere(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, radius, thickness, testMesh, invertSDF); + }) + .def("addCollisionHollowSphere", [](PBD::DistanceFieldCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::VertexData& pd, const unsigned int offset, const unsigned int numVertices, const Real radius, const Real thickness, const bool testMesh, const bool invertSDF) + { + cd.addCollisionHollowSphere(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, radius, thickness, testMesh, invertSDF); + }) + + .def("addCollisionHollowBox", [](PBD::DistanceFieldCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::ParticleData& pd, const unsigned int offset, const unsigned int numVertices, const Vector3r& box, const Real thickness, const bool testMesh, const bool invertSDF) + { + cd.addCollisionHollowBox(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, box, thickness, testMesh, invertSDF); + }) + .def("addCollisionHollowBox", [](PBD::DistanceFieldCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::VertexData& pd, const unsigned int offset, const unsigned int numVertices, const Vector3r& box, const Real thickness, const bool testMesh, const bool invertSDF) + { + cd.addCollisionHollowBox(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, box, thickness, testMesh, invertSDF); + }) + + .def("addCollisionObjectWithoutGeometry", [](PBD::DistanceFieldCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::ParticleData &pd, const unsigned int offset, const unsigned int numVertices, const bool testMesh) + { + cd.addCollisionObjectWithoutGeometry(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, testMesh); + }) + .def("addCollisionObjectWithoutGeometry", [](PBD::DistanceFieldCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::VertexData& vd, const unsigned int offset, const unsigned int numVertices, const bool testMesh) + { + cd.addCollisionObjectWithoutGeometry(bodyIndex, bodyType, &vd.getPosition(offset), numVertices, testMesh); + }) + + .def("isDistanceFieldCollisionObject", &PBD::DistanceFieldCollisionDetection::isDistanceFieldCollisionObject); + + py::class_>(m_sub, "CubicSDFCollisionDetectionGridPtr") + ; + + py::class_(m_sub, "CubicSDFCollisionDetection") + .def(py::init<>()) + .def("addCubicSDFCollisionObject", [](PBD::CubicSDFCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::ParticleData& pd, const unsigned int offset, const unsigned int numVertices, const std::string& sdfFile, const Vector3r& scale, const bool testMesh, const bool invertSDF) + { + cd.addCubicSDFCollisionObject(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, sdfFile, scale, testMesh, invertSDF); + }) + .def("addCubicSDFCollisionObject", [](PBD::CubicSDFCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::VertexData& pd, const unsigned int offset, const unsigned int numVertices, const std::string& sdfFile, const Vector3r& scale, const bool testMesh, const bool invertSDF) + { + cd.addCubicSDFCollisionObject(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, sdfFile, scale, testMesh, invertSDF); + }) + .def("addCubicSDFCollisionObject", [](PBD::CubicSDFCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::ParticleData& pd, const unsigned int offset, const unsigned int numVertices, PBD::CubicSDFCollisionDetection::GridPtr sdf, const Vector3r& scale, const bool testMesh, const bool invertSDF) + { + cd.addCubicSDFCollisionObject(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, sdf, scale, testMesh, invertSDF); + }) + .def("addCubicSDFCollisionObject", [](PBD::CubicSDFCollisionDetection& cd, const unsigned int bodyIndex, const unsigned int bodyType, + const PBD::VertexData& pd, const unsigned int offset, const unsigned int numVertices, const PBD::CubicSDFCollisionDetection::GridPtr sdf, const Vector3r& scale, const bool testMesh, const bool invertSDF) + { + cd.addCubicSDFCollisionObject(bodyIndex, bodyType, &pd.getPosition(offset), numVertices, sdf, scale, testMesh, invertSDF); + }) + .def_static("generateSDF", [](const PBD::VertexData &vd, const Utilities::IndexedFaceMesh &mesh, const Eigen::Matrix &resolution) -> PBD::CubicSDFCollisionDetection::GridPtr + { + const std::vector& faces = mesh.getFaces(); + const unsigned int nFaces = mesh.numFaces(); + +#ifdef USE_DOUBLE + Discregrid::TriangleMesh sdfMesh(&vd.getPosition(0)[0], faces.data(), vd.size(), nFaces); +#else + // if type is float, copy vector to double vector + std::vector doubleVec; + doubleVec.resize(3 * vd.size()); + for (unsigned int i = 0; i < vd.size(); i++) + for (unsigned int j = 0; j < 3; j++) + doubleVec[3 * i + j] = vd.getPosition(i)[j]; + Discregrid::TriangleMesh sdfMesh(&doubleVec[0], faces.data(), vd.size(), nFaces); +#endif + Discregrid::MeshDistance md(sdfMesh); + Eigen::AlignedBox3d domain; + for (auto const& x : sdfMesh.vertices()) + { + domain.extend(x); + } + domain.max() += 0.1 * Eigen::Vector3d::Ones(); + domain.min() -= 0.1 * Eigen::Vector3d::Ones(); + + std::cout << "Set SDF resolution: " << resolution[0] << ", " << resolution[1] << ", " << resolution[2] << std::endl; + //PBD::CubicSDFCollisionDetection::Grid *sdf = new PBD::CubicSDFCollisionDetection::Grid(domain, std::array({ resolution[0], resolution[1], resolution[2] })); + auto sdf = std::make_shared(domain, std::array({ resolution[0], resolution[1], resolution[2] })); + auto func = Discregrid::DiscreteGrid::ContinuousFunction{}; + func = [&md](Eigen::Vector3d const& xi) {return md.signedDistanceCached(xi); }; + std::cout << "Generate SDF\n"; + sdf->addFunction(func, true); + return sdf; + }) + ; +} \ No newline at end of file diff --git a/pyPBD/ConstraintsModule.cpp b/pyPBD/ConstraintsModule.cpp new file mode 100644 index 00000000..010ed125 --- /dev/null +++ b/pyPBD/ConstraintsModule.cpp @@ -0,0 +1,170 @@ +#include "common.h" + +#include + +#include +#include +#include +#include +#include + +namespace py = pybind11; + +#define CONSTRAINT(name, base) \ + py::class_(m_sub, #name) \ + .def(py::init<>()) \ + .def_readwrite_static("TYPE_ID", &PBD::name::TYPE_ID) + +#define CONSTRAINT_JOINTINFO(name, base) \ + CONSTRAINT(name, base) \ + .def_readwrite("jointInfo", &PBD::name::m_jointInfo) + +void ConstraintsModule(py::module m_sub) +{ + + py::class_(m_sub, "Constraint") + .def_readwrite("bodies", &PBD::Constraint::m_bodies) + .def("getTypeId", &PBD::Constraint::getTypeId) + .def("initConstraintBeforeProjection", &PBD::Constraint::initConstraintBeforeProjection) + .def("updateConstraint", &PBD::Constraint::updateConstraint) + .def("solvePositionConstraint", &PBD::Constraint::solvePositionConstraint) + .def("solveVelocityConstraint", &PBD::Constraint::solveVelocityConstraint); + + CONSTRAINT_JOINTINFO(BallJoint, Constraint); + CONSTRAINT_JOINTINFO(BallOnLineJoint, Constraint); + CONSTRAINT_JOINTINFO(HingeJoint, Constraint); + CONSTRAINT_JOINTINFO(UniversalJoint, Constraint); + CONSTRAINT_JOINTINFO(SliderJoint, Constraint); + + py::class_(m_sub, "MotorJoint") + .def("getTarget", &PBD::MotorJoint::getTarget) + .def("setTarget", &PBD::MotorJoint::setTarget) + .def("getTargetSequence", &PBD::MotorJoint::getTargetSequence) + .def("setTargetSequence", &PBD::MotorJoint::setTargetSequence) + .def("getRepeatSequence", &PBD::MotorJoint::getRepeatSequence) + .def("setRepeatSequence", &PBD::MotorJoint::setRepeatSequence); + + CONSTRAINT_JOINTINFO(TargetPositionMotorSliderJoint, MotorJoint); + CONSTRAINT_JOINTINFO(TargetVelocityMotorSliderJoint, MotorJoint); + CONSTRAINT_JOINTINFO(TargetAngleMotorHingeJoint, MotorJoint); + CONSTRAINT_JOINTINFO(TargetVelocityMotorHingeJoint, MotorJoint); + CONSTRAINT_JOINTINFO(DamperJoint, Constraint) + .def_readwrite("stiffness", &PBD::DamperJoint::m_stiffness) + .def_readwrite("lambda", &PBD::DamperJoint::m_lambda); + CONSTRAINT_JOINTINFO(RigidBodyParticleBallJoint, Constraint); + CONSTRAINT_JOINTINFO(RigidBodySpring, Constraint) + .def_readwrite("restLength", &PBD::RigidBodySpring::m_restLength) + .def_readwrite("stiffness", &PBD::RigidBodySpring::m_stiffness) + .def_readwrite("lambda", &PBD::RigidBodySpring::m_lambda); + CONSTRAINT_JOINTINFO(DistanceJoint, Constraint) + .def_readwrite("restLength", &PBD::DistanceJoint::m_restLength); + + CONSTRAINT(DistanceConstraint, Constraint) + .def_readwrite("stiffness", &PBD::DistanceConstraint::m_stiffness) + .def_readwrite("restLength", &PBD::DistanceConstraint::m_restLength); + CONSTRAINT(DistanceConstraint_XPBD, Constraint) + .def_readwrite("stiffness", &PBD::DistanceConstraint_XPBD::m_stiffness) + .def_readwrite("restLength", &PBD::DistanceConstraint_XPBD::m_restLength) + .def_readwrite("lambda", &PBD::DistanceConstraint_XPBD::m_lambda); + CONSTRAINT(DihedralConstraint, Constraint) + .def_readwrite("stiffness", &PBD::DihedralConstraint::m_stiffness) + .def_readwrite("restAngle", &PBD::DihedralConstraint::m_restAngle); + CONSTRAINT(IsometricBendingConstraint, Constraint) + .def_readwrite("stiffness", &PBD::IsometricBendingConstraint::m_stiffness) + .def_readwrite("Q", &PBD::IsometricBendingConstraint::m_Q); + CONSTRAINT(IsometricBendingConstraint_XPBD, Constraint) + .def_readwrite("stiffness", &PBD::IsometricBendingConstraint_XPBD::m_stiffness) + .def_readwrite("Q", &PBD::IsometricBendingConstraint_XPBD::m_Q) + .def_readwrite("lambda", &PBD::IsometricBendingConstraint_XPBD::m_lambda); + CONSTRAINT(FEMTriangleConstraint, Constraint) + .def_readwrite("xxStiffness", &PBD::FEMTriangleConstraint::m_xxStiffness) + .def_readwrite("yyStiffness", &PBD::FEMTriangleConstraint::m_yyStiffness) + .def_readwrite("xyStiffness", &PBD::FEMTriangleConstraint::m_xyStiffness) + .def_readwrite("xyPoissonRatio", &PBD::FEMTriangleConstraint::m_xyPoissonRatio) + .def_readwrite("yxPoissonRatio", &PBD::FEMTriangleConstraint::m_yxPoissonRatio) + .def_readwrite("area", &PBD::FEMTriangleConstraint::m_area) + .def_readwrite("invRestMat", &PBD::FEMTriangleConstraint::m_invRestMat); + CONSTRAINT(StrainTriangleConstraint, Constraint) + .def_readwrite("xxStiffness", &PBD::StrainTriangleConstraint::m_xxStiffness) + .def_readwrite("yyStiffness", &PBD::StrainTriangleConstraint::m_yyStiffness) + .def_readwrite("xyStiffness", &PBD::StrainTriangleConstraint::m_xyStiffness) + .def_readwrite("normalizeStretch", &PBD::StrainTriangleConstraint::m_normalizeStretch) + .def_readwrite("normalizeShear", &PBD::StrainTriangleConstraint::m_normalizeShear) + .def_readwrite("invRestMat", &PBD::StrainTriangleConstraint::m_invRestMat); + CONSTRAINT(VolumeConstraint, Constraint) + .def_readwrite("stiffness", &PBD::VolumeConstraint::m_stiffness) + .def_readwrite("restVolume", &PBD::VolumeConstraint::m_restVolume); + CONSTRAINT(VolumeConstraint_XPBD, Constraint) + .def_readwrite("stiffness", &PBD::VolumeConstraint_XPBD::m_stiffness) + .def_readwrite("restVolume", &PBD::VolumeConstraint_XPBD::m_restVolume) + .def_readwrite("lambda", &PBD::VolumeConstraint_XPBD::m_lambda); + CONSTRAINT(FEMTetConstraint, Constraint) + .def_readwrite("stiffness", &PBD::FEMTetConstraint::m_stiffness) + .def_readwrite("poissonRatio", &PBD::FEMTetConstraint::m_poissonRatio) + .def_readwrite("volume", &PBD::FEMTetConstraint::m_volume) + .def_readwrite("invRestMat", &PBD::FEMTetConstraint::m_invRestMat); + CONSTRAINT(StrainTetConstraint, Constraint) + .def_readwrite("stretchStiffness", &PBD::StrainTetConstraint::m_stretchStiffness) + .def_readwrite("shearStiffness", &PBD::StrainTetConstraint::m_shearStiffness) + .def_readwrite("normalizeStretch", &PBD::StrainTetConstraint::m_normalizeStretch) + .def_readwrite("normalizeShear", &PBD::StrainTetConstraint::m_normalizeShear) + .def_readwrite("invRestMat", &PBD::StrainTetConstraint::m_invRestMat); + py::class_(m_sub, "ShapeMatchingConstraint") + .def(py::init()) + .def_readwrite("stiffness", &PBD::ShapeMatchingConstraint::m_stiffness) + .def_readwrite_static("TYPE_ID", &PBD::ShapeMatchingConstraint::TYPE_ID) + .def_readwrite("restCm", &PBD::ShapeMatchingConstraint::m_restCm); + CONSTRAINT(StretchShearConstraint, Constraint) + .def_readwrite("stretchingStiffness", &PBD::StretchShearConstraint::m_stretchingStiffness) + .def_readwrite("shearingStiffness1", &PBD::StretchShearConstraint::m_shearingStiffness1) + .def_readwrite("shearingStiffness2", &PBD::StretchShearConstraint::m_shearingStiffness2) + .def_readwrite("restLength", &PBD::StretchShearConstraint::m_restLength); + CONSTRAINT(BendTwistConstraint, Constraint) + .def_readwrite("twistingStiffness", &PBD::BendTwistConstraint::m_twistingStiffness) + .def_readwrite("bendingStiffness1", &PBD::BendTwistConstraint::m_bendingStiffness1) + .def_readwrite("bendingStiffness2", &PBD::BendTwistConstraint::m_bendingStiffness2) + .def_readwrite("restDarbouxVector", &PBD::BendTwistConstraint::m_restDarbouxVector); + CONSTRAINT(StretchBendingTwistingConstraint, Constraint) + .def_readwrite("averageRadius", &PBD::StretchBendingTwistingConstraint::m_averageRadius) + .def_readwrite("averageSegmentLength", &PBD::StretchBendingTwistingConstraint::m_averageSegmentLength) + .def_readwrite("restDarbouxVector", &PBD::StretchBendingTwistingConstraint::m_restDarbouxVector) + .def_readwrite("stiffnessCoefficientK", &PBD::StretchBendingTwistingConstraint::m_stiffnessCoefficientK) + .def_readwrite("stretchCompliance", &PBD::StretchBendingTwistingConstraint::m_stretchCompliance) + .def_readwrite("bendingAndTorsionCompliance", &PBD::StretchBendingTwistingConstraint::m_bendingAndTorsionCompliance) + .def_readwrite("lambdaSum", &PBD::StretchBendingTwistingConstraint::m_lambdaSum); + CONSTRAINT(DirectPositionBasedSolverForStiffRodsConstraint, Constraint) + .def("initConstraint", &PBD::DirectPositionBasedSolverForStiffRodsConstraint::initConstraint); + + py::class_(m_sub, "RigidBodyContactConstraint") + .def_readwrite("bodies", &PBD::RigidBodyContactConstraint::m_bodies) + .def_readwrite("stiffness", &PBD::RigidBodyContactConstraint::m_stiffness) + .def_readwrite("frictionCoeff", &PBD::RigidBodyContactConstraint::m_frictionCoeff) + .def_readwrite("sum_impulses", &PBD::RigidBodyContactConstraint::m_sum_impulses) + .def_readwrite("constraintInfo", &PBD::RigidBodyContactConstraint::m_constraintInfo) + .def("getTypeId", &PBD::RigidBodyContactConstraint::getTypeId) + .def("initConstraint", &PBD::RigidBodyContactConstraint::initConstraint) + .def("solveVelocityConstraint", &PBD::RigidBodyContactConstraint::solveVelocityConstraint); + + py::class_(m_sub, "ParticleRigidBodyContactConstraint") + .def_readwrite("bodies", &PBD::ParticleRigidBodyContactConstraint::m_bodies) + .def_readwrite("stiffness", &PBD::ParticleRigidBodyContactConstraint::m_stiffness) + .def_readwrite("frictionCoeff", &PBD::ParticleRigidBodyContactConstraint::m_frictionCoeff) + .def_readwrite("sum_impulses", &PBD::ParticleRigidBodyContactConstraint::m_sum_impulses) + .def_readwrite("constraintInfo", &PBD::ParticleRigidBodyContactConstraint::m_constraintInfo) + .def("initConstraint", &PBD::ParticleRigidBodyContactConstraint::initConstraint) + .def("solveVelocityConstraint", &PBD::ParticleRigidBodyContactConstraint::solveVelocityConstraint); + + py::class_(m_sub, "ParticleTetContactConstraint") + .def_readwrite("bodies", &PBD::ParticleTetContactConstraint::m_bodies) + .def_readwrite("solidIndex", &PBD::ParticleTetContactConstraint::m_solidIndex) + .def_readwrite("tetIndex", &PBD::ParticleTetContactConstraint::m_tetIndex) + .def_readwrite("frictionCoeff", &PBD::ParticleTetContactConstraint::m_frictionCoeff) + .def_readwrite("bary", &PBD::ParticleTetContactConstraint::m_bary) + .def_readwrite("lambda", &PBD::ParticleTetContactConstraint::m_lambda) + .def_readwrite("constraintInfo", &PBD::ParticleTetContactConstraint::m_constraintInfo) + .def_readwrite("x", &PBD::ParticleTetContactConstraint::m_x) + .def_readwrite("v", &PBD::ParticleTetContactConstraint::m_v) + .def("initConstraint", &PBD::ParticleTetContactConstraint::initConstraint) + .def("solvePositionConstraint", &PBD::ParticleTetContactConstraint::solvePositionConstraint) + .def("solveVelocityConstraint", &PBD::ParticleTetContactConstraint::solveVelocityConstraint); +} \ No newline at end of file diff --git a/pyPBD/ParameterObjectModule.cpp b/pyPBD/ParameterObjectModule.cpp new file mode 100644 index 00000000..8a1cbb46 --- /dev/null +++ b/pyPBD/ParameterObjectModule.cpp @@ -0,0 +1,30 @@ +// +// Created by sjeske on 1/22/20. +// +#include "common.h" + +#include +#include +#include + +#include +#include + +namespace py = pybind11; + +void ParameterObjectModule(py::module m_sub){ + //auto m_sub = m.def_submodule("Common"); + py::class_(m_sub, "ParameterObject") + // .def(py::init<>()) TODO: no constructor for now because this object does not need to be constructable + .def("getValueBool", &GenParam::ParameterObject::getValue) + .def("getValueInt", &GenParam::ParameterObject::getValue) + .def("getValueUInt", &GenParam::ParameterObject::getValue) + .def("getValueFloat", &GenParam::ParameterObject::getValue) + .def("getValueString", &GenParam::ParameterObject::getValue) + + .def("setValueBool", &GenParam::ParameterObject::setValue) + .def("setValueInt", &GenParam::ParameterObject::setValue) + .def("setValueUInt", &GenParam::ParameterObject::setValue) + .def("setValueFloat", &GenParam::ParameterObject::setValue) + .def("setValueString", &GenParam::ParameterObject::setValue); +} diff --git a/pyPBD/ParticleDataModule.cpp b/pyPBD/ParticleDataModule.cpp new file mode 100644 index 00000000..e5b4cd1a --- /dev/null +++ b/pyPBD/ParticleDataModule.cpp @@ -0,0 +1,59 @@ +#include "common.h" + +#include +#include "Simulation/TimeStepController.h" +#include "Simulation/TimeManager.h" +#include "PositionBasedDynamics/TimeIntegration.h" + +#include +#include +#include +#include + +namespace py = pybind11; + + +void ParticleDataModule(py::module m_sub) +{ + py::class_(m_sub, "VertexData") + .def(py::init<>()) + .def("addVertex", &PBD::VertexData::addVertex) + .def("getPosition", (const Vector3r & (PBD::VertexData::*)(const unsigned int)const)(&PBD::VertexData::getPosition)) + .def("setPosition", &PBD::VertexData::setPosition) + .def("resize", &PBD::VertexData::resize) + .def("reserve", &PBD::VertexData::reserve) + .def("release", &PBD::VertexData::release) + .def("size", &PBD::VertexData::size) + //.def("getVertices", &PBD::VertexData::getVertices, py::return_value_policy::reference); + .def("getVertices", [](PBD::VertexData& vd) -> py::memoryview { + void* base_ptr = const_cast(&(*vd.getVertices())[0][0]); + int num_vert = vd.size(); + return py::memoryview::from_buffer((Real*)base_ptr, { num_vert, 3 }, { sizeof(Real) * 3, sizeof(Real) }, true); + }); + + py::class_(m_sub, "ParticleData") + .def(py::init<>()) + .def("addVertex", &PBD::ParticleData::addVertex) + .def("getPosition", (const Vector3r & (PBD::ParticleData::*)(const unsigned int)const)(&PBD::ParticleData::getPosition)) + .def("setPosition", &PBD::ParticleData::setPosition) + .def("getPosition0", (const Vector3r & (PBD::ParticleData::*)(const unsigned int)const)(&PBD::ParticleData::getPosition0)) + .def("setPosition0", &PBD::ParticleData::setPosition0) + .def("getMass", (const Real(PBD::ParticleData::*)(const unsigned int)const)(&PBD::ParticleData::getMass)) + .def("getInvMass", &PBD::ParticleData::getInvMass) + .def("setMass", &PBD::ParticleData::setMass) + .def("getVelocity", (const Vector3r & (PBD::ParticleData::*)(const unsigned int)const)(&PBD::ParticleData::getVelocity)) + .def("setVelocity", &PBD::ParticleData::setVelocity) + .def("getAcceleration", (const Vector3r & (PBD::ParticleData::*)(const unsigned int)const)(&PBD::ParticleData::getAcceleration)) + .def("setAcceleration", &PBD::ParticleData::setAcceleration) + .def("getNumberOfParticles", &PBD::ParticleData::getNumberOfParticles) + .def("resize", &PBD::ParticleData::resize) + .def("reserve", &PBD::ParticleData::reserve) + .def("release", &PBD::ParticleData::release) + .def("size", &PBD::ParticleData::size) + //.def("getVertices", &PBD::ParticleData::getVertices, py::return_value_policy::reference); + .def("getVertices", [](PBD::ParticleData &pd) -> py::memoryview { + void* base_ptr = const_cast(&(*pd.getVertices())[0][0]); + int num_vert = pd.getNumberOfParticles(); + return py::memoryview::from_buffer((Real*)base_ptr, { num_vert, 3 }, { sizeof(Real) * 3, sizeof(Real) }, true); + }); +} \ No newline at end of file diff --git a/pyPBD/RigidBodyModule.cpp b/pyPBD/RigidBodyModule.cpp new file mode 100644 index 00000000..ef5c1e58 --- /dev/null +++ b/pyPBD/RigidBodyModule.cpp @@ -0,0 +1,118 @@ +#include "common.h" + +#include + +#include +#include +#include +#include +#include + +namespace py = pybind11; + +template +using overload_cast_ = pybind11::detail::overload_cast_impl; + +#define GET_QUAT_FCT_CONVERT(name) .def(#name, [](PBD::RigidBody& obj) { return obj.name().coeffs(); }) +#define SET_QUAT_FCT_CONVERT(name) \ + .def(#name, [](PBD::RigidBody& obj, const Vector4r& qVec) \ + { \ + Quaternionr q; \ + q.coeffs() = qVec; \ + obj.name(q); \ + }) + +void RigidBodyModule(py::module m_sub) +{ + py::class_(m_sub, "RigidBodyGeometry") + .def(py::init<>()) + .def("getMesh", &PBD::RigidBodyGeometry::getMesh) + .def("getVertexData", (const PBD::VertexData & (PBD::RigidBodyGeometry::*)()const)(&PBD::RigidBodyGeometry::getVertexData)) + .def("getVertexDataLocal", (const PBD::VertexData & (PBD::RigidBodyGeometry::*)()const)(&PBD::RigidBodyGeometry::getVertexDataLocal)) + .def("initMesh", &PBD::RigidBodyGeometry::initMesh) + .def("updateMeshTransformation", &PBD::RigidBodyGeometry::updateMeshTransformation) + .def("updateMeshNormals", &PBD::RigidBodyGeometry::updateMeshNormals) + ; + + py::class_(m_sub, "RigidBody") + .def(py::init<>()) + .def("initBody", [](PBD::RigidBody& obj, const Real density, + const Vector3r& x, const Vector4r& qVec, + const PBD::VertexData& vertices, const Utilities::IndexedFaceMesh& mesh, + const Vector3r& scale) + { + Quaternionr q; + q.coeffs() = qVec; + obj.initBody(density, x, q, vertices, mesh, scale); + }) + .def("initBody", [](PBD::RigidBody& obj, const Real mass, + const Vector3r& x, const Vector3r& inertiaTensor, const Vector4r& qVec, + const PBD::VertexData& vertices, const Utilities::IndexedFaceMesh& mesh, + const Vector3r& scale) + { + Quaternionr q; + q.coeffs() = qVec; + obj.initBody(mass, x, inertiaTensor, q, vertices, mesh, scale); + }) + .def("reset", &PBD::RigidBody::reset) + .def("updateInverseTransformation", &PBD::RigidBody::updateInverseTransformation) + .def("rotationUpdated", &PBD::RigidBody::rotationUpdated) + .def("updateInertiaW", &PBD::RigidBody::updateInertiaW) + .def("determineMassProperties", &PBD::RigidBody::determineMassProperties) + .def("getTransformationR", &PBD::RigidBody::getTransformationR) + .def("getTransformationV1", &PBD::RigidBody::getTransformationV1) + .def("getTransformationV2", &PBD::RigidBody::getTransformationV2) + .def("getTransformationRXV1", &PBD::RigidBody::getTransformationRXV1) + .def("getMass", (const Real&(PBD::RigidBody::*)()const)(&PBD::RigidBody::getMass)) + .def("setMass", &PBD::RigidBody::setMass) + .def("getInvMass", &PBD::RigidBody::getInvMass) + .def("getPosition", (const Vector3r & (PBD::RigidBody::*)()const)(&PBD::RigidBody::getPosition)) + .def("setPosition", &PBD::RigidBody::setPosition) + .def("getLastPosition", (const Vector3r & (PBD::RigidBody::*)()const)(&PBD::RigidBody::getLastPosition)) + .def("setLastPosition", &PBD::RigidBody::setLastPosition) + .def("getOldPosition", (const Vector3r & (PBD::RigidBody::*)()const)(&PBD::RigidBody::getOldPosition)) + .def("setOldPosition", &PBD::RigidBody::setOldPosition) + .def("getPosition0", (const Vector3r & (PBD::RigidBody::*)()const)(&PBD::RigidBody::getPosition0)) + .def("setPosition0", &PBD::RigidBody::setPosition0) + .def("getPositionInitial_MAT", (const Vector3r & (PBD::RigidBody::*)()const)(&PBD::RigidBody::getPositionInitial_MAT)) + .def("setPositionInitial_MAT", &PBD::RigidBody::setPositionInitial_MAT) + .def("getVelocity", (const Vector3r & (PBD::RigidBody::*)()const)(&PBD::RigidBody::getVelocity)) + .def("setVelocity", &PBD::RigidBody::setVelocity) + .def("getVelocity0", (const Vector3r & (PBD::RigidBody::*)()const)(&PBD::RigidBody::getVelocity0)) + .def("setVelocity0", &PBD::RigidBody::setVelocity0) + .def("getAcceleration", (const Vector3r & (PBD::RigidBody::*)()const)(&PBD::RigidBody::getAcceleration)) + .def("setAcceleration", &PBD::RigidBody::setAcceleration) + .def("getInertiaTensor", &PBD::RigidBody::getInertiaTensor) + .def("setInertiaTensor", &PBD::RigidBody::setInertiaTensor) + .def("getInertiaTensorW", (const Matrix3r & (PBD::RigidBody::*)()const)(&PBD::RigidBody::getInertiaTensorW)) + .def("getInertiaTensorInverse", &PBD::RigidBody::getInertiaTensorInverse) + .def("getInertiaTensorInverseW", (const Matrix3r & (PBD::RigidBody::*)()const)(&PBD::RigidBody::getInertiaTensorInverseW)) + .def("setInertiaTensorInverseW", &PBD::RigidBody::setInertiaTensorInverseW) + GET_QUAT_FCT_CONVERT(getRotation) + SET_QUAT_FCT_CONVERT(setRotation) + GET_QUAT_FCT_CONVERT(getLastRotation) + SET_QUAT_FCT_CONVERT(setLastRotation) + GET_QUAT_FCT_CONVERT(getOldRotation) + SET_QUAT_FCT_CONVERT(setOldRotation) + GET_QUAT_FCT_CONVERT(getRotation0) + SET_QUAT_FCT_CONVERT(setRotation0) + GET_QUAT_FCT_CONVERT(getRotationMAT) + SET_QUAT_FCT_CONVERT(setRotationMAT) + GET_QUAT_FCT_CONVERT(getRotationInitial) + SET_QUAT_FCT_CONVERT(setRotationInitial) + .def("getRotationMatrix", (const Matrix3r & (PBD::RigidBody::*)()const)(&PBD::RigidBody::getRotationMatrix)) + .def("setRotationMatrix", &PBD::RigidBody::setRotationMatrix) + .def("getAngularVelocity", (const Vector3r & (PBD::RigidBody::*)()const)(&PBD::RigidBody::getAngularVelocity)) + .def("setAngularVelocity", &PBD::RigidBody::setAngularVelocity) + .def("getAngularVelocity0", (const Vector3r & (PBD::RigidBody::*)()const)(&PBD::RigidBody::getAngularVelocity0)) + .def("setAngularVelocity0", &PBD::RigidBody::setAngularVelocity0) + .def("getTorque", (const Vector3r & (PBD::RigidBody::*)()const)(&PBD::RigidBody::getTorque)) + .def("setTorque", &PBD::RigidBody::setTorque) + .def("getRestitutionCoeff", &PBD::RigidBody::getRestitutionCoeff) + .def("setRestitutionCoeff", &PBD::RigidBody::setRestitutionCoeff) + .def("getFrictionCoeff", &PBD::RigidBody::getFrictionCoeff) + .def("setFrictionCoeff", &PBD::RigidBody::setFrictionCoeff) + .def("getGeometry", &PBD::RigidBody::getGeometry) + ; + +} \ No newline at end of file diff --git a/pyPBD/SimulationModelModule.cpp b/pyPBD/SimulationModelModule.cpp new file mode 100644 index 00000000..48b54275 --- /dev/null +++ b/pyPBD/SimulationModelModule.cpp @@ -0,0 +1,265 @@ +#include "common.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace py = pybind11; + +void SimulationModelModule(py::module m_sub) +{ + py::class_(m_sub, "TriangleModel") + .def(py::init<>()) + .def("getParticleMesh", (const PBD::TriangleModel::ParticleMesh& (PBD::TriangleModel::*)()const)(&PBD::TriangleModel::getParticleMesh)) + .def("cleanupModel", &PBD::TriangleModel::cleanupModel) + .def("getIndexOffset", &PBD::TriangleModel::getIndexOffset) + .def("initMesh", &PBD::TriangleModel::initMesh) + .def("updateMeshNormals", &PBD::TriangleModel::updateMeshNormals) + .def("getRestitutionCoeff", &PBD::TriangleModel::getRestitutionCoeff) + .def("setRestitutionCoeff", &PBD::TriangleModel::setRestitutionCoeff) + .def("getFrictionCoeff", &PBD::TriangleModel::getFrictionCoeff) + .def("setFrictionCoeff", &PBD::TriangleModel::setFrictionCoeff); + + py::class_(m_sub, "TetModel") + .def(py::init<>()) + .def("getInitialX", &PBD::TetModel::getInitialX) + .def("setInitialX", &PBD::TetModel::setInitialX) + .def("getInitialR", &PBD::TetModel::getInitialR) + .def("setInitialR", &PBD::TetModel::setInitialR) + .def("getInitialScale", &PBD::TetModel::getInitialScale) + .def("setInitialScale", &PBD::TetModel::setInitialScale) + + .def("getSurfaceMesh", &PBD::TetModel::getSurfaceMesh) + .def("getVisVertices", &PBD::TetModel::getVisVertices) + .def("getVisMesh", &PBD::TetModel::getVisMesh) + .def("getParticleMesh", (const PBD::TetModel::ParticleMesh & (PBD::TetModel::*)()const)(&PBD::TetModel::getParticleMesh)) + .def("cleanupModel", &PBD::TetModel::cleanupModel) + .def("getIndexOffset", &PBD::TetModel::getIndexOffset) + .def("initMesh", &PBD::TetModel::initMesh) + .def("updateMeshNormals", &PBD::TetModel::updateMeshNormals) + .def("attachVisMesh", &PBD::TetModel::attachVisMesh) + .def("updateVisMesh", &PBD::TetModel::updateVisMesh) + .def("getRestitutionCoeff", &PBD::TetModel::getRestitutionCoeff) + .def("setRestitutionCoeff", &PBD::TetModel::setRestitutionCoeff) + .def("getFrictionCoeff", &PBD::TetModel::getFrictionCoeff) + .def("setFrictionCoeff", &PBD::TetModel::setFrictionCoeff); + + py::bind_pointer_vector>(m_sub, "VecTriangleModels"); + py::bind_pointer_vector>(m_sub, "VecTetModels"); + py::bind_pointer_vector>(m_sub, "VecRigidBodies"); + py::bind_vector>(m_sub, "VecConstraints"); + + py::class_(m_sub, "SimulationModel") + .def(py::init<>()) + .def("init", &PBD::SimulationModel::init) + .def("reset", &PBD::SimulationModel::reset) + .def("cleanup", &PBD::SimulationModel::cleanup) + .def("resetContacts", &PBD::SimulationModel::resetContacts) + .def("updateConstraints", &PBD::SimulationModel::updateConstraints) + .def("initConstraintGroups", &PBD::SimulationModel::initConstraintGroups) + + .def("addTriangleModel", []( + PBD::SimulationModel &model, + const unsigned int nPoints, + const unsigned int nFaces, + std::vector &points, + std::vector &indices, + const PBD::TriangleModel::ParticleMesh::UVIndices& uvIndices, + const PBD::TriangleModel::ParticleMesh::UVs& uvs) + { + model.addTriangleModel(nPoints, nFaces, points.data(), indices.data(), uvIndices, uvs); + }) + .def("addTriangleModelCollision", []( + PBD::SimulationModel& model, + const unsigned int nPoints, + const unsigned int nFaces, + std::vector& points, + std::vector& indices, + const PBD::TriangleModel::ParticleMesh::UVIndices& uvIndices, + const PBD::TriangleModel::ParticleMesh::UVs& uvs) + { + auto& triModels = model.getTriangleModels(); + int i = triModels.size(); + model.addTriangleModel(nPoints, nFaces, points.data(), indices.data(), uvIndices, uvs); + PBD::ParticleData& pd = model.getParticles(); + const unsigned int nVert = triModels[i]->getParticleMesh().numVertices(); + unsigned int offset = triModels[i]->getIndexOffset(); + PBD::Simulation* sim = PBD::Simulation::getCurrent(); + PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); + if (cd != nullptr) + cd->addCollisionObjectWithoutGeometry(i, PBD::CollisionDetection::CollisionObject::TriangleModelCollisionObjectType, &pd.getPosition(offset), nVert, true); + }) + .def("addRegularTriangleModel", &PBD::SimulationModel::addRegularTriangleModel) + .def("addRegularTriangleModelCollision", [](PBD::SimulationModel &model, + const int width, const int height, + const Vector3r& translation, + const Matrix3r& rotation, + const Vector2r& scale) + { + auto &triModels = model.getTriangleModels(); + int i = triModels.size(); + model.addRegularTriangleModel(width, height, translation, rotation, scale); + PBD::ParticleData& pd = model.getParticles(); + const unsigned int nVert = triModels[i]->getParticleMesh().numVertices(); + unsigned int offset = triModels[i]->getIndexOffset(); + PBD::Simulation* sim = PBD::Simulation::getCurrent(); + PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); + if (cd != nullptr) + cd->addCollisionObjectWithoutGeometry(i, PBD::CollisionDetection::CollisionObject::TriangleModelCollisionObjectType, &pd.getPosition(offset), nVert, true); + }) + .def("addTetModel", []( + PBD::SimulationModel& model, + const unsigned int nPoints, + const unsigned int nTets, + std::vector& points, + std::vector& indices) + { + model.addTetModel(nPoints, nTets, points.data(), indices.data()); + }) + .def("addTetModelCollision", []( + PBD::SimulationModel& model, + const unsigned int nPoints, + const unsigned int nTets, + std::vector& points, + std::vector& indices) + { + auto& tetModels = model.getTetModels(); + int i = tetModels.size(); + model.addTetModel(nPoints, nTets, points.data(), indices.data()); + PBD::ParticleData& pd = model.getParticles(); + const unsigned int nVert = tetModels[i]->getParticleMesh().numVertices(); + unsigned int offset = tetModels[i]->getIndexOffset(); + PBD::Simulation* sim = PBD::Simulation::getCurrent(); + PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); + if (cd != nullptr) + cd->addCollisionObjectWithoutGeometry(i, PBD::CollisionDetection::CollisionObject::TetModelCollisionObjectType, &pd.getPosition(offset), nVert, true); + }) + .def("addRegularTetModel", &PBD::SimulationModel::addRegularTetModel) + .def("addRegularTetModelCollision", [](PBD::SimulationModel &model, + const int width, const int height, const int depth, + const Vector3r& translation, + const Matrix3r& rotation, + const Vector3r& scale) + { + auto &tetModels = model.getTetModels(); + int i = tetModels.size(); + model.addRegularTetModel(width, height, depth, translation, rotation, scale); + PBD::ParticleData& pd = model.getParticles(); + const unsigned int nVert = tetModels[i]->getParticleMesh().numVertices(); + unsigned int offset = tetModels[i]->getIndexOffset(); + PBD::Simulation* sim = PBD::Simulation::getCurrent(); + PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); + if (cd != nullptr) + cd->addCollisionObjectWithoutGeometry(i, PBD::CollisionDetection::CollisionObject::TetModelCollisionObjectType, &pd.getPosition(offset), nVert, true); + }) + .def("addLineModel", []( + PBD::SimulationModel& model, + const unsigned int nPoints, + const unsigned int nQuaternions, + std::vector& points, + std::vector& quaternions, + std::vector& indices, + std::vector& indicesQuaternions) + { + model.addLineModel(nPoints, nQuaternions, points.data(), quaternions.data(), indices.data(), indicesQuaternions.data()); + }) + .def("addBallJoint", &PBD::SimulationModel::addBallJoint) + .def("addBallOnLineJoint", &PBD::SimulationModel::addBallOnLineJoint) + .def("addHingeJoint", &PBD::SimulationModel::addHingeJoint) + .def("addTargetAngleMotorHingeJoint", &PBD::SimulationModel::addTargetAngleMotorHingeJoint) + .def("addTargetVelocityMotorHingeJoint", &PBD::SimulationModel::addTargetVelocityMotorHingeJoint) + .def("addUniversalJoint", &PBD::SimulationModel::addUniversalJoint) + .def("addSliderJoint", &PBD::SimulationModel::addSliderJoint) + .def("addTargetPositionMotorSliderJoint", &PBD::SimulationModel::addTargetPositionMotorSliderJoint) + .def("addTargetVelocityMotorSliderJoint", &PBD::SimulationModel::addTargetVelocityMotorSliderJoint) + .def("addRigidBodyParticleBallJoint", &PBD::SimulationModel::addRigidBodyParticleBallJoint) + .def("addRigidBodySpring", &PBD::SimulationModel::addRigidBodySpring) + .def("addDistanceJoint", &PBD::SimulationModel::addDistanceJoint) + .def("addDamperJoint", &PBD::SimulationModel::addDamperJoint) + .def("addRigidBodyContactConstraint", &PBD::SimulationModel::addRigidBodyContactConstraint) + .def("addParticleRigidBodyContactConstraint", &PBD::SimulationModel::addParticleRigidBodyContactConstraint) + .def("addParticleSolidContactConstraint", &PBD::SimulationModel::addParticleSolidContactConstraint) + .def("addDistanceConstraint", &PBD::SimulationModel::addDistanceConstraint) + .def("addDistanceConstraint_XPBD", &PBD::SimulationModel::addDistanceConstraint_XPBD) + .def("addDihedralConstraint", &PBD::SimulationModel::addDihedralConstraint) + .def("addIsometricBendingConstraint", &PBD::SimulationModel::addIsometricBendingConstraint) + .def("addIsometricBendingConstraint_XPBD", &PBD::SimulationModel::addIsometricBendingConstraint_XPBD) + .def("addFEMTriangleConstraint", &PBD::SimulationModel::addFEMTriangleConstraint) + .def("addStrainTriangleConstraint", &PBD::SimulationModel::addStrainTriangleConstraint) + .def("addVolumeConstraint", &PBD::SimulationModel::addVolumeConstraint) + .def("addVolumeConstraint_XPBD", &PBD::SimulationModel::addVolumeConstraint_XPBD) + .def("addFEMTetConstraint", &PBD::SimulationModel::addFEMTetConstraint) + .def("addStrainTetConstraint", &PBD::SimulationModel::addStrainTetConstraint) + .def("addShapeMatchingConstraint", []( + PBD::SimulationModel& model, + const unsigned int numberOfParticles, + const std::vector& particleIndices, + const std::vector& numClusters, + const Real stiffness) + { + model.addShapeMatchingConstraint(numberOfParticles, particleIndices.data(), numClusters.data(), stiffness); + }) + + + .def("addStretchShearConstraint", &PBD::SimulationModel::addStretchShearConstraint) + .def("addBendTwistConstraint", &PBD::SimulationModel::addBendTwistConstraint) + .def("addStretchBendingTwistingConstraint", &PBD::SimulationModel::addStretchBendingTwistingConstraint) + .def("addDirectPositionBasedSolverForStiffRodsConstraint", &PBD::SimulationModel::addDirectPositionBasedSolverForStiffRodsConstraint) + + .def("getParticles", &PBD::SimulationModel::getParticles, py::return_value_policy::reference) + .def("getRigidBodies", &PBD::SimulationModel::getRigidBodies, py::return_value_policy::reference) + .def("getTriangleModels", &PBD::SimulationModel::getTriangleModels, py::return_value_policy::reference) + .def("getTetModels", &PBD::SimulationModel::getTetModels, py::return_value_policy::reference) + .def("getLineModels", &PBD::SimulationModel::getLineModels, py::return_value_policy::reference) + .def("getConstraints", &PBD::SimulationModel::getConstraints, py::return_value_policy::reference) + .def("getOrientations", &PBD::SimulationModel::getOrientations, py::return_value_policy::reference) + .def("getRigidBodyContactConstraints", &PBD::SimulationModel::getRigidBodyContactConstraints, py::return_value_policy::reference) + .def("getParticleRigidBodyContactConstraints", &PBD::SimulationModel::getParticleRigidBodyContactConstraints, py::return_value_policy::reference) + .def("getParticleSolidContactConstraints", &PBD::SimulationModel::getParticleSolidContactConstraints, py::return_value_policy::reference) + .def("getConstraintGroups", &PBD::SimulationModel::getConstraintGroups, py::return_value_policy::reference) + .def("resetContacts", &PBD::SimulationModel::resetContacts) + + .def("addClothConstraints", &PBD::SimulationModel::addClothConstraints) + .def("addBendingConstraints", &PBD::SimulationModel::addBendingConstraints) + .def("addSolidConstraints", &PBD::SimulationModel::addSolidConstraints) + + .def("getContactStiffnessRigidBody", &PBD::SimulationModel::getContactStiffnessRigidBody) + .def("setContactStiffnessRigidBody", &PBD::SimulationModel::setContactStiffnessRigidBody) + .def("getContactStiffnessParticleRigidBody", &PBD::SimulationModel::getContactStiffnessParticleRigidBody) + .def("setContactStiffnessParticleRigidBody", &PBD::SimulationModel::setContactStiffnessParticleRigidBody) + + .def("addRigidBody", [](PBD::SimulationModel &model, const Real density, + const PBD::VertexData& vertices, + const Utilities::IndexedFaceMesh& mesh, + const Vector3r& x, const Real angle, const Vector3r &rotationAxis, + const Vector3r& scale, + const PBD::CubicSDFCollisionDetection::GridPtr sdf) + { + PBD::SimulationModel::RigidBodyVector& rbs = model.getRigidBodies(); + PBD::RigidBody *rb = new PBD::RigidBody(); + rb->initBody(density, x, Quaternionr(AngleAxisr(angle, rotationAxis)), vertices, mesh, scale); + rbs.push_back(rb); + if (sdf != nullptr) + { + PBD::Simulation* sim = PBD::Simulation::getCurrent(); + PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); + if (cd != nullptr) + { + const std::vector* vertices = rb->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert = static_cast(vertices->size()); + cd->addCubicSDFCollisionObject(rbs.size() - 1, + PBD::CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, + vertices->data(), nVert, sdf, scale, true, false); + } + } + return rb; + }, py::return_value_policy::reference) + ; + +} \ No newline at end of file diff --git a/pyPBD/SimulationModule.cpp b/pyPBD/SimulationModule.cpp new file mode 100644 index 00000000..5c56c015 --- /dev/null +++ b/pyPBD/SimulationModule.cpp @@ -0,0 +1,34 @@ +#include "common.h" + +#include +#include + +#include + +namespace py = pybind11; + +void SimulationModule(py::module m_sub) { + // --------------------------------------- + // Class Simulation + // --------------------------------------- + py::class_(m_sub, "Simulation") + .def(py::init<>()) + .def_static("getCurrent", &PBD::Simulation::getCurrent, py::return_value_policy::reference) + .def_static("setCurrent", &PBD::Simulation::setCurrent) + .def_static("hasCurrent", &PBD::Simulation::hasCurrent) + + .def("init", &PBD::Simulation::init) + .def("reset", &PBD::Simulation::reset) + .def("getModel", &PBD::Simulation::getModel, py::return_value_policy::reference_internal) + .def("setModel", &PBD::Simulation::setModel) + .def("getTimeStep", &PBD::Simulation::getTimeStep, py::return_value_policy::reference_internal) + .def("setTimeStep", &PBD::Simulation::setTimeStep) + .def("initDefault", [](PBD::Simulation &sim) + { + sim.setModel(new PBD::SimulationModel()); + sim.getModel()->init(); + PBD::CubicSDFCollisionDetection* cd = new PBD::CubicSDFCollisionDetection(); + sim.getTimeStep()->setCollisionDetection(*sim.getModel(), cd); + }) + ; +} \ No newline at end of file diff --git a/pyPBD/TimeModule.cpp b/pyPBD/TimeModule.cpp new file mode 100644 index 00000000..b8daefa7 --- /dev/null +++ b/pyPBD/TimeModule.cpp @@ -0,0 +1,23 @@ +#include "common.h" + +#include + +#include + +namespace py = pybind11; + +void TimeModule(py::module m_sub) { + // --------------------------------------- + // Class Time Manager + // --------------------------------------- + py::class_(m_sub, "TimeManager") + .def(py::init<>()) + .def_static("getCurrent", &PBD::TimeManager::getCurrent, py::return_value_policy::reference) + .def_static("setCurrent", &PBD::TimeManager::setCurrent) + .def_static("hasCurrent", &PBD::TimeManager::hasCurrent) + + .def("getTime", &PBD::TimeManager::getTime) + .def("setTime", &PBD::TimeManager::setTime) + .def("getTimeStepSize", &PBD::TimeManager::getTimeStepSize) + .def("setTimeStepSize", &PBD::TimeManager::setTimeStepSize); +} \ No newline at end of file diff --git a/pyPBD/TimeStepModule.cpp b/pyPBD/TimeStepModule.cpp new file mode 100644 index 00000000..a3375f77 --- /dev/null +++ b/pyPBD/TimeStepModule.cpp @@ -0,0 +1,32 @@ +#include "common.h" + +#include +#include + +#include +#include +#include +#include + +namespace py = pybind11; + +void TimeStepModule(py::module m_sub) +{ + py::class_(m_sub, "TimeStep") + //.def(py::init<>()) + .def("step", &PBD::TimeStep::step) + .def("reset", &PBD::TimeStep::reset) + .def("init", &PBD::TimeStep::init) + .def("setCollisionDetection", &PBD::TimeStep::setCollisionDetection) + .def("getCollisionDetection", &PBD::TimeStep::getCollisionDetection, py::return_value_policy::reference_internal); + + py::class_(m_sub, "TimeStepController") + .def_readwrite_static("NUM_SUB_STEPS", &PBD::TimeStepController::NUM_SUB_STEPS) + .def_readwrite_static("MAX_ITERATIONS", &PBD::TimeStepController::MAX_ITERATIONS) + .def_readwrite_static("MAX_ITERATIONS_V", &PBD::TimeStepController::MAX_ITERATIONS_V) + .def_readwrite_static("VELOCITY_UPDATE_METHOD", &PBD::TimeStepController::VELOCITY_UPDATE_METHOD) + .def_readwrite_static("ENUM_VUPDATE_FIRST_ORDER", &PBD::TimeStepController::ENUM_VUPDATE_FIRST_ORDER) + .def_readwrite_static("ENUM_VUPDATE_SECOND_ORDER", &PBD::TimeStepController::ENUM_VUPDATE_SECOND_ORDER) + + .def(py::init<>()); +} \ No newline at end of file diff --git a/pyPBD/UtilitiesModule.cpp b/pyPBD/UtilitiesModule.cpp new file mode 100644 index 00000000..20995393 --- /dev/null +++ b/pyPBD/UtilitiesModule.cpp @@ -0,0 +1,282 @@ +#include "common.h" + +#include +#include +#include +#include +#include +#include + +namespace py = pybind11; + +template +using overload_cast_ = pybind11::detail::overload_cast_impl; + +void UtilitiesModule(py::module m_sub) +{ + using Faces = Utilities::IndexedFaceMesh::Faces; + using FaceNormals = Utilities::IndexedFaceMesh::FaceNormals; + using VertexNormals = Utilities::IndexedFaceMesh::VertexNormals; + using UVIndices = Utilities::IndexedFaceMesh::UVIndices; + using UVs = Utilities::IndexedFaceMesh::UVs; + + py::class_(m_sub, "IndexedFaceMesh") + .def(py::init<>()) + .def("release", &Utilities::IndexedFaceMesh::release) + .def("isClosed", &Utilities::IndexedFaceMesh::isClosed) + .def("initMesh", &Utilities::IndexedFaceMesh::initMesh) + .def("addFace", overload_cast_()(&Utilities::IndexedFaceMesh::addFace)) + .def("addFace", overload_cast_()(&Utilities::IndexedFaceMesh::addFace)) + .def("addUV", &Utilities::IndexedFaceMesh::addUV) + .def("addUVIndex", &Utilities::IndexedFaceMesh::addUVIndex) + + .def("getFaces", [](Utilities::IndexedFaceMesh& mesh) -> py::memoryview { + const std::vector& faces = mesh.getFaces(); + unsigned int* base_ptr = const_cast(&faces[0]); + return py::memoryview::from_buffer(base_ptr, { (int)faces.size() }, { sizeof(unsigned int) }, true); + }) + .def("getFaceNormals", [](Utilities::IndexedFaceMesh& mesh) -> py::memoryview { + const auto& n = mesh.getFaceNormals(); + Real* base_ptr = const_cast(&n[0][0]); + return py::memoryview::from_buffer(base_ptr, { (int) n.size(), 3 }, { sizeof(Real) * 3, sizeof(Real) }, true); + }) + .def("getVertexNormals", [](Utilities::IndexedFaceMesh& mesh) -> py::memoryview { + const auto& n = mesh.getVertexNormals(); + Real* base_ptr = const_cast(&n[0][0]); + return py::memoryview::from_buffer(base_ptr, { (int) n.size(), 3 }, { sizeof(Real) * 3, sizeof(Real) }, true); + }) + .def("getEdges", (const Utilities::IndexedFaceMesh::Edges & (Utilities::IndexedFaceMesh::*)()const)(&Utilities::IndexedFaceMesh::getEdges)) + // .def("getEdges", (Edges & (Utilities::IndexedFaceMesh::*)())(&Utilities::IndexedFaceMesh::getEdges)) // TODO: wont work by reference + .def("getFacesEdges", (const Utilities::IndexedFaceMesh::FacesEdges & (Utilities::IndexedFaceMesh::*)()const)(&Utilities::IndexedFaceMesh::getFacesEdges)) + .def("getUVIndices", (const UVIndices & (Utilities::IndexedFaceMesh::*)()const)(&Utilities::IndexedFaceMesh::getUVIndices)) + .def("getUVs", (const UVs & (Utilities::IndexedFaceMesh::*)()const)(&Utilities::IndexedFaceMesh::getUVs)) + .def("getVertexFaces", (const Utilities::IndexedFaceMesh::VerticesFaces & (Utilities::IndexedFaceMesh::*)()const)(&Utilities::IndexedFaceMesh::getVertexFaces)) + .def("getVertexEdges", (const Utilities::IndexedFaceMesh::VerticesEdges & (Utilities::IndexedFaceMesh::*)()const)(&Utilities::IndexedFaceMesh::getVertexEdges)) + .def("numVertices", &Utilities::IndexedFaceMesh::numVertices) + .def("numFaces", &Utilities::IndexedFaceMesh::numFaces) + .def("numEdges", &Utilities::IndexedFaceMesh::numEdges) + .def("numUVs", &Utilities::IndexedFaceMesh::numUVs) + .def("copyUVs", &Utilities::IndexedFaceMesh::copyUVs) + .def("getVerticesPerFace", &Utilities::IndexedFaceMesh::getVerticesPerFace) + .def("buildNeighbors", &Utilities::IndexedFaceMesh::buildNeighbors) + .def("updateNormalsVertexData", &Utilities::IndexedFaceMesh::updateNormals) + .def("updateVertexNormalsVertexData", &Utilities::IndexedFaceMesh::updateVertexNormals) + .def("updateNormalsParticleData", &Utilities::IndexedFaceMesh::updateNormals) + .def("updateVertexNormalsParticleData", &Utilities::IndexedFaceMesh::updateVertexNormals); + + py::class_(m_sub, "IndexedFaceMeshEdge") + .def(py::init<>()) + .def_readwrite("m_face", &Utilities::IndexedFaceMesh::Edge::m_face) + .def_readwrite("m_vert", &Utilities::IndexedFaceMesh::Edge::m_vert); + + py::class_(m_sub, "IndexedTetMesh") + .def(py::init<>()) + .def("release", &Utilities::IndexedTetMesh::release) + .def("initMesh", &Utilities::IndexedTetMesh::initMesh) + .def("addTet", overload_cast_()(&Utilities::IndexedTetMesh::addTet)) + .def("addTet", overload_cast_()(&Utilities::IndexedTetMesh::addTet)) + + .def("getFaces", [](Utilities::IndexedTetMesh& mesh) -> py::memoryview { + const std::vector& faces = mesh.getFaces(); + unsigned int* base_ptr = const_cast(&faces[0]); + return py::memoryview::from_buffer(base_ptr, { (int)faces.size() }, { sizeof(unsigned int) }, true); + }) + .def("getTets", [](Utilities::IndexedTetMesh& mesh) -> py::memoryview { + const std::vector& tets = mesh.getTets(); + unsigned int* base_ptr = const_cast(&tets[0]); + return py::memoryview::from_buffer(base_ptr, { (int)tets.size() }, { sizeof(unsigned int) }, true); + }) + .def("getEdges", (const Utilities::IndexedTetMesh::Edges& (Utilities::IndexedTetMesh::*)()const)(&Utilities::IndexedTetMesh::getEdges)) + .def("getFaceData", (const Utilities::IndexedTetMesh::FaceData& (Utilities::IndexedTetMesh::*)()const)(&Utilities::IndexedTetMesh::getFaceData)) + .def("getTetData", (const Utilities::IndexedTetMesh::TetData& (Utilities::IndexedTetMesh::*)()const)(&Utilities::IndexedTetMesh::getTetData)) + .def("getVertexTets", (const Utilities::IndexedTetMesh::VerticesTets& (Utilities::IndexedTetMesh::*)()const)(&Utilities::IndexedTetMesh::getVertexTets)) + .def("getVertexFaces", (const Utilities::IndexedTetMesh::VerticesFaces & (Utilities::IndexedTetMesh::*)()const)(&Utilities::IndexedTetMesh::getVertexFaces)) + .def("getVertexEdges", (const Utilities::IndexedTetMesh::VerticesEdges& (Utilities::IndexedTetMesh::*)()const)(&Utilities::IndexedTetMesh::getVertexEdges)) + .def("numVertices", &Utilities::IndexedTetMesh::numVertices) + .def("numFaces", &Utilities::IndexedTetMesh::numFaces) + .def("numTets", &Utilities::IndexedTetMesh::numTets) + .def("numEdges", &Utilities::IndexedTetMesh::numEdges) + .def("buildNeighbors", &Utilities::IndexedTetMesh::buildNeighbors); + + py::class_(m_sub, "IndexedTetMeshEdge") + .def(py::init<>()) + .def_readwrite("m_vert", &Utilities::IndexedTetMesh::Edge::m_vert); + + py::class_(m_sub, "IndexedTetMeshFace") + .def(py::init<>()) + .def_readwrite("m_tets", &Utilities::IndexedTetMesh::Face::m_tets) + .def_readwrite("m_edges", &Utilities::IndexedTetMesh::Face::m_edges); + + py::class_(m_sub, "IndexedTetMeshTet") + .def(py::init<>()) + .def_readwrite("m_faces", &Utilities::IndexedTetMesh::Tet::m_faces) + .def_readwrite("m_edges", &Utilities::IndexedTetMesh::Tet::m_edges); + + py::class_(m_sub, "MeshFaceIndices") + .def(py::init<>()) + .def_readwrite("posIndices", &Utilities::MeshFaceIndices::posIndices) + .def_readwrite("texIndices", &Utilities::MeshFaceIndices::texIndices) + .def_readwrite("normalIndices", &Utilities::MeshFaceIndices::normalIndices); + + py::class_(m_sub, "OBJLoader") + .def(py::init<>()) + .def("loadObj", []( + const std::string& filename, + const Utilities::OBJLoader::Vec3f& scale) + { + std::vector x; + std::vector normals; + std::vector texCoords; + std::vector faces; + Utilities::OBJLoader::loadObj(filename, &x, &faces, &normals, &texCoords, scale); + return py::make_tuple(x, normals, texCoords, faces); + }) + .def("loadObjToMesh", [](const std::string& filename, const Vector3r& scale) + { + PBD::VertexData vd; + Utilities::IndexedFaceMesh mesh; + std::vector x; + std::vector normals; + std::vector texCoords; + std::vector faces; + Utilities::OBJLoader::Vec3f s = { (float)scale[0], (float)scale[1], (float)scale[2] }; + Utilities::OBJLoader::loadObj(filename, &x, &faces, &normals, &texCoords, s); + + mesh.release(); + const unsigned int nPoints = (unsigned int)x.size(); + const unsigned int nFaces = (unsigned int)faces.size(); + const unsigned int nTexCoords = (unsigned int)texCoords.size(); + mesh.initMesh(nPoints, nFaces * 2, nFaces); + vd.reserve(nPoints); + for (unsigned int i = 0; i < nPoints; i++) + { + vd.addVertex(Vector3r(x[i][0], x[i][1], x[i][2])); + } + for (unsigned int i = 0; i < nTexCoords; i++) + { + mesh.addUV(texCoords[i][0], texCoords[i][1]); + } + for (unsigned int i = 0; i < nFaces; i++) + { + // Reduce the indices by one + int posIndices[3]; + int texIndices[3]; + for (int j = 0; j < 3; j++) + { + posIndices[j] = faces[i].posIndices[j] - 1; + if (nTexCoords > 0) + { + texIndices[j] = faces[i].texIndices[j] - 1; + mesh.addUVIndex(texIndices[j]); + } + } + + mesh.addFace(&posIndices[0]); + } + mesh.buildNeighbors(); + + mesh.updateNormals(vd, 0); + mesh.updateVertexNormals(vd); + return py::make_tuple(vd, mesh); + }); + + py::class_(m_sub, "TetGenLoader") + .def(py::init<>()) + .def("loadTetFile", []( + const std::string& filename) + { + std::vector x; + std::vector tets; + Utilities::TetGenLoader::loadTetFile(filename, x, tets); + return py::make_tuple(x, tets); + }) + .def("loadTetgenModel", []( + const std::string& nodeFilename, const std::string& eleFilename) + { + std::vector x; + std::vector tets; + Utilities::TetGenLoader::loadTetgenModel(nodeFilename, eleFilename, x, tets); + return py::make_tuple(x, tets); + }); + + // --------------------------------------- + // Logger + // --------------------------------------- + py::enum_(m_sub, "LogLevel") + .value("DEBUG", Utilities::LogLevel::DEBUG) + .value("INFO", Utilities::LogLevel::INFO) + .value("WARN", Utilities::LogLevel::WARN) + .value("ERR", Utilities::LogLevel::ERR); + + py::class_(m_sub, "ConsoleSink") + .def(py::init<>([](const Utilities::LogLevel minLevel) { + return Utilities::ConsoleSink(minLevel); + })) + .def("write", &Utilities::ConsoleSink::write); + + // TODO: check if it is okay to use shared pointer and implement the actual logger functions + py::class_>(m_sub, "FileSink") + .def(py::init<>([](const Utilities::LogLevel minLevel, const std::string& fileName) { + return std::make_shared(minLevel, fileName); + })) + .def("write", &Utilities::FileSink::write); + + py::class_(m_sub, "Logger") + .def(py::init<>()) + .def("addConsoleSink", [](const Utilities::LogLevel &level) + { + Utilities::logger.addSink(std::unique_ptr(new Utilities::ConsoleSink(level))); + }) + .def("addFileSink", [](const Utilities::LogLevel& level, const std::string &fileName) + { + Utilities::logger.addSink(std::unique_ptr(new Utilities::FileSink(level, fileName))); + }) + .def("write", &Utilities::Logger::write); + + // --------------------------------------- + // Timing + // --------------------------------------- + // TODO: Timing and find a way for everything to be actually printed + py::class_(m_sub, "TimingHelper") + .def(py::init<>()) + .def_readwrite("start", &Utilities::TimingHelper::start) + .def_readwrite("name", &Utilities::TimingHelper::name); + + py::class_(m_sub, "AverageTime") + .def(py::init<>()) + .def_readwrite("totalTime", &Utilities::AverageTime::totalTime) + .def_readwrite("counter", &Utilities::AverageTime::counter) + .def_readwrite("name", &Utilities::AverageTime::name); + + py::class_(m_sub, "IDFactory") + .def(py::init<>()) + .def_static("getId", &Utilities::IDFactory::getId); + + py::class_(m_sub, "Timing") + .def(py::init<>()) + .def_readwrite_static("m_dontPrintTimes", &Utilities::Timing::m_dontPrintTimes) + .def_readwrite_static("m_startCounter", &Utilities::Timing::m_startCounter) + .def_readwrite_static("m_stopCounter", &Utilities::Timing::m_stopCounter) + .def_readwrite_static("m_timingStack", &Utilities::Timing::m_timingStack) + .def_readwrite_static("m_averageTimes", &Utilities::Timing::m_averageTimes) + .def_static("reset", &Utilities::Timing::reset) + .def_static("startTiming", &Utilities::Timing::startTiming) + .def_static("stopTimingPrint", []() + { + static int timing_timerId = -1; + Utilities::Timing::stopTiming(true); + }) + .def_static("stopTimingAvgPrint", []() + { + static int timing_timerId = -1; + Utilities::Timing::stopTiming(true, timing_timerId); + }) + .def_static("stopTimingAvg", []() + { + static int timing_timerId = -1; + Utilities::Timing::stopTiming(false, timing_timerId); + }) + .def_static("printAverageTimes", &Utilities::Timing::printAverageTimes) + .def_static("printTimeSums", &Utilities::Timing::printTimeSums); + +} diff --git a/pyPBD/bind_pointer_vector.h b/pyPBD/bind_pointer_vector.h new file mode 100644 index 00000000..1cafaaaa --- /dev/null +++ b/pyPBD/bind_pointer_vector.h @@ -0,0 +1,252 @@ +// +// Created by sjeske on 2/11/20. +// + +#pragma once + +#include +#include + +#include +#include +#include + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +// Vector modifiers -- requires a copyable vector_type: +// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems +// silly to allow deletion but not insertion, so include them here too.) +template +void vector_pointer_modifiers(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using TNoPtr = typename std::remove_pointer::type; + using SizeType = typename Vector::size_type; + using DiffType = typename Vector::difference_type; + + auto wrap_i = [](DiffType i, SizeType n) { + if (i < 0) + i += n; + if (i < 0 || (SizeType)i >= n) + throw index_error(); + return i; + }; + + cl.def("append", + [](Vector &v, const T &value) { + v.push_back(new TNoPtr(*value)); + }, + arg("x"), + "Add an item to the end of the list"); + + cl.def(init([](iterable it) { + auto v = std::unique_ptr(new Vector()); + v->reserve(len_hint(it)); + for (handle h : it) + v->push_back(new TNoPtr(*h.cast())); + return v.release(); + })); + + cl.def("clear", + [](Vector &v) { + for(const auto & elem : v) + delete elem; + v.clear(); + }, + "Clear the contents" + ); + + cl.def("extend", + [](Vector &v, const Vector &src) { + for(const auto & elem : src) + v.push_back(new TNoPtr(*elem)); + }, + arg("L"), + "Extend the list by appending all the items in the given list" + ); + + cl.def("extend", + [](Vector &v, iterable it) { + const size_t old_size = v.size(); + v.reserve(old_size + len_hint(it)); + try { + for (handle h : it) { + v.push_back(new TNoPtr(*h.cast())); + } + } catch (const cast_error &) { + for(auto it = v.begin() + static_cast(old_size); it != v.end(); it++) + delete *it; + v.erase(v.begin() + static_cast(old_size), v.end()); + try { + v.shrink_to_fit(); + } catch (const std::exception &) { + // Do nothing + } + throw; + } + }, + arg("L"), + "Extend the list by appending all the items in the given list" + ); + + cl.def("insert", + [](Vector &v, DiffType i, const T &x) { + // Can't use wrap_i; i == v.size() is OK + if (i < 0) + i += v.size(); + if (i < 0 || (SizeType)i > v.size()) + throw index_error(); + v.insert(v.begin() + i, new TNoPtr(*x)); + }, + arg("i") , arg("x"), + "Insert an item at a given position." + ); + + cl.def("pop", + [](Vector &v) { + if (v.empty()) + throw index_error(); + T t = new TNoPtr(*v.back()); + delete v.back(); + v.pop_back(); + return t; + }, + "Remove and return the last item" + ); + + cl.def("pop", + [wrap_i](Vector &v, DiffType i) { + i = wrap_i(i, v.size()); + T t = new TNoPtr(*v[(SizeType) i]); + delete v[(SizeType) i]; + v.erase(v.begin() + i); + return t; + }, + arg("i"), + "Remove and return the item at index ``i``" + ); + + cl.def("__setitem__", + [wrap_i](Vector &v, DiffType i, const T &t) { + i = wrap_i(i, v.size()); + *v[(SizeType)i] = *t; + } + ); + + /// Slicing protocol + cl.def("__getitem__", + [](const Vector &v, slice slice) -> Vector * { + size_t start, stop, step, slicelength; + + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + Vector *seq = new Vector(); + seq->reserve((size_t) slicelength); + + for (size_t i=0; ipush_back(v[start]); // TODO: Memory management still taken care of by C++ + start += step; + } + return seq; + }, + arg("s"), + "Retrieve list elements using a slice object" + ); + + cl.def("__setitem__", + [](Vector &v, slice slice, const Vector &value) { + size_t start, stop, step, slicelength; + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + if (slicelength != value.size()) + throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); + + for (size_t i=0; i, typename... Args> +class_ bind_pointer_vector(handle scope, std::string const &name, Args&&... args) { + using Class_ = class_; + + // If the value_type is unregistered (e.g. a converting type) or is itself registered + // module-local then make the vector binding module-local as well: + using vtype = typename Vector::value_type; + auto vtype_info = detail::get_type_info(typeid(vtype)); + bool local = !vtype_info || vtype_info->module_local; + + Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); + + // Declare the buffer interface if a buffer_protocol() is passed in + detail::vector_buffer(cl); + + cl.def(init<>()); + + // Register copy constructor (if possible) + detail::vector_if_copy_constructible(cl); + + // Register comparison-related operators and functions (if possible) + detail::vector_if_equal_operator(cl); + + // Register stream insertion operator (if possible) + detail::vector_if_insertion_operator(cl, name); + + // Modifiers require copyable vector value type + detail::vector_pointer_modifiers(cl); + + // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive + detail::vector_accessor(cl); + + cl.def("__bool__", + [](const Vector &v) -> bool { + return !v.empty(); + }, + "Check whether the list is nonempty" + ); + + cl.def("__len__", &Vector::size); + + return cl; + } + +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/pyPBD/common.h b/pyPBD/common.h new file mode 100644 index 00000000..a09d445e --- /dev/null +++ b/pyPBD/common.h @@ -0,0 +1,18 @@ +#ifndef PBD_COMMON_H +#define PBD_COMMON_H + +#include +#include +#include +#include + +#include "Common/Common.h" +#include "Simulation/SimulationModel.h" + +//PYBIND11_MAKE_OPAQUE(std::vector) +PYBIND11_MAKE_OPAQUE(std::vector) +PYBIND11_MAKE_OPAQUE(std::vector) +PYBIND11_MAKE_OPAQUE(std::vector) +PYBIND11_MAKE_OPAQUE(std::vector) + +#endif //PBD_COMMON_H diff --git a/pyPBD/examples/armadillo.py b/pyPBD/examples/armadillo.py new file mode 100644 index 00000000..45959f9f --- /dev/null +++ b/pyPBD/examples/armadillo.py @@ -0,0 +1,147 @@ +import pygame +from pygame.locals import * + +from OpenGL.GL import * +from OpenGL.GLU import * + +from render_tools import * +from math_tools import * + +import pypbd as pbd +import numpy as np + + +# 1 = distance constraints (PBD) +# 2 = FEM tet constraints (PBD) +# 3 = strain tet constraints (PBD) +# 4 = shape matching +# 5 = distance constraints (XPBD) +simModel = 2 + + +def buildModel(): + sim = pbd.Simulation.getCurrent() + sim.initDefault() + model = sim.getModel() + + rbs = model.getRigidBodies() + + createMesh(simModel) + + fileName2 = "../../data/models/torus.obj" + (vdTorus, meshTorus) = pbd.OBJLoader.loadObjToMesh(fileName2, [1, 1, 1]) + + # torus + cd = sim.getTimeStep().getCollisionDetection() + torusSDF = pbd.CubicSDFCollisionDetection.generateSDF(vdTorus, meshTorus, [30, 30, 30]) + rb = model.addRigidBody(1.0, # density + vdTorus, meshTorus, # vertices, mesh + [0.0, 1.5, 0.0], # position + 0.0, [1,0,0], # angle, rotation axis + [2.0, 2.0, 2.0], # scaling + torusSDF) # signed distance field for collision detection + rb.setMass(0.0) + rb.setFrictionCoeff(0.3) + + cd.setTolerance(0.05) + + ts = sim.getTimeStep() + ts.setValueUInt(pbd.TimeStepController.NUM_SUB_STEPS, 5) + + +# Create a particle model mesh +def createMesh(simModel): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + + fileNameNode = "../../data/models/armadillo_4k.node" + fileNameEle = "../../data/models/armadillo_4k.ele" + (positions, tets) = pbd.TetGenLoader.loadTetgenModel(fileNameNode, fileNameEle) + + # move up and scale + pos = np.array(positions) + for x in pos: + x[0] *= 1.5 + x[1] = x[1]*1.5 + 6 + x[2] *= 1.5 + model.addTetModelCollision(len(pos), int(len(tets)/4), pos, tets) + + # init constraints + tetModel = model.getTetModels()[0] + stiffness = 1.0 + if (simModel == 5): + stiffness = 100000 + poissonRatio = 0.3 + model.addSolidConstraints(tetModel, simModel, stiffness, poissonRatio, stiffness, False, False) + + print("Number of triangles: " + str(tetModel.getParticleMesh().numTets())) + print("Number of vertices: " + str(len(pos))) + +def render(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + + rbs = model.getRigidBodies() + + for rb in rbs: + vd = rb.getGeometry().getVertexData() + mesh = rb.getGeometry().getMesh() + drawMesh(vd, mesh, 0, [0,0.2,0.7]) + + pd = model.getParticles() + tetModel = model.getTetModels()[0] + offset = tetModel.getIndexOffset() + drawMesh(pd, tetModel.getSurfaceMesh(), offset, [0,0.2,0.7]) + + glPushMatrix() + glLoadIdentity() + drawText([-0.95,0.9], "Time: {:.2f}".format(pbd.TimeManager.getCurrent().getTime())) + glPopMatrix() + +def timeStep(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + for i in range(8): + sim.getTimeStep().step(model) + + for tetModel in model.getTetModels(): + tetModel.updateMeshNormals(model.getParticles()) + +def reset(): + pbd.Simulation.getCurrent().reset() + pbd.Simulation.getCurrent().getModel().cleanup() + cd.cleanup() + buildModel() + +def main(): + pbd.Logger.addConsoleSink(pbd.LogLevel.INFO) + + initGL(1280, 1024) + #initLights() + gluLookAt (0, 10, -20, 0, 0, 0, 0, 1, 0) + glRotatef(20.0, 0, 1, 0) + + buildModel() + + running = True + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_r: + reset() + if event.key == pygame.K_ESCAPE: + running = False + + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) + glClearColor(0.3, 0.3, 0.3, 1.0) + timeStep() + render() + pygame.display.flip() + + pygame.quit() + pbd.Timing.printAverageTimes() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/pyPBD/examples/beam_model.py b/pyPBD/examples/beam_model.py new file mode 100644 index 00000000..a074d65e --- /dev/null +++ b/pyPBD/examples/beam_model.py @@ -0,0 +1,117 @@ +import pygame +from pygame.locals import * + +from OpenGL.GL import * +from OpenGL.GLU import * +from render_tools import * + +import pypbd as pbd +import numpy as np + +width = 30 +depth = 5 +height = 5 + +def buildModel(): + sim = pbd.Simulation.getCurrent() + sim.initDefault() + model = sim.getModel() + + # 1 = distance constraints (PBD) + # 2 = FEM tet constraints (PBD) + # 3 = strain tet constraints (PBD) + # 4 = shape matching + # 5 = distance constraints (XPBD) + simModel = 2 + + createMesh(simModel) + + ts = sim.getTimeStep() + ts.setValueUInt(pbd.TimeStepController.NUM_SUB_STEPS, 3) + + +# Create a particle model mesh +def createMesh(simModel): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + model.addRegularTetModel(width, height, depth, [5,0,0], np.identity(3), [10, 1.5, 1.5]) + + pd = model.getParticles() + + # Set mass of points to zero => make it static + for i in range(1): + for j in range(height): + for k in range(depth): + pd.setMass(i*height*depth + j*depth + k, 0.0) + + # init constraints + tetModels = model.getTetModels() + stiffness = 1.0 + if (simModel == 5): + stiffness = 100000 + poissonRatio = 0.3 + for tetModel in tetModels: + model.addSolidConstraints(tetModel, simModel, stiffness, poissonRatio, stiffness, False, False) + + tetModel.updateMeshNormals(pd); + + print("Number of tets: " + str(tetModel.getParticleMesh().numTets())) + print("Number of vertices: " + str(width*height*depth)) + +def render(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + pd = model.getParticles() + tetModel = model.getTetModels()[0] + offset = tetModel.getIndexOffset() + drawMesh(pd, tetModel.getSurfaceMesh(), offset, [0,0.2,0.7]) + + glPushMatrix() + glLoadIdentity() + drawText([-0.95,0.9], "Time: {:.2f}".format(pbd.TimeManager.getCurrent().getTime())) + glPopMatrix() + +def timeStep(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + for i in range(8): + sim.getTimeStep().step(model) + + for tetModel in model.getTetModels(): + tetModel.updateMeshNormals(model.getParticles()) + +def reset(): + pbd.Simulation.getCurrent().reset() + pbd.Simulation.getCurrent().getModel().cleanup() + buildModel() + +def main(): + pbd.Logger.addConsoleSink(pbd.LogLevel.INFO) + + initGL(1280, 1024) + gluLookAt (5, 4, 15, 5, -1, 0, 0, 1, 0) + + buildModel() + + running = True + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_r: + reset() + if event.key == pygame.K_ESCAPE: + running = False + + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) + glClearColor(0.3, 0.3, 0.3, 1.0) + timeStep() + render() + pygame.display.flip() + + pygame.quit() + pbd.Timing.printAverageTimes() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/pyPBD/examples/bunny_cloth.py b/pyPBD/examples/bunny_cloth.py new file mode 100644 index 00000000..d033f344 --- /dev/null +++ b/pyPBD/examples/bunny_cloth.py @@ -0,0 +1,175 @@ +import pygame +from pygame.locals import * + +from OpenGL.GL import * +from OpenGL.GLU import * + +from render_tools import * +from math_tools import * + +import pypbd as pbd +import numpy as np + +nRows = 50 +nCols = 50 +width = 10.0 +height = 10.0 + +# 1 = distance constraints (PBD) +# 2 = FEM triangle constraints (PBD) +# 3 = strain triangle constraints (PBD) +# 4 = distance constraints (XPBD) +simModel = 2 + +# 1 = dihedral angle (PBD) +# 2 = isometric bending (PBD) +# 3 = isometric bending (XPBD) +bendingModel = 3 + +def buildModel(): + sim = pbd.Simulation.getCurrent() + sim.initDefault() + model = sim.getModel() + + rbs = model.getRigidBodies() + + createMesh(simModel, bendingModel) + + fileName = "../../data/models/cube.obj" + (vd, mesh) = pbd.OBJLoader.loadObjToMesh(fileName, [1, 1, 1]) + + fileName2 = "../../data/models/torus.obj" + (vdTorus, meshTorus) = pbd.OBJLoader.loadObjToMesh(fileName2, [1, 1, 1]) + + # floor + floorSDF = pbd.CubicSDFCollisionDetection.generateSDF(vd, mesh, [30, 30, 30]) + rb = model.addRigidBody(1.0, # density + vd, mesh, # vertices, mesh + [0.0, -2.5, 0.0], # position + 0.0, [1,0,0], # angle, rotation axis + [100.0, 1.0, 100.0], # scaling + floorSDF) # signed distance field for collision detection + rb.setMass(0.0) + + # torus + cd = sim.getTimeStep().getCollisionDetection() + torusSDF = pbd.CubicSDFCollisionDetection.generateSDF(vdTorus, meshTorus, [30, 30, 30]) + rb = model.addRigidBody(1.0, # density + vdTorus, meshTorus, # vertices, mesh + [0.0, 1.5, 0.0], # position + 0.0, [1,0,0], # angle, rotation axis + [2.0, 2.0, 2.0], # scaling + torusSDF) # signed distance field for collision detection + rb.setMass(0.0) + rb.setFrictionCoeff(0.1) + + cd.setTolerance(0.05) + + ts = sim.getTimeStep() + ts.setValueUInt(pbd.TimeStepController.NUM_SUB_STEPS, 5) + + +# Create a particle model mesh +def createMesh(simModel, bendingModel): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + + fileName2 = "../../data/models/bunny_10k.obj" + (vd, mesh) = pbd.OBJLoader.loadObjToMesh(fileName2, [5,5,5]) + + # move up + pos = np.array(vd.getVertices()) + for x in pos: + x[1] += 5 + model.addTriangleModelCollision(vd.size(), mesh.numFaces(), pos, np.array(mesh.getFaces()), [], []) + + # Set mass of points to zero => make it static + #pd.setMass(0, 0.0) + #pd.setMass((nRows-1)*nCols, 0.0) + + # init constraints + triModels = model.getTriangleModels() + for triModel in triModels: + stiffness = 1.0 + if (simModel == 4): + stiffness = 100000 + poissonRatio = 0.3 + model.addClothConstraints(triModel, simModel, stiffness, stiffness, stiffness, stiffness, + poissonRatio, poissonRatio, False, False) + + bending_stiffness = 0.5 + if (bendingModel == 3): + bending_stiffness = 1000.0 + model.addBendingConstraints(triModel, bendingModel, bending_stiffness) + + print("Number of triangles: " + str(triModel.getParticleMesh().numFaces())) + print("Number of vertices: " + str(nRows*nCols)) + +def render(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + + rbs = model.getRigidBodies() + + for rb in rbs: + vd = rb.getGeometry().getVertexData() + mesh = rb.getGeometry().getMesh() + drawMesh(vd, mesh, 0, [0,0.2,0.7]) + + pd = model.getParticles() + triModel = model.getTriangleModels()[0] + offset = triModel.getIndexOffset() + drawMesh(pd, triModel.getParticleMesh(), offset, [0,0.3,0.8]) + + glPushMatrix() + glLoadIdentity() + drawText([-0.95,0.9], "Time: {:.2f}".format(pbd.TimeManager.getCurrent().getTime())) + glPopMatrix() + +def timeStep(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + for i in range(8): + sim.getTimeStep().step(model) + + for triModel in model.getTriangleModels(): + triModel.updateMeshNormals(model.getParticles()) + +def reset(): + pbd.Simulation.getCurrent().reset() + pbd.Simulation.getCurrent().getModel().cleanup() + cd.cleanup() + buildModel() + +def main(): + pbd.Logger.addConsoleSink(pbd.LogLevel.INFO) + + initGL(1280, 1024) + #initLights() + gluLookAt (0, 10, -20, 0, 0, 0, 0, 1, 0) + glRotatef(20.0, 0, 1, 0) + + buildModel() + + running = True + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_r: + reset() + if event.key == pygame.K_ESCAPE: + running = False + + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) + glClearColor(0.3, 0.3, 0.3, 1.0) + timeStep() + render() + pygame.display.flip() + + pygame.quit() + pbd.Timing.printAverageTimes() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/pyPBD/examples/chain_model.py b/pyPBD/examples/chain_model.py new file mode 100644 index 00000000..c82167ba --- /dev/null +++ b/pyPBD/examples/chain_model.py @@ -0,0 +1,123 @@ +import pygame +from pygame.locals import * + +from OpenGL.GL import * +from OpenGL.GLU import * +from render_tools import * + +import pypbd as pbd +import numpy as np +import math + +numberOfBodies = 10 +width = 1.0 +height = 0.1 +depth = 0.1 + +def buildModel(): + sim = pbd.Simulation.getCurrent() + sim.initDefault() + + createModel() + + ts = sim.getTimeStep() + ts.setValueUInt(pbd.TimeStepController.NUM_SUB_STEPS, 3) + ts.setValueUInt(pbd.TimeStepController.MAX_ITERATIONS, 1) + +def createModel(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + rbs = model.getRigidBodies() + + fileName = "../../data/models/cube.obj" + (vd, mesh) = pbd.OBJLoader.loadObjToMesh(fileName, [width, height, depth]) + + fileName2 = "../../data/models/bunny_10k.obj" + (vd2, mesh2) = pbd.OBJLoader.loadObjToMesh(fileName2, [2, 2, 2]) + + density = 1.0 + for i in range(numberOfBodies-1): + model.addRigidBody(1.0, # density + vd, mesh, # vertices, mesh + [i*width, 0.0, 0.0], # position + 0.0, [1,0,0], # angle, rotation axis + [1, 1, 1], # scaling + None) # signed distance field for collision detection + + # Make first body static + rbs[0].setMass(0.0) + + # bunny + t = np.array([0.411 + (numberOfBodies - 1.0)*width, -1.776, 0.356]) + model.addRigidBody(density, # density + vd2, mesh2, # vertices, mesh + t, # position + math.pi/6.0, [0,0,1], # angle, rotation axis + [1, 1, 1], # scaling + None) # signed distance field for collision detection + + for i in range(numberOfBodies-1): + model.addBallJoint(i, i + 1, [i*width + 0.5*width, 0.0, 0.0]) + + +def render(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + rbs = model.getRigidBodies() + for rb in rbs: + vd = rb.getGeometry().getVertexData() + mesh = rb.getGeometry().getMesh() + drawMesh(vd, mesh, 0, [0,0.2,0.7]) + + cs = model.getConstraints() + for c in cs: + if (c.getTypeId() == pbd.BallJoint.TYPE_ID): + drawSphere(c.jointInfo[:,2], 0.15, [0.0, 0.6, 0.2]) + + glPushMatrix() + glLoadIdentity() + drawText([-0.95,0.9], "Time: {:.2f}".format(pbd.TimeManager.getCurrent().getTime())) + glPopMatrix() + +def timeStep(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + for i in range(8): + sim.getTimeStep().step(model) + +def reset(): + pbd.Simulation.getCurrent().reset() + pbd.Simulation.getCurrent().getModel().cleanup() + buildModel() + +def main(): + pbd.Logger.addConsoleSink(pbd.LogLevel.INFO) + + initGL(1280, 1024) + initLights() + gluLookAt (1.5, -2, 20, 1.5, -2, 0, 0, 1, 0) + + buildModel() + + running = True + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_r: + reset() + if event.key == pygame.K_ESCAPE: + running = False + + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) + glClearColor(0.3, 0.3, 0.3, 1.0) + timeStep() + render() + pygame.display.flip() + + pygame.quit() + pbd.Timing.printAverageTimes() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/pyPBD/examples/cloth_collision.py b/pyPBD/examples/cloth_collision.py new file mode 100644 index 00000000..af2a3675 --- /dev/null +++ b/pyPBD/examples/cloth_collision.py @@ -0,0 +1,167 @@ +import pygame +from pygame.locals import * + +from OpenGL.GL import * +from OpenGL.GLU import * + +from render_tools import * +from math_tools import * + +import pypbd as pbd +import numpy as np + +nRows = 50 +nCols = 50 +width = 10.0 +height = 10.0 + +# 1 = distance constraints (PBD) +# 2 = FEM triangle constraints (PBD) +# 3 = strain triangle constraints (PBD) +# 4 = distance constraints (XPBD) +simModel = 2 + +# 1 = dihedral angle (PBD) +# 2 = isometric bending (PBD) +# 3 = isometric bending (XPBD) +bendingModel = 2 + +def buildModel(): + sim = pbd.Simulation.getCurrent() + sim.initDefault() + model = sim.getModel() + + rbs = model.getRigidBodies() + + createMesh(simModel, bendingModel) + + fileName = "../../data/models/cube.obj" + (vd, mesh) = pbd.OBJLoader.loadObjToMesh(fileName, [1, 1, 1]) + + fileName2 = "../../data/models/torus.obj" + (vdTorus, meshTorus) = pbd.OBJLoader.loadObjToMesh(fileName2, [1, 1, 1]) + + # floor + floorSDF = pbd.CubicSDFCollisionDetection.generateSDF(vd, mesh, [30, 30, 30]) + rb = model.addRigidBody(1.0, # density + vd, mesh, # vertices, mesh + [0.0, -2.5, 0.0], # position + 0.0, [1,0,0], # angle, rotation axis + [100.0, 1.0, 100.0], # scaling + floorSDF) # signed distance field for collision detection + rb.setMass(0.0) + + # torus + cd = sim.getTimeStep().getCollisionDetection() + torusSDF = pbd.CubicSDFCollisionDetection.generateSDF(vdTorus, meshTorus, [30, 30, 30]) + rb = model.addRigidBody(1.0, # density + vdTorus, meshTorus, # vertices, mesh + [0.0, 1.5, 0.0], # position + 0.0, [1,0,0], # angle, rotation axis + [2.0, 2.0, 2.0], # scaling + torusSDF) # signed distance field for collision detection + rb.setMass(0.0) + rb.setFrictionCoeff(0.1) + + cd.setTolerance(0.05) + + ts = sim.getTimeStep() + ts.setValueUInt(pbd.TimeStepController.NUM_SUB_STEPS, 3) + + +# Create a particle model mesh +def createMesh(simModel, bendingModel): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + model.addRegularTriangleModelCollision(nCols, nRows, [-5.0, 4.0, -5.0], rotation_matrix(math.pi*0.5, [1.0, 0.0, 0.0]), [width, height]) + + # Set mass of points to zero => make it static + #pd.setMass(0, 0.0) + #pd.setMass((nRows-1)*nCols, 0.0) + + # init constraints + triModels = model.getTriangleModels() + for triModel in triModels: + stiffness = 1.0 + if (simModel == 4): + stiffness = 100000 + poissonRatio = 0.3 + model.addClothConstraints(triModel, simModel, stiffness, stiffness, stiffness, stiffness, + poissonRatio, poissonRatio, False, False) + + bending_stiffness = 0.01 + if (bendingModel == 3): + bending_stiffness = 100.0 + model.addBendingConstraints(triModel, bendingModel, bending_stiffness) + + print("Number of triangles: " + str(triModel.getParticleMesh().numFaces())) + print("Number of vertices: " + str(nRows*nCols)) + +def render(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + + rbs = model.getRigidBodies() + + for rb in rbs: + vd = rb.getGeometry().getVertexData() + mesh = rb.getGeometry().getMesh() + drawMesh(vd, mesh, 0, [0,0.2,0.7]) + + pd = model.getParticles() + triModel = model.getTriangleModels()[0] + offset = triModel.getIndexOffset() + drawMesh(pd, triModel.getParticleMesh(), offset, [0,0.3,0.8]) + + glPushMatrix() + glLoadIdentity() + drawText([-0.95,0.9], "Time: {:.2f}".format(pbd.TimeManager.getCurrent().getTime())) + glPopMatrix() + +def timeStep(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + for i in range(8): + sim.getTimeStep().step(model) + + for triModel in model.getTriangleModels(): + triModel.updateMeshNormals(model.getParticles()) + +def reset(): + pbd.Simulation.getCurrent().reset() + pbd.Simulation.getCurrent().getModel().cleanup() + cd.cleanup() + buildModel() + +def main(): + pbd.Logger.addConsoleSink(pbd.LogLevel.INFO) + + initGL(1280, 1024) + #initLights() + gluLookAt (0, 10, -20, 0, 0, 0, 0, 1, 0) + glRotatef(20.0, 0, 1, 0) + + buildModel() + + running = True + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_r: + reset() + if event.key == pygame.K_ESCAPE: + running = False + + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) + glClearColor(0.3, 0.3, 0.3, 1.0) + timeStep() + render() + pygame.display.flip() + + pygame.quit() + pbd.Timing.printAverageTimes() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/pyPBD/examples/cloth_model.py b/pyPBD/examples/cloth_model.py new file mode 100644 index 00000000..32be9f14 --- /dev/null +++ b/pyPBD/examples/cloth_model.py @@ -0,0 +1,126 @@ +import pygame +from pygame.locals import * + +from OpenGL.GL import * +from OpenGL.GLU import * + +from render_tools import * +from math_tools import * + +import pypbd as pbd +import numpy as np + +nRows = 50 +nCols = 50 +width = 10.0 +height = 10.0 + +def buildModel(): + sim = pbd.Simulation.getCurrent() + sim.initDefault() + model = sim.getModel() + + # 1 = distance constraints (PBD) + # 2 = FEM triangle constraints (PBD) + # 3 = strain triangle constraints (PBD) + # 4 = distance constraints (XPBD) + simModel = 2 + + # 1 = dihedral angle (PBD) + # 2 = isometric bending (PBD) + # 3 = isometric bending (XPBD) + bendingModel = 2 + + createMesh(simModel, bendingModel) + + ts = sim.getTimeStep() + ts.setValueUInt(pbd.TimeStepController.NUM_SUB_STEPS, 3) + + +# Create a particle model mesh +def createMesh(simModel, bendingModel): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + model.addRegularTriangleModel(nCols, nRows, [0,0,0], rotation_matrix(math.pi*0.5, [1.0, 0.0, 0.0]), [width, height]) + + # Set mass of points to zero => make it static + pd = model.getParticles() + pd.setMass(0, 0.0) + pd.setMass(nRows-1, 0.0) + + # init constraints + triModels = model.getTriangleModels() + for triModel in triModels: + stiffness = 1.0 + if (simModel == 4): + stiffness = 100000 + poissonRatio = 0.3 + model.addClothConstraints(triModel, simModel, stiffness, stiffness, stiffness, stiffness, + poissonRatio, poissonRatio, False, False) + + bending_stiffness = 0.01 + if (bendingModel == 3): + bending_stiffness = 50.0 + model.addBendingConstraints(triModel, bendingModel, bending_stiffness) + + print("Number of triangles: " + str(triModel.getParticleMesh().numFaces())) + print("Number of vertices: " + str(nRows*nCols)) + +def render(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + pd = model.getParticles() + triModel = model.getTriangleModels()[0] + offset = triModel.getIndexOffset() + drawMesh(pd, triModel.getParticleMesh(), offset, [0,0.3,0.8]) + + glPushMatrix() + glLoadIdentity() + drawText([-0.95,0.9], "Time: {:.2f}".format(pbd.TimeManager.getCurrent().getTime())) + glPopMatrix() + +def timeStep(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + for i in range(8): + sim.getTimeStep().step(model) + + for triModel in model.getTriangleModels(): + triModel.updateMeshNormals(model.getParticles()) + +def reset(): + pbd.Simulation.getCurrent().reset() + pbd.Simulation.getCurrent().getModel().cleanup() + buildModel() + +def main(): + pbd.Logger.addConsoleSink(pbd.LogLevel.INFO) + + initGL(1280, 1024) + gluLookAt (5, 10, 20, 5, -5, 0, 0, 1, 0) + glRotatef(20.0, 0, 1, 0) + + buildModel() + + running = True + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_r: + reset() + if event.key == pygame.K_ESCAPE: + running = False + + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) + glClearColor(0.3, 0.3, 0.3, 1.0) + timeStep() + render() + pygame.display.flip() + + pygame.quit() + pbd.Timing.printAverageTimes() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/pyPBD/examples/math_tools.py b/pyPBD/examples/math_tools.py new file mode 100644 index 00000000..f927f235 --- /dev/null +++ b/pyPBD/examples/math_tools.py @@ -0,0 +1,73 @@ +import math + +###################################################### +# compute rotation matrix +###################################################### +def rotation_matrix(angle, axis): + x = axis[0] + y = axis[1] + z = axis[2] + d = math.sqrt(x*x + y*y + z*z) + if d < 1.0e-6: + print ("Vector of rotation matrix is zero!") + return + x = x/d; + y = y/d; + z = z/d; + + x2 = x*x; + y2 = y*y; + z2 = z*z; + s = math.sin(angle); + c = math.cos(angle); + c1 = 1.0-c; + xyc = x*y*c1; + xzc = x*z*c1; + yzc = y*z*c1; + xs=x*s; + ys=y*s; + zs=z*s; + + return [[c + x2*c1, xyc-zs, xzc+ys], + [xyc+zs, c+y2*c1, yzc-xs], + [xzc-ys, yzc+xs, c+z2*c1]] + +###################################################### +# compute matrix vector product +###################################################### +def matrix_vec_product(A, v): + res = [0,0,0] + for i in range(0,3): + for j in range(0,3): + res[i] += A[i][j] * v[j]; + return res + +###################################################### +# compute cross product +###################################################### +def cross_product(a, b): + res = [0,0,0] + res[0] = a[1]*b[2] - a[2]*b[1]; + res[1] = a[2]*b[0] - a[0]*b[2]; + res[2] = a[0]*b[1] - a[1]*b[0]; + return res + +###################################################### +# scale vector +###################################################### +def scale_vector(v, s): + res = [0,0,0] + res[0] = s*v[0]; + res[1] = s*v[1]; + res[2] = s*v[2]; + return res + +###################################################### +# add vector +###################################################### +def add_vector(v1, v2): + res = [0,0,0] + res[0] = v1[0] + v2[0]; + res[1] = v1[1] + v2[1]; + res[2] = v1[2] + v2[2]; + return res \ No newline at end of file diff --git a/pyPBD/examples/pendulum.py b/pyPBD/examples/pendulum.py new file mode 100644 index 00000000..98545a80 --- /dev/null +++ b/pyPBD/examples/pendulum.py @@ -0,0 +1,97 @@ +import pygame +from pygame.locals import * + +from OpenGL.GL import * +from OpenGL.GLU import * +from render_tools import * + +import pypbd as pbd +import numpy as np + +numParticles = 30 +size = 4.0 + +def buildModel(): + sim = pbd.Simulation.getCurrent() + sim.initDefault() + model = sim.getModel() + + pd = model.getParticles() + + ts = sim.getTimeStep() + ts.setValueUInt(pbd.TimeStepController.NUM_SUB_STEPS, 10) + + dx = size / (numParticles - 1) + for i in range(numParticles): + pd.addVertex([dx*i,0,0]) + pd.setAcceleration(i, [0,-9.81,0]) + for i in range(numParticles-1): + model.addDistanceConstraint_XPBD(i,i+1, 1000000.0) + + pd.setMass(0, 0.0) + +def render(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + pd = model.getParticles() + glLineWidth(2.0) + glColor3f(1.0, 1.0, 1.0) + glBegin(GL_LINE_STRIP) + for i in range(numParticles): + glVertex3fv(pd.getPosition(i)) + glEnd() + + glPointSize(8.0) + glColor3f(0.1, 0.7, 0.3) + glBegin(GL_POINTS) + for i in range(numParticles): + glVertex3fv(pd.getPosition(i)) + glEnd() + + glPushMatrix() + glLoadIdentity() + drawText([-0.95,0.9], "Time: {:.2f}".format(pbd.TimeManager.getCurrent().getTime())) + glPopMatrix() + +def timeStep(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + for i in range(8): + sim.getTimeStep().step(model) + +def reset(): + pbd.Simulation.getCurrent().reset() + pbd.Simulation.getCurrent().getModel().cleanup() + buildModel() + +def main(): + pbd.Logger.addConsoleSink(pbd.LogLevel.INFO) + + initGL(1280, 1024) + gluLookAt (0, -2, -10, 0, -2, 0, 0, 1, 0) + + buildModel() + + running = True + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_r: + reset() + if event.key == pygame.K_ESCAPE: + running = False + + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) + glClearColor(0.3, 0.3, 0.3, 1.0) + timeStep() + render() + pygame.display.flip() + pygame.time.wait(5) # slow down simulation since it is too fast + + pygame.quit() + pbd.Timing.printAverageTimes() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/pyPBD/examples/render_tools.py b/pyPBD/examples/render_tools.py new file mode 100644 index 00000000..ec860776 --- /dev/null +++ b/pyPBD/examples/render_tools.py @@ -0,0 +1,199 @@ +import pygame +from pygame.locals import * + +from OpenGL.GL import * +from OpenGL.GLU import * +from OpenGL.GL import shaders +import numpy as np + +sphereQuadric = None + +def initLights(): + glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.2, 0.2, 1]) + glLightfv(GL_LIGHT0, GL_DIFFUSE, [1,1,1,1]) + glLightfv(GL_LIGHT0, GL_SPECULAR, [1,1,1,1]) + glLightfv(GL_LIGHT0, GL_POSITION, [3,-10,-10,0]) + glEnable(GL_LIGHT0) + + glLightfv(GL_LIGHT1, GL_AMBIENT, [0.2, 0.2, 0.2, 1]) + glLightfv(GL_LIGHT1, GL_DIFFUSE, [1,1,1,1]) + glLightfv(GL_LIGHT1, GL_SPECULAR, [1,1,1,1]) + glLightfv(GL_LIGHT1, GL_POSITION, [3,10,-10,0]) + glEnable(GL_LIGHT1) + + glLightfv(GL_LIGHT2, GL_AMBIENT, [0.2, 0.2, 0.2, 1]) + glLightfv(GL_LIGHT2, GL_DIFFUSE, [1,1,1,1]) + glLightfv(GL_LIGHT2, GL_SPECULAR, [1,1,1,1]) + glLightfv(GL_LIGHT2, GL_POSITION, [3,-10,10,0]) + glEnable(GL_LIGHT2) + + glLightfv(GL_LIGHT3, GL_AMBIENT, [0.2, 0.2, 0.2, 1]) + glLightfv(GL_LIGHT3, GL_DIFFUSE, [1,1,1,1]) + glLightfv(GL_LIGHT3, GL_SPECULAR, [1,1,1,1]) + glLightfv(GL_LIGHT3, GL_POSITION, [3,10,10,0]) + glEnable(GL_LIGHT3) + + glEnable(GL_LIGHTING) + glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE) + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE) + +def drawMesh(pd, mesh, offset, color): + global shader + global UNIFORM_LOCATIONS + shaders.glUseProgram(shader) + + mv = glGetFloatv(GL_MODELVIEW_MATRIX) + glUniformMatrix4fv(UNIFORM_LOCATIONS["modelview_matrix"], 1, GL_FALSE, mv) + pm = glGetFloatv(GL_PROJECTION_MATRIX) + glUniformMatrix4fv(UNIFORM_LOCATIONS["projection_matrix"], 1, GL_FALSE, pm) + glUniform3fv(UNIFORM_LOCATIONS["surface_color"], 1, color) + glUniform1f(UNIFORM_LOCATIONS["shininess"], 5.0) + glUniform1f(UNIFORM_LOCATIONS["specular_factor"], 0.2) + + faces = mesh.getFaces() + + #glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color) + #glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color) + #glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, [1,1,1,1]) + #glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 15.0) + #glColor3fv(color) + + vertices = np.reshape(pd.getVertices(), -1) + normals = np.reshape(np.negative(np.array(mesh.getVertexNormals())), -1) + + #glEnableClientState(GL_VERTEX_ARRAY) + #glEnableClientState(GL_NORMAL_ARRAY) + #glVertexPointer(3, GL_DOUBLE, 0, vertices) + #glNormalPointer(GL_DOUBLE, 0, normals) + + glEnableVertexAttribArray(0) + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, normals); + + glDrawElements(GL_TRIANGLES, 3 * mesh.numFaces(), GL_UNSIGNED_INT, mesh.getFaces()) + + #glDisableClientState(GL_VERTEX_ARRAY) + #glDisableClientState(GL_NORMAL_ARRAY) + glDisableVertexAttribArray(0) + glDisableVertexAttribArray(2) + shaders.glUseProgram(0) + +def drawSphere(translation, radius, color, subDivision=16): + global sphereQuadric + speccolor = [1.0, 1.0, 1.0, 1.0] + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, color) + glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, color) + glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, speccolor) + glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, 5.0); + glColor3fv(color) + + if (sphereQuadric == None): + sphereQuadric = gluNewQuadric(); + gluQuadricNormals(sphereQuadric, GLU_SMOOTH); + + glPushMatrix () + glTranslated ((translation)[0], (translation)[1], (translation)[2]) + gluSphere(sphereQuadric, radius, subDivision, subDivision) + glPopMatrix() + + + +def drawText(position, textString, size = 36): + font = pygame.font.Font (None, size) + textSurface = font.render(textString, True, (255,255,255,255), (0,0,0,255)) + textData = pygame.image.tostring(textSurface, "RGBA", True) + glRasterPos2d(*position) + glDrawPixels(textSurface.get_width(), textSurface.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, textData) + +def initShader(): + global shader + global UNIFORM_LOCATIONS + + VERTEX_SHADER = shaders.compileShader("""#version 330 + uniform mat4 modelview_matrix; + uniform mat4 projection_matrix; + + layout(location = 0) in vec3 position; + layout(location = 1) in vec3 normal; + + out VertexData + { + vec3 normal; + vec3 view; + } + outData; + + void main() + { + vec4 v_position = projection_matrix * modelview_matrix * vec4(position, 1.0); + outData.normal = mat3(modelview_matrix) * normal; + outData.view = -v_position.xyz; + gl_Position = v_position; + } + """, GL_VERTEX_SHADER) + + FRAGMENT_SHADER = shaders.compileShader("""#version 330 + uniform vec3 surface_color; + uniform float shininess; + uniform float specular_factor; + + layout(location = 0, index = 0) out vec4 frag_color; + + in VertexData + { + vec3 normal; + vec3 view; + } + inData; + + void main() + { + vec3 normal = inData.normal; + + if (!gl_FrontFacing) + { + normal = -inData.normal; + } + + const vec3 ambient = vec3(0.6, 0.6, 0.6); + const vec3 diffuse = vec3(1.0, 1.0, 1.0); + const vec3 specular = vec3(1.0, 1.0, 1.0); + + vec3 eye_n = normalize(normal); + vec3 eye_v = normalize(inData.view); + const vec3 eye_l = vec3(0.0, 0.0, 1.0); + float cos_ln = max(dot(eye_l, eye_n), 0.0); + vec3 h = normalize(eye_l + eye_v); + float cos_nh = max(dot(eye_n, h), 0.0); + + vec3 color = surface_color * (ambient + diffuse * cos_ln + specular_factor * specular * pow(cos_nh, shininess)); + + frag_color = vec4(color, 1.0); + } + """, GL_FRAGMENT_SHADER) + shader = shaders.compileProgram(VERTEX_SHADER,FRAGMENT_SHADER) + + UNIFORM_LOCATIONS = { + 'modelview_matrix': glGetUniformLocation(shader, 'modelview_matrix'), + 'projection_matrix': glGetUniformLocation(shader, 'projection_matrix'), + 'surface_color': glGetUniformLocation(shader, 'surface_color'), + 'shininess': glGetUniformLocation(shader, 'shininess'), + 'specular_factor': glGetUniformLocation(shader, 'specular_factor'), + } + +def initGL(resX, resY): + pygame.init() + display = (resX, resY) + pygame.display.set_mode(display, DOUBLEBUF|OPENGL) + + glEnable (GL_DEPTH_TEST) + glEnable (GL_NORMALIZE) + glShadeModel (GL_SMOOTH) + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL) + + gluPerspective(45, (display[0]/display[1]), 0.5, 50.0) + + initShader() + + diff --git a/pyPBD/examples/rigid_body_cloth_coupling.py b/pyPBD/examples/rigid_body_cloth_coupling.py new file mode 100644 index 00000000..a24b4d76 --- /dev/null +++ b/pyPBD/examples/rigid_body_cloth_coupling.py @@ -0,0 +1,264 @@ +import pygame +from pygame.locals import * + +from OpenGL.GL import * +from OpenGL.GLU import * + +from math_tools import * +from render_tools import * + +import pypbd as pbd +import numpy as np + +nRows = 30 +nCols = 30 +clothWidth = 10.0 +clothHeight = 10.0 +width = 0.2 +height = 2.0 +depth = 0.2 + +def buildModel(): + sim = pbd.Simulation.getCurrent() + sim.initDefault() + model = sim.getModel() + + # 1 = distance constraints (PBD) + # 2 = FEM triangle constraints (PBD) + # 3 = strain triangle constraints (PBD) + # 4 = distance constraints (XPBD) + simModel = 2 + + # 1 = dihedral angle (PBD) + # 2 = isometric bending (PBD) + # 3 = isometric bending (XPBD) + bendingModel = 2 + + createMesh(simModel, bendingModel) + createRigidBodyModel() + + ts = sim.getTimeStep() + ts.setValueUInt(pbd.TimeStepController.NUM_SUB_STEPS, 3) + + +# Create a particle model mesh +def createMesh(simModel, bendingModel): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + model.addRegularTriangleModel(nCols, nRows, [-5,4,-5], rotation_matrix(math.pi*0.5, [1.0, 0.0, 0.0]), [clothWidth, clothHeight]) + + # init constraints + triModels = model.getTriangleModels() + for triModel in triModels: + stiffness = 1.0 + if (simModel == 4): + stiffness = 100000 + poissonRatio = 0.3 + model.addClothConstraints(triModel, simModel, stiffness, stiffness, stiffness, stiffness, + poissonRatio, poissonRatio, False, False) + + bending_stiffness = 0.01 + if (bendingModel == 3): + bending_stiffness = 50.0 + model.addBendingConstraints(triModel, bendingModel, bending_stiffness) + + print("Number of triangles: " + str(triModel.getParticleMesh().numFaces())) + print("Number of vertices: " + str(nRows*nCols)) + + + +# Compute diagonal inertia tensor +def computeInertiaTensorBox(mass, width, height, depth): + Ix = (mass / 12.0) * (height*height + depth*depth) + Iy = (mass / 12.0) * (width*width + depth*depth) + Iz = (mass / 12.0) * (width*width + height*height) + return np.array([Ix, Iy, Iz]) + +def createRigidBodyModel(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + rb = model.getRigidBodies() + + fileName = "../../data/models/cube.obj" + (vd, mesh) = pbd.OBJLoader.loadObjToMesh(fileName, [width, height, depth]) + + (vd_static, mesh_static) = pbd.OBJLoader.loadObjToMesh(fileName, [0.5, 0.5, 0.5]) + + # -5, -5 + body = model.addRigidBody(1.0, # density + vd_static, mesh_static, # vertices, mesh + [-5.0, 0.0, -5.0], # position + 0.0, [1,0,0], # angle, rotation axis + [1, 1, 1], # scaling + None) # signed distance field for collision detection + body.setMass(0.0) + + # dynamic body + body = model.addRigidBody(1.0, + vd, mesh, + [-5.0, 1.0, -5.0], + 0.0, [1,0,0], + [1, 1, 1], + None) + + body = model.addRigidBody(1.0, + vd, mesh, + [-5.0, 3.0, -5.0], + 0.0, [1,0,0], + [1, 1, 1], + None) + + model.addBallJoint(0, 1, [-5.0, 0.0, -5.0]) + model.addBallJoint(1, 2, [-5.0, 2.0, -5.0]) + + # 5, -5 + body = model.addRigidBody(1.0, # density + vd_static, mesh_static, # vertices, mesh + [5.0, 0.0, -5.0], # position + 0.0, [1,0,0], # angle, rotation axis + [1, 1, 1], # scaling + None) # signed distance field for collision detection + body.setMass(0.0) + + # dynamic body + body = model.addRigidBody(1.0, + vd, mesh, + [5.0, 1.0, -5.0], + 0.0, [1,0,0], + [1, 1, 1], + None) + + body = model.addRigidBody(1.0, + vd, mesh, + [5.0, 3.0, -5.0], + 0.0, [1,0,0], + [1, 1, 1], + None) + + model.addBallJoint(3, 4, [5.0, 0.0, -5.0]) + model.addBallJoint(4, 5, [5.0, 2.0, -5.0]) + + # 5, 5 + body = model.addRigidBody(1.0, # density + vd_static, mesh_static, # vertices, mesh + [5.0, 0.0, 5.0], # position + 0.0, [1,0,0], # angle, rotation axis + [1, 1, 1], # scaling + None) # signed distance field for collision detection + body.setMass(0.0) + + # dynamic body + body = model.addRigidBody(1.0, + vd, mesh, + [5.0, 1.0, 5.0], + 0.0, [1,0,0], + [1, 1, 1], + None) + + body = model.addRigidBody(1.0, + vd, mesh, + [5.0, 3.0, 5.0], + 0.0, [1,0,0], + [1, 1, 1], + None) + + model.addBallJoint(6, 7, [5.0, 0.0, 5.0]) + model.addBallJoint(7, 8, [5.0, 2.0, 5.0]) + + # -5, 5 + body = model.addRigidBody(1.0, # density + vd_static, mesh_static, # vertices, mesh + [-5.0, 0.0, 5.0], # position + 0.0, [1,0,0], # angle, rotation axis + [1, 1, 1], # scaling + None) # signed distance field for collision detection + body.setMass(0.0) + + # dynamic body + body = model.addRigidBody(1.0, + vd, mesh, + [-5.0, 1.0, 5.0], + 0.0, [1,0,0], + [1, 1, 1], + None) + + body = model.addRigidBody(1.0, + vd, mesh, + [-5.0, 3.0, 5.0], + 0.0, [1,0,0], + [1, 1, 1], + None) + + model.addBallJoint(9, 10, [-5.0, 0.0, 5.0]) + model.addBallJoint(10, 11, [-5.0, 2.0, 5.0]) + + model.addRigidBodyParticleBallJoint(2, 0) + model.addRigidBodyParticleBallJoint(5, nCols - 1) + model.addRigidBodyParticleBallJoint(8, nRows*nCols - 1) + model.addRigidBodyParticleBallJoint(11, (nRows-1)*nCols) + +def render(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + pd = model.getParticles() + triModel = model.getTriangleModels()[0] + offset = triModel.getIndexOffset() + drawMesh(pd, triModel.getParticleMesh(), offset, [0,0.3,0.8]) + + rbs = model.getRigidBodies() + for rb in rbs: + vd = rb.getGeometry().getVertexData() + mesh = rb.getGeometry().getMesh() + drawMesh(vd, mesh, 0, [0.5,0.5,0.5]) + + glPushMatrix() + glLoadIdentity() + drawText([-0.95,0.9], "Time: {:.2f}".format(pbd.TimeManager.getCurrent().getTime())) + glPopMatrix() + +def timeStep(): + sim = pbd.Simulation.getCurrent() + model = sim.getModel() + for i in range(8): + sim.getTimeStep().step(model) + + for triModel in model.getTriangleModels(): + triModel.updateMeshNormals(model.getParticles()) + +def reset(): + pbd.Simulation.getCurrent().reset() + pbd.Simulation.getCurrent().getModel().cleanup() + buildModel() + +def main(): + pbd.Logger.addConsoleSink(pbd.LogLevel.INFO) + + initGL(1280, 1024) + #initLights() + gluLookAt (0, 10, -30, 0, 0, 0, 0, 1, 0) + glRotatef(20.0, 0, 1, 0) + + buildModel() + + running = True + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_r: + reset() + if event.key == pygame.K_ESCAPE: + running = False + + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) + glClearColor(0.3, 0.3, 0.3, 1.0) + timeStep() + render() + pygame.display.flip() + + pygame.quit() + pbd.Timing.printAverageTimes() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/pyPBD/main.cpp b/pyPBD/main.cpp new file mode 100644 index 00000000..367a3756 --- /dev/null +++ b/pyPBD/main.cpp @@ -0,0 +1,37 @@ +#include + +// Put this here for now +#include "common.h" + +#include +#include +#include +INIT_LOGGING +INIT_TIMING + +namespace py = pybind11; + +void CollisionDetectionModule(py::module); +void ConstraintsModule(py::module); +void ParameterObjectModule(py::module); +void ParticleDataModule(py::module); +void RigidBodyModule(py::module); +void SimulationModelModule(py::module); +void SimulationModule(py::module); +void TimeStepModule(py::module); +void TimeModule(py::module); +void UtilitiesModule(py::module); + +PYBIND11_MODULE(pypbd, m) +{ + CollisionDetectionModule(m); + ParticleDataModule(m); + RigidBodyModule(m); + ParameterObjectModule(m); + SimulationModelModule(m); + SimulationModule(m); + ConstraintsModule(m); + TimeStepModule(m); + TimeModule(m); + UtilitiesModule(m); +} \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..13fb0d1c --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[bdist_wheel] +dist-dir = build/dist + +[egg_info] +egg_base = build/ \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..fc7916df --- /dev/null +++ b/setup.py @@ -0,0 +1,125 @@ +import os +import re +import sys +import platform +import subprocess +import multiprocessing as mp +import argparse + +from setuptools import setup, Extension, find_packages +from setuptools.command.build_ext import build_ext +from distutils.version import LooseVersion + +# Extract cmake arguments +parser = argparse.ArgumentParser(add_help=False) +parser.add_argument("-D", action='append', dest='cmake', + help="CMake Options") +parser.add_argument("--manylinux-build", action='store_true', dest='manylinux_build') +args, other_args = parser.parse_known_args(sys.argv) +cmake_clargs = args.cmake +sys.argv = other_args + +# Project binding name +name = "pyPBD" +internal_name = "pypbd" + + +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)) + + if platform.system() == "Windows": + cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', out.decode()).group(1)) + if cmake_version < '3.1.0': + raise RuntimeError("CMake >= 3.1.0 is required on Windows") + + for ext in self.extensions: + self.build_extension(ext) + + def build_extension(self, ext): + extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) + bin_dir_windows = os.path.join(os.path.abspath(self.build_temp), "bin") + cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, + '-DPYTHON_EXECUTABLE=' + sys.executable] + + cfg = 'Debug' if self.debug else 'Release' + build_args = ['--config', cfg] + + # Add cmake command line arguments + if cmake_clargs is not None: + cmake_args += ['-D{}'.format(arg) for arg in cmake_clargs] + + if platform.system() == "Windows": + cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir), + '-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=' + bin_dir_windows, + '-DCMAKE_RUNTIME_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), bin_dir_windows)] + if sys.maxsize > 2**32: + cmake_args += ['-A', 'x64'] + build_args += ['--', '/m'] + else: + cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg] + build_args += ['--', '-j{}'.format(mp.cpu_count())] + + env = os.environ.copy() + env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''), + self.distribution.get_version()) + + # Add position independent code flags if using gcc on linux probably + if platform.system() == "Linux": + cmake_args += ['-DCMAKE_CXX_FLAGS=-fPIC', '-DCMAKE_C_FLAGS=-fPIC'] + + # Using relative rpath messes up repairing the wheel file. The relative rpath is only necessary when + # building locally from source + if not args.manylinux_build: + cmake_args += ['-DCMAKE_INSTALL_RPATH={}'.format("$ORIGIN"), + '-DCMAKE_BUILD_WITH_INSTALL_RPATH:BOOL=ON', + '-DCMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=OFF'] + + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + + if not args.manylinux_build: + subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env) + subprocess.check_call(['cmake', '--build', '.', '--target', internal_name] + build_args, cwd=self.build_temp) + else: + subprocess.check_call(['cmake3', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env) + subprocess.check_call(['cmake3', '--build', '.', '--target', internal_name] + build_args, cwd=self.build_temp) + + +# Get Readme text for long description +cur_dir = os.path.abspath(os.path.dirname(__file__)) +with open(os.path.join(cur_dir, "README.md"), 'r') as f: + long_description = f.read() + +# read version +f = open("version", "r") +pbd_version = f.readline().strip() +f.close() + +setup( + name=name, + version=pbd_version, + author='Interactive Computer Graphics', + author_email='', + description='PBD Project Python Bindings', + long_description=long_description, + long_description_content_type='text/markdown', + url='https://github.com/InteractiveComputerGraphics/PositionBasedDynamics', + license="MIT", + keywords="simulation rigid-bodies rigid-body-dynamics position-based-dynamics deformable-solids", + ext_modules=[CMakeExtension(name)], + cmdclass=dict(build_ext=CMakeBuild), + packages=find_packages(), + zip_safe=False, + install_requires=['numpy<=1.19.3'] +) \ No newline at end of file diff --git a/version b/version new file mode 100644 index 00000000..359a5b95 --- /dev/null +++ b/version @@ -0,0 +1 @@ +2.0.0 \ No newline at end of file From 173b70054eb41efa060ae40cea253eefc1635114 Mon Sep 17 00:00:00 2001 From: Jan Bender Date: Mon, 20 Dec 2021 17:37:23 +0100 Subject: [PATCH 2/5] - Linux CI fix --- .github/workflows/build-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 6209885f..a349c538 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -45,7 +45,7 @@ jobs: # python-versions: 'cp37-cp37m' python-versions: '${{ matrix.python-version }}' build-requirements: '' - system-packages: 'cmake3' + system-packages: 'cmake3 libXrandr-devel libXinerama-devel libXcursor-devel libXi-devel' package-path: '' pip-wheel-args: '--manylinux-build' From 4b747cbadb7449519419ee77dd78d97b7f8ab073 Mon Sep 17 00:00:00 2001 From: Jan Bender Date: Mon, 20 Dec 2021 19:54:17 +0100 Subject: [PATCH 3/5] - Linux CI --- CMake/Common.cmake | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/CMake/Common.cmake b/CMake/Common.cmake index 6e22b79d..97f98173 100644 --- a/CMake/Common.cmake +++ b/CMake/Common.cmake @@ -14,6 +14,16 @@ set(PBD_BINARY_MINSIZEREL_POSTFIX "_ms" CACHE INTERNAL "Postfix for executables" include(CMakeDependentOption) +cmake_dependent_option(USE_PYTHON_BINDINGS "Generate Python Bindings using PyBind11" ON "PYTHON_EXECUTABLE" OFF) +if (USE_PYTHON_BINDINGS AND UNIX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + message(STATUS "Adding -fPIC option when generating Python bindings using GCC") +endif () + +option(CI_BUILD "Build on CI System" OFF) +mark_as_advanced(CI_BUILD) + if (NOT WIN32) if (NOT EXISTS ${CMAKE_BINARY_DIR}/CMakeCache.txt) if (NOT CMAKE_BUILD_TYPE) @@ -45,9 +55,14 @@ endif (MSVC) if (UNIX OR MINGW) set(CMAKE_USE_RELATIVE_PATHS "1") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG") - # Set compiler flags for "release" - set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -march=native") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -DNDEBUG -march=native") + # Set compiler flags for "release" Use generic 64 bit instructions when building on CI + if (CI_BUILD) + set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -march=x86-64") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -DNDEBUG -march=x86-64") + else() + set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -march=native") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -DNDEBUG -march=native") + endif () endif (UNIX OR MINGW) if(MINGW) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O1 -Wa,-mbig-obj") @@ -67,10 +82,3 @@ OPTION(USE_DOUBLE_PRECISION "Use double precision" ON) if (USE_DOUBLE_PRECISION) add_definitions( -DUSE_DOUBLE) endif (USE_DOUBLE_PRECISION) - -cmake_dependent_option(USE_PYTHON_BINDINGS "Generate Python Bindings using PyBind11" ON "PYTHON_EXECUTABLE" OFF) -if (USE_PYTHON_BINDINGS AND UNIX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") - message(STATUS "Adding -fPIC option when generating Python bindings using GCC") -endif () From dac7a9d3d2949462af1cca837a787ff8f20313d1 Mon Sep 17 00:00:00 2001 From: Jan Bender Date: Mon, 20 Dec 2021 20:04:38 +0100 Subject: [PATCH 4/5] - CI fix --- CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7393978c..029cbea7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,13 +40,12 @@ if ((DEFINED Discregrid_INCLUDE_DIR) AND (DEFINED Discregrid_DEBUG_LIB) AND (DEF else() ExternalProject_Add( Ext_Discregrid - PREFIX "${ExternalInstallDir}/Discregrid" + PREFIX "${CMAKE_BINARY_DIR}/extern/Discregrid" GIT_REPOSITORY https://github.com/InteractiveComputerGraphics/Discregrid.git GIT_TAG "0b69062ff9c56fbb6dcecd296652028bedbacf0e" INSTALL_DIR ${ExternalInstallDir}/Discregrid CMAKE_ARGS -DCMAKE_BUILD_TYPE:STRING=${EXT_CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=${ExternalInstallDir}/Discregrid - -DBUILD_AS_SHARED_LIBS:BOOL=0 - -DBUILD_CMD_EXECUTABLE:BOOL=0 -DEIGEN3_INCLUDE_DIR:PATH=${EIGEN3_INCLUDE_DIR} + -DBUILD_CMD_EXECUTABLE:BOOL=0 -DEIGEN3_INCLUDE_DIR:PATH=${EIGEN3_INCLUDE_DIR} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} ) ExternalProject_Get_Property(Ext_Discregrid INSTALL_DIR) set(Discregrid_INCLUDE_DIR ${INSTALL_DIR}/include) From faeee1f83896fc897cacea813735f91c324e57cb Mon Sep 17 00:00:00 2001 From: Jan Bender Date: Wed, 22 Dec 2021 14:54:19 +0100 Subject: [PATCH 5/5] - extended and modified Python interface - more Python examples - bugfixes --- Changelog.txt | 5 + .../DistanceFieldDemos/ClothCollisionDemo.cpp | 12 +- .../DeformableCollisionDemo.cpp | 12 +- .../RigidBodyCollisionDemo.cpp | 36 +-- Demos/SceneLoaderDemo/SceneLoaderDemo.cpp | 26 +- ...ectPositionBasedSolverForStiffRodsDemo.cpp | 20 +- README.md | 17 +- Simulation/BoundingSphereHierarchy.cpp | 4 + Simulation/BoundingSphereHierarchy.h | 1 + Simulation/CollisionDetection.cpp | 30 +- Simulation/CollisionDetection.h | 60 ++-- .../DistanceFieldCollisionDetection.cpp | 18 +- Simulation/ParticleData.h | 8 +- Simulation/SimulationModel.cpp | 8 +- Simulation/TetModel.cpp | 3 + pyPBD/ParticleDataModule.cpp | 4 +- pyPBD/SimulationModelModule.cpp | 264 +++++++++++++----- pyPBD/examples/armadillo.py | 66 +++-- pyPBD/examples/beam_model.py | 14 +- pyPBD/examples/bunny_cloth.py | 118 +++++--- pyPBD/examples/chain_model.py | 34 ++- pyPBD/examples/cloth_collision.py | 124 +++++--- pyPBD/examples/cloth_model.py | 66 +++-- pyPBD/examples/math_tools.py | 48 +--- pyPBD/examples/pendulum.py | 29 +- pyPBD/examples/render_tools.py | 3 +- pyPBD/examples/rigid_body_cloth_coupling.py | 129 ++++----- version | 2 +- 28 files changed, 731 insertions(+), 430 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 4c343e77..99fb7c80 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,8 @@ +2.0.1 + - extended and modified Python interface + - more Python examples + - bugfixes + 2.0.0 - added Python binding - cleaned up demos diff --git a/Demos/DistanceFieldDemos/ClothCollisionDemo.cpp b/Demos/DistanceFieldDemos/ClothCollisionDemo.cpp index e481a783..92ce4f14 100644 --- a/Demos/DistanceFieldDemos/ClothCollisionDemo.cpp +++ b/Demos/DistanceFieldDemos/ClothCollisionDemo.cpp @@ -257,13 +257,13 @@ void buildModel () Simulation::getCurrent()->getTimeStep()->setCollisionDetection(*model, &cd); cd.setTolerance(static_cast(0.05)); - const std::vector *vertices1 = rb[0]->getGeometry().getVertexDataLocal().getVertices(); - const unsigned int nVert1 = static_cast(vertices1->size()); - cd.addCollisionBox(0, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices1)[0], nVert1, Vector3r(100.0, 1.0, 100.0)); + const std::vector &vertices1 = rb[0]->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert1 = static_cast(vertices1.size()); + cd.addCollisionBox(0, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices1.data(), nVert1, Vector3r(100.0, 1.0, 100.0)); - const std::vector *vertices2 = rb[1]->getGeometry().getVertexDataLocal().getVertices(); - const unsigned int nVert2 = static_cast(vertices2->size()); - cd.addCollisionTorus(1, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices2)[0], nVert2, Vector2r(2.0, 1.0)); + const std::vector &vertices2 = rb[1]->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert2 = static_cast(vertices2.size()); + cd.addCollisionTorus(1, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices2.data(), nVert2, Vector2r(2.0, 1.0)); SimulationModel::TriangleModelVector &tm = model->getTriangleModels(); ParticleData &pd = model->getParticles(); diff --git a/Demos/DistanceFieldDemos/DeformableCollisionDemo.cpp b/Demos/DistanceFieldDemos/DeformableCollisionDemo.cpp index d4b46b0d..a07311f3 100644 --- a/Demos/DistanceFieldDemos/DeformableCollisionDemo.cpp +++ b/Demos/DistanceFieldDemos/DeformableCollisionDemo.cpp @@ -242,13 +242,13 @@ void buildModel () Simulation::getCurrent()->getTimeStep()->setCollisionDetection(*model, &cd); cd.setTolerance(static_cast(0.05)); - const std::vector *vertices1 = rb[0]->getGeometry().getVertexDataLocal().getVertices(); - const unsigned int nVert1 = static_cast(vertices1->size()); - cd.addCollisionBox(0, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices1)[0], nVert1, Vector3r(100.0, 1.0, 100.0)); + const std::vector &vertices1 = rb[0]->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert1 = static_cast(vertices1.size()); + cd.addCollisionBox(0, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices1.data(), nVert1, Vector3r(100.0, 1.0, 100.0)); - const std::vector *vertices2 = rb[1]->getGeometry().getVertexDataLocal().getVertices(); - const unsigned int nVert2 = static_cast(vertices2->size()); - cd.addCollisionTorus(1, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices2)[0], nVert2, Vector2r(3.0, 1.5)); + const std::vector &vertices2 = rb[1]->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert2 = static_cast(vertices2.size()); + cd.addCollisionTorus(1, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices2.data(), nVert2, Vector2r(3.0, 1.5)); SimulationModel::TetModelVector &tm = model->getTetModels(); ParticleData &pd = model->getParticles(); diff --git a/Demos/DistanceFieldDemos/RigidBodyCollisionDemo.cpp b/Demos/DistanceFieldDemos/RigidBodyCollisionDemo.cpp index 6780cea7..9a6eaa25 100644 --- a/Demos/DistanceFieldDemos/RigidBodyCollisionDemo.cpp +++ b/Demos/DistanceFieldDemos/RigidBodyCollisionDemo.cpp @@ -260,10 +260,10 @@ void createBodyModel() vdBox, meshBox, Vector3r(100.0, 1.0, 100.0)); rb[rbIndex]->setMass(0.0); - const std::vector *vertices = rb[rbIndex]->getGeometry().getVertexDataLocal().getVertices(); - const unsigned int nVert = static_cast(vertices->size()); + const std::vector &vertices = rb[rbIndex]->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert = static_cast(vertices.size()); - cd.addCollisionBox(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, Vector3r(100.0, 1.0, 100.0)); + cd.addCollisionBox(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, Vector3r(100.0, 1.0, 100.0)); rbIndex++; Real current_z = startz_piles; @@ -280,9 +280,9 @@ void createBodyModel() Vector3r(0.5, 10.0, 0.5)); rb[rbIndex]->setMass(0.0); - const std::vector *vertices = rb[rbIndex]->getGeometry().getVertexDataLocal().getVertices(); - const unsigned int nVert = static_cast(vertices->size()); - cd.addCollisionCylinder(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, Vector2r(0.5, 10.0)); + const std::vector &vertices = rb[rbIndex]->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert = static_cast(vertices.size()); + cd.addCollisionCylinder(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, Vector2r(0.5, 10.0)); current_x += dx_piles; rbIndex++; } @@ -319,9 +319,9 @@ void createBodyModel() vdTorus, meshTorus, Vector3r(2.0, 2.0, 2.0)); - const std::vector *vertices = rb[rbIndex]->getGeometry().getVertexDataLocal().getVertices(); - const unsigned int nVert = static_cast(vertices->size()); - cd.addCollisionTorus(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, Vector2r(2.0, 1.0)); + const std::vector &vertices = rb[rbIndex]->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert = static_cast(vertices.size()); + cd.addCollisionTorus(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, Vector2r(2.0, 1.0)); } else if (currentType == 1) { @@ -331,9 +331,9 @@ void createBodyModel() vdCube, meshCube, Vector3r(4.0, 1.0, 1.0)); - const std::vector *vertices = rb[rbIndex]->getGeometry().getVertexDataLocal().getVertices(); - const unsigned int nVert = static_cast(vertices->size()); - cd.addCollisionBox(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, Vector3r(4.0, 1.0, 1.0)); + const std::vector &vertices = rb[rbIndex]->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert = static_cast(vertices.size()); + cd.addCollisionBox(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, Vector3r(4.0, 1.0, 1.0)); } else if (currentType == 2) { @@ -342,9 +342,9 @@ void createBodyModel() q, //Quaternionr(1.0, 0.0, 0.0, 0.0), vdSphere, meshSphere); - const std::vector *vertices = rb[rbIndex]->getGeometry().getVertexDataLocal().getVertices(); - const unsigned int nVert = static_cast(vertices->size()); - cd.addCollisionSphere(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, 2.0); + const std::vector &vertices = rb[rbIndex]->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert = static_cast(vertices.size()); + cd.addCollisionSphere(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, 2.0); } else if (currentType == 3) { @@ -354,9 +354,9 @@ void createBodyModel() vdCylinder, meshCylinder, Vector3r(0.75, 5.0, 0.75)); - const std::vector *vertices = rb[rbIndex]->getGeometry().getVertexDataLocal().getVertices(); - const unsigned int nVert = static_cast(vertices->size()); - cd.addCollisionCylinder(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, Vector2r(0.75, 5.0)); + const std::vector &vertices = rb[rbIndex]->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert = static_cast(vertices.size()); + cd.addCollisionCylinder(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, Vector2r(0.75, 5.0)); } currentType = (currentType + 1) % 4; current_z += dz_bodies; diff --git a/Demos/SceneLoaderDemo/SceneLoaderDemo.cpp b/Demos/SceneLoaderDemo/SceneLoaderDemo.cpp index 73637d37..e3c7c447 100644 --- a/Demos/SceneLoaderDemo/SceneLoaderDemo.cpp +++ b/Demos/SceneLoaderDemo/SceneLoaderDemo.cpp @@ -662,29 +662,29 @@ void readScene(const bool readFile) rb[i]->setRestitutionCoeff(rbd.m_restitutionCoeff); rb[i]->setFrictionCoeff(rbd.m_frictionCoeff); - const std::vector *vertices = rb[i]->getGeometry().getVertexDataLocal().getVertices(); - const unsigned int nVert = static_cast(vertices->size()); + const std::vector &vertices = rb[i]->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert = static_cast(vertices.size()); switch (rbd.m_collisionObjectType) { case SceneLoader::No_Collision_Object: break; case SceneLoader::Sphere: - cd.addCollisionSphere(i, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, rbd.m_collisionObjectScale[0], rbd.m_testMesh, rbd.m_invertSDF); + cd.addCollisionSphere(i, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, rbd.m_collisionObjectScale[0], rbd.m_testMesh, rbd.m_invertSDF); break; case SceneLoader::Box: - cd.addCollisionBox(i, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, rbd.m_collisionObjectScale, rbd.m_testMesh, rbd.m_invertSDF); + cd.addCollisionBox(i, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, rbd.m_collisionObjectScale, rbd.m_testMesh, rbd.m_invertSDF); break; case SceneLoader::Cylinder: - cd.addCollisionCylinder(i, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, rbd.m_collisionObjectScale.head<2>(), rbd.m_testMesh, rbd.m_invertSDF); + cd.addCollisionCylinder(i, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, rbd.m_collisionObjectScale.head<2>(), rbd.m_testMesh, rbd.m_invertSDF); break; case SceneLoader::Torus: - cd.addCollisionTorus(i, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, rbd.m_collisionObjectScale.head<2>(), rbd.m_testMesh, rbd.m_invertSDF); + cd.addCollisionTorus(i, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, rbd.m_collisionObjectScale.head<2>(), rbd.m_testMesh, rbd.m_invertSDF); break; case SceneLoader::HollowSphere: - cd.addCollisionHollowSphere(i, PBD::CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, rbd.m_collisionObjectScale[0], rbd.m_thicknessSDF, rbd.m_testMesh, rbd.m_invertSDF); + cd.addCollisionHollowSphere(i, PBD::CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, rbd.m_collisionObjectScale[0], rbd.m_thicknessSDF, rbd.m_testMesh, rbd.m_invertSDF); break; case SceneLoader::HollowBox: - cd.addCollisionHollowBox(i, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, rbd.m_collisionObjectScale, rbd.m_thicknessSDF, rbd.m_testMesh, rbd.m_invertSDF); + cd.addCollisionHollowBox(i, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, rbd.m_collisionObjectScale, rbd.m_thicknessSDF, rbd.m_testMesh, rbd.m_invertSDF); break; case SceneLoader::SDF: { @@ -695,10 +695,10 @@ void readScene(const bool readFile) const string resStr = to_string(rbd.m_resolutionSDF[0]) + "_" + to_string(rbd.m_resolutionSDF[1]) + "_" + to_string(rbd.m_resolutionSDF[2]); const std::string modelFileName = FileSystem::getFileNameWithExt(rbd.m_modelFile); const string sdfFileName = FileSystem::normalizePath(cachePath + "/" + modelFileName + "_" + resStr + ".csdf"); - cd.addCubicSDFCollisionObject(i, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, distanceFields[sdfFileName], rbd.m_collisionObjectScale, rbd.m_testMesh, rbd.m_invertSDF); + cd.addCubicSDFCollisionObject(i, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, distanceFields[sdfFileName], rbd.m_collisionObjectScale, rbd.m_testMesh, rbd.m_invertSDF); } else - cd.addCubicSDFCollisionObject(i, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, distanceFields[rbd.m_collisionObjectFileName], rbd.m_collisionObjectScale, rbd.m_testMesh, rbd.m_invertSDF); + cd.addCubicSDFCollisionObject(i, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, distanceFields[rbd.m_collisionObjectFileName], rbd.m_collisionObjectScale, rbd.m_testMesh, rbd.m_invertSDF); break; } } @@ -1076,7 +1076,7 @@ void exportOBJ() { const IndexedFaceMesh& mesh = model->getTriangleModels()[i]->getParticleMesh(); const unsigned int offset = model->getTriangleModels()[i]->getIndexOffset(); - const Vector3r* x = model->getParticles().getVertices()->data(); + const Vector3r* x = model->getParticles().getVertices().data(); std::string fileName = "triangle_model"; fileName = fileName + std::to_string(i) + "_" + std::to_string(frameCounter) + ".obj"; @@ -1089,7 +1089,7 @@ void exportOBJ() { const IndexedFaceMesh& mesh = model->getTetModels()[i]->getVisMesh(); const unsigned int offset = model->getTetModels()[i]->getIndexOffset(); - const Vector3r* x = model->getTetModels()[i]->getVisVertices().getVertices()->data(); + const Vector3r* x = model->getTetModels()[i]->getVisVertices().getVertices().data(); std::string fileName = "tet_model"; fileName = fileName + std::to_string(i) + "_" + std::to_string(frameCounter) + ".obj"; @@ -1101,7 +1101,7 @@ void exportOBJ() for (unsigned int i = 0; i < model->getRigidBodies().size(); i++) { const IndexedFaceMesh& mesh = model->getRigidBodies()[i]->getGeometry().getMesh(); - const Vector3r *x = model->getRigidBodies()[i]->getGeometry().getVertexData().getVertices()->data(); + const Vector3r *x = model->getRigidBodies()[i]->getGeometry().getVertexData().getVertices().data(); std::string fileName = "rigid_body"; fileName = fileName + std::to_string(i) + "_" + std::to_string(frameCounter) + ".obj"; diff --git a/Demos/StiffRodsDemos/DirectPositionBasedSolverForStiffRodsDemo.cpp b/Demos/StiffRodsDemos/DirectPositionBasedSolverForStiffRodsDemo.cpp index 536a9304..4e4320f7 100644 --- a/Demos/StiffRodsDemos/DirectPositionBasedSolverForStiffRodsDemo.cpp +++ b/Demos/StiffRodsDemos/DirectPositionBasedSolverForStiffRodsDemo.cpp @@ -701,29 +701,29 @@ void readScene() rb[rbIndex]->setRestitutionCoeff(rbd.m_restitutionCoeff); rb[rbIndex]->setFrictionCoeff(rbd.m_frictionCoeff); - const std::vector *vertices = rb[rbIndex]->getGeometry().getVertexDataLocal().getVertices(); - const unsigned int nVert = static_cast(vertices->size()); + const std::vector &vertices = rb[rbIndex]->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert = static_cast(vertices.size()); switch (rbd.m_collisionObjectType) { case SceneLoader::No_Collision_Object: break; case SceneLoader::Sphere: - cd.addCollisionSphere(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, rbd.m_collisionObjectScale[0], rbd.m_testMesh, rbd.m_invertSDF); + cd.addCollisionSphere(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, rbd.m_collisionObjectScale[0], rbd.m_testMesh, rbd.m_invertSDF); break; case SceneLoader::Box: - cd.addCollisionBox(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, rbd.m_collisionObjectScale, rbd.m_testMesh, rbd.m_invertSDF); + cd.addCollisionBox(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, rbd.m_collisionObjectScale, rbd.m_testMesh, rbd.m_invertSDF); break; case SceneLoader::Cylinder: - cd.addCollisionCylinder(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, rbd.m_collisionObjectScale.head<2>(), rbd.m_testMesh, rbd.m_invertSDF); + cd.addCollisionCylinder(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, rbd.m_collisionObjectScale.head<2>(), rbd.m_testMesh, rbd.m_invertSDF); break; case SceneLoader::Torus: - cd.addCollisionTorus(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, rbd.m_collisionObjectScale.head<2>(), rbd.m_testMesh, rbd.m_invertSDF); + cd.addCollisionTorus(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, rbd.m_collisionObjectScale.head<2>(), rbd.m_testMesh, rbd.m_invertSDF); break; case SceneLoader::HollowSphere: - cd.addCollisionHollowSphere(rbIndex, PBD::CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, rbd.m_collisionObjectScale[0], rbd.m_thicknessSDF, rbd.m_testMesh, rbd.m_invertSDF); + cd.addCollisionHollowSphere(rbIndex, PBD::CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, rbd.m_collisionObjectScale[0], rbd.m_thicknessSDF, rbd.m_testMesh, rbd.m_invertSDF); break; case SceneLoader::HollowBox: - cd.addCollisionHollowBox(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, rbd.m_collisionObjectScale, rbd.m_thicknessSDF, rbd.m_testMesh, rbd.m_invertSDF); + cd.addCollisionHollowBox(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, rbd.m_collisionObjectScale, rbd.m_thicknessSDF, rbd.m_testMesh, rbd.m_invertSDF); break; case SceneLoader::SDF: { @@ -734,10 +734,10 @@ void readScene() const string resStr = to_string(rbd.m_resolutionSDF[0]) + "_" + to_string(rbd.m_resolutionSDF[1]) + "_" + to_string(rbd.m_resolutionSDF[2]); const std::string modelFileName = FileSystem::getFileNameWithExt(rbd.m_modelFile); const string sdfFileName = FileSystem::normalizePath(cachePath + "/" + modelFileName + "_" + resStr + ".csdf"); - cd.addCubicSDFCollisionObject(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, distanceFields[sdfFileName], rbd.m_collisionObjectScale, rbd.m_testMesh, rbd.m_invertSDF); + cd.addCubicSDFCollisionObject(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, distanceFields[sdfFileName], rbd.m_collisionObjectScale, rbd.m_testMesh, rbd.m_invertSDF); } else - cd.addCubicSDFCollisionObject(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, &(*vertices)[0], nVert, distanceFields[rbd.m_collisionObjectFileName], rbd.m_collisionObjectScale, rbd.m_testMesh, rbd.m_invertSDF); + cd.addCubicSDFCollisionObject(rbIndex, CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, distanceFields[rbd.m_collisionObjectFileName], rbd.m_collisionObjectScale, rbd.m_testMesh, rbd.m_invertSDF); break; } } diff --git a/README.md b/README.md index 490de499..4c7a9c9a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This library supports the physically-based simulation of mechanical effects. In the last years position-based simulation methods have become popular in the graphics community. In contrast to classical simulation approaches these methods compute the position changes in each simulation step directly, based on the solution of a quasi-static problem. Therefore, position-based approaches are fast, stable and controllable which make them well-suited for use in interactive environments. However, these methods are generally not as accurate as force-based methods but still provide visual plausibility. Hence, the main application areas of position-based simulation are virtual reality, computer games and special effects in movies and commercials. -The PositionBasedDynamics library allows the position-based handling of many types of constraints in a physically-based simulation. The library uses [CMake](http://www.cmake.org/), [Eigen](http://eigen.tuxfamily.org/), [json](https://github.com/nlohmann/json/) and [AntTweakBar](http://anttweakbar.sourceforge.net/) (only for the demos). All external dependencies are included. +The PositionBasedDynamics library allows the position-based handling of many types of constraints in a physically-based simulation. The library uses [CMake](http://www.cmake.org/), [Eigen](http://eigen.tuxfamily.org/), [json](https://github.com/nlohmann/json/), [pybind](https://github.com/pybind/pybind11), [glfw](https://www.glfw.org/) and [AntTweakBar](http://anttweakbar.sourceforge.net/) (only for the demos). All external dependencies are included. Furthermore we use our own library: - [Discregrid](https://github.com/InteractiveComputerGraphics/Discregrid/) to generate cubic signed distance fields for the collision detection @@ -14,12 +14,17 @@ Furthermore we use our own library: ## News +* We added a Python interface: [pyPBD](https://pypi.org/project/pyPBD/) * Our new [paper](https://animation.rwth-aachen.de/publication/0557/) about a Direct Position-Based Solver for Stiff Rods uses the PositionBasedDynamics library. You can watch the video [here](https://www.youtube.com/watch?v=EFH9xt4omls). * PBD now has a collision detection based on cubic signed distance fields * SPlisHSPlasH is our new open-source fluid simulator which uses the PositionBasedDynamics library to handle rigid-fluid coupling. It can be downloaded here: [https://github.com/InteractiveComputerGraphics/SPlisHSPlasH](https://github.com/InteractiveComputerGraphics/SPlisHSPlasH) * Our new [paper](http://interactive-graphics.de/index.php/research/98-hierarchical-hp-adaptive-signed-distance-fields) about adaptive signed distance fields uses the PositionBasedDynamics library. You can watch the video [here](https://www.youtube.com/watch?v=x_Iq2yM4FcA). +## Forum + +On our [GitHub discussions](https://github.com/InteractiveComputerGraphics/PositionBasedDynamics/discussions) page you can ask questions, discuss about simulation topics, and share ideas. + ## Build Instructions This project is based on [CMake](https://cmake.org/). Simply generate project, Makefiles, etc. using [CMake](https://cmake.org/) and compile the project with the compiler of your choice. The code was tested with the following configurations: @@ -28,6 +33,16 @@ This project is based on [CMake](https://cmake.org/). Simply generate project, M Note: Please use a 64-bit target on a 64-bit operating system. 32-bit builds on a 64-bit OS are not supported. +## Python Installation Instruction + +For Windows and Linux targets there exists prebuilt python wheel files which can be installed using + +``` +pip install pypbd +``` + +These are available for different Python Versions. See also here: [pyPBD](https://pypi.org/project/pyPBD/). + ## Documentation The API documentation can be found here: diff --git a/Simulation/BoundingSphereHierarchy.cpp b/Simulation/BoundingSphereHierarchy.cpp index 9bd8c9f3..1a0ce16b 100644 --- a/Simulation/BoundingSphereHierarchy.cpp +++ b/Simulation/BoundingSphereHierarchy.cpp @@ -120,6 +120,10 @@ void TetMeshBSH::init(const Vector3r *vertices, const unsigned int numVertices, } } +void TetMeshBSH::updateVertices(const Vector3r* vertices) +{ + m_vertices = vertices; +} void BVHTest::traverse(PointCloudBSH const& b1, TetMeshBSH const& b2, TraversalCallback func) diff --git a/Simulation/BoundingSphereHierarchy.h b/Simulation/BoundingSphereHierarchy.h index 40a1136f..86e1714f 100644 --- a/Simulation/BoundingSphereHierarchy.h +++ b/Simulation/BoundingSphereHierarchy.h @@ -44,6 +44,7 @@ namespace PBD const final; void compute_hull_approx(unsigned int b, unsigned int n, BoundingSphere& hull) const final; + void updateVertices(const Vector3r* vertices); private: const Vector3r *m_vertices; diff --git a/Simulation/CollisionDetection.cpp b/Simulation/CollisionDetection.cpp index 7972895e..cc65fe03 100644 --- a/Simulation/CollisionDetection.cpp +++ b/Simulation/CollisionDetection.cpp @@ -5,10 +5,16 @@ using namespace PBD; using namespace Utilities; int CollisionDetection::CollisionObjectWithoutGeometry::TYPE_ID = IDFactory::getId(); +const unsigned int CollisionDetection::RigidBodyContactType = 0; +const unsigned int CollisionDetection::ParticleContactType = 1; +const unsigned int CollisionDetection::ParticleRigidBodyContactType = 2; +const unsigned int CollisionDetection::ParticleSolidContactType = 3; +const unsigned int CollisionDetection::CollisionObject::RigidBodyCollisionObjectType = 0; +const unsigned int CollisionDetection::CollisionObject::TriangleModelCollisionObjectType = 1; +const unsigned int CollisionDetection::CollisionObject::TetModelCollisionObjectType = 2; -CollisionDetection::CollisionDetection() : - m_collisionObjects() +CollisionDetection::CollisionDetection() : m_collisionObjects() { m_collisionObjects.reserve(1000); m_contactCB = NULL; @@ -29,32 +35,31 @@ void CollisionDetection::cleanup() } void CollisionDetection::addRigidBodyContact(const unsigned int rbIndex1, const unsigned int rbIndex2, - const Vector3r &cp1, const Vector3r &cp2, - const Vector3r &normal, const Real dist, - const Real restitutionCoeff, const Real frictionCoeff) + const Vector3r &cp1, const Vector3r &cp2, + const Vector3r &normal, const Real dist, + const Real restitutionCoeff, const Real frictionCoeff) { if (m_contactCB) m_contactCB(RigidBodyContactType, rbIndex1, rbIndex2, cp1, cp2, normal, dist, restitutionCoeff, frictionCoeff, m_contactCBUserData); } void CollisionDetection::addParticleRigidBodyContact(const unsigned int particleIndex, const unsigned int rbIndex, - const Vector3r &cp1, const Vector3r &cp2, - const Vector3r &normal, const Real dist, - const Real restitutionCoeff, const Real frictionCoeff) + const Vector3r &cp1, const Vector3r &cp2, + const Vector3r &normal, const Real dist, + const Real restitutionCoeff, const Real frictionCoeff) { if (m_contactCB) m_contactCB(ParticleRigidBodyContactType, particleIndex, rbIndex, cp1, cp2, normal, dist, restitutionCoeff, frictionCoeff, m_contactCBUserData); } -void CollisionDetection::addParticleSolidContact(const unsigned int particleIndex, const unsigned int solidIndex, - const unsigned int tetIndex, const Vector3r &bary, const Vector3r &cp1, const Vector3r &cp2, - const Vector3r &normal, const Real dist, const Real restitutionCoeff, const Real frictionCoeff) +void CollisionDetection::addParticleSolidContact(const unsigned int particleIndex, const unsigned int solidIndex, + const unsigned int tetIndex, const Vector3r &bary, const Vector3r &cp1, const Vector3r &cp2, + const Vector3r &normal, const Real dist, const Real restitutionCoeff, const Real frictionCoeff) { if (m_solidContactCB) m_solidContactCB(ParticleSolidContactType, particleIndex, solidIndex, tetIndex, bary, cp1, cp2, normal, dist, restitutionCoeff, frictionCoeff, m_contactCBUserData); } - void CollisionDetection::addCollisionObject(const unsigned int bodyIndex, const unsigned int bodyType) { CollisionObjectWithoutGeometry *co = new CollisionObjectWithoutGeometry(); @@ -163,4 +168,3 @@ void CollisionDetection::updateAABB(const Vector3r &p, AABB &aabb) if (aabb.m_p[1][2] < p[2]) aabb.m_p[1][2] = p[2]; } - diff --git a/Simulation/CollisionDetection.h b/Simulation/CollisionDetection.h index 328b3214..2d04defa 100644 --- a/Simulation/CollisionDetection.h +++ b/Simulation/CollisionDetection.h @@ -10,27 +10,27 @@ namespace PBD class CollisionDetection { public: - static const unsigned int RigidBodyContactType = 0; - static const unsigned int ParticleContactType = 1; - static const unsigned int ParticleRigidBodyContactType = 2; - static const unsigned int ParticleSolidContactType = 3; - - typedef void(*ContactCallbackFunction)(const unsigned int contactType, const unsigned int bodyIndex1, const unsigned int bodyIndex2, - const Vector3r &cp1, const Vector3r &cp2, - const Vector3r &normal, const Real dist, - const Real restitutionCoeff, const Real frictionCoeff, void *userData); - - typedef void(*SolidContactCallbackFunction)(const unsigned int contactType, const unsigned int bodyIndex1, const unsigned int bodyIndex2, - const unsigned int tetIndex, const Vector3r &bary, - const Vector3r &cp1, const Vector3r &cp2, - const Vector3r &normal, const Real dist, - const Real restitutionCoeff, const Real frictionCoeff, void *userData); + static const unsigned int RigidBodyContactType; // = 0; + static const unsigned int ParticleContactType; // = 1; + static const unsigned int ParticleRigidBodyContactType; // = 2; + static const unsigned int ParticleSolidContactType; // = 3; + + typedef void (*ContactCallbackFunction)(const unsigned int contactType, const unsigned int bodyIndex1, const unsigned int bodyIndex2, + const Vector3r &cp1, const Vector3r &cp2, + const Vector3r &normal, const Real dist, + const Real restitutionCoeff, const Real frictionCoeff, void *userData); + + typedef void (*SolidContactCallbackFunction)(const unsigned int contactType, const unsigned int bodyIndex1, const unsigned int bodyIndex2, + const unsigned int tetIndex, const Vector3r &bary, + const Vector3r &cp1, const Vector3r &cp2, + const Vector3r &normal, const Real dist, + const Real restitutionCoeff, const Real frictionCoeff, void *userData); struct CollisionObject { - static const unsigned int RigidBodyCollisionObjectType = 0; - static const unsigned int TriangleModelCollisionObjectType = 1; - static const unsigned int TetModelCollisionObjectType = 2; + static const unsigned int RigidBodyCollisionObjectType; // = 0; + static const unsigned int TriangleModelCollisionObjectType; // = 1; + static const unsigned int TetModelCollisionObjectType; // = 2; AABB m_aabb; unsigned int m_bodyIndex; @@ -53,7 +53,7 @@ namespace PBD SolidContactCallbackFunction m_solidContactCB; void *m_contactCBUserData; void *m_solidContactCBUserData; - std::vector m_collisionObjects; + std::vector m_collisionObjects; void updateAABB(const Vector3r &p, AABB &aabb); @@ -67,24 +67,24 @@ namespace PBD void setTolerance(Real val) { m_tolerance = val; } void addRigidBodyContact(const unsigned int rbIndex1, const unsigned int rbIndex2, - const Vector3r &cp1, const Vector3r &cp2, - const Vector3r &normal, const Real dist, - const Real restitutionCoeff, const Real frictionCoeff); + const Vector3r &cp1, const Vector3r &cp2, + const Vector3r &normal, const Real dist, + const Real restitutionCoeff, const Real frictionCoeff); void addParticleRigidBodyContact(const unsigned int particleIndex, const unsigned int rbIndex, - const Vector3r &cp1, const Vector3r &cp2, - const Vector3r &normal, const Real dist, - const Real restitutionCoeff, const Real frictionCoeff); + const Vector3r &cp1, const Vector3r &cp2, + const Vector3r &normal, const Real dist, + const Real restitutionCoeff, const Real frictionCoeff); void addParticleSolidContact(const unsigned int particleIndex, const unsigned int solidIndex, - const unsigned int tetIndex, const Vector3r &bary, - const Vector3r &cp1, const Vector3r &cp2, - const Vector3r &normal, const Real dist, - const Real restitutionCoeff, const Real frictionCoeff); + const unsigned int tetIndex, const Vector3r &bary, + const Vector3r &cp1, const Vector3r &cp2, + const Vector3r &normal, const Real dist, + const Real restitutionCoeff, const Real frictionCoeff); virtual void addCollisionObject(const unsigned int bodyIndex, const unsigned int bodyType); - std::vector &getCollisionObjects() { return m_collisionObjects; } + std::vector &getCollisionObjects() { return m_collisionObjects; } virtual void collisionDetection(SimulationModel &model) = 0; diff --git a/Simulation/DistanceFieldCollisionDetection.cpp b/Simulation/DistanceFieldCollisionDetection.cpp index fdc0bd2d..9764d0c0 100644 --- a/Simulation/DistanceFieldCollisionDetection.cpp +++ b/Simulation/DistanceFieldCollisionDetection.cpp @@ -68,12 +68,26 @@ void DistanceFieldCollisionDetection::collisionDetection(SimulationModel &model) if (co->m_bodyType == CollisionDetection::CollisionObject::TriangleModelCollisionObjectType) { DistanceFieldCollisionObject *sco = (DistanceFieldCollisionObject*)co; + + TriangleModel* tm = triModels[co->m_bodyIndex]; + const unsigned int offset = tm->getIndexOffset(); + const IndexedFaceMesh& mesh = tm->getParticleMesh(); + const unsigned int numVert = mesh.numVertices(); + sco->m_bvh.init(&pd.getPosition(offset), numVert); sco->m_bvh.update(); } - - if (co->m_bodyType == CollisionDetection::CollisionObject::TetModelCollisionObjectType) + else if (co->m_bodyType == CollisionDetection::CollisionObject::TetModelCollisionObjectType) { + TetModel* tm = tetModels[co->m_bodyIndex]; + const unsigned int offset = tm->getIndexOffset(); + const IndexedTetMesh& mesh = tm->getParticleMesh(); + const unsigned int numVert = mesh.numVertices(); + DistanceFieldCollisionObject *sco = (DistanceFieldCollisionObject*)co; + sco->m_bvh.init(&pd.getPosition(offset), numVert); + sco->m_bvhTets.updateVertices(&pd.getPosition(offset)); + sco->m_bvhTets0.updateVertices(&pd.getPosition(offset)); + sco->m_bvh.update(); sco->m_bvhTets.update(); } diff --git a/Simulation/ParticleData.h b/Simulation/ParticleData.h index 7064249f..ff2e2eb6 100644 --- a/Simulation/ParticleData.h +++ b/Simulation/ParticleData.h @@ -74,9 +74,9 @@ namespace PBD return (unsigned int)m_x.size(); } - FORCE_INLINE const std::vector* getVertices() + FORCE_INLINE const std::vector& getVertices() const { - return &m_x; + return m_x; } }; @@ -255,9 +255,9 @@ namespace PBD return (unsigned int) m_x.size(); } - FORCE_INLINE const std::vector* getVertices() + FORCE_INLINE const std::vector& getVertices() const { - return &m_x; + return m_x; } /** Resize the array containing the particle data. diff --git a/Simulation/SimulationModel.cpp b/Simulation/SimulationModel.cpp index 978e5bf6..35969bdf 100644 --- a/Simulation/SimulationModel.cpp +++ b/Simulation/SimulationModel.cpp @@ -660,10 +660,12 @@ void SimulationModel::addRegularTriangleModel(const int width, const int height, const unsigned int nPoints = height * width; const unsigned int nFaces = nIndices / 3; + const auto modelIndex = m_triangleModels.size(); addTriangleModel(nPoints, nFaces, points.data(), indices.data(), uvIndices, uvs); + const auto offset = m_triangleModels[modelIndex]->getIndexOffset(); ParticleData& pd = getParticles(); - for (unsigned int i = 0; i < pd.getNumberOfParticles(); i++) + for (unsigned int i = offset; i < offset + m_triangleModels[modelIndex]->getParticleMesh().numVertices(); i++) pd.setMass(i, 1.0); } @@ -760,10 +762,12 @@ void SimulationModel::addRegularTetModel(const int width, const int height, cons } } } + const auto modelIndex = m_tetModels.size(); addTetModel(width * height * depth, (unsigned int)indices.size() / 4u, points.data(), indices.data()); + const auto offset = m_tetModels[modelIndex]->getIndexOffset(); ParticleData& pd = getParticles(); - for (unsigned int i = 0; i < pd.getNumberOfParticles(); i++) + for (unsigned int i = offset; i < offset + m_tetModels[modelIndex]->getParticleMesh().numVertices(); i++) { pd.setMass(i, 1.0); } diff --git a/Simulation/TetModel.cpp b/Simulation/TetModel.cpp index 47e5c354..102bca93 100644 --- a/Simulation/TetModel.cpp +++ b/Simulation/TetModel.cpp @@ -12,6 +12,9 @@ TetModel::TetModel() : m_visMesh(), m_particleMesh() { + m_initialX.setZero(); + m_initialR.setIdentity(); + m_initialScale.setOnes(); m_restitutionCoeff = static_cast(0.6); m_frictionCoeff = static_cast(0.2); } diff --git a/pyPBD/ParticleDataModule.cpp b/pyPBD/ParticleDataModule.cpp index e5b4cd1a..fd1c2b20 100644 --- a/pyPBD/ParticleDataModule.cpp +++ b/pyPBD/ParticleDataModule.cpp @@ -26,7 +26,7 @@ void ParticleDataModule(py::module m_sub) .def("size", &PBD::VertexData::size) //.def("getVertices", &PBD::VertexData::getVertices, py::return_value_policy::reference); .def("getVertices", [](PBD::VertexData& vd) -> py::memoryview { - void* base_ptr = const_cast(&(*vd.getVertices())[0][0]); + void* base_ptr = const_cast(&(vd.getVertices())[0][0]); int num_vert = vd.size(); return py::memoryview::from_buffer((Real*)base_ptr, { num_vert, 3 }, { sizeof(Real) * 3, sizeof(Real) }, true); }); @@ -52,7 +52,7 @@ void ParticleDataModule(py::module m_sub) .def("size", &PBD::ParticleData::size) //.def("getVertices", &PBD::ParticleData::getVertices, py::return_value_policy::reference); .def("getVertices", [](PBD::ParticleData &pd) -> py::memoryview { - void* base_ptr = const_cast(&(*pd.getVertices())[0][0]); + void* base_ptr = const_cast(&(pd.getVertices())[0][0]); int num_vert = pd.getNumberOfParticles(); return py::memoryview::from_buffer((Real*)base_ptr, { num_vert, 3 }, { sizeof(Real) * 3, sizeof(Real) }, true); }); diff --git a/pyPBD/SimulationModelModule.cpp b/pyPBD/SimulationModelModule.cpp index 48b54275..3e84a983 100644 --- a/pyPBD/SimulationModelModule.cpp +++ b/pyPBD/SimulationModelModule.cpp @@ -12,6 +12,38 @@ namespace py = pybind11; +PBD::CubicSDFCollisionDetection::GridPtr generateSDF(const std::vector &vertices, const std::vector& faces, const Eigen::Matrix& resolution) +{ + const unsigned int nFaces = faces.size()/3; +#ifdef USE_DOUBLE + Discregrid::TriangleMesh sdfMesh((vertices[0]).data(), faces.data(), vertices.size(), nFaces); +#else + // if type is float, copy vector to double vector + std::vector doubleVec; + doubleVec.resize(3 * vertices.size()); + for (unsigned int i = 0; i < vertices.size(); i++) + for (unsigned int j = 0; j < 3; j++) + doubleVec[3 * i + j] = vertices[i][j]; + Discregrid::TriangleMesh sdfMesh(&doubleVec[0], faces.data(), vertices.size(), nFaces); +#endif + Discregrid::MeshDistance md(sdfMesh); + Eigen::AlignedBox3d domain; + for (auto const& x : sdfMesh.vertices()) + { + domain.extend(x); + } + domain.max() += 0.1 * Eigen::Vector3d::Ones(); + domain.min() -= 0.1 * Eigen::Vector3d::Ones(); + + std::cout << "Set SDF resolution: " << resolution[0] << ", " << resolution[1] << ", " << resolution[2] << std::endl; + auto sdf = std::make_shared(domain, std::array({ resolution[0], resolution[1], resolution[2] })); + auto func = Discregrid::DiscreteGrid::ContinuousFunction{}; + func = [&md](Eigen::Vector3d const& xi) {return md.signedDistanceCached(xi); }; + std::cout << "Generate SDF\n"; + sdf->addFunction(func, true); + return sdf; +} + void SimulationModelModule(py::module m_sub) { py::class_(m_sub, "TriangleModel") @@ -63,101 +95,138 @@ void SimulationModelModule(py::module m_sub) .def("resetContacts", &PBD::SimulationModel::resetContacts) .def("updateConstraints", &PBD::SimulationModel::updateConstraints) .def("initConstraintGroups", &PBD::SimulationModel::initConstraintGroups) - - .def("addTriangleModel", []( - PBD::SimulationModel &model, - const unsigned int nPoints, - const unsigned int nFaces, - std::vector &points, - std::vector &indices, - const PBD::TriangleModel::ParticleMesh::UVIndices& uvIndices, - const PBD::TriangleModel::ParticleMesh::UVs& uvs) - { - model.addTriangleModel(nPoints, nFaces, points.data(), indices.data(), uvIndices, uvs); - }) - .def("addTriangleModelCollision", []( + .def("addTriangleModel", []( PBD::SimulationModel& model, - const unsigned int nPoints, - const unsigned int nFaces, std::vector& points, std::vector& indices, const PBD::TriangleModel::ParticleMesh::UVIndices& uvIndices, - const PBD::TriangleModel::ParticleMesh::UVs& uvs) + const PBD::TriangleModel::ParticleMesh::UVs& uvs, + const bool testMesh) { auto& triModels = model.getTriangleModels(); int i = triModels.size(); - model.addTriangleModel(nPoints, nFaces, points.data(), indices.data(), uvIndices, uvs); - PBD::ParticleData& pd = model.getParticles(); - const unsigned int nVert = triModels[i]->getParticleMesh().numVertices(); - unsigned int offset = triModels[i]->getIndexOffset(); - PBD::Simulation* sim = PBD::Simulation::getCurrent(); - PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); - if (cd != nullptr) - cd->addCollisionObjectWithoutGeometry(i, PBD::CollisionDetection::CollisionObject::TriangleModelCollisionObjectType, &pd.getPosition(offset), nVert, true); - }) - .def("addRegularTriangleModel", &PBD::SimulationModel::addRegularTriangleModel) - .def("addRegularTriangleModelCollision", [](PBD::SimulationModel &model, + model.addTriangleModel(points.size(), indices.size()/3, points.data(), indices.data(), uvIndices, uvs); + if (testMesh) + { + PBD::ParticleData& pd = model.getParticles(); + const unsigned int nVert = triModels[i]->getParticleMesh().numVertices(); + unsigned int offset = triModels[i]->getIndexOffset(); + PBD::Simulation* sim = PBD::Simulation::getCurrent(); + PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); + if (cd != nullptr) + cd->addCollisionObjectWithoutGeometry(i, PBD::CollisionDetection::CollisionObject::TriangleModelCollisionObjectType, &pd.getPosition(offset), nVert, true); + } + return triModels[i]; + }, py::arg("points"), py::arg("indices"), py::arg("uvIndices") = PBD::TriangleModel::ParticleMesh::UVIndices(), + py::arg("uvs") = PBD::TriangleModel::ParticleMesh::UVs(), py::arg("testMesh") = false, + py::return_value_policy::reference) + .def("addRegularTriangleModel", [](PBD::SimulationModel &model, const int width, const int height, const Vector3r& translation, const Matrix3r& rotation, - const Vector2r& scale) + const Vector2r& scale, + const bool testMesh) { auto &triModels = model.getTriangleModels(); int i = triModels.size(); model.addRegularTriangleModel(width, height, translation, rotation, scale); - PBD::ParticleData& pd = model.getParticles(); - const unsigned int nVert = triModels[i]->getParticleMesh().numVertices(); - unsigned int offset = triModels[i]->getIndexOffset(); - PBD::Simulation* sim = PBD::Simulation::getCurrent(); - PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); - if (cd != nullptr) - cd->addCollisionObjectWithoutGeometry(i, PBD::CollisionDetection::CollisionObject::TriangleModelCollisionObjectType, &pd.getPosition(offset), nVert, true); - }) + if (testMesh) + { + PBD::ParticleData& pd = model.getParticles(); + const unsigned int nVert = triModels[i]->getParticleMesh().numVertices(); + unsigned int offset = triModels[i]->getIndexOffset(); + PBD::Simulation* sim = PBD::Simulation::getCurrent(); + PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); + if (cd != nullptr) + cd->addCollisionObjectWithoutGeometry(i, PBD::CollisionDetection::CollisionObject::TriangleModelCollisionObjectType, &pd.getPosition(offset), nVert, true); + } + return triModels[i]; + }, py::arg("width"), py::arg("height"), py::arg("translation") = Vector3r::Zero(), + py::arg("rotation") = Matrix3r::Identity(), py::arg("scale") = Vector2r::Ones(), py::arg("testMesh") = false, + py::return_value_policy::reference) .def("addTetModel", []( PBD::SimulationModel& model, - const unsigned int nPoints, - const unsigned int nTets, - std::vector& points, - std::vector& indices) - { - model.addTetModel(nPoints, nTets, points.data(), indices.data()); - }) - .def("addTetModelCollision", []( - PBD::SimulationModel& model, - const unsigned int nPoints, - const unsigned int nTets, std::vector& points, - std::vector& indices) + std::vector& indices, + const bool testMesh, + bool generateCollisionObject, const Eigen::Matrix& resolution) { auto& tetModels = model.getTetModels(); int i = tetModels.size(); - model.addTetModel(nPoints, nTets, points.data(), indices.data()); + model.addTetModel(points.size(), indices.size()/4, points.data(), indices.data()); + PBD::ParticleData& pd = model.getParticles(); - const unsigned int nVert = tetModels[i]->getParticleMesh().numVertices(); - unsigned int offset = tetModels[i]->getIndexOffset(); + PBD::TetModel* tetModel = tetModels[i]; + const unsigned int nVert = tetModel->getParticleMesh().numVertices(); + unsigned int offset = tetModel->getIndexOffset(); PBD::Simulation* sim = PBD::Simulation::getCurrent(); - PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); - if (cd != nullptr) - cd->addCollisionObjectWithoutGeometry(i, PBD::CollisionDetection::CollisionObject::TetModelCollisionObjectType, &pd.getPosition(offset), nVert, true); - }) - .def("addRegularTetModel", &PBD::SimulationModel::addRegularTetModel) - .def("addRegularTetModelCollision", [](PBD::SimulationModel &model, + if (generateCollisionObject) + { + auto &surfaceMesh = tetModel->getSurfaceMesh(); + PBD::CubicSDFCollisionDetection::GridPtr sdf = generateSDF(points, surfaceMesh.getFaces(), resolution); + if (sdf != nullptr) + { + PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); + if (cd != nullptr) + { + auto index = cd->getCollisionObjects().size(); + cd->addCubicSDFCollisionObject(i, + PBD::CollisionDetection::CollisionObject::TetModelCollisionObjectType, + &pd.getPosition(offset), nVert, sdf, Vector3r::Ones(), testMesh, false); + + const unsigned int modelIndex = cd->getCollisionObjects()[index]->m_bodyIndex; + PBD::TetModel* tm = tetModels[modelIndex]; + const unsigned int offset = tm->getIndexOffset(); + const Utilities::IndexedTetMesh& mesh = tm->getParticleMesh(); + + ((PBD::DistanceFieldCollisionDetection::DistanceFieldCollisionObject*)cd->getCollisionObjects()[index])->initTetBVH(&pd.getPosition(offset), mesh.numVertices(), mesh.getTets().data(), mesh.numTets(), cd->getTolerance()); + } + } + } + else if (testMesh) + { + PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); + if (cd != nullptr) + { + auto index = cd->getCollisionObjects().size(); + cd->addCollisionObjectWithoutGeometry(i, PBD::CollisionDetection::CollisionObject::TetModelCollisionObjectType, &pd.getPosition(offset), nVert, true); + + const unsigned int modelIndex = cd->getCollisionObjects()[index]->m_bodyIndex; + PBD::TetModel* tm = tetModels[modelIndex]; + const unsigned int offset = tm->getIndexOffset(); + const Utilities::IndexedTetMesh& mesh = tm->getParticleMesh(); + + ((PBD::DistanceFieldCollisionDetection::DistanceFieldCollisionObject*)cd->getCollisionObjects()[index])->initTetBVH(&pd.getPosition(offset), mesh.numVertices(), mesh.getTets().data(), mesh.numTets(), cd->getTolerance()); + } + } + return tetModel; + }, py::arg("points"), py::arg("indices"), py::arg("testMesh") = false, + py::arg("generateCollisionObject") = false, py::arg("resolution") = Eigen::Matrix(30, 30, 30), + py::return_value_policy::reference) + .def("addRegularTetModel", [](PBD::SimulationModel &model, const int width, const int height, const int depth, const Vector3r& translation, const Matrix3r& rotation, - const Vector3r& scale) + const Vector3r& scale, + const bool testMesh) { auto &tetModels = model.getTetModels(); int i = tetModels.size(); model.addRegularTetModel(width, height, depth, translation, rotation, scale); - PBD::ParticleData& pd = model.getParticles(); - const unsigned int nVert = tetModels[i]->getParticleMesh().numVertices(); - unsigned int offset = tetModels[i]->getIndexOffset(); - PBD::Simulation* sim = PBD::Simulation::getCurrent(); - PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); - if (cd != nullptr) - cd->addCollisionObjectWithoutGeometry(i, PBD::CollisionDetection::CollisionObject::TetModelCollisionObjectType, &pd.getPosition(offset), nVert, true); - }) + if (testMesh) + { + PBD::ParticleData& pd = model.getParticles(); + const unsigned int nVert = tetModels[i]->getParticleMesh().numVertices(); + unsigned int offset = tetModels[i]->getIndexOffset(); + PBD::Simulation* sim = PBD::Simulation::getCurrent(); + PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); + if (cd != nullptr) + cd->addCollisionObjectWithoutGeometry(i, PBD::CollisionDetection::CollisionObject::TetModelCollisionObjectType, &pd.getPosition(offset), nVert, true); + } + return tetModels[i]; + }, py::arg("width"), py::arg("height"), py::arg("depth"), py::arg("translation") = Vector3r::Zero(), + py::arg("rotation") = Matrix3r::Identity(), py::arg("scale") = Vector3r::Ones(), py::arg("testMesh") = false, + py::return_value_policy::reference) .def("addLineModel", []( PBD::SimulationModel& model, const unsigned int nPoints, @@ -237,13 +306,14 @@ void SimulationModelModule(py::module m_sub) .def("addRigidBody", [](PBD::SimulationModel &model, const Real density, const PBD::VertexData& vertices, const Utilities::IndexedFaceMesh& mesh, - const Vector3r& x, const Real angle, const Vector3r &rotationAxis, + const Vector3r& translation, const Matrix3r& rotation, const Vector3r& scale, + const bool testMesh, const PBD::CubicSDFCollisionDetection::GridPtr sdf) { PBD::SimulationModel::RigidBodyVector& rbs = model.getRigidBodies(); PBD::RigidBody *rb = new PBD::RigidBody(); - rb->initBody(density, x, Quaternionr(AngleAxisr(angle, rotationAxis)), vertices, mesh, scale); + rb->initBody(density, translation, Quaternionr(rotation), vertices, mesh, scale); rbs.push_back(rb); if (sdf != nullptr) { @@ -251,15 +321,65 @@ void SimulationModelModule(py::module m_sub) PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); if (cd != nullptr) { - const std::vector* vertices = rb->getGeometry().getVertexDataLocal().getVertices(); - const unsigned int nVert = static_cast(vertices->size()); + const std::vector& vertices = rb->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert = static_cast(vertices.size()); cd->addCubicSDFCollisionObject(rbs.size() - 1, PBD::CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, - vertices->data(), nVert, sdf, scale, true, false); + vertices.data(), nVert, sdf, scale, testMesh, false); + } + } + return rb; + }, py::arg("density"), py::arg("vertices"), py::arg("mesh"), py::arg("translation") = Vector3r::Zero(), + py::arg("rotation") = Matrix3r::Identity(), py::arg("scale") = Vector3r::Ones(), py::arg("testMesh") = false, + py::arg("sdf"), + py::return_value_policy::reference) + .def("addRigidBody", [](PBD::SimulationModel &model, const Real density, + const PBD::VertexData& vertices, + const Utilities::IndexedFaceMesh& mesh, + const Vector3r& translation, const Matrix3r &rotation, + const Vector3r& scale, + const bool testMesh, + const bool generateCollisionObject, const Eigen::Matrix& resolution) + { + PBD::Simulation* sim = PBD::Simulation::getCurrent(); + PBD::SimulationModel::RigidBodyVector& rbs = model.getRigidBodies(); + PBD::RigidBody *rb = new PBD::RigidBody(); + auto i = rbs.size(); + rb->initBody(density, translation, Quaternionr(rotation), vertices, mesh, scale); + rbs.push_back(rb); + + if (generateCollisionObject) + { + PBD::CubicSDFCollisionDetection::GridPtr sdf = generateSDF(vertices.getVertices(), mesh.getFaces(), resolution); + if (sdf != nullptr) + { + PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); + if (cd != nullptr) + { + const std::vector& vertices = rb->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert = static_cast(vertices.size()); + cd->addCubicSDFCollisionObject(rbs.size() - 1, + PBD::CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, + vertices.data(), nVert, sdf, scale, testMesh, false); + } + } + } + else if (testMesh) + { + PBD::CubicSDFCollisionDetection* cd = dynamic_cast(sim->getTimeStep()->getCollisionDetection()); + if (cd != nullptr) + { + auto index = cd->getCollisionObjects().size(); + const std::vector& vertices = rbs[i]->getGeometry().getVertexDataLocal().getVertices(); + const unsigned int nVert = static_cast(vertices.size()); + cd->addCollisionObjectWithoutGeometry(i, PBD::CollisionDetection::CollisionObject::RigidBodyCollisionObjectType, vertices.data(), nVert, true); } } return rb; - }, py::return_value_policy::reference) + }, py::arg("density"), py::arg("vertices"), py::arg("mesh"), py::arg("translation") = Vector3r::Zero(), + py::arg("rotation") = Matrix3r::Identity(), py::arg("scale") = Vector3r::Ones(), py::arg("testMesh") = false, + py::arg("generateCollisionObject") = false, py::arg("resolution") = Eigen::Matrix(30,30,30), + py::return_value_policy::reference) ; } \ No newline at end of file diff --git a/pyPBD/examples/armadillo.py b/pyPBD/examples/armadillo.py index 45959f9f..bbc47376 100644 --- a/pyPBD/examples/armadillo.py +++ b/pyPBD/examples/armadillo.py @@ -21,28 +21,34 @@ def buildModel(): sim = pbd.Simulation.getCurrent() + + # Init simulation and collision detection with standard settings sim.initDefault() model = sim.getModel() rbs = model.getRigidBodies() + # Create the mesh for the solid model createMesh(simModel) + # Load geometry from OBJ file fileName2 = "../../data/models/torus.obj" (vdTorus, meshTorus) = pbd.OBJLoader.loadObjToMesh(fileName2, [1, 1, 1]) # torus - cd = sim.getTimeStep().getCollisionDetection() - torusSDF = pbd.CubicSDFCollisionDetection.generateSDF(vdTorus, meshTorus, [30, 30, 30]) - rb = model.addRigidBody(1.0, # density - vdTorus, meshTorus, # vertices, mesh - [0.0, 1.5, 0.0], # position - 0.0, [1,0,0], # angle, rotation axis - [2.0, 2.0, 2.0], # scaling - torusSDF) # signed distance field for collision detection + rb = model.addRigidBody(1.0, # density + vdTorus, meshTorus, # vertices, mesh + [0.0, 1.5, 0.0], # position + np.identity(3), # rotation matrix + [2.0, 2.0, 2.0], # scaling + testMesh=True, # test mesh vertices against other signed distance fields + generateCollisionObject= True, # automatically generate an SDF and a corresponding collision object + resolution=[30, 30, 30]) # resolution of the signed distance field rb.setMass(0.0) rb.setFrictionCoeff(0.3) - + + # Set collision tolerance (distance threshold) + cd = sim.getTimeStep().getCollisionDetection() cd.setTolerance(0.05) ts = sim.getTimeStep() @@ -54,75 +60,97 @@ def createMesh(simModel): sim = pbd.Simulation.getCurrent() model = sim.getModel() + # Load TetGen model from file fileNameNode = "../../data/models/armadillo_4k.node" fileNameEle = "../../data/models/armadillo_4k.ele" (positions, tets) = pbd.TetGenLoader.loadTetgenModel(fileNameNode, fileNameEle) - # move up and scale + # move up and scale vertices pos = np.array(positions) for x in pos: x[0] *= 1.5 x[1] = x[1]*1.5 + 6 x[2] *= 1.5 - model.addTetModelCollision(len(pos), int(len(tets)/4), pos, tets) + # Generata tetrahedra model + tetModel = model.addTetModel(pos, tets, testMesh=True) # init constraints - tetModel = model.getTetModels()[0] stiffness = 1.0 if (simModel == 5): stiffness = 100000 poissonRatio = 0.3 + + # Generate a default set of constraints for the tet model to simulate stretching and shearing resistance + # simModel: + # 1 = distance constraints (PBD) + # 2 = FEM tet constraints (PBD) + # 3 = strain tet constraints (PBD) + # 4 = shape matching + # 5 = distance constraints (XPBD) model.addSolidConstraints(tetModel, simModel, stiffness, poissonRatio, stiffness, False, False) - print("Number of triangles: " + str(tetModel.getParticleMesh().numTets())) + print("Number of tets: " + str(tetModel.getParticleMesh().numTets())) print("Number of vertices: " + str(len(pos))) - + +# Render all bodies def render(): sim = pbd.Simulation.getCurrent() model = sim.getModel() - rbs = model.getRigidBodies() - + # render meshes of rigid bodies + rbs = model.getRigidBodies() for rb in rbs: vd = rb.getGeometry().getVertexData() mesh = rb.getGeometry().getMesh() drawMesh(vd, mesh, 0, [0,0.2,0.7]) + # render mesh of tet model pd = model.getParticles() tetModel = model.getTetModels()[0] offset = tetModel.getIndexOffset() drawMesh(pd, tetModel.getSurfaceMesh(), offset, [0,0.2,0.7]) + # render time glPushMatrix() glLoadIdentity() drawText([-0.95,0.9], "Time: {:.2f}".format(pbd.TimeManager.getCurrent().getTime())) glPopMatrix() +# Perform simulation steps def timeStep(): sim = pbd.Simulation.getCurrent() model = sim.getModel() + + # We perform 8 simulation steps per render step for i in range(8): sim.getTimeStep().step(model) + # Update mesh normals of tet model for rendering for tetModel in model.getTetModels(): tetModel.updateMeshNormals(model.getParticles()) +# Reset the simulation def reset(): - pbd.Simulation.getCurrent().reset() - pbd.Simulation.getCurrent().getModel().cleanup() + sim = pbd.Simulation.getCurrent() + sim.reset() + sim.getModel().cleanup() + cd = sim.getTimeStep().getCollisionDetection() cd.cleanup() buildModel() def main(): + # Activate logger to output info on the console pbd.Logger.addConsoleSink(pbd.LogLevel.INFO) + # init OpenGL initGL(1280, 1024) - #initLights() gluLookAt (0, 10, -20, 0, 0, 0, 0, 1, 0) glRotatef(20.0, 0, 1, 0) + # build simulation model buildModel() + # start event loop running = True while running: for event in pygame.event.get(): diff --git a/pyPBD/examples/beam_model.py b/pyPBD/examples/beam_model.py index a074d65e..13b20860 100644 --- a/pyPBD/examples/beam_model.py +++ b/pyPBD/examples/beam_model.py @@ -34,7 +34,7 @@ def buildModel(): def createMesh(simModel): sim = pbd.Simulation.getCurrent() model = sim.getModel() - model.addRegularTetModel(width, height, depth, [5,0,0], np.identity(3), [10, 1.5, 1.5]) + tetModel = model.addRegularTetModel(width, height, depth, translation=[5,0,0], scale=[10, 1.5, 1.5]) pd = model.getParticles() @@ -45,18 +45,16 @@ def createMesh(simModel): pd.setMass(i*height*depth + j*depth + k, 0.0) # init constraints - tetModels = model.getTetModels() stiffness = 1.0 if (simModel == 5): stiffness = 100000 poissonRatio = 0.3 - for tetModel in tetModels: - model.addSolidConstraints(tetModel, simModel, stiffness, poissonRatio, stiffness, False, False) + model.addSolidConstraints(tetModel, simModel, stiffness, poissonRatio, stiffness, False, False) - tetModel.updateMeshNormals(pd); - - print("Number of tets: " + str(tetModel.getParticleMesh().numTets())) - print("Number of vertices: " + str(width*height*depth)) + tetModel.updateMeshNormals(pd); + + print("Number of tets: " + str(tetModel.getParticleMesh().numTets())) + print("Number of vertices: " + str(width*height*depth)) def render(): sim = pbd.Simulation.getCurrent() diff --git a/pyPBD/examples/bunny_cloth.py b/pyPBD/examples/bunny_cloth.py index d033f344..852ffd36 100644 --- a/pyPBD/examples/bunny_cloth.py +++ b/pyPBD/examples/bunny_cloth.py @@ -28,41 +28,52 @@ def buildModel(): sim = pbd.Simulation.getCurrent() + + # Init simulation and collision detection with standard settings sim.initDefault() model = sim.getModel() - rbs = model.getRigidBodies() + # Create the mesh for the cloth model createMesh(simModel, bendingModel) + # Load geometry from OBJ files fileName = "../../data/models/cube.obj" (vd, mesh) = pbd.OBJLoader.loadObjToMesh(fileName, [1, 1, 1]) fileName2 = "../../data/models/torus.obj" (vdTorus, meshTorus) = pbd.OBJLoader.loadObjToMesh(fileName2, [1, 1, 1]) - # floor + ## There are two ways to define a rigid body with a corresponding collision object: + # + # Floor: You can first generate a signed distance field floorSDF = pbd.CubicSDFCollisionDetection.generateSDF(vd, mesh, [30, 30, 30]) + # and use it as parameter for addRigidBody(). In this way the SDF can + # be directly used for multiple bodies and it is stored only once. rb = model.addRigidBody(1.0, # density vd, mesh, # vertices, mesh [0.0, -2.5, 0.0], # position - 0.0, [1,0,0], # angle, rotation axis + np.identity(3), # rotation matrix [100.0, 1.0, 100.0], # scaling + True, # test mesh vertices against other signed distance fields floorSDF) # signed distance field for collision detection - rb.setMass(0.0) + rb.setMass(0.0) # make body static - # torus - cd = sim.getTimeStep().getCollisionDetection() - torusSDF = pbd.CubicSDFCollisionDetection.generateSDF(vdTorus, meshTorus, [30, 30, 30]) - rb = model.addRigidBody(1.0, # density - vdTorus, meshTorus, # vertices, mesh - [0.0, 1.5, 0.0], # position - 0.0, [1,0,0], # angle, rotation axis - [2.0, 2.0, 2.0], # scaling - torusSDF) # signed distance field for collision detection - rb.setMass(0.0) - rb.setFrictionCoeff(0.1) - + # Torus: Alternatively, addRigidBody() automatically generates the SDF for you + # by setting generateCollisionObject= True. + rb = model.addRigidBody(1.0, # density + vdTorus, meshTorus, # vertices, mesh + [0.0, 1.5, 0.0], # position + np.identity(3), # rotation matrix + [2.0, 2.0, 2.0], # scaling + testMesh=True, # test mesh vertices against other signed distance fields + generateCollisionObject= True, # automatically generate an SDF and a corresponding collision object + resolution=[30, 30, 30]) # resolution of the signed distance field + rb.setMass(0.0) # make body static + rb.setFrictionCoeff(0.1) # set friction coefficient + + # Set collision tolerance (distance threshold) + cd = sim.getTimeStep().getCollisionDetection() cd.setTolerance(0.05) ts = sim.getTimeStep() @@ -74,6 +85,7 @@ def createMesh(simModel, bendingModel): sim = pbd.Simulation.getCurrent() model = sim.getModel() + # Load geometry from file fileName2 = "../../data/models/bunny_10k.obj" (vd, mesh) = pbd.OBJLoader.loadObjToMesh(fileName2, [5,5,5]) @@ -81,76 +93,96 @@ def createMesh(simModel, bendingModel): pos = np.array(vd.getVertices()) for x in pos: x[1] += 5 - model.addTriangleModelCollision(vd.size(), mesh.numFaces(), pos, np.array(mesh.getFaces()), [], []) - - # Set mass of points to zero => make it static - #pd.setMass(0, 0.0) - #pd.setMass((nRows-1)*nCols, 0.0) + triModel = model.addTriangleModel(pos, mesh.getFaces(), testMesh=True) # init constraints - triModels = model.getTriangleModels() - for triModel in triModels: - stiffness = 1.0 - if (simModel == 4): - stiffness = 100000 - poissonRatio = 0.3 - model.addClothConstraints(triModel, simModel, stiffness, stiffness, stiffness, stiffness, - poissonRatio, poissonRatio, False, False) - - bending_stiffness = 0.5 - if (bendingModel == 3): - bending_stiffness = 1000.0 - model.addBendingConstraints(triModel, bendingModel, bending_stiffness) - - print("Number of triangles: " + str(triModel.getParticleMesh().numFaces())) - print("Number of vertices: " + str(nRows*nCols)) + stiffness = 1.0 + if (simModel == 4): + stiffness = 100000 + poissonRatio = 0.3 + + # Generate a default set of constraints for the cloth model to simulate stretching and shearing resistance + # simModel: + # 1 = distance constraints (PBD) + # 2 = FEM triangle constraints (PBD) + # 3 = strain triangle constraints (PBD) + # 4 = distance constraints (XPBD) + model.addClothConstraints(triModel, simModel, stiffness, stiffness, stiffness, stiffness, + poissonRatio, poissonRatio, False, False) + + bending_stiffness = 0.5 + if (bendingModel == 3): + bending_stiffness = 1000.0 + # Generate a default set of bending constraints for the cloth model + # bendingModel: + # 1 = dihedral angle (PBD) + # 2 = isometric bending (PBD) + # 3 = isometric bending (XPBD) + model.addBendingConstraints(triModel, bendingModel, bending_stiffness) + + print("Number of triangles: " + str(triModel.getParticleMesh().numFaces())) + print("Number of vertices: " + str(nRows*nCols)) + +# Render all bodies def render(): sim = pbd.Simulation.getCurrent() model = sim.getModel() - rbs = model.getRigidBodies() - + # render meshes of rigid bodies + rbs = model.getRigidBodies() for rb in rbs: vd = rb.getGeometry().getVertexData() mesh = rb.getGeometry().getMesh() drawMesh(vd, mesh, 0, [0,0.2,0.7]) + # render mesh of cloth model pd = model.getParticles() triModel = model.getTriangleModels()[0] offset = triModel.getIndexOffset() drawMesh(pd, triModel.getParticleMesh(), offset, [0,0.3,0.8]) + # render time glPushMatrix() glLoadIdentity() drawText([-0.95,0.9], "Time: {:.2f}".format(pbd.TimeManager.getCurrent().getTime())) glPopMatrix() +# Perform simulation steps def timeStep(): sim = pbd.Simulation.getCurrent() model = sim.getModel() + + # We perform 8 simulation steps per render step for i in range(8): sim.getTimeStep().step(model) + # Update mesh normals of cloth model for rendering for triModel in model.getTriangleModels(): triModel.updateMeshNormals(model.getParticles()) +# Reset the simulation def reset(): - pbd.Simulation.getCurrent().reset() - pbd.Simulation.getCurrent().getModel().cleanup() + sim = pbd.Simulation.getCurrent() + sim.reset() + sim.getModel().cleanup() + cd = sim.getTimeStep().getCollisionDetection() cd.cleanup() buildModel() def main(): + # Activate logger to output info on the console pbd.Logger.addConsoleSink(pbd.LogLevel.INFO) - + + # init OpenGL initGL(1280, 1024) - #initLights() gluLookAt (0, 10, -20, 0, 0, 0, 0, 1, 0) glRotatef(20.0, 0, 1, 0) + # build simulation model buildModel() + # start event loop running = True while running: for event in pygame.event.get(): diff --git a/pyPBD/examples/chain_model.py b/pyPBD/examples/chain_model.py index c82167ba..3f36f214 100644 --- a/pyPBD/examples/chain_model.py +++ b/pyPBD/examples/chain_model.py @@ -3,7 +3,9 @@ from OpenGL.GL import * from OpenGL.GLU import * + from render_tools import * +from math_tools import * import pypbd as pbd import numpy as np @@ -16,6 +18,8 @@ def buildModel(): sim = pbd.Simulation.getCurrent() + + # Init simulation and collision detection with standard settings sim.initDefault() createModel() @@ -29,76 +33,94 @@ def createModel(): model = sim.getModel() rbs = model.getRigidBodies() + # Load geometry from OBJ files fileName = "../../data/models/cube.obj" (vd, mesh) = pbd.OBJLoader.loadObjToMesh(fileName, [width, height, depth]) fileName2 = "../../data/models/bunny_10k.obj" (vd2, mesh2) = pbd.OBJLoader.loadObjToMesh(fileName2, [2, 2, 2]) + # add chain bodies density = 1.0 for i in range(numberOfBodies-1): model.addRigidBody(1.0, # density vd, mesh, # vertices, mesh [i*width, 0.0, 0.0], # position - 0.0, [1,0,0], # angle, rotation axis + np.identity(3), # angle, rotation axis [1, 1, 1], # scaling + False, # test mesh vertices against other signed distance fields None) # signed distance field for collision detection # Make first body static rbs[0].setMass(0.0) - # bunny + # add bunny t = np.array([0.411 + (numberOfBodies - 1.0)*width, -1.776, 0.356]) + rotation = rotation_matrix(math.pi/6.0, [0,0,1]) model.addRigidBody(density, # density vd2, mesh2, # vertices, mesh t, # position - math.pi/6.0, [0,0,1], # angle, rotation axis + rotation, # angle, rotation axis [1, 1, 1], # scaling + False, # test mesh vertices against other signed distance fields None) # signed distance field for collision detection + # generate ball joints for i in range(numberOfBodies-1): model.addBallJoint(i, i + 1, [i*width + 0.5*width, 0.0, 0.0]) - +# Render all bodies def render(): sim = pbd.Simulation.getCurrent() model = sim.getModel() + + # render meshes of rigid bodies rbs = model.getRigidBodies() for rb in rbs: vd = rb.getGeometry().getVertexData() mesh = rb.getGeometry().getMesh() drawMesh(vd, mesh, 0, [0,0.2,0.7]) + # render constraints cs = model.getConstraints() for c in cs: if (c.getTypeId() == pbd.BallJoint.TYPE_ID): drawSphere(c.jointInfo[:,2], 0.15, [0.0, 0.6, 0.2]) + # render time glPushMatrix() glLoadIdentity() drawText([-0.95,0.9], "Time: {:.2f}".format(pbd.TimeManager.getCurrent().getTime())) glPopMatrix() +# Perform simulation steps def timeStep(): sim = pbd.Simulation.getCurrent() model = sim.getModel() + + # We perform 8 simulation steps per render step for i in range(8): sim.getTimeStep().step(model) - + +# Reset the simulation def reset(): pbd.Simulation.getCurrent().reset() pbd.Simulation.getCurrent().getModel().cleanup() buildModel() def main(): + # Activate logger to output info on the console pbd.Logger.addConsoleSink(pbd.LogLevel.INFO) - + + # init OpenGL initGL(1280, 1024) initLights() gluLookAt (1.5, -2, 20, 1.5, -2, 0, 0, 1, 0) + # build simulation model buildModel() + # start event loop running = True while running: for event in pygame.event.get(): diff --git a/pyPBD/examples/cloth_collision.py b/pyPBD/examples/cloth_collision.py index af2a3675..5a65ff38 100644 --- a/pyPBD/examples/cloth_collision.py +++ b/pyPBD/examples/cloth_collision.py @@ -28,41 +28,51 @@ def buildModel(): sim = pbd.Simulation.getCurrent() + + # Init simulation and collision detection with standard settings sim.initDefault() model = sim.getModel() - rbs = model.getRigidBodies() - + # Create the mesh for the cloth model createMesh(simModel, bendingModel) + # Load geometry from OBJ files fileName = "../../data/models/cube.obj" (vd, mesh) = pbd.OBJLoader.loadObjToMesh(fileName, [1, 1, 1]) fileName2 = "../../data/models/torus.obj" (vdTorus, meshTorus) = pbd.OBJLoader.loadObjToMesh(fileName2, [1, 1, 1]) - # floor + ## There are two ways to define a rigid body with a corresponding collision object: + # + # Floor: You can first generate a signed distance field floorSDF = pbd.CubicSDFCollisionDetection.generateSDF(vd, mesh, [30, 30, 30]) + # and use it as parameter for addRigidBody(). In this way the SDF can + # be directly used for multiple bodies and it is stored only once. rb = model.addRigidBody(1.0, # density vd, mesh, # vertices, mesh [0.0, -2.5, 0.0], # position - 0.0, [1,0,0], # angle, rotation axis + np.identity(3), # rotation matrix [100.0, 1.0, 100.0], # scaling + True, # test mesh vertices against other signed distance fields floorSDF) # signed distance field for collision detection - rb.setMass(0.0) + rb.setMass(0.0) # make body static - # torus - cd = sim.getTimeStep().getCollisionDetection() - torusSDF = pbd.CubicSDFCollisionDetection.generateSDF(vdTorus, meshTorus, [30, 30, 30]) - rb = model.addRigidBody(1.0, # density - vdTorus, meshTorus, # vertices, mesh - [0.0, 1.5, 0.0], # position - 0.0, [1,0,0], # angle, rotation axis - [2.0, 2.0, 2.0], # scaling - torusSDF) # signed distance field for collision detection - rb.setMass(0.0) - rb.setFrictionCoeff(0.1) - + # Torus: Alternatively, addRigidBody() automatically generates the SDF for you + # by setting generateCollisionObject= True. + rb = model.addRigidBody(1.0, # density + vdTorus, meshTorus, # vertices, mesh + [0.0, 1.5, 0.0], # position + np.identity(3), # rotation matrix + [2.0, 2.0, 2.0], # scaling + testMesh=True, # test mesh vertices against other signed distance fields + generateCollisionObject= True, # automatically generate an SDF and a corresponding collision object + resolution=[30, 30, 30]) # resolution of the signed distance field + rb.setMass(0.0) # make body static + rb.setFrictionCoeff(0.1) # set friction coefficient + + # Set collision tolerance (distance threshold) + cd = sim.getTimeStep().getCollisionDetection() cd.setTolerance(0.05) ts = sim.getTimeStep() @@ -73,76 +83,102 @@ def buildModel(): def createMesh(simModel, bendingModel): sim = pbd.Simulation.getCurrent() model = sim.getModel() - model.addRegularTriangleModelCollision(nCols, nRows, [-5.0, 4.0, -5.0], rotation_matrix(math.pi*0.5, [1.0, 0.0, 0.0]), [width, height]) - - # Set mass of points to zero => make it static - #pd.setMass(0, 0.0) - #pd.setMass((nRows-1)*nCols, 0.0) + + # Generate a regular triangle model with nCols*nRows vertices + triModel = model.addRegularTriangleModel(nCols, nRows, + translation=[-5.0, 4.0, -5.0], # position + rotation=rotation_matrix(math.pi*0.5, [1.0, 0.0, 0.0]), # rotation matrix + scale=[width, height], # scaling + testMesh=True) # test mesh vertices against other signed distance fields # init constraints - triModels = model.getTriangleModels() - for triModel in triModels: - stiffness = 1.0 - if (simModel == 4): - stiffness = 100000 - poissonRatio = 0.3 - model.addClothConstraints(triModel, simModel, stiffness, stiffness, stiffness, stiffness, - poissonRatio, poissonRatio, False, False) + stiffness = 1.0 + if (simModel == 4): + stiffness = 100000 + poissonRatio = 0.3 + + # Generate a default set of constraints for the cloth model to simulate stretching and shearing resistance + # simModel: + # 1 = distance constraints (PBD) + # 2 = FEM triangle constraints (PBD) + # 3 = strain triangle constraints (PBD) + # 4 = distance constraints (XPBD) + model.addClothConstraints(triModel, simModel, stiffness, stiffness, stiffness, stiffness, + poissonRatio, poissonRatio, False, False) - bending_stiffness = 0.01 - if (bendingModel == 3): - bending_stiffness = 100.0 - model.addBendingConstraints(triModel, bendingModel, bending_stiffness) - - print("Number of triangles: " + str(triModel.getParticleMesh().numFaces())) - print("Number of vertices: " + str(nRows*nCols)) + bending_stiffness = 0.01 + if (bendingModel == 3): + bending_stiffness = 100.0 + # Generate a default set of bending constraints for the cloth model + # bendingModel: + # 1 = dihedral angle (PBD) + # 2 = isometric bending (PBD) + # 3 = isometric bending (XPBD) + model.addBendingConstraints(triModel, bendingModel, bending_stiffness) + + print("Number of triangles: " + str(triModel.getParticleMesh().numFaces())) + print("Number of vertices: " + str(nRows*nCols)) + +# Render all bodies def render(): sim = pbd.Simulation.getCurrent() model = sim.getModel() - rbs = model.getRigidBodies() - + # render meshes of rigid bodies + rbs = model.getRigidBodies() for rb in rbs: vd = rb.getGeometry().getVertexData() mesh = rb.getGeometry().getMesh() drawMesh(vd, mesh, 0, [0,0.2,0.7]) - + + # render mesh of cloth model pd = model.getParticles() triModel = model.getTriangleModels()[0] offset = triModel.getIndexOffset() drawMesh(pd, triModel.getParticleMesh(), offset, [0,0.3,0.8]) + # render time glPushMatrix() glLoadIdentity() drawText([-0.95,0.9], "Time: {:.2f}".format(pbd.TimeManager.getCurrent().getTime())) glPopMatrix() +# Perform simulation steps def timeStep(): sim = pbd.Simulation.getCurrent() model = sim.getModel() + + # We perform 8 simulation steps per render step for i in range(8): sim.getTimeStep().step(model) + # Update mesh normals of cloth model for rendering for triModel in model.getTriangleModels(): triModel.updateMeshNormals(model.getParticles()) +# Reset the simulation def reset(): - pbd.Simulation.getCurrent().reset() - pbd.Simulation.getCurrent().getModel().cleanup() + sim = pbd.Simulation.getCurrent() + sim.reset() + sim.getModel().cleanup() + cd = sim.getTimeStep().getCollisionDetection() cd.cleanup() buildModel() def main(): + # Activate logger to output info on the console pbd.Logger.addConsoleSink(pbd.LogLevel.INFO) - + + # init OpenGL initGL(1280, 1024) - #initLights() gluLookAt (0, 10, -20, 0, 0, 0, 0, 1, 0) glRotatef(20.0, 0, 1, 0) + # build simulation model buildModel() + # start event loop running = True while running: for event in pygame.event.get(): diff --git a/pyPBD/examples/cloth_model.py b/pyPBD/examples/cloth_model.py index 32be9f14..990ef7e1 100644 --- a/pyPBD/examples/cloth_model.py +++ b/pyPBD/examples/cloth_model.py @@ -41,7 +41,13 @@ def buildModel(): def createMesh(simModel, bendingModel): sim = pbd.Simulation.getCurrent() model = sim.getModel() - model.addRegularTriangleModel(nCols, nRows, [0,0,0], rotation_matrix(math.pi*0.5, [1.0, 0.0, 0.0]), [width, height]) + + # Generate a regular triangle model with nCols*nRows vertices + triModel = model.addRegularTriangleModel(nCols, nRows, + [0,0,0], # position + rotation_matrix(math.pi*0.5, [1.0, 0.0, 0.0]), # rotation matrix + [width, height], # scaling + testMesh=False) # test mesh vertices against other signed distance fields # Set mass of points to zero => make it static pd = model.getParticles() @@ -49,59 +55,83 @@ def createMesh(simModel, bendingModel): pd.setMass(nRows-1, 0.0) # init constraints - triModels = model.getTriangleModels() - for triModel in triModels: - stiffness = 1.0 - if (simModel == 4): - stiffness = 100000 - poissonRatio = 0.3 - model.addClothConstraints(triModel, simModel, stiffness, stiffness, stiffness, stiffness, - poissonRatio, poissonRatio, False, False) + stiffness = 1.0 + if (simModel == 4): + stiffness = 100000 + poissonRatio = 0.3 + + # Generate a default set of constraints for the cloth model to simulate stretching and shearing resistance + # simModel: + # 1 = distance constraints (PBD) + # 2 = FEM triangle constraints (PBD) + # 3 = strain triangle constraints (PBD) + # 4 = distance constraints (XPBD) + model.addClothConstraints(triModel, simModel, stiffness, stiffness, stiffness, stiffness, + poissonRatio, poissonRatio, False, False) - bending_stiffness = 0.01 - if (bendingModel == 3): - bending_stiffness = 50.0 - model.addBendingConstraints(triModel, bendingModel, bending_stiffness) - - print("Number of triangles: " + str(triModel.getParticleMesh().numFaces())) - print("Number of vertices: " + str(nRows*nCols)) + bending_stiffness = 0.01 + if (bendingModel == 3): + bending_stiffness = 50.0 + # Generate a default set of bending constraints for the cloth model + # bendingModel: + # 1 = dihedral angle (PBD) + # 2 = isometric bending (PBD) + # 3 = isometric bending (XPBD) + model.addBendingConstraints(triModel, bendingModel, bending_stiffness) + + print("Number of triangles: " + str(triModel.getParticleMesh().numFaces())) + print("Number of vertices: " + str(nRows*nCols)) + +# Render all bodies def render(): sim = pbd.Simulation.getCurrent() model = sim.getModel() + + # render mesh of cloth model pd = model.getParticles() triModel = model.getTriangleModels()[0] offset = triModel.getIndexOffset() drawMesh(pd, triModel.getParticleMesh(), offset, [0,0.3,0.8]) + # render time glPushMatrix() glLoadIdentity() drawText([-0.95,0.9], "Time: {:.2f}".format(pbd.TimeManager.getCurrent().getTime())) glPopMatrix() +# Perform simulation steps def timeStep(): sim = pbd.Simulation.getCurrent() model = sim.getModel() + + # We perform 8 simulation steps per render step for i in range(8): sim.getTimeStep().step(model) + # Update mesh normals of cloth model for rendering for triModel in model.getTriangleModels(): triModel.updateMeshNormals(model.getParticles()) - + +# Reset the simulation def reset(): pbd.Simulation.getCurrent().reset() pbd.Simulation.getCurrent().getModel().cleanup() buildModel() def main(): + # Activate logger to output info on the console pbd.Logger.addConsoleSink(pbd.LogLevel.INFO) - + + # init OpenGL initGL(1280, 1024) gluLookAt (5, 10, 20, 5, -5, 0, 0, 1, 0) glRotatef(20.0, 0, 1, 0) + # build simulation model buildModel() + # start event loop running = True while running: for event in pygame.event.get(): diff --git a/pyPBD/examples/math_tools.py b/pyPBD/examples/math_tools.py index f927f235..f8bfd53f 100644 --- a/pyPBD/examples/math_tools.py +++ b/pyPBD/examples/math_tools.py @@ -1,4 +1,6 @@ import math +import numpy as np + ###################################################### # compute rotation matrix @@ -28,46 +30,8 @@ def rotation_matrix(angle, axis): ys=y*s; zs=z*s; - return [[c + x2*c1, xyc-zs, xzc+ys], - [xyc+zs, c+y2*c1, yzc-xs], - [xzc-ys, yzc+xs, c+z2*c1]] + return np.array(((c + x2*c1, xyc-zs, xzc+ys), + (xyc+zs, c+y2*c1, yzc-xs), + (xzc-ys, yzc+xs, c+z2*c1))) -###################################################### -# compute matrix vector product -###################################################### -def matrix_vec_product(A, v): - res = [0,0,0] - for i in range(0,3): - for j in range(0,3): - res[i] += A[i][j] * v[j]; - return res - -###################################################### -# compute cross product -###################################################### -def cross_product(a, b): - res = [0,0,0] - res[0] = a[1]*b[2] - a[2]*b[1]; - res[1] = a[2]*b[0] - a[0]*b[2]; - res[2] = a[0]*b[1] - a[1]*b[0]; - return res - -###################################################### -# scale vector -###################################################### -def scale_vector(v, s): - res = [0,0,0] - res[0] = s*v[0]; - res[1] = s*v[1]; - res[2] = s*v[2]; - return res - -###################################################### -# add vector -###################################################### -def add_vector(v1, v2): - res = [0,0,0] - res[0] = v1[0] + v2[0]; - res[1] = v1[1] + v2[1]; - res[2] = v1[2] + v2[2]; - return res \ No newline at end of file + \ No newline at end of file diff --git a/pyPBD/examples/pendulum.py b/pyPBD/examples/pendulum.py index 98545a80..c492ce36 100644 --- a/pyPBD/examples/pendulum.py +++ b/pyPBD/examples/pendulum.py @@ -13,6 +13,8 @@ def buildModel(): sim = pbd.Simulation.getCurrent() + + # Init simulation and collision detection with standard settings sim.initDefault() model = sim.getModel() @@ -21,18 +23,24 @@ def buildModel(): ts = sim.getTimeStep() ts.setValueUInt(pbd.TimeStepController.NUM_SUB_STEPS, 10) + # add particles dx = size / (numParticles - 1) for i in range(numParticles): pd.addVertex([dx*i,0,0]) - pd.setAcceleration(i, [0,-9.81,0]) + + # link them by distance constraints for i in range(numParticles-1): model.addDistanceConstraint_XPBD(i,i+1, 1000000.0) + # make first particle static pd.setMass(0, 0.0) +# render pendulum def render(): sim = pbd.Simulation.getCurrent() model = sim.getModel() + + # render distance constraints pd = model.getParticles() glLineWidth(2.0) glColor3f(1.0, 1.0, 1.0) @@ -41,6 +49,7 @@ def render(): glVertex3fv(pd.getPosition(i)) glEnd() + # render particles glPointSize(8.0) glColor3f(0.1, 0.7, 0.3) glBegin(GL_POINTS) @@ -48,30 +57,40 @@ def render(): glVertex3fv(pd.getPosition(i)) glEnd() + # render time glPushMatrix() glLoadIdentity() drawText([-0.95,0.9], "Time: {:.2f}".format(pbd.TimeManager.getCurrent().getTime())) glPopMatrix() +# Perform simulation steps def timeStep(): sim = pbd.Simulation.getCurrent() model = sim.getModel() + + # We perform 8 simulation steps per render step for i in range(8): sim.getTimeStep().step(model) +# Reset the simulation def reset(): - pbd.Simulation.getCurrent().reset() - pbd.Simulation.getCurrent().getModel().cleanup() - buildModel() + sim = pbd.Simulation.getCurrent() + sim.reset() + sim.getModel().cleanup() + buildModel() def main(): + # Activate logger to output info on the console pbd.Logger.addConsoleSink(pbd.LogLevel.INFO) - + + # init OpenGL initGL(1280, 1024) gluLookAt (0, -2, -10, 0, -2, 0, 0, 1, 0) + # build simulation model buildModel() + # start event loop running = True while running: for event in pygame.event.get(): diff --git a/pyPBD/examples/render_tools.py b/pyPBD/examples/render_tools.py index ec860776..06c27a07 100644 --- a/pyPBD/examples/render_tools.py +++ b/pyPBD/examples/render_tools.py @@ -58,7 +58,8 @@ def drawMesh(pd, mesh, offset, color): #glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 15.0) #glColor3fv(color) - vertices = np.reshape(pd.getVertices(), -1) + v = np.array(pd.getVertices()) + vertices = np.reshape(v[offset:], -1) normals = np.reshape(np.negative(np.array(mesh.getVertexNormals())), -1) #glEnableClientState(GL_VERTEX_ARRAY) diff --git a/pyPBD/examples/rigid_body_cloth_coupling.py b/pyPBD/examples/rigid_body_cloth_coupling.py index a24b4d76..3555b158 100644 --- a/pyPBD/examples/rigid_body_cloth_coupling.py +++ b/pyPBD/examples/rigid_body_cloth_coupling.py @@ -20,6 +20,8 @@ def buildModel(): sim = pbd.Simulation.getCurrent() + + # Init simulation and collision detection with standard settings sim.initDefault() model = sim.getModel() @@ -34,7 +36,9 @@ def buildModel(): # 3 = isometric bending (XPBD) bendingModel = 2 + # Create the mesh for the cloth model createMesh(simModel, bendingModel) + # Create rigid bodies createRigidBodyModel() ts = sim.getTimeStep() @@ -45,68 +49,74 @@ def buildModel(): def createMesh(simModel, bendingModel): sim = pbd.Simulation.getCurrent() model = sim.getModel() - model.addRegularTriangleModel(nCols, nRows, [-5,4,-5], rotation_matrix(math.pi*0.5, [1.0, 0.0, 0.0]), [clothWidth, clothHeight]) + + # Generate a regular triangle model with nCols*nRows vertices + triModel = model.addRegularTriangleModel(nCols, nRows, + translation=[-5.0, 4.0, -5.0], # position + rotation=rotation_matrix(math.pi*0.5, [1.0, 0.0, 0.0]), # rotation matrix + scale=[clothWidth, clothHeight], # scaling + testMesh=False) # test mesh vertices against other signed distance fields # init constraints - triModels = model.getTriangleModels() - for triModel in triModels: - stiffness = 1.0 - if (simModel == 4): - stiffness = 100000 - poissonRatio = 0.3 - model.addClothConstraints(triModel, simModel, stiffness, stiffness, stiffness, stiffness, - poissonRatio, poissonRatio, False, False) + stiffness = 1.0 + if (simModel == 4): + stiffness = 100000 + poissonRatio = 0.3 + + # Generate a default set of constraints for the cloth model to simulate stretching and shearing resistance + # simModel: + # 1 = distance constraints (PBD) + # 2 = FEM triangle constraints (PBD) + # 3 = strain triangle constraints (PBD) + # 4 = distance constraints (XPBD) + model.addClothConstraints(triModel, simModel, stiffness, stiffness, stiffness, stiffness, + poissonRatio, poissonRatio, False, False) - bending_stiffness = 0.01 - if (bendingModel == 3): - bending_stiffness = 50.0 - model.addBendingConstraints(triModel, bendingModel, bending_stiffness) + bending_stiffness = 0.01 + if (bendingModel == 3): + bending_stiffness = 50.0 - print("Number of triangles: " + str(triModel.getParticleMesh().numFaces())) - print("Number of vertices: " + str(nRows*nCols)) - - - -# Compute diagonal inertia tensor -def computeInertiaTensorBox(mass, width, height, depth): - Ix = (mass / 12.0) * (height*height + depth*depth) - Iy = (mass / 12.0) * (width*width + depth*depth) - Iz = (mass / 12.0) * (width*width + height*height) - return np.array([Ix, Iy, Iz]) + # Generate a default set of bending constraints for the cloth model + # bendingModel: + # 1 = dihedral angle (PBD) + # 2 = isometric bending (PBD) + # 3 = isometric bending (XPBD) + model.addBendingConstraints(triModel, bendingModel, bending_stiffness) + print("Number of triangles: " + str(triModel.getParticleMesh().numFaces())) + print("Number of vertices: " + str(nRows*nCols)) + + def createRigidBodyModel(): sim = pbd.Simulation.getCurrent() model = sim.getModel() rb = model.getRigidBodies() + # Load geometry from OBJ files fileName = "../../data/models/cube.obj" (vd, mesh) = pbd.OBJLoader.loadObjToMesh(fileName, [width, height, depth]) - (vd_static, mesh_static) = pbd.OBJLoader.loadObjToMesh(fileName, [0.5, 0.5, 0.5]) # -5, -5 - body = model.addRigidBody(1.0, # density - vd_static, mesh_static, # vertices, mesh - [-5.0, 0.0, -5.0], # position - 0.0, [1,0,0], # angle, rotation axis - [1, 1, 1], # scaling - None) # signed distance field for collision detection + body = model.addRigidBody(1.0, # density + vd_static, mesh_static, # vertices, mesh + translation=[-5.0, 0.0, -5.0], # position + testMesh=False, # no collision handling + generateCollisionObject=False) body.setMass(0.0) # dynamic body body = model.addRigidBody(1.0, vd, mesh, [-5.0, 1.0, -5.0], - 0.0, [1,0,0], - [1, 1, 1], - None) + testMesh=False, # no collision handling + generateCollisionObject=False) body = model.addRigidBody(1.0, vd, mesh, [-5.0, 3.0, -5.0], - 0.0, [1,0,0], - [1, 1, 1], - None) + testMesh=False, # no collision handling + generateCollisionObject=False) model.addBallJoint(0, 1, [-5.0, 0.0, -5.0]) model.addBallJoint(1, 2, [-5.0, 2.0, -5.0]) @@ -115,25 +125,22 @@ def createRigidBodyModel(): body = model.addRigidBody(1.0, # density vd_static, mesh_static, # vertices, mesh [5.0, 0.0, -5.0], # position - 0.0, [1,0,0], # angle, rotation axis - [1, 1, 1], # scaling - None) # signed distance field for collision detection + testMesh=False, # no collision handling + generateCollisionObject=False) body.setMass(0.0) # dynamic body body = model.addRigidBody(1.0, vd, mesh, [5.0, 1.0, -5.0], - 0.0, [1,0,0], - [1, 1, 1], - None) + testMesh=False, # no collision handling + generateCollisionObject=False) body = model.addRigidBody(1.0, vd, mesh, [5.0, 3.0, -5.0], - 0.0, [1,0,0], - [1, 1, 1], - None) + testMesh=False, # no collision handling + generateCollisionObject=False) model.addBallJoint(3, 4, [5.0, 0.0, -5.0]) model.addBallJoint(4, 5, [5.0, 2.0, -5.0]) @@ -142,25 +149,22 @@ def createRigidBodyModel(): body = model.addRigidBody(1.0, # density vd_static, mesh_static, # vertices, mesh [5.0, 0.0, 5.0], # position - 0.0, [1,0,0], # angle, rotation axis - [1, 1, 1], # scaling - None) # signed distance field for collision detection + testMesh=False, # no collision handling + generateCollisionObject=False) body.setMass(0.0) # dynamic body body = model.addRigidBody(1.0, vd, mesh, [5.0, 1.0, 5.0], - 0.0, [1,0,0], - [1, 1, 1], - None) + testMesh=False, # no collision handling + generateCollisionObject=False) body = model.addRigidBody(1.0, vd, mesh, [5.0, 3.0, 5.0], - 0.0, [1,0,0], - [1, 1, 1], - None) + testMesh=False, # no collision handling + generateCollisionObject=False) model.addBallJoint(6, 7, [5.0, 0.0, 5.0]) model.addBallJoint(7, 8, [5.0, 2.0, 5.0]) @@ -169,25 +173,22 @@ def createRigidBodyModel(): body = model.addRigidBody(1.0, # density vd_static, mesh_static, # vertices, mesh [-5.0, 0.0, 5.0], # position - 0.0, [1,0,0], # angle, rotation axis - [1, 1, 1], # scaling - None) # signed distance field for collision detection + testMesh=False, # no collision handling + generateCollisionObject=False) body.setMass(0.0) # dynamic body body = model.addRigidBody(1.0, vd, mesh, [-5.0, 1.0, 5.0], - 0.0, [1,0,0], - [1, 1, 1], - None) + testMesh=False, # no collision handling + generateCollisionObject=False) body = model.addRigidBody(1.0, vd, mesh, [-5.0, 3.0, 5.0], - 0.0, [1,0,0], - [1, 1, 1], - None) + testMesh=False, # no collision handling + generateCollisionObject=False) model.addBallJoint(9, 10, [-5.0, 0.0, 5.0]) model.addBallJoint(10, 11, [-5.0, 2.0, 5.0]) diff --git a/version b/version index 359a5b95..10bf840e 100644 --- a/version +++ b/version @@ -1 +1 @@ -2.0.0 \ No newline at end of file +2.0.1 \ No newline at end of file