diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 5463ef857beed42f718ba1800eccc0d85bf9805c..2785904a0cb1fecac206637a1e7991ae0dafb616 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,5 +1,9 @@ 0067c8aa66aac548601e2a3fd029aa264cc59f2a 76b68f785df31b00e153290b45ec290a9c5f7963 +# ruff --fix . (Guilhem Saurel, 2023-10-25) +02cef56abfacee590c8444fd379c8837bf007fa1 +# black . (Guilhem Saurel, 2023-10-25) +febfbcbe9c98cdb4e0c7bbf4554a6925b391834b # ruff --fix . (Guilhem Saurel, 2023-10-24) 58dee5ae90eded5125825a2da0fe76a5031f3334 # black . (Guilhem Saurel, 2023-10-24) diff --git a/.github/workflows/conda/environment_macos_linux.yml b/.github/workflows/conda/environment_macos_linux.yml new file mode 100644 index 0000000000000000000000000000000000000000..4a4655fbd32b392a40da531f39635545eb50a344 --- /dev/null +++ b/.github/workflows/conda/environment_macos_linux.yml @@ -0,0 +1,22 @@ +name: fcl +channels: + - conda-forge + - nodefaults +dependencies: + - eigen + - octomap + - assimp + - numpy + - boost + - eigenpy + - python + - doxygen + - lxml + - pylatexenc + - qhull + - cmake + - ccache + - cxx-compiler + - llvm-openmp + - pkg-config + - ninja diff --git a/.github/workflows/conda/conda-env.yml b/.github/workflows/conda/environment_windows.yml similarity index 80% rename from .github/workflows/conda/conda-env.yml rename to .github/workflows/conda/environment_windows.yml index 54d08f51413b0cbf873f73e674554e1fdc371e91..8c6fccaede6be219700d402eb53df1d3cb08adea 100644 --- a/.github/workflows/conda/conda-env.yml +++ b/.github/workflows/conda/environment_windows.yml @@ -14,3 +14,7 @@ dependencies: - lxml - pylatexenc - qhull + - cmake + - ccache + - pkg-config + - ninja diff --git a/.github/workflows/macos-linux-conda.yml b/.github/workflows/macos-linux-conda.yml index 9a83ac9a3470f2ce633803f522d2bdeb5f606891..cad761ce84754c96233de2d68b9b6c32f4d28c3a 100644 --- a/.github/workflows/macos-linux-conda.yml +++ b/.github/workflows/macos-linux-conda.yml @@ -4,63 +4,75 @@ on: [push,pull_request] jobs: hpp-fcl-conda: - name: CI on ${{ matrix.os }} with Conda + name: CI on ${{ matrix.os }} with Conda Python ${{ matrix.python-version }} - ${{ matrix.build_type }} ${{ matrix.cxx_options }} runs-on: ${{ matrix.os }} + env: + CCACHE_BASEDIR: "${GITHUB_WORKSPACE}" + CCACHE_DIR: "${GITHUB_WORKSPACE}/.ccache" + CCACHE_COMPRESS: true + CCACHE_COMPRESSLEVEL: 6 strategy: fail-fast: false matrix: os: ["ubuntu-latest", "macos-latest"] python-version: ["3.8", "3.12"] + cxx_options: ['', '-mavx2'] + build_type: [Release, Debug] + exclude: + - build_type: Debug + cxx_options: -mavx2 + os: macos-latest + - build_type: Release + cxx_options: -mavx2 + os: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - - uses: conda-incubator/setup-miniconda@v2 + - uses: actions/cache@v3 with: - activate-environment: hpp-fcl + path: .ccache + key: ccache-macos-linux-conda-${{ matrix.os }}-${{ matrix.build_type }}-${{ matrix.cxx_options }}-${{ matrix.python-version }}-${{ github.sha }} + restore-keys: ccache-macos-linux-conda-${{ matrix.os }}-${{ matrix.build_type }}-${{ matrix.cxx_options }}-${{ matrix.python-version }}- + + - uses: conda-incubator/setup-miniconda@v3 + with: + activate-environment: fcl auto-update-conda: true - environment-file: .github/workflows/conda/conda-env.yml + environment-file: .github/workflows/conda/environment_macos_linux.yml python-version: ${{ matrix.python-version }} - - - name: Install compilers on OSX - if: contains(matrix.os, 'macos') - shell: bash -l {0} - run: | - conda activate hpp-fcl - conda install compilers=1.4.2 -c conda-forge - - - name: Install cmake and update conda - shell: bash -l {0} - run: | - conda activate hpp-fcl - conda install cmake -c main - conda install llvm-openmp -c conda-forge + auto-activate-base: false - name: Build hpp-fcl - shell: bash -l {0} + shell: bash -el {0} run: | - conda activate hpp-fcl conda list echo $CONDA_PREFIX mkdir build cd build - cmake .. -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX -DCMAKE_BUILD_TYPE=Release -DPYTHON_EXECUTABLE=$(which python3) -DGENERATE_PYTHON_STUBS=ON -DHPP_FCL_HAS_QHULL=ON - make -j2 - make build_tests - export CTEST_OUTPUT_ON_FAILURE=1 - make test - make install + cmake .. \ + -G "Ninja" \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX \ + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ + -DCMAKE_CXX_FLAGS=${{ matrix.cxx_options }} \ + -DPYTHON_EXECUTABLE=$(which python3) \ + -DGENERATE_PYTHON_STUBS=ON \ + -DHPP_FCL_HAS_QHULL=ON + cmake --build . -j2 + ctest --output-on-failure + cmake --install . - name: Uninstall hpp-fcl - shell: bash -l {0} + shell: bash -el {0} run: | cd build - make uninstall + cmake --build . --target uninstall check: if: always() diff --git a/.github/workflows/windows-conda-clang.yml b/.github/workflows/windows-conda-clang.yml index 22c27a61f4a82111a633a553c23462c38fbde577..33fc913ca4889d6ff0379910054ed584a11a3e0c 100644 --- a/.github/workflows/windows-conda-clang.yml +++ b/.github/workflows/windows-conda-clang.yml @@ -4,6 +4,12 @@ on: [push,pull_request] jobs: build: runs-on: ${{ matrix.os }} + env: + CCACHE_BASEDIR: "${GITHUB_WORKSPACE}" + CCACHE_DIR: "${GITHUB_WORKSPACE}/.ccache" + CCACHE_COMPRESS: true + CCACHE_COMPRESSLEVEL: 6 + strategy: fail-fast: false matrix: @@ -15,42 +21,40 @@ jobs: compiler: clang-cl steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - - uses: conda-incubator/setup-miniconda@v2 - env: - ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' + + - uses: actions/cache@v3 + with: + path: .ccache + key: ccache-windows-conda-clang-${{ matrix.os }}-${{ matrix.compiler }}-${{ github.sha }} + restore-keys: ccache-windows-conda-clang-${{ matrix.os }}-${{ matrix.compiler }}- + + - uses: conda-incubator/setup-miniconda@v3 with: activate-environment: fcl - environment-file: .github/workflows/conda/conda-env.yml + auto-update-conda: true + environment-file: .github/workflows/conda/environment_windows.yml python-version: "3.10" - - name: Install cmake and update conda - run: | - conda install cmake -c main + auto-activate-base: false - name: Build FCL shell: cmd /C CALL {0} - env: - ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' run: | - :: unset extra Boost envs - set Boost_ROOT= - set BOOST_ROOT_1_69_0= - set BOOST_ROOT_1_72_0= - set PATH=%PATH:C:\hostedtoolcache\windows\Boost\1.72.0;=% - call "%programfiles(x86)%\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64 + :: Tell Ninja to use clang-cl + set CC=clang-cl + set CXX=clang-cl :: Create build mkdir build pushd build - :: Configure - set PKG_CONFIG_PATH=%CONDA_PREFIX%\Library\share\pkgconfig:%CONDA_PREFIX%\Library\share\pkgconfig cmake ^ - -G "Visual Studio 16 2019" -T "ClangCl" -DCMAKE_GENERATOR_PLATFORM=x64 ^ + -G "Ninja" ^ -DCMAKE_INSTALL_PREFIX=%CONDA_PREFIX%\Library ^ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache ^ -DCMAKE_BUILD_TYPE=Release ^ -DGENERATE_PYTHON_STUBS=ON ^ -DPYTHON_SITELIB=%CONDA_PREFIX%\Lib\site-packages ^ @@ -65,7 +69,7 @@ jobs: if errorlevel 1 exit 1 :: Testing - ctest --output-on-failure -C Release -V + ctest --output-on-failure -C Release if errorlevel 1 exit 1 :: Test Python import diff --git a/.github/workflows/windows-conda-v142.yml b/.github/workflows/windows-conda-v142.yml index 6b183034fab908930b690fe2a64a0ea14cda128f..e09444ba3bcec0831fc33f9626957bd95c90a1cc 100644 --- a/.github/workflows/windows-conda-v142.yml +++ b/.github/workflows/windows-conda-v142.yml @@ -4,6 +4,12 @@ on: [push,pull_request] jobs: build: runs-on: ${{ matrix.os }} + env: + CCACHE_BASEDIR: "${GITHUB_WORKSPACE}" + CCACHE_DIR: "${GITHUB_WORKSPACE}/.ccache" + CCACHE_COMPRESS: true + CCACHE_COMPRESSLEVEL: 6 + strategy: fail-fast: false matrix: @@ -14,31 +20,27 @@ jobs: os: windows-2019 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - - uses: conda-incubator/setup-miniconda@v2 - env: - ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' + + - uses: actions/cache@v3 + with: + path: .ccache + key: ccache-windows-conda-vs-${{ matrix.os }}-${{ matrix.compiler }}-${{ github.sha }} + restore-keys: ccache-windows-conda-vs-${{ matrix.os }}-${{ matrix.compiler }}- + + - uses: conda-incubator/setup-miniconda@v3 with: activate-environment: fcl - environment-file: .github/workflows/conda/conda-env.yml + auto-update-conda: true + environment-file: .github/workflows/conda/environment_windows.yml python-version: "3.10" - - name: Install cmake and update conda - run: | - conda install cmake -c main + auto-activate-base: false - name: Build FCL shell: cmd /C CALL {0} - env: - ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' run: | - :: unset extra Boost envs - set Boost_ROOT= - set BOOST_ROOT_1_69_0= - set BOOST_ROOT_1_72_0= - set PATH=%PATH:C:\hostedtoolcache\windows\Boost\1.72.0;=% - call "%programfiles(x86)%\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64 :: Create build @@ -46,10 +48,10 @@ jobs: pushd build :: Configure - set PKG_CONFIG_PATH=%CONDA_PREFIX%\Library\share\pkgconfig:%CONDA_PREFIX%\Library\share\pkgconfig cmake ^ - -G "Visual Studio 16 2019" -T "v142" -DCMAKE_GENERATOR_PLATFORM=x64 ^ + -G "Ninja" ^ -DCMAKE_INSTALL_PREFIX=%CONDA_PREFIX%\Library ^ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache ^ -DCMAKE_BUILD_TYPE=Release ^ -DGENERATE_PYTHON_STUBS=ON ^ -DPYTHON_SITELIB=%CONDA_PREFIX%\Lib\site-packages ^ @@ -64,7 +66,7 @@ jobs: if errorlevel 1 exit 1 :: Testing - ctest --output-on-failure -C Release -V + ctest --output-on-failure -C Release if errorlevel 1 exit 1 :: Test Python import diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d8957a80edf4031b519b56e6cdeb3b35f5a2dd2..e489ce9c349f1229344e27c425bafffcb0ab3e54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,7 +107,7 @@ CMAKE_DEPENDENT_OPTION(GENERATE_PYTHON_STUBS "Generate the Python stubs associat ADD_PROJECT_DEPENDENCY(Eigen3 REQUIRED PKG_CONFIG_REQUIRES "eigen3 >= 3.0.0") if(BUILD_PYTHON_INTERFACE) - FIND_PACKAGE(eigenpy 2.7.10 REQUIRED) + FIND_PACKAGE(eigenpy 2.9.2 REQUIRED) endif() # Required dependencies @@ -243,7 +243,6 @@ SET(${PROJECT_NAME}_HEADERS include/hpp/fcl/BVH/BVH_utility.h include/hpp/fcl/collision_object.h include/hpp/fcl/collision_utility.h - include/hpp/fcl/octree.h include/hpp/fcl/hfield.h include/hpp/fcl/fwd.hh include/hpp/fcl/mesh_loader/assimp.h @@ -257,7 +256,6 @@ SET(${PROJECT_NAME}_HEADERS include/hpp/fcl/internal/traversal_node_bvh_shape.h include/hpp/fcl/internal/traversal_node_bvhs.h include/hpp/fcl/internal/traversal_node_hfield_shape.h - include/hpp/fcl/internal/traversal_node_octree.h include/hpp/fcl/internal/traversal_node_setup.h include/hpp/fcl/internal/traversal_node_shapes.h include/hpp/fcl/internal/traversal_recurse.h @@ -282,6 +280,14 @@ SET(${PROJECT_NAME}_HEADERS include/hpp/fcl/timings.h ) +IF(HPP_FCL_HAS_OCTOMAP) + LIST(APPEND ${PROJECT_NAME}_HEADERS + include/hpp/fcl/octree.h + include/hpp/fcl/serialization/octree.h + include/hpp/fcl/internal/traversal_node_octree.h + ) +ENDIF(HPP_FCL_HAS_OCTOMAP) + add_subdirectory(doc) add_subdirectory(src) if (BUILD_PYTHON_INTERFACE) diff --git a/LICENSE b/LICENSE index 529a238fa178840a0341d29281d0630b9d558838..c0a96bba948f1a4bddadb778c6a636bfcd204e25 100644 --- a/LICENSE +++ b/LICENSE @@ -2,6 +2,8 @@ Software License Agreement (BSD License) Copyright (c) 2008-2014, Willow Garage, Inc. Copyright (c) 2014-2015, Open Source Robotics Foundation + Copyright (c) 2014-2023, CNRS + Copyright (c) 2018-2023, INRIA All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index f1bde0d7fc551d20fa2a370c98bc0c05d77d5b96..00f7e3694e1781fdd6baf0f6e761a48456c5ce74 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,149 @@ On the other hand, why do we care about dedicated collision detection solvers li One can observe that GJK-based approaches largely outperform solutions based on classic optimization solvers (e.g., QP solver like [ProxQP](https://github.com/Simple-Robotics/proxsuite)), notably for large geometries composed of tens or hundreds of vertices. +## C++ example +Both the C++ library and the python bindings can be installed as simply as `conda -c conda-forge install hpp-fcl`. +The `.so` library, include files and python bindings will then be installed under `$CONDA_PREFIX/lib`, `$CONDA_PREFIX/include` and `$CONDA_PREFIX/lib/python3.XX/site-packages`. + +Here is an example of using HPP-FCL in C++: +```cpp +#include "hpp/fcl/math/transform.h" +#include "hpp/fcl/mesh_loader/loader.h" +#include "hpp/fcl/BVH/BVH_model.h" +#include "hpp/fcl/collision.h" +#include "hpp/fcl/collision_data.h" +#include <iostream> +#include <memory> + +// Function to load a convex mesh from a `.obj`, `.stl` or `.dae` file. +// +// This function imports the object inside the file as a BVHModel, i.e. a point cloud +// which is hierarchically transformed into a tree of bounding volumes. +// The leaves of this tree are the individual points of the point cloud +// stored in the `.obj` file. +// This BVH can then be used for collision detection. +// +// For better computational efficiency, we sometimes prefer to work with +// the convex hull of the point cloud. This insures that the underlying object +// is convex and thus very fast collision detection algorithms such as +// GJK or EPA can be called with this object. +// Consequently, after creating the BVH structure from the point cloud, this function +// also computes its convex hull. +std::shared_ptr<hpp::fcl::ConvexBase> loadConvexMesh(const std::string& file_name) { + hpp::fcl::NODE_TYPE bv_type = hpp::fcl::BV_AABB; + hpp::fcl::MeshLoader loader(bv_type); + hpp::fcl::BVHModelPtr_t bvh = loader.load(file_name); + bvh->buildConvexHull(true, "Qt"); + return bvh->convex; +} + +int main() { + // Create the hppfcl shapes. + // Hppfcl supports many primitive shapes: boxes, spheres, capsules, cylinders, ellipsoids, cones, planes, + // halfspace and convex meshes (i.e. convex hulls of clouds of points). + // It also supports BVHs (bounding volumes hierarchies), height-fields and octrees. + std::shared_ptr<hpp::fcl::Ellipsoid> shape1 = std::make_shared<hpp::fcl::Ellipsoid>(0.7, 1.0, 0.8); + std::shared_ptr<hpp::fcl::ConvexBase> shape2 = loadConvexMesh("../path/to/mesh/file.obj"); + + // Define the shapes' placement in 3D space + hpp::fcl::Transform3f T1; + T1.setQuatRotation(hpp::fcl::Quaternion3f::UnitRandom()); + T1.setTranslation(hpp::fcl::Vec3f::Random()); + hpp::fcl::Transform3f T2 = hpp::fcl::Transform3f::Identity(); + T2.setQuatRotation(hpp::fcl::Quaternion3f::UnitRandom()); + T2.setTranslation(hpp::fcl::Vec3f::Random()); + + // Define collision requests and results. + // + // The collision request allows to set parameters for the collision pair. + // For example, we can set a positive or negative security margin. + // If the distance between the shapes is less than the security margin, the shapes + // will be considered in collision. + // Setting a positive security margin can be usefull in motion planning, + // i.e to prevent shapes from getting too close to one another. + // In physics simulation, allowing a negative security margin may be usefull to stabilize the simulation. + hpp::fcl::CollisionRequest col_req; + col_req.security_margin = 1e-1; + // A collision result stores the result of the collision test (signed distance between the shapes, + // witness points location, normal etc.) + hpp::fcl::CollisionResult col_res; + + // Collision call + hpp::fcl::collide(shape1.get(), T1, shape2.get(), T2, col_req, col_res); + + // We can access the collision result once it has been populated + std::cout << "Collision? " << col_res.isCollision() << "\n"; + if (col_res.isCollision()) { + hpp::fcl::Contact contact = col_res.getContact(0); + // The penetration depth does **not** take into account the security margin. + // Consequently, the penetration depth is the true signed distance which separates the shapes. + // To have the distance which takes into account the security margin, we can simply add the two together. + std::cout << "Penetration depth: " << contact.penetration_depth << "\n"; + std::cout << "Distance between the shapes including the security margin: " << contact.penetration_depth + col_req.security_margin << "\n"; + std::cout << "Witness point on shape1: " << contact.nearest_points[0].transpose() << "\n"; + std::cout << "Witness point on shape2: " << contact.nearest_points[1].transpose() << "\n"; + std::cout << "Normal: " << contact.normal.transpose() << "\n"; + } + + // Before calling another collision test, it is important to clear the previous results stored in the collision result. + col_res.clear(); + + return 0; +} +``` + +## Python example +Here is the C++ example from above translated in python using HPP-FCL's python bindings: +```python +import numpy as np +import hppfcl +# Optional: +# The Pinocchio library is a rigid body algorithms library and has a handy SE3 module. +# It can be installed as simply as `conda -c conda-forge install pinocchio`. +# Installing pinocchio also installs hpp-fcl. +import pinocchio as pin + +def loadConvexMesh(file_name: str): + loader = hppfcl.MeshLoader() + bvh: hppfcl.BVHModelBase = loader.load(file_name) + bvh.buildConvexHull(True, "Qt") + return bvh.convex + +if __name__ == "__main__": + # Create hppfcl shapes + shape1 = hppfcl.Ellipsoid(0.7, 1.0, 0.8) + shape2 = loadConvexMesh("../path/to/mesh/file.obj") + + # Define the shapes' placement in 3D space + T1 = hppfcl.Transform3f() + T1.setTranslation(pin.SE3.Random().translation) + T1.setRotation(pin.SE3.Random().rotation) + T2 = hppfcl.Transform3f(); + # Using np arrays also works + T1.setTranslation(np.random.rand(3)) + T2.setRotation(pin.SE3.Random().rotation) + + # Define collision requests and results + col_req = hppfcl.CollisionRequest() + col_res = hppfcl.CollisionResult() + + # Collision call + hppfcl.collide(shape1, T1, shape2, T2, col_req, col_res) + + # Accessing the collision result once it has been populated + print("Is collision? ", {col_res.isCollision()}) + if col_res.isCollision(): + contact: hppfcl.Contact = col_res.getContact(0) + print("Penetration depth: ", contact.penetration_depth) + print("Distance between the shapes including the security margin: ", contact.penetration_depth + col_req.security_margin) + print("Witness point shape1: ", contact.getNearestPoint1()) + print("Witness point shape2: ", contact.getNearestPoint2()) + print("Normal: ", contact.normal) + + # Before running another collision call, it is important to clear the old one + col_res.clear() +``` + ## Acknowledgments The development of **HPP-FCL** is actively supported by the [Gepetto team](http://projects.laas.fr/gepetto/) [@LAAS-CNRS](http://www.laas.fr), the [Willow team](https://www.di.ens.fr/willow/) [@INRIA](http://www.inria.fr) and, to some extend, [Eureka Robotics](https://eurekarobotics.com/). diff --git a/include/hpp/fcl/BV/BV_node.h b/include/hpp/fcl/BV/BV_node.h index 8ba57ea26823555a0942c9c88050019ca19aea96..300b24ecc87ac55f48a4b4c335f51e4948e1a0a8 100644 --- a/include/hpp/fcl/BV/BV_node.h +++ b/include/hpp/fcl/BV/BV_node.h @@ -39,9 +39,7 @@ #define HPP_FCL_BV_NODE_H #include <hpp/fcl/data_types.h> - #include <hpp/fcl/BV/BV.h> -#include <iostream> namespace hpp { namespace fcl { diff --git a/include/hpp/fcl/BV/OBB.h b/include/hpp/fcl/BV/OBB.h index 9d765a7126b97a647bf4ec0a2d967eef38ffd151..abbf3b6917aae8f074d5d9ca3fe865d96a351bda 100644 --- a/include/hpp/fcl/BV/OBB.h +++ b/include/hpp/fcl/BV/OBB.h @@ -50,6 +50,8 @@ struct CollisionRequest; /// @brief Oriented bounding box class struct HPP_FCL_DLLAPI OBB { + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + /// @brief Orientation of OBB. axis[i] is the ith column of the orientation /// matrix for the box; it is also the i-th principle direction of the box. We /// assume that axis[0] corresponds to the axis with the longest box edge, diff --git a/include/hpp/fcl/BV/OBBRSS.h b/include/hpp/fcl/BV/OBBRSS.h index e5fb8a7f48106aa4d0b206aa810e1d9b720e4fc5..fc45d318ec670da3c23ded58defdade992a642c1 100644 --- a/include/hpp/fcl/BV/OBBRSS.h +++ b/include/hpp/fcl/BV/OBBRSS.h @@ -52,6 +52,8 @@ struct CollisionRequest; /// @brief Class merging the OBB and RSS, can handle collision and distance /// simultaneously struct HPP_FCL_DLLAPI OBBRSS { + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + /// @brief OBB member, for rotation OBB obb; diff --git a/include/hpp/fcl/BV/RSS.h b/include/hpp/fcl/BV/RSS.h index 6e624d4ca128f130d440d460f51ac66321879168..c11e09d86225220ebde633ba5f9a96013a5a1709 100644 --- a/include/hpp/fcl/BV/RSS.h +++ b/include/hpp/fcl/BV/RSS.h @@ -51,6 +51,8 @@ struct CollisionRequest; /// @brief A class for rectangle sphere-swept bounding volume struct HPP_FCL_DLLAPI RSS { + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + /// @brief Orientation of RSS. axis[i] is the ith column of the orientation /// matrix for the RSS; it is also the i-th principle direction of the RSS. We /// assume that axis[0] corresponds to the axis with the longest length, diff --git a/include/hpp/fcl/BV/kDOP.h b/include/hpp/fcl/BV/kDOP.h index 92d82ba4f904fc0dedf9987669cd6301859769de..0ce92f66d322647c2d4a122407c988d138601eb7 100644 --- a/include/hpp/fcl/BV/kDOP.h +++ b/include/hpp/fcl/BV/kDOP.h @@ -95,6 +95,8 @@ class HPP_FCL_DLLAPI KDOP { Eigen::Array<FCL_REAL, N, 1> dist_; public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + /// @brief Creating kDOP containing nothing KDOP(); @@ -165,11 +167,6 @@ class HPP_FCL_DLLAPI KDOP { //// @brief Check whether one point is inside the KDOP bool inside(const Vec3f& p) const; - - public: - /// \cond - EIGEN_MAKE_ALIGNED_OPERATOR_NEW - /// \endcond }; template <short N> diff --git a/include/hpp/fcl/BV/kIOS.h b/include/hpp/fcl/BV/kIOS.h index 35dc5f211808ed8dafb41ab5909b398bec0be005..f776f0a15dcc8bc5666cd024fcfd3c338b17c592 100644 --- a/include/hpp/fcl/BV/kIOS.h +++ b/include/hpp/fcl/BV/kIOS.h @@ -53,6 +53,8 @@ struct CollisionRequest; class HPP_FCL_DLLAPI kIOS { /// @brief One sphere in kIOS struct HPP_FCL_DLLAPI kIOS_Sphere { + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + Vec3f o; FCL_REAL r; @@ -92,6 +94,8 @@ class HPP_FCL_DLLAPI kIOS { } public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + /// @brief Equality operator bool operator==(const kIOS& other) const { bool res = obb == other.obb && num_spheres == other.num_spheres; diff --git a/include/hpp/fcl/BVH/BVH_model.h b/include/hpp/fcl/BVH/BVH_model.h index a57fc7b845071605c260222102c38c8426f7b760..61a01a9dedf0e54d7f790376499b287e9ee88ac3 100644 --- a/include/hpp/fcl/BVH/BVH_model.h +++ b/include/hpp/fcl/BVH/BVH_model.h @@ -44,6 +44,8 @@ #include <hpp/fcl/BVH/BVH_internal.h> #include <hpp/fcl/BV/BV_node.h> #include <vector> +#include <memory> +#include <iostream> namespace hpp { namespace fcl { @@ -63,13 +65,13 @@ class BVSplitter; class HPP_FCL_DLLAPI BVHModelBase : public CollisionGeometry { public: /// @brief Geometry point data - Vec3f* vertices; + std::shared_ptr<std::vector<Vec3f>> vertices; /// @brief Geometry triangle index data, will be NULL for point clouds - Triangle* tri_indices; + std::shared_ptr<std::vector<Triangle>> tri_indices; /// @brief Geometry point data in previous frame - Vec3f* prev_vertices; + std::shared_ptr<std::vector<Vec3f>> prev_vertices; /// @brief Number of triangles unsigned int num_tris; @@ -100,11 +102,7 @@ class HPP_FCL_DLLAPI BVHModelBase : public CollisionGeometry { BVHModelBase(const BVHModelBase& other); /// @brief deconstruction, delete mesh data related. - virtual ~BVHModelBase() { - delete[] vertices; - delete[] tri_indices; - delete[] prev_vertices; - } + virtual ~BVHModelBase() {} /// @brief Get the object type: it is a BVH OBJECT_TYPE getObjectType() const { return OT_BVH; } @@ -203,13 +201,28 @@ class HPP_FCL_DLLAPI BVHModelBase : public CollisionGeometry { Vec3f computeCOM() const { FCL_REAL vol = 0; Vec3f com(0, 0, 0); + if (!(vertices.get())) { + std::cerr << "BVH Error in `computeCOM`! The BVHModel does not contain " + "vertices." + << std::endl; + return com; + } + const std::vector<Vec3f>& vertices_ = *vertices; + if (!(tri_indices.get())) { + std::cerr << "BVH Error in `computeCOM`! The BVHModel does not contain " + "triangles." + << std::endl; + return com; + } + const std::vector<Triangle>& tri_indices_ = *tri_indices; + for (unsigned int i = 0; i < num_tris; ++i) { - const Triangle& tri = tri_indices[i]; + const Triangle& tri = tri_indices_[i]; FCL_REAL d_six_vol = - (vertices[tri[0]].cross(vertices[tri[1]])).dot(vertices[tri[2]]); + (vertices_[tri[0]].cross(vertices_[tri[1]])).dot(vertices_[tri[2]]); vol += d_six_vol; - com += - (vertices[tri[0]] + vertices[tri[1]] + vertices[tri[2]]) * d_six_vol; + com += (vertices_[tri[0]] + vertices_[tri[1]] + vertices_[tri[2]]) * + d_six_vol; } return com / (vol * 4); @@ -217,10 +230,24 @@ class HPP_FCL_DLLAPI BVHModelBase : public CollisionGeometry { FCL_REAL computeVolume() const { FCL_REAL vol = 0; + if (!(vertices.get())) { + std::cerr << "BVH Error in `computeCOM`! The BVHModel does not contain " + "vertices." + << std::endl; + return vol; + } + const std::vector<Vec3f>& vertices_ = *vertices; + if (!(tri_indices.get())) { + std::cerr << "BVH Error in `computeCOM`! The BVHModel does not contain " + "triangles." + << std::endl; + return vol; + } + const std::vector<Triangle>& tri_indices_ = *tri_indices; for (unsigned int i = 0; i < num_tris; ++i) { - const Triangle& tri = tri_indices[i]; + const Triangle& tri = tri_indices_[i]; FCL_REAL d_six_vol = - (vertices[tri[0]].cross(vertices[tri[1]])).dot(vertices[tri[2]]); + (vertices_[tri[0]].cross(vertices_[tri[1]])).dot(vertices_[tri[2]]); vol += d_six_vol; } @@ -234,11 +261,25 @@ class HPP_FCL_DLLAPI BVHModelBase : public CollisionGeometry { C_canonical << 1 / 60.0, 1 / 120.0, 1 / 120.0, 1 / 120.0, 1 / 60.0, 1 / 120.0, 1 / 120.0, 1 / 120.0, 1 / 60.0; + if (!(vertices.get())) { + std::cerr << "BVH Error in `computeMomentofInertia`! The BVHModel does " + "not contain vertices." + << std::endl; + return C; + } + const std::vector<Vec3f>& vertices_ = *vertices; + if (!(vertices.get())) { + std::cerr << "BVH Error in `computeMomentofInertia`! The BVHModel does " + "not contain vertices." + << std::endl; + return C; + } + const std::vector<Triangle>& tri_indices_ = *tri_indices; for (unsigned int i = 0; i < num_tris; ++i) { - const Triangle& tri = tri_indices[i]; - const Vec3f& v1 = vertices[tri[0]]; - const Vec3f& v2 = vertices[tri[1]]; - const Vec3f& v3 = vertices[tri[2]]; + const Triangle& tri = tri_indices_[i]; + const Vec3f& v1 = vertices_[tri[0]]; + const Vec3f& v2 = vertices_[tri[1]]; + const Vec3f& v3 = vertices_[tri[2]]; Matrix3f A; A << v1.transpose(), v2.transpose(), v3.transpose(); C += A.derived().transpose() * C_canonical * A * (v1.cross(v2)).dot(v3); @@ -274,11 +315,14 @@ class HPP_FCL_DLLAPI BVHModel : public BVHModelBase { typedef BVHModelBase Base; public: + using bv_node_vector_t = + std::vector<BVNode<BV>, Eigen::aligned_allocator<BVNode<BV>>>; + /// @brief Split rule to split one BV node into two children - shared_ptr<BVSplitter<BV> > bv_splitter; + shared_ptr<BVSplitter<BV>> bv_splitter; /// @brief Fitting rule to fit a BV node to a set of geometry primitives - shared_ptr<BVFitter<BV> > bv_fitter; + shared_ptr<BVFitter<BV>> bv_fitter; /// @brief Default constructor to build an empty BVH BVHModel(); @@ -293,10 +337,7 @@ class HPP_FCL_DLLAPI BVHModel : public BVHModelBase { virtual BVHModel<BV>* clone() const { return new BVHModel(*this); } /// @brief deconstruction, delete mesh data related. - ~BVHModel() { - delete[] bvs; - delete[] primitive_indices; - } + ~BVHModel() {} /// @brief We provide getBV() and getNumBVs() because BVH may be compressed /// (in future), so we must provide some flexibility here @@ -304,13 +345,13 @@ class HPP_FCL_DLLAPI BVHModel : public BVHModelBase { /// @brief Access the bv giving the its index const BVNode<BV>& getBV(unsigned int i) const { assert(i < num_bvs); - return bvs[i]; + return (*bvs)[i]; } /// @brief Access the bv giving the its index BVNode<BV>& getBV(unsigned int i) { assert(i < num_bvs); - return bvs[i]; + return (*bvs)[i]; } /// @brief Get the number of bv in the BVH @@ -336,10 +377,10 @@ class HPP_FCL_DLLAPI BVHModel : public BVHModelBase { bool allocateBVs(); unsigned int num_bvs_allocated; - unsigned int* primitive_indices; + std::shared_ptr<std::vector<unsigned int>> primitive_indices; /// @brief Bounding volume hierarchy - BVNode<BV>* bvs; + std::shared_ptr<bv_node_vector_t> bvs; /// @brief Number of BV nodes in bounding volume hierarchy unsigned int num_bvs; @@ -370,15 +411,19 @@ class HPP_FCL_DLLAPI BVHModel : public BVHModelBase { /// OBBRSS), special implementation is provided. void makeParentRelativeRecurse(int bv_id, Matrix3f& parent_axes, const Vec3f& parent_c) { - if (!bvs[bv_id].isLeaf()) { - makeParentRelativeRecurse(bvs[bv_id].first_child, parent_axes, - bvs[bv_id].getCenter()); - - makeParentRelativeRecurse(bvs[bv_id].first_child + 1, parent_axes, - bvs[bv_id].getCenter()); + bv_node_vector_t& bvs_ = *bvs; + if (!bvs_[static_cast<size_t>(bv_id)].isLeaf()) { + makeParentRelativeRecurse(bvs_[static_cast<size_t>(bv_id)].first_child, + parent_axes, + bvs_[static_cast<size_t>(bv_id)].getCenter()); + + makeParentRelativeRecurse( + bvs_[static_cast<size_t>(bv_id)].first_child + 1, parent_axes, + bvs_[static_cast<size_t>(bv_id)].getCenter()); } - bvs[bv_id].bv = translate(bvs[bv_id].bv, -parent_c); + bvs_[static_cast<size_t>(bv_id)].bv = + translate(bvs_[static_cast<size_t>(bv_id)].bv, -parent_c); } private: @@ -435,8 +480,14 @@ class HPP_FCL_DLLAPI BVHModel : public BVHModelBase { if (num_bvs != other.num_bvs) return false; - for (unsigned int k = 0; k < num_bvs; ++k) { - if (bvs[k] != other.bvs[k]) return false; + if ((!(bvs.get()) && other.bvs.get()) || (bvs.get() && !(other.bvs.get()))) + return false; + if (bvs.get() && other.bvs.get()) { + const bv_node_vector_t& bvs_ = *bvs; + const bv_node_vector_t& other_bvs_ = *(other.bvs); + for (unsigned int k = 0; k < num_bvs; ++k) { + if (bvs_[k] != other_bvs_[k]) return false; + } } return true; @@ -475,13 +526,13 @@ template <> NODE_TYPE BVHModel<OBBRSS>::getNodeType() const; template <> -NODE_TYPE BVHModel<KDOP<16> >::getNodeType() const; +NODE_TYPE BVHModel<KDOP<16>>::getNodeType() const; template <> -NODE_TYPE BVHModel<KDOP<18> >::getNodeType() const; +NODE_TYPE BVHModel<KDOP<18>>::getNodeType() const; template <> -NODE_TYPE BVHModel<KDOP<24> >::getNodeType() const; +NODE_TYPE BVHModel<KDOP<24>>::getNodeType() const; } // namespace fcl diff --git a/include/hpp/fcl/broadphase/broadphase_SaP.h b/include/hpp/fcl/broadphase/broadphase_SaP.h index 0751401d1c80715170fab74be162f8f55438bfe1..a71f78ad44864940617085bc54d122b82fae569b 100644 --- a/include/hpp/fcl/broadphase/broadphase_SaP.h +++ b/include/hpp/fcl/broadphase/broadphase_SaP.h @@ -152,9 +152,9 @@ class HPP_FCL_DLLAPI SaPCollisionManager : public BroadPhaseCollisionManager { /// @brief set the value of the end point Vec3f& getVal(); - FCL_REAL getVal(size_t i) const; + FCL_REAL getVal(int i) const; - FCL_REAL& getVal(size_t i); + FCL_REAL& getVal(int i); }; /// @brief A pair of objects that are not culling away and should further diff --git a/include/hpp/fcl/broadphase/default_broadphase_callbacks.h b/include/hpp/fcl/broadphase/default_broadphase_callbacks.h index bf240fded3c0ca2b33d515dbd9522b454f93134e..4dcdfc42cfaebb2a73167509c2b6cdd96bea19b7 100644 --- a/include/hpp/fcl/broadphase/default_broadphase_callbacks.h +++ b/include/hpp/fcl/broadphase/default_broadphase_callbacks.h @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Copyright (c) 2020, Toyota Research Institute - * Copyright (c) 2022, INRIA + * Copyright (c) 2022-2023, INRIA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -238,9 +238,12 @@ struct HPP_FCL_DLLAPI CollisionCallBackCollect : CollisionCallBackBase { /// @brief Reset the callback void init(); - /// @brief Check wether a collision pair exists + /// @brief Check whether a collision pair exists bool exist(const CollisionPair& pair) const; + /// @brief Check whether a collision pair exists + bool exist(CollisionObject* o1, CollisionObject* o2) const; + virtual ~CollisionCallBackCollect(){}; protected: diff --git a/include/hpp/fcl/broadphase/detail/morton.h b/include/hpp/fcl/broadphase/detail/morton.h index fb480d387a55e2472a3771015bec0b347a3b8862..cf9968af49aedd32c405add78c08a66b1ed0846c 100644 --- a/include/hpp/fcl/broadphase/detail/morton.h +++ b/include/hpp/fcl/broadphase/detail/morton.h @@ -40,6 +40,7 @@ #define HPP_FCL_MORTON_H #include "hpp/fcl/BV/AABB.h" +#include <cstdint> #include <bitset> namespace hpp { diff --git a/include/hpp/fcl/collision_data.h b/include/hpp/fcl/collision_data.h index bf4357365b31a595821f17ea269776c53fc5ef47..9516b4cb6dbb92649f3a84f61a3fb7b2fa3a8bae 100644 --- a/include/hpp/fcl/collision_data.h +++ b/include/hpp/fcl/collision_data.h @@ -39,6 +39,7 @@ #define HPP_FCL_COLLISION_DATA_H #include <vector> +#include <array> #include <set> #include <limits> @@ -70,9 +71,30 @@ struct HPP_FCL_DLLAPI Contact { /// if object 2 is octree, it is the id of the cell int b2; - /// @brief contact normal, pointing from o1 to o2 + /// @brief contact normal, pointing from o1 to o2. + /// The normal defined as the normalized separation vector: + /// normal = (p2 - p1) / dist(o1, o2), where p1 = nearest_points[0] + /// belongs to o1 and p2 = nearest_points[1] belongs to o2 and dist(o1, o2) is + /// the **signed** distance between o1 and o2. The normal always points from + /// o1 to o2. + /// @note The separation vector is the smallest vector such that if o1 is + /// translated by it, o1 and o2 are in touching contact (they share at least + /// one contact point but have a zero intersection volume). If the shapes + /// overlap, dist(o1, o2) = -((p2-p1).norm()). Otherwise, dist(o1, o2) = + /// (p2-p1).norm(). Vec3f normal; + /// @brief nearest points associated to this contact. + /// @note Also referred as "witness points" in other collision libraries. + /// The points p1 = nearest_points[0] and p2 = nearest_points[1] verify the + /// property that dist(o1, o2) * (p1 - p2) is the separation vector between o1 + /// and o2, with dist(o1, o2) being the **signed** distance separating o1 from + /// o2. See \ref DistanceResult::normal for the definition of the separation + /// vector. If o1 and o2 have multiple contacts, the nearest_points are + /// associated with the contact which has the greatest penetration depth. + /// TODO (louis): rename `nearest_points` to `witness_points`. + std::array<Vec3f, 2> nearest_points; + /// @brief contact position, in world space Vec3f pos; @@ -83,11 +105,19 @@ struct HPP_FCL_DLLAPI Contact { static const int NONE = -1; /// @brief Default constructor - Contact() : o1(NULL), o2(NULL), b1(NONE), b2(NONE) {} + Contact() : o1(NULL), o2(NULL), b1(NONE), b2(NONE) { + penetration_depth = (std::numeric_limits<FCL_REAL>::max)(); + nearest_points[0] = nearest_points[1] = normal = pos = + Vec3f::Constant(std::numeric_limits<FCL_REAL>::quiet_NaN()); + } Contact(const CollisionGeometry* o1_, const CollisionGeometry* o2_, int b1_, int b2_) - : o1(o1_), o2(o2_), b1(b1_), b2(b2_) {} + : o1(o1_), o2(o2_), b1(b1_), b2(b2_) { + penetration_depth = (std::numeric_limits<FCL_REAL>::max)(); + nearest_points[0] = nearest_points[1] = normal = pos = + Vec3f::Constant(std::numeric_limits<FCL_REAL>::quiet_NaN()); + } Contact(const CollisionGeometry* o1_, const CollisionGeometry* o2_, int b1_, int b2_, const Vec3f& pos_, const Vec3f& normal_, FCL_REAL depth_) @@ -97,7 +127,24 @@ struct HPP_FCL_DLLAPI Contact { b2(b2_), normal(normal_), pos(pos_), - penetration_depth(depth_) {} + penetration_depth(depth_) { + nearest_points[0] = pos - 0.5 * depth_ * normal_; + nearest_points[1] = pos + 0.5 * depth_ * normal_; + } + + Contact(const CollisionGeometry* o1_, const CollisionGeometry* o2_, int b1_, + int b2_, const Vec3f& p1, const Vec3f& p2, const Vec3f& normal_, + FCL_REAL depth_) + : o1(o1_), + o2(o2_), + b1(b1_), + b2(b2_), + normal(normal_), + penetration_depth(depth_) { + nearest_points[0] = p1; + nearest_points[1] = p2; + pos = (p1 + p2) / 2; + } bool operator<(const Contact& other) const { if (b1 == other.b1) return b2 < other.b2; @@ -107,17 +154,21 @@ struct HPP_FCL_DLLAPI Contact { bool operator==(const Contact& other) const { return o1 == other.o1 && o2 == other.o2 && b1 == other.b1 && b2 == other.b2 && normal == other.normal && pos == other.pos && + nearest_points[0] == other.nearest_points[0] && + nearest_points[1] == other.nearest_points[1] && penetration_depth == other.penetration_depth; } bool operator!=(const Contact& other) const { return !(*this == other); } + + FCL_REAL getDistanceToCollision(const CollisionRequest& request) const; }; struct QueryResult; /// @brief base class for all query requests struct HPP_FCL_DLLAPI QueryRequest { - // @briefInitial guess to use for the GJK algorithm + // @brief Initial guess to use for the GJK algorithm GJKInitialGuess gjk_initial_guess; /// @brief whether enable gjk initial guess @@ -146,6 +197,18 @@ struct HPP_FCL_DLLAPI QueryRequest { /// @brief the support function initial guess set by user support_func_guess_t cached_support_func_guess; + /// @brief max number of faces for EPA + size_t epa_max_face_num; + + /// @brief max number of vertices for EPA + size_t epa_max_vertex_num; + + /// @brief max number of iterations for EPA + size_t epa_max_iterations; + + /// @brief tolerance for EPA + FCL_REAL epa_tolerance; + /// @brief enable timings when performing collision/distance request bool enable_timings; @@ -165,6 +228,10 @@ struct HPP_FCL_DLLAPI QueryRequest { gjk_max_iterations(128), cached_gjk_guess(1, 0, 0), cached_support_func_guess(support_func_guess_t::Zero()), + epa_max_face_num(128), + epa_max_vertex_num(64), + epa_max_iterations(255), + epa_tolerance(1e-6), enable_timings(false), collision_distance_threshold( Eigen::NumTraits<FCL_REAL>::dummy_precision()) {} @@ -184,9 +251,20 @@ struct HPP_FCL_DLLAPI QueryRequest { HPP_FCL_COMPILER_DIAGNOSTIC_IGNORED_DEPRECECATED_DECLARATIONS return gjk_initial_guess == other.gjk_initial_guess && enable_cached_gjk_guess == other.enable_cached_gjk_guess && + gjk_variant == other.gjk_variant && + gjk_convergence_criterion == other.gjk_convergence_criterion && + gjk_convergence_criterion_type == + other.gjk_convergence_criterion_type && + gjk_tolerance == other.gjk_tolerance && + gjk_max_iterations == other.gjk_max_iterations && cached_gjk_guess == other.cached_gjk_guess && cached_support_func_guess == other.cached_support_func_guess && - enable_timings == other.enable_timings; + epa_max_face_num == other.epa_max_face_num && + epa_max_vertex_num == other.epa_max_vertex_num && + epa_max_iterations == other.epa_max_iterations && + epa_tolerance == other.epa_tolerance && + enable_timings == other.enable_timings && + collision_distance_threshold == other.collision_distance_threshold; HPP_FCL_COMPILER_DIAGNOSTIC_POP } }; @@ -233,11 +311,13 @@ enum CollisionRequestFlag { /// @brief request to the collision algorithm struct HPP_FCL_DLLAPI CollisionRequest : QueryRequest { - /// @brief The maximum number of contacts will return + /// @brief The maximum number of contacts that can be returned size_t num_max_contacts; /// @brief whether the contact information (normal, penetration depth and - /// contact position) will return + /// contact position) will return. + /// @note Only effective if the collision pair involves an Octree. + /// Otherwise, it is always true. bool enable_contact; /// Whether a lower bound on distance is returned when objects are disjoint @@ -298,6 +378,11 @@ struct HPP_FCL_DLLAPI CollisionRequest : QueryRequest { } }; +inline FCL_REAL Contact::getDistanceToCollision( + const CollisionRequest& request) const { + return penetration_depth - request.security_margin; +} + /// @brief collision result struct HPP_FCL_DLLAPI CollisionResult : QueryResult { private: @@ -307,18 +392,30 @@ struct HPP_FCL_DLLAPI CollisionResult : QueryResult { public: /// Lower bound on distance between objects if they are disjoint. /// See \ref hpp_fcl_collision_and_distance_lower_bound_computation - /// @note computed only on request (or if it does not add any computational - /// overhead). + /// @note Always computed. If \ref CollisionRequest::distance_upper_bound is + /// set to infinity, distance_lower_bound is the actual distance between the + /// shapes. FCL_REAL distance_lower_bound; - /// @brief nearest points - /// available only when distance_lower_bound is inferior to + /// @brief normal associated to nearest_points. + /// Same as `CollisionResult::nearest_points` but for the normal. + Vec3f normal; + + /// @brief nearest points. + /// A `CollisionResult` can have multiple contacts. + /// The nearest points in `CollisionResults` correspond to the witness points + /// associated with the smallest distance i.e the `distance_lower_bound`. + /// For bounding volumes and BVHs, these nearest points are available + /// only when distance_lower_bound is inferior to /// CollisionRequest::break_distance. - Vec3f nearest_points[2]; + std::array<Vec3f, 2> nearest_points; public: CollisionResult() - : distance_lower_bound((std::numeric_limits<FCL_REAL>::max)()) {} + : distance_lower_bound((std::numeric_limits<FCL_REAL>::max)()) { + nearest_points[0] = nearest_points[1] = normal = + Vec3f::Constant(std::numeric_limits<FCL_REAL>::quiet_NaN()); + } /// @brief Update the lower bound only if the distance is inferior. inline void updateDistanceLowerBound(const FCL_REAL& distance_lower_bound_) { @@ -332,7 +429,10 @@ struct HPP_FCL_DLLAPI CollisionResult : QueryResult { /// @brief whether two CollisionResult are the same or not inline bool operator==(const CollisionResult& other) const { return contacts == other.contacts && - distance_lower_bound == other.distance_lower_bound; + distance_lower_bound == other.distance_lower_bound && + nearest_points[0] == other.nearest_points[0] && + nearest_points[1] == other.nearest_points[1] && + normal == other.normal; } /// @brief return binary collision result @@ -344,8 +444,9 @@ struct HPP_FCL_DLLAPI CollisionResult : QueryResult { /// @brief get the i-th contact calculated const Contact& getContact(size_t i) const { if (contacts.size() == 0) - throw std::invalid_argument( - "The number of contacts is zero. No Contact can be returned."); + HPP_FCL_THROW_PRETTY( + "The number of contacts is zero. No Contact can be returned.", + std::invalid_argument); if (i < contacts.size()) return contacts[i]; @@ -356,8 +457,9 @@ struct HPP_FCL_DLLAPI CollisionResult : QueryResult { /// @brief set the i-th contact calculated void setContact(size_t i, const Contact& c) { if (contacts.size() == 0) - throw std::invalid_argument( - "The number of contacts is zero. No Contact can be returned."); + HPP_FCL_THROW_PRETTY( + "The number of contacts is zero. No Contact can be returned.", + std::invalid_argument); if (i < contacts.size()) contacts[i] = c; @@ -377,8 +479,9 @@ struct HPP_FCL_DLLAPI CollisionResult : QueryResult { void clear() { distance_lower_bound = (std::numeric_limits<FCL_REAL>::max)(); contacts.clear(); - distance_lower_bound = (std::numeric_limits<FCL_REAL>::max)(); timings.clear(); + nearest_points[0] = nearest_points[1] = normal = + Vec3f::Constant(std::numeric_limits<FCL_REAL>::quiet_NaN()); } /// @brief reposition Contact objects when fcl inverts them @@ -419,16 +522,17 @@ struct HPP_FCL_DLLAPI DistanceRequest : QueryRequest { /// @brief distance result struct HPP_FCL_DLLAPI DistanceResult : QueryResult { public: - /// @brief minimum distance between two objects. if two objects are in + /// @brief minimum distance between two objects. If two objects are in /// collision, min_distance <= 0. FCL_REAL min_distance; - /// @brief nearest points - Vec3f nearest_points[2]; - - /// In case both objects are in collision, store the normal + /// @brief normal. Vec3f normal; + /// @brief nearest points. + /// See CollisionResult::nearest_points. + std::array<Vec3f, 2> nearest_points; + /// @brief collision object 1 const CollisionGeometry* o1; @@ -537,7 +641,7 @@ struct HPP_FCL_DLLAPI DistanceResult : QueryResult { namespace internal { inline void updateDistanceLowerBoundFromBV(const CollisionRequest& /*req*/, CollisionResult& res, - const FCL_REAL& sqrDistLowerBound) { + const FCL_REAL sqrDistLowerBound) { // BV cannot find negative distance. if (res.distance_lower_bound <= 0) return; FCL_REAL new_dlb = std::sqrt(sqrDistLowerBound); // - req.security_margin; @@ -547,11 +651,13 @@ inline void updateDistanceLowerBoundFromBV(const CollisionRequest& /*req*/, inline void updateDistanceLowerBoundFromLeaf(const CollisionRequest&, CollisionResult& res, const FCL_REAL& distance, - const Vec3f& p0, const Vec3f& p1) { + const Vec3f& p0, const Vec3f& p1, + const Vec3f& normal) { if (distance < res.distance_lower_bound) { res.distance_lower_bound = distance; res.nearest_points[0] = p0; res.nearest_points[1] = p1; + res.normal = normal; } } } // namespace internal diff --git a/include/hpp/fcl/collision_object.h b/include/hpp/fcl/collision_object.h index 7403f92306a611607cea540d2821e6004c50475c..fae6514644394076b22f8c9f782996578c8a15ea 100644 --- a/include/hpp/fcl/collision_object.h +++ b/include/hpp/fcl/collision_object.h @@ -260,10 +260,18 @@ class HPP_FCL_DLLAPI CollisionObject { if (t.getRotation().isIdentity()) { aabb = translate(cgeom->aabb_local, t.getTranslation()); } else { - Vec3f center(t.transform(cgeom->aabb_center)); - Vec3f delta(Vec3f::Constant(cgeom->aabb_radius)); - aabb.min_ = center - delta; - aabb.max_ = center + delta; + aabb.min_ = aabb.max_ = t.getTranslation(); + + Vec3f min_world, max_world; + for (int k = 0; k < 3; ++k) { + min_world.array() = t.getRotation().row(k).array() * + cgeom->aabb_local.min_.transpose().array(); + max_world.array() = t.getRotation().row(k).array() * + cgeom->aabb_local.max_.transpose().array(); + + aabb.min_[k] += (min_world.array().min)(max_world.array()).sum(); + aabb.max_[k] += (min_world.array().max)(max_world.array()).sum(); + } } } diff --git a/include/hpp/fcl/data_types.h b/include/hpp/fcl/data_types.h index 952a5cd127549bceb992be6f447bef715baba520..8474fec27cbe51504d8b000c0e14bae32510da44 100644 --- a/include/hpp/fcl/data_types.h +++ b/include/hpp/fcl/data_types.h @@ -3,6 +3,7 @@ * * Copyright (c) 2011-2014, Willow Garage, Inc. * Copyright (c) 2014-2015, Open Source Robotics Foundation + * Copyright (c) 2023, Inria * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -64,10 +65,12 @@ namespace hpp { namespace fcl { typedef double FCL_REAL; typedef Eigen::Matrix<FCL_REAL, 3, 1> Vec3f; +typedef Eigen::Matrix<FCL_REAL, 6, 1> Vec6f; typedef Eigen::Matrix<FCL_REAL, Eigen::Dynamic, 1> VecXf; typedef Eigen::Matrix<FCL_REAL, 3, 3> Matrix3f; -typedef Eigen::Matrix<FCL_REAL, Eigen::Dynamic, 3> Matrixx3f; -typedef Eigen::Matrix<Eigen::DenseIndex, Eigen::Dynamic, 3> Matrixx3i; +typedef Eigen::Matrix<FCL_REAL, Eigen::Dynamic, 3, Eigen::RowMajor> Matrixx3f; +typedef Eigen::Matrix<Eigen::DenseIndex, Eigen::Dynamic, 3, Eigen::RowMajor> + Matrixx3i; typedef Eigen::Matrix<FCL_REAL, Eigen::Dynamic, Eigen::Dynamic> MatrixXf; typedef Eigen::Vector2i support_func_guess_t; @@ -80,7 +83,7 @@ typedef Eigen::Vector2i support_func_guess_t; enum GJKInitialGuess { DefaultGuess, CachedGuess, BoundingVolumeGuess }; /// @brief Variant to use for the GJK algorithm -enum GJKVariant { DefaultGJK, NesterovAcceleration }; +enum GJKVariant { DefaultGJK, PolyakAcceleration, NesterovAcceleration }; /// @brief Which convergence criterion is used to stop the algorithm (when the /// shapes are not in collision). (default) VDB: Van den Bergen (A Fast and diff --git a/include/hpp/fcl/fwd.hh b/include/hpp/fcl/fwd.hh index 0585ce3b43dc5d6f2b8456aed3998da3dadc0755..f40179365f3978da567c9daec898951a4550b790 100644 --- a/include/hpp/fcl/fwd.hh +++ b/include/hpp/fcl/fwd.hh @@ -54,6 +54,12 @@ #define HPP_FCL_UNUSED_VARIABLE(var) (void)(var) +#ifdef NDEBUG +#define HPP_FCL_ONLY_USED_FOR_DEBUG(var) HPP_FCL_UNUSED_VARIABLE(var) +#else +#define HPP_FCL_ONLY_USED_FOR_DEBUG(var) +#endif + #define HPP_FCL_THROW_PRETTY(message, exception) \ { \ std::stringstream ss; \ diff --git a/include/hpp/fcl/hfield.h b/include/hpp/fcl/hfield.h index adb8f3d013e62068e5d9b6002d9c3ee73523637a..6c066d0dc1cd19b8769ee4b7891f43e947421a82 100644 --- a/include/hpp/fcl/hfield.h +++ b/include/hpp/fcl/hfield.h @@ -1,7 +1,7 @@ /* * Software License Agreement (BSD License) * - * Copyright (c) 2021, INRIA + * Copyright (c) 2021-2024, INRIA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,6 +38,7 @@ #define HPP_FCL_HEIGHT_FIELD_H #include <hpp/fcl/fwd.hh> +#include <hpp/fcl/data_types.h> #include <hpp/fcl/collision_object.h> #include <hpp/fcl/BV/BV_node.h> #include <hpp/fcl/BVH/BVH_internal.h> @@ -51,6 +52,8 @@ namespace fcl { /// @{ struct HPP_FCL_DLLAPI HFNodeBase { + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + /// @brief An index for first child node or primitive /// If the value is positive, it is the index of the first child bv node /// If the value is negative, it is -(primitive index + 1) @@ -103,6 +106,8 @@ struct HPP_FCL_DLLAPI HFNodeBase { template <typename BV> struct HPP_FCL_DLLAPI HFNode : public HFNodeBase { + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + typedef HFNodeBase Base; /// @brief bounding volume storing the geometry @@ -135,16 +140,11 @@ struct HPP_FCL_DLLAPI HFNode : public HFNodeBase { Vec3f getCenter() const { return bv.center(); } /// @brief Access to the orientation of the BV - const Matrix3f& getOrientation() const { - static const Matrix3f id3 = Matrix3f::Identity(); - return id3; + hpp::fcl::Matrix3f::IdentityReturnType getOrientation() const { + return Matrix3f::Identity(); } virtual ~HFNode() {} - - /// \cond - EIGEN_MAKE_ALIGNED_OPERATOR_NEW - /// \endcond }; namespace details { @@ -181,10 +181,12 @@ struct UpdateBoundingVolume<AABB> { template <typename BV> class HPP_FCL_DLLAPI HeightField : public CollisionGeometry { public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + typedef CollisionGeometry Base; typedef HFNode<BV> Node; - typedef std::vector<Node> BVS; + typedef std::vector<Node, Eigen::aligned_allocator<Node> > BVS; /// @brief Constructing an empty HeightField HeightField() @@ -465,9 +467,6 @@ class HPP_FCL_DLLAPI HeightField : public CollisionGeometry { y_grid == other.y_grid && bvs == other.bvs && num_bvs == other.num_bvs; } - - public: - EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; /// @brief Specialization of getNodeType() for HeightField with different BV diff --git a/include/hpp/fcl/internal/shape_shape_func.h b/include/hpp/fcl/internal/shape_shape_func.h index 181302ea8c32ea47cd6a4787eb9d74bf1af93e3c..89f4f104061bd2ba3fa8c15663976738b846791c 100644 --- a/include/hpp/fcl/internal/shape_shape_func.h +++ b/include/hpp/fcl/internal/shape_shape_func.h @@ -66,6 +66,7 @@ struct ShapeShapeCollider { if (request.isSatisfied(result)) return result.numContacts(); DistanceResult distanceResult; + // (louis) enable_contact not used yet but may be in the future DistanceRequest distanceRequest(request.enable_contact); FCL_REAL distance = ShapeShapeDistance<T_SH1, T_SH2>( o1, tf1, o2, tf2, nsolver, distanceRequest, distanceResult); @@ -73,20 +74,19 @@ struct ShapeShapeCollider { size_t num_contacts = 0; const Vec3f& p1 = distanceResult.nearest_points[0]; const Vec3f& p2 = distanceResult.nearest_points[1]; + const Vec3f& normal = distanceResult.normal; FCL_REAL distToCollision = distance - request.security_margin; internal::updateDistanceLowerBoundFromLeaf(request, result, distToCollision, - p1, p2); + p1, p2, normal); if (distToCollision <= request.collision_distance_threshold && result.numContacts() < request.num_max_contacts) { if (result.numContacts() < request.num_max_contacts) { const Vec3f& p1 = distanceResult.nearest_points[0]; const Vec3f& p2 = distanceResult.nearest_points[1]; - Contact contact( - o1, o2, distanceResult.b1, distanceResult.b2, (p1 + p2) / 2, - (distance <= 0 ? distanceResult.normal : (p2 - p1).normalized()), - -distance); + Contact contact(o1, o2, distanceResult.b1, distanceResult.b2, p1, p2, + normal, distance); result.addContact(contact); } @@ -136,6 +136,11 @@ SHAPE_SHAPE_DISTANCE_SPECIALIZATION(Sphere, Halfspace); SHAPE_SHAPE_DISTANCE_SPECIALIZATION(Sphere, Plane); SHAPE_SHAPE_DISTANCE_SPECIALIZATION(Sphere, Sphere); SHAPE_SHAPE_DISTANCE_SPECIALIZATION(Sphere, Cylinder); +SHAPE_SHAPE_DISTANCE_SPECIALIZATION(Sphere, Capsule); +SHAPE_SHAPE_DISTANCE_SPECIALIZATION(Ellipsoid, Halfspace); +SHAPE_SHAPE_DISTANCE_SPECIALIZATION(Ellipsoid, Plane); +// TODO +// SHAPE_SHAPE_DISTANCE_SPECIALIZATION(TriangleP, TriangleP); SHAPE_SHAPE_DISTANCE_SPECIALIZATION(ConvexBase, Halfspace); SHAPE_SHAPE_DISTANCE_SPECIALIZATION(TriangleP, Halfspace); diff --git a/include/hpp/fcl/internal/tools.h b/include/hpp/fcl/internal/tools.h index 52a2ecad726c960f9c2be5d07ab1b521dbd04a5c..346141a500ae534a2379059bb04ed09d9e6ac91d 100644 --- a/include/hpp/fcl/internal/tools.h +++ b/include/hpp/fcl/internal/tools.h @@ -108,8 +108,7 @@ void eigen(const Eigen::MatrixBase<Derived>& m, int n = 3; int j, iq, ip, i; Scalar tresh, theta, tau, t, sm, s, h, g, c; - int nrot; - HPP_FCL_UNUSED_VARIABLE(nrot); + Scalar b[3]; Scalar z[3]; Scalar v[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; @@ -120,8 +119,6 @@ void eigen(const Eigen::MatrixBase<Derived>& m, z[ip] = 0; } - nrot = 0; - for (i = 0; i < 50; ++i) { sm = 0; for (ip = 0; ip < n; ++ip) @@ -189,7 +186,6 @@ void eigen(const Eigen::MatrixBase<Derived>& m, v[j][ip] = g - s * (h + g * tau); v[j][iq] = h + s * (g - h * tau); } - nrot++; } } } diff --git a/include/hpp/fcl/internal/traversal_node_bvh_shape.h b/include/hpp/fcl/internal/traversal_node_bvh_shape.h index f1ced715e6c41d15321a792d21561056288a1901..d7521d88e6bb897c72546bc321420fa4d692ced4 100644 --- a/include/hpp/fcl/internal/traversal_node_bvh_shape.h +++ b/include/hpp/fcl/internal/traversal_node_bvh_shape.h @@ -193,38 +193,30 @@ class MeshShapeCollisionTraversalNode Vec3f normal; Vec3f c1, c2; // closest point - bool collision; if (RTIsIdentity) { static const Transform3f Id; - collision = nsolver->shapeTriangleInteraction( - *(this->model2), this->tf2, p1, p2, p3, Id, distance, c2, c1, normal); + nsolver->shapeTriangleInteraction(*(this->model2), this->tf2, p1, p2, p3, + Id, distance, c2, c1, normal); } else { - collision = nsolver->shapeTriangleInteraction(*(this->model2), this->tf2, - p1, p2, p3, this->tf1, - distance, c2, c1, normal); + nsolver->shapeTriangleInteraction(*(this->model2), this->tf2, p1, p2, p3, + this->tf1, distance, c2, c1, normal); } + // TODO @louis: change things here as in hfields & ShapeShapeDistance FCL_REAL distToCollision = distance - this->request.security_margin; - if (collision) { + if (distToCollision <= this->request.collision_distance_threshold) { sqrDistLowerBound = 0; if (this->request.num_max_contacts > this->result->numContacts()) { this->result->addContact(Contact(this->model1, this->model2, - primitive_id, Contact::NONE, c1, - -normal, -distance)); + primitive_id, Contact::NONE, c1, c2, + -normal, distance)); assert(this->result->isCollision()); } - } else if (distToCollision <= this->request.collision_distance_threshold) { - sqrDistLowerBound = 0; - if (this->request.num_max_contacts > this->result->numContacts()) { - this->result->addContact( - Contact(this->model1, this->model2, primitive_id, Contact::NONE, - .5 * (c1 + c2), (c2 - c1).normalized(), -distance)); - } } else sqrDistLowerBound = distToCollision * distToCollision; - internal::updateDistanceLowerBoundFromLeaf(this->request, *this->result, - distToCollision, c1, c2); + internal::updateDistanceLowerBoundFromLeaf( + this->request, *this->result, distToCollision, c1, c2, -normal); assert(this->result->isCollision() || sqrDistLowerBound > 0); } diff --git a/include/hpp/fcl/internal/traversal_node_bvhs.h b/include/hpp/fcl/internal/traversal_node_bvhs.h index 9880bec43c81efbbcd75bc620735a75e94d52856..c4c40a20f5e40b478a1c145654e7f6c5a97a9747 100644 --- a/include/hpp/fcl/internal/traversal_node_bvhs.h +++ b/include/hpp/fcl/internal/traversal_node_bvhs.h @@ -216,23 +216,22 @@ class MeshCollisionTraversalNode : public BVHCollisionTraversalNode<BV> { if (distToCollision <= this->request.collision_distance_threshold) { // collision sqrDistLowerBound = 0; - Vec3f p(p1); // contact point if (this->result->numContacts() < this->request.num_max_contacts) { // How much (Q1, Q2, Q3) should be moved so that all vertices are // above (P1, P2, P3). - if (distance > 0) { - normal = (p2 - p1).normalized(); - p = .5 * (p1 + p2); - } + // The normal is already computed by GJK, no need to recompute it. + // if (distance > 0) { + // normal = (p2 - p1).normalized(); + // } this->result->addContact(Contact(this->model1, this->model2, - primitive_id1, primitive_id2, p, - normal, -distance)); + primitive_id1, primitive_id2, p1, p2, + normal, distance)); } } else sqrDistLowerBound = distToCollision * distToCollision; internal::updateDistanceLowerBoundFromLeaf(this->request, *this->result, - distToCollision, p1, p2); + distToCollision, p1, p2, normal); } Vec3f* vertices1; diff --git a/include/hpp/fcl/internal/traversal_node_hfield_shape.h b/include/hpp/fcl/internal/traversal_node_hfield_shape.h index 9fdcb85d1aafcf24d00fddf706ae5a6b6bd1d6a0..1774c68f641c24091dc7cc1e4cd59a3c295f85c2 100644 --- a/include/hpp/fcl/internal/traversal_node_hfield_shape.h +++ b/include/hpp/fcl/internal/traversal_node_hfield_shape.h @@ -1,7 +1,7 @@ /* * Software License Agreement (BSD License) * - * Copyright (c) 2021, INRIA. + * Copyright (c) 2021-2023, INRIA. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -45,6 +45,7 @@ #include <hpp/fcl/shape/geometric_shapes_utility.h> #include <hpp/fcl/internal/traversal_node_base.h> #include <hpp/fcl/internal/traversal.h> +#include <hpp/fcl/internal/intersect.h> #include <hpp/fcl/hfield.h> #include <hpp/fcl/shape/convex.h> @@ -73,26 +74,27 @@ Convex<Quadrilateral> buildConvexQuadrilateral(const HFNode<BV>& node, "max_height is lower than min_height"); // Check whether the geometry // is degenerated - Vec3f* pts = new Vec3f[8]; - pts[0] = Vec3f(x0, y0, min_height); - pts[1] = Vec3f(x0, y1, min_height); - pts[2] = Vec3f(x1, y1, min_height); - pts[3] = Vec3f(x1, y0, min_height); - pts[4] = Vec3f(x0, y0, cell(0, 0)); - pts[5] = Vec3f(x0, y1, cell(1, 0)); - pts[6] = Vec3f(x1, y1, cell(1, 1)); - pts[7] = Vec3f(x1, y0, cell(0, 1)); - - Quadrilateral* polygons = new Quadrilateral[6]; - polygons[0].set(0, 3, 2, 1); // x+ side - polygons[1].set(0, 1, 5, 4); // y- side - polygons[2].set(1, 2, 6, 5); // x- side - polygons[3].set(2, 3, 7, 6); // y+ side - polygons[4].set(3, 0, 4, 7); // z- side - polygons[5].set(4, 5, 6, 7); // z+ side - - return Convex<Quadrilateral>(true, - pts, // points + std::shared_ptr<std::vector<Vec3f>> pts(new std::vector<Vec3f>({ + Vec3f(x0, y0, min_height), + Vec3f(x0, y1, min_height), + Vec3f(x1, y1, min_height), + Vec3f(x1, y0, min_height), + Vec3f(x0, y0, cell(0, 0)), + Vec3f(x0, y1, cell(1, 0)), + Vec3f(x1, y1, cell(1, 1)), + Vec3f(x1, y0, cell(0, 1)), + })); + + std::shared_ptr<std::vector<Quadrilateral>> polygons( + new std::vector<Quadrilateral>(6)); + (*polygons)[0].set(0, 3, 2, 1); // x+ side + (*polygons)[1].set(0, 1, 5, 4); // y- side + (*polygons)[2].set(1, 2, 6, 5); // x- side + (*polygons)[3].set(2, 3, 7, 6); // y+ side + (*polygons)[4].set(3, 0, 4, 7); // z- side + (*polygons)[5].set(4, 5, 6, 7); // z+ side + + return Convex<Quadrilateral>(pts, // points 8, // num points polygons, 6 // number of polygons @@ -121,56 +123,278 @@ void buildConvexTriangles(const HFNode<BV>& node, const HeightField<BV>& model, HPP_FCL_UNUSED_VARIABLE(max_height); { - Vec3f* pts = new Vec3f[8]; - pts[0] = Vec3f(x0, y0, min_height); - pts[1] = Vec3f(x0, y1, min_height); - pts[2] = Vec3f(x1, y1, min_height); - pts[3] = Vec3f(x1, y0, min_height); - pts[4] = Vec3f(x0, y0, cell(0, 0)); - pts[5] = Vec3f(x0, y1, cell(1, 0)); - pts[6] = Vec3f(x1, y1, cell(1, 1)); - pts[7] = Vec3f(x1, y0, cell(0, 1)); - - Triangle* triangles = new Triangle[8]; - triangles[0].set(0, 1, 3); // bottom - triangles[1].set(4, 5, 7); // top - triangles[2].set(0, 1, 4); - triangles[3].set(4, 1, 5); - triangles[4].set(1, 7, 3); - triangles[5].set(1, 5, 7); - triangles[6].set(0, 3, 7); - triangles[7].set(7, 4, 0); - - convex1.set(true, - pts, // points - 8, // num points + std::shared_ptr<std::vector<Vec3f>> pts(new std::vector<Vec3f>({ + Vec3f(x0, y0, min_height), + Vec3f(x0, y1, min_height), + Vec3f(x1, y0, min_height), + Vec3f(x0, y0, cell(0, 0)), + Vec3f(x0, y1, cell(1, 0)), + Vec3f(x1, y0, cell(0, 1)), + })); + + std::shared_ptr<std::vector<Triangle>> triangles( + new std::vector<Triangle>(8)); + (*triangles)[0].set(0, 1, 2); // bottom + (*triangles)[1].set(3, 5, 4); // top + (*triangles)[2].set(0, 3, 1); + (*triangles)[3].set(3, 4, 1); + (*triangles)[4].set(1, 5, 2); + (*triangles)[5].set(1, 4, 5); + (*triangles)[6].set(0, 2, 5); + (*triangles)[7].set(5, 3, 0); + + convex1.set(pts, // points + 6, // num points triangles, 8 // number of polygons ); } { - Vec3f* pts = new Vec3f[8]; - memcpy(pts, convex1.points, 8 * sizeof(Vec3f)); - - Triangle* triangles = new Triangle[8]; - triangles[0].set(3, 2, 1); // top - triangles[1].set(5, 6, 7); // bottom - triangles[2].set(1, 2, 5); - triangles[3].set(5, 2, 6); - triangles[4].set(1, 3, 7); - triangles[5].set(1, 7, 5); - triangles[6].set(2, 3, 7); - triangles[7].set(6, 2, 3); - - convex2.set(true, - pts, // points - 8, // num points + std::shared_ptr<std::vector<Vec3f>> pts(new std::vector<Vec3f>({ + Vec3f(x0, y1, min_height), + Vec3f(x1, y1, min_height), + Vec3f(x1, y0, min_height), + Vec3f(x0, y1, cell(1, 0)), + Vec3f(x1, y1, cell(1, 1)), + Vec3f(x1, y0, cell(0, 1)), + })); + + std::shared_ptr<std::vector<Triangle>> triangles( + new std::vector<Triangle>(8)); + (*triangles)[0].set(2, 0, 1); // bottom + (*triangles)[1].set(3, 5, 4); // top + (*triangles)[2].set(0, 3, 1); + (*triangles)[3].set(3, 4, 1); + (*triangles)[4].set(0, 2, 5); + (*triangles)[5].set(0, 5, 3); + (*triangles)[6].set(1, 5, 2); + (*triangles)[7].set(4, 2, 1); + + convex2.set(pts, // points + 6, // num points triangles, 8 // number of polygons ); } } + +inline Vec3f projectTriangle(const Vec3f& pointA, const Vec3f& pointB, + const Vec3f& pointC, const Vec3f& point) { + const Project::ProjectResult result = + Project::projectTriangle(pointA, pointB, pointC, point); + Vec3f res = result.parameterization[0] * pointA + + result.parameterization[1] * pointB + + result.parameterization[2] * pointC; + + return res; +} + +template <typename Polygone, typename Shape> +bool binCorrection(const Convex<Polygone>& convex, const Shape& shape, + const Transform3f& shape_pose, FCL_REAL& distance, + Vec3f& contact_1, Vec3f& contact_2, Vec3f& normal, + Vec3f& normal_top, bool& is_collision) { + const Polygone& top_triangle = (*(convex.polygons))[1]; + const std::vector<Vec3f>& points = *(convex.points); + const Vec3f pointA = points[top_triangle[0]]; + const Vec3f pointB = points[top_triangle[1]]; + const Vec3f pointC = points[top_triangle[2]]; + normal_top = (pointB - pointA).cross(pointC - pointA).normalized(); + if (normal_top[2] < 0) normal_top *= -1.; + + assert(!normal_top.array().isNaN().any() && "normal_top is ill-defined"); + + const Vec3f contact_1_projected = + projectTriangle(pointA, pointB, pointC, contact_1); + + bool hfield_witness_is_on_bin_side; + if (!contact_1_projected.isApprox(contact_1)) { + hfield_witness_is_on_bin_side = true; + } else { + hfield_witness_is_on_bin_side = false; + normal = normal_top; + } + + // We correct only if there is a collision with the bin + if (is_collision) { + int hint = 0; + const Vec3f _support = getSupport( + &shape, -shape_pose.rotation().transpose() * normal_top, true, hint); + const Vec3f support = + shape_pose.rotation() * _support + shape_pose.translation(); + + // Project support into the inclined bin having triangle + const FCL_REAL offset_plane = normal_top.dot(pointA); + const Plane projection_plane(normal_top, offset_plane); + const FCL_REAL distance_support_projection_plane = + projection_plane.signedDistance(support); + + const Vec3f projected_support = + support - distance_support_projection_plane * normal_top; + + // We need now to project the projected in the triangle shape + contact_1 = projectTriangle(pointA, pointB, pointC, projected_support); + contact_2 = contact_1 + distance_support_projection_plane * normal_top; + normal = normal_top; + distance = -(contact_1 - contact_2).norm(); + } + + return hfield_witness_is_on_bin_side; +} + +template <typename Polygone, typename Shape, int Options> +bool shapeDistance(const GJKSolver* nsolver, const Convex<Polygone>& convex1, + const Convex<Polygone>& convex2, const Transform3f& tf1, + const Shape& shape, const Transform3f& tf2, + FCL_REAL& distance, Vec3f& c1, Vec3f& c2, Vec3f& normal, + Vec3f& normal_top, bool& hfield_witness_is_on_bin_side) { + enum { RTIsIdentity = Options & RelativeTransformationIsIdentity }; + + const Transform3f Id; + Vec3f contact1_1, contact1_2, normal1, normal1_top; + Vec3f contact2_1, contact2_2, normal2, normal2_top; + FCL_REAL distance1, distance2; + bool collision1, collision2; + bool hfield_witness_is_on_bin_side1, hfield_witness_is_on_bin_side2; + if (RTIsIdentity) + nsolver->shapeDistance(convex1, Id, shape, tf2, distance1, contact1_1, + contact1_2, normal1); + else + nsolver->shapeDistance(convex1, tf1, shape, tf2, distance1, contact1_1, + contact1_2, normal1); + collision1 = (distance1 < 0); + + hfield_witness_is_on_bin_side1 = + binCorrection(convex1, shape, tf2, distance1, contact1_1, contact1_2, + normal1, normal1_top, collision1); + + if (RTIsIdentity) + nsolver->shapeDistance(convex2, Id, shape, tf2, distance2, contact2_1, + contact2_2, normal); + else + nsolver->shapeDistance(convex2, tf1, shape, tf2, distance2, contact2_1, + contact2_2, normal2); + collision2 = (distance2 < 0); + + hfield_witness_is_on_bin_side2 = + binCorrection(convex2, shape, tf2, distance2, contact2_1, contact2_2, + normal2, normal2_top, collision2); + + if (collision1 && collision2) { + if (distance1 > distance2) // switch values + { + distance = distance2; + c1 = contact2_1; + c2 = contact2_2; + normal = normal2; + normal_top = normal2_top; + hfield_witness_is_on_bin_side = hfield_witness_is_on_bin_side2; + } else { + distance = distance1; + c1 = contact1_1; + c2 = contact1_2; + normal = normal1; + normal_top = normal1_top; + hfield_witness_is_on_bin_side = hfield_witness_is_on_bin_side1; + } + return true; + } else if (collision1) { + distance = distance1; + c1 = contact1_1; + c2 = contact1_2; + normal = normal1; + normal_top = normal1_top; + hfield_witness_is_on_bin_side = hfield_witness_is_on_bin_side1; + return true; + } else if (collision2) { + distance = distance2; + c1 = contact2_1; + c2 = contact2_2; + normal = normal2; + normal_top = normal2_top; + hfield_witness_is_on_bin_side = hfield_witness_is_on_bin_side2; + return true; + } + + if (distance1 > distance2) // switch values + { + distance = distance2; + c1 = contact2_1; + c2 = contact2_2; + normal = normal2; + normal_top = normal2_top; + hfield_witness_is_on_bin_side = hfield_witness_is_on_bin_side2; + } else { + distance = distance1; + c1 = contact1_1; + c2 = contact1_2; + normal = normal1; + normal_top = normal1_top; + hfield_witness_is_on_bin_side = hfield_witness_is_on_bin_side1; + } + return false; +} + +template <typename Polygone, typename Shape, int Options> +bool shapeCollision(const GJKSolver* nsolver, const Convex<Polygone>& convex1, + const Convex<Polygone>& convex2, const Transform3f& tf1, + const Shape& shape, const Transform3f& tf2, + FCL_REAL& distance_lower_bound, Vec3f& contact_point, + Vec3f& normal) { + enum { RTIsIdentity = Options & RelativeTransformationIsIdentity }; + + const Transform3f Id; + Vec3f contact_point2, normal2; + FCL_REAL distance_lower_bound2; + bool collision1, collision2; + if (RTIsIdentity) + collision1 = + nsolver->shapeIntersect(convex1, Id, shape, tf2, distance_lower_bound, + true, &contact_point, &normal); + else + collision1 = + nsolver->shapeIntersect(convex1, tf1, shape, tf2, distance_lower_bound, + true, &contact_point, &normal); + + if (RTIsIdentity) + collision2 = + nsolver->shapeIntersect(convex2, Id, shape, tf2, distance_lower_bound2, + true, &contact_point2, &normal2); + else + collision2 = + nsolver->shapeIntersect(convex2, tf1, shape, tf2, distance_lower_bound2, + true, &contact_point2, &normal2); + + if (collision1 && collision2) { + // In some case, EPA might returns something like + // -(std::numeric_limits<FCL_REAL>::max)(). + if (distance_lower_bound != -(std::numeric_limits<FCL_REAL>::max)() && + distance_lower_bound2 != -(std::numeric_limits<FCL_REAL>::max)()) { + if (distance_lower_bound > distance_lower_bound2) // switch values + { + distance_lower_bound = distance_lower_bound2; + contact_point = contact_point2; + normal = normal2; + } + } else if (distance_lower_bound2 != + -(std::numeric_limits<FCL_REAL>::max)()) { + distance_lower_bound = distance_lower_bound2; + contact_point = contact_point2; + normal = normal2; + } + return true; + } else if (collision1) { + return true; + } else if (collision2) { + distance_lower_bound = distance_lower_bound2; + contact_point = contact_point2; + normal = normal2; + return true; + } + + return false; +} } // namespace details /// @brief Traversal node for collision between height field and shape @@ -198,6 +422,7 @@ class HeightFieldShapeCollisionTraversalNode nsolver = NULL; shape_inflation.setZero(); + count = 0; } /// @brief Whether the BV node in the first BVH tree is leaf @@ -243,112 +468,10 @@ class HeightFieldShapeCollisionTraversalNode return disjoint; } - template <typename Polygone> - bool shapeDistance(const Convex<Polygone>& convex1, - const Convex<Polygone>& convex2, const Transform3f& tf1, - const S& shape, const Transform3f& tf2, FCL_REAL& distance, - Vec3f& c1, Vec3f& c2, Vec3f& normal) const { - const Transform3f Id; - Vec3f contact2_1, contact2_2, normal2; - FCL_REAL distance2; - bool collision1, collision2; - if (RTIsIdentity) - collision1 = !nsolver->shapeDistance(convex1, Id, shape, tf2, distance, - c1, c2, normal); - else - collision1 = !nsolver->shapeDistance(convex1, tf1, shape, tf2, distance, - c1, c2, normal); - - if (RTIsIdentity) - collision2 = !nsolver->shapeDistance(convex2, Id, shape, tf2, distance2, - c1, c2, normal); - else - collision2 = !nsolver->shapeDistance(convex2, tf1, shape, tf2, distance2, - contact2_1, contact2_2, normal2); - - if (collision1 && collision2) { - if (distance > distance2) // switch values - { - distance = distance2; - c1 = contact2_1; - c2 = contact2_2; - normal = normal2; - } - return true; - } else if (collision1) { - return true; - } else if (collision2) { - distance = distance2; - c1 = contact2_1; - c2 = contact2_2; - normal = normal2; - return true; - } - - return false; - } - - template <typename Polygone> - bool shapeCollision(const Convex<Polygone>& convex1, - const Convex<Polygone>& convex2, const Transform3f& tf1, - const S& shape, const Transform3f& tf2, - FCL_REAL& distance_lower_bound, Vec3f& contact_point, - Vec3f& normal) const { - const Transform3f Id; - Vec3f contact_point2, normal2; - FCL_REAL distance_lower_bound2; - bool collision1, collision2; - if (RTIsIdentity) - collision1 = - nsolver->shapeIntersect(convex1, Id, shape, tf2, distance_lower_bound, - true, &contact_point, &normal); - else - collision1 = nsolver->shapeIntersect(convex1, tf1, shape, tf2, - distance_lower_bound, true, - &contact_point, &normal); - - if (RTIsIdentity) - collision2 = nsolver->shapeIntersect(convex2, Id, shape, tf2, - distance_lower_bound2, true, - &contact_point2, &normal2); - else - collision2 = nsolver->shapeIntersect(convex2, tf1, shape, tf2, - distance_lower_bound2, true, - &contact_point2, &normal2); - - if (collision1 && collision2) { - // In some case, EPA might returns something like - // -(std::numeric_limits<FCL_REAL>::max)(). - if (distance_lower_bound != -(std::numeric_limits<FCL_REAL>::max)() && - distance_lower_bound2 != -(std::numeric_limits<FCL_REAL>::max)()) { - if (distance_lower_bound > distance_lower_bound2) // switch values - { - distance_lower_bound = distance_lower_bound2; - contact_point = contact_point2; - normal = normal2; - } - } else if (distance_lower_bound2 != - -(std::numeric_limits<FCL_REAL>::max)()) { - distance_lower_bound = distance_lower_bound2; - contact_point = contact_point2; - normal = normal2; - } - return true; - } else if (collision1) { - return true; - } else if (collision2) { - distance_lower_bound = distance_lower_bound2; - contact_point = contact_point2; - normal = normal2; - return true; - } - - return false; - } - /// @brief Intersection testing between leaves (one Convex and one shape) void leafCollides(unsigned int b1, unsigned int /*b2*/, FCL_REAL& sqrDistLowerBound) const { + count++; if (this->enable_statistics) this->num_leaf_tests++; const HFNode<BV>& node = this->model1->getBV(b1); @@ -363,33 +486,33 @@ class HeightFieldShapeCollisionTraversalNode ConvexTriangle convex1, convex2; details::buildConvexTriangles(node, *this->model1, convex1, convex2); + // Compute aabb_local for BoundingVolumeGuess case in the GJK solver + if (nsolver->gjk_initial_guess == GJKInitialGuess::BoundingVolumeGuess) { + convex1.computeLocalAABB(); + convex2.computeLocalAABB(); + } + FCL_REAL distance; // Vec3f contact_point, normal; - Vec3f c1, c2, normal; + Vec3f c1, c2, normal, normal_top; + bool hfield_witness_is_on_bin_side; - bool collision = - this->shapeDistance(convex1, convex2, this->tf1, *(this->model2), - this->tf2, distance, c1, c2, normal); + bool collision = details::shapeDistance<Triangle, S, Options>( + nsolver, convex1, convex2, this->tf1, *(this->model2), this->tf2, + distance, c1, c2, normal, normal_top, hfield_witness_is_on_bin_side); - // this->shapeCollision(convex1, convex2, this->tf1, *(this->model2), - // this->tf2, - // distance, contact_point, normal); - - FCL_REAL distToCollision = distance - this->request.security_margin; + FCL_REAL distToCollision = + distance - this->request.security_margin * (normal_top.dot(normal)); if (distToCollision <= this->request.collision_distance_threshold) { sqrDistLowerBound = 0; - if (this->request.num_max_contacts > this->result->numContacts()) { - this->result->addContact(Contact(this->model1, this->model2, (int)b1, - (int)Contact::NONE, .5 * (c1 + c2), - (c2 - c1).normalized(), -distance)); - } - } else if (collision && this->request.security_margin >= 0) { - sqrDistLowerBound = 0; - if (this->request.num_max_contacts > this->result->numContacts()) { - this->result->addContact(Contact(this->model1, this->model2, (int)b1, - (int)Contact::NONE, c1, normal, - -distance)); - assert(this->result->isCollision()); + if (this->result->numContacts() < this->request.num_max_contacts) { + if (normal_top.isApprox(normal) && + (collision || !hfield_witness_is_on_bin_side)) { + this->result->addContact(Contact(this->model1, this->model2, (int)b1, + (int)Contact::NONE, c1, c2, normal, + distance)); + assert(this->result->isCollision()); + } } } else sqrDistLowerBound = distToCollision * distToCollision; @@ -397,7 +520,7 @@ class HeightFieldShapeCollisionTraversalNode // const Vec3f c1 = contact_point - distance * 0.5 * normal; // const Vec3f c2 = contact_point + distance * 0.5 * normal; internal::updateDistanceLowerBoundFromLeaf(this->request, *this->result, - distToCollision, c1, c2); + distToCollision, c1, c2, normal); assert(this->result->isCollision() || sqrDistLowerBound > 0); } @@ -413,6 +536,7 @@ class HeightFieldShapeCollisionTraversalNode mutable int num_bv_tests; mutable int num_leaf_tests; mutable FCL_REAL query_time_seconds; + mutable int count; }; /// @} diff --git a/include/hpp/fcl/internal/traversal_node_octree.h b/include/hpp/fcl/internal/traversal_node_octree.h index 7aea1bfa9eacb7682f05ef7c5d5f6fa3d69dec8b..e1b5c8ed9a23ee21678ba5cccb39b7e2d4fd14dc 100644 --- a/include/hpp/fcl/internal/traversal_node_octree.h +++ b/include/hpp/fcl/internal/traversal_node_octree.h @@ -3,6 +3,7 @@ * * Copyright (c) 2011-2014, Willow Garage, Inc. * Copyright (c) 2014-2015, Open Source Robotics Foundation + * Copyright (c) 2022-2023, INRIA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,7 +43,9 @@ #include <hpp/fcl/collision_data.h> #include <hpp/fcl/internal/traversal_node_base.h> +#include <hpp/fcl/internal/traversal_node_hfield_shape.h> #include <hpp/fcl/narrowphase/narrowphase.h> +#include <hpp/fcl/hfield.h> #include <hpp/fcl/octree.h> #include <hpp/fcl/BVH/BVH_model.h> #include <hpp/fcl/shape/geometric_shapes_utility.h> @@ -148,6 +151,34 @@ class HPP_FCL_DLLAPI OcTreeSolver { tree2->getRootBV(), tf1, tf2); } + template <typename BV> + void OcTreeHeightFieldIntersect( + const OcTree* tree1, const HeightField<BV>* tree2, const Transform3f& tf1, + const Transform3f& tf2, const CollisionRequest& request_, + CollisionResult& result_, FCL_REAL& sqrDistLowerBound) const { + crequest = &request_; + cresult = &result_; + + OcTreeHeightFieldIntersectRecurse(tree1, tree1->getRoot(), + tree1->getRootBV(), tree2, 0, tf1, tf2, + sqrDistLowerBound); + } + + template <typename BV> + void HeightFieldOcTreeIntersect(const HeightField<BV>* tree1, + const OcTree* tree2, const Transform3f& tf1, + const Transform3f& tf2, + const CollisionRequest& request_, + CollisionResult& result_, + FCL_REAL& sqrDistLowerBound) const { + crequest = &request_; + cresult = &result_; + + HeightFieldOcTreeIntersectRecurse(tree1, 0, tree2, tree2->getRoot(), + tree2->getRootBV(), tf1, tf2, + sqrDistLowerBound); + } + /// @brief collision between octree and shape template <typename S> void OcTreeShapeIntersect(const OcTree* tree, const S& s, @@ -225,6 +256,10 @@ class HPP_FCL_DLLAPI OcTreeSolver { Transform3f box_tf; constructBox(bv1, tf1, box, box_tf); + if (solver->gjk_initial_guess == GJKInitialGuess::BoundingVolumeGuess) { + box.computeLocalAABB(); + } + FCL_REAL dist; Vec3f closest_p1, closest_p2, normal; solver->shapeDistance(box, box_tf, s, tf2, dist, closest_p1, closest_p2, @@ -294,6 +329,9 @@ class HPP_FCL_DLLAPI OcTreeSolver { Box box; Transform3f box_tf; constructBox(bv1, tf1, box, box_tf); + if (solver->gjk_initial_guess == GJKInitialGuess::BoundingVolumeGuess) { + box.computeLocalAABB(); + } bool contactNotAdded = (cresult->numContacts() >= crequest->num_max_contacts); @@ -309,6 +347,8 @@ class HPP_FCL_DLLAPI OcTreeSolver { c.b2, c.pos, c.normal, c.penetration_depth)); } + // no need to call `internal::updateDistanceLowerBoundFromLeaf` here + // as it is already done internally in `ShapeShapeCollide` above. return crequest->isSatisfied(*cresult); } @@ -339,11 +379,16 @@ class HPP_FCL_DLLAPI OcTreeSolver { Transform3f box_tf; constructBox(bv1, tf1, box, box_tf); - int primitive_id = tree2->getBV(root2).primitiveId(); - const Triangle& tri_id = tree2->tri_indices[primitive_id]; - const Vec3f& p1 = tree2->vertices[tri_id[0]]; - const Vec3f& p2 = tree2->vertices[tri_id[1]]; - const Vec3f& p3 = tree2->vertices[tri_id[2]]; + if (solver->gjk_initial_guess == GJKInitialGuess::BoundingVolumeGuess) { + box.computeLocalAABB(); + } + + size_t primitive_id = + static_cast<size_t>(tree2->getBV(root2).primitiveId()); + const Triangle& tri_id = (*(tree2->tri_indices))[primitive_id]; + const Vec3f& p1 = (*(tree2->vertices))[tri_id[0]]; + const Vec3f& p2 = (*(tree2->vertices))[tri_id[1]]; + const Vec3f& p3 = (*(tree2->vertices))[tri_id[2]]; FCL_REAL dist; Vec3f closest_p1, closest_p2, normal; @@ -351,7 +396,8 @@ class HPP_FCL_DLLAPI OcTreeSolver { closest_p1, closest_p2, normal); dresult->update(dist, tree1, tree2, (int)(root1 - tree1->getRoot()), - primitive_id, closest_p1, closest_p2, normal); + static_cast<int>(primitive_id), closest_p1, closest_p2, + normal); return drequest->isSatisfied(*dresult); } else @@ -451,34 +497,32 @@ class HPP_FCL_DLLAPI OcTreeSolver { Box box; Transform3f box_tf; constructBox(bv1, tf1, box, box_tf); + if (solver->gjk_initial_guess == GJKInitialGuess::BoundingVolumeGuess) { + box.computeLocalAABB(); + } - int primitive_id = bvn2.primitiveId(); - const Triangle& tri_id = tree2->tri_indices[primitive_id]; - const Vec3f& p1 = tree2->vertices[tri_id[0]]; - const Vec3f& p2 = tree2->vertices[tri_id[1]]; - const Vec3f& p3 = tree2->vertices[tri_id[2]]; + size_t primitive_id = static_cast<size_t>(bvn2.primitiveId()); + const Triangle& tri_id = (*(tree2->tri_indices))[primitive_id]; + const Vec3f& p1 = (*(tree2->vertices))[tri_id[0]]; + const Vec3f& p2 = (*(tree2->vertices))[tri_id[1]]; + const Vec3f& p3 = (*(tree2->vertices))[tri_id[2]]; Vec3f c1, c2, normal; FCL_REAL distance; - bool collision = solver->shapeTriangleInteraction( - box, box_tf, p1, p2, p3, tf2, distance, c1, c2, normal); + solver->shapeTriangleInteraction(box, box_tf, p1, p2, p3, tf2, distance, + c1, c2, normal); FCL_REAL distToCollision = distance - crequest->security_margin; if (cresult->numContacts() < crequest->num_max_contacts) { - if (collision) { - cresult->addContact(Contact(tree1, tree2, - (int)(root1 - tree1->getRoot()), - primitive_id, c1, normal, -distance)); - } else if (distToCollision < 0) { + if (distToCollision <= crequest->collision_distance_threshold) { cresult->addContact(Contact( - tree1, tree2, (int)(root1 - tree1->getRoot()), primitive_id, - .5 * (c1 + c2), (c2 - c1).normalized(), -distance)); + tree1, tree2, (int)(root1 - tree1->getRoot()), + static_cast<int>(primitive_id), c1, c2, normal, distance)); } } - internal::updateDistanceLowerBoundFromLeaf(*crequest, *cresult, - distToCollision, c1, c2); - + internal::updateDistanceLowerBoundFromLeaf( + *crequest, *cresult, distToCollision, c1, c2, normal); return crequest->isSatisfied(*cresult); } @@ -509,6 +553,240 @@ class HPP_FCL_DLLAPI OcTreeSolver { return false; } + /// \return True if the request is satisfied. + template <typename BV> + bool OcTreeHeightFieldIntersectRecurse( + const OcTree* tree1, const OcTree::OcTreeNode* root1, const AABB& bv1, + const HeightField<BV>* tree2, unsigned int root2, const Transform3f& tf1, + const Transform3f& tf2, FCL_REAL& sqrDistLowerBound) const { + // FIXME(jmirabel) I do not understand why the BVHModel was traversed. The + // code in this if(!root1) did not output anything so the empty OcTree is + // considered free. Should an empty OcTree be considered free ? + + // Empty OcTree is considered free. + if (!root1) return false; + HFNode<BV> const& bvn2 = tree2->getBV(root2); + + /// stop when 1) bounding boxes of two objects not overlap; OR + /// 2) at least one of the nodes is free; OR + /// 2) (two uncertain nodes OR one node occupied and one node + /// uncertain) AND cost not required + if (tree1->isNodeFree(root1)) + return false; + else if ((tree1->isNodeUncertain(root1) || tree2->isUncertain())) + return false; + else { + OBB obb1, obb2; + convertBV(bv1, tf1, obb1); + convertBV(bvn2.bv, tf2, obb2); + FCL_REAL sqrDistLowerBound_; + if (!obb1.overlap(obb2, *crequest, sqrDistLowerBound_)) { + if (sqrDistLowerBound_ < sqrDistLowerBound) + sqrDistLowerBound = sqrDistLowerBound_; + internal::updateDistanceLowerBoundFromBV(*crequest, *cresult, + sqrDistLowerBound); + return false; + } + } + + // Check if leaf collides. + if (!tree1->nodeHasChildren(root1) && bvn2.isLeaf()) { + assert(tree1->isNodeOccupied(root1)); // it isn't free nor uncertain. + Box box; + Transform3f box_tf; + constructBox(bv1, tf1, box, box_tf); + if (solver->gjk_initial_guess == GJKInitialGuess::BoundingVolumeGuess) { + box.computeLocalAABB(); + } + + typedef Convex<Triangle> ConvexTriangle; + ConvexTriangle convex1, convex2; + details::buildConvexTriangles(bvn2, *tree2, convex1, convex2); + if (solver->gjk_initial_guess == GJKInitialGuess::BoundingVolumeGuess) { + convex1.computeLocalAABB(); + convex2.computeLocalAABB(); + } + + Vec3f c1, c2, normal, normal_top; + FCL_REAL distance; + bool hfield_witness_is_on_bin_side; + + bool collision = details::shapeDistance<Triangle, Box, 0>( + solver, convex1, convex2, tf2, box, box_tf, distance, c2, c1, normal, + normal_top, hfield_witness_is_on_bin_side); + + FCL_REAL distToCollision = + distance - crequest->security_margin * (normal_top.dot(normal)); + + if (distToCollision <= crequest->collision_distance_threshold) { + sqrDistLowerBound = 0; + if (crequest->num_max_contacts > cresult->numContacts()) { + if (normal_top.isApprox(normal) && + (collision || !hfield_witness_is_on_bin_side)) { + cresult->addContact( + Contact(tree1, tree2, (int)(root1 - tree1->getRoot()), + (int)Contact::NONE, c1, c2, -normal, distance)); + } + } + } else + sqrDistLowerBound = distToCollision * distToCollision; + + // const Vec3f c1 = contact_point - distance * 0.5 * normal; + // const Vec3f c2 = contact_point + distance * 0.5 * normal; + internal::updateDistanceLowerBoundFromLeaf( + *crequest, *cresult, distToCollision, c1, c2, -normal); + + assert(cresult->isCollision() || sqrDistLowerBound > 0); + return crequest->isSatisfied(*cresult); + } + + // Determine which tree to traverse first. + if (bvn2.isLeaf() || + (tree1->nodeHasChildren(root1) && (bv1.size() > bvn2.bv.size()))) { + for (unsigned int i = 0; i < 8; ++i) { + if (tree1->nodeChildExists(root1, i)) { + const OcTree::OcTreeNode* child = tree1->getNodeChild(root1, i); + AABB child_bv; + computeChildBV(bv1, i, child_bv); + + if (OcTreeHeightFieldIntersectRecurse(tree1, child, child_bv, tree2, + root2, tf1, tf2, + sqrDistLowerBound)) + return true; + } + } + } else { + if (OcTreeHeightFieldIntersectRecurse(tree1, root1, bv1, tree2, + (unsigned int)bvn2.leftChild(), tf1, + tf2, sqrDistLowerBound)) + return true; + + if (OcTreeHeightFieldIntersectRecurse(tree1, root1, bv1, tree2, + (unsigned int)bvn2.rightChild(), + tf1, tf2, sqrDistLowerBound)) + return true; + } + + return false; + } + + /// \return True if the request is satisfied. + template <typename BV> + bool HeightFieldOcTreeIntersectRecurse( + const HeightField<BV>* tree1, unsigned int root1, const OcTree* tree2, + const OcTree::OcTreeNode* root2, const AABB& bv2, const Transform3f& tf1, + const Transform3f& tf2, FCL_REAL& sqrDistLowerBound) const { + // FIXME(jmirabel) I do not understand why the BVHModel was traversed. The + // code in this if(!root1) did not output anything so the empty OcTree is + // considered free. Should an empty OcTree be considered free ? + + // Empty OcTree is considered free. + if (!root2) return false; + HFNode<BV> const& bvn1 = tree1->getBV(root1); + + /// stop when 1) bounding boxes of two objects not overlap; OR + /// 2) at least one of the nodes is free; OR + /// 2) (two uncertain nodes OR one node occupied and one node + /// uncertain) AND cost not required + if (tree2->isNodeFree(root2)) + return false; + else if ((tree2->isNodeUncertain(root2) || tree1->isUncertain())) + return false; + else { + OBB obb1, obb2; + convertBV(bvn1.bv, tf1, obb1); + convertBV(bv2, tf2, obb2); + FCL_REAL sqrDistLowerBound_; + if (!obb2.overlap(obb1, *crequest, sqrDistLowerBound_)) { + if (sqrDistLowerBound_ < sqrDistLowerBound) + sqrDistLowerBound = sqrDistLowerBound_; + internal::updateDistanceLowerBoundFromBV(*crequest, *cresult, + sqrDistLowerBound); + return false; + } + } + + // Check if leaf collides. + if (!tree2->nodeHasChildren(root2) && bvn1.isLeaf()) { + assert(tree2->isNodeOccupied(root2)); // it isn't free nor uncertain. + Box box; + Transform3f box_tf; + constructBox(bv2, tf2, box, box_tf); + if (solver->gjk_initial_guess == GJKInitialGuess::BoundingVolumeGuess) { + box.computeLocalAABB(); + } + + typedef Convex<Triangle> ConvexTriangle; + ConvexTriangle convex1, convex2; + details::buildConvexTriangles(bvn1, *tree1, convex1, convex2); + if (solver->gjk_initial_guess == GJKInitialGuess::BoundingVolumeGuess) { + convex1.computeLocalAABB(); + convex2.computeLocalAABB(); + } + + Vec3f c1, c2, normal, normal_top; + FCL_REAL distance; + bool hfield_witness_is_on_bin_side; + + bool collision = details::shapeDistance<Triangle, Box, 0>( + solver, convex1, convex2, tf1, box, box_tf, distance, c1, c2, normal, + normal_top, hfield_witness_is_on_bin_side); + + FCL_REAL distToCollision = + distance - crequest->security_margin * (normal_top.dot(normal)); + + if (distToCollision <= crequest->collision_distance_threshold) { + sqrDistLowerBound = 0; + if (crequest->num_max_contacts > cresult->numContacts()) { + if (normal_top.isApprox(normal) && + (collision || !hfield_witness_is_on_bin_side)) { + cresult->addContact(Contact(tree1, tree2, (int)Contact::NONE, + (int)(root2 - tree2->getRoot()), c1, c2, + normal, distance)); + } + } + } else + sqrDistLowerBound = distToCollision * distToCollision; + + // const Vec3f c1 = contact_point - distance * 0.5 * normal; + // const Vec3f c2 = contact_point + distance * 0.5 * normal; + internal::updateDistanceLowerBoundFromLeaf( + *crequest, *cresult, distToCollision, c1, c2, normal); + + assert(cresult->isCollision() || sqrDistLowerBound > 0); + return crequest->isSatisfied(*cresult); + } + + // Determine which tree to traverse first. + if (bvn1.isLeaf() || + (tree2->nodeHasChildren(root2) && (bv2.size() > bvn1.bv.size()))) { + for (unsigned int i = 0; i < 8; ++i) { + if (tree2->nodeChildExists(root2, i)) { + const OcTree::OcTreeNode* child = tree2->getNodeChild(root2, i); + AABB child_bv; + computeChildBV(bv2, i, child_bv); + + if (HeightFieldOcTreeIntersectRecurse(tree1, root1, tree2, child, + child_bv, tf1, tf2, + sqrDistLowerBound)) + return true; + } + } + } else { + if (HeightFieldOcTreeIntersectRecurse( + tree1, (unsigned int)bvn1.leftChild(), tree2, root2, bv2, tf1, + tf2, sqrDistLowerBound)) + return true; + + if (HeightFieldOcTreeIntersectRecurse( + tree1, (unsigned int)bvn1.rightChild(), tree2, root2, bv2, tf1, + tf2, sqrDistLowerBound)) + return true; + } + + return false; + } + bool OcTreeDistanceRecurse(const OcTree* tree1, const OcTree::OcTreeNode* root1, const AABB& bv1, const OcTree* tree2, @@ -522,6 +800,11 @@ class HPP_FCL_DLLAPI OcTreeSolver { constructBox(bv1, tf1, box1, box1_tf); constructBox(bv2, tf2, box2, box2_tf); + if (solver->gjk_initial_guess == GJKInitialGuess::BoundingVolumeGuess) { + box1.computeLocalAABB(); + box2.computeLocalAABB(); + } + FCL_REAL dist; Vec3f closest_p1, closest_p2, normal; solver->shapeDistance(box1, box1_tf, box2, box2_tf, dist, closest_p1, @@ -619,7 +902,7 @@ class HPP_FCL_DLLAPI OcTreeSolver { sqrt(sqrDistLowerBound) - crequest->security_margin; return false; } - if (!crequest->enable_contact) { // Overlap + if (crequest->enable_contact) { // Overlap if (cresult->numContacts() < crequest->num_max_contacts) cresult->addContact( Contact(tree1, tree2, static_cast<int>(root1 - tree1->getRoot()), @@ -637,26 +920,26 @@ class HPP_FCL_DLLAPI OcTreeSolver { constructBox(bv1, tf1, box1, box1_tf); constructBox(bv2, tf2, box2, box2_tf); + if (solver->gjk_initial_guess == GJKInitialGuess::BoundingVolumeGuess) { + box1.computeLocalAABB(); + box2.computeLocalAABB(); + } + FCL_REAL distance; Vec3f c1, c2, normal; - bool collision = solver->shapeDistance(box1, box1_tf, box2, box2_tf, - distance, c1, c2, normal); + solver->shapeDistance(box1, box1_tf, box2, box2_tf, distance, c1, c2, + normal); FCL_REAL distToCollision = distance - crequest->security_margin; if (cresult->numContacts() < crequest->num_max_contacts) { - if (collision) - cresult->addContact( - Contact(tree1, tree2, static_cast<int>(root1 - tree1->getRoot()), - static_cast<int>(root2 - tree2->getRoot()), c1, normal, - -distance)); - else if (distToCollision <= 0) + if (distToCollision <= crequest->collision_distance_threshold) cresult->addContact( Contact(tree1, tree2, static_cast<int>(root1 - tree1->getRoot()), - static_cast<int>(root2 - tree2->getRoot()), - .5 * (c1 + c2), (c2 - c1).normalized(), -distance)); + static_cast<int>(root2 - tree2->getRoot()), c1, c2, + normal, distance)); } - internal::updateDistanceLowerBoundFromLeaf(*crequest, *cresult, - distToCollision, c1, c2); + internal::updateDistanceLowerBoundFromLeaf( + *crequest, *cresult, distToCollision, c1, c2, normal); return crequest->isSatisfied(*cresult); } @@ -840,7 +1123,6 @@ class HPP_FCL_DLLAPI OcTreeMeshCollisionTraversalNode void leafCollides(unsigned int, unsigned int, FCL_REAL& sqrDistLowerBound) const { - std::cout << "leafCollides" << std::endl; otsolver->OcTreeMeshIntersect(model1, model2, tf1, tf2, request, *result); sqrDistLowerBound = std::max((FCL_REAL)0, result->distance_lower_bound); sqrDistLowerBound *= sqrDistLowerBound; @@ -854,6 +1136,68 @@ class HPP_FCL_DLLAPI OcTreeMeshCollisionTraversalNode const OcTreeSolver* otsolver; }; +/// @brief Traversal node for octree-height-field collision +template <typename BV> +class HPP_FCL_DLLAPI OcTreeHeightFieldCollisionTraversalNode + : public CollisionTraversalNodeBase { + public: + OcTreeHeightFieldCollisionTraversalNode(const CollisionRequest& request) + : CollisionTraversalNodeBase(request) { + model1 = NULL; + model2 = NULL; + + otsolver = NULL; + } + + bool BVDisjoints(unsigned int, unsigned int, FCL_REAL&) const { + return false; + } + + void leafCollides(unsigned int, unsigned int, + FCL_REAL& sqrDistLowerBound) const { + otsolver->OcTreeHeightFieldIntersect(model1, model2, tf1, tf2, request, + *result, sqrDistLowerBound); + } + + const OcTree* model1; + const HeightField<BV>* model2; + + Transform3f tf1, tf2; + + const OcTreeSolver* otsolver; +}; + +/// @brief Traversal node for octree-height-field collision +template <typename BV> +class HPP_FCL_DLLAPI HeightFieldOcTreeCollisionTraversalNode + : public CollisionTraversalNodeBase { + public: + HeightFieldOcTreeCollisionTraversalNode(const CollisionRequest& request) + : CollisionTraversalNodeBase(request) { + model1 = NULL; + model2 = NULL; + + otsolver = NULL; + } + + bool BVDisjoints(unsigned int, unsigned int, FCL_REAL&) const { + return false; + } + + void leafCollides(unsigned int, unsigned int, + FCL_REAL& sqrDistLowerBound) const { + otsolver->HeightFieldOcTreeIntersect(model1, model2, tf1, tf2, request, + *result, sqrDistLowerBound); + } + + const HeightField<BV>* model1; + const OcTree* model2; + + Transform3f tf1, tf2; + + const OcTreeSolver* otsolver; +}; + /// @} /// @addtogroup Traversal_For_Distance diff --git a/include/hpp/fcl/internal/traversal_node_setup.h b/include/hpp/fcl/internal/traversal_node_setup.h index e73512634650ed9bea731651e8c3bbbc4ba586d5..545b5fd728aafef0664245618a564609210c54e1 100644 --- a/include/hpp/fcl/internal/traversal_node_setup.h +++ b/include/hpp/fcl/internal/traversal_node_setup.h @@ -222,6 +222,46 @@ bool initialize(OcTreeMeshCollisionTraversalNode<BV>& node, return true; } +/// @brief Initialize traversal node for collision between one octree and one +/// height field, given current object transform +template <typename BV> +bool initialize(OcTreeHeightFieldCollisionTraversalNode<BV>& node, + const OcTree& model1, const Transform3f& tf1, + const HeightField<BV>& model2, const Transform3f& tf2, + const OcTreeSolver* otsolver, CollisionResult& result) { + node.result = &result; + + node.model1 = &model1; + node.model2 = &model2; + + node.otsolver = otsolver; + + node.tf1 = tf1; + node.tf2 = tf2; + + return true; +} + +/// @brief Initialize traversal node for collision between one height field and +/// one octree, given current object transform +template <typename BV> +bool initialize(HeightFieldOcTreeCollisionTraversalNode<BV>& node, + const HeightField<BV>& model1, const Transform3f& tf1, + const OcTree& model2, const Transform3f& tf2, + const OcTreeSolver* otsolver, CollisionResult& result) { + node.result = &result; + + node.model1 = &model1; + node.model2 = &model2; + + node.otsolver = otsolver; + + node.tf1 = tf1; + node.tf2 = tf2; + + return true; +} + /// @brief Initialize traversal node for distance between one mesh and one /// octree, given current object transform template <typename BV> @@ -298,11 +338,13 @@ bool initialize(MeshShapeCollisionTraversalNode<BV, S>& node, "model1 should be of type BVHModelType::BVH_MODEL_TRIANGLES.", std::invalid_argument) - if (!tf1.isIdentity()) // TODO(jcarpent): vectorized version + if (!tf1.isIdentity() && + model1.vertices.get()) // TODO(jcarpent): vectorized version { std::vector<Vec3f> vertices_transformed(model1.num_vertices); + const std::vector<Vec3f>& model1_vertices_ = *(model1.vertices); for (unsigned int i = 0; i < model1.num_vertices; ++i) { - const Vec3f& p = model1.vertices[i]; + const Vec3f& p = model1_vertices_[i]; Vec3f new_v = tf1.transform(p); vertices_transformed[i] = new_v; } @@ -322,8 +364,9 @@ bool initialize(MeshShapeCollisionTraversalNode<BV, S>& node, computeBV(model2, tf2, node.model2_bv); - node.vertices = model1.vertices; - node.tri_indices = model1.tri_indices; + node.vertices = model1.vertices.get() ? model1.vertices->data() : NULL; + node.tri_indices = + model1.tri_indices.get() ? model1.tri_indices->data() : NULL; node.result = &result; @@ -350,8 +393,9 @@ bool initialize(MeshShapeCollisionTraversalNode<BV, S, 0>& node, computeBV(model2, tf2, node.model2_bv); - node.vertices = model1.vertices; - node.tri_indices = model1.tri_indices; + node.vertices = model1.vertices.get() ? model1.vertices->data() : NULL; + node.tri_indices = + model1.tri_indices.get() ? model1.tri_indices->data() : NULL; node.result = &result; @@ -407,8 +451,9 @@ static inline bool setupShapeMeshCollisionOrientedNode( computeBV(model1, tf1, node.model1_bv); - node.vertices = model2.vertices; - node.tri_indices = model2.tri_indices; + node.vertices = model2.vertices.get() ? model2.vertices->data() : NULL; + node.tri_indices = + model2.tri_indices.get() ? model2.tri_indices->data() : NULL; node.result = &result; @@ -434,10 +479,11 @@ bool initialize( "model2 should be of type BVHModelType::BVH_MODEL_TRIANGLES.", std::invalid_argument) - if (!tf1.isIdentity()) { + if (!tf1.isIdentity() && model1.vertices.get()) { std::vector<Vec3f> vertices_transformed1(model1.num_vertices); + const std::vector<Vec3f>& model1_vertices_ = *(model1.vertices); for (unsigned int i = 0; i < model1.num_vertices; ++i) { - Vec3f& p = model1.vertices[i]; + const Vec3f& p = model1_vertices_[i]; Vec3f new_v = tf1.transform(p); vertices_transformed1[i] = new_v; } @@ -449,10 +495,11 @@ bool initialize( tf1.setIdentity(); } - if (!tf2.isIdentity()) { + if (!tf2.isIdentity() && model2.vertices.get()) { std::vector<Vec3f> vertices_transformed2(model2.num_vertices); + const std::vector<Vec3f>& model2_vertices_ = *(model2.vertices); for (unsigned int i = 0; i < model2.num_vertices; ++i) { - Vec3f& p = model2.vertices[i]; + const Vec3f& p = model2_vertices_[i]; Vec3f new_v = tf2.transform(p); vertices_transformed2[i] = new_v; } @@ -469,11 +516,13 @@ bool initialize( node.model2 = &model2; node.tf2 = tf2; - node.vertices1 = model1.vertices; - node.vertices2 = model2.vertices; + node.vertices1 = model1.vertices.get() ? model1.vertices->data() : NULL; + node.vertices2 = model2.vertices.get() ? model2.vertices->data() : NULL; - node.tri_indices1 = model1.tri_indices; - node.tri_indices2 = model2.tri_indices; + node.tri_indices1 = + model1.tri_indices.get() ? model1.tri_indices->data() : NULL; + node.tri_indices2 = + model2.tri_indices.get() ? model2.tri_indices->data() : NULL; node.result = &result; @@ -494,11 +543,13 @@ bool initialize(MeshCollisionTraversalNode<BV, 0>& node, "model2 should be of type BVHModelType::BVH_MODEL_TRIANGLES.", std::invalid_argument) - node.vertices1 = model1.vertices; - node.vertices2 = model2.vertices; + node.vertices1 = model1.vertices ? model1.vertices->data() : NULL; + node.vertices2 = model2.vertices ? model2.vertices->data() : NULL; - node.tri_indices1 = model1.tri_indices; - node.tri_indices2 = model2.tri_indices; + node.tri_indices1 = + model1.tri_indices.get() ? model1.tri_indices->data() : NULL; + node.tri_indices2 = + model2.tri_indices.get() ? model2.tri_indices->data() : NULL; node.model1 = &model1; node.tf1 = tf1; @@ -549,10 +600,11 @@ bool initialize( "model2 should be of type BVHModelType::BVH_MODEL_TRIANGLES.", std::invalid_argument) - if (!tf1.isIdentity()) { + if (!tf1.isIdentity() && model1.vertices.get()) { std::vector<Vec3f> vertices_transformed1(model1.num_vertices); + const std::vector<Vec3f>& model1_vertices_ = *(model1.vertices); for (unsigned int i = 0; i < model1.num_vertices; ++i) { - const Vec3f& p = model1.vertices[i]; + const Vec3f& p = model1_vertices_[i]; Vec3f new_v = tf1.transform(p); vertices_transformed1[i] = new_v; } @@ -564,10 +616,11 @@ bool initialize( tf1.setIdentity(); } - if (!tf2.isIdentity()) { + if (!tf2.isIdentity() && model2.vertices.get()) { std::vector<Vec3f> vertices_transformed2(model2.num_vertices); + const std::vector<Vec3f>& model2_vertices_ = *(model2.vertices); for (unsigned int i = 0; i < model2.num_vertices; ++i) { - const Vec3f& p = model2.vertices[i]; + const Vec3f& p = model2_vertices_[i]; Vec3f new_v = tf2.transform(p); vertices_transformed2[i] = new_v; } @@ -587,11 +640,13 @@ bool initialize( node.model2 = &model2; node.tf2 = tf2; - node.vertices1 = model1.vertices; - node.vertices2 = model2.vertices; + node.vertices1 = model1.vertices.get() ? model1.vertices->data() : NULL; + node.vertices2 = model2.vertices.get() ? model2.vertices->data() : NULL; - node.tri_indices1 = model1.tri_indices; - node.tri_indices2 = model2.tri_indices; + node.tri_indices1 = + model1.tri_indices.get() ? model1.tri_indices->data() : NULL; + node.tri_indices2 = + model2.tri_indices.get() ? model2.tri_indices->data() : NULL; return true; } @@ -619,11 +674,13 @@ bool initialize(MeshDistanceTraversalNode<BV, 0>& node, node.model2 = &model2; node.tf2 = tf2; - node.vertices1 = model1.vertices; - node.vertices2 = model2.vertices; + node.vertices1 = model1.vertices.get() ? model1.vertices->data() : NULL; + node.vertices2 = model2.vertices.get() ? model2.vertices->data() : NULL; - node.tri_indices1 = model1.tri_indices; - node.tri_indices2 = model2.tri_indices; + node.tri_indices1 = + model1.tri_indices.get() ? model1.tri_indices->data() : NULL; + node.tri_indices2 = + model2.tri_indices.get() ? model2.tri_indices->data() : NULL; relativeTransform(tf1.getRotation(), tf1.getTranslation(), tf2.getRotation(), tf2.getTranslation(), node.RT.R, node.RT.T); @@ -644,10 +701,11 @@ bool initialize(MeshShapeDistanceTraversalNode<BV, S>& node, "model1 should be of type BVHModelType::BVH_MODEL_TRIANGLES.", std::invalid_argument) - if (!tf1.isIdentity()) { + if (!tf1.isIdentity() && model1.vertices.get()) { + const std::vector<Vec3f>& model1_vertices_ = *(model1.vertices); std::vector<Vec3f> vertices_transformed1(model1.num_vertices); for (unsigned int i = 0; i < model1.num_vertices; ++i) { - const Vec3f& p = model1.vertices[i]; + const Vec3f& p = model1_vertices_[i]; Vec3f new_v = tf1.transform(p); vertices_transformed1[i] = new_v; } @@ -668,8 +726,9 @@ bool initialize(MeshShapeDistanceTraversalNode<BV, S>& node, node.tf2 = tf2; node.nsolver = nsolver; - node.vertices = model1.vertices; - node.tri_indices = model1.tri_indices; + node.vertices = model1.vertices.get() ? model1.vertices->data() : NULL; + node.tri_indices = + model1.tri_indices.get() ? model1.tri_indices->data() : NULL; computeBV(model2, tf2, node.model2_bv); @@ -689,10 +748,11 @@ bool initialize(ShapeMeshDistanceTraversalNode<S, BV>& node, const S& model1, "model2 should be of type BVHModelType::BVH_MODEL_TRIANGLES.", std::invalid_argument) - if (!tf2.isIdentity()) { + if (!tf2.isIdentity() && model2.vertices.get()) { std::vector<Vec3f> vertices_transformed(model2.num_vertices); + const std::vector<Vec3f>& model2_vertices_ = *(model2.vertices); for (unsigned int i = 0; i < model2.num_vertices; ++i) { - const Vec3f& p = model2.vertices[i]; + const Vec3f& p = model2_vertices_[i]; Vec3f new_v = tf2.transform(p); vertices_transformed[i] = new_v; } @@ -713,8 +773,9 @@ bool initialize(ShapeMeshDistanceTraversalNode<S, BV>& node, const S& model1, node.tf2 = tf2; node.nsolver = nsolver; - node.vertices = model2.vertices; - node.tri_indices = model2.tri_indices; + node.vertices = model2.vertices.get() ? model2.vertices->data() : NULL; + node.tri_indices = + model2.tri_indices.get() ? model2.tri_indices->data() : NULL; computeBV(model1, tf1, node.model1_bv); @@ -745,8 +806,9 @@ static inline bool setupMeshShapeDistanceOrientedNode( computeBV(model2, tf2, node.model2_bv); - node.vertices = model1.vertices; - node.tri_indices = model1.tri_indices; + node.vertices = model1.vertices.get() ? model1.vertices->data() : NULL; + node.tri_indices = + model1.tri_indices.get() ? model1.tri_indices->data() : NULL; return true; } @@ -812,8 +874,9 @@ static inline bool setupShapeMeshDistanceOrientedNode( computeBV(model1, tf1, node.model1_bv); - node.vertices = model2.vertices; - node.tri_indices = model2.tri_indices; + node.vertices = model2.vertices.get() ? model2.vertices->data() : NULL; + node.tri_indices = + model2.tri_indices.get() ? model2.tri_indices->data() : NULL; node.R = tf2.getRotation(); node.T = tf2.getTranslation(); diff --git a/include/hpp/fcl/math/transform.h b/include/hpp/fcl/math/transform.h index 9b4cec0a6ff99362025646d13157e2a945fb29df..9db5b5a75497e2166a5634e2959f712558cda442 100644 --- a/include/hpp/fcl/math/transform.h +++ b/include/hpp/fcl/math/transform.h @@ -38,14 +38,16 @@ #ifndef HPP_FCL_TRANSFORM_H #define HPP_FCL_TRANSFORM_H +#include <hpp/fcl/fwd.hh> #include <hpp/fcl/data_types.h> namespace hpp { namespace fcl { -typedef Eigen::Quaternion<FCL_REAL> Quaternion3f; +HPP_FCL_DEPRECATED typedef Eigen::Quaternion<FCL_REAL> Quaternion3f; +typedef Eigen::Quaternion<FCL_REAL> Quatf; -static inline std::ostream& operator<<(std::ostream& o, const Quaternion3f& q) { +static inline std::ostream& operator<<(std::ostream& o, const Quatf& q) { o << "(" << q.w() << " " << q.x() << " " << q.y() << " " << q.z() << ")"; return o; } @@ -74,14 +76,14 @@ class HPP_FCL_DLLAPI Transform3f { /// @brief Construct transform from rotation and translation template <typename Vector3Like> - Transform3f(const Quaternion3f& q_, const Eigen::MatrixBase<Vector3Like>& T_) + Transform3f(const Quatf& q_, const Eigen::MatrixBase<Vector3Like>& T_) : R(q_.toRotationMatrix()), T(T_) {} /// @brief Construct transform from rotation Transform3f(const Matrix3f& R_) : R(R_), T(Vec3f::Zero()) {} /// @brief Construct transform from rotation - Transform3f(const Quaternion3f& q_) : R(q_), T(Vec3f::Zero()) {} + Transform3f(const Quatf& q_) : R(q_), T(Vec3f::Zero()) {} /// @brief Construct transform from translation Transform3f(const Vec3f& T_) : R(Matrix3f::Identity()), T(T_) {} @@ -115,7 +117,7 @@ class HPP_FCL_DLLAPI Transform3f { inline Matrix3f& rotation() { return R; } /// @brief get quaternion - inline Quaternion3f getQuatRotation() const { return Quaternion3f(R); } + inline Quatf getQuatRotation() const { return Quatf(R); } /// @brief set transform from rotation and translation template <typename Matrix3Like, typename Vector3Like> @@ -126,7 +128,7 @@ class HPP_FCL_DLLAPI Transform3f { } /// @brief set transform from rotation and translation - inline void setTransform(const Quaternion3f& q_, const Vec3f& T_) { + inline void setTransform(const Quatf& q_, const Vec3f& T_) { R = q_.toRotationMatrix(); T = T_; } @@ -144,9 +146,7 @@ class HPP_FCL_DLLAPI Transform3f { } /// @brief set transform from rotation - inline void setQuatRotation(const Quaternion3f& q_) { - R = q_.toRotationMatrix(); - } + inline void setQuatRotation(const Quatf& q_) { R = q_.toRotationMatrix(); } /// @brief transform a given vector by the transform template <typename Derived> @@ -206,9 +206,9 @@ class HPP_FCL_DLLAPI Transform3f { }; template <typename Derived> -inline Quaternion3f fromAxisAngle(const Eigen::MatrixBase<Derived>& axis, - FCL_REAL angle) { - return Quaternion3f(Eigen::AngleAxis<FCL_REAL>(angle, axis)); +inline Quatf fromAxisAngle(const Eigen::MatrixBase<Derived>& axis, + FCL_REAL angle) { + return Quatf(Eigen::AngleAxis<FCL_REAL>(angle, axis)); } } // namespace fcl diff --git a/include/hpp/fcl/narrowphase/gjk.h b/include/hpp/fcl/narrowphase/gjk.h index cce65d0b9cc370bee340e7d3463c076680fe2889..282a0f47969ad1862d378a50e67bd3a5d3a60169 100644 --- a/include/hpp/fcl/narrowphase/gjk.h +++ b/include/hpp/fcl/narrowphase/gjk.h @@ -3,6 +3,7 @@ * * Copyright (c) 2011-2014, Willow Garage, Inc. * Copyright (c) 2014-2015, Open Source Robotics Foundation + * Copyright (c) 2021-2022, INRIA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -162,6 +163,8 @@ struct HPP_FCL_DLLAPI GJK { /// Valid: GJK converged and the shapes are not in collision. /// Inside: GJK converged and the shapes are in collision. /// Failed: GJK did not converge. + /// EarlyStopped: GJK found a separating hyperplane and exited before + /// converting. The shapes are not in collision. enum Status { Valid, Inside, Failed, EarlyStopped }; MinkowskiDiff const* shape; @@ -252,10 +255,9 @@ struct HPP_FCL_DLLAPI GJK { const FCL_REAL& omega); /// @brief Get GJK number of iterations. - inline size_t getIterations() { return iterations; } - - /// @brief Get GJK tolerance. - inline FCL_REAL getTolerance() { return tolerance; } + inline int getIterationsMomentumStopped() const { + return iterations_momentum_stop; + } private: SimplexV store_v[4]; @@ -266,10 +268,14 @@ struct HPP_FCL_DLLAPI GJK { Status status; unsigned int max_iterations; - FCL_REAL tolerance; FCL_REAL distance_upper_bound; + int iterations_momentum_stop; + + public: + FCL_REAL tolerance; size_t iterations; + private: /// @brief discard one vertex from the simplex inline void removeVertex(Simplex& simplex); @@ -290,8 +296,8 @@ struct HPP_FCL_DLLAPI GJK { /// which reuse checks from edges. /// Finally, in addition to the voronoi procedure, checks relying on the order /// of construction - // of the simplex are added. To know more about these, visit - // https://caseymuratori.com/blog_0003. + /// of the simplex are added. To know more about these, visit + /// https://caseymuratori.com/blog_0003. bool projectLineOrigin(const Simplex& current, Simplex& next); /// @brief Project origin (0) onto triangle a-b-c @@ -303,11 +309,6 @@ struct HPP_FCL_DLLAPI GJK { bool projectTetrahedraOrigin(const Simplex& current, Simplex& next); }; -static const size_t EPA_MAX_FACES = 128; -static const size_t EPA_MAX_VERTICES = 64; -static const FCL_REAL EPA_EPS = 0.000001; -static const size_t EPA_MAX_ITERATIONS = 255; - /// @brief class for EPA algorithm struct HPP_FCL_DLLAPI EPA { typedef GJK::SimplexV SimplexV; @@ -357,11 +358,13 @@ struct HPP_FCL_DLLAPI EPA { SimplexHorizon() : cf(NULL), ff(NULL), nf(0) {} }; - private: - unsigned int max_face_num; - unsigned int max_vertex_num; - unsigned int max_iterations; + public: + size_t max_face_num; + size_t max_vertex_num; + size_t max_iterations; + FCL_REAL tolerance; + size_t iterations; public: enum Status { @@ -385,8 +388,8 @@ struct HPP_FCL_DLLAPI EPA { size_t nextsv; SimplexList hull, stock; - EPA(unsigned int max_face_num_, unsigned int max_vertex_num_, - unsigned int max_iterations_, FCL_REAL tolerance_) + EPA(size_t max_face_num_, size_t max_vertex_num_, size_t max_iterations_, + FCL_REAL tolerance_) : max_face_num(max_face_num_), max_vertex_num(max_vertex_num_), max_iterations(max_iterations_), diff --git a/include/hpp/fcl/narrowphase/narrowphase.h b/include/hpp/fcl/narrowphase/narrowphase.h index 519f3aecca13c0f72d33252a5ec9ea9e7dadc532..daa72bf5661a0141d68b279a671134eb9c370448 100644 --- a/include/hpp/fcl/narrowphase/narrowphase.h +++ b/include/hpp/fcl/narrowphase/narrowphase.h @@ -5,7 +5,7 @@ * Copyright (c) 2014-2015, Open Source Robotics Foundation * Copyright (c) 2018-2019, Centre National de la Recherche Scientifique * All rights reserved. - * Copyright (c) 2021-2022, INRIA + * Copyright (c) 2021-2023, INRIA * All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -132,7 +132,7 @@ struct HPP_FCL_DLLAPI GJKSolver { gjk.getClosestPoints(shape, w0, w1); distance_lower_bound = gjk.distance; if (normal) - (*normal).noalias() = tf1.getRotation() * (w0 - w1).normalized(); + (*normal).noalias() = tf1.getRotation() * (w1 - w0).normalized(); if (contact_points) *contact_points = tf1.transform((w0 + w1) / 2); return true; } else { @@ -213,7 +213,8 @@ struct HPP_FCL_DLLAPI GJKSolver { gjk.getClosestPoints(shape, w0, w1); distance = gjk.distance; normal.noalias() = tf1.getRotation() * (w0 - w1).normalized(); - p1 = p2 = tf1.transform((w0 + w1) / 2); + p1 = tf1.transform(w0); + p2 = tf1.transform(w1); } else { details::EPA epa(epa_max_face_num, epa_max_vertex_num, epa_max_iterations, epa_tolerance); @@ -225,17 +226,27 @@ struct HPP_FCL_DLLAPI GJKSolver { epa.getClosestPoints(shape, w0, w1); distance = -epa.depth; normal.noalias() = tf1.getRotation() * epa.normal; - p1 = p2 = tf1.transform(w0 - epa.normal * (epa.depth * 0.5)); + p1 = tf1.transform(w0); + p2 = tf1.transform(w1); assert(distance <= 1e-6); } else { distance = -(std::numeric_limits<FCL_REAL>::max)(); gjk.getClosestPoints(shape, w0, w1); - p1 = p2 = tf1.transform(w0); + p1 = tf1.transform(w0); + p2 = tf1.transform(w1); } } break; case details::GJK::Valid: case details::GJK::EarlyStopped: + col = false; + gjk.getClosestPoints(shape, w0, w1); + distance = gjk.distance; + normal.noalias() = -tf1.getRotation() * gjk.ray; + normal.normalize(); + p1 = tf1.transform(w0); + p2 = tf1.transform(w1); + break; case details::GJK::Failed: col = false; @@ -247,11 +258,13 @@ struct HPP_FCL_DLLAPI GJKSolver { p1 = tf1.transform(p1); p2 = tf1.transform(p2); + normal.setZero(); assert(distance > 0); break; default: assert(false && "should not reach type part."); - throw std::logic_error("GJKSolver: should not reach this part."); + HPP_FCL_THROW_PRETTY("GJKSolver: should not reach this part.", + std::logic_error) } return col; } @@ -296,7 +309,7 @@ struct HPP_FCL_DLLAPI GJKSolver { // assert (distance == (w0 - w1).norm()); distance = gjk.distance; - normal.noalias() = tf1.getRotation() * gjk.ray; + normal.noalias() = -tf1.getRotation() * gjk.ray; normal.normalize(); p1 = tf1.transform(p1); p2 = tf1.transform(p2); @@ -318,6 +331,7 @@ struct HPP_FCL_DLLAPI GJKSolver { normal.normalize(); p1 = tf1.transform(p1); p2 = tf1.transform(p2); + return true; } else { details::EPA epa(epa_max_face_num, epa_max_vertex_num, epa_max_iterations, epa_tolerance); @@ -333,7 +347,7 @@ struct HPP_FCL_DLLAPI GJKSolver { normal.noalias() = tf1.getRotation() * epa.normal; p1 = tf1.transform(w0); p2 = tf1.transform(w1); - return false; + return true; } distance = -(std::numeric_limits<FCL_REAL>::max)(); gjk.getClosestPoints(shape, p1, p2); @@ -373,12 +387,6 @@ struct HPP_FCL_DLLAPI GJKSolver { support_func_cached_guess = support_func_guess_t::Zero(); distance_upper_bound = (std::numeric_limits<FCL_REAL>::max)(); - // EPS settings - epa_max_face_num = 128; - epa_max_vertex_num = 64; - epa_max_iterations = 255; - epa_tolerance = 1e-6; - set(request); } @@ -400,6 +408,12 @@ struct HPP_FCL_DLLAPI GJKSolver { cached_guess = request.cached_gjk_guess; support_func_cached_guess = request.cached_support_func_guess; } + + // EPA settings + epa_max_face_num = request.epa_max_face_num; + epa_max_vertex_num = request.epa_max_vertex_num; + epa_max_iterations = request.epa_max_iterations; + epa_tolerance = request.epa_tolerance; } /// @brief Constructor from a CollisionRequest @@ -411,12 +425,6 @@ struct HPP_FCL_DLLAPI GJKSolver { support_func_cached_guess = support_func_guess_t::Zero(); distance_upper_bound = (std::numeric_limits<FCL_REAL>::max)(); - // EPS settings - epa_max_face_num = 128; - epa_max_vertex_num = 64; - epa_max_iterations = 255; - epa_tolerance = 1e-6; - set(request); } @@ -443,6 +451,12 @@ struct HPP_FCL_DLLAPI GJKSolver { // security margin. Otherwise, we will likely miss some collisions. distance_upper_bound = (std::max)( 0., (std::max)(request.distance_upper_bound, request.security_margin)); + + // EPA settings + epa_max_face_num = request.epa_max_face_num; + epa_max_vertex_num = request.epa_max_vertex_num; + epa_max_iterations = request.epa_max_iterations; + epa_tolerance = request.epa_tolerance; } /// @brief Copy constructor @@ -473,16 +487,16 @@ struct HPP_FCL_DLLAPI GJKSolver { bool operator!=(const GJKSolver& other) const { return !(*this == other); } /// @brief maximum number of simplex face used in EPA algorithm - unsigned int epa_max_face_num; + mutable size_t epa_max_face_num; /// @brief maximum number of simplex vertex used in EPA algorithm - unsigned int epa_max_vertex_num; + mutable size_t epa_max_vertex_num; /// @brief maximum number of iterations used for EPA iterations - unsigned int epa_max_iterations; + mutable size_t epa_max_iterations; /// @brief the threshold used in EPA to stop iteration - FCL_REAL epa_tolerance; + mutable FCL_REAL epa_tolerance; /// @brief the threshold used in GJK to stop iteration mutable FCL_REAL gjk_tolerance; diff --git a/include/hpp/fcl/octree.h b/include/hpp/fcl/octree.h index 19d9d1fd8787b7676ceb7dc2343345d1c998b3f5..5f45c342000e49ced16f2bcf0a0670c82399e9a0 100644 --- a/include/hpp/fcl/octree.h +++ b/include/hpp/fcl/octree.h @@ -3,6 +3,7 @@ * * Copyright (c) 2011-2014, Willow Garage, Inc. * Copyright (c) 2014-2015, Open Source Robotics Foundation + * Copyright (c) 2022-2023, Inria * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +39,7 @@ #ifndef HPP_FCL_OCTREE_H #define HPP_FCL_OCTREE_H -#include <boost/array.hpp> +#include <algorithm> #include <octomap/octomap.h> #include <hpp/fcl/fwd.hh> @@ -51,7 +52,7 @@ namespace fcl { /// @brief Octree is one type of collision geometry which can encode uncertainty /// information in the sensor data. class HPP_FCL_DLLAPI OcTree : public CollisionGeometry { - private: + protected: shared_ptr<const octomap::OcTree> tree; FCL_REAL default_occupancy; @@ -85,6 +86,7 @@ class HPP_FCL_DLLAPI OcTree : public CollisionGeometry { free_threshold = 0; } + /// \brief Copy constructor OcTree(const OcTree& other) : CollisionGeometry(other), tree(other.tree), @@ -92,13 +94,38 @@ class HPP_FCL_DLLAPI OcTree : public CollisionGeometry { occupancy_threshold(other.occupancy_threshold), free_threshold(other.free_threshold) {} + /// \brief Clone *this into a new Octree OcTree* clone() const { return new OcTree(*this); } + /// \brief Returns the tree associated to the underlying octomap OcTree. + shared_ptr<const octomap::OcTree> getTree() const { return tree; } + void exportAsObjFile(const std::string& filename) const; /// @brief compute the AABB for the octree in its local coordinate system void computeLocalAABB() { - aabb_local = getRootBV(); + typedef Eigen::Matrix<float, 3, 1> Vec3float; + Vec3float max_extent, min_extent; + + octomap::OcTree::iterator it = + tree->begin((unsigned char)tree->getTreeDepth()); + octomap::OcTree::iterator end = tree->end(); + + if (it == end) return; + + max_extent = min_extent = Eigen::Map<Vec3float>(&it.getCoordinate().x()); + for (++it; it != end; ++it) { + Eigen::Map<Vec3float> pos(&it.getCoordinate().x()); + max_extent = max_extent.array().max(pos.array()); + min_extent = min_extent.array().min(pos.array()); + } + + // Account for the size of the boxes. + const FCL_REAL resolution = tree->getResolution(); + max_extent.array() += float(resolution / 2.); + min_extent.array() -= float(resolution / 2.); + + aabb_local = AABB(min_extent.cast<FCL_REAL>(), max_extent.cast<FCL_REAL>()); aabb_center = aabb_local.center(); aabb_radius = (aabb_local.min_ - aabb_center).norm(); } @@ -111,9 +138,15 @@ class HPP_FCL_DLLAPI OcTree : public CollisionGeometry { return AABB(Vec3f(-delta, -delta, -delta), Vec3f(delta, delta, delta)); } - /// @brief Returns the depth of octree + /// @brief Returns the depth of the octree unsigned int getTreeDepth() const { return tree->getTreeDepth(); } + /// @brief Returns the size of the octree + unsigned long size() const { return tree->size(); } + + /// @brief Returns the resolution of the octree + FCL_REAL getResolution() const { return tree->getResolution(); } + /// @brief get the root node of the octree OcTreeNode* getRoot() const { return tree->getRoot(); } @@ -137,8 +170,8 @@ class HPP_FCL_DLLAPI OcTree : public CollisionGeometry { /// @brief transform the octree into a bunch of boxes; uncertainty information /// is kept in the boxes. However, we only keep the occupied boxes (i.e., the /// boxes whose occupied probability is higher enough). - std::vector<boost::array<FCL_REAL, 6> > toBoxes() const { - std::vector<boost::array<FCL_REAL, 6> > boxes; + std::vector<Vec6f> toBoxes() const { + std::vector<Vec6f> boxes; boxes.reserve(tree->size() / 2); for (octomap::OcTree::iterator it = tree->begin((unsigned char)tree->getTreeDepth()), @@ -146,20 +179,42 @@ class HPP_FCL_DLLAPI OcTree : public CollisionGeometry { it != end; ++it) { // if(tree->isNodeOccupied(*it)) if (isNodeOccupied(&*it)) { - FCL_REAL size = it.getSize(); FCL_REAL x = it.getX(); FCL_REAL y = it.getY(); FCL_REAL z = it.getZ(); + FCL_REAL size = it.getSize(); FCL_REAL c = (*it).getOccupancy(); FCL_REAL t = tree->getOccupancyThres(); - boost::array<FCL_REAL, 6> box = {{x, y, z, size, c, t}}; + Vec6f box; + box << x, y, z, size, c, t; boxes.push_back(box); } } return boxes; } + /// \brief Returns a byte description of *this + std::vector<uint8_t> tobytes() const { + typedef Eigen::Matrix<float, 3, 1> Vec3float; + const size_t total_size = (tree->size() * sizeof(FCL_REAL) * 3) / 2; + std::vector<uint8_t> bytes; + bytes.reserve(total_size); + + for (octomap::OcTree::iterator + it = tree->begin((unsigned char)tree->getTreeDepth()), + end = tree->end(); + it != end; ++it) { + const Vec3f box_pos = + Eigen::Map<Vec3float>(&it.getCoordinate().x()).cast<FCL_REAL>(); + if (isNodeOccupied(&*it)) + std::copy(box_pos.data(), box_pos.data() + sizeof(FCL_REAL) * 3, + std::back_inserter(bytes)); + } + + return bytes; + } + /// @brief the threshold used to decide whether one node is occupied, this is /// NOT the octree occupied_thresold FCL_REAL getOccupancyThres() const { return occupancy_threshold; } @@ -225,7 +280,7 @@ class HPP_FCL_DLLAPI OcTree : public CollisionGeometry { if (other_ptr == nullptr) return false; const OcTree& other = *other_ptr; - return tree.get() == other.tree.get() && + return (tree.get() == other.tree.get() || toBoxes() == other.toBoxes()) && default_occupancy == other.default_occupancy && occupancy_threshold == other.occupancy_threshold && free_threshold == other.free_threshold; diff --git a/include/hpp/fcl/serialization/BVH_model.h b/include/hpp/fcl/serialization/BVH_model.h index d48d20b2ac2997ee21a50a9c4e9835122ef4fa18..47e4fb787a2ff31a19d75423fe25859a6ae07874 100644 --- a/include/hpp/fcl/serialization/BVH_model.h +++ b/include/hpp/fcl/serialization/BVH_model.h @@ -32,10 +32,11 @@ void save(Archive &ar, const hpp::fcl::BVHModelBase &bvh_model, if (!(bvh_model.build_state == BVH_BUILD_STATE_PROCESSED || bvh_model.build_state == BVH_BUILD_STATE_UPDATED) && (bvh_model.getModelType() == BVH_MODEL_TRIANGLES)) { - throw std::invalid_argument( + HPP_FCL_THROW_PRETTY( "The BVH model is not in a BVH_BUILD_STATE_PROCESSED or " "BVH_BUILD_STATE_UPDATED state.\n" - "The BVHModel could not be serialized."); + "The BVHModel could not be serialized.", + std::invalid_argument); } ar &make_nvp("base", @@ -43,37 +44,13 @@ void save(Archive &ar, const hpp::fcl::BVHModelBase &bvh_model, bvh_model)); ar &make_nvp("num_vertices", bvh_model.num_vertices); - if (bvh_model.num_vertices > 0) { - typedef Eigen::Matrix<FCL_REAL, 3, Eigen::Dynamic> AsVertixMatrix; - const Eigen::Map<const AsVertixMatrix> vertices_map( - reinterpret_cast<const double *>(bvh_model.vertices), 3, - bvh_model.num_vertices); - ar &make_nvp("vertices", vertices_map); - } + ar &make_nvp("vertices", bvh_model.vertices); ar &make_nvp("num_tris", bvh_model.num_tris); - if (bvh_model.num_tris > 0) { - typedef Eigen::Matrix<Triangle::index_type, 3, Eigen::Dynamic> - AsTriangleMatrix; - const Eigen::Map<const AsTriangleMatrix> tri_indices_map( - reinterpret_cast<const Triangle::index_type *>(bvh_model.tri_indices), - 3, bvh_model.num_tris); - ar &make_nvp("tri_indices", tri_indices_map); - } + ar &make_nvp("tri_indices", bvh_model.tri_indices); ar &make_nvp("build_state", bvh_model.build_state); - if (bvh_model.prev_vertices) { - const bool has_prev_vertices = true; - ar << make_nvp("has_prev_vertices", has_prev_vertices); - typedef Eigen::Matrix<FCL_REAL, 3, Eigen::Dynamic> AsVertixMatrix; - const Eigen::Map<const AsVertixMatrix> prev_vertices_map( - reinterpret_cast<const double *>(bvh_model.prev_vertices), 3, - bvh_model.num_vertices); - ar &make_nvp("prev_vertices", prev_vertices_map); - } else { - const bool has_prev_vertices = false; - ar &make_nvp("has_prev_vertices", has_prev_vertices); - } + ar &make_nvp("prev_vertices", bvh_model.prev_vertices); // if(bvh_model.convex) // { @@ -96,65 +73,14 @@ void load(Archive &ar, hpp::fcl::BVHModelBase &bvh_model, boost::serialization::base_object<hpp::fcl::CollisionGeometry>( bvh_model)); - unsigned int num_vertices; - ar >> make_nvp("num_vertices", num_vertices); - if (num_vertices != bvh_model.num_vertices) { - delete[] bvh_model.vertices; - bvh_model.vertices = NULL; - bvh_model.num_vertices = num_vertices; - if (num_vertices > 0) bvh_model.vertices = new Vec3f[num_vertices]; - } - if (num_vertices > 0) { - typedef Eigen::Matrix<FCL_REAL, 3, Eigen::Dynamic> AsVertixMatrix; - Eigen::Map<AsVertixMatrix> vertices_map( - reinterpret_cast<double *>(bvh_model.vertices), 3, - bvh_model.num_vertices); - ar >> make_nvp("vertices", vertices_map); - } else - bvh_model.vertices = NULL; - - unsigned int num_tris; - ar >> make_nvp("num_tris", num_tris); - - if (num_tris != bvh_model.num_tris) { - delete[] bvh_model.tri_indices; - bvh_model.tri_indices = NULL; - bvh_model.num_tris = num_tris; - if (num_tris > 0) bvh_model.tri_indices = new Triangle[num_tris]; - } - if (num_tris > 0) { - typedef Eigen::Matrix<Triangle::index_type, 3, Eigen::Dynamic> - AsTriangleMatrix; - Eigen::Map<AsTriangleMatrix> tri_indices_map( - reinterpret_cast<Triangle::index_type *>(bvh_model.tri_indices), 3, - bvh_model.num_tris); - ar &make_nvp("tri_indices", tri_indices_map); - } else - bvh_model.tri_indices = NULL; + ar >> make_nvp("num_vertices", bvh_model.num_vertices); + ar >> make_nvp("vertices", bvh_model.vertices); + ar >> make_nvp("num_tris", bvh_model.num_tris); + ar >> make_nvp("tri_indices", bvh_model.tri_indices); ar >> make_nvp("build_state", bvh_model.build_state); - typedef internal::BVHModelBaseAccessor Accessor; - reinterpret_cast<Accessor &>(bvh_model).num_tris_allocated = num_tris; - reinterpret_cast<Accessor &>(bvh_model).num_vertices_allocated = num_vertices; - - bool has_prev_vertices; - ar >> make_nvp("has_prev_vertices", has_prev_vertices); - if (has_prev_vertices) { - if (num_vertices != bvh_model.num_vertices) { - delete[] bvh_model.prev_vertices; - bvh_model.prev_vertices = NULL; - if (num_vertices > 0) bvh_model.prev_vertices = new Vec3f[num_vertices]; - } - if (num_vertices > 0) { - typedef Eigen::Matrix<FCL_REAL, 3, Eigen::Dynamic> AsVertixMatrix; - Eigen::Map<AsVertixMatrix> prev_vertices_map( - reinterpret_cast<double *>(bvh_model.prev_vertices), 3, - bvh_model.num_vertices); - ar &make_nvp("prev_vertices", prev_vertices_map); - } - } else - bvh_model.prev_vertices = NULL; + ar >> make_nvp("prev_vertices", bvh_model.prev_vertices); // bool has_convex = true; // ar >> make_nvp("has_convex",has_convex); @@ -228,14 +154,14 @@ void save(Archive &ar, const hpp::fcl::BVHModel<BV> &bvh_model_, // } // - if (bvh_model.bvs) { + if (bvh_model.bvs.get()) { const bool with_bvs = true; ar &make_nvp("with_bvs", with_bvs); ar &make_nvp("num_bvs", bvh_model.num_bvs); ar &make_nvp( "bvs", make_array( - reinterpret_cast<const char *>(bvh_model.bvs), + reinterpret_cast<const char *>(bvh_model.bvs->data()), sizeof(Node) * (std::size_t)bvh_model.num_bvs)); // Assuming BVs are POD. } else { @@ -281,16 +207,18 @@ void load(Archive &ar, hpp::fcl::BVHModel<BV> &bvh_model_, ar >> make_nvp("num_bvs", num_bvs); if (num_bvs != bvh_model.num_bvs) { - delete[] bvh_model.bvs; - bvh_model.bvs = NULL; + bvh_model.bvs.reset(); bvh_model.num_bvs = num_bvs; - if (num_bvs > 0) bvh_model.bvs = new BVNode<BV>[num_bvs]; + if (num_bvs > 0) + bvh_model.bvs.reset(new + typename BVHModel<BV>::bv_node_vector_t(num_bvs)); } if (num_bvs > 0) { - ar >> make_nvp("bvs", make_array(reinterpret_cast<char *>(bvh_model.bvs), - sizeof(Node) * (std::size_t)num_bvs)); + ar >> make_nvp("bvs", + make_array(reinterpret_cast<char *>(bvh_model.bvs->data()), + sizeof(Node) * (std::size_t)num_bvs)); } else - bvh_model.bvs = NULL; + bvh_model.bvs.reset(); } } @@ -302,7 +230,7 @@ namespace fcl { namespace internal { template <typename BV> -struct memory_footprint_evaluator< ::hpp::fcl::BVHModel<BV> > { +struct memory_footprint_evaluator<::hpp::fcl::BVHModel<BV>> { static size_t run(const ::hpp::fcl::BVHModel<BV> &bvh_model) { return static_cast<size_t>(bvh_model.memUsage(false)); } @@ -312,4 +240,13 @@ struct memory_footprint_evaluator< ::hpp::fcl::BVHModel<BV> > { } // namespace fcl } // namespace hpp +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::BVHModel<::hpp::fcl::AABB>) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::BVHModel<::hpp::fcl::OBB>) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::BVHModel<::hpp::fcl::RSS>) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::BVHModel<::hpp::fcl::OBBRSS>) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::BVHModel<::hpp::fcl::kIOS>) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::BVHModel<::hpp::fcl::KDOP<16>>) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::BVHModel<::hpp::fcl::KDOP<18>>) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::BVHModel<::hpp::fcl::KDOP<24>>) + #endif // ifndef HPP_FCL_SERIALIZATION_BVH_MODEL_H diff --git a/include/hpp/fcl/serialization/collision_data.h b/include/hpp/fcl/serialization/collision_data.h index a40f532796438a2a8ac717dfc812930d6f3b4681..c399780855213a2461db5456a9f91b6e03977118 100644 --- a/include/hpp/fcl/serialization/collision_data.h +++ b/include/hpp/fcl/serialization/collision_data.h @@ -17,6 +17,7 @@ void save(Archive& ar, const hpp::fcl::Contact& contact, ar& make_nvp("b1", contact.b1); ar& make_nvp("b2", contact.b2); ar& make_nvp("normal", contact.normal); + ar& make_nvp("nearest_points", contact.nearest_points); ar& make_nvp("pos", contact.pos); ar& make_nvp("penetration_depth", contact.penetration_depth); } @@ -27,6 +28,10 @@ void load(Archive& ar, hpp::fcl::Contact& contact, ar >> make_nvp("b1", contact.b1); ar >> make_nvp("b2", contact.b2); ar >> make_nvp("normal", contact.normal); + std::array<hpp::fcl::Vec3f, 2> nearest_points; + ar >> make_nvp("nearest_points", nearest_points); + contact.nearest_points[0] = nearest_points[0]; + contact.nearest_points[1] = nearest_points[1]; ar >> make_nvp("pos", contact.pos); ar >> make_nvp("penetration_depth", contact.penetration_depth); contact.o1 = NULL; @@ -71,6 +76,7 @@ void serialize(Archive& ar, hpp::fcl::CollisionRequest& collision_request, collision_request.enable_distance_lower_bound); ar& make_nvp("security_margin", collision_request.security_margin); ar& make_nvp("break_distance", collision_request.break_distance); + ar& make_nvp("distance_upper_bound", collision_request.distance_upper_bound); } template <class Archive> @@ -80,6 +86,8 @@ void save(Archive& ar, const hpp::fcl::CollisionResult& collision_result, collision_result)); ar& make_nvp("contacts", collision_result.getContacts()); ar& make_nvp("distance_lower_bound", collision_result.distance_lower_bound); + ar& make_nvp("nearest_points", collision_result.nearest_points); + ar& make_nvp("normal", collision_result.normal); } template <class Archive> @@ -94,6 +102,11 @@ void load(Archive& ar, hpp::fcl::CollisionResult& collision_result, for (size_t k = 0; k < contacts.size(); ++k) collision_result.addContact(contacts[k]); ar >> make_nvp("distance_lower_bound", collision_result.distance_lower_bound); + std::array<hpp::fcl::Vec3f, 2> nearest_points; + ar >> make_nvp("nearest_points", nearest_points); + collision_result.nearest_points[0] = nearest_points[0]; + collision_result.nearest_points[1] = nearest_points[1]; + ar >> make_nvp("normal", collision_result.normal); } HPP_FCL_SERIALIZATION_SPLIT(hpp::fcl::CollisionResult) @@ -115,7 +128,7 @@ void save(Archive& ar, const hpp::fcl::DistanceResult& distance_result, ar& make_nvp("base", boost::serialization::base_object<hpp::fcl::QueryResult>( distance_result)); ar& make_nvp("min_distance", distance_result.min_distance); - ar& make_nvp("nearest_points", make_array(distance_result.nearest_points, 2)); + ar& make_nvp("nearest_points", distance_result.nearest_points); ar& make_nvp("normal", distance_result.normal); ar& make_nvp("b1", distance_result.b1); ar& make_nvp("b2", distance_result.b2); @@ -128,8 +141,10 @@ void load(Archive& ar, hpp::fcl::DistanceResult& distance_result, make_nvp("base", boost::serialization::base_object<hpp::fcl::QueryResult>( distance_result)); ar >> make_nvp("min_distance", distance_result.min_distance); - ar >> - make_nvp("nearest_points", make_array(distance_result.nearest_points, 2)); + std::array<hpp::fcl::Vec3f, 2> nearest_points; + ar >> make_nvp("nearest_points", nearest_points); + distance_result.nearest_points[0] = nearest_points[0]; + distance_result.nearest_points[1] = nearest_points[1]; ar >> make_nvp("normal", distance_result.normal); ar >> make_nvp("b1", distance_result.b1); ar >> make_nvp("b2", distance_result.b2); diff --git a/include/hpp/fcl/serialization/collision_object.h b/include/hpp/fcl/serialization/collision_object.h index 4264b38e353fd1df43ba387152e139fe4246ddd9..018728cf57af1e0e67f97c1d5689ab77fead4674 100644 --- a/include/hpp/fcl/serialization/collision_object.h +++ b/include/hpp/fcl/serialization/collision_object.h @@ -41,4 +41,55 @@ HPP_FCL_SERIALIZATION_SPLIT(hpp::fcl::CollisionGeometry) } // namespace serialization } // namespace boost +namespace hpp { +namespace fcl { + +// fwd declaration +template <typename BV> +class HeightField; + +template <typename PolygonT> +class Convex; + +struct OBB; +struct OBBRSS; +class AABB; + +class OcTree; +class Box; +class Sphere; +class Ellipsoid; +class Capsule; +class Cone; +class TriangleP; +class Cylinder; +class Halfspace; +class Plane; + +namespace serialization { +template <> +struct register_type<CollisionGeometry> { + template <class Archive> + static void on(Archive& ar) { + ar.template register_type<Box>(); + ar.template register_type<Sphere>(); + ar.template register_type<Ellipsoid>(); + ar.template register_type<TriangleP>(); + ar.template register_type<Capsule>(); + ar.template register_type<Cone>(); + ar.template register_type<Cylinder>(); + ar.template register_type<Halfspace>(); + ar.template register_type<Plane>(); + ar.template register_type<OcTree>(); + ar.template register_type<HeightField<OBB>>(); + ar.template register_type<HeightField<OBBRSS>>(); + ar.template register_type<HeightField<AABB>>(); + ar.template register_type<Convex<Triangle>>(); + ; + } +}; +} // namespace serialization +} // namespace fcl +} // namespace hpp + #endif // ifndef HPP_FCL_SERIALIZATION_COLLISION_OBJECT_H diff --git a/include/hpp/fcl/serialization/convex.h b/include/hpp/fcl/serialization/convex.h index dc027bc84edb9ce94b211150201bedb9b34eb2e9..29a4f0310539f0f93256c56d48f615c4d3b13ed9 100644 --- a/include/hpp/fcl/serialization/convex.h +++ b/include/hpp/fcl/serialization/convex.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2024 INRIA // #ifndef HPP_FCL_SERIALIZATION_CONVEX_H @@ -19,7 +19,6 @@ namespace serialization { namespace internal { struct ConvexBaseAccessor : hpp::fcl::ConvexBase { typedef hpp::fcl::ConvexBase Base; - using Base::own_storage_; }; } // namespace internal @@ -29,31 +28,59 @@ void serialize(Archive &ar, hpp::fcl::ConvexBase &convex_base, const unsigned int /*version*/) { using namespace hpp::fcl; - typedef internal::ConvexBaseAccessor Accessor; - Accessor &accessor = reinterpret_cast<Accessor &>(convex_base); - ar &make_nvp("base", boost::serialization::base_object<hpp::fcl::ShapeBase>( convex_base)); const unsigned int num_points_previous = convex_base.num_points; + const unsigned int num_normals_and_offsets_previous = + convex_base.num_normals_and_offsets; ar &make_nvp("num_points", convex_base.num_points); + ar &make_nvp("num_normals_and_offsets", convex_base.num_normals_and_offsets); if (Archive::is_loading::value) { - if (num_points_previous != convex_base.num_points || - !accessor.own_storage_) { - delete[] convex_base.points; - convex_base.points = new hpp::fcl::Vec3f[convex_base.num_points]; - accessor.own_storage_ = true; + if (num_points_previous != convex_base.num_points) { + convex_base.points.reset(); + if (convex_base.num_points > 0) + convex_base.points.reset( + new std::vector<Vec3f>(convex_base.num_points)); + } + + if (num_normals_and_offsets_previous != + convex_base.num_normals_and_offsets) { + convex_base.normals.reset(); + convex_base.offsets.reset(); + if (convex_base.num_normals_and_offsets > 0) { + convex_base.normals.reset( + new std::vector<Vec3f>(convex_base.num_normals_and_offsets)); + convex_base.offsets.reset( + new std::vector<double>(convex_base.num_normals_and_offsets)); + } } } - { + if (convex_base.num_points > 0) { typedef Eigen::Matrix<FCL_REAL, 3, Eigen::Dynamic> MatrixPoints; Eigen::Map<MatrixPoints> points_map( - reinterpret_cast<double *>(convex_base.points), 3, + reinterpret_cast<double *>(convex_base.points->data()), 3, convex_base.num_points); ar &make_nvp("points", points_map); } + if (convex_base.num_normals_and_offsets > 0) { + typedef Eigen::Matrix<FCL_REAL, 3, Eigen::Dynamic> MatrixPoints; + Eigen::Map<MatrixPoints> normals_map( + reinterpret_cast<double *>(convex_base.normals->data()), 3, + convex_base.num_normals_and_offsets); + ar &make_nvp("normals", normals_map); + } + + if (convex_base.num_normals_and_offsets > 0) { + typedef Eigen::Matrix<FCL_REAL, 1, Eigen::Dynamic> VecOfDoubles; + Eigen::Map<VecOfDoubles> offsets_map( + reinterpret_cast<double *>(convex_base.offsets->data()), 1, + convex_base.num_normals_and_offsets); + ar &make_nvp("offsets", offsets_map); + } + ar &make_nvp("center", convex_base.center); // We don't save neighbors as they will be computed directly by calling // fillNeighbors. @@ -75,19 +102,18 @@ void serialize(Archive &ar, hpp::fcl::Convex<PolygonT> &convex_, typedef internal::ConvexAccessor<PolygonT> Accessor; Accessor &convex = reinterpret_cast<Accessor &>(convex_); - ar &make_nvp("base", boost::serialization::base_object<ConvexBase>(convex)); + ar &make_nvp("base", boost::serialization::base_object<ConvexBase>(convex_)); const unsigned int num_polygons_previous = convex.num_polygons; ar &make_nvp("num_polygons", convex.num_polygons); if (Archive::is_loading::value) { if (num_polygons_previous != convex.num_polygons) { - delete[] convex.polygons; - convex.polygons = new PolygonT[convex.num_polygons]; + convex.polygons.reset(new std::vector<PolygonT>(convex.num_polygons)); } } - ar &make_array<PolygonT>(convex.polygons, convex.num_polygons); + ar &make_array<PolygonT>(convex.polygons->data(), convex.num_polygons); if (Archive::is_loading::value) convex.fillNeighbors(); } @@ -95,6 +121,9 @@ void serialize(Archive &ar, hpp::fcl::Convex<PolygonT> &convex_, } // namespace serialization } // namespace boost +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(hpp::fcl::Convex<hpp::fcl::Triangle>) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(hpp::fcl::Convex<hpp::fcl::Quadrilateral>) + namespace hpp { namespace fcl { diff --git a/include/hpp/fcl/serialization/fwd.h b/include/hpp/fcl/serialization/fwd.h index 51ef149082a8b98074ca46fad02c3b69041a8fd3..0075ac064983ae7942da712c120d32846336ef6b 100644 --- a/include/hpp/fcl/serialization/fwd.h +++ b/include/hpp/fcl/serialization/fwd.h @@ -1,12 +1,22 @@ // -// Copyright (c) 2021 INRIA +// Copyright (c) 2021-2024 INRIA // #ifndef HPP_FCL_SERIALIZATION_FWD_H #define HPP_FCL_SERIALIZATION_FWD_H +#include <type_traits> + +#include <boost/archive/text_iarchive.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/xml_iarchive.hpp> +#include <boost/archive/xml_oarchive.hpp> +#include <boost/archive/binary_iarchive.hpp> +#include <boost/archive/binary_oarchive.hpp> + #include <boost/serialization/split_free.hpp> #include <boost/serialization/shared_ptr.hpp> +#include <boost/serialization/export.hpp> #include "hpp/fcl/serialization/eigen.h" @@ -16,4 +26,88 @@ split_free(ar, value, version); \ } +#define HPP_FCL_SERIALIZATION_DECLARE_EXPORT(T) \ + BOOST_CLASS_EXPORT_KEY(T) \ + namespace boost { \ + namespace archive { \ + namespace detail { \ + namespace extra_detail { \ + template <> \ + struct init_guid<T> { \ + static guid_initializer<T> const& g; \ + }; \ + } \ + } \ + } \ + } \ + /**/ + +#define HPP_FCL_SERIALIZATION_DEFINE_EXPORT(T) \ + namespace boost { \ + namespace archive { \ + namespace detail { \ + namespace extra_detail { \ + guid_initializer<T> const& init_guid<T>::g = \ + ::boost::serialization::singleton< \ + guid_initializer<T> >::get_mutable_instance() \ + .export_guid(); \ + } \ + } \ + } \ + } \ + /**/ + +namespace hpp { +namespace fcl { +namespace serialization { +namespace detail { + +template <class Derived, class Base> +struct init_cast_register {}; + +template <class Derived, class Base> +struct cast_register_initializer { + void init(std::true_type) const { + boost::serialization::void_cast_register<Derived, Base>(); + } + + void init(std::false_type) const {} + + cast_register_initializer const& init() const { + BOOST_STATIC_WARNING((std::is_base_of<Base, Derived>::value)); + init(std::is_base_of<Base, Derived>()); + return *this; + } +}; + +} // namespace detail + +template <typename T> +struct register_type { + template <class Archive> + static void on(Archive& /*ar*/) {} +}; +} // namespace serialization +} // namespace fcl +} // namespace hpp + +#define HPP_FCL_SERIALIZATION_CAST_REGISTER(Derived, Base) \ + namespace hpp { \ + namespace fcl { \ + namespace serialization { \ + namespace detail { \ + template <> \ + struct init_cast_register<Derived, Base> { \ + static cast_register_initializer<Derived, Base> const& g; \ + }; \ + cast_register_initializer<Derived, Base> const& init_cast_register< \ + Derived, Base>::g = \ + ::boost::serialization::singleton< \ + cast_register_initializer<Derived, Base> >::get_mutable_instance() \ + .init(); \ + } \ + } \ + } \ + } + #endif // ifndef HPP_FCL_SERIALIZATION_FWD_H diff --git a/include/hpp/fcl/serialization/geometric_shapes.h b/include/hpp/fcl/serialization/geometric_shapes.h index 0459c5f60e91e5924593f328567ce05c6324d304..c56b23ac2862b5448d3fa2299a9bd6c8cfbd3261 100644 --- a/include/hpp/fcl/serialization/geometric_shapes.h +++ b/include/hpp/fcl/serialization/geometric_shapes.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 INRIA +// Copyright (c) 2021-2024 INRIA // #ifndef HPP_FCL_SERIALIZATION_GEOMETRIC_SHAPES_H @@ -7,6 +7,7 @@ #include "hpp/fcl/shape/geometric_shapes.h" #include "hpp/fcl/serialization/fwd.h" +#include "hpp/fcl/serialization/collision_object.h" namespace boost { namespace serialization { @@ -101,4 +102,16 @@ void serialize(Archive& ar, hpp::fcl::Plane& plane, } // namespace serialization } // namespace boost +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::ShapeBase) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::CollisionGeometry) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::TriangleP) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::Box) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::Sphere) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::Ellipsoid) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::Capsule) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::Cone) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::Cylinder) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::Halfspace) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::Plane) + #endif // ifndef HPP_FCL_SERIALIZATION_GEOMETRIC_SHAPES_H diff --git a/include/hpp/fcl/serialization/hfield.h b/include/hpp/fcl/serialization/hfield.h index 7ae2433ef0f3719d547e6d0f19737b53219545c0..15c4ad5bdefc0ef65dc608b54fe17d948df002db 100644 --- a/include/hpp/fcl/serialization/hfield.h +++ b/include/hpp/fcl/serialization/hfield.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2021 INRIA +// Copyright (c) 2021-2024 INRIA // #ifndef HPP_FCL_SERIALIZATION_HFIELD_H @@ -72,4 +72,9 @@ void serialize(Archive &ar, hpp::fcl::HeightField<BV> &hf_model, } // namespace serialization } // namespace boost +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::HeightField<::hpp::fcl::AABB>) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::HeightField<::hpp::fcl::OBB>) +HPP_FCL_SERIALIZATION_DECLARE_EXPORT( + ::hpp::fcl::HeightField<::hpp::fcl::OBBRSS>) + #endif // ifndef HPP_FCL_SERIALIZATION_HFIELD_H diff --git a/include/hpp/fcl/serialization/octree.h b/include/hpp/fcl/serialization/octree.h new file mode 100644 index 0000000000000000000000000000000000000000..8ea430022c6a5a0d8134a7c77ef948959d72b65a --- /dev/null +++ b/include/hpp/fcl/serialization/octree.h @@ -0,0 +1,103 @@ +// +// Copyright (c) 2023-2024 INRIA +// + +#ifndef HPP_FCL_SERIALIZATION_OCTREE_H +#define HPP_FCL_SERIALIZATION_OCTREE_H + +#include <sstream> +#include <iostream> + +#include <boost/serialization/string.hpp> + +#include "hpp/fcl/octree.h" +#include "hpp/fcl/serialization/fwd.h" + +namespace boost { +namespace serialization { + +namespace internal { +struct OcTreeAccessor : hpp::fcl::OcTree { + typedef hpp::fcl::OcTree Base; + using Base::default_occupancy; + using Base::free_threshold; + using Base::occupancy_threshold; + using Base::tree; +}; +} // namespace internal + +template <class Archive> +void save_construct_data(Archive &ar, const hpp::fcl::OcTree *octree_ptr, + const unsigned int /*version*/) { + const double resolution = octree_ptr->getResolution(); + ar << make_nvp("resolution", resolution); +} + +template <class Archive> +void save(Archive &ar, const hpp::fcl::OcTree &octree, + const unsigned int /*version*/) { + typedef internal::OcTreeAccessor Accessor; + const Accessor &access = reinterpret_cast<const Accessor &>(octree); + + std::ostringstream stream; + access.tree->write(stream); + const std::string stream_str = stream.str(); + auto size = stream_str.size(); + // We can't directly serialize stream_str because it contains binary data. + // This create a bug on Windows with text_archive + ar << make_nvp("tree_data_size", size); + ar << make_nvp("tree_data", + make_array(stream_str.c_str(), stream_str.size())); + + ar << make_nvp("base", base_object<hpp::fcl::CollisionGeometry>(octree)); + ar << make_nvp("default_occupancy", access.default_occupancy); + ar << make_nvp("occupancy_threshold", access.occupancy_threshold); + ar << make_nvp("free_threshold", access.free_threshold); +} + +template <class Archive> +void load_construct_data(Archive &ar, hpp::fcl::OcTree *octree_ptr, + const unsigned int /*version*/) { + double resolution; + ar >> make_nvp("resolution", resolution); + new (octree_ptr) hpp::fcl::OcTree(resolution); +} + +template <class Archive> +void load(Archive &ar, hpp::fcl::OcTree &octree, + const unsigned int /*version*/) { + typedef internal::OcTreeAccessor Accessor; + Accessor &access = reinterpret_cast<Accessor &>(octree); + + std::size_t tree_data_size; + ar >> make_nvp("tree_data_size", tree_data_size); + + std::string stream_str; + stream_str.resize(tree_data_size); + /// TODO use stream_str.data in C++17 + assert(tree_data_size > 0 && "tree_data_size should be greater than 0"); + ar >> make_nvp("tree_data", make_array(&stream_str[0], tree_data_size)); + std::istringstream stream(stream_str); + + octomap::AbstractOcTree *new_tree = octomap::AbstractOcTree::read(stream); + access.tree = std::shared_ptr<const octomap::OcTree>( + dynamic_cast<octomap::OcTree *>(new_tree)); + + ar >> make_nvp("base", base_object<hpp::fcl::CollisionGeometry>(octree)); + ar >> make_nvp("default_occupancy", access.default_occupancy); + ar >> make_nvp("occupancy_threshold", access.occupancy_threshold); + ar >> make_nvp("free_threshold", access.free_threshold); +} + +template <class Archive> +void serialize(Archive &ar, hpp::fcl::OcTree &octree, + const unsigned int version) { + split_free(ar, octree, version); +} + +} // namespace serialization +} // namespace boost + +HPP_FCL_SERIALIZATION_DECLARE_EXPORT(::hpp::fcl::OcTree) + +#endif // ifndef HPP_FCL_SERIALIZATION_OCTREE_H diff --git a/include/hpp/fcl/shape/convex.h b/include/hpp/fcl/shape/convex.h index dc348e9d43a4fecadbe4b6f56e14633e6b1634b9..99ea403fefed1c1f18c7a5e5950613895946cde7 100644 --- a/include/hpp/fcl/shape/convex.h +++ b/include/hpp/fcl/shape/convex.h @@ -50,7 +50,7 @@ template <typename PolygonT> class Convex : public ConvexBase { public: /// @brief Construct an uninitialized convex object - Convex() : ConvexBase(), polygons(NULL), num_polygons(0) {} + Convex() : ConvexBase(), num_polygons(0) {} /// @brief Constructing a convex, providing normal and offset of each polytype /// surface, and the points and shape topology information \param ownStorage @@ -61,8 +61,9 @@ class Convex : public ConvexBase { /// \param polygons_ \copydoc Convex::polygons /// \param num_polygons_ the number of polygons. /// \note num_polygons_ is not the allocated size of polygons_. - Convex(bool ownStorage, Vec3f* points_, unsigned int num_points_, - PolygonT* polygons_, unsigned int num_polygons_); + Convex(std::shared_ptr<std::vector<Vec3f>> points_, unsigned int num_points_, + std::shared_ptr<std::vector<PolygonT>> polygons_, + unsigned int num_polygons_); /// @brief Copy constructor /// Only the list of neighbors is copied. @@ -73,7 +74,7 @@ class Convex : public ConvexBase { /// @brief An array of PolygonT object. /// PolygonT should contains a list of vertices for each polygon, /// in counter clockwise order. - PolygonT* polygons; + std::shared_ptr<std::vector<PolygonT>> polygons; unsigned int num_polygons; /// based on http://number-none.com/blow/inertia/bb_inertia.doc @@ -94,8 +95,9 @@ class Convex : public ConvexBase { /// \param num_polygons the number of polygons. /// \note num_polygons is not the allocated size of polygons. /// - void set(bool ownStorage, Vec3f* points, unsigned int num_points, - PolygonT* polygons, unsigned int num_polygons); + void set(std::shared_ptr<std::vector<Vec3f>> points, unsigned int num_points, + std::shared_ptr<std::vector<PolygonT>> polygons, + unsigned int num_polygons); /// @brief Clone (deep copy). virtual Convex<PolygonT>* clone() const; diff --git a/include/hpp/fcl/shape/details/convex.hxx b/include/hpp/fcl/shape/details/convex.hxx index 2a53de92841cf0542e703fb6f8ecdc3ca4870780..49934c8f1f20a0c6727519a744dbfb4988031ec3 100644 --- a/include/hpp/fcl/shape/details/convex.hxx +++ b/include/hpp/fcl/shape/details/convex.hxx @@ -39,41 +39,39 @@ #include <set> #include <vector> +#include <iostream> namespace hpp { namespace fcl { template <typename PolygonT> -Convex<PolygonT>::Convex(bool own_storage, Vec3f* points_, - unsigned int num_points_, PolygonT* polygons_, +Convex<PolygonT>::Convex(std::shared_ptr<std::vector<Vec3f>> points_, + unsigned int num_points_, + std::shared_ptr<std::vector<PolygonT>> polygons_, unsigned int num_polygons_) : ConvexBase(), polygons(polygons_), num_polygons(num_polygons_) { - initialize(own_storage, points_, num_points_); + initialize(points_, num_points_); fillNeighbors(); } template <typename PolygonT> Convex<PolygonT>::Convex(const Convex<PolygonT>& other) - : ConvexBase(other), - polygons(other.polygons), - num_polygons(other.num_polygons) { - if (own_storage_) { - polygons = new PolygonT[num_polygons]; - std::copy(other.polygons, other.polygons + num_polygons, polygons); - } + : ConvexBase(other), num_polygons(other.num_polygons) { + if (other.polygons.get()) { + polygons.reset(new std::vector<PolygonT>(*(other.polygons))); + } else + polygons.reset(); } template <typename PolygonT> -Convex<PolygonT>::~Convex() { - if (own_storage_) delete[] polygons; -} +Convex<PolygonT>::~Convex() {} template <typename PolygonT> -void Convex<PolygonT>::set(bool own_storage, Vec3f* points_, - unsigned int num_points_, PolygonT* polygons_, +void Convex<PolygonT>::set(std::shared_ptr<std::vector<Vec3f>> points_, + unsigned int num_points_, + std::shared_ptr<std::vector<PolygonT>> polygons_, unsigned int num_polygons_) { - if (own_storage_) delete[] polygons; - ConvexBase::set(own_storage, points_, num_points_); + ConvexBase::set(points_, num_points_); num_polygons = num_polygons_; polygons = polygons_; @@ -83,17 +81,7 @@ void Convex<PolygonT>::set(bool own_storage, Vec3f* points_, template <typename PolygonT> Convex<PolygonT>* Convex<PolygonT>::clone() const { - Vec3f* cloned_points = new Vec3f[num_points]; - std::copy(points, points + num_points, cloned_points); - - PolygonT* cloned_polygons = new PolygonT[num_polygons]; - std::copy(polygons, polygons + num_polygons, cloned_polygons); - - Convex* copy_ptr = new Convex(true, cloned_points, num_points, - cloned_polygons, num_polygons); - - copy_ptr->ShapeBase::operator=(*this); - return copy_ptr; + return new Convex(*this); } template <typename PolygonT> @@ -107,13 +95,27 @@ Matrix3f Convex<PolygonT>::computeMomentofInertia() const { C_canonical << 1 / 60.0, 1 / 120.0, 1 / 120.0, 1 / 120.0, 1 / 60.0, 1 / 120.0, 1 / 120.0, 1 / 120.0, 1 / 60.0; + if (!(points.get())) { + std::cerr + << "Error in `Convex::computeMomentofInertia`! Convex has no vertices." + << std::endl; + return C; + } + const std::vector<Vec3f>& points_ = *points; + if (!(polygons.get())) { + std::cerr + << "Error in `Convex::computeMomentofInertia`! Convex has no polygons." + << std::endl; + return C; + } + const std::vector<PolygonT>& polygons_ = *polygons; for (unsigned int i = 0; i < num_polygons; ++i) { - const PolygonT& polygon = polygons[i]; + const PolygonT& polygon = polygons_[i]; // compute the center of the polygon Vec3f plane_center(0, 0, 0); for (size_type j = 0; j < polygon.size(); ++j) - plane_center += points[polygon[(index_type)j]]; + plane_center += points_[polygon[(index_type)j]]; plane_center /= polygon.size(); // compute the volume of tetrahedron making by neighboring two points, the @@ -123,8 +125,8 @@ Matrix3f Convex<PolygonT>::computeMomentofInertia() const { index_type e_first = polygon[static_cast<index_type>(j)]; index_type e_second = polygon[static_cast<index_type>((j + 1) % polygon.size())]; - const Vec3f& v1 = points[e_first]; - const Vec3f& v2 = points[e_second]; + const Vec3f& v1 = points_[e_first]; + const Vec3f& v2 = points_[e_second]; Matrix3f A; A << v1.transpose(), v2.transpose(), v3.transpose(); // this is A' in the original document @@ -142,12 +144,24 @@ Vec3f Convex<PolygonT>::computeCOM() const { Vec3f com(0, 0, 0); FCL_REAL vol = 0; + if (!(points.get())) { + std::cerr << "Error in `Convex::computeCOM`! Convex has no vertices." + << std::endl; + return com; + } + const std::vector<Vec3f>& points_ = *points; + if (!(polygons.get())) { + std::cerr << "Error in `Convex::computeCOM`! Convex has no polygons." + << std::endl; + return com; + } + const std::vector<PolygonT>& polygons_ = *polygons; for (unsigned int i = 0; i < num_polygons; ++i) { - const PolygonT& polygon = polygons[i]; + const PolygonT& polygon = polygons_[i]; // compute the center of the polygon Vec3f plane_center(0, 0, 0); for (size_type j = 0; j < polygon.size(); ++j) - plane_center += points[polygon[(index_type)j]]; + plane_center += points_[polygon[(index_type)j]]; plane_center /= polygon.size(); // compute the volume of tetrahedron making by neighboring two points, the @@ -157,11 +171,11 @@ Vec3f Convex<PolygonT>::computeCOM() const { index_type e_first = polygon[static_cast<index_type>(j)]; index_type e_second = polygon[static_cast<index_type>((j + 1) % polygon.size())]; - const Vec3f& v1 = points[e_first]; - const Vec3f& v2 = points[e_second]; + const Vec3f& v1 = points_[e_first]; + const Vec3f& v2 = points_[e_second]; FCL_REAL d_six_vol = (v1.cross(v2)).dot(v3); vol += d_six_vol; - com += (points[e_first] + points[e_second] + plane_center) * d_six_vol; + com += (points_[e_first] + points_[e_second] + plane_center) * d_six_vol; } } @@ -174,13 +188,25 @@ FCL_REAL Convex<PolygonT>::computeVolume() const { typedef typename PolygonT::index_type index_type; FCL_REAL vol = 0; + if (!(points.get())) { + std::cerr << "Error in `Convex::computeVolume`! Convex has no vertices." + << std::endl; + return vol; + } + const std::vector<Vec3f>& points_ = *points; + if (!(polygons.get())) { + std::cerr << "Error in `Convex::computeVolume`! Convex has no polygons." + << std::endl; + return vol; + } + const std::vector<PolygonT>& polygons_ = *polygons; for (unsigned int i = 0; i < num_polygons; ++i) { - const PolygonT& polygon = polygons[i]; + const PolygonT& polygon = polygons_[i]; // compute the center of the polygon Vec3f plane_center(0, 0, 0); for (size_type j = 0; j < polygon.size(); ++j) - plane_center += points[polygon[(index_type)j]]; + plane_center += points_[polygon[(index_type)j]]; plane_center /= polygon.size(); // compute the volume of tetrahedron making by neighboring two points, the @@ -190,8 +216,8 @@ FCL_REAL Convex<PolygonT>::computeVolume() const { index_type e_first = polygon[static_cast<index_type>(j)]; index_type e_second = polygon[static_cast<index_type>((j + 1) % polygon.size())]; - const Vec3f& v1 = points[e_first]; - const Vec3f& v2 = points[e_second]; + const Vec3f& v1 = points_[e_first]; + const Vec3f& v2 = points_[e_second]; FCL_REAL d_six_vol = (v1.cross(v2)).dot(v3); vol += d_six_vol; } @@ -202,16 +228,20 @@ FCL_REAL Convex<PolygonT>::computeVolume() const { template <typename PolygonT> void Convex<PolygonT>::fillNeighbors() { - if (neighbors) delete[] neighbors; - neighbors = new Neighbors[num_points]; + neighbors.reset(new std::vector<Neighbors>(num_points)); typedef typename PolygonT::size_type size_type; typedef typename PolygonT::index_type index_type; - std::vector<std::set<index_type> > nneighbors(num_points); + std::vector<std::set<index_type>> nneighbors(num_points); unsigned int c_nneighbors = 0; + if (!(polygons.get())) { + std::cerr << "Error in `Convex::fillNeighbors`! Convex has no polygons." + << std::endl; + } + const std::vector<PolygonT>& polygons_ = *polygons; for (unsigned int l = 0; l < num_polygons; ++l) { - const PolygonT& polygon = polygons[l]; + const PolygonT& polygon = polygons_[l]; const size_type n = polygon.size(); for (size_type j = 0; j < polygon.size(); ++j) { @@ -231,12 +261,12 @@ void Convex<PolygonT>::fillNeighbors() { } } - if (nneighbors_) delete[] nneighbors_; - nneighbors_ = new unsigned int[c_nneighbors]; + nneighbors_.reset(new std::vector<unsigned int>(c_nneighbors)); - unsigned int* p_nneighbors = nneighbors_; + unsigned int* p_nneighbors = nneighbors_->data(); + std::vector<Neighbors>& neighbors_ = *neighbors; for (unsigned int i = 0; i < num_points; ++i) { - Neighbors& n = neighbors[i]; + Neighbors& n = neighbors_[i]; if (nneighbors[i].size() >= (std::numeric_limits<unsigned char>::max)()) HPP_FCL_THROW_PRETTY("Too many neighbors.", std::logic_error); n.count_ = (unsigned char)nneighbors[i].size(); @@ -244,7 +274,7 @@ void Convex<PolygonT>::fillNeighbors() { p_nneighbors = std::copy(nneighbors[i].begin(), nneighbors[i].end(), p_nneighbors); } - assert(p_nneighbors == nneighbors_ + c_nneighbors); + assert(p_nneighbors == nneighbors_->data() + c_nneighbors); } } // namespace fcl diff --git a/include/hpp/fcl/shape/geometric_shapes.h b/include/hpp/fcl/shape/geometric_shapes.h index c469bd9725db6947578ffd84da707d8ab85ceb5a..cd1197fda8cee2c1c893fcf90156ce8c4341e8b2 100644 --- a/include/hpp/fcl/shape/geometric_shapes.h +++ b/include/hpp/fcl/shape/geometric_shapes.h @@ -43,6 +43,14 @@ #include <hpp/fcl/collision_object.h> #include <hpp/fcl/data_types.h> #include <string.h> +#include <vector> +#include <memory> + +#ifdef HPP_FCL_HAS_QHULL +namespace orgQhull { +class Qhull; +} +#endif namespace hpp { namespace fcl { @@ -592,37 +600,48 @@ class HPP_FCL_DLLAPI ConvexBase : public ShapeBase { /// Qhull. /// \note hpp-fcl must have been compiled with option \c HPP_FCL_HAS_QHULL set /// to \c ON. - static ConvexBase* convexHull(const Vec3f* points, unsigned int num_points, - bool keepTriangles, + static ConvexBase* convexHull(std::shared_ptr<std::vector<Vec3f>>& points, + unsigned int num_points, bool keepTriangles, const char* qhullCommand = NULL); - virtual ~ConvexBase(); - - /// @brief Clone (deep copy). - virtual ConvexBase* clone() const { - ConvexBase* copy_ptr = new ConvexBase(*this); - ConvexBase& copy = *copy_ptr; + // TODO(louis): put this method in private sometime in the future. + HPP_FCL_DEPRECATED static ConvexBase* convexHull( + const Vec3f* points, unsigned int num_points, bool keepTriangles, + const char* qhullCommand = NULL); - if (!copy.own_storage_) { - copy.points = new Vec3f[copy.num_points]; - std::copy(points, points + num_points, copy.points); - } - copy.own_storage_ = true; - copy.ShapeBase::operator=(*this); + virtual ~ConvexBase(); - return copy_ptr; - } + /// @brief Clone (deep copy). + /// This method is consistent with BVHModel `clone` method. + /// The copy constructor is called, which duplicates the data. + virtual ConvexBase* clone() const { return new ConvexBase(*this); } /// @brief Compute AABB void computeLocalAABB(); - /// @brief Get node type: a conex polytope + /// @brief Get node type: a convex polytope NODE_TYPE getNodeType() const { return GEOM_CONVEX; } /// @brief An array of the points of the polygon. - Vec3f* points; + std::shared_ptr<std::vector<Vec3f>> points; unsigned int num_points; + /// @brief An array of the normals of the polygon. + std::shared_ptr<std::vector<Vec3f>> normals; + /// @brief An array of the offsets to the normals of the polygon. + /// Note: there are as many offsets as normals. + std::shared_ptr<std::vector<double>> offsets; + unsigned int num_normals_and_offsets; + +#ifdef HPP_FCL_HAS_QHULL + public: + void buildDoubleDescription(); + + protected: + void buildDoubleDescriptionFromQHullResult(const orgQhull::Qhull& qh); +#endif + + public: struct HPP_FCL_DLLAPI Neighbors { unsigned char count_; unsigned int* n_; @@ -649,10 +668,11 @@ class HPP_FCL_DLLAPI ConvexBase : public ShapeBase { bool operator!=(const Neighbors& other) const { return !(*this == other); } }; + /// Neighbors of each vertex. /// It is an array of size num_points. For each vertex, it contains the number /// of neighbors and a list of indices to them. - Neighbors* neighbors; + std::shared_ptr<std::vector<Neighbors>> neighbors; /// @brief center of the convex polytope, this is used for collision: center /// is guaranteed in the internal of the polytope (as it is convex) @@ -663,11 +683,9 @@ class HPP_FCL_DLLAPI ConvexBase : public ShapeBase { /// Initialization is done with ConvexBase::initialize. ConvexBase() : ShapeBase(), - points(NULL), num_points(0), - neighbors(NULL), - nneighbors_(NULL), - own_storage_(false) {} + num_normals_and_offsets(0), + center(Vec3f::Zero()) {} /// @brief Initialize the points of the convex shape /// This also initializes the ConvexBase::center. @@ -675,22 +693,22 @@ class HPP_FCL_DLLAPI ConvexBase : public ShapeBase { /// \param ownStorage weither the ConvexBase owns the data. /// \param points_ list of 3D points /// /// \param num_points_ number of 3D points - void initialize(bool ownStorage, Vec3f* points_, unsigned int num_points_); + void initialize(std::shared_ptr<std::vector<Vec3f>> points_, + unsigned int num_points_); /// @brief Set the points of the convex shape. /// /// \param ownStorage weither the ConvexBase owns the data. /// \param points_ list of 3D points /// /// \param num_points_ number of 3D points - void set(bool ownStorage, Vec3f* points_, unsigned int num_points_); + void set(std::shared_ptr<std::vector<Vec3f>> points_, + unsigned int num_points_); /// @brief Copy constructor /// Only the list of neighbors is copied. ConvexBase(const ConvexBase& other); - unsigned int* nneighbors_; - - bool own_storage_; + std::shared_ptr<std::vector<unsigned int>> nneighbors_; private: void computeCenter(); @@ -703,12 +721,48 @@ class HPP_FCL_DLLAPI ConvexBase : public ShapeBase { if (num_points != other.num_points) return false; - for (unsigned int i = 0; i < num_points; ++i) { - if (points[i] != other.points[i]) return false; + if ((!(points.get()) && other.points.get()) || + (points.get() && !(other.points.get()))) + return false; + if (points.get() && other.points.get()) { + const std::vector<Vec3f>& points_ = *points; + const std::vector<Vec3f>& other_points_ = *(other.points); + for (unsigned int i = 0; i < num_points; ++i) { + if (points_[i] != (other_points_)[i]) return false; + } + } + + if ((!(neighbors.get()) && other.neighbors.get()) || + (neighbors.get() && !(other.neighbors.get()))) + return false; + if (neighbors.get() && other.neighbors.get()) { + const std::vector<Neighbors>& neighbors_ = *neighbors; + const std::vector<Neighbors>& other_neighbors_ = *(other.neighbors); + for (unsigned int i = 0; i < num_points; ++i) { + if (neighbors_[i] != other_neighbors_[i]) return false; + } + } + + if ((!(normals.get()) && other.normals.get()) || + (normals.get() && !(other.normals.get()))) + return false; + if (normals.get() && other.normals.get()) { + const std::vector<Vec3f>& normals_ = *normals; + const std::vector<Vec3f>& other_normals_ = *(other.normals); + for (unsigned int i = 0; i < num_normals_and_offsets; ++i) { + if (normals_[i] != other_normals_[i]) return false; + } } - for (unsigned int i = 0; i < num_points; ++i) { - if (neighbors[i] != other.neighbors[i]) return false; + if ((!(offsets.get()) && other.offsets.get()) || + (offsets.get() && !(other.offsets.get()))) + return false; + if (offsets.get() && other.offsets.get()) { + const std::vector<double>& offsets_ = *offsets; + const std::vector<double>& other_offsets_ = *(other.offsets); + for (unsigned int i = 0; i < num_normals_and_offsets; ++i) { + if (offsets_[i] != other_offsets_[i]) return false; + } } return center == other.center; diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index df7abe3d3fd75ca543d1ebc8218d7227cee1d2b5..8154917ee1f6dded0a6b0cac4fde902914746ed5 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -49,6 +49,7 @@ SET(${LIBRARY_NAME}_HEADERS broadphase/broadphase_collision_manager.hh broadphase/broadphase_callbacks.hh pickle.hh + utils/std-pair.hh ) SET(ENABLE_PYTHON_DOXYGEN_AUTODOC TRUE CACHE BOOL "Enable automatic documentation of Python bindings from Doxygen documentation") @@ -129,7 +130,6 @@ IF(ENABLE_DOXYGEN_AUTODOC) TARGET_COMPILE_DEFINITIONS(${LIBRARY_NAME} PRIVATE -DHPP_FCL_HAS_DOXYGEN_AUTODOC) ENDIF() -TARGET_LINK_BOOST_PYTHON(${LIBRARY_NAME} PUBLIC) TARGET_LINK_LIBRARIES(${LIBRARY_NAME} PUBLIC ${PROJECT_NAME} eigenpy::eigenpy diff --git a/python/broadphase/broadphase.cc b/python/broadphase/broadphase.cc index 49953bcb0af58699015295a16da5baf06884ade7..9e5ee524347c3ca8396fe27cab960981d5eb0cee 100644 --- a/python/broadphase/broadphase.cc +++ b/python/broadphase/broadphase.cc @@ -34,6 +34,7 @@ #include <hpp/fcl/fwd.hh> #include "../fcl.hh" +#include "../utils/std-pair.hh" #include "hpp/fcl/broadphase/broadphase_dynamic_AABB_tree.h" #include "hpp/fcl/broadphase/broadphase_dynamic_AABB_tree_array.h" @@ -66,25 +67,34 @@ void exposeBroadPhase() { DistanceCallBackBaseWrapper::expose(); // CollisionCallBackDefault - bp::class_<CollisionCallBackDefault, bp::bases<CollisionCallBackBase> >( + bp::class_<CollisionCallBackDefault, bp::bases<CollisionCallBackBase>>( "CollisionCallBackDefault", bp::no_init) .def(dv::init<CollisionCallBackDefault>()) .DEF_RW_CLASS_ATTRIB(CollisionCallBackDefault, data); // DistanceCallBackDefault - bp::class_<DistanceCallBackDefault, bp::bases<DistanceCallBackBase> >( + bp::class_<DistanceCallBackDefault, bp::bases<DistanceCallBackBase>>( "DistanceCallBackDefault", bp::no_init) .def(dv::init<DistanceCallBackDefault>()) .DEF_RW_CLASS_ATTRIB(DistanceCallBackDefault, data); // CollisionCallBackCollect - bp::class_<CollisionCallBackCollect, bp::bases<CollisionCallBackBase> >( + bp::class_<CollisionCallBackCollect, bp::bases<CollisionCallBackBase>>( "CollisionCallBackCollect", bp::no_init) .def(dv::init<CollisionCallBackCollect, const size_t>()) .DEF_CLASS_FUNC(CollisionCallBackCollect, numCollisionPairs) .DEF_CLASS_FUNC2(CollisionCallBackCollect, getCollisionPairs, bp::return_value_policy<bp::copy_const_reference>()) - .DEF_CLASS_FUNC(CollisionCallBackCollect, exist); + .def(dv::member_func( + "exist", (bool(CollisionCallBackCollect::*)( + const CollisionCallBackCollect::CollisionPair &) const) & + CollisionCallBackCollect::exist)) + .def(dv::member_func("exist", + (bool(CollisionCallBackCollect::*)( + CollisionObject *, CollisionObject *) const) & + CollisionCallBackCollect::exist)); + + StdPairConverter<CollisionCallBackCollect::CollisionPair>::registration(); bp::class_<CollisionData>("CollisionData", bp::no_init) .def(dv::init<CollisionData>()) @@ -118,9 +128,9 @@ void exposeBroadPhase() { detail::SpatialHash> HashTable; typedef SpatialHashingCollisionManager<HashTable> Derived; - bp::class_<Derived, bp::bases<BroadPhaseCollisionManager> >( + bp::class_<Derived, bp::bases<BroadPhaseCollisionManager>>( "SpatialHashingCollisionManager", bp::no_init) .def(dv::init<Derived, FCL_REAL, const Vec3f &, const Vec3f &, - bp::optional<unsigned int> >()); + bp::optional<unsigned int>>()); } } diff --git a/python/collision-geometries.cc b/python/collision-geometries.cc index 9801851f672fc35afa8450e2c094d8413847931a..45c1cf39a9e9cdf3cb58a1ac919a4c9772259a92 100644 --- a/python/collision-geometries.cc +++ b/python/collision-geometries.cc @@ -83,16 +83,19 @@ struct BVHModelBaseWrapper { static Vec3f& vertex(BVHModelBase& bvh, unsigned int i) { if (i >= bvh.num_vertices) throw std::out_of_range("index is out of range"); - return bvh.vertices[i]; + return (*(bvh.vertices))[i]; } static RefRowMatrixX3 vertices(BVHModelBase& bvh) { - return MapRowMatrixX3(bvh.vertices[0].data(), bvh.num_vertices, 3); + if (bvh.num_vertices > 0) + return MapRowMatrixX3(bvh.vertices->data()->data(), bvh.num_vertices, 3); + else + return MapRowMatrixX3(NULL, bvh.num_vertices, 3); } static Triangle tri_indices(const BVHModelBase& bvh, unsigned int i) { if (i >= bvh.num_tris) throw std::out_of_range("index is out of range"); - return bvh.tri_indices[i]; + return (*bvh.tri_indices)[i]; } }; @@ -101,7 +104,7 @@ void exposeBVHModel(const std::string& bvname) { typedef BVHModel<BV> BVH; const std::string type_name = "BVHModel" + bvname; - class_<BVH, bases<BVHModelBase>, shared_ptr<BVH> >( + class_<BVH, bases<BVHModelBase>, shared_ptr<BVH>>( type_name.c_str(), doxygen::class_doc<BVH>(), no_init) .def(dv::init<BVH>()) .def(dv::init<BVH, const BVH&>()) @@ -120,12 +123,12 @@ void exposeHeightField(const std::string& bvname) { typedef typename Geometry::Node Node; const std::string type_name = "HeightField" + bvname; - class_<Geometry, bases<Base>, shared_ptr<Geometry> >( + class_<Geometry, bases<Base>, shared_ptr<Geometry>>( type_name.c_str(), doxygen::class_doc<Geometry>(), no_init) - .def(dv::init<HeightField<BV> >()) + .def(dv::init<HeightField<BV>>()) .def(dv::init<HeightField<BV>, const HeightField<BV>&>()) .def(dv::init<HeightField<BV>, FCL_REAL, FCL_REAL, const MatrixXf&, - bp::optional<FCL_REAL> >()) + bp::optional<FCL_REAL>>()) .DEF_CLASS_FUNC(Geometry, getXDim) .DEF_CLASS_FUNC(Geometry, getYDim) @@ -157,23 +160,49 @@ struct ConvexBaseWrapper { typedef Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::RowMajor> RowMatrixX3; typedef Eigen::Map<RowMatrixX3> MapRowMatrixX3; typedef Eigen::Ref<RowMatrixX3> RefRowMatrixX3; + typedef Eigen::VectorXd VecOfDoubles; + typedef Eigen::Map<VecOfDoubles> MapVecOfDoubles; + typedef Eigen::Ref<VecOfDoubles> RefVecOfDoubles; static Vec3f& point(const ConvexBase& convex, unsigned int i) { if (i >= convex.num_points) throw std::out_of_range("index is out of range"); - return convex.points[i]; + return (*(convex.points))[i]; } static RefRowMatrixX3 points(const ConvexBase& convex) { - return MapRowMatrixX3(convex.points[0].data(), convex.num_points, 3); + return MapRowMatrixX3((*(convex.points))[0].data(), convex.num_points, 3); + } + + static Vec3f& normal(const ConvexBase& convex, unsigned int i) { + if (i >= convex.num_normals_and_offsets) + throw std::out_of_range("index is out of range"); + return (*(convex.normals))[i]; + } + + static RefRowMatrixX3 normals(const ConvexBase& convex) { + return MapRowMatrixX3((*(convex.normals))[0].data(), + convex.num_normals_and_offsets, 3); + } + + static double offset(const ConvexBase& convex, unsigned int i) { + if (i >= convex.num_normals_and_offsets) + throw std::out_of_range("index is out of range"); + return (*(convex.offsets))[i]; + } + + static RefVecOfDoubles offsets(const ConvexBase& convex) { + return MapVecOfDoubles(convex.offsets->data(), + convex.num_normals_and_offsets, 1); } static list neighbors(const ConvexBase& convex, unsigned int i) { if (i >= convex.num_points) throw std::out_of_range("index is out of range"); list n; - for (unsigned char j = 0; j < convex.neighbors[i].count(); ++j) - n.append(convex.neighbors[i][j]); + const std::vector<ConvexBase::Neighbors>& neighbors_ = *(convex.neighbors); + for (unsigned char j = 0; j < neighbors_[i].count(); ++j) + n.append(neighbors_[i][j]); return n; } @@ -191,16 +220,21 @@ struct ConvexWrapper { static PolygonT polygons(const Convex_t& convex, unsigned int i) { if (i >= convex.num_polygons) throw std::out_of_range("index is out of range"); - return convex.polygons[i]; + return (*convex.polygons)[i]; } static shared_ptr<Convex_t> constructor(const Vec3fs& _points, const Triangles& _tris) { - Vec3f* points = new Vec3f[_points.size()]; - for (std::size_t i = 0; i < _points.size(); ++i) points[i] = _points[i]; - Triangle* tris = new Triangle[_tris.size()]; - for (std::size_t i = 0; i < _tris.size(); ++i) tris[i] = _tris[i]; - return shared_ptr<Convex_t>(new Convex_t(true, points, + std::shared_ptr<std::vector<Vec3f>> points( + new std::vector<Vec3f>(_points.size())); + std::vector<Vec3f>& points_ = *points; + for (std::size_t i = 0; i < _points.size(); ++i) points_[i] = _points[i]; + + std::shared_ptr<std::vector<Triangle>> tris( + new std::vector<Triangle>(_tris.size())); + std::vector<Triangle>& tris_ = *tris; + for (std::size_t i = 0; i < _tris.size(); ++i) tris_[i] = _tris[i]; + return shared_ptr<Convex_t>(new Convex_t(points, (unsigned int)_points.size(), tris, (unsigned int)_tris.size())); } @@ -222,9 +256,9 @@ void exposeComputeMemoryFootprint() { defComputeMemoryFootprint<Halfspace>(); defComputeMemoryFootprint<TriangleP>(); - defComputeMemoryFootprint<BVHModel<OBB> >(); - defComputeMemoryFootprint<BVHModel<RSS> >(); - defComputeMemoryFootprint<BVHModel<OBBRSS> >(); + defComputeMemoryFootprint<BVHModel<OBB>>(); + defComputeMemoryFootprint<BVHModel<RSS>>(); + defComputeMemoryFootprint<BVHModel<OBBRSS>>(); } void exposeShapes() { @@ -233,7 +267,7 @@ void exposeShapes() { //.def ("getObjectType", &CollisionGeometry::getObjectType) ; - class_<Box, bases<ShapeBase>, shared_ptr<Box> >( + class_<Box, bases<ShapeBase>, shared_ptr<Box>>( "Box", doxygen::class_doc<ShapeBase>(), no_init) .def(dv::init<Box>()) .def(dv::init<Box, const Box&>()) @@ -244,7 +278,7 @@ void exposeShapes() { return_value_policy<manage_new_object>()) .def_pickle(PickleObject<Box>()); - class_<Capsule, bases<ShapeBase>, shared_ptr<Capsule> >( + class_<Capsule, bases<ShapeBase>, shared_ptr<Capsule>>( "Capsule", doxygen::class_doc<Capsule>(), no_init) .def(dv::init<Capsule>()) .def(dv::init<Capsule, FCL_REAL, FCL_REAL>()) @@ -255,7 +289,7 @@ void exposeShapes() { return_value_policy<manage_new_object>()) .def_pickle(PickleObject<Capsule>()); - class_<Cone, bases<ShapeBase>, shared_ptr<Cone> >( + class_<Cone, bases<ShapeBase>, shared_ptr<Cone>>( "Cone", doxygen::class_doc<Cone>(), no_init) .def(dv::init<Cone>()) .def(dv::init<Cone, FCL_REAL, FCL_REAL>()) @@ -270,39 +304,50 @@ void exposeShapes() { "ConvexBase", doxygen::class_doc<ConvexBase>(), no_init) .DEF_RO_CLASS_ATTRIB(ConvexBase, center) .DEF_RO_CLASS_ATTRIB(ConvexBase, num_points) + .DEF_RO_CLASS_ATTRIB(ConvexBase, num_normals_and_offsets) .def("point", &ConvexBaseWrapper::point, bp::args("self", "index"), "Retrieve the point given by its index.", bp::return_internal_reference<>()) .def("points", &ConvexBaseWrapper::point, bp::args("self", "index"), "Retrieve the point given by its index.", ::hpp::fcl::python::deprecated_member< - bp::return_internal_reference<> >()) + bp::return_internal_reference<>>()) .def("points", &ConvexBaseWrapper::points, bp::args("self"), "Retrieve all the points.", bp::with_custodian_and_ward_postcall<0, 1>()) // .add_property ("points", // bp::make_function(&ConvexBaseWrapper::points,bp::with_custodian_and_ward_postcall<0,1>()), // "Points of the convex.") + .def("normal", &ConvexBaseWrapper::normal, bp::args("self", "index"), + "Retrieve the normal given by its index.", + bp::return_internal_reference<>()) + .def("normals", &ConvexBaseWrapper::normals, bp::args("self"), + "Retrieve all the normals.", + bp::with_custodian_and_ward_postcall<0, 1>()) + .def("offset", &ConvexBaseWrapper::offset, bp::args("self", "index"), + "Retrieve the offset given by its index.") + .def("offsets", &ConvexBaseWrapper::offsets, bp::args("self"), + "Retrieve all the offsets.", + bp::with_custodian_and_ward_postcall<0, 1>()) .def("neighbors", &ConvexBaseWrapper::neighbors) .def("convexHull", &ConvexBaseWrapper::convexHull, - doxygen::member_func_doc(&ConvexBase::convexHull), + // doxygen::member_func_doc(&ConvexBase::convexHull), return_value_policy<manage_new_object>()) .staticmethod("convexHull") .def("clone", &ConvexBase::clone, doxygen::member_func_doc(&ConvexBase::clone), return_value_policy<manage_new_object>()); - class_<Convex<Triangle>, bases<ConvexBase>, shared_ptr<Convex<Triangle> >, - noncopyable>("Convex", doxygen::class_doc<Convex<Triangle> >(), - no_init) + class_<Convex<Triangle>, bases<ConvexBase>, shared_ptr<Convex<Triangle>>, + noncopyable>("Convex", doxygen::class_doc<Convex<Triangle>>(), no_init) .def("__init__", make_constructor(&ConvexWrapper<Triangle>::constructor)) - .def(dv::init<Convex<Triangle> >()) + .def(dv::init<Convex<Triangle>>()) .def(dv::init<Convex<Triangle>, const Convex<Triangle>&>()) .DEF_RO_CLASS_ATTRIB(Convex<Triangle>, num_polygons) .def("polygons", &ConvexWrapper<Triangle>::polygons) - .def_pickle(PickleObject<Convex<Triangle> >()); + .def_pickle(PickleObject<Convex<Triangle>>()); - class_<Cylinder, bases<ShapeBase>, shared_ptr<Cylinder> >( + class_<Cylinder, bases<ShapeBase>, shared_ptr<Cylinder>>( "Cylinder", doxygen::class_doc<Cylinder>(), no_init) .def(dv::init<Cylinder>()) .def(dv::init<Cylinder, FCL_REAL, FCL_REAL>()) @@ -314,7 +359,7 @@ void exposeShapes() { return_value_policy<manage_new_object>()) .def_pickle(PickleObject<Cylinder>()); - class_<Halfspace, bases<ShapeBase>, shared_ptr<Halfspace> >( + class_<Halfspace, bases<ShapeBase>, shared_ptr<Halfspace>>( "Halfspace", doxygen::class_doc<Halfspace>(), no_init) .def(dv::init<Halfspace, const Vec3f&, FCL_REAL>()) .def(dv::init<Halfspace, const Halfspace&>()) @@ -327,7 +372,7 @@ void exposeShapes() { return_value_policy<manage_new_object>()) .def_pickle(PickleObject<Halfspace>()); - class_<Plane, bases<ShapeBase>, shared_ptr<Plane> >( + class_<Plane, bases<ShapeBase>, shared_ptr<Plane>>( "Plane", doxygen::class_doc<Plane>(), no_init) .def(dv::init<Plane, const Vec3f&, FCL_REAL>()) .def(dv::init<Plane, const Plane&>()) @@ -339,7 +384,7 @@ void exposeShapes() { return_value_policy<manage_new_object>()) .def_pickle(PickleObject<Plane>()); - class_<Sphere, bases<ShapeBase>, shared_ptr<Sphere> >( + class_<Sphere, bases<ShapeBase>, shared_ptr<Sphere>>( "Sphere", doxygen::class_doc<Sphere>(), no_init) .def(dv::init<Sphere>()) .def(dv::init<Sphere, const Sphere&>()) @@ -349,7 +394,7 @@ void exposeShapes() { return_value_policy<manage_new_object>()) .def_pickle(PickleObject<Sphere>()); - class_<Ellipsoid, bases<ShapeBase>, shared_ptr<Ellipsoid> >( + class_<Ellipsoid, bases<ShapeBase>, shared_ptr<Ellipsoid>>( "Ellipsoid", doxygen::class_doc<Ellipsoid>(), no_init) .def(dv::init<Ellipsoid>()) .def(dv::init<Ellipsoid, FCL_REAL, FCL_REAL, FCL_REAL>()) @@ -361,7 +406,7 @@ void exposeShapes() { return_value_policy<manage_new_object>()) .def_pickle(PickleObject<Ellipsoid>()); - class_<TriangleP, bases<ShapeBase>, shared_ptr<TriangleP> >( + class_<TriangleP, bases<ShapeBase>, shared_ptr<TriangleP>>( "TriangleP", doxygen::class_doc<TriangleP>(), no_init) .def(dv::init<TriangleP>()) .def(dv::init<TriangleP, const Vec3f&, const Vec3f&, const Vec3f&>()) @@ -582,7 +627,7 @@ void exposeCollisionGeometries() { .def("vertices", &BVHModelBaseWrapper::vertex, bp::args("self", "index"), "Retrieve the vertex given by its index.", ::hpp::fcl::python::deprecated_member< - bp::return_internal_reference<> >()) + bp::return_internal_reference<>>()) .def("vertices", &BVHModelBaseWrapper::vertices, bp::args("self"), "Retrieve all the vertices.", bp::with_custodian_and_ward_postcall<0, 1>()) @@ -634,11 +679,11 @@ void exposeCollisionObject() { if (!eigenpy::register_symbolic_link_to_registered_type<CollisionObject>()) { class_<CollisionObject, CollisionObjectPtr_t>("CollisionObject", no_init) .def(dv::init<CollisionObject, const CollisionGeometryPtr_t&, - bp::optional<bool> >()) + bp::optional<bool>>()) .def(dv::init<CollisionObject, const CollisionGeometryPtr_t&, - const Transform3f&, bp::optional<bool> >()) + const Transform3f&, bp::optional<bool>>()) .def(dv::init<CollisionObject, const CollisionGeometryPtr_t&, - const Matrix3f&, const Vec3f&, bp::optional<bool> >()) + const Matrix3f&, const Vec3f&, bp::optional<bool>>()) .DEF_CLASS_FUNC(CollisionObject, getObjectType) .DEF_CLASS_FUNC(CollisionObject, getNodeType) diff --git a/python/collision.cc b/python/collision.cc index eebd892f9551efdfc9f37d0da1cb035fab7484c2..ce84a718001308bea9df01e10a81462a2f8a3c4b 100644 --- a/python/collision.cc +++ b/python/collision.cc @@ -59,6 +59,15 @@ const CollisionGeometry* geto(const Contact& c) { return index == 1 ? c.o1 : c.o2; } +struct ContactWrapper { + static Vec3f getNearestPoint1(const Contact& contact) { + return contact.nearest_points[0]; + } + static Vec3f getNearestPoint2(const Contact& contact) { + return contact.nearest_points[1]; + } +}; + void exposeCollisionAPI() { if (!eigenpy::register_symbolic_link_to_registered_type< CollisionRequestFlag>()) { @@ -152,9 +161,14 @@ void exposeCollisionAPI() { make_function(&geto<2>, return_value_policy<reference_existing_object>()), doxygen::class_attrib_doc<Contact>("o2")) + .def("getNearestPoint1", &ContactWrapper::getNearestPoint1, + doxygen::class_attrib_doc<Contact>("nearest_points")) + .def("getNearestPoint2", &ContactWrapper::getNearestPoint2, + doxygen::class_attrib_doc<Contact>("nearest_points")) .DEF_RW_CLASS_ATTRIB(Contact, b1) .DEF_RW_CLASS_ATTRIB(Contact, b2) .DEF_RW_CLASS_ATTRIB(Contact, normal) + .DEF_RW_CLASS_ATTRIB(Contact, nearest_points) .DEF_RW_CLASS_ATTRIB(Contact, pos) .DEF_RW_CLASS_ATTRIB(Contact, penetration_depth) .def(self == self) diff --git a/python/distance.cc b/python/distance.cc index d69427f23aac510b883b7e9a89a1a484e2892bd1..8c8090bb68f1d8aeb36d9a9c14ca1c520dd1f4f9 100644 --- a/python/distance.cc +++ b/python/distance.cc @@ -88,6 +88,7 @@ void exposeDistanceAPI() { doxygen::class_attrib_doc<DistanceResult>("nearest_points")) .def("getNearestPoint2", &DistanceRequestWrapper::getNearestPoint2, doxygen::class_attrib_doc<DistanceResult>("nearest_points")) + .DEF_RO_CLASS_ATTRIB(DistanceResult, nearest_points) .DEF_RO_CLASS_ATTRIB(DistanceResult, o1) .DEF_RO_CLASS_ATTRIB(DistanceResult, o2) .DEF_RW_CLASS_ATTRIB(DistanceResult, b1) diff --git a/python/gjk.cc b/python/gjk.cc index c9e9f856f33015e13f54689669d892c90036b337..0673a96847ed4911e1c7ec738f3e80975727517f 100644 --- a/python/gjk.cc +++ b/python/gjk.cc @@ -87,10 +87,19 @@ void exposeGJK() { if (!eigenpy::register_symbolic_link_to_registered_type<GJKVariant>()) { enum_<GJKVariant>("GJKVariant") .value("DefaultGJK", GJKVariant::DefaultGJK) + .value("PolyakAcceleration", GJKVariant::PolyakAcceleration) .value("NesterovAcceleration", GJKVariant::NesterovAcceleration) .export_values(); } + if (!eigenpy::register_symbolic_link_to_registered_type<GJKInitialGuess>()) { + enum_<GJKInitialGuess>("GJKInitialGuess") + .value("DefaultGuess", GJKInitialGuess::DefaultGuess) + .value("CachedGuess", GJKInitialGuess::CachedGuess) + .value("BoundingVolumeGuess", GJKInitialGuess::BoundingVolumeGuess) + .export_values(); + } + if (!eigenpy::register_symbolic_link_to_registered_type< GJKConvergenceCriterion>()) { enum_<GJKConvergenceCriterion>("GJKConvergenceCriterion") @@ -117,12 +126,13 @@ void exposeGJK() { .DEF_RW_CLASS_ATTRIB(GJK, gjk_variant) .DEF_RW_CLASS_ATTRIB(GJK, convergence_criterion) .DEF_RW_CLASS_ATTRIB(GJK, convergence_criterion_type) + .DEF_RW_CLASS_ATTRIB(GJK, iterations) + .DEF_RW_CLASS_ATTRIB(GJK, tolerance) .DEF_CLASS_FUNC(GJK, evaluate) .DEF_CLASS_FUNC(GJK, hasClosestPoints) .DEF_CLASS_FUNC(GJK, hasPenetrationInformation) .DEF_CLASS_FUNC(GJK, getClosestPoints) .DEF_CLASS_FUNC(GJK, setDistanceEarlyBreak) - .DEF_CLASS_FUNC(GJK, getGuessFromSimplex) - .DEF_CLASS_FUNC(GJK, getIterations); + .DEF_CLASS_FUNC(GJK, getGuessFromSimplex); } } diff --git a/python/math.cc b/python/math.cc index 9e92cb12ba4330e61cafba181636ff454019b5fe..2793f27caaed1d0c25f8b7672a94dd48b79c032a 100644 --- a/python/math.cc +++ b/python/math.cc @@ -77,10 +77,9 @@ void exposeMaths() { .def(dv::init<Transform3f>()) .def(dv::init<Transform3f, const Matrix3f::MatrixBase&, const Vec3f::MatrixBase&>()) - .def(dv::init<Transform3f, const Quaternion3f&, - const Vec3f::MatrixBase&>()) + .def(dv::init<Transform3f, const Quatf&, const Vec3f::MatrixBase&>()) .def(dv::init<Transform3f, const Matrix3f&>()) - .def(dv::init<Transform3f, const Quaternion3f&>()) + .def(dv::init<Transform3f, const Quatf&>()) .def(dv::init<Transform3f, const Vec3f&>()) .def(dv::init<Transform3f, const Transform3f&>()) @@ -102,7 +101,7 @@ void exposeMaths() { &Transform3f::setTransform<Matrix3f, Vec3f>)) .def(dv::member_func( "setTransform", - static_cast<void (Transform3f::*)(const Quaternion3f&, const Vec3f&)>( + static_cast<void (Transform3f::*)(const Quatf&, const Vec3f&)>( &Transform3f::setTransform))) .def(dv::member_func("setIdentity", &Transform3f::setIdentity)) .def(dv::member_func("Identity", &Transform3f::Identity)) diff --git a/python/octree.cc b/python/octree.cc index e0721af11872c0e6bdc983b7170847ea4862cca0..76ebc8b7f27ba8850ce16150e88803d336498469 100644 --- a/python/octree.cc +++ b/python/octree.cc @@ -1,12 +1,32 @@ #include "fcl.hh" +#include <eigenpy/std-vector.hpp> + #include <hpp/fcl/fwd.hh> #include <hpp/fcl/octree.h> #ifdef HPP_FCL_HAS_DOXYGEN_AUTODOC #include "doxygen_autodoc/functions.h" +#include "doxygen_autodoc/hpp/fcl/octree.h" +#endif + +bp::object toPyBytes(std::vector<uint8_t>& bytes) { +#if PY_MAJOR_VERSION == 2 + PyObject* py_buf = + PyBuffer_FromMemory((char*)bytes.data(), Py_ssize_t(bytes.size())); + return bp::object(bp::handle<>(py_buf)); +#else + PyObject* py_buf = PyMemoryView_FromMemory( + (char*)bytes.data(), Py_ssize_t(bytes.size()), PyBUF_READ); + return bp::object(bp::handle<>(PyBytes_FromObject(py_buf))); #endif +} + +bp::object tobytes(const hpp::fcl::OcTree& self) { + std::vector<uint8_t> bytes = self.tobytes(); + return toPyBytes(bytes); +} void exposeOctree() { using namespace hpp::fcl; @@ -16,7 +36,11 @@ void exposeOctree() { bp::class_<OcTree, bp::bases<CollisionGeometry>, shared_ptr<OcTree> >( "OcTree", doxygen::class_doc<OcTree>(), bp::no_init) .def(dv::init<OcTree, FCL_REAL>()) + .def("clone", &OcTree::clone, doxygen::member_func_doc(&OcTree::clone), + bp::return_value_policy<bp::manage_new_object>()) .def(dv::member_func("getTreeDepth", &OcTree::getTreeDepth)) + .def(dv::member_func("size", &OcTree::size)) + .def(dv::member_func("getResolution", &OcTree::getResolution)) .def(dv::member_func("getOccupancyThres", &OcTree::getOccupancyThres)) .def(dv::member_func("getFreeThres", &OcTree::getFreeThres)) .def(dv::member_func("getDefaultOccupancy", &OcTree::getDefaultOccupancy)) @@ -24,7 +48,12 @@ void exposeOctree() { &OcTree::setCellDefaultOccupancy)) .def(dv::member_func("setOccupancyThres", &OcTree::setOccupancyThres)) .def(dv::member_func("setFreeThres", &OcTree::setFreeThres)) - .def(dv::member_func("getRootBV", &OcTree::getRootBV)); + .def(dv::member_func("getRootBV", &OcTree::getRootBV)) + .def(dv::member_func("toBoxes", &OcTree::toBoxes)) + .def("tobytes", tobytes, doxygen::member_func_doc(&OcTree::tobytes)); doxygen::def("makeOctree", &makeOctree); + eigenpy::enableEigenPySpecific<Vec6f>(); + eigenpy::StdVectorPythonVisitor<std::vector<Vec6f>, true>::expose( + "StdVec_Vec6"); } diff --git a/python/utils/std-pair.hh b/python/utils/std-pair.hh new file mode 100644 index 0000000000000000000000000000000000000000..80946720122c5903d8462c8fc9cb5df269e992d4 --- /dev/null +++ b/python/utils/std-pair.hh @@ -0,0 +1,65 @@ +// +// Copyright (c) 2023 INRIA +// + +#ifndef HPP_FCL_PYTHON_UTILS_STD_PAIR_H +#define HPP_FCL_PYTHON_UTILS_STD_PAIR_H + +#include <boost/python.hpp> +#include <utility> + +template <typename pair_type> +struct StdPairConverter { + typedef typename pair_type::first_type T1; + typedef typename pair_type::second_type T2; + + static PyObject* convert(const pair_type& pair) { + return boost::python::incref( + boost::python::make_tuple(pair.first, pair.second).ptr()); + } + + static void* convertible(PyObject* obj) { + if (!PyTuple_CheckExact(obj)) return 0; + if (PyTuple_Size(obj) != 2) return 0; + { + boost::python::tuple tuple(boost::python::borrowed(obj)); + boost::python::extract<T1> elt1(tuple[0]); + if (!elt1.check()) return 0; + boost::python::extract<T2> elt2(tuple[1]); + if (!elt2.check()) return 0; + } + return obj; + } + + static void construct( + PyObject* obj, + boost::python::converter::rvalue_from_python_stage1_data* memory) { + boost::python::tuple tuple(boost::python::borrowed(obj)); + void* storage = + reinterpret_cast< + boost::python::converter::rvalue_from_python_storage<pair_type>*>( + reinterpret_cast<void*>(memory)) + ->storage.bytes; + new (storage) pair_type(boost::python::extract<T1>(tuple[0]), + boost::python::extract<T2>(tuple[1])); + memory->convertible = storage; + } + + static PyTypeObject const* get_pytype() { + PyTypeObject const* py_type = &PyTuple_Type; + return py_type; + } + + static void registration() { + boost::python::converter::registry::push_back( + reinterpret_cast<void* (*)(_object*)>(&convertible), &construct, + boost::python::type_id<pair_type>() +#ifndef BOOST_PYTHON_NO_PY_SIGNATURES + , + get_pytype +#endif + ); + } +}; + +#endif // ifndef HPP_FCL_PYTHON_UTILS_STD_PAIR_H diff --git a/python/version.cc b/python/version.cc index f83798629e90fa12b77f9db82f7091ca58420d94..4bc0eea9337a880f1653e78372991ee355de950e 100644 --- a/python/version.cc +++ b/python/version.cc @@ -1,5 +1,5 @@ // -// Copyright (c) 2019 CNRS INRIA +// Copyright (c) 2019-2023 CNRS INRIA // #include "hpp/fcl/config.hh" @@ -25,6 +25,18 @@ void exposeVersion() { bp::scope().attr("HPP_FCL_MINOR_VERSION") = HPP_FCL_MINOR_VERSION; bp::scope().attr("HPP_FCL_PATCH_VERSION") = HPP_FCL_PATCH_VERSION; +#if HPP_FCL_HAS_QHULL + bp::scope().attr("WITH_QHULL") = true; +#else + bp::scope().attr("WITH_QHULL") = false; +#endif + +#if HPP_FCL_HAS_OCTOMAP + bp::scope().attr("WITH_OCTOMAP") = true; +#else + bp::scope().attr("WITH_OCTOMAP") = false; +#endif + bp::def("checkVersionAtLeast", &checkVersionAtLeast, bp::args("major", "minor", "patch"), "Checks if the current version of hpp-fcl is at least" diff --git a/src/BV/OBB.cpp b/src/BV/OBB.cpp index 10d5089538bc0fce4ec03b59e7586ed53ffff426..69b740034fac0ab19536f3234b66e90051ad0ba7 100644 --- a/src/BV/OBB.cpp +++ b/src/BV/OBB.cpp @@ -116,10 +116,10 @@ inline OBB merge_largedist(const OBB& b1, const OBB& b2) { inline OBB merge_smalldist(const OBB& b1, const OBB& b2) { OBB b; b.To = (b1.To + b2.To) * 0.5; - Quaternion3f q0(b1.axes), q1(b2.axes); + Quatf q0(b1.axes), q1(b2.axes); if (q0.dot(q1) < 0) q1.coeffs() *= -1; - Quaternion3f q((q0.coeffs() + q1.coeffs()).normalized()); + Quatf q((q0.coeffs() + q1.coeffs()).normalized()); b.axes = q.toRotationMatrix(); Vec3f vertex[8], diff; diff --git a/src/BVH/BVH_model.cpp b/src/BVH/BVH_model.cpp index 7aff1b6b329325e77810632a4c11185f7805c330..3327fd071dae6df3aae092250ea9a19cdd4444d7 100644 --- a/src/BVH/BVH_model.cpp +++ b/src/BVH/BVH_model.cpp @@ -36,6 +36,7 @@ /** \author Jia Pan */ +#include "hpp/fcl/BV/BV_node.h" #include <hpp/fcl/BVH/BVH_model.h> #include <iostream> @@ -51,10 +52,7 @@ namespace hpp { namespace fcl { BVHModelBase::BVHModelBase() - : vertices(NULL), - tri_indices(NULL), - prev_vertices(NULL), - num_tris(0), + : num_tris(0), num_vertices(0), build_state(BVH_BUILD_STATE_EMPTY), num_tris_allocated(0), @@ -68,24 +66,20 @@ BVHModelBase::BVHModelBase(const BVHModelBase& other) build_state(other.build_state), num_tris_allocated(other.num_tris), num_vertices_allocated(other.num_vertices) { - if (other.vertices) { - vertices = new Vec3f[num_vertices]; - std::copy(other.vertices, other.vertices + num_vertices, vertices); + if (other.vertices.get() && other.vertices->size() > 0) { + vertices.reset(new std::vector<Vec3f>(*(other.vertices))); } else - vertices = nullptr; + vertices.reset(); - if (other.tri_indices) { - tri_indices = new Triangle[num_tris]; - std::copy(other.tri_indices, other.tri_indices + num_tris, tri_indices); + if (other.tri_indices.get() && other.tri_indices->size() > 0) { + tri_indices.reset(new std::vector<Triangle>(*(other.tri_indices))); } else - tri_indices = nullptr; + tri_indices.reset(); - if (other.prev_vertices) { - prev_vertices = new Vec3f[num_vertices]; - std::copy(other.prev_vertices, other.prev_vertices + num_vertices, - prev_vertices); + if (other.prev_vertices.get() && other.prev_vertices->size() > 0) { + prev_vertices.reset(new std::vector<Vec3f>(*(other.prev_vertices))); } else - prev_vertices = nullptr; + prev_vertices.reset(); } bool BVHModelBase::isEqual(const CollisionGeometry& _other) const { @@ -98,15 +92,34 @@ bool BVHModelBase::isEqual(const CollisionGeometry& _other) const { if (!result) return false; - for (size_t k = 0; k < static_cast<size_t>(num_tris); ++k) - if (tri_indices[k] != other.tri_indices[k]) return false; + if ((!(tri_indices.get()) && other.tri_indices.get()) || + (tri_indices.get() && !(other.tri_indices.get()))) + return false; + if (tri_indices.get() && other.tri_indices.get()) { + const std::vector<Triangle>& tri_indices_ = *(tri_indices); + const std::vector<Triangle>& other_tri_indices_ = *(other.tri_indices); + for (size_t k = 0; k < static_cast<size_t>(num_tris); ++k) + if (tri_indices_[k] != other_tri_indices_[k]) return false; + } - for (size_t k = 0; k < static_cast<size_t>(num_vertices); ++k) - if (vertices[k] != other.vertices[k]) return false; + if ((!(vertices.get()) && other.vertices.get()) || + (vertices.get() && !(other.vertices.get()))) + return false; + if (vertices.get() && other.vertices.get()) { + const std::vector<Vec3f>& vertices_ = *(vertices); + const std::vector<Vec3f>& other_vertices_ = *(other.vertices); + for (size_t k = 0; k < static_cast<size_t>(num_vertices); ++k) + if (vertices_[k] != other_vertices_[k]) return false; + } - if (prev_vertices != nullptr && other.prev_vertices != nullptr) { + if ((!(prev_vertices.get()) && other.prev_vertices.get()) || + (prev_vertices.get() && !(other.prev_vertices.get()))) + return false; + if (prev_vertices.get() && other.prev_vertices.get()) { + const std::vector<Vec3f>& prev_vertices_ = *(prev_vertices); + const std::vector<Vec3f>& other_prev_vertices_ = *(other.prev_vertices); for (size_t k = 0; k < static_cast<size_t>(num_vertices); ++k) { - if (prev_vertices[k] != other.prev_vertices[k]) return false; + if (prev_vertices_[k] != other_prev_vertices_[k]) return false; } } @@ -114,18 +127,28 @@ bool BVHModelBase::isEqual(const CollisionGeometry& _other) const { } void BVHModelBase::buildConvexRepresentation(bool share_memory) { + if (!(vertices.get())) { + std::cerr << "BVH Error in `buildConvexRepresentation`! The BVHModel has " + "no vertices." + << std::endl; + return; + } + if (!(tri_indices.get())) { + std::cerr << "BVH Error in `buildConvexRepresentation`! The BVHModel has " + "no triangles." + << std::endl; + return; + } + if (!convex) { - Vec3f* points = vertices; - Triangle* polygons = tri_indices; + std::shared_ptr<std::vector<Vec3f>> points = vertices; + std::shared_ptr<std::vector<Triangle>> polygons = tri_indices; if (!share_memory) { - points = new Vec3f[num_vertices]; - std::copy(vertices, vertices + num_vertices, points); - - polygons = new Triangle[num_tris]; - std::copy(tri_indices, tri_indices + num_tris, polygons); + points.reset(new std::vector<Vec3f>(*(vertices))); + polygons.reset(new std::vector<Triangle>(*(tri_indices))); } - convex.reset(new Convex<Triangle>(!share_memory, points, num_vertices, - polygons, num_tris)); + convex.reset( + new Convex<Triangle>(points, num_vertices, polygons, num_tris)); } } @@ -141,41 +164,26 @@ BVHModel<BV>::BVHModel(const BVHModel<BV>& other) : BVHModelBase(other), bv_splitter(other.bv_splitter), bv_fitter(other.bv_fitter) { - if (other.primitive_indices) { - unsigned int num_primitives = 0; - switch (other.getModelType()) { - case BVH_MODEL_TRIANGLES: - num_primitives = num_tris; - break; - case BVH_MODEL_POINTCLOUD: - num_primitives = num_vertices; - break; - default:; - } - - primitive_indices = new unsigned int[num_primitives]; - std::copy(other.primitive_indices, other.primitive_indices + num_primitives, - primitive_indices); + if (other.primitive_indices.get()) { + primitive_indices.reset( + new std::vector<unsigned int>(*(other.primitive_indices))); } else - primitive_indices = nullptr; + primitive_indices.reset(); num_bvs = num_bvs_allocated = other.num_bvs; - if (other.bvs) { - bvs = new BVNode<BV>[num_bvs]; - std::copy(other.bvs, other.bvs + num_bvs, bvs); + if (other.bvs.get()) { + bvs.reset(new bv_node_vector_t(*(other.bvs))); } else - bvs = nullptr; + bvs.reset(); } int BVHModelBase::beginModel(unsigned int num_tris_, unsigned int num_vertices_) { if (build_state != BVH_BUILD_STATE_EMPTY) { - delete[] vertices; - vertices = nullptr; - delete[] tri_indices; - tri_indices = nullptr; - delete[] prev_vertices; - prev_vertices = nullptr; + vertices.reset(); + tri_indices.reset(); + tri_indices.reset(); + prev_vertices.reset(); num_vertices_allocated = num_vertices = num_tris_allocated = num_tris = 0; deleteBVs(); @@ -187,26 +195,33 @@ int BVHModelBase::beginModel(unsigned int num_tris_, num_vertices_allocated = num_vertices_; num_tris_allocated = num_tris_; - tri_indices = new Triangle[num_tris_allocated]; - - if (!tri_indices) { - std::cerr << "BVH Error! Out of memory for tri_indices array on " - "BeginModel() call!" - << std::endl; - return BVH_ERR_MODEL_OUT_OF_MEMORY; - } + if (num_tris_allocated > 0) { + tri_indices.reset(new std::vector<Triangle>(num_tris_allocated)); + if (!(tri_indices.get())) { + std::cerr << "BVH Error! Out of memory for tri_indices array on " + "BeginModel() call!" + << std::endl; + return BVH_ERR_MODEL_OUT_OF_MEMORY; + } + } else + tri_indices.reset(); - vertices = new Vec3f[num_vertices_allocated]; - if (!vertices) { - std::cerr - << "BVH Error! Out of memory for vertices array on BeginModel() call!" - << std::endl; - return BVH_ERR_MODEL_OUT_OF_MEMORY; - } + if (num_vertices_allocated > 0) { + vertices.reset(new std::vector<Vec3f>(num_vertices_allocated)); + if (!(vertices.get())) { + std::cerr + << "BVH Error! Out of memory for vertices array on BeginModel() call!" + << std::endl; + return BVH_ERR_MODEL_OUT_OF_MEMORY; + } + } else { + vertices.reset(); + prev_vertices.reset(); + }; if (build_state != BVH_BUILD_STATE_EMPTY) { std::cerr - << "BVH Warning! Call beginModel() on a BVHModel that is not empty. " + << "BVH Warning! Calling beginModel() on a BVHModel that is not empty. " "This model was cleared and previous triangles/vertices were lost." << std::endl; build_state = BVH_BUILD_STATE_EMPTY; @@ -228,21 +243,23 @@ int BVHModelBase::addVertex(const Vec3f& p) { } if (num_vertices >= num_vertices_allocated) { - Vec3f* temp = new Vec3f[num_vertices_allocated * 2]; - if (!temp) { + std::shared_ptr<std::vector<Vec3f>> temp( + new std::vector<Vec3f>(num_vertices_allocated * 2)); + if (!(temp.get())) { std::cerr << "BVH Error! Out of memory for vertices array on addVertex() call!" << std::endl; return BVH_ERR_MODEL_OUT_OF_MEMORY; } - std::copy(vertices, vertices + num_vertices, temp); - delete[] vertices; + for (size_t i = 0; i < num_vertices; ++i) { + (*temp)[i] = (*vertices)[i]; + } vertices = temp; num_vertices_allocated *= 2; } - vertices[num_vertices] = p; + (*vertices)[num_vertices] = p; num_vertices += 1; return BVH_OK; @@ -260,25 +277,29 @@ int BVHModelBase::addTriangles(const Matrixx3i& triangles) { const unsigned int num_tris_to_add = (unsigned int)triangles.rows(); if (num_tris + num_tris_to_add > num_tris_allocated) { - Triangle* temp = new Triangle[num_tris_allocated * 2 + num_tris_to_add]; - if (!temp) { + std::shared_ptr<std::vector<Triangle>> temp( + new std::vector<Triangle>(num_tris_allocated * 2 + num_tris_to_add)); + if (!(temp.get())) { std::cerr << "BVH Error! Out of memory for tri_indices array on " "addSubModel() call!" << std::endl; return BVH_ERR_MODEL_OUT_OF_MEMORY; } - std::copy(tri_indices, tri_indices + num_tris, temp); - delete[] tri_indices; + for (size_t i = 0; i < num_tris; ++i) { + (*temp)[i] = (*tri_indices)[i]; + } tri_indices = temp; num_tris_allocated = num_tris_allocated * 2 + num_tris_to_add; } + std::vector<Triangle>& tri_indices_ = *tri_indices; for (Eigen::DenseIndex i = 0; i < triangles.rows(); ++i) { const Matrixx3i::ConstRowXpr triangle = triangles.row(i); - tri_indices[num_tris++].set(static_cast<Triangle::index_type>(triangle[0]), - static_cast<Triangle::index_type>(triangle[1]), - static_cast<Triangle::index_type>(triangle[2])); + tri_indices_[num_tris++].set( + static_cast<Triangle::index_type>(triangle[0]), + static_cast<Triangle::index_type>(triangle[1]), + static_cast<Triangle::index_type>(triangle[2])); } return BVH_OK; @@ -295,21 +316,24 @@ int BVHModelBase::addVertices(const Matrixx3f& points) { if (num_vertices + points.rows() > num_vertices_allocated) { num_vertices_allocated = num_vertices + (unsigned int)points.rows(); - Vec3f* temp = new Vec3f[num_vertices_allocated]; - if (!temp) { + std::shared_ptr<std::vector<Vec3f>> temp( + new std::vector<Vec3f>(num_vertices_allocated)); + if (!(temp.get())) { std::cerr << "BVH Error! Out of memory for vertices array on addVertex() call!" << std::endl; return BVH_ERR_MODEL_OUT_OF_MEMORY; } - std::copy(vertices, vertices + num_vertices, temp); - delete[] vertices; + for (size_t i = 0; i < num_vertices; ++i) { + (*temp)[i] = (*vertices)[i]; + } vertices = temp; } + std::vector<Vec3f>& vertices_ = *vertices; for (Eigen::DenseIndex id = 0; id < points.rows(); ++id) - vertices[num_vertices++] = points.row(id).transpose(); + vertices_[num_vertices++] = points.row(id).transpose(); return BVH_OK; } @@ -325,47 +349,51 @@ int BVHModelBase::addTriangle(const Vec3f& p1, const Vec3f& p2, } if (num_vertices + 2 >= num_vertices_allocated) { - Vec3f* temp = new Vec3f[num_vertices_allocated * 2 + 2]; - if (!temp) { + std::shared_ptr<std::vector<Vec3f>> temp( + new std::vector<Vec3f>(num_vertices_allocated * 2 + 2)); + if (!(temp.get())) { std::cerr << "BVH Error! Out of memory for vertices array on " "addTriangle() call!" << std::endl; return BVH_ERR_MODEL_OUT_OF_MEMORY; } - std::copy(vertices, vertices + num_vertices, temp); - delete[] vertices; + for (size_t i = 0; i < num_vertices; ++i) { + (*temp)[i] = (*vertices)[i]; + } vertices = temp; num_vertices_allocated = num_vertices_allocated * 2 + 2; } const unsigned int offset = num_vertices; - vertices[num_vertices] = p1; + (*vertices)[num_vertices] = p1; num_vertices++; - vertices[num_vertices] = p2; + (*vertices)[num_vertices] = p2; num_vertices++; - vertices[num_vertices] = p3; + (*vertices)[num_vertices] = p3; num_vertices++; if (num_tris >= num_tris_allocated) { - Triangle* temp = new Triangle[num_tris_allocated * 2]; - if (!temp) { + std::shared_ptr<std::vector<Triangle>> temp( + new std::vector<Triangle>(num_tris_allocated * 2)); + if (!(temp.get())) { std::cerr << "BVH Error! Out of memory for tri_indices array on " "addTriangle() call!" << std::endl; return BVH_ERR_MODEL_OUT_OF_MEMORY; } - std::copy(tri_indices, tri_indices + num_tris, temp); - delete[] tri_indices; + for (size_t i = 0; i < num_tris; ++i) { + (*temp)[i] = (*tri_indices)[i]; + } tri_indices = temp; num_tris_allocated *= 2; } - tri_indices[num_tris].set((Triangle::index_type)offset, - (Triangle::index_type)(offset + 1), - (Triangle::index_type)(offset + 2)); + (*tri_indices)[num_tris].set((Triangle::index_type)offset, + (Triangle::index_type)(offset + 1), + (Triangle::index_type)(offset + 2)); num_tris++; return BVH_OK; @@ -373,7 +401,7 @@ int BVHModelBase::addTriangle(const Vec3f& p1, const Vec3f& p2, int BVHModelBase::addSubModel(const std::vector<Vec3f>& ps) { if (build_state == BVH_BUILD_STATE_PROCESSED) { - std::cerr << "BVH Warning! Call addSubModel() in a wrong order. " + std::cerr << "BVH Warning! Calling addSubModel() in a wrong order. " "addSubModel() was ignored. Must do a beginModel() to clear " "the model for addition of new vertices." << std::endl; @@ -383,24 +411,26 @@ int BVHModelBase::addSubModel(const std::vector<Vec3f>& ps) { const unsigned int num_vertices_to_add = (unsigned int)ps.size(); if (num_vertices + num_vertices_to_add - 1 >= num_vertices_allocated) { - Vec3f* temp = - new Vec3f[num_vertices_allocated * 2 + num_vertices_to_add - 1]; - if (!temp) { + std::shared_ptr<std::vector<Vec3f>> temp(new std::vector<Vec3f>( + num_vertices_allocated * 2 + num_vertices_to_add - 1)); + if (!(temp.get())) { std::cerr << "BVH Error! Out of memory for vertices array on " "addSubModel() call!" << std::endl; return BVH_ERR_MODEL_OUT_OF_MEMORY; } - std::copy(vertices, vertices + num_vertices, temp); - delete[] vertices; + for (size_t i = 0; i < num_vertices; ++i) { + (*temp)[i] = (*vertices)[i]; + } vertices = temp; num_vertices_allocated = num_vertices_allocated * 2 + num_vertices_to_add - 1; } + std::vector<Vec3f>& vertices_ = *vertices; for (size_t i = 0; i < (size_t)num_vertices_to_add; ++i) { - vertices[num_vertices] = ps[i]; + vertices_[num_vertices] = ps[i]; num_vertices++; } @@ -410,7 +440,7 @@ int BVHModelBase::addSubModel(const std::vector<Vec3f>& ps) { int BVHModelBase::addSubModel(const std::vector<Vec3f>& ps, const std::vector<Triangle>& ts) { if (build_state == BVH_BUILD_STATE_PROCESSED) { - std::cerr << "BVH Warning! Call addSubModel() in a wrong order. " + std::cerr << "BVH Warning! Calling addSubModel() in a wrong order. " "addSubModel() was ignored. Must do a beginModel() to clear " "the model for addition of new vertices." << std::endl; @@ -420,17 +450,18 @@ int BVHModelBase::addSubModel(const std::vector<Vec3f>& ps, const unsigned int num_vertices_to_add = (unsigned int)ps.size(); if (num_vertices + num_vertices_to_add - 1 >= num_vertices_allocated) { - Vec3f* temp = - new Vec3f[num_vertices_allocated * 2 + num_vertices_to_add - 1]; - if (!temp) { + std::shared_ptr<std::vector<Vec3f>> temp(new std::vector<Vec3f>( + num_vertices_allocated * 2 + num_vertices_to_add - 1)); + if (!(temp.get())) { std::cerr << "BVH Error! Out of memory for vertices array on " "addSubModel() call!" << std::endl; return BVH_ERR_MODEL_OUT_OF_MEMORY; } - std::copy(vertices, vertices + num_vertices, temp); - delete[] vertices; + for (size_t i = 0; i < num_vertices; ++i) { + (*temp)[i] = (*vertices)[i]; + } vertices = temp; num_vertices_allocated = num_vertices_allocated * 2 + num_vertices_to_add - 1; @@ -438,32 +469,36 @@ int BVHModelBase::addSubModel(const std::vector<Vec3f>& ps, const unsigned int offset = num_vertices; + std::vector<Vec3f>& vertices_ = *vertices; for (size_t i = 0; i < (size_t)num_vertices_to_add; ++i) { - vertices[num_vertices] = ps[i]; + vertices_[num_vertices] = ps[i]; num_vertices++; } const unsigned int num_tris_to_add = (unsigned int)ts.size(); if (num_tris + num_tris_to_add - 1 >= num_tris_allocated) { - Triangle* temp = new Triangle[num_tris_allocated * 2 + num_tris_to_add - 1]; - if (!temp) { + std::shared_ptr<std::vector<Triangle>> temp(new std::vector<Triangle>( + num_tris_allocated * 2 + num_tris_to_add - 1)); + if (!(temp.get())) { std::cerr << "BVH Error! Out of memory for tri_indices array on " "addSubModel() call!" << std::endl; return BVH_ERR_MODEL_OUT_OF_MEMORY; } - std::copy(tri_indices, tri_indices + num_tris, temp); - delete[] tri_indices; + for (size_t i = 0; i < num_tris; ++i) { + (*temp)[i] = (*tri_indices)[i]; + } tri_indices = temp; num_tris_allocated = num_tris_allocated * 2 + num_tris_to_add - 1; } + std::vector<Triangle>& tri_indices_ = *tri_indices; for (size_t i = 0; i < (size_t)num_tris_to_add; ++i) { const Triangle& t = ts[i]; - tri_indices[num_tris].set(t[0] + (size_t)offset, t[1] + (size_t)offset, - t[2] + (size_t)offset); + tri_indices_[num_tris].set(t[0] + (size_t)offset, t[1] + (size_t)offset, + t[2] + (size_t)offset); num_tris++; } @@ -487,36 +522,46 @@ int BVHModelBase::endModel() { if (num_tris_allocated > num_tris) { if (num_tris > 0) { - Triangle* new_tris = new Triangle[num_tris]; - if (!new_tris) { + std::shared_ptr<std::vector<Triangle>> new_tris( + new std::vector<Triangle>(num_tris)); + if (!(new_tris.get())) { std::cerr << "BVH Error! Out of memory for tri_indices array in " "endModel() call!" << std::endl; return BVH_ERR_MODEL_OUT_OF_MEMORY; } - std::copy(tri_indices, tri_indices + num_tris, new_tris); - delete[] tri_indices; + + for (size_t i = 0; i < num_tris; ++i) { + (*new_tris)[i] = (*tri_indices)[i]; + } tri_indices = new_tris; num_tris_allocated = num_tris; } else { - delete[] tri_indices; - tri_indices = NULL; + tri_indices.reset(); num_tris = num_tris_allocated = 0; } } if (num_vertices_allocated > num_vertices) { - Vec3f* new_vertices = new Vec3f[num_vertices]; - if (!new_vertices) { - std::cerr - << "BVH Error! Out of memory for vertices array in endModel() call!" - << std::endl; - return BVH_ERR_MODEL_OUT_OF_MEMORY; + if (num_vertices > 0) { + std::shared_ptr<std::vector<Vec3f>> new_vertices( + new std::vector<Vec3f>(num_vertices)); + if (!(new_vertices.get())) { + std::cerr + << "BVH Error! Out of memory for vertices array in endModel() call!" + << std::endl; + return BVH_ERR_MODEL_OUT_OF_MEMORY; + } + + for (size_t i = 0; i < num_vertices; ++i) { + (*new_vertices)[i] = (*vertices)[i]; + } + vertices = new_vertices; + num_vertices_allocated = num_vertices; + } else { + vertices.reset(); + num_vertices = num_vertices_allocated = 0; } - std::copy(vertices, vertices + num_vertices, new_vertices); - delete[] vertices; - vertices = new_vertices; - num_vertices_allocated = num_vertices; } // construct BVH tree @@ -538,8 +583,7 @@ int BVHModelBase::beginReplaceModel() { return BVH_ERR_BUILD_EMPTY_PREVIOUS_FRAME; } - if (prev_vertices) delete[] prev_vertices; - prev_vertices = NULL; + if (prev_vertices.get()) prev_vertices.reset(); num_vertex_updated = 0; @@ -557,7 +601,7 @@ int BVHModelBase::replaceVertex(const Vec3f& p) { return BVH_ERR_BUILD_OUT_OF_SEQUENCE; } - vertices[num_vertex_updated] = p; + (*vertices)[num_vertex_updated] = p; num_vertex_updated++; return BVH_OK; @@ -573,11 +617,11 @@ int BVHModelBase::replaceTriangle(const Vec3f& p1, const Vec3f& p2, return BVH_ERR_BUILD_OUT_OF_SEQUENCE; } - vertices[num_vertex_updated] = p1; + (*vertices)[num_vertex_updated] = p1; num_vertex_updated++; - vertices[num_vertex_updated] = p2; + (*vertices)[num_vertex_updated] = p2; num_vertex_updated++; - vertices[num_vertex_updated] = p3; + (*vertices)[num_vertex_updated] = p3; num_vertex_updated++; return BVH_OK; } @@ -591,8 +635,9 @@ int BVHModelBase::replaceSubModel(const std::vector<Vec3f>& ps) { return BVH_ERR_BUILD_OUT_OF_SEQUENCE; } + std::vector<Vec3f>& vertices_ = *vertices; for (unsigned int i = 0; i < ps.size(); ++i) { - vertices[num_vertex_updated] = ps[i]; + vertices_[num_vertex_updated] = ps[i]; num_vertex_updated++; } return BVH_OK; @@ -635,13 +680,13 @@ int BVHModelBase::beginUpdateModel() { return BVH_ERR_BUILD_EMPTY_PREVIOUS_FRAME; } - if (prev_vertices) { - Vec3f* temp = prev_vertices; + if (prev_vertices.get()) { + std::shared_ptr<std::vector<Vec3f>> temp = prev_vertices; prev_vertices = vertices; vertices = temp; } else { prev_vertices = vertices; - vertices = new Vec3f[num_vertices]; + vertices.reset(new std::vector<Vec3f>(num_vertices)); } num_vertex_updated = 0; @@ -660,7 +705,7 @@ int BVHModelBase::updateVertex(const Vec3f& p) { return BVH_ERR_BUILD_OUT_OF_SEQUENCE; } - vertices[num_vertex_updated] = p; + (*vertices)[num_vertex_updated] = p; num_vertex_updated++; return BVH_OK; @@ -676,11 +721,11 @@ int BVHModelBase::updateTriangle(const Vec3f& p1, const Vec3f& p2, return BVH_ERR_BUILD_OUT_OF_SEQUENCE; } - vertices[num_vertex_updated] = p1; + (*vertices)[num_vertex_updated] = p1; num_vertex_updated++; - vertices[num_vertex_updated] = p2; + (*vertices)[num_vertex_updated] = p2; num_vertex_updated++; - vertices[num_vertex_updated] = p3; + (*vertices)[num_vertex_updated] = p3; num_vertex_updated++; return BVH_OK; } @@ -694,8 +739,9 @@ int BVHModelBase::updateSubModel(const std::vector<Vec3f>& ps) { return BVH_ERR_BUILD_OUT_OF_SEQUENCE; } + std::vector<Vec3f>& vertices_ = *vertices; for (unsigned int i = 0; i < ps.size(); ++i) { - vertices[num_vertex_updated] = ps[i]; + vertices_[num_vertex_updated] = ps[i]; num_vertex_updated++; } return BVH_OK; @@ -735,15 +781,16 @@ int BVHModelBase::endUpdateModel(bool refit, bool bottomup) { void BVHModelBase::computeLocalAABB() { AABB aabb_; + const std::vector<Vec3f>& vertices_ = *vertices; for (unsigned int i = 0; i < num_vertices; ++i) { - aabb_ += vertices[i]; + aabb_ += vertices_[i]; } aabb_center = aabb_.center(); aabb_radius = 0; for (unsigned int i = 0; i < num_vertices; ++i) { - FCL_REAL r = (aabb_center - vertices[i]).squaredNorm(); + FCL_REAL r = (aabb_center - vertices_[i]).squaredNorm(); if (r > aabb_radius) aabb_radius = r; } @@ -759,16 +806,12 @@ BVHModel<BV>::BVHModel() bv_splitter(new BVSplitter<BV>(SPLIT_METHOD_MEAN)), bv_fitter(new BVFitter<BV>()), num_bvs_allocated(0), - primitive_indices(NULL), - bvs(NULL), num_bvs(0) {} template <typename BV> void BVHModel<BV>::deleteBVs() { - delete[] bvs; - bvs = NULL; - delete[] primitive_indices; - primitive_indices = NULL; + bvs.reset(); + primitive_indices.reset(); num_bvs_allocated = num_bvs = 0; } @@ -781,9 +824,10 @@ bool BVHModel<BV>::allocateBVs() { else num_bvs_to_be_allocated = 2 * num_tris - 1; - bvs = new BVNode<BV>[num_bvs_to_be_allocated]; - primitive_indices = new unsigned int[num_bvs_to_be_allocated]; - if (!bvs || !primitive_indices) { + bvs.reset(new bv_node_vector_t(num_bvs_to_be_allocated)); + primitive_indices.reset( + new std::vector<unsigned int>(num_bvs_to_be_allocated)); + if (!(bvs.get()) || !(primitive_indices.get())) { std::cerr << "BVH Error! Out of memory for BV array in endModel()!" << std::endl; return false; @@ -814,9 +858,11 @@ int BVHModel<BV>::memUsage(const bool msg) const { template <typename BV> int BVHModel<BV>::buildTree() { // set BVFitter - bv_fitter->set(vertices, tri_indices, getModelType()); + Vec3f* vertices_ = vertices.get() ? vertices->data() : NULL; + Triangle* tri_indices_ = tri_indices.get() ? tri_indices->data() : NULL; + bv_fitter->set(vertices_, tri_indices_, getModelType()); // set SplitRule - bv_splitter->set(vertices, tri_indices, getModelType()); + bv_splitter->set(vertices_, tri_indices_, getModelType()); num_bvs = 1; @@ -833,7 +879,8 @@ int BVHModel<BV>::buildTree() { return BVH_ERR_UNSUPPORTED_FUNCTION; } - for (unsigned int i = 0; i < num_primitives; ++i) primitive_indices[i] = i; + std::vector<unsigned int>& primitive_indices_ = *primitive_indices; + for (unsigned int i = 0; i < num_primitives; ++i) primitive_indices_[i] = i; recursiveBuildTree(0, 0, num_primitives); bv_fitter->clear(); @@ -846,8 +893,9 @@ template <typename BV> int BVHModel<BV>::recursiveBuildTree(int bv_id, unsigned int first_primitive, unsigned int num_primitives) { BVHModelType type = getModelType(); - BVNode<BV>* bvnode = bvs + bv_id; - unsigned int* cur_primitive_indices = primitive_indices + first_primitive; + BVNode<BV>* bvnode = bvs->data() + bv_id; + unsigned int* cur_primitive_indices = + primitive_indices->data() + first_primitive; // constructing BV BV bv = bv_fitter->fit(cur_primitive_indices, num_primitives); @@ -864,15 +912,17 @@ int BVHModel<BV>::recursiveBuildTree(int bv_id, unsigned int first_primitive, num_bvs += 2; unsigned int c1 = 0; + const std::vector<Vec3f>& vertices_ = *vertices; + const std::vector<Triangle>& tri_indices_ = *tri_indices; for (unsigned int i = 0; i < num_primitives; ++i) { Vec3f p; if (type == BVH_MODEL_POINTCLOUD) - p = vertices[cur_primitive_indices[i]]; + p = vertices_[cur_primitive_indices[i]]; else if (type == BVH_MODEL_TRIANGLES) { - const Triangle& t = tri_indices[cur_primitive_indices[i]]; - const Vec3f& p1 = vertices[t[0]]; - const Vec3f& p2 = vertices[t[1]]; - const Vec3f& p3 = vertices[t[2]]; + const Triangle& t = tri_indices_[cur_primitive_indices[i]]; + const Vec3f& p1 = vertices_[t[0]]; + const Vec3f& p2 = vertices_[t[1]]; + const Vec3f& p3 = vertices_[t[2]]; p = (p1 + p2 + p3) / 3.; } else { @@ -932,31 +982,32 @@ int BVHModel<BV>::refitTree_bottomup() { template <typename BV> int BVHModel<BV>::recursiveRefitTree_bottomup(int bv_id) { - BVNode<BV>* bvnode = bvs + bv_id; + BVNode<BV>* bvnode = bvs->data() + bv_id; if (bvnode->isLeaf()) { BVHModelType type = getModelType(); int primitive_id = -(bvnode->first_child + 1); if (type == BVH_MODEL_POINTCLOUD) { BV bv; - if (prev_vertices) { + if (prev_vertices.get()) { Vec3f v[2]; - v[0] = prev_vertices[primitive_id]; - v[1] = vertices[primitive_id]; + v[0] = (*prev_vertices)[static_cast<size_t>(primitive_id)]; + v[1] = (*vertices)[static_cast<size_t>(primitive_id)]; fit(v, 2, bv); } else - fit(vertices + primitive_id, 1, bv); + fit(vertices->data() + primitive_id, 1, bv); bvnode->bv = bv; } else if (type == BVH_MODEL_TRIANGLES) { BV bv; - const Triangle& triangle = tri_indices[primitive_id]; + const Triangle& triangle = + (*tri_indices)[static_cast<size_t>(primitive_id)]; - if (prev_vertices) { + if (prev_vertices.get()) { Vec3f v[6]; for (Triangle::index_type i = 0; i < 3; ++i) { - v[i] = prev_vertices[triangle[i]]; - v[i + 3] = vertices[triangle[i]]; + v[i] = (*prev_vertices)[triangle[i]]; + v[i + 3] = (*vertices)[triangle[i]]; } fit(v, 6, bv); @@ -967,7 +1018,7 @@ int BVHModel<BV>::recursiveRefitTree_bottomup(int bv_id) { // bvnode->num_primitives); Vec3f v[3]; for (int i = 0; i < 3; ++i) { - v[i] = vertices[triangle[(Triangle::index_type)i]]; + v[i] = (*vertices)[triangle[(Triangle::index_type)i]]; } fit(v, 3, bv); @@ -981,7 +1032,8 @@ int BVHModel<BV>::recursiveRefitTree_bottomup(int bv_id) { } else { recursiveRefitTree_bottomup(bvnode->leftChild()); recursiveRefitTree_bottomup(bvnode->rightChild()); - bvnode->bv = bvs[bvnode->leftChild()].bv + bvs[bvnode->rightChild()].bv; + bvnode->bv = (*bvs)[static_cast<size_t>(bvnode->leftChild())].bv + + (*bvs)[static_cast<size_t>(bvnode->rightChild())].bv; // TODO use bv_fitter to build BV. See comment in refitTree_bottomup // unsigned int* cur_primitive_indices = primitive_indices + // bvnode->first_primitive; bvnode->bv = @@ -993,11 +1045,16 @@ int BVHModel<BV>::recursiveRefitTree_bottomup(int bv_id) { template <typename BV> int BVHModel<BV>::refitTree_topdown() { - bv_fitter->set(vertices, prev_vertices, tri_indices, getModelType()); + Vec3f* vertices_ = vertices.get() ? vertices->data() : NULL; + Vec3f* prev_vertices_ = prev_vertices.get() ? prev_vertices->data() : NULL; + Triangle* tri_indices_ = tri_indices.get() ? tri_indices->data() : NULL; + bv_fitter->set(vertices_, prev_vertices_, tri_indices_, getModelType()); + BVNode<BV>* bvs_ = bvs->data(); + unsigned int* primitive_indices_ = primitive_indices->data(); for (unsigned int i = 0; i < num_bvs; ++i) { - BV bv = bv_fitter->fit(primitive_indices + bvs[i].first_primitive, - bvs[i].num_primitives); - bvs[i].bv = bv; + BV bv = bv_fitter->fit(primitive_indices_ + bvs_[i].first_primitive, + bvs_[i].num_primitives); + bvs_[i].bv = bv; } bv_fitter->clear(); @@ -1008,11 +1065,14 @@ int BVHModel<BV>::refitTree_topdown() { template <> void BVHModel<OBB>::makeParentRelativeRecurse(int bv_id, Matrix3f& parent_axes, const Vec3f& parent_c) { - OBB& obb = bvs[bv_id].bv; - if (!bvs[bv_id].isLeaf()) { - makeParentRelativeRecurse(bvs[bv_id].first_child, obb.axes, obb.To); - - makeParentRelativeRecurse(bvs[bv_id].first_child + 1, obb.axes, obb.To); + bv_node_vector_t& bvs_ = *bvs; + OBB& obb = bvs_[static_cast<size_t>(bv_id)].bv; + if (!bvs_[static_cast<size_t>(bv_id)].isLeaf()) { + makeParentRelativeRecurse(bvs_[static_cast<size_t>(bv_id)].first_child, + obb.axes, obb.To); + + makeParentRelativeRecurse(bvs_[static_cast<size_t>(bv_id)].first_child + 1, + obb.axes, obb.To); } // make self parent relative @@ -1026,11 +1086,14 @@ void BVHModel<OBB>::makeParentRelativeRecurse(int bv_id, Matrix3f& parent_axes, template <> void BVHModel<RSS>::makeParentRelativeRecurse(int bv_id, Matrix3f& parent_axes, const Vec3f& parent_c) { - RSS& rss = bvs[bv_id].bv; - if (!bvs[bv_id].isLeaf()) { - makeParentRelativeRecurse(bvs[bv_id].first_child, rss.axes, rss.Tr); - - makeParentRelativeRecurse(bvs[bv_id].first_child + 1, rss.axes, rss.Tr); + bv_node_vector_t& bvs_ = *bvs; + RSS& rss = bvs_[static_cast<size_t>(bv_id)].bv; + if (!bvs_[static_cast<size_t>(bv_id)].isLeaf()) { + makeParentRelativeRecurse(bvs_[static_cast<size_t>(bv_id)].first_child, + rss.axes, rss.Tr); + + makeParentRelativeRecurse(bvs_[static_cast<size_t>(bv_id)].first_child + 1, + rss.axes, rss.Tr); } // make self parent relative @@ -1045,12 +1108,15 @@ template <> void BVHModel<OBBRSS>::makeParentRelativeRecurse(int bv_id, Matrix3f& parent_axes, const Vec3f& parent_c) { - OBB& obb = bvs[bv_id].bv.obb; - RSS& rss = bvs[bv_id].bv.rss; - if (!bvs[bv_id].isLeaf()) { - makeParentRelativeRecurse(bvs[bv_id].first_child, obb.axes, obb.To); - - makeParentRelativeRecurse(bvs[bv_id].first_child + 1, obb.axes, obb.To); + bv_node_vector_t& bvs_ = *bvs; + OBB& obb = bvs_[static_cast<size_t>(bv_id)].bv.obb; + RSS& rss = bvs_[static_cast<size_t>(bv_id)].bv.rss; + if (!bvs_[static_cast<size_t>(bv_id)].isLeaf()) { + makeParentRelativeRecurse(bvs_[static_cast<size_t>(bv_id)].first_child, + obb.axes, obb.To); + + makeParentRelativeRecurse(bvs_[static_cast<size_t>(bv_id)].first_child + 1, + obb.axes, obb.To); } // make self parent relative @@ -1088,23 +1154,23 @@ NODE_TYPE BVHModel<OBBRSS>::getNodeType() const { } template <> -NODE_TYPE BVHModel<KDOP<16> >::getNodeType() const { +NODE_TYPE BVHModel<KDOP<16>>::getNodeType() const { return BV_KDOP16; } template <> -NODE_TYPE BVHModel<KDOP<18> >::getNodeType() const { +NODE_TYPE BVHModel<KDOP<18>>::getNodeType() const { return BV_KDOP18; } template <> -NODE_TYPE BVHModel<KDOP<24> >::getNodeType() const { +NODE_TYPE BVHModel<KDOP<24>>::getNodeType() const { return BV_KDOP24; } -template class BVHModel<KDOP<16> >; -template class BVHModel<KDOP<18> >; -template class BVHModel<KDOP<24> >; +template class BVHModel<KDOP<16>>; +template class BVHModel<KDOP<18>>; +template class BVHModel<KDOP<24>>; template class BVHModel<OBB>; template class BVHModel<AABB>; template class BVHModel<RSS>; diff --git a/src/BVH/BVH_utility.cpp b/src/BVH/BVH_utility.cpp index 12c3f0009960f3a46f10bbe0212541c478b41e5f..0302744e48cb029e5229d769e86d96838097cd86 100644 --- a/src/BVH/BVH_utility.cpp +++ b/src/BVH/BVH_utility.cpp @@ -62,22 +62,24 @@ BVHModel<BV>* BVHExtract(const BVHModel<BV>& model, const Transform3f& pose, std::vector<bool> keep_vertex(model.num_vertices, false); std::vector<bool> keep_tri(model.num_tris, false); unsigned int ntri = 0; + const std::vector<Vec3f>& model_vertices_ = *(model.vertices); + const std::vector<Triangle>& model_tri_indices_ = *(model.tri_indices); for (unsigned int i = 0; i < model.num_tris; ++i) { - const Triangle& t = model.tri_indices[i]; + const Triangle& t = model_tri_indices_[i]; bool keep_this_tri = keep_vertex[t[0]] || keep_vertex[t[1]] || keep_vertex[t[2]]; if (!keep_this_tri) { for (unsigned int j = 0; j < 3; ++j) { - if (aabb.contain(q * model.vertices[t[j]])) { + if (aabb.contain(q * model_vertices_[t[j]])) { keep_this_tri = true; break; } } - const Vec3f& p0 = model.vertices[t[0]]; - const Vec3f& p1 = model.vertices[t[1]]; - const Vec3f& p2 = model.vertices[t[2]]; + const Vec3f& p0 = model_vertices_[t[0]]; + const Vec3f& p1 = model_vertices_[t[1]]; + const Vec3f& p2 = model_vertices_[t[2]]; Vec3f c1, c2, normal; FCL_REAL distance; if (!keep_this_tri && @@ -99,20 +101,22 @@ BVHModel<BV>* BVHExtract(const BVHModel<BV>& model, const Transform3f& pose, new_model->beginModel(ntri, std::min(ntri * 3, model.num_vertices)); std::vector<unsigned int> idxConversion(model.num_vertices); assert(new_model->num_vertices == 0); + std::vector<Vec3f>& new_model_vertices_ = *(new_model->vertices); for (unsigned int i = 0; i < keep_vertex.size(); ++i) { if (keep_vertex[i]) { idxConversion[i] = new_model->num_vertices; - new_model->vertices[new_model->num_vertices] = model.vertices[i]; + new_model_vertices_[new_model->num_vertices] = model_vertices_[i]; new_model->num_vertices++; } } assert(new_model->num_tris == 0); + std::vector<Triangle>& new_model_tri_indices_ = *(new_model->tri_indices); for (unsigned int i = 0; i < keep_tri.size(); ++i) { if (keep_tri[i]) { - new_model->tri_indices[new_model->num_tris].set( - idxConversion[model.tri_indices[i][0]], - idxConversion[model.tri_indices[i][1]], - idxConversion[model.tri_indices[i][2]]); + new_model_tri_indices_[new_model->num_tris].set( + idxConversion[model_tri_indices_[i][0]], + idxConversion[model_tri_indices_[i][1]], + idxConversion[model_tri_indices_[i][2]]); new_model->num_tris++; } } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 474e4a3310915be2f0b22760611e089d177f1758..4cfc36f957e19cdb3d5125a086d0cbee5dd66836 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,7 @@ # # Software License Agreement (BSD License) # -# Copyright (c) 2014, 2020 CNRS-LAAS +# Copyright (c) 2014, 2020-2024 CNRS-LAAS INRIA # Author: Florent Lamiraux # All rights reserved. # @@ -78,6 +78,9 @@ set(${LIBRARY_NAME}_SOURCES distance/sphere_cylinder.cpp distance/sphere_halfspace.cpp distance/sphere_plane.cpp + distance/sphere_capsule.cpp + distance/ellipsoid_halfspace.cpp + distance/ellipsoid_plane.cpp distance/convex_halfspace.cpp distance/triangle_halfspace.cpp intersect.cpp @@ -93,6 +96,7 @@ set(${LIBRARY_NAME}_SOURCES mesh_loader/assimp.cpp mesh_loader/loader.cpp hfield.cpp + serialization/serialization.cpp ) if(HPP_FCL_HAS_OCTOMAP) @@ -178,6 +182,10 @@ if(UNIX) set_target_properties(${PROJECT_NAME} PROPERTIES INSTALL_RPATH "${${PROJECT_NAME}_INSTALL_RPATH}") endif() +IF(MSVC) + target_compile_options(${PROJECT_NAME} PUBLIC "/bigobj") +ENDIF() + # IDE sources and headers sorting ADD_SOURCE_GROUP(${LIBRARY_NAME}_SOURCES) ADD_HEADER_GROUP(PROJECT_HEADERS_FULL_PATH) diff --git a/src/broadphase/broadphase_SaP.cpp b/src/broadphase/broadphase_SaP.cpp index e956012394401b4750e8a4319a3249a85a21db20..4da0c995af2667b9d64ddc21907410bbc1d0118f 100644 --- a/src/broadphase/broadphase_SaP.cpp +++ b/src/broadphase/broadphase_SaP.cpp @@ -124,10 +124,10 @@ void SaPCollisionManager::registerObjects( std::sort( endpoints.begin(), endpoints.end(), std::bind(std::less<FCL_REAL>(), - std::bind(static_cast<FCL_REAL (EndPoint::*)(size_t) const>( + std::bind(static_cast<FCL_REAL (EndPoint::*)(int) const>( &EndPoint::getVal), std::placeholders::_1, coord), - std::bind(static_cast<FCL_REAL (EndPoint::*)(size_t) const>( + std::bind(static_cast<FCL_REAL (EndPoint::*)(int) const>( &EndPoint::getVal), std::placeholders::_2, coord))); @@ -181,28 +181,29 @@ void SaPCollisionManager::registerObjects( //============================================================================== void SaPCollisionManager::registerObject(CollisionObject* obj) { - SaPAABB* curr = new SaPAABB; - curr->cached = obj->getAABB(); - curr->obj = obj; - curr->lo = new EndPoint; - curr->lo->minmax = 0; - curr->lo->aabb = curr; - - curr->hi = new EndPoint; - curr->hi->minmax = 1; - curr->hi->aabb = curr; - + // Initialize a new SaPAABB associated with current Collision Object + SaPAABB* new_sap = new SaPAABB; + new_sap->cached = obj->getAABB(); + new_sap->obj = obj; + + new_sap->lo = new EndPoint; + new_sap->lo->minmax = 0; + new_sap->lo->aabb = new_sap; + + new_sap->hi = new EndPoint; + new_sap->hi->minmax = 1; + new_sap->hi->aabb = new_sap; for (int coord = 0; coord < 3; ++coord) { EndPoint* current = elist[coord]; // first insert the lo end point if (current == nullptr) // empty list { - elist[coord] = curr->lo; - curr->lo->prev[coord] = curr->lo->next[coord] = nullptr; + elist[coord] = new_sap->lo; + new_sap->lo->prev[coord] = new_sap->lo->next[coord] = nullptr; } else // otherwise, find the correct location in the list and insert { - EndPoint* curr_lo = curr->lo; + EndPoint* curr_lo = new_sap->lo; FCL_REAL curr_lo_val = curr_lo->getVal()[coord]; while ((current->getVal()[coord] < curr_lo_val) && (current->next[coord] != nullptr)) @@ -211,13 +212,16 @@ void SaPCollisionManager::registerObject(CollisionObject* obj) { if (current->getVal()[coord] >= curr_lo_val) { curr_lo->prev[coord] = current->prev[coord]; curr_lo->next[coord] = current; - if (current->prev[coord] == nullptr) - elist[coord] = curr_lo; + if (current->prev[coord] == nullptr) // current was the first box + elist[coord] = + curr_lo; // new_sap->lo becomes the new first box on the axis else current->prev[coord]->next[coord] = curr_lo; - current->prev[coord] = curr_lo; - } else { + current->prev[coord] = + curr_lo; // new_sap->lo becomes the predecessor of current + } else { // current->next[coord] == nullptr, so the current is just the + // cell before current-> curr_lo->prev[coord] = current; curr_lo->next[coord] = nullptr; current->next[coord] = curr_lo; @@ -225,16 +229,16 @@ void SaPCollisionManager::registerObject(CollisionObject* obj) { } // now insert hi end point - current = curr->lo; + current = new_sap->lo; - EndPoint* curr_hi = curr->hi; + EndPoint* curr_hi = new_sap->hi; FCL_REAL curr_hi_val = curr_hi->getVal()[coord]; if (coord == 0) { while ((current->getVal()[coord] < curr_hi_val) && (current->next[coord] != nullptr)) { - if (current != curr->lo) - if (current->aabb->cached.overlap(curr->cached)) + if (current != new_sap->lo) + if (current->aabb->cached.overlap(new_sap->cached)) overlap_pairs.emplace_back(current->aabb->obj, obj); current = current->next[coord]; @@ -248,9 +252,7 @@ void SaPCollisionManager::registerObject(CollisionObject* obj) { if (current->getVal()[coord] >= curr_hi_val) { curr_hi->prev[coord] = current->prev[coord]; curr_hi->next[coord] = current; - if (current->prev[coord] == nullptr) - elist[coord] = curr_hi; - else + if (current->prev[coord] != nullptr) current->prev[coord]->next[coord] = curr_hi; current->prev[coord] = curr_hi; @@ -261,9 +263,9 @@ void SaPCollisionManager::registerObject(CollisionObject* obj) { } } - AABB_arr.push_back(curr); + AABB_arr.push_back(new_sap); - obj_aabb_map[obj] = curr; + obj_aabb_map[obj] = new_sap; updateVelist(); } @@ -275,8 +277,8 @@ void SaPCollisionManager::setup() { FCL_REAL scale[3]; scale[0] = (velist[0].back())->getVal(0) - velist[0][0]->getVal(0); scale[1] = (velist[1].back())->getVal(1) - velist[1][0]->getVal(1); - ; scale[2] = (velist[2].back())->getVal(2) - velist[2][0]->getVal(2); + int axis = 0; if (scale[axis] < scale[1]) axis = 1; if (scale[axis] < scale[2]) axis = 2; @@ -289,19 +291,18 @@ void SaPCollisionManager::update_(SaPAABB* updated_aabb) { SaPAABB* current = updated_aabb; - Vec3f new_min = current->obj->getAABB().min_; - Vec3f new_max = current->obj->getAABB().max_; + const AABB current_aabb = current->obj->getAABB(); - SaPAABB dummy; - dummy.cached = current->obj->getAABB(); + const Vec3f& new_min = current_aabb.min_; + const Vec3f& new_max = current_aabb.max_; for (int coord = 0; coord < 3; ++coord) { int direction; // -1 reverse, 0 nochange, 1 forward EndPoint* temp; - if (current->lo->getVal((size_t)coord) > new_min[coord]) + if (current->lo->getVal(coord) > new_min[coord]) direction = -1; - else if (current->lo->getVal((size_t)coord) < new_min[coord]) + else if (current->lo->getVal(coord) < new_min[coord]) direction = 1; else direction = 0; @@ -309,11 +310,10 @@ void SaPCollisionManager::update_(SaPAABB* updated_aabb) { if (direction == -1) { // first update the "lo" endpoint of the interval if (current->lo->prev[coord] != nullptr) { - temp = current->lo; - while ((temp != nullptr) && - (temp->getVal((size_t)coord) > new_min[coord])) { + temp = current->lo->prev[coord]; + while ((temp != nullptr) && (temp->getVal(coord) > new_min[coord])) { if (temp->minmax == 1) - if (temp->aabb->cached.overlap(dummy.cached)) + if (temp->aabb->cached.overlap(current_aabb)) addToOverlapPairs(SaPPair(temp->aabb->obj, current->obj)); temp = temp->prev[coord]; } @@ -335,40 +335,46 @@ void SaPCollisionManager::update_(SaPAABB* updated_aabb) { } } - current->lo->getVal((size_t)coord) = new_min[coord]; + // Update the value of the lower bound along axis coord + current->lo->getVal(coord) = new_min[coord]; // update hi end point - temp = current->hi; - while (temp->getVal((size_t)coord) > new_max[coord]) { - if ((temp->minmax == 0) && - (temp->aabb->cached.overlap(current->cached))) - removeFromOverlapPairs(SaPPair(temp->aabb->obj, current->obj)); - temp = temp->prev[coord]; - } + if (current->hi->prev[coord] != nullptr) { + temp = current->hi->prev[coord]; + + while ((temp != nullptr) && (temp->getVal(coord) > new_max[coord])) { + if ((temp->minmax == 0) && + (temp->aabb->cached.overlap(current->cached))) + removeFromOverlapPairs(SaPPair(temp->aabb->obj, current->obj)); + temp = temp->prev[coord]; + } - current->hi->prev[coord]->next[coord] = current->hi->next[coord]; - if (current->hi->next[coord] != nullptr) - current->hi->next[coord]->prev[coord] = current->hi->prev[coord]; - current->hi->prev[coord] = temp; - current->hi->next[coord] = temp->next[coord]; - if (temp->next[coord] != nullptr) - temp->next[coord]->prev[coord] = current->hi; - temp->next[coord] = current->hi; + current->hi->prev[coord]->next[coord] = current->hi->next[coord]; + if (current->hi->next[coord] != nullptr) + current->hi->next[coord]->prev[coord] = current->hi->prev[coord]; + current->hi->prev[coord] = temp; // Wrong line + current->hi->next[coord] = temp->next[coord]; + if (temp->next[coord] != nullptr) + temp->next[coord]->prev[coord] = current->hi; + temp->next[coord] = current->hi; + + current->hi->getVal(coord) = new_max[coord]; + } - current->hi->getVal((size_t)coord) = new_max[coord]; + current->hi->getVal(coord) = new_max[coord]; } else if (direction == 1) { // here, we first update the "hi" endpoint. if (current->hi->next[coord] != nullptr) { - temp = current->hi; + temp = current->hi->next[coord]; while ((temp->next[coord] != nullptr) && - (temp->getVal((size_t)coord) < new_max[coord])) { + (temp->getVal(coord) < new_max[coord])) { if (temp->minmax == 0) - if (temp->aabb->cached.overlap(dummy.cached)) + if (temp->aabb->cached.overlap(current_aabb)) addToOverlapPairs(SaPPair(temp->aabb->obj, current->obj)); temp = temp->next[coord]; } - if (temp->getVal((size_t)coord) < new_max[coord]) { + if (temp->getVal(coord) < new_max[coord]) { current->hi->prev[coord]->next[coord] = current->hi->next[coord]; current->hi->next[coord]->prev[coord] = current->hi->prev[coord]; current->hi->prev[coord] = temp; @@ -384,31 +390,34 @@ void SaPCollisionManager::update_(SaPAABB* updated_aabb) { } } - current->hi->getVal((size_t)coord) = new_max[coord]; + current->hi->getVal(coord) = new_max[coord]; // then, update the "lo" endpoint of the interval. - temp = current->lo; + if (current->lo->next[coord] != nullptr) { + temp = current->lo->next[coord]; - while (temp->getVal((size_t)coord) < new_min[coord]) { - if ((temp->minmax == 1) && - (temp->aabb->cached.overlap(current->cached))) - removeFromOverlapPairs(SaPPair(temp->aabb->obj, current->obj)); - temp = temp->next[coord]; - } + while ((temp->next[coord] != nullptr) && + (temp->getVal(coord) < new_min[coord])) { + if ((temp->minmax == 1) && + (temp->aabb->cached.overlap(current->cached))) + removeFromOverlapPairs(SaPPair(temp->aabb->obj, current->obj)); + temp = temp->next[coord]; + } - if (current->lo->prev[coord] != nullptr) - current->lo->prev[coord]->next[coord] = current->lo->next[coord]; - else - elist[coord] = current->lo->next[coord]; - current->lo->next[coord]->prev[coord] = current->lo->prev[coord]; - current->lo->prev[coord] = temp->prev[coord]; - current->lo->next[coord] = temp; - if (temp->prev[coord] != nullptr) - temp->prev[coord]->next[coord] = current->lo; - else - elist[coord] = current->lo; - temp->prev[coord] = current->lo; - current->lo->getVal((size_t)coord) = new_min[coord]; + if (current->lo->prev[coord] != nullptr) + current->lo->prev[coord]->next[coord] = current->lo->next[coord]; + else + elist[coord] = current->lo->next[coord]; + current->lo->next[coord]->prev[coord] = current->lo->prev[coord]; + current->lo->prev[coord] = temp->prev[coord]; + current->lo->next[coord] = temp; + if (temp->prev[coord] != nullptr) + temp->prev[coord]->next[coord] = current->lo; + else + elist[coord] = current->lo; + temp->prev[coord] = current->lo; + current->lo->getVal(coord) = new_min[coord]; + } } } } @@ -512,10 +521,10 @@ bool SaPCollisionManager::collide_(CollisionObject* obj, const auto res_it = std::upper_bound( velist[axis].begin(), velist[axis].end(), &dummy, std::bind(std::less<FCL_REAL>(), - std::bind(static_cast<FCL_REAL (EndPoint::*)(size_t) const>( + std::bind(static_cast<FCL_REAL (EndPoint::*)(int) const>( &EndPoint::getVal), std::placeholders::_1, axis), - std::bind(static_cast<FCL_REAL (EndPoint::*)(size_t) const>( + std::bind(static_cast<FCL_REAL (EndPoint::*)(int) const>( &EndPoint::getVal), std::placeholders::_2, axis))); @@ -526,8 +535,7 @@ bool SaPCollisionManager::collide_(CollisionObject* obj, while (pos != end_pos) { if (pos->aabb->obj != obj) { - if ((pos->minmax == 0) && - (pos->aabb->hi->getVal((size_t)axis) >= min_val)) { + if ((pos->minmax == 0) && (pos->aabb->hi->getVal(axis) >= min_val)) { if (pos->aabb->cached.overlap(obj->getAABB())) if ((*callback)(obj, pos->aabb->obj)) return true; } @@ -607,10 +615,10 @@ bool SaPCollisionManager::distance_(CollisionObject* obj, const auto res_it = std::upper_bound( velist[axis].begin(), velist[axis].end(), &dummy, std::bind(std::less<FCL_REAL>(), - std::bind(static_cast<FCL_REAL (EndPoint::*)(size_t) const>( + std::bind(static_cast<FCL_REAL (EndPoint::*)(int) const>( &EndPoint::getVal), std::placeholders::_1, axis), - std::bind(static_cast<FCL_REAL (EndPoint::*)(size_t) const>( + std::bind(static_cast<FCL_REAL (EndPoint::*)(int) const>( &EndPoint::getVal), std::placeholders::_2, axis))); @@ -622,8 +630,7 @@ bool SaPCollisionManager::distance_(CollisionObject* obj, while (pos != end_pos) { // can change to pos->aabb->hi->getVal(axis) >= min_val - min_dist, and // then update start_pos to end_pos. but this seems slower. - if ((pos->minmax == 0) && - (pos->aabb->hi->getVal((size_t)axis) >= min_val)) { + if ((pos->minmax == 0) && (pos->aabb->hi->getVal(axis) >= min_val)) { CollisionObject* curr_obj = pos->aabb->obj; if (curr_obj != obj) { if (!this->enable_tested_set_) { @@ -789,19 +796,19 @@ Vec3f& SaPCollisionManager::EndPoint::getVal() { } //============================================================================== -FCL_REAL SaPCollisionManager::EndPoint::getVal(size_t i) const { +FCL_REAL SaPCollisionManager::EndPoint::getVal(int i) const { if (minmax) - return aabb->cached.max_[(int)i]; + return aabb->cached.max_[i]; else - return aabb->cached.min_[(int)i]; + return aabb->cached.min_[i]; } //============================================================================== -FCL_REAL& SaPCollisionManager::EndPoint::getVal(size_t i) { +FCL_REAL& SaPCollisionManager::EndPoint::getVal(int i) { if (minmax) - return aabb->cached.max_[(int)i]; + return aabb->cached.max_[i]; else - return aabb->cached.min_[(int)i]; + return aabb->cached.min_[i]; } //============================================================================== diff --git a/src/broadphase/broadphase_dynamic_AABB_tree.cpp b/src/broadphase/broadphase_dynamic_AABB_tree.cpp index ab36051c7f69d6b0c56dcc359828c50953e35fd6..9338cc42434d704194f8028f65c48f0d2c60da73 100644 --- a/src/broadphase/broadphase_dynamic_AABB_tree.cpp +++ b/src/broadphase/broadphase_dynamic_AABB_tree.cpp @@ -39,7 +39,7 @@ #include <limits> -#if HPP_FCL_HAVE_OCTOMAP +#ifdef HPP_FCL_HAVE_OCTOMAP #include "hpp/fcl/octree.h" #endif @@ -515,7 +515,8 @@ void DynamicAABBTreeCollisionManager::update() { DynamicAABBNode* node = it->second; node->bv = obj->getAABB(); if (node->bv.volume() <= 0.) - throw std::invalid_argument("The bounding volume has a negative volume."); + HPP_FCL_THROW_PRETTY("The bounding volume has a negative volume.", + std::invalid_argument) } dtree.refit(); diff --git a/src/broadphase/broadphase_dynamic_AABB_tree_array.cpp b/src/broadphase/broadphase_dynamic_AABB_tree_array.cpp index 2bc5cab0db2f1fb27358f0e1f11857378305a8b5..4921408372dd16c780f64029559d1f1073c36e85 100644 --- a/src/broadphase/broadphase_dynamic_AABB_tree_array.cpp +++ b/src/broadphase/broadphase_dynamic_AABB_tree_array.cpp @@ -37,7 +37,7 @@ #include "hpp/fcl/broadphase/broadphase_dynamic_AABB_tree_array.h" -#if HPP_FCL_HAVE_OCTOMAP +#ifdef HPP_FCL_HAVE_OCTOMAP #include "hpp/fcl/octree.h" #endif namespace hpp { diff --git a/src/broadphase/default_broadphase_callbacks.cpp b/src/broadphase/default_broadphase_callbacks.cpp index d76cd28ed47ed13fac8c328ee784bf93a968d24f..ea88687d4edad880639477d9205e296731907c46 100644 --- a/src/broadphase/default_broadphase_callbacks.cpp +++ b/src/broadphase/default_broadphase_callbacks.cpp @@ -112,6 +112,11 @@ CollisionCallBackCollect::getCollisionPairs() const { void CollisionCallBackCollect::init() { collision_pairs.clear(); } +bool CollisionCallBackCollect::exist(CollisionObject* o1, + CollisionObject* o2) const { + return exist(std::make_pair(o1, o2)); +} + bool CollisionCallBackCollect::exist(const CollisionPair& pair) const { return std::find(collision_pairs.begin(), collision_pairs.end(), pair) != collision_pairs.end(); diff --git a/src/collision.cpp b/src/collision.cpp index 244aee4d6f0e4d6246c7e946824fa5dc44a4fc59..52fc3ed1c43f8563c1e5c1487b9495e6ee2c0b68 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -56,6 +56,7 @@ void CollisionResult::swapObjects() { it != contacts.end(); ++it) { std::swap(it->o1, it->o2); std::swap(it->b1, it->b2); + it->nearest_points[0].swap(it->nearest_points[1]); it->normal *= -1; } } @@ -70,7 +71,7 @@ std::size_t collide(const CollisionObject* o1, const CollisionObject* o2, std::size_t collide(const CollisionGeometry* o1, const Transform3f& tf1, const CollisionGeometry* o2, const Transform3f& tf2, const CollisionRequest& request, CollisionResult& result) { - // If securit margin is set to -infinity, return that there is no collision + // If security margin is set to -infinity, return that there is no collision if (request.security_margin == -std::numeric_limits<FCL_REAL>::infinity()) { result.clear(); return false; @@ -104,6 +105,8 @@ std::size_t collide(const CollisionGeometry* o1, const Transform3f& tf1, res = looktable.collision_matrix[node_type2][node_type1]( o2, tf2, o1, tf1, &solver, request, result); result.swapObjects(); + result.nearest_points[0].swap(result.nearest_points[1]); + result.normal *= -1; } } else { if (!looktable.collision_matrix[node_type1][node_type2]) { @@ -169,9 +172,18 @@ std::size_t ComputeCollision::run(const Transform3f& tf1, if (swap_geoms) { res = func(o2, tf2, o1, tf1, &solver, request, result); result.swapObjects(); + result.nearest_points[0].swap(result.nearest_points[1]); + result.normal *= -1; } else { res = func(o1, tf1, o2, tf2, &solver, request, result); } + + if (solver.gjk_initial_guess == GJKInitialGuess::CachedGuess || + solver.enable_cached_guess) { + result.cached_gjk_guess = solver.cached_guess; + result.cached_support_func_guess = solver.support_func_cached_guess; + } + return res; } @@ -191,12 +203,6 @@ std::size_t ComputeCollision::operator()(const Transform3f& tf1, } else res = run(tf1, tf2, request, result); - if (solver.gjk_initial_guess == GJKInitialGuess::CachedGuess || - solver.enable_cached_guess) { - result.cached_gjk_guess = solver.cached_guess; - result.cached_support_func_guess = solver.support_func_cached_guess; - } - return res; } diff --git a/src/collision_func_matrix.cpp b/src/collision_func_matrix.cpp index 997c11444dc7b0b9c190286aab64239c11cef6d5..73d702afe350c73969e8b5a02a43e1820cf87be0 100644 --- a/src/collision_func_matrix.cpp +++ b/src/collision_func_matrix.cpp @@ -319,8 +319,10 @@ CollisionFunctionMatrix::CollisionFunctionMatrix() { &ShapeShapeCollide<Ellipsoid, Cylinder>; collision_matrix[GEOM_ELLIPSOID][GEOM_CONVEX] = &ShapeShapeCollide<Ellipsoid, ConvexBase>; - // TODO Louis: Ellipsoid - Plane - // TODO Louis: Ellipsoid - Halfspace + collision_matrix[GEOM_ELLIPSOID][GEOM_PLANE] = + &ShapeShapeCollide<Ellipsoid, Plane>; + collision_matrix[GEOM_ELLIPSOID][GEOM_HALFSPACE] = + &ShapeShapeCollide<Ellipsoid, Halfspace>; collision_matrix[GEOM_ELLIPSOID][GEOM_ELLIPSOID] = &ShapeShapeCollide<Ellipsoid, Ellipsoid>; @@ -403,7 +405,8 @@ CollisionFunctionMatrix::CollisionFunctionMatrix() { collision_matrix[GEOM_PLANE][GEOM_PLANE] = &ShapeShapeCollide<Plane, Plane>; collision_matrix[GEOM_PLANE][GEOM_HALFSPACE] = &ShapeShapeCollide<Plane, Halfspace>; - // TODO Louis: Ellipsoid - Plane + collision_matrix[GEOM_PLANE][GEOM_ELLIPSOID] = + &ShapeShapeCollide<Plane, Ellipsoid>; collision_matrix[GEOM_HALFSPACE][GEOM_BOX] = &ShapeShapeCollide<Halfspace, Box>; @@ -421,7 +424,8 @@ CollisionFunctionMatrix::CollisionFunctionMatrix() { &ShapeShapeCollide<Halfspace, Plane>; collision_matrix[GEOM_HALFSPACE][GEOM_HALFSPACE] = &ShapeShapeCollide<Halfspace, Halfspace>; - // TODO Louis: Ellipsoid - Halfspace + collision_matrix[GEOM_HALFSPACE][GEOM_ELLIPSOID] = + &ShapeShapeCollide<Halfspace, Ellipsoid>; collision_matrix[BV_AABB][GEOM_BOX] = &BVHShapeCollider<AABB, Box>::collide; collision_matrix[BV_AABB][GEOM_SPHERE] = @@ -657,6 +661,10 @@ CollisionFunctionMatrix::CollisionFunctionMatrix() { &OctreeCollide<OcTree, BVHModel<KDOP<18> > >; collision_matrix[GEOM_OCTREE][BV_KDOP24] = &OctreeCollide<OcTree, BVHModel<KDOP<24> > >; + collision_matrix[GEOM_OCTREE][HF_AABB] = + &OctreeCollide<OcTree, HeightField<AABB> >; + collision_matrix[GEOM_OCTREE][HF_OBBRSS] = + &OctreeCollide<OcTree, HeightField<OBBRSS> >; collision_matrix[BV_AABB][GEOM_OCTREE] = &OctreeCollide<BVHModel<AABB>, OcTree>; @@ -672,6 +680,10 @@ CollisionFunctionMatrix::CollisionFunctionMatrix() { &OctreeCollide<BVHModel<KDOP<18> >, OcTree>; collision_matrix[BV_KDOP24][GEOM_OCTREE] = &OctreeCollide<BVHModel<KDOP<24> >, OcTree>; + collision_matrix[HF_AABB][GEOM_OCTREE] = + &OctreeCollide<HeightField<AABB>, OcTree>; + collision_matrix[HF_OBBRSS][GEOM_OCTREE] = + &OctreeCollide<HeightField<OBBRSS>, OcTree>; #endif } // template struct CollisionFunctionMatrix; diff --git a/src/collision_utility.cpp b/src/collision_utility.cpp index 586f6f6352376124a8bf73a91561bf6c38900b3d..05fd42969192210617b974b995175a0c970f276b 100644 --- a/src/collision_utility.cpp +++ b/src/collision_utility.cpp @@ -60,7 +60,8 @@ CollisionGeometry* extractBVH(const CollisionGeometry* model, case BV_KDOP24: return extractBVHtpl<KDOP<24> >(model, pose, aabb); default: - throw std::runtime_error("Unknown type of bounding volume"); + HPP_FCL_THROW_PRETTY("Unknown type of bounding volume", + std::runtime_error); } } } // namespace details @@ -72,8 +73,9 @@ CollisionGeometry* extract(const CollisionGeometry* model, return details::extractBVH(model, pose, aabb); // case OT_GEOM: return model; default: - throw std::runtime_error( - "Extraction is not implemented for this type of object"); + HPP_FCL_THROW_PRETTY( + "Extraction is not implemented for this type of object", + std::runtime_error); } } } // namespace fcl diff --git a/src/distance.cpp b/src/distance.cpp index 1199cdca2aa1008d9ed24bb7fa84f322f24fd495..bc7f542bdbcf20b5c7d8d7822f0cf828c366a39c 100644 --- a/src/distance.cpp +++ b/src/distance.cpp @@ -88,9 +88,8 @@ FCL_REAL distance(const CollisionGeometry* o1, const Transform3f& tf1, const CollisionGeometry* tmpo = result.o1; result.o1 = result.o2; result.o2 = tmpo; - Vec3f tmpn(result.nearest_points[0]); - result.nearest_points[0] = result.nearest_points[1]; - result.nearest_points[1] = tmpn; + result.nearest_points[0].swap(result.nearest_points[1]); + result.normal *= -1; } } } else { @@ -153,6 +152,7 @@ FCL_REAL ComputeDistance::run(const Transform3f& tf1, const Transform3f& tf2, if (request.enable_nearest_points) { std::swap(result.o1, result.o2); result.nearest_points[0].swap(result.nearest_points[1]); + result.normal *= -1; } } else { res = func(o1, tf1, o2, tf2, &solver, request, result); diff --git a/src/distance/capsule_capsule.cpp b/src/distance/capsule_capsule.cpp index af126ce7355da4d6dcbd580dc6d96835e4bc4422..16caadd1c9fe7d13735510ede01e89e8f89afce7 100644 --- a/src/distance/capsule_capsule.cpp +++ b/src/distance/capsule_capsule.cpp @@ -80,7 +80,7 @@ template <> FCL_REAL ShapeShapeDistance<Capsule, Capsule>( const CollisionGeometry* o1, const Transform3f& tf1, const CollisionGeometry* o2, const Transform3f& tf2, const GJKSolver*, - const DistanceRequest& request, DistanceResult& result) { + const DistanceRequest& /*request*/, DistanceResult& result) { const Capsule* capsule1 = static_cast<const Capsule*>(o1); const Capsule* capsule2 = static_cast<const Capsule*>(o2); @@ -153,18 +153,15 @@ FCL_REAL ShapeShapeDistance<Capsule, Capsule>( // witness points achieving the distance between the two segments FCL_REAL distance = (w1 - w2).norm(); - const Vec3f normal = (w1 - w2) / distance; - result.normal = normal; // capsule spcecific distance computation distance = distance - (radius1 + radius2); result.min_distance = distance; - // witness points for the capsules - if (request.enable_nearest_points) { - result.nearest_points[0] = w1 - radius1 * normal; - result.nearest_points[1] = w2 + radius2 * normal; - } + // Normal points from o1 to o2 + result.normal = (w2 - w1).normalized(); + result.nearest_points[0] = w1 + radius1 * result.normal; + result.nearest_points[1] = w2 - radius2 * result.normal; return distance; } diff --git a/src/distance/ellipsoid_halfspace.cpp b/src/distance/ellipsoid_halfspace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a9088da2acad429885413ee56b715258d9dfce9 --- /dev/null +++ b/src/distance/ellipsoid_halfspace.cpp @@ -0,0 +1,85 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2021-2022, INRIA + * 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 name of Open Source Robotics Foundation 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 OWNER 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. + */ + +/** \author Louis Montaut */ + +#include <cmath> +#include <limits> +#include <hpp/fcl/math/transform.h> +#include <hpp/fcl/shape/geometric_shapes.h> + +#include <hpp/fcl/internal/shape_shape_func.h> +#include "../narrowphase/details.h" + +namespace hpp { +namespace fcl { +struct GJKSolver; + +template <> +FCL_REAL ShapeShapeDistance<Ellipsoid, Halfspace>( + const CollisionGeometry* o1, const Transform3f& tf1, + const CollisionGeometry* o2, const Transform3f& tf2, const GJKSolver*, + const DistanceRequest&, DistanceResult& result) { + const Ellipsoid& s1 = static_cast<const Ellipsoid&>(*o1); + const Halfspace& s2 = static_cast<const Halfspace&>(*o2); + details::halfspaceDistance(s2, tf2, s1, tf1, result.min_distance, + result.nearest_points[1], result.nearest_points[0], + result.normal); + result.o1 = o1; + result.o2 = o2; + result.b1 = -1; + result.b2 = -1; + result.normal = -result.normal; + return result.min_distance; +} + +template <> +FCL_REAL ShapeShapeDistance<Halfspace, Ellipsoid>( + const CollisionGeometry* o1, const Transform3f& tf1, + const CollisionGeometry* o2, const Transform3f& tf2, const GJKSolver*, + const DistanceRequest&, DistanceResult& result) { + const Halfspace& s1 = static_cast<const Halfspace&>(*o1); + const Ellipsoid& s2 = static_cast<const Ellipsoid&>(*o2); + details::halfspaceDistance(s1, tf1, s2, tf2, result.min_distance, + result.nearest_points[0], result.nearest_points[1], + result.normal); + result.o1 = o1; + result.o2 = o2; + result.b1 = -1; + result.b2 = -1; + return result.min_distance; +} +} // namespace fcl + +} // namespace hpp diff --git a/src/distance/ellipsoid_plane.cpp b/src/distance/ellipsoid_plane.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e8a4cdfb35d39b88afaf77970beef97806de679d --- /dev/null +++ b/src/distance/ellipsoid_plane.cpp @@ -0,0 +1,85 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2021-2022, INRIA + * 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 name of Open Source Robotics Foundation 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 OWNER 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. + */ + +/** \author Louis Montaut */ + +#include <cmath> +#include <limits> +#include <hpp/fcl/math/transform.h> +#include <hpp/fcl/shape/geometric_shapes.h> + +#include <hpp/fcl/internal/shape_shape_func.h> +#include "../narrowphase/details.h" + +namespace hpp { +namespace fcl { +struct GJKSolver; + +template <> +FCL_REAL ShapeShapeDistance<Ellipsoid, Plane>( + const CollisionGeometry* o1, const Transform3f& tf1, + const CollisionGeometry* o2, const Transform3f& tf2, const GJKSolver*, + const DistanceRequest&, DistanceResult& result) { + const Ellipsoid& s1 = static_cast<const Ellipsoid&>(*o1); + const Plane& s2 = static_cast<const Plane&>(*o2); + details::ellipsoidPlaneIntersect(s1, tf1, s2, tf2, result.min_distance, + result.nearest_points[0], + result.nearest_points[1], result.normal); + result.o1 = o1; + result.o2 = o2; + result.b1 = -1; + result.b2 = -1; + return result.min_distance; +} + +template <> +FCL_REAL ShapeShapeDistance<Plane, Ellipsoid>( + const CollisionGeometry* o1, const Transform3f& tf1, + const CollisionGeometry* o2, const Transform3f& tf2, const GJKSolver*, + const DistanceRequest&, DistanceResult& result) { + const Plane& s1 = static_cast<const Plane&>(*o1); + const Ellipsoid& s2 = static_cast<const Ellipsoid&>(*o2); + details::ellipsoidPlaneIntersect(s2, tf2, s1, tf1, result.min_distance, + result.nearest_points[1], + result.nearest_points[0], result.normal); + result.o1 = o1; + result.o2 = o2; + result.b1 = -1; + result.b2 = -1; + result.normal = -result.normal; + return result.min_distance; +} +} // namespace fcl + +} // namespace hpp diff --git a/src/distance/sphere_capsule.cpp b/src/distance/sphere_capsule.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ce4fee0e8494918965807817e7f06cee07346d9d --- /dev/null +++ b/src/distance/sphere_capsule.cpp @@ -0,0 +1,85 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2021-2022, CNRS + * Author: Louis Montaut + * 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 name of Open Source Robotics Foundation 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 OWNER 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. + */ + +#include <cmath> +#include <limits> +#include <hpp/fcl/math/transform.h> +#include <hpp/fcl/shape/geometric_shapes.h> + +#include <hpp/fcl/internal/shape_shape_func.h> +#include "../narrowphase/details.h" + +namespace hpp { +namespace fcl { +struct GJKSolver; + +template <> +FCL_REAL ShapeShapeDistance<Sphere, Capsule>( + const CollisionGeometry* o1, const Transform3f& tf1, + const CollisionGeometry* o2, const Transform3f& tf2, const GJKSolver*, + const DistanceRequest&, DistanceResult& result) { + const Sphere& s1 = static_cast<const Sphere&>(*o1); + const Capsule& s2 = static_cast<const Capsule&>(*o2); + details::sphereCapsuleDistance(s1, tf1, s2, tf2, result.min_distance, + result.nearest_points[0], + result.nearest_points[1], result.normal); + result.o1 = o1; + result.o2 = o2; + result.b1 = -1; + result.b2 = -1; + return result.min_distance; +} + +template <> +FCL_REAL ShapeShapeDistance<Capsule, Sphere>( + const CollisionGeometry* o1, const Transform3f& tf1, + const CollisionGeometry* o2, const Transform3f& tf2, const GJKSolver*, + const DistanceRequest&, DistanceResult& result) { + const Capsule& s1 = static_cast<const Capsule&>(*o1); + const Sphere& s2 = static_cast<const Sphere&>(*o2); + details::sphereCapsuleDistance(s2, tf2, s1, tf1, result.min_distance, + result.nearest_points[1], + result.nearest_points[0], result.normal); + result.o1 = o1; + result.o2 = o2; + result.b1 = -1; + result.b2 = -1; + result.normal = -result.normal; + return result.min_distance; +} + +} // namespace fcl + +} // namespace hpp diff --git a/src/distance/sphere_sphere.cpp b/src/distance/sphere_sphere.cpp index 13a2b960b6da10138eded24f79d11627b43e107f..17038166e161a3b63d4349b92e41feee1f274b06 100644 --- a/src/distance/sphere_sphere.cpp +++ b/src/distance/sphere_sphere.cpp @@ -74,22 +74,10 @@ FCL_REAL ShapeShapeDistance<Sphere, Sphere>( FCL_REAL dist = c1c2.norm(); Vec3f unit(0, 0, 0); if (dist > epsilon) unit = c1c2 / dist; - FCL_REAL penetrationDepth; - penetrationDepth = r1 + r2 - dist; - bool collision = (penetrationDepth >= 0); - result.min_distance = -penetrationDepth; - if (collision) { - // Take contact point at the middle of intersection between each sphere - // and segment [c1 c2]. - FCL_REAL abscissa = .5 * r1 + .5 * (dist - r2); - Vec3f contact = center1 + abscissa * unit; - result.nearest_points[0] = result.nearest_points[1] = contact; - return result.min_distance; - } else { - FCL_REAL abs1(r1), abs2(dist - r2); - result.nearest_points[0] = center1 + abs1 * unit; - result.nearest_points[1] = center1 + abs2 * unit; - } + result.min_distance = dist - (r1 + r2); + result.normal = unit; + result.nearest_points[0] = center1 + r1 * unit; + result.nearest_points[1] = center2 - r2 * unit; return result.min_distance; } @@ -101,7 +89,7 @@ std::size_t ShapeShapeCollider<Sphere, Sphere>::run( const Sphere* s1 = static_cast<const Sphere*>(o1); const Sphere* s2 = static_cast<const Sphere*>(o2); - // We assume that capsules are centered at the origin. + // We assume that spheres are centered at the origin. const fcl::Vec3f& center1 = tf1.getTranslation(); const fcl::Vec3f& center2 = tf2.getTranslation(); FCL_REAL r1 = s1->radius; @@ -110,21 +98,17 @@ std::size_t ShapeShapeCollider<Sphere, Sphere>::run( Vec3f c1c2 = center2 - center1; FCL_REAL dist = c1c2.norm(); - Vec3f unit(0, 0, 0); - if (dist > epsilon) unit = c1c2 / dist; + Vec3f normal(0, 0, 0); + if (dist > epsilon) normal = c1c2 / dist; // Unlike in distance computation, we consider the security margin. FCL_REAL distToCollision = dist - (r1 + r2 + margin); + Vec3f p1 = center1 + normal * r1; + Vec3f p2 = center2 - normal * r2; internal::updateDistanceLowerBoundFromLeaf(request, result, distToCollision, - center1 + unit * r1, - center2 - unit * r2); + p1, p2, normal); if (distToCollision <= request.collision_distance_threshold) { - // Take contact point at the middle of intersection between each sphere - // and segment [c1 c2]. - FCL_REAL abscissa = .5 * r1 + .5 * (dist - r2); - Vec3f contactPoint = center1 + abscissa * unit; - Contact contact(o1, o2, -1, -1, contactPoint, unit, - -(distToCollision + margin)); + Contact contact(o1, o2, -1, -1, p1, p2, normal, distToCollision + margin); result.addContact(contact); return 1; } diff --git a/src/distance_func_matrix.cpp b/src/distance_func_matrix.cpp index ca295473afdf05798403861284d90aec0c8606bd..8bc115da16ce9e3d50ccff0fd89a9fe1bfd892b6 100644 --- a/src/distance_func_matrix.cpp +++ b/src/distance_func_matrix.cpp @@ -66,6 +66,22 @@ FCL_REAL Distance(const CollisionGeometry* o1, const Transform3f& tf1, #endif +HPP_FCL_LOCAL FCL_REAL distance_function_not_implemented( + const CollisionGeometry* o1, const Transform3f& /*tf1*/, + const CollisionGeometry* o2, const Transform3f& /*tf2*/, + const GJKSolver* /*nsolver*/, const DistanceRequest& /*request*/, + DistanceResult& /*result*/) { + NODE_TYPE node_type1 = o1->getNodeType(); + NODE_TYPE node_type2 = o2->getNodeType(); + + HPP_FCL_THROW_PRETTY("Distance function between node type " + << std::string(get_node_type_name(node_type1)) + << " and node type " + << std::string(get_node_type_name(node_type2)) + << " is not yet supported.", + std::invalid_argument); +} + template <typename T_SH1, typename T_SH2> FCL_REAL ShapeShapeDistance(const CollisionGeometry* o1, const Transform3f& tf1, const CollisionGeometry* o2, const Transform3f& tf2, @@ -322,8 +338,10 @@ DistanceFunctionMatrix::DistanceFunctionMatrix() { &ShapeShapeDistance<Ellipsoid, Cylinder>; distance_matrix[GEOM_ELLIPSOID][GEOM_CONVEX] = &ShapeShapeDistance<Ellipsoid, ConvexBase>; - // TODO Louis: Ellipsoid - Plane - // TODO Louis: Ellipsoid - Halfspace + distance_matrix[GEOM_ELLIPSOID][GEOM_PLANE] = + &ShapeShapeDistance<Ellipsoid, Plane>; + distance_matrix[GEOM_ELLIPSOID][GEOM_HALFSPACE] = + &ShapeShapeDistance<Ellipsoid, Halfspace>; distance_matrix[GEOM_ELLIPSOID][GEOM_ELLIPSOID] = &ShapeShapeDistance<Ellipsoid, Ellipsoid>; @@ -406,7 +424,8 @@ DistanceFunctionMatrix::DistanceFunctionMatrix() { distance_matrix[GEOM_PLANE][GEOM_PLANE] = &ShapeShapeDistance<Plane, Plane>; distance_matrix[GEOM_PLANE][GEOM_HALFSPACE] = &ShapeShapeDistance<Plane, Halfspace>; - // TODO Louis: Ellipsoid - Plane + distance_matrix[GEOM_PLANE][GEOM_ELLIPSOID] = + &ShapeShapeDistance<Plane, Ellipsoid>; distance_matrix[GEOM_HALFSPACE][GEOM_BOX] = &ShapeShapeDistance<Halfspace, Box>; @@ -424,7 +443,8 @@ DistanceFunctionMatrix::DistanceFunctionMatrix() { &ShapeShapeDistance<Halfspace, Plane>; distance_matrix[GEOM_HALFSPACE][GEOM_HALFSPACE] = &ShapeShapeDistance<Halfspace, Halfspace>; - // TODO Louis: Ellipsoid - Halfspace + distance_matrix[GEOM_HALFSPACE][GEOM_ELLIPSOID] = + &ShapeShapeDistance<Halfspace, Ellipsoid>; /* AABB distance not implemented */ /* @@ -645,6 +665,8 @@ DistanceFunctionMatrix::DistanceFunctionMatrix() { &Distance<BVHModel<KDOP<18> >, OcTree>; distance_matrix[BV_KDOP24][GEOM_OCTREE] = &Distance<BVHModel<KDOP<24> >, OcTree>; + distance_matrix[HF_AABB][GEOM_OCTREE] = &distance_function_not_implemented; + distance_matrix[HF_OBBRSS][GEOM_OCTREE] = &distance_function_not_implemented; #endif } // template struct DistanceFunctionMatrix; diff --git a/src/mesh_loader/assimp.cpp b/src/mesh_loader/assimp.cpp index 634beba1f5c6973729ed8856a489896d3f75ad36..495ace6024066c5cc3cdc535d0b60242c4dd5036 100644 --- a/src/mesh_loader/assimp.cpp +++ b/src/mesh_loader/assimp.cpp @@ -89,12 +89,13 @@ void Loader::load(const std::string& resource_path) { std::string("Could not load resource ") + resource_path + std::string("\n") + importer->GetErrorString() + std::string("\n") + "Hint: the mesh directory may be wrong."); - throw std::invalid_argument(exception_message); + HPP_FCL_THROW_PRETTY(exception_message.c_str(), std::invalid_argument); } if (!scene->HasMeshes()) - throw std::invalid_argument(std::string("No meshes found in file ") + - resource_path); + HPP_FCL_THROW_PRETTY( + (std::string("No meshes found in file ") + resource_path).c_str(), + std::invalid_argument); } /** diff --git a/src/mesh_loader/loader.cpp b/src/mesh_loader/loader.cpp index 3153f1a8d2e1446dcf4f1053fd51c1faca768324..cc5543e1ea30bba687a62468a4ea44c9b909e087 100644 --- a/src/mesh_loader/loader.cpp +++ b/src/mesh_loader/loader.cpp @@ -85,7 +85,8 @@ BVHModelPtr_t MeshLoader::load(const std::string& filename, case BV_KDOP24: return _load<KDOP<24> >(filename, scale); default: - throw std::invalid_argument("Unhandled bouding volume type."); + HPP_FCL_THROW_PRETTY("Unhandled bouding volume type.", + std::invalid_argument); } } @@ -94,8 +95,9 @@ CollisionGeometryPtr_t MeshLoader::loadOctree(const std::string& filename) { shared_ptr<octomap::OcTree> octree(new octomap::OcTree(filename)); return CollisionGeometryPtr_t(new hpp::fcl::OcTree(octree)); #else - throw std::logic_error( - "hpp-fcl compiled without OctoMap. Cannot create OcTrees."); + HPP_FCL_THROW_PRETTY( + "hpp-fcl compiled without OctoMap. Cannot create OcTrees.", + std::logic_error); #endif } diff --git a/src/narrowphase/details.h b/src/narrowphase/details.h index bcc3b218da988845cf2ba9c0bb70a102b87f6d9f..7baed8c280f4e02f940e5e19cde668cf265ceede 100644 --- a/src/narrowphase/details.h +++ b/src/narrowphase/details.h @@ -4,6 +4,7 @@ * Copyright (c) 2011-2014, Willow Garage, Inc. * Copyright (c) 2014-2015, Open Source Robotics Foundation * Copyright (c) 2018-2019, Centre National de la Recherche Scientifique + * Copyright (c) 2021-2022, INRIA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -123,7 +124,6 @@ inline bool sphereCapsuleDistance(const Sphere& s1, const Transform3f& tf1, p2 = segment_point - normal * s2.radius; if (dist <= 0) { - p1 = p2 = .5 * (p1 + p2); return false; } return true; @@ -179,23 +179,19 @@ inline bool sphereCylinderDistance(const Sphere& s1, const Transform3f& tf1, assert(fabs(dist) - (p1 - p2).norm() < eps); } else { // Center of sphere is on cylinder boundary - normal = .5 * (A + B) - p2; + normal = p2 - .5 * (A + B); + assert(u.dot(normal) >= 0); normal.normalize(); - p1 = p2; dist = -r1; + p1 = S + r1 * normal; } } } else if (s <= (s2.halfLength * 2)) { // 0 < s <= s2.lz normal = -v; dist = dPS - r1 - r2; - if (dPS <= r2) { - // Sphere center is inside cylinder - p1 = p2 = S; - } else { - p2 = P + r2 * v; - p1 = S - r1 * v; - } + p2 = P + r2 * v; + p1 = S - r1 * v; } else { // lz < s if (dPS <= r2) { @@ -216,16 +212,13 @@ inline bool sphereCylinderDistance(const Sphere& s1, const Transform3f& tf1, assert(fabs(dist) - (p1 - p2).norm() < eps); } else { // Center of sphere is on cylinder boundary - normal = .5 * (A + B) - p2; + normal = p2 - .5 * (A + B); normal.normalize(); - p1 = p2; + p1 = S + r1 * normal; dist = -r1; } } } - if (dist < 0) { - p1 = p2 = .5 * (p1 + p2); - } return (dist > 0); } @@ -1384,15 +1377,13 @@ inline bool sphereHalfspaceIntersect(const Sphere& s1, const Transform3f& tf1, Halfspace new_s2 = transform(s2, tf2); const Vec3f& center = tf1.getTranslation(); distance = new_s2.signedDistance(center) - s1.radius; - if (distance <= 0) { - normal = -new_s2.n; // pointing from s1 to s2 - p1 = p2 = center - new_s2.n * s1.radius - (distance * 0.5) * new_s2.n; - return true; - } else { - p1 = center - s1.radius * new_s2.n; - p2 = p1 - distance * new_s2.n; - return false; - } + normal = -new_s2.n; // pointing from s1 to s2 + p1 = center - s1.radius * new_s2.n; + p2 = p1 - distance * new_s2.n; + assert(new_s2.distance(p2) < + 3 * sqrt(std::numeric_limits<FCL_REAL>::epsilon())); + if (distance > 0) return false; + return true; } /// @brief box half space, a, b, c = +/- edge size @@ -1419,6 +1410,10 @@ inline bool boxHalfspaceIntersect(const Box& s1, const Transform3f& tf1, const Halfspace& s2, const Transform3f& tf2, FCL_REAL& distance, Vec3f& p1, Vec3f& p2, Vec3f& normal) { + // TODO: when witness face of box is parallel to the plane/halfspace, + // we may want to return more than one contact point. For example, + // we can return the four corners of the box. + static const FCL_REAL eps(sqrt(std::numeric_limits<FCL_REAL>::epsilon())); Halfspace new_s2 = transform(s2, tf2); const Matrix3f& R = tf1.getRotation(); @@ -1429,37 +1424,25 @@ inline bool boxHalfspaceIntersect(const Box& s1, const Transform3f& tf1, // A: scalar products of each side with normal const Vec3f A(Q.cwiseProduct(s1.halfSide)); + normal = -new_s2.n; distance = new_s2.signedDistance(T) - A.lpNorm<1>(); + + // compute p1 -> point of the box deepest inside the hyperplane + p1 = T; + for (Vec3f::Index i = 0; i < 3; ++i) { + // scalar product between box axis and hyperplane normal + FCL_REAL alpha(R.col(i).dot(new_s2.n)); + if (alpha > eps) { + p1 -= R.col(i) * s1.halfSide[i]; + } else if (alpha < -eps) { + p1 += R.col(i) * s1.halfSide[i]; + } + } + p2 = p1 - distance * new_s2.n; + assert(new_s2.signedDistance(p2) < 3 * eps); if (distance > 0) { - p1.noalias() = T + R * (A.array() > 0).select(s1.halfSide, -s1.halfSide); - p2.noalias() = p1 - distance * new_s2.n; return false; } - - /// find deepest point - Vec3f p(T); - int sign = 0; - - if (std::abs(Q[0] - 1) < halfspaceIntersectTolerance<FCL_REAL>() || - std::abs(Q[0] + 1) < halfspaceIntersectTolerance<FCL_REAL>()) { - sign = (A[0] > 0) ? -1 : 1; - p += R.col(0) * (s1.halfSide[0] * sign); - } else if (std::abs(Q[1] - 1) < halfspaceIntersectTolerance<FCL_REAL>() || - std::abs(Q[1] + 1) < halfspaceIntersectTolerance<FCL_REAL>()) { - sign = (A[1] > 0) ? -1 : 1; - p += R.col(1) * (s1.halfSide[1] * sign); - } else if (std::abs(Q[2] - 1) < halfspaceIntersectTolerance<FCL_REAL>() || - std::abs(Q[2] + 1) < halfspaceIntersectTolerance<FCL_REAL>()) { - sign = (A[2] > 0) ? -1 : 1; - p += R.col(2) * (s1.halfSide[2] * sign); - } else { - p.noalias() += R * (A.array() > 0).select(-s1.halfSide, s1.halfSide); - } - - /// compute the contact point from the deepest point - normal = -new_s2.n; - p1 = p2 = p - new_s2.n * (distance * 0.5); - return true; } @@ -1468,6 +1451,9 @@ inline bool capsuleHalfspaceIntersect(const Capsule& s1, const Transform3f& tf1, const Transform3f& tf2, FCL_REAL& distance, Vec3f& p1, Vec3f& p2, Vec3f& normal) { + static const FCL_REAL eps(sqrt(std::numeric_limits<FCL_REAL>::epsilon())); + HPP_FCL_ONLY_USED_FOR_DEBUG(eps); + Halfspace new_s2 = transform(s2, tf2); const Matrix3f& R = tf1.getRotation(); @@ -1475,19 +1461,22 @@ inline bool capsuleHalfspaceIntersect(const Capsule& s1, const Transform3f& tf1, Vec3f dir_z = R.col(2); + normal = -new_s2.n; FCL_REAL cosa = dir_z.dot(new_s2.n); if (std::abs(cosa) < halfspaceIntersectTolerance<FCL_REAL>()) { // Capsule parallel to plane + // TODO: here we are returning p1 as the middle of the capsule's segment (+ + // radius) but we might want to return both ends of the capsule instead. For + // that we need to implement the possibility of returning multiple contact + // points. FCL_REAL signed_dist = new_s2.signedDistance(T); distance = signed_dist - s1.radius; + p1 = T - s1.radius * new_s2.n; + p2 = p1 - distance * new_s2.n; + assert(new_s2.distance(p2) < 3 * eps); if (distance > 0) { - p1 = T - s1.radius * new_s2.n; - p2 = p1 - distance * new_s2.n; return false; } - - normal = -new_s2.n; - p1 = p2 = T + new_s2.n * (-0.5 * distance - s1.radius); return true; } else { int sign = (cosa > 0) ? -1 : 1; @@ -1497,15 +1486,12 @@ inline bool capsuleHalfspaceIntersect(const Capsule& s1, const Transform3f& tf1, FCL_REAL signed_dist = new_s2.signedDistance(p); distance = signed_dist - s1.radius; + p1 = p - s1.radius * new_s2.n; + p2 = p1 - distance * new_s2.n; + assert(new_s2.distance(p2) < 3 * eps); if (distance > 0) { - p1 = T - s1.radius * new_s2.n; - p2 = p1 - distance * new_s2.n; return false; } - normal = -new_s2.n; - // deepest point - Vec3f c = p - new_s2.n * s1.radius; - p1 = p2 = c - (0.5 * distance) * new_s2.n; return true; } } @@ -1524,17 +1510,19 @@ inline bool cylinderHalfspaceIntersect(const Cylinder& s1, Vec3f dir_z = R.col(2); FCL_REAL cosa = dir_z.dot(new_s2.n); - if (cosa < halfspaceIntersectTolerance<FCL_REAL>()) { + normal = -new_s2.n; + if (std::abs(cosa) < halfspaceIntersectTolerance<FCL_REAL>()) { + // Cylinder is parallel to plane. + // TODO: we might want to return more than one contact point in this case. FCL_REAL signed_dist = new_s2.signedDistance(T); distance = signed_dist - s1.radius; + p1 = T - s1.radius * new_s2.n; + p2 = p1 - distance * new_s2.n; + assert(new_s2.distance(p2) <= + 3 * sqrt(std::numeric_limits<FCL_REAL>::epsilon())); if (distance > 0) { - // TODO: compute closest points - p1 = p2 = Vec3f(0, 0, 0); return false; } - - normal = -new_s2.n; - p1 = p2 = T - (0.5 * distance + s1.radius) * new_s2.n; return true; } else { Vec3f C = dir_z * cosa - new_s2.n; @@ -1549,15 +1537,14 @@ inline bool cylinderHalfspaceIntersect(const Cylinder& s1, int sign = (cosa > 0) ? -1 : 1; // deepest point - Vec3f p = T + dir_z * (s1.halfLength * sign) + C; - distance = new_s2.signedDistance(p); + p1 = T + dir_z * (s1.halfLength * sign) + C; + distance = new_s2.signedDistance(p1); + p2 = p1 - distance * new_s2.n; + assert(new_s2.distance(p2) <= + 3 * sqrt(std::numeric_limits<FCL_REAL>::epsilon())); if (distance > 0) { - // TODO: compute closest points - p1 = p2 = Vec3f(0, 0, 0); return false; } else { - normal = -new_s2.n; - p1 = p2 = p - (0.5 * distance) * new_s2.n; return true; } } @@ -1575,16 +1562,18 @@ inline bool coneHalfspaceIntersect(const Cone& s1, const Transform3f& tf1, Vec3f dir_z = R.col(2); FCL_REAL cosa = dir_z.dot(new_s2.n); - if (cosa < halfspaceIntersectTolerance<FCL_REAL>()) { + normal = -new_s2.n; + if (std::abs(cosa) < halfspaceIntersectTolerance<FCL_REAL>()) { + // cone axis is parallel to plane FCL_REAL signed_dist = new_s2.signedDistance(T); distance = signed_dist - s1.radius; + p1 = T - dir_z * (s1.halfLength) - new_s2.n * s1.radius; + p2 = p1 - distance * new_s2.n; + assert(new_s2.distance(p2) <= + 3 * sqrt(std::numeric_limits<FCL_REAL>::epsilon())); if (distance > 0) { - p1 = p2 = Vec3f(0, 0, 0); return false; } else { - normal = -new_s2.n; - p1 = p2 = - T - dir_z * (s1.halfLength) - new_s2.n * (0.5 * distance + s1.radius); return true; } } else { @@ -1604,12 +1593,17 @@ inline bool coneHalfspaceIntersect(const Cone& s1, const Transform3f& tf1, FCL_REAL d1 = new_s2.signedDistance(a1); FCL_REAL d2 = new_s2.signedDistance(a2); + distance = std::min(d1, d2); + // TODO: when d1 == d2, we may want to return more than one contact point. + // There is an infinite amount but we may want to return a1 and a2 for + // example. + p1 = ((d1 < d2) ? a1 : a2); + p2 = p1 - distance * new_s2.n; + assert(new_s2.distance(p2) <= + 3 * sqrt(std::numeric_limits<FCL_REAL>::epsilon())); if (d1 > 0 && d2 > 0) return false; else { - distance = std::min(d1, d2); - normal = -new_s2.n; - p1 = p2 = ((d1 < d2) ? a1 : a2) - (0.5 * distance) * new_s2.n; return true; } } @@ -1624,8 +1618,9 @@ inline bool convexHalfspaceIntersect( Vec3f v; FCL_REAL depth = (std::numeric_limits<FCL_REAL>::max)(); + const std::vector<Vec3f>& points_ = *(s1.points); for (unsigned int i = 0; i < s1.num_points; ++i) { - Vec3f p = tf1.transform(s1.points[i]); + Vec3f p = tf1.transform(points_[i]); FCL_REAL d = new_s2.signedDistance(p); if (d < depth) { @@ -1673,18 +1668,17 @@ inline bool halfspaceTriangleIntersect(const Halfspace& s1, depth = d; v = p; } + // v is the vertex with minimal projection abscissa (depth) on normal to // plane, distance = depth; + normal = new_s1.n; + p1 = v - depth * normal; + p2 = v; if (depth <= 0) { - normal = new_s1.n; - p1 = p2 = v - (0.5 * depth) * new_s1.n; return true; - } else { - p1 = v - depth * new_s1.n; - p2 = v; - return false; } + return false; } /// @brief return whether plane collides with halfspace @@ -1810,13 +1804,15 @@ inline bool halfspaceDistance(const Halfspace& h, const Transform3f& tf1, FCL_REAL& dist, Vec3f& p1, Vec3f& p2, Vec3f& normal) { Vec3f n_w = tf1.getRotation() * h.n; + FCL_REAL d_w = h.d + n_w.dot(tf1.getTranslation()); Vec3f n_2(tf2.getRotation().transpose() * n_w); int hint = 0; p2 = getSupport(&s, -n_2, true, hint); - p2 = tf2.transform(p2); + p2.noalias() = tf2.transform(p2); - dist = (p2 - tf1.getTranslation()).dot(n_w) - h.d; + dist = p2.dot(n_w) - d_w; p1 = p2 - dist * n_w; + assert(std::abs(p1.dot(n_w) - d_w) <= 1e-8); normal = n_w; return dist <= 0; @@ -1848,23 +1844,16 @@ inline bool spherePlaneIntersect(const Sphere& s1, const Transform3f& tf1, const Vec3f& center = tf1.getTranslation(); FCL_REAL signed_dist = new_s2.signedDistance(center); distance = std::abs(signed_dist) - s1.radius; - if (distance <= 0) { - if (signed_dist > 0) - normal = -new_s2.n; - else - normal = new_s2.n; - p1 = p2 = center - new_s2.n * signed_dist; - return true; - } else { - if (signed_dist > 0) { - p1 = center - s1.radius * new_s2.n; - p2 = center - signed_dist * new_s2.n; - } else { - p1 = center + s1.radius * new_s2.n; - p2 = center + signed_dist * new_s2.n; - } - return false; - } + if (signed_dist > 0) + normal = -new_s2.n; + else + normal = new_s2.n; + p1 = center + s1.radius * normal; + p2 = p1 + distance * normal; + assert(new_s2.distance(p2) < + 3 * sqrt(std::numeric_limits<FCL_REAL>::epsilon())); + if (distance > 0) return false; + return false; } /// @brief box half space, a, b, c = +/- edge size @@ -1893,66 +1882,39 @@ inline bool boxPlaneIntersect(const Box& s1, const Transform3f& tf1, const Vec3f A(Q.cwiseProduct(s1.halfSide)); const FCL_REAL signed_dist = new_s2.signedDistance(T); - distance = std::abs(signed_dist) - A.lpNorm<1>(); - if (distance > 0) { - // Is the box above or below the plane - const bool positive = signed_dist > 0; - // Set p1 at the center of the box - p1 = T; - for (Vec3f::Index i = 0; i < 3; ++i) { - // scalar product between box axis and plane normal - FCL_REAL alpha((positive ? 1 : -1) * R.col(i).dot(new_s2.n)); - if (alpha > eps) { - p1 -= R.col(i) * s1.halfSide[i]; - } else if (alpha < -eps) { - p1 += R.col(i) * s1.halfSide[i]; - } - } - p2 = p1 - (positive ? distance : -distance) * new_s2.n; - assert(new_s2.distance(p2) < 3 * eps); - return false; - } - - // find the deepest point - Vec3f p = T; - - // when center is on the positive side of the plane, use a, b, c - // make (R^T n) (a v1 + b v2 + c v3) the minimum - // otherwise, use a, b, c make (R^T n) (a v1 + b v2 + c v3) the maximum - int sign = (signed_dist > 0) ? 1 : -1; - - if (std::abs(Q[0] - 1) < planeIntersectTolerance<FCL_REAL>() || - std::abs(Q[0] + 1) < planeIntersectTolerance<FCL_REAL>()) { - int sign2 = (A[0] > 0) ? -sign : sign; - p.noalias() += R.col(0) * (s1.halfSide[0] * sign2); - } else if (std::abs(Q[1] - 1) < planeIntersectTolerance<FCL_REAL>() || - std::abs(Q[1] + 1) < planeIntersectTolerance<FCL_REAL>()) { - int sign2 = (A[1] > 0) ? -sign : sign; - p.noalias() += R.col(1) * (s1.halfSide[1] * sign2); - } else if (std::abs(Q[2] - 1) < planeIntersectTolerance<FCL_REAL>() || - std::abs(Q[2] + 1) < planeIntersectTolerance<FCL_REAL>()) { - int sign2 = (A[2] > 0) ? -sign : sign; - p.noalias() += R.col(2) * (s1.halfSide[2] * sign2); - } else { - Vec3f tmp(sign * R * s1.halfSide); - p.noalias() += (A.array() > 0).select(-tmp, tmp); - } - - // compute the contact point by project the deepest point onto the plane if (signed_dist > 0) normal = -new_s2.n; else normal = new_s2.n; - p1 = p2.noalias() = p - new_s2.n * new_s2.signedDistance(p); - + distance = std::abs(signed_dist) - A.lpNorm<1>(); + // Is the box above or below the plane + const bool positive = signed_dist > 0; + // TODO: if the plane is parallel to a face of the box, + // we should not return only one corner as witness point p1. + // Compute p1, point of the box deepest in the plane + p1 = T; + for (Vec3f::Index i = 0; i < 3; ++i) { + // scalar product between box axis and plane normal + // we also take into account which side of the plane the box is + FCL_REAL alpha((positive ? 1 : -1) * R.col(i).dot(new_s2.n)); + if (alpha > eps) { + p1 -= R.col(i) * s1.halfSide[i]; + } else if (alpha < -eps) { + p1 += R.col(i) * s1.halfSide[i]; + } + } + p2 = p1 - (positive ? distance : -distance) * new_s2.n; + assert(new_s2.distance(p2) < 3 * eps); + if (distance > 0) { + return false; + } return true; } /// Taken from book Real Time Collision Detection, from Christer Ericson -/// @param pb the closest point to the sphere center on the box surface -/// @param ps when colliding, matches pb, which is inside the sphere. -/// when not colliding, the closest point on the sphere -/// @param normal direction of motion of the box +/// @param pb the witness point on the box surface +/// @param ps the witness point on the sphere. +/// @param normal pointing from box to sphere /// @return true if the distance is negative (the shape overlaps). inline bool boxSphereDistance(const Box& b, const Transform3f& tfb, const Sphere& s, const Transform3f& tfs, @@ -1997,11 +1959,12 @@ inline bool boxSphereDistance(const Box& b, const Transform3f& tfb, normal = -Rb.col(axis); dist = -min_d - s.radius; } + ps = os - s.radius * normal; if (!outside || dist <= 0) { - ps = pb; + // project point pb onto the box's surface + pb = ps - dist * normal; return true; } else { - ps = os - s.radius * normal; return false; } } @@ -2017,85 +1980,79 @@ inline bool capsulePlaneIntersect(const Capsule& s1, const Transform3f& tf1, const Vec3f& T1 = tf1.getTranslation(); Vec3f dir_z = R1.col(2); + FCL_REAL cosa = dir_z.dot(new_s2.n); - // ends of capsule inner segment - Vec3f a1 = T1 + dir_z * s1.halfLength; - Vec3f a2 = T1 - dir_z * s1.halfLength; - - FCL_REAL d1 = new_s2.signedDistance(a1); - FCL_REAL d2 = new_s2.signedDistance(a2); - - FCL_REAL abs_d1 = std::abs(d1); - FCL_REAL abs_d2 = std::abs(d2); - - // two end points on different side of the plane - // the contact point is the intersect of axis with the plane - // the normal is the direction to avoid intersection - // the depth is the minimum distance to resolve the collision - if (d1 * d2 < -planeIntersectTolerance<FCL_REAL>()) { - if (abs_d1 < abs_d2) { - distance = -abs_d1 - s1.radius; - p1 = p2 = - a1 * (abs_d2 / (abs_d1 + abs_d2)) + a2 * (abs_d1 / (abs_d1 + abs_d2)); - if (d1 < 0) - normal = -new_s2.n; - else - normal = new_s2.n; - } else { - distance = -abs_d2 - s1.radius; - p1 = p2 = - a1 * (abs_d2 / (abs_d1 + abs_d2)) + a2 * (abs_d1 / (abs_d1 + abs_d2)); - if (d2 < 0) - normal = -new_s2.n; - else - normal = new_s2.n; - } - assert(!p1.hasNaN() && !p2.hasNaN()); - return true; - } - - if (abs_d1 > s1.radius && abs_d2 > s1.radius) { - // Here both capsule ends are on the same side of the plane - if (d1 > 0) + if (std::abs(cosa) < planeIntersectTolerance<FCL_REAL>()) { + // The capsule axis is parallel to the plane. + FCL_REAL d = new_s2.signedDistance(T1); + distance = std::abs(d) - s1.radius; + if (d < 0) normal = new_s2.n; else normal = -new_s2.n; - if (abs_d1 < abs_d2) { - distance = abs_d1 - s1.radius; - p1 = a1 - s1.radius * normal; - p2 = p1 - distance * normal; - } else { - distance = abs_d2 - s1.radius; - p1 = a2 - s1.radius * normal; - p2 = p1 - distance * normal; + p1 = T1 + s1.radius * normal; + p2 = p1 + distance * normal; + assert(new_s2.distance(p2) <= + 3 * sqrt(std::numeric_limits<FCL_REAL>::epsilon())); + if (distance > 0) + return false; + else { + return true; } - assert(!p1.hasNaN() && !p2.hasNaN()); - return false; } else { - // Both capsule ends are on the same side of the plane, but one - // is closer than the capsule radius, hence collision - distance = std::min(abs_d1, abs_d2) - s1.radius; - - if (abs_d1 <= s1.radius && abs_d2 <= s1.radius) { - Vec3f c1 = a1 - new_s2.n * d1; - Vec3f c2 = a2 - new_s2.n * d2; - p1 = p2 = (c1 + c2) * 0.5; - } else if (abs_d1 <= s1.radius) { - Vec3f c = a1 - new_s2.n * d1; - p1 = p2 = c; - } else if (abs_d2 <= s1.radius) { - Vec3f c = a2 - new_s2.n * d2; - p1 = p2 = c; + // ends of capsule inner segment + Vec3f a1 = T1 + dir_z * s1.halfLength; + Vec3f a2 = T1 - dir_z * s1.halfLength; + + FCL_REAL d1 = new_s2.signedDistance(a1); + FCL_REAL d2 = new_s2.signedDistance(a2); + + FCL_REAL abs_d1 = std::abs(d1); + FCL_REAL abs_d2 = std::abs(d2); + + if (d1 * d2 <= 0) { + // Two end points of the capsule are on different sides of the plane. + if (abs_d1 < abs_d2) { + distance = -abs_d1 - s1.radius; + if (d1 > 0) + normal = new_s2.n; + else + normal = -new_s2.n; + p1 = a1 + s1.radius * normal; + } else { + distance = -abs_d2 - s1.radius; + if (d2 > 0) + normal = new_s2.n; + else + normal = -new_s2.n; + p1 = a2 + s1.radius * normal; + } + assert(distance <= 0); + p2 = p1 + distance * normal; + assert(new_s2.distance(p2) <= + 3 * sqrt(std::numeric_limits<FCL_REAL>::epsilon())); + assert(!p1.hasNaN() && !p2.hasNaN()); + return true; } else { - assert(false); - } + // Both capsule ends are on the same side of the plane. + distance = std::min(abs_d1, abs_d2) - s1.radius; + if (d1 < 0) + normal = new_s2.n; + else + normal = -new_s2.n; - if (d1 < 0) - normal = new_s2.n; - else - normal = -new_s2.n; - assert(!p1.hasNaN() && !p2.hasNaN()); - return true; + if (abs_d1 <= abs_d2) { + p1 = a1 + s1.radius * normal; + } else { + p1 = a2 + s1.radius * normal; + } + p2 = p1 + distance * normal; + assert(new_s2.distance(p2) <= + 3 * sqrt(std::numeric_limits<FCL_REAL>::epsilon())); + assert(!p1.hasNaN() && !p2.hasNaN()); + if (distance > 0) return true; + return false; + } } assert(false); } @@ -2121,14 +2078,17 @@ inline bool cylinderPlaneIntersect(const Cylinder& s1, const Transform3f& tf1, if (std::abs(cosa) < planeIntersectTolerance<FCL_REAL>()) { FCL_REAL d = new_s2.signedDistance(T); distance = std::abs(d) - s1.radius; + if (d < 0) + normal = new_s2.n; + else + normal = -new_s2.n; + p1 = T + s1.radius * normal; + p2 = p1 + distance * normal; + assert(new_s2.distance(p2) <= + 3 * sqrt(std::numeric_limits<FCL_REAL>::epsilon())); if (distance > 0) return false; else { - if (d < 0) - normal = new_s2.n; - else - normal = -new_s2.n; - p1 = p2 = T - new_s2.n * d; return true; } } else { @@ -2156,29 +2116,31 @@ inline bool cylinderPlaneIntersect(const Cylinder& s1, const Transform3f& tf1, FCL_REAL d1 = new_s2.signedDistance(c1); FCL_REAL d2 = new_s2.signedDistance(c2); - - if (d1 * d2 <= 0) { - FCL_REAL abs_d1 = std::abs(d1); - FCL_REAL abs_d2 = std::abs(d2); - - if (abs_d1 > abs_d2) { - distance = -abs_d2; - p1 = p2 = c2 - new_s2.n * d2; - if (d2 < 0) - normal = -new_s2.n; - else - normal = new_s2.n; - } else { - distance = -abs_d1; - p1 = p2 = c1 - new_s2.n * d1; - if (d1 < 0) - normal = -new_s2.n; - else - normal = new_s2.n; - } - return true; - } else - return false; + FCL_REAL abs_d1 = std::abs(d1); + FCL_REAL abs_d2 = std::abs(d2); + bool intersect = (d1 * d2 <= 0); + FCL_REAL sign = (intersect ? -1. : 1.); + + if (abs_d1 > abs_d2) { + distance = (intersect ? -1. : 1.) * abs_d2; + p1 = c2; + if (d2 < 0) + normal = sign * new_s2.n; + else + normal = -sign * new_s2.n; + } else { + distance = (intersect ? -1. : 1.) * abs_d1; + p1 = c1; + if (d1 < 0) + normal = sign * new_s2.n; + else + normal = -sign * new_s2.n; + } + p2 = p1 + distance * normal; + assert(new_s2.distance(p2) <= + 3 * sqrt(std::numeric_limits<FCL_REAL>::epsilon())); + if (distance > 0) return false; + return true; } } @@ -2197,16 +2159,17 @@ inline bool conePlaneIntersect(const Cone& s1, const Transform3f& tf1, if (std::abs(cosa) < planeIntersectTolerance<FCL_REAL>()) { FCL_REAL d = new_s2.signedDistance(T); distance = std::abs(d) - s1.radius; + if (d > 0) + normal = -new_s2.n; + else + normal = new_s2.n; + p1 = T - dir_z * s1.halfLength + s1.radius * normal; + p2 = p1 + distance * normal; + assert(new_s2.distance(p2) <= + 3 * sqrt(std::numeric_limits<FCL_REAL>::epsilon())); if (distance > 0) { - p1 = p2 = Vec3f(0, 0, 0); return false; } else { - if (d < 0) - normal = new_s2.n; - else - normal = -new_s2.n; - p1 = p2 = T - dir_z * (s1.halfLength) + - dir_z * (-distance / s1.radius * s1.halfLength) - new_s2.n * d; return true; } } else { @@ -2220,6 +2183,10 @@ inline bool conePlaneIntersect(const Cone& s1, const Transform3f& tf1, C *= s; } + // We are looking for the point of the cone deepest into the plane, + // on one side or the other. + // There are 3 candidates: the tip of the cone and the 2 points of the + // cone's circle opposite to each other aligned with axis C. Vec3f c[3]; c[0] = T + dir_z * (s1.halfLength); c[1] = T - dir_z * (s1.halfLength) + C; @@ -2231,65 +2198,64 @@ inline bool conePlaneIntersect(const Cone& s1, const Transform3f& tf1, d[2] = new_s2.signedDistance(c[2]); if ((d[0] >= 0 && d[1] >= 0 && d[2] >= 0) || - (d[0] <= 0 && d[1] <= 0 && d[2] <= 0)) + (d[0] <= 0 && d[1] <= 0 && d[2] <= 0)) { + // All three candidate points on the cone are on the same side of the + // plane. + bool positive = (d[0] >= 0); + FCL_REAL sign = (positive ? 1. : -1.); + normal = -sign * new_s2.n; + + distance = sign * d[0]; + // p1 is the point of the cone closest to the plane. + int index_closest = 0; + for (int i = 1; i < 3; ++i) { + if (distance >= sign * d[i]) { + distance = sign * d[i]; + index_closest = i; + } + } + assert(distance >= 0); + p1 = c[index_closest]; + p2 = p1 + distance * normal; + assert(new_s2.distance(p2) <= + 3 * sqrt(std::numeric_limits<FCL_REAL>::epsilon())); return false; - else { + } else { + // The cone is overlapping the plane. + // We look for the 2 points on each side of the plane which have the + // deepest penetration. + // Out of these 2 points, p1 is the one with the smallest penetration. bool positive[3]; for (std::size_t i = 0; i < 3; ++i) positive[i] = (d[i] >= 0); - int n_positive = 0; FCL_REAL d_positive = 0, d_negative = 0; - for (std::size_t i = 0; i < 3; ++i) { + int index_positive(0), index_negative(0); + for (int i = 0; i < 3; ++i) { if (positive[i]) { - n_positive++; - if (d_positive <= d[i]) d_positive = d[i]; + if (d_positive <= d[i]) { + d_positive = d[i]; + index_positive = i; + } } else { - if (d_negative <= -d[i]) d_negative = -d[i]; + if (d_negative <= -d[i]) { + d_negative = -d[i]; + index_negative = i; + } } } distance = -std::min(d_positive, d_negative); - if (d_positive > d_negative) + assert(distance <= 0); + if (d_positive > d_negative) { normal = -new_s2.n; - else - normal = new_s2.n; - Vec3f p[2]; - Vec3f q; - - FCL_REAL p_d[2]; - FCL_REAL q_d(0); - - if (n_positive == 2) { - for (std::size_t i = 0, j = 0; i < 3; ++i) { - if (positive[i]) { - p[j] = c[i]; - p_d[j] = d[i]; - j++; - } else { - q = c[i]; - q_d = d[i]; - } - } - - Vec3f t1 = (-p[0] * q_d + q * p_d[0]) / (-q_d + p_d[0]); - Vec3f t2 = (-p[1] * q_d + q * p_d[1]) / (-q_d + p_d[1]); - p1 = p2 = (t1 + t2) * 0.5; + p1 = c[index_negative]; } else { - for (std::size_t i = 0, j = 0; i < 3; ++i) { - if (!positive[i]) { - p[j] = c[i]; - p_d[j] = d[i]; - j++; - } else { - q = c[i]; - q_d = d[i]; - } - } - - Vec3f t1 = (p[0] * q_d - q * p_d[0]) / (q_d - p_d[0]); - Vec3f t2 = (p[1] * q_d - q * p_d[1]) / (q_d - p_d[1]); - p1 = p2 = (t1 + t2) * 0.5; + normal = new_s2.n; + p1 = c[index_positive]; } + p2 = p1 + distance * normal; + assert(new_s2.distance(p2) <= + 3 * sqrt(std::numeric_limits<FCL_REAL>::epsilon())); } return true; } @@ -2305,8 +2271,9 @@ inline bool convexPlaneIntersect(const ConvexBase& s1, const Transform3f& tf1, FCL_REAL d_min = (std::numeric_limits<FCL_REAL>::max)(), d_max = -(std::numeric_limits<FCL_REAL>::max)(); + const std::vector<Vec3f>& points_ = *(s1.points); for (unsigned int i = 0; i < s1.num_points; ++i) { - Vec3f p = tf1.transform(s1.points[i]); + Vec3f p = tf1.transform(points_[i]); FCL_REAL d = new_s2.signedDistance(p); @@ -2507,6 +2474,30 @@ inline FCL_REAL computePenetration(const Vec3f& P1, const Vec3f& P2, return computePenetration(globalP1, globalP2, globalP3, globalQ1, globalQ2, globalQ3, normal); } + +inline bool ellipsoidPlaneIntersect(const Ellipsoid& s1, const Transform3f& tf1, + const Plane& s2, const Transform3f& tf2, + FCL_REAL& distance, Vec3f& p1, Vec3f& p2, + Vec3f& normal) { + Vec3f n_w = tf2.getRotation() * s2.n; + FCL_REAL d_w = s2.d + n_w.dot(tf2.getTranslation()); + + const Vec3f& center = tf1.getTranslation(); + FCL_REAL signed_dist = n_w.dot(center) - d_w; + if (signed_dist > 0) + normal = -n_w; + else { + normal = n_w; + d_w = s2.d - n_w.dot(tf2.getTranslation()); + } + int hint = 0; + p1 = getSupport(&s1, tf1.getRotation().transpose() * normal, true, hint); + p1.noalias() = tf1.transform(p1); + distance = -normal.dot(p1) - d_w; + p2 = p1 + distance * normal; + assert(std::abs(-normal.dot(p2) - d_w) <= 1e-8); + return distance <= 0; +} } // namespace details } // namespace fcl } // namespace hpp diff --git a/src/narrowphase/gjk.cpp b/src/narrowphase/gjk.cpp index 4d4b1374c97332879cb4f70091c3dfc24cdff783..c40fd5364b21795ad42bb48e2af0351869a31962 100644 --- a/src/narrowphase/gjk.cpp +++ b/src/narrowphase/gjk.cpp @@ -3,6 +3,7 @@ * * Copyright (c) 2011-2014, Willow Garage, Inc. * Copyright (c) 2014-2015, Open Source Robotics Foundation + * Copyright (c) 2021-2022, INRIA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,6 +36,7 @@ /** \author Jia Pan */ +#include "hpp/fcl/shape/geometric_shapes.h" #include <hpp/fcl/narrowphase/gjk.h> #include <hpp/fcl/internal/intersect.h> #include <hpp/fcl/internal/tools.h> @@ -171,11 +173,11 @@ void getShapeSupportLog(const ConvexBase* convex, const Vec3f& dir, MinkowskiDiff::ShapeData* data) { assert(data != NULL); - const Vec3f* pts = convex->points; - const ConvexBase::Neighbors* nn = convex->neighbors; + const std::vector<Vec3f>& pts = *(convex->points); + const std::vector<ConvexBase::Neighbors>& nn = *(convex->neighbors); if (hint < 0 || hint >= (int)convex->num_points) hint = 0; - FCL_REAL maxdot = pts[hint].dot(dir); + FCL_REAL maxdot = pts[static_cast<size_t>(hint)].dot(dir); std::vector<int8_t>& visited = data->visited; visited.assign(convex->num_points, false); visited[static_cast<std::size_t>(hint)] = true; @@ -183,7 +185,7 @@ void getShapeSupportLog(const ConvexBase* convex, const Vec3f& dir, // equal. Yet, the neighbors must be visited. bool found = true, loose_check = true; while (found) { - const ConvexBase::Neighbors& n = nn[hint]; + const ConvexBase::Neighbors& n = nn[static_cast<size_t>(hint)]; found = false; for (int in = 0; in < n.count(); ++in) { const unsigned int ip = n[in]; @@ -204,24 +206,25 @@ void getShapeSupportLog(const ConvexBase* convex, const Vec3f& dir, } } - support = pts[hint]; + support = pts[static_cast<size_t>(hint)]; } void getShapeSupportLinear(const ConvexBase* convex, const Vec3f& dir, Vec3f& support, int& hint, MinkowskiDiff::ShapeData*) { - const Vec3f* pts = convex->points; + const std::vector<Vec3f>& pts = *(convex->points); hint = 0; FCL_REAL maxdot = pts[0].dot(dir); for (int i = 1; i < (int)convex->num_points; ++i) { - FCL_REAL dot = pts[i].dot(dir); + FCL_REAL dot = pts[static_cast<size_t>(i)].dot(dir); if (dot > maxdot) { maxdot = dot; hint = i; } } - support = pts[hint]; + + support = pts[static_cast<size_t>(hint)]; } void getShapeSupport(const ConvexBase* convex, const Vec3f& dir, Vec3f& support, @@ -257,6 +260,11 @@ inline void getShapeSupport(const LargeConvex* convex, const Vec3f& dir, : dir, \ support, hint, NULL) +inline void getSphereSupport(const Sphere* sphere, const Vec3f& dir, + Vec3f& support) { + support = sphere->radius * (dir.normalized()); +} + Vec3f getSupport(const ShapeBase* shape, const Vec3f& dir, bool dirIsNormalized, int& hint) { Vec3f support; @@ -268,7 +276,7 @@ Vec3f getSupport(const ShapeBase* shape, const Vec3f& dir, bool dirIsNormalized, CALL_GET_SHAPE_SUPPORT(Box); break; case GEOM_SPHERE: - CALL_GET_SHAPE_SUPPORT(Sphere); + getSphereSupport(static_cast<const Sphere*>(shape), dir, support); break; case GEOM_ELLIPSOID: CALL_GET_SHAPE_SUPPORT(Ellipsoid); @@ -394,7 +402,7 @@ MinkowskiDiff::GetSupportFunction makeGetSupportFunction1( return getSupportFuncTpl<Shape0, SmallConvex, false>; } default: - throw std::logic_error("Unsupported geometric shape"); + HPP_FCL_THROW_PRETTY("Unsupported geometric shape.", std::logic_error); } } @@ -443,7 +451,7 @@ MinkowskiDiff::GetSupportFunction makeGetSupportFunction0( s1, identity, inflation, linear_log_convex_threshold); break; default: - throw std::logic_error("Unsupported geometric shape"); + HPP_FCL_THROW_PRETTY("Unsupported geometric shape", std::logic_error); } } @@ -474,7 +482,7 @@ bool getNormalizeSupportDirection(const ShapeBase* shape) { return (bool)shape_traits<ConvexBase>::NeedNesterovNormalizeHeuristic; break; default: - throw std::logic_error("Unsupported geometric shape"); + HPP_FCL_THROW_PRETTY("Unsupported geometric shape", std::logic_error); } } @@ -523,6 +531,8 @@ void GJK::initialize() { gjk_variant = GJKVariant::DefaultGJK; convergence_criterion = GJKConvergenceCriterion::VDB; convergence_criterion_type = GJKConvergenceCriterionType::Relative; + iterations = 0; + iterations_momentum_stop = 0; } Vec3f GJK::getGuessFromSimplex() const { return ray; } @@ -576,7 +586,8 @@ bool getClosestPoints(const GJK::Simplex& simplex, Vec3f& w0, Vec3f& w1) { vs[2]->w, vs[3]->w); break; default: - throw std::logic_error("The simplex rank must be in [ 1, 4 ]"); + HPP_FCL_THROW_PRETTY("The simplex rank must be in [ 1, 4 ]", + std::logic_error); } w0.setZero(); w1.setZero(); @@ -650,6 +661,9 @@ GJK::Status GJK::evaluate(const MinkowskiDiff& shape_, const Vec3f& guess, // Momentum GJKVariant current_gjk_variant = gjk_variant; + // If at the end of gjk, iterations_stop_momentum has a value of -1, + // this means momentum has not been stopped. + if (current_gjk_variant != DefaultGJK) iterations_momentum_stop = -1; Vec3f w = ray; Vec3f dir = ray; Vec3f y; @@ -700,8 +714,13 @@ GJK::Status GJK::evaluate(const MinkowskiDiff& shape_, const Vec3f& guess, } break; + case PolyakAcceleration: + momentum = 1 / (FCL_REAL(iterations) + 1); + dir = momentum * dir + (1 - momentum) * ray; + break; + default: - throw std::logic_error("Invalid momentum variant."); + HPP_FCL_THROW_PRETTY("Invalid momentum variant.", std::logic_error); } appendVertex(curr_simplex, -dir, false, @@ -725,7 +744,8 @@ GJK::Status GJK::evaluate(const MinkowskiDiff& shape_, const Vec3f& guess, if (frank_wolfe_duality_gap - tolerance <= 0) { removeVertex(simplices[current]); current_gjk_variant = DefaultGJK; // move back to classic GJK - continue; // continue to next iteration + iterations_momentum_stop = static_cast<int>(iterations); + continue; // continue to next iteration } } @@ -770,7 +790,7 @@ GJK::Status GJK::evaluate(const MinkowskiDiff& shape_, const Vec3f& guess, inside = projectTetrahedraOrigin(curr_simplex, next_simplex); break; default: - throw std::logic_error("Invalid simplex rank"); + HPP_FCL_THROW_PRETTY("Invalid simplex rank", std::logic_error); } assert(nfree + next_simplex.rank == 4); current = next; @@ -807,13 +827,15 @@ bool GJK::checkConvergence(const Vec3f& w, const FCL_REAL& rl, FCL_REAL& alpha, diff = rl - alpha; switch (convergence_criterion_type) { case Absolute: - throw std::logic_error("VDB convergence criterion is relative."); + HPP_FCL_THROW_PRETTY("VDB convergence criterion is relative.", + std::logic_error); break; case Relative: check_passed = (diff - tolerance * rl) <= 0; break; default: - throw std::logic_error("Invalid convergence criterion type."); + HPP_FCL_THROW_PRETTY("Invalid convergence criterion type.", + std::logic_error); } break; @@ -828,7 +850,8 @@ bool GJK::checkConvergence(const Vec3f& w, const FCL_REAL& rl, FCL_REAL& alpha, check_passed = ((diff / tolerance * rl) - tolerance * rl) <= 0; break; default: - throw std::logic_error("Invalid convergence criterion type."); + HPP_FCL_THROW_PRETTY("Invalid convergence criterion type.", + std::logic_error); } break; @@ -845,12 +868,13 @@ bool GJK::checkConvergence(const Vec3f& w, const FCL_REAL& rl, FCL_REAL& alpha, check_passed = ((diff / tolerance * rl) - tolerance * rl) <= 0; break; default: - throw std::logic_error("Invalid convergence criterion type."); + HPP_FCL_THROW_PRETTY("Invalid convergence criterion type.", + std::logic_error); } break; default: - throw std::logic_error("Invalid convergence criterion."); + HPP_FCL_THROW_PRETTY("Invalid convergence criterion.", std::logic_error); } return check_passed; } @@ -1446,6 +1470,7 @@ void EPA::initialize() { nextsv = 0; for (size_t i = 0; i < max_face_num; ++i) stock.append(&fc_store[max_face_num - i - 1]); + iterations = 0; } bool EPA::getEdgeDist(SimplexF* face, SimplexV* a, SimplexV* b, @@ -1561,7 +1586,7 @@ EPA::Status EPA::evaluate(GJK& gjk, const Vec3f& guess) { // minimum distance to origin) to split SimplexF outer = *best; size_t pass = 0; - size_t iterations = 0; + iterations = 0; // set the face connectivity bind(tetrahedron[0], 0, tetrahedron[1], 0); @@ -1622,7 +1647,7 @@ EPA::Status EPA::evaluate(GJK& gjk, const Vec3f& guess) { // the origin. status = FallBack; // TODO: define a better normal - assert(simplex.rank == 1 && simplex.vertex[0]->w.isZero(gjk.getTolerance())); + assert(simplex.rank == 1 && simplex.vertex[0]->w.isZero(gjk.tolerance)); normal = -guess; FCL_REAL nl = normal.norm(); if (nl > 0) diff --git a/src/octree.cpp b/src/octree.cpp index 3d2ff6ea18a5911c2769ff22264c7cb44c4a20b9..7e01605965be4e3df605a279c89df4c4f124f1f2 100644 --- a/src/octree.cpp +++ b/src/octree.cpp @@ -1,7 +1,7 @@ /* * Software License Agreement (BSD License) * - * Copyright (c) 2020, INRIA + * Copyright (c) 2020-2023, INRIA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,6 +33,7 @@ */ #include <hpp/fcl/octree.h> +#include <array> namespace hpp { namespace fcl { @@ -54,13 +55,13 @@ struct Neighbors { void hasNeighboordPlusZ() { value |= 0x20; } }; // struct neighbors -void computeNeighbors(const std::vector<boost::array<FCL_REAL, 6> >& boxes, +void computeNeighbors(const std::vector<Vec6f>& boxes, std::vector<Neighbors>& neighbors) { - typedef std::vector<boost::array<FCL_REAL, 6> > VectorArray6; + typedef std::vector<Vec6f> VectorVec6f; FCL_REAL fixedSize = -1; FCL_REAL e(1e-8); for (std::size_t i = 0; i < boxes.size(); ++i) { - const boost::array<FCL_REAL, 6>& box(boxes[i]); + const Vec6f& box(boxes[i]); Neighbors& n(neighbors[i]); FCL_REAL x(box[0]); FCL_REAL y(box[1]); @@ -71,9 +72,9 @@ void computeNeighbors(const std::vector<boost::array<FCL_REAL, 6> >& boxes, else assert(s == fixedSize); - for (VectorArray6::const_iterator it = boxes.begin(); it != boxes.end(); + for (VectorVec6f::const_iterator it = boxes.begin(); it != boxes.end(); ++it) { - const boost::array<FCL_REAL, 6>& otherBox = *it; + const Vec6f& otherBox = *it; FCL_REAL xo(otherBox[0]); FCL_REAL yo(otherBox[1]); FCL_REAL zo(otherBox[2]); @@ -105,7 +106,7 @@ void computeNeighbors(const std::vector<boost::array<FCL_REAL, 6> >& boxes, } // namespace internal void OcTree::exportAsObjFile(const std::string& filename) const { - std::vector<boost::array<FCL_REAL, 6> > boxes(this->toBoxes()); + std::vector<Vec6f> boxes(this->toBoxes()); std::vector<internal::Neighbors> neighbors(boxes.size()); internal::computeNeighbors(boxes, neighbors); // compute list of vertices and faces @@ -113,12 +114,12 @@ void OcTree::exportAsObjFile(const std::string& filename) const { typedef std::vector<Vec3f> VectorVec3f; std::vector<Vec3f> vertices; - typedef boost::array<std::size_t, 4> Array4; + typedef std::array<std::size_t, 4> Array4; typedef std::vector<Array4> VectorArray4; std::vector<Array4> faces; for (std::size_t i = 0; i < boxes.size(); ++i) { - const boost::array<FCL_REAL, 6>& box(boxes[i]); + const Vec6f& box(boxes[i]); internal::Neighbors& n(neighbors[i]); FCL_REAL x(box[0]); @@ -165,8 +166,10 @@ void OcTree::exportAsObjFile(const std::string& filename) const { std::ofstream os; os.open(filename); if (!os.is_open()) - throw std::runtime_error(std::string("failed to open file \"") + filename + - std::string("\"")); + HPP_FCL_THROW_PRETTY( + (std::string("failed to open file \"") + filename + std::string("\"")) + .c_str(), + std::runtime_error); // write vertices os << "# list of vertices\n"; for (VectorVec3f::const_iterator it = vertices.begin(); it != vertices.end(); @@ -191,9 +194,7 @@ OcTreePtr_t makeOctree( shared_ptr<octomap::OcTree> octree(new octomap::OcTree(resolution)); for (Eigen::DenseIndex row_id = 0; row_id < point_cloud.rows(); ++row_id) { RowType row = point_cloud.row(row_id); - octomap::point3d p(static_cast<float>(row[0]), static_cast<float>(row[1]), - static_cast<float>(row[2])); - octree->updateNode(p, true); + octree->updateNode(row[0], row[1], row[2], true, true); } octree->updateInnerOccupancy(); diff --git a/src/serialization/serialization.cpp b/src/serialization/serialization.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7ef1d28a220473675d6981e7c07fd51a65770235 --- /dev/null +++ b/src/serialization/serialization.cpp @@ -0,0 +1,87 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2024, INRIA + * 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 name of Open Source Robotics Foundation 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 OWNER 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. + */ + +/** \author Justin Carpentier */ + +#include "hpp/fcl/serialization/fwd.h" + +using namespace hpp::fcl; + +#include "hpp/fcl/serialization/geometric_shapes.h" +#include "hpp/fcl/serialization/convex.h" +#include "hpp/fcl/serialization/hfield.h" +#include "hpp/fcl/serialization/BVH_model.h" +#ifdef HPP_FCL_HAS_OCTOMAP +#include "hpp/fcl/serialization/octree.h" +#endif + +HPP_FCL_SERIALIZATION_DEFINE_EXPORT(ShapeBase) +HPP_FCL_SERIALIZATION_DEFINE_EXPORT(CollisionGeometry) +HPP_FCL_SERIALIZATION_DEFINE_EXPORT(TriangleP) +HPP_FCL_SERIALIZATION_DEFINE_EXPORT(Box) +HPP_FCL_SERIALIZATION_DEFINE_EXPORT(Sphere) +HPP_FCL_SERIALIZATION_DEFINE_EXPORT(Ellipsoid) +HPP_FCL_SERIALIZATION_DEFINE_EXPORT(Capsule) +HPP_FCL_SERIALIZATION_DEFINE_EXPORT(Cone) +HPP_FCL_SERIALIZATION_DEFINE_EXPORT(Cylinder) +HPP_FCL_SERIALIZATION_DEFINE_EXPORT(Halfspace) +HPP_FCL_SERIALIZATION_DEFINE_EXPORT(Plane) + +#define EXPORT_AND_CAST(Derived, Base) \ + HPP_FCL_SERIALIZATION_CAST_REGISTER(Derived, Base) \ + HPP_FCL_SERIALIZATION_DEFINE_EXPORT(Derived) \ + /**/ + +HPP_FCL_SERIALIZATION_CAST_REGISTER(ConvexBase, CollisionGeometry) +EXPORT_AND_CAST(Convex<Triangle>, ConvexBase) +EXPORT_AND_CAST(Convex<Quadrilateral>, ConvexBase) + +HPP_FCL_SERIALIZATION_DEFINE_EXPORT(HeightField<AABB>) +HPP_FCL_SERIALIZATION_DEFINE_EXPORT(HeightField<OBB>) +HPP_FCL_SERIALIZATION_DEFINE_EXPORT(HeightField<OBBRSS>) + +HPP_FCL_SERIALIZATION_CAST_REGISTER(BVHModelBase, CollisionGeometry) + +EXPORT_AND_CAST(BVHModel<AABB>, BVHModelBase) +EXPORT_AND_CAST(BVHModel<OBB>, BVHModelBase) +EXPORT_AND_CAST(BVHModel<RSS>, BVHModelBase) +EXPORT_AND_CAST(BVHModel<OBBRSS>, BVHModelBase) +EXPORT_AND_CAST(BVHModel<kIOS>, BVHModelBase) +EXPORT_AND_CAST(BVHModel<KDOP<16>>, BVHModelBase) +EXPORT_AND_CAST(BVHModel<KDOP<18>>, BVHModelBase) +EXPORT_AND_CAST(BVHModel<KDOP<24>>, BVHModelBase) + +#ifdef HPP_FCL_HAS_OCTOMAP +HPP_FCL_SERIALIZATION_DEFINE_EXPORT(OcTree) +#endif diff --git a/src/shape/convex.cpp b/src/shape/convex.cpp index a2a05a974b040f44bc30b188a50ed0efe28e9397..c50d1e7c6e9e6569d3c3649e5643642abd3d0bd7 100644 --- a/src/shape/convex.cpp +++ b/src/shape/convex.cpp @@ -13,7 +13,6 @@ using orgQhull::Qhull; using orgQhull::QhullFacet; using orgQhull::QhullPoint; using orgQhull::QhullRidgeSet; -using orgQhull::QhullVertex; using orgQhull::QhullVertexList; using orgQhull::QhullVertexSet; #endif @@ -25,9 +24,9 @@ namespace fcl { // the vector `triangle barycentre - convex_tri.center` is positive. void reorderTriangle(const Convex<Triangle>* convex_tri, Triangle& tri) { Vec3f p0, p1, p2; - p0 = convex_tri->points[tri[0]]; - p1 = convex_tri->points[tri[1]]; - p2 = convex_tri->points[tri[2]]; + p0 = (*(convex_tri->points))[tri[0]]; + p1 = (*(convex_tri->points))[tri[1]]; + p2 = (*(convex_tri->points))[tri[2]]; Vec3f barycentre_tri, center_barycenter; barycentre_tri = (p0 + p1 + p2) / 3; @@ -43,14 +42,25 @@ void reorderTriangle(const Convex<Triangle>* convex_tri, Triangle& tri) { } } +ConvexBase* ConvexBase::convexHull(std::shared_ptr<std::vector<Vec3f>>& pts, + unsigned int num_points, bool keepTriangles, + const char* qhullCommand) { + HPP_FCL_COMPILER_DIAGNOSTIC_PUSH + HPP_FCL_COMPILER_DIAGNOSTIC_IGNORED_DEPRECECATED_DECLARATIONS + return ConvexBase::convexHull(pts->data(), num_points, keepTriangles, + qhullCommand); + HPP_FCL_COMPILER_DIAGNOSTIC_POP +} + ConvexBase* ConvexBase::convexHull(const Vec3f* pts, unsigned int num_points, bool keepTriangles, const char* qhullCommand) { #ifdef HPP_FCL_HAS_QHULL if (num_points <= 3) { - throw std::invalid_argument( + HPP_FCL_THROW_PRETTY( "You shouldn't use this function with less than" - " 4 points."); + " 4 points.", + std::invalid_argument); } assert(pts[0].data() + 3 == pts[1].data()); @@ -61,7 +71,7 @@ ConvexBase* ConvexBase::convexHull(const Vec3f* pts, unsigned int num_points, if (qh.qhullStatus() != qh_ERRnone) { if (qh.hasQhullMessage()) std::cerr << qh.qhullMessage() << std::endl; - throw std::logic_error("Qhull failed"); + HPP_FCL_THROW_PRETTY("Qhull failed", std::logic_error); } typedef std::size_t index_type; @@ -71,15 +81,16 @@ ConvexBase* ConvexBase::convexHull(const Vec3f* pts, unsigned int num_points, std::vector<int> pts_to_vertices(num_points, -1); // Initialize the vertices - int nvertex = (qh.vertexCount()); - Vec3f* vertices = new Vec3f[size_t(nvertex)]; + size_t nvertex = static_cast<size_t>(qh.vertexCount()); + std::shared_ptr<std::vector<Vec3f>> vertices( + new std::vector<Vec3f>(size_t(nvertex))); QhullVertexList vertexList(qh.vertexList()); - int i_vertex = 0; + size_t i_vertex = 0; for (QhullVertexList::const_iterator v = vertexList.begin(); v != vertexList.end(); ++v) { QhullPoint pt((*v).point()); pts_to_vertices[(size_t)pt.id()] = (int)i_vertex; - vertices[i_vertex] = Vec3f(pt[0], pt[1], pt[2]); + (*vertices)[i_vertex] = Vec3f(pt[0], pt[1], pt[2]); ++i_vertex; } assert(i_vertex == nvertex); @@ -90,14 +101,15 @@ ConvexBase* ConvexBase::convexHull(const Vec3f* pts, unsigned int num_points, convex = convex_tri = new Convex<Triangle>(); else convex = new ConvexBase; - convex->initialize(true, vertices, static_cast<unsigned int>(nvertex)); + convex->initialize(vertices, static_cast<unsigned int>(nvertex)); // Build the neighbors - convex->neighbors = new Neighbors[size_t(nvertex)]; - std::vector<std::set<index_type> > nneighbors(static_cast<size_t>(nvertex)); + convex->neighbors.reset(new std::vector<Neighbors>(size_t(nvertex))); + std::vector<std::set<index_type>> nneighbors(static_cast<size_t>(nvertex)); if (keepTriangles) { convex_tri->num_polygons = static_cast<unsigned int>(qh.facetCount()); - convex_tri->polygons = new Triangle[convex_tri->num_polygons]; + convex_tri->polygons.reset( + new std::vector<Triangle>(convex_tri->num_polygons)); convex_tri->computeCenter(); } @@ -121,7 +133,7 @@ ConvexBase* ConvexBase::convexHull(const Vec3f* pts, unsigned int num_points, f_vertices[2].point().id())])); if (keepTriangles) { reorderTriangle(convex_tri, tri); - convex_tri->polygons[i_polygon++] = tri; + (*convex_tri->polygons)[i_polygon++] = tri; } for (size_t j = 0; j < n; ++j) { size_t i = (j == 0) ? n - 1 : j - 1; @@ -132,9 +144,10 @@ ConvexBase* ConvexBase::convexHull(const Vec3f* pts, unsigned int num_points, } } else { if (keepTriangles) { // TODO I think there is a memory leak here. - throw std::invalid_argument( + HPP_FCL_THROW_PRETTY( "You requested to keep triangles so you " - "must pass option \"Qt\" to qhull via the qhull command argument."); + "must pass option \"Qt\" to qhull via the qhull command argument.", + std::invalid_argument); } // Non-simplicial faces have more than 3 vertices and contains a list of // rigdes. Ridges are (3-1)D simplex (i.e. one edge). We mark the two @@ -158,30 +171,78 @@ ConvexBase* ConvexBase::convexHull(const Vec3f* pts, unsigned int num_points, } } } - assert(!keepTriangles || i_polygon == qh.facetCount()); + assert(!keepTriangles || static_cast<int>(i_polygon) == qh.facetCount()); + + // Build the double representation (free in this case because qhull has + // alreday run) + convex->buildDoubleDescriptionFromQHullResult(qh); // Fill the neighbor attribute of the returned object. - convex->nneighbors_ = new unsigned int[c_nneighbors]; - unsigned int* p_nneighbors = convex->nneighbors_; + convex->nneighbors_.reset(new std::vector<unsigned int>(c_nneighbors)); + unsigned int* p_nneighbors = convex->nneighbors_->data(); + std::vector<Neighbors>& neighbors_ = *(convex->neighbors); for (size_t i = 0; i < static_cast<size_t>(nvertex); ++i) { - Neighbors& n = convex->neighbors[i]; + Neighbors& n = neighbors_[i]; if (nneighbors[i].size() >= (std::numeric_limits<unsigned char>::max)()) - throw std::logic_error("Too many neighbors."); + HPP_FCL_THROW_PRETTY("Too many neighbors.", std::logic_error); n.count_ = (unsigned char)nneighbors[i].size(); n.n_ = p_nneighbors; p_nneighbors = std::copy(nneighbors[i].begin(), nneighbors[i].end(), p_nneighbors); } - assert(p_nneighbors == convex->nneighbors_ + c_nneighbors); + assert(p_nneighbors == convex->nneighbors_->data() + c_nneighbors); return convex; #else - throw std::logic_error( - "Library built without qhull. Cannot build object of this type."); + HPP_FCL_THROW_PRETTY( + "Library built without qhull. Cannot build object of this type.", + std::logic_error); HPP_FCL_UNUSED_VARIABLE(pts); HPP_FCL_UNUSED_VARIABLE(num_points); HPP_FCL_UNUSED_VARIABLE(keepTriangles); HPP_FCL_UNUSED_VARIABLE(qhullCommand); #endif } + +#ifdef HPP_FCL_HAS_QHULL +void ConvexBase::buildDoubleDescription() { + if (num_points <= 3) { + HPP_FCL_THROW_PRETTY( + "You shouldn't use this function with a convex less than" + " 4 points.", + std::invalid_argument); + } + + Qhull qh; + const char* command = "Qt"; + qh.runQhull("", 3, static_cast<int>(num_points), (*points)[0].data(), + command); + + if (qh.qhullStatus() != qh_ERRnone) { + if (qh.hasQhullMessage()) std::cerr << qh.qhullMessage() << std::endl; + HPP_FCL_THROW_PRETTY("Qhull failed", std::logic_error); + } + + buildDoubleDescriptionFromQHullResult(qh); +} + +void ConvexBase::buildDoubleDescriptionFromQHullResult(const Qhull& qh) { + num_normals_and_offsets = static_cast<unsigned int>(qh.facetCount()); + normals.reset(new std::vector<Vec3f>(num_normals_and_offsets)); + std::vector<Vec3f>& normals_ = *normals; + offsets.reset(new std::vector<double>(num_normals_and_offsets)); + std::vector<double>& offsets_ = *offsets; + unsigned int i_normal = 0; + for (QhullFacet facet = qh.beginFacet(); facet != qh.endFacet(); + facet = facet.next()) { + const orgQhull::QhullHyperplane& plane = facet.hyperplane(); + normals_[i_normal] = Vec3f(plane.coordinates()[0], plane.coordinates()[1], + plane.coordinates()[2]); + offsets_[i_normal] = plane.offset(); + i_normal++; + } + assert(static_cast<int>(i_normal) == qh.facetCount()); +} +#endif + } // namespace fcl } // namespace hpp diff --git a/src/shape/geometric_shapes.cpp b/src/shape/geometric_shapes.cpp index 8c05a2d027bb5ad5ce876ee50bd42e412b3084e0..4ac11a01730ff8b0fdcfa105e0c25fc20b2f585a 100644 --- a/src/shape/geometric_shapes.cpp +++ b/src/shape/geometric_shapes.cpp @@ -41,57 +41,74 @@ namespace hpp { namespace fcl { -void ConvexBase::initialize(bool own_storage, Vec3f* points_, +void ConvexBase::initialize(std::shared_ptr<std::vector<Vec3f>> points_, unsigned int num_points_) { points = points_; num_points = num_points_; - own_storage_ = own_storage; + num_normals_and_offsets = 0; + normals.reset(); + offsets.reset(); computeCenter(); } -void ConvexBase::set(bool own_storage_, Vec3f* points_, +void ConvexBase::set(std::shared_ptr<std::vector<Vec3f>> points_, unsigned int num_points_) { - if (own_storage_ && points) delete[] points; - initialize(own_storage_, points_, num_points_); + initialize(points_, num_points_); } ConvexBase::ConvexBase(const ConvexBase& other) : ShapeBase(other), num_points(other.num_points), - center(other.center), - own_storage_(other.own_storage_) { - if (neighbors) delete[] neighbors; - if (nneighbors_) delete[] nneighbors_; - if (own_storage_) { - if (own_storage_ && points) delete[] points; - - points = new Vec3f[num_points]; - std::copy(other.points, other.points + num_points, points); + num_normals_and_offsets(other.num_normals_and_offsets), + center(other.center) { + if (other.points.get() && other.points->size() > 0) { + // Deep copy of other points + points.reset(new std::vector<Vec3f>(*other.points)); } else - points = other.points; + points.reset(); + + if (other.nneighbors_.get() && other.nneighbors_->size() > 0) { + // Deep copy the list of all the neighbors of all the points + nneighbors_.reset(new std::vector<unsigned int>(*(other.nneighbors_))); + if (other.neighbors.get() && other.neighbors->size() > 0) { + // Fill each neighbors for each point in the Convex object. + neighbors.reset(new std::vector<Neighbors>(other.neighbors->size())); + assert(neighbors->size() == points->size()); + unsigned int* p_nneighbors = nneighbors_->data(); + + std::vector<Neighbors>& neighbors_ = *neighbors; + const std::vector<Neighbors>& other_neighbors_ = *(other.neighbors); + for (size_t i = 0; i < neighbors->size(); ++i) { + Neighbors& n = neighbors_[i]; + n.count_ = other_neighbors_[i].count_; + n.n_ = p_nneighbors; + p_nneighbors += n.count_; + } + } else + neighbors.reset(); + } else + nneighbors_.reset(); - neighbors = new Neighbors[num_points]; - std::copy(other.neighbors, other.neighbors + num_points, neighbors); + if (other.normals.get() && other.normals->size() > 0) { + normals.reset(new std::vector<Vec3f>(*(other.normals))); + } else + normals.reset(); - std::size_t c_nneighbors = 0; - for (std::size_t i = 0; i < num_points; ++i) - c_nneighbors += neighbors[i].count(); - nneighbors_ = new unsigned int[c_nneighbors]; - std::copy(other.nneighbors_, other.nneighbors_ + c_nneighbors, nneighbors_); + if (other.offsets.get() && other.offsets->size() > 0) { + offsets.reset(new std::vector<double>(*(other.offsets))); + } else + offsets.reset(); ShapeBase::operator=(*this); } -ConvexBase::~ConvexBase() { - if (neighbors) delete[] neighbors; - if (nneighbors_) delete[] nneighbors_; - if (own_storage_ && points) delete[] points; -} +ConvexBase::~ConvexBase() {} void ConvexBase::computeCenter() { center.setZero(); + const std::vector<Vec3f>& points_ = *points; for (std::size_t i = 0; i < num_points; ++i) - center += points[i]; // TODO(jcarpent): vectorization + center += points_[i]; // TODO(jcarpent): vectorization center /= num_points; } diff --git a/src/shape/geometric_shapes_utility.cpp b/src/shape/geometric_shapes_utility.cpp index 181487cde33a25bf19ae33b384c1dd660fb12dbd..2f7d0992322fcbd7808e27280c37afcde298ea60 100644 --- a/src/shape/geometric_shapes_utility.cpp +++ b/src/shape/geometric_shapes_utility.cpp @@ -226,8 +226,9 @@ std::vector<Vec3f> getBoundVertices(const Cylinder& cylinder, std::vector<Vec3f> getBoundVertices(const ConvexBase& convex, const Transform3f& tf) { std::vector<Vec3f> result(convex.num_points); + const std::vector<Vec3f>& points_ = *(convex.points); for (std::size_t i = 0; i < convex.num_points; ++i) { - result[i] = tf.transform(convex.points[i]); + result[i] = tf.transform(points_[i]); } return result; @@ -354,8 +355,9 @@ void computeBV<AABB, ConvexBase>(const ConvexBase& s, const Transform3f& tf, const Vec3f& T = tf.getTranslation(); AABB bv_; + const std::vector<Vec3f>& points_ = *(s.points); for (std::size_t i = 0; i < s.num_points; ++i) { - Vec3f new_p = R * s.points[i] + T; + Vec3f new_p = R * points_[i] + T; bv_ += new_p; } @@ -492,7 +494,7 @@ void computeBV<OBB, ConvexBase>(const ConvexBase& s, const Transform3f& tf, const Matrix3f& R = tf.getRotation(); const Vec3f& T = tf.getTranslation(); - fit(s.points, s.num_points, bv); + fit(s.points->data(), s.num_points, bv); bv.axes.applyOnTheLeft(R); diff --git a/src/traits_traversal.h b/src/traits_traversal.h index a8d20c68b51ae26e61f31f1c8cdcf47b63ab6557..2df829ef741b26cbad57e3dfb6d94803d8137ff2 100644 --- a/src/traits_traversal.h +++ b/src/traits_traversal.h @@ -48,6 +48,16 @@ struct HPP_FCL_LOCAL TraversalTraitsCollision<BVHModel<T_BVH>, OcTree> { typedef MeshOcTreeCollisionTraversalNode<T_BVH> CollisionTraversal_t; }; +template <typename T_HF> +struct HPP_FCL_LOCAL TraversalTraitsCollision<OcTree, HeightField<T_HF> > { + typedef OcTreeHeightFieldCollisionTraversalNode<T_HF> CollisionTraversal_t; +}; + +template <typename T_HF> +struct HPP_FCL_LOCAL TraversalTraitsCollision<HeightField<T_HF>, OcTree> { + typedef HeightFieldOcTreeCollisionTraversalNode<T_HF> CollisionTraversal_t; +}; + #endif // TraversalTraitsDistance for distance_func_matrix.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d708a4ee3838b54f23b1f405ff8850308f5ea622..96e26d8091c162cb32576f5bf0947f237778e13c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -28,6 +28,7 @@ add_fcl_test(math math.cpp) add_fcl_test(collision collision.cpp) add_fcl_test(distance distance.cpp) +add_fcl_test(normal_and_nearest_points normal_and_nearest_points.cpp) add_fcl_test(distance_lower_bound distance_lower_bound.cpp) add_fcl_test(security_margin security_margin.cpp) add_fcl_test(geometric_shapes geometric_shapes.cpp) @@ -52,7 +53,7 @@ add_fcl_test(hfields hfields.cpp) add_fcl_test(profiling profiling.cpp) add_fcl_test(gjk gjk.cpp) -add_fcl_test(nesterov_gjk nesterov_gjk.cpp) +add_fcl_test(accelerated_gjk accelerated_gjk.cpp) add_fcl_test(gjk_convergence_criterion gjk_convergence_criterion.cpp) if(HPP_FCL_HAS_OCTOMAP) add_fcl_test(octree octree.cpp) diff --git a/test/nesterov_gjk.cpp b/test/accelerated_gjk.cpp similarity index 73% rename from test/nesterov_gjk.cpp rename to test/accelerated_gjk.cpp index a75aeb157d50145c64e209538ba0532ca6f80ac7..6d2c80964dc1b566a2e907df4564a3e7be857694 100644 --- a/test/nesterov_gjk.cpp +++ b/test/accelerated_gjk.cpp @@ -77,6 +77,12 @@ BOOST_AUTO_TEST_CASE(set_gjk_variant) { BOOST_CHECK(solver.gjk_variant == GJKVariant::NesterovAcceleration); BOOST_CHECK(gjk.gjk_variant == GJKVariant::NesterovAcceleration); + + solver.gjk_variant = GJKVariant::PolyakAcceleration; + gjk.gjk_variant = GJKVariant::PolyakAcceleration; + + BOOST_CHECK(solver.gjk_variant == GJKVariant::PolyakAcceleration); + BOOST_CHECK(gjk.gjk_variant == GJKVariant::PolyakAcceleration); } BOOST_AUTO_TEST_CASE(need_nesterov_normalize_support_direction) { @@ -97,13 +103,15 @@ BOOST_AUTO_TEST_CASE(need_nesterov_normalize_support_direction) { BOOST_CHECK(mink_diff3.normalize_support_direction == true); } -void test_nesterov_gjk(const ShapeBase& shape0, const ShapeBase& shape1) { +void test_accelerated_gjk(const ShapeBase& shape0, const ShapeBase& shape1) { // Solvers unsigned int max_iterations = 128; FCL_REAL tolerance = 1e-6; GJK gjk(max_iterations, tolerance); GJK gjk_nesterov(max_iterations, tolerance); gjk_nesterov.gjk_variant = GJKVariant::NesterovAcceleration; + GJK gjk_polyak(max_iterations, tolerance); + gjk_polyak.gjk_variant = GJKVariant::PolyakAcceleration; // Minkowski difference MinkowskiDiff mink_diff; @@ -132,6 +140,9 @@ void test_nesterov_gjk(const ShapeBase& shape0, const ShapeBase& shape1) { BOOST_CHECK(res_gjk_1 == res_gjk_2); EIGEN_VECTOR_IS_APPROX(ray_gjk, gjk.ray, 1e-8); + // -------------- + // -- Nesterov -- + // -------------- GJK::Status res_nesterov_gjk_1 = gjk_nesterov.evaluate(mink_diff, init_guess, init_support_guess); Vec3f ray_nesterov = gjk_nesterov.ray; @@ -147,8 +158,29 @@ void test_nesterov_gjk(const ShapeBase& shape0, const ShapeBase& shape1) { // Make sure GJK and Nesterov accelerated GJK converges in a reasonable // amount of iterations - BOOST_CHECK(gjk.getIterations() < max_iterations); - BOOST_CHECK(gjk_nesterov.getIterations() < max_iterations); + BOOST_CHECK(gjk.iterations < max_iterations); + BOOST_CHECK(gjk_nesterov.iterations < max_iterations); + + // ------------ + // -- Polyak -- + // ------------ + GJK::Status res_polyak_gjk_1 = + gjk_polyak.evaluate(mink_diff, init_guess, init_support_guess); + Vec3f ray_polyak = gjk_polyak.ray; + GJK::Status res_polyak_gjk_2 = + gjk_polyak.evaluate(mink_diff, init_guess, init_support_guess); + BOOST_CHECK(res_polyak_gjk_1 == res_polyak_gjk_2); + EIGEN_VECTOR_IS_APPROX(ray_polyak, gjk_polyak.ray, 1e-8); + + // Make sure GJK and Polyak accelerated GJK find the same distance between + // the shapes + BOOST_CHECK(res_polyak_gjk_1 == res_gjk_1); + BOOST_CHECK_SMALL(fabs(ray_gjk.norm() - ray_polyak.norm()), 1e-4); + + // Make sure GJK and Polyak accelerated GJK converges in a reasonable + // amount of iterations + BOOST_CHECK(gjk.iterations < max_iterations); + BOOST_CHECK(gjk_polyak.iterations < max_iterations); } } @@ -156,8 +188,8 @@ BOOST_AUTO_TEST_CASE(ellipsoid_ellipsoid) { Ellipsoid ellipsoid0 = Ellipsoid(0.3, 0.4, 0.5); Ellipsoid ellipsoid1 = Ellipsoid(1.5, 1.4, 1.3); - test_nesterov_gjk(ellipsoid0, ellipsoid1); - test_nesterov_gjk(ellipsoid0, ellipsoid1); + test_accelerated_gjk(ellipsoid0, ellipsoid1); + test_accelerated_gjk(ellipsoid0, ellipsoid1); } BOOST_AUTO_TEST_CASE(ellipsoid_capsule) { @@ -166,10 +198,10 @@ BOOST_AUTO_TEST_CASE(ellipsoid_capsule) { Capsule capsule0 = Capsule(0.1, 0.3); Capsule capsule1 = Capsule(1.1, 1.3); - test_nesterov_gjk(ellipsoid0, capsule0); - test_nesterov_gjk(ellipsoid0, capsule1); - test_nesterov_gjk(ellipsoid1, capsule0); - test_nesterov_gjk(ellipsoid1, capsule1); + test_accelerated_gjk(ellipsoid0, capsule0); + test_accelerated_gjk(ellipsoid0, capsule1); + test_accelerated_gjk(ellipsoid1, capsule0); + test_accelerated_gjk(ellipsoid1, capsule1); } BOOST_AUTO_TEST_CASE(ellipsoid_box) { @@ -178,10 +210,10 @@ BOOST_AUTO_TEST_CASE(ellipsoid_box) { Box box0 = Box(0.1, 0.2, 0.3); Box box1 = Box(1.1, 1.2, 1.3); - test_nesterov_gjk(ellipsoid0, box0); - test_nesterov_gjk(ellipsoid0, box1); - test_nesterov_gjk(ellipsoid1, box0); - test_nesterov_gjk(ellipsoid1, box1); + test_accelerated_gjk(ellipsoid0, box0); + test_accelerated_gjk(ellipsoid0, box1); + test_accelerated_gjk(ellipsoid1, box0); + test_accelerated_gjk(ellipsoid1, box1); } BOOST_AUTO_TEST_CASE(ellipsoid_mesh) { @@ -190,10 +222,10 @@ BOOST_AUTO_TEST_CASE(ellipsoid_mesh) { Convex<Triangle> cvx0 = constructPolytopeFromEllipsoid(ellipsoid0); Convex<Triangle> cvx1 = constructPolytopeFromEllipsoid(ellipsoid1); - test_nesterov_gjk(ellipsoid0, cvx0); - test_nesterov_gjk(ellipsoid0, cvx1); - test_nesterov_gjk(ellipsoid1, cvx0); - test_nesterov_gjk(ellipsoid1, cvx1); + test_accelerated_gjk(ellipsoid0, cvx0); + test_accelerated_gjk(ellipsoid0, cvx1); + test_accelerated_gjk(ellipsoid1, cvx0); + test_accelerated_gjk(ellipsoid1, cvx1); } BOOST_AUTO_TEST_CASE(capsule_mesh) { @@ -204,27 +236,27 @@ BOOST_AUTO_TEST_CASE(capsule_mesh) { Capsule capsule0 = Capsule(0.1, 0.3); Capsule capsule1 = Capsule(1.1, 1.3); - test_nesterov_gjk(capsule0, cvx0); - test_nesterov_gjk(capsule0, cvx1); - test_nesterov_gjk(capsule1, cvx0); - test_nesterov_gjk(capsule1, cvx1); + test_accelerated_gjk(capsule0, cvx0); + test_accelerated_gjk(capsule0, cvx1); + test_accelerated_gjk(capsule1, cvx0); + test_accelerated_gjk(capsule1, cvx1); } BOOST_AUTO_TEST_CASE(capsule_capsule) { Capsule capsule0 = Capsule(0.1, 0.3); Capsule capsule1 = Capsule(1.1, 1.3); - test_nesterov_gjk(capsule0, capsule0); - test_nesterov_gjk(capsule1, capsule1); - test_nesterov_gjk(capsule0, capsule1); + test_accelerated_gjk(capsule0, capsule0); + test_accelerated_gjk(capsule1, capsule1); + test_accelerated_gjk(capsule0, capsule1); } BOOST_AUTO_TEST_CASE(box_box) { Box box0 = Box(0.1, 0.2, 0.3); Box box1 = Box(1.1, 1.2, 1.3); - test_nesterov_gjk(box0, box0); - test_nesterov_gjk(box0, box1); - test_nesterov_gjk(box1, box1); + test_accelerated_gjk(box0, box0); + test_accelerated_gjk(box0, box1); + test_accelerated_gjk(box1, box1); } BOOST_AUTO_TEST_CASE(box_mesh) { @@ -235,10 +267,10 @@ BOOST_AUTO_TEST_CASE(box_mesh) { Convex<Triangle> cvx0 = constructPolytopeFromEllipsoid(ellipsoid0); Convex<Triangle> cvx1 = constructPolytopeFromEllipsoid(ellipsoid1); - test_nesterov_gjk(box0, cvx0); - test_nesterov_gjk(box0, cvx1); - test_nesterov_gjk(box1, cvx0); - test_nesterov_gjk(box1, cvx1); + test_accelerated_gjk(box0, cvx0); + test_accelerated_gjk(box0, cvx1); + test_accelerated_gjk(box1, cvx0); + test_accelerated_gjk(box1, cvx1); } BOOST_AUTO_TEST_CASE(mesh_mesh) { @@ -247,7 +279,7 @@ BOOST_AUTO_TEST_CASE(mesh_mesh) { Convex<Triangle> cvx0 = constructPolytopeFromEllipsoid(ellipsoid0); Convex<Triangle> cvx1 = constructPolytopeFromEllipsoid(ellipsoid1); - test_nesterov_gjk(cvx0, cvx0); - test_nesterov_gjk(cvx0, cvx1); - test_nesterov_gjk(cvx1, cvx1); + test_accelerated_gjk(cvx0, cvx0); + test_accelerated_gjk(cvx0, cvx1); + test_accelerated_gjk(cvx1, cvx1); } diff --git a/test/bvh_models.cpp b/test/bvh_models.cpp index bb84030545a83cbb4bdab34a89e5d0f36c975a32..10c54a39ace52694a1754dacfd4b7d893b7305a3 100644 --- a/test/bvh_models.cpp +++ b/test/bvh_models.cpp @@ -206,7 +206,7 @@ void testBVHModelTriangles() { pose.setTranslation(Vec3f(0, 0, 0)); FCL_REAL sqrt2_2 = std::sqrt(2) / 2; - pose.setQuatRotation(Quaternion3f(sqrt2_2, sqrt2_2, 0, 0)); + pose.setQuatRotation(Quatf(sqrt2_2, sqrt2_2, 0, 0)); cropped.reset(BVHExtract(*model, pose, aabb)); BOOST_REQUIRE(cropped); BOOST_CHECK(cropped->build_state == BVH_BUILD_STATE_PROCESSED); @@ -214,7 +214,7 @@ void testBVHModelTriangles() { BOOST_CHECK_EQUAL(cropped->num_tris, model->num_tris - 2); pose.setTranslation(-Vec3f(1, 1, 1)); - pose.setQuatRotation(Quaternion3f::Identity()); + pose.setQuatRotation(Quatf::Identity()); cropped.reset(BVHExtract(*model, pose, aabb)); BOOST_CHECK(!cropped); diff --git a/test/convex.cpp b/test/convex.cpp index d55cab48a01876563ba91dbe961f9203ee7f8f82..ff789b98f9e641d77d8f63972f0635f254dc694a 100644 --- a/test/convex.cpp +++ b/test/convex.cpp @@ -46,26 +46,21 @@ using namespace hpp::fcl; Convex<Quadrilateral> buildBox(FCL_REAL l, FCL_REAL w, FCL_REAL d) { - Vec3f* pts = new Vec3f[8]; - pts[0] = Vec3f(l, w, d); - pts[1] = Vec3f(l, w, -d); - pts[2] = Vec3f(l, -w, d); - pts[3] = Vec3f(l, -w, -d); - pts[4] = Vec3f(-l, w, d); - pts[5] = Vec3f(-l, w, -d); - pts[6] = Vec3f(-l, -w, d); - pts[7] = Vec3f(-l, -w, -d); - - Quadrilateral* polygons = new Quadrilateral[6]; - polygons[0].set(0, 2, 3, 1); // x+ side - polygons[1].set(2, 6, 7, 3); // y- side - polygons[2].set(4, 5, 7, 6); // x- side - polygons[3].set(0, 1, 5, 4); // y+ side - polygons[4].set(1, 3, 7, 5); // z- side - polygons[5].set(0, 2, 6, 4); // z+ side - - return Convex<Quadrilateral>(true, - pts, // points + std::shared_ptr<std::vector<Vec3f>> pts(new std::vector<Vec3f>( + {Vec3f(l, w, d), Vec3f(l, w, -d), Vec3f(l, -w, d), Vec3f(l, -w, -d), + Vec3f(-l, w, d), Vec3f(-l, w, -d), Vec3f(-l, -w, d), + Vec3f(-l, -w, -d)})); + + std::shared_ptr<std::vector<Quadrilateral>> polygons( + new std::vector<Quadrilateral>(6)); + (*polygons)[0].set(0, 2, 3, 1); // x+ side + (*polygons)[1].set(2, 6, 7, 3); // y- side + (*polygons)[2].set(4, 5, 7, 6); // x- side + (*polygons)[3].set(0, 1, 5, 4); // y+ side + (*polygons)[4].set(1, 3, 7, 5); // z- side + (*polygons)[5].set(0, 2, 6, 4); // z+ side + + return Convex<Quadrilateral>(pts, // points 8, // num points polygons, 6 // number of polygons @@ -77,40 +72,40 @@ BOOST_AUTO_TEST_CASE(convex) { Convex<Quadrilateral> box(buildBox(l, w, d)); // Check neighbors - for (int i = 0; i < 8; ++i) { - BOOST_CHECK_EQUAL(box.neighbors[i].count(), 3); + for (size_t i = 0; i < 8; ++i) { + BOOST_CHECK_EQUAL((*box.neighbors)[i].count(), 3); } - BOOST_CHECK_EQUAL(box.neighbors[0][0], 1); - BOOST_CHECK_EQUAL(box.neighbors[0][1], 2); - BOOST_CHECK_EQUAL(box.neighbors[0][2], 4); + BOOST_CHECK_EQUAL((*box.neighbors)[0][0], 1); + BOOST_CHECK_EQUAL((*box.neighbors)[0][1], 2); + BOOST_CHECK_EQUAL((*box.neighbors)[0][2], 4); - BOOST_CHECK_EQUAL(box.neighbors[1][0], 0); - BOOST_CHECK_EQUAL(box.neighbors[1][1], 3); - BOOST_CHECK_EQUAL(box.neighbors[1][2], 5); + BOOST_CHECK_EQUAL((*box.neighbors)[1][0], 0); + BOOST_CHECK_EQUAL((*box.neighbors)[1][1], 3); + BOOST_CHECK_EQUAL((*box.neighbors)[1][2], 5); - BOOST_CHECK_EQUAL(box.neighbors[2][0], 0); - BOOST_CHECK_EQUAL(box.neighbors[2][1], 3); - BOOST_CHECK_EQUAL(box.neighbors[2][2], 6); + BOOST_CHECK_EQUAL((*box.neighbors)[2][0], 0); + BOOST_CHECK_EQUAL((*box.neighbors)[2][1], 3); + BOOST_CHECK_EQUAL((*box.neighbors)[2][2], 6); - BOOST_CHECK_EQUAL(box.neighbors[3][0], 1); - BOOST_CHECK_EQUAL(box.neighbors[3][1], 2); - BOOST_CHECK_EQUAL(box.neighbors[3][2], 7); + BOOST_CHECK_EQUAL((*box.neighbors)[3][0], 1); + BOOST_CHECK_EQUAL((*box.neighbors)[3][1], 2); + BOOST_CHECK_EQUAL((*box.neighbors)[3][2], 7); - BOOST_CHECK_EQUAL(box.neighbors[4][0], 0); - BOOST_CHECK_EQUAL(box.neighbors[4][1], 5); - BOOST_CHECK_EQUAL(box.neighbors[4][2], 6); + BOOST_CHECK_EQUAL((*box.neighbors)[4][0], 0); + BOOST_CHECK_EQUAL((*box.neighbors)[4][1], 5); + BOOST_CHECK_EQUAL((*box.neighbors)[4][2], 6); - BOOST_CHECK_EQUAL(box.neighbors[5][0], 1); - BOOST_CHECK_EQUAL(box.neighbors[5][1], 4); - BOOST_CHECK_EQUAL(box.neighbors[5][2], 7); + BOOST_CHECK_EQUAL((*box.neighbors)[5][0], 1); + BOOST_CHECK_EQUAL((*box.neighbors)[5][1], 4); + BOOST_CHECK_EQUAL((*box.neighbors)[5][2], 7); - BOOST_CHECK_EQUAL(box.neighbors[6][0], 2); - BOOST_CHECK_EQUAL(box.neighbors[6][1], 4); - BOOST_CHECK_EQUAL(box.neighbors[6][2], 7); + BOOST_CHECK_EQUAL((*box.neighbors)[6][0], 2); + BOOST_CHECK_EQUAL((*box.neighbors)[6][1], 4); + BOOST_CHECK_EQUAL((*box.neighbors)[6][2], 7); - BOOST_CHECK_EQUAL(box.neighbors[7][0], 3); - BOOST_CHECK_EQUAL(box.neighbors[7][1], 5); - BOOST_CHECK_EQUAL(box.neighbors[7][2], 6); + BOOST_CHECK_EQUAL((*box.neighbors)[7][0], 3); + BOOST_CHECK_EQUAL((*box.neighbors)[7][1], 5); + BOOST_CHECK_EQUAL((*box.neighbors)[7][2], 6); } template <typename Sa, typename Sb> @@ -201,42 +196,38 @@ BOOST_AUTO_TEST_CASE(compare_convex_box) { #ifdef HPP_FCL_HAS_QHULL BOOST_AUTO_TEST_CASE(convex_hull_throw) { - std::vector<Vec3f> points({ - Vec3f(1, 1, 1), - Vec3f(0, 0, 0), - Vec3f(1, 0, 0), - }); + std::shared_ptr<std::vector<Vec3f>> points( + new std::vector<Vec3f>({Vec3f(1, 1, 1), Vec3f(0, 0, 0), Vec3f(1, 0, 0)})); - BOOST_CHECK_THROW(ConvexBase::convexHull(points.data(), 0, false, NULL), + BOOST_CHECK_THROW(ConvexBase::convexHull(points, 0, false, NULL), std::invalid_argument); - BOOST_CHECK_THROW(ConvexBase::convexHull(points.data(), 1, false, NULL), + BOOST_CHECK_THROW(ConvexBase::convexHull(points, 1, false, NULL), std::invalid_argument); - BOOST_CHECK_THROW(ConvexBase::convexHull(points.data(), 2, false, NULL), + BOOST_CHECK_THROW(ConvexBase::convexHull(points, 2, false, NULL), std::invalid_argument); - BOOST_CHECK_THROW(ConvexBase::convexHull(points.data(), 3, false, NULL), + BOOST_CHECK_THROW(ConvexBase::convexHull(points, 3, false, NULL), std::invalid_argument); } BOOST_AUTO_TEST_CASE(convex_hull_quad) { - std::vector<Vec3f> points({ + std::shared_ptr<std::vector<Vec3f>> points(new std::vector<Vec3f>({ Vec3f(1, 1, 1), Vec3f(0, 0, 0), Vec3f(1, 0, 0), Vec3f(0, 0, 1), - }); + })); - ConvexBase* convexHull = ConvexBase::convexHull( - points.data(), (unsigned int)points.size(), false, NULL); + ConvexBase* convexHull = ConvexBase::convexHull(points, 4, false, NULL); BOOST_REQUIRE_EQUAL(convexHull->num_points, 4); - BOOST_CHECK_EQUAL(convexHull->neighbors[0].count(), 3); - BOOST_CHECK_EQUAL(convexHull->neighbors[1].count(), 3); - BOOST_CHECK_EQUAL(convexHull->neighbors[2].count(), 3); + BOOST_CHECK_EQUAL((*(convexHull->neighbors))[0].count(), 3); + BOOST_CHECK_EQUAL((*(convexHull->neighbors))[1].count(), 3); + BOOST_CHECK_EQUAL((*(convexHull->neighbors))[2].count(), 3); delete convexHull; } BOOST_AUTO_TEST_CASE(convex_hull_box_like) { - std::vector<Vec3f> points({ + std::shared_ptr<std::vector<Vec3f>> points(new std::vector<Vec3f>({ Vec3f(1, 1, 1), Vec3f(1, 1, -1), Vec3f(1, -1, 1), @@ -247,29 +238,79 @@ BOOST_AUTO_TEST_CASE(convex_hull_box_like) { Vec3f(-1, -1, -1), Vec3f(0, 0, 0), Vec3f(0, 0, 0.99), - }); + })); - ConvexBase* convexHull = ConvexBase::convexHull( - points.data(), (unsigned int)points.size(), false, NULL); + ConvexBase* convexHull = ConvexBase::convexHull(points, 9, false, NULL); BOOST_REQUIRE_EQUAL(8, convexHull->num_points); - for (int i = 0; i < 8; ++i) { - BOOST_CHECK(convexHull->points[i].cwiseAbs() == Vec3f(1, 1, 1)); - BOOST_CHECK_EQUAL(convexHull->neighbors[i].count(), 3); + { + const std::vector<Vec3f>& cvxhull_points = *(convexHull->points); + for (size_t i = 0; i < 8; ++i) { + BOOST_CHECK(cvxhull_points[i].cwiseAbs() == Vec3f(1, 1, 1)); + BOOST_CHECK_EQUAL((*(convexHull->neighbors))[i].count(), 3); + } } delete convexHull; - convexHull = ConvexBase::convexHull(points.data(), - (unsigned int)points.size(), true, NULL); + convexHull = ConvexBase::convexHull(points, 9, true, NULL); Convex<Triangle>* convex_tri = dynamic_cast<Convex<Triangle>*>(convexHull); BOOST_CHECK(convex_tri != NULL); BOOST_REQUIRE_EQUAL(8, convexHull->num_points); - for (int i = 0; i < 8; ++i) { - BOOST_CHECK(convexHull->points[i].cwiseAbs() == Vec3f(1, 1, 1)); - BOOST_CHECK(convexHull->neighbors[i].count() >= 3); - BOOST_CHECK(convexHull->neighbors[i].count() <= 6); + { + const std::vector<Vec3f>& cvxhull_points = *(convexHull->points); + for (size_t i = 0; i < 8; ++i) { + BOOST_CHECK(cvxhull_points[i].cwiseAbs() == Vec3f(1, 1, 1)); + BOOST_CHECK((*(convexHull->neighbors))[i].count() >= 3); + BOOST_CHECK((*(convexHull->neighbors))[i].count() <= 6); + } } delete convexHull; } + +BOOST_AUTO_TEST_CASE(convex_copy_constructor) { + Convex<Triangle>* convexHullTriCopy; + { + std::shared_ptr<std::vector<Vec3f>> points(new std::vector<Vec3f>({ + Vec3f(1, 1, 1), + Vec3f(1, 1, -1), + Vec3f(1, -1, 1), + Vec3f(1, -1, -1), + Vec3f(-1, 1, 1), + Vec3f(-1, 1, -1), + Vec3f(-1, -1, 1), + Vec3f(-1, -1, -1), + Vec3f(0, 0, 0), + })); + + Convex<Triangle>* convexHullTri = dynamic_cast<Convex<Triangle>*>( + ConvexBase::convexHull(points, 9, true, NULL)); + convexHullTriCopy = new Convex<Triangle>(*convexHullTri); + BOOST_CHECK(*convexHullTri == *convexHullTriCopy); + } + Convex<Triangle>* convexHullTriCopyOfCopy = + new Convex<Triangle>(*convexHullTriCopy); + BOOST_CHECK(*convexHullTriCopyOfCopy == *convexHullTriCopy); +} + +BOOST_AUTO_TEST_CASE(convex_clone) { + std::shared_ptr<std::vector<Vec3f>> points(new std::vector<Vec3f>({ + Vec3f(1, 1, 1), + Vec3f(1, 1, -1), + Vec3f(1, -1, 1), + Vec3f(1, -1, -1), + Vec3f(-1, 1, 1), + Vec3f(-1, 1, -1), + Vec3f(-1, -1, 1), + Vec3f(-1, -1, -1), + Vec3f(0, 0, 0), + })); + + Convex<Triangle>* convexHullTri = dynamic_cast<Convex<Triangle>*>( + ConvexBase::convexHull(points, 9, true, NULL)); + Convex<Triangle>* convexHullTriCopy; + convexHullTriCopy = convexHullTri->clone(); + BOOST_CHECK(*convexHullTri == *convexHullTriCopy); +} + #endif diff --git a/test/geometric_shapes.cpp b/test/geometric_shapes.cpp index d909e6e2122a533374fb81b3ae60b686ae16c115..213bf9b34b1f53483295be9c4e1759f2a4b9cb9f 100644 --- a/test/geometric_shapes.cpp +++ b/test/geometric_shapes.cpp @@ -239,12 +239,12 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_cylinderbox) { Box s2(1.6, 0.6, 0.025); Transform3f tf1( - Quaternion3f(0.5279170511703305, -0.50981118132505521, - -0.67596178682051911, 0.0668715876735793), + Quatf(0.5279170511703305, -0.50981118132505521, -0.67596178682051911, + 0.0668715876735793), Vec3f(0.041218354748013122, 1.2022554710435607, 0.77338855025700015)); Transform3f tf2( - Quaternion3f(0.70738826916719977, 0, 0, 0.70682518110536596), + Quatf(0.70738826916719977, 0, 0, 0.70682518110536596), Vec3f(-0.29936284351096382, 0.80023864435868775, 0.71750000000000003)); GJKSolver solver; @@ -285,8 +285,8 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_cylinderbox) { s1 = Cylinder(0.06, 0.1); tf1.setTranslation( Vec3f(-0.66734052046473924, 0.22219183277457269, 0.76825248755616293)); - tf1.setQuatRotation(Quaternion3f(0.52613359459338371, 0.32189408354839893, - 0.70415587451837913, -0.35175580165512249)); + tf1.setQuatRotation(Quatf(0.52613359459338371, 0.32189408354839893, + 0.70415587451837913, -0.35175580165512249)); res = solver.shapeDistance(s1, tf1, s2, tf2, distance, p1, p2, normal); } @@ -450,7 +450,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_boxbox) { // FCL_REAL depth; Vec3f normal; - Quaternion3f q; + Quatf q; q = AngleAxis((FCL_REAL)3.140 / 6, UnitZ); tf1 = Transform3f(); @@ -996,7 +996,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacesphere) { tf1 = Transform3f(); tf2 = Transform3f(); contact << -5, 0, 0; - depth = 10; + depth = -10; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1004,7 +1004,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacesphere) { tf1 = transform; tf2 = transform; contact = transform.transform(Vec3f(-5, 0, 0)); - depth = 10; + depth = -10; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1012,7 +1012,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacesphere) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(5, 0, 0)); contact << -2.5, 0, 0; - depth = 15; + depth = -15; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1020,7 +1020,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacesphere) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(5, 0, 0)); contact = transform.transform(Vec3f(-2.5, 0, 0)); - depth = 15; + depth = -15; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1028,7 +1028,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacesphere) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(-5, 0, 0)); contact << -7.5, 0, 0; - depth = 5; + depth = -5; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1036,7 +1036,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacesphere) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(-5, 0, 0)); contact = transform.transform(Vec3f(-7.5, 0, 0)); - depth = 5; + depth = -5; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1054,7 +1054,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacesphere) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(10.1, 0, 0)); contact << 0.05, 0, 0; - depth = 20.1; + depth = -20.1; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1062,7 +1062,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacesphere) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(10.1, 0, 0)); contact = transform.transform(Vec3f(0.05, 0, 0)); - depth = 20.1; + depth = -20.1; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1081,51 +1081,77 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planesphere) { Vec3f contact; FCL_REAL depth; Vec3f normal; + Vec3f p1, p2; - tf1 = Transform3f(); + FCL_REAL eps = 1e-6; + tf1 = Transform3f(Vec3f(eps, 0, 0)); tf2 = Transform3f(); - contact.setZero(); - depth = 10; + depth = -10 + eps; + p1 << -10 + eps, 0, 0; + p2 << 0, 0, 0; + contact << (p1 + p2) / 2; + normal << -1, 0, 0; // (1, 0, 0) or (-1, 0, 0) + SET_LINE; + testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); + + tf1 = transform * tf1; + tf2 = transform; + contact = transform.transform((p1 + p2) / 2); + normal = + transform.getRotation() * Vec3f(-1, 0, 0); // (1, 0, 0) or (-1, 0, 0) + SET_LINE; + testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); + + eps = -1e-6; + tf1 = Transform3f(Vec3f(eps, 0, 0)); + tf2 = Transform3f(); + depth = -10 - eps; + p1 << 10 + eps, 0, 0; + p2 << 0, 0, 0; + contact << (p1 + p2) / 2; normal << 1, 0, 0; // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); - tf1 = transform; + tf1 = transform * tf1; tf2 = transform; - contact = transform.transform(Vec3f(0, 0, 0)); - depth = 10; + contact = transform.transform((p1 + p2) / 2); normal = transform.getRotation() * Vec3f(1, 0, 0); // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); tf1 = Transform3f(); tf2 = Transform3f(Vec3f(5, 0, 0)); - contact << 5, 0, 0; - depth = 5; + p1 << 10, 0, 0; + p2 << 5, 0, 0; + contact << (p1 + p2) / 2; + depth = -5; normal << 1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(5, 0, 0)); - contact = transform.transform(Vec3f(5, 0, 0)); - depth = 5; + contact = transform.transform((p1 + p2) / 2); + depth = -5; normal = transform.getRotation() * Vec3f(1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = Transform3f(); tf2 = Transform3f(Vec3f(-5, 0, 0)); - contact << -5, 0, 0; - depth = 5; + p1 << -10, 0, 0; + p2 << -5, 0, 0; + contact << (p1 + p2) / 2; + depth = -5; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(-5, 0, 0)); - contact = transform.transform(Vec3f(-5, 0, 0)); - depth = 5; + contact = transform.transform((p1 + p2) / 2); + depth = -5; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1168,7 +1194,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacebox) { tf1 = Transform3f(); tf2 = Transform3f(); contact << -1.25, 0, 0; - depth = 2.5; + depth = -2.5; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1176,7 +1202,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacebox) { tf1 = transform; tf2 = transform; contact = transform.transform(Vec3f(-1.25, 0, 0)); - depth = 2.5; + depth = -2.5; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1184,7 +1210,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacebox) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(1.25, 0, 0)); contact << -0.625, 0, 0; - depth = 3.75; + depth = -3.75; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1192,7 +1218,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacebox) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(1.25, 0, 0)); contact = transform.transform(Vec3f(-0.625, 0, 0)); - depth = 3.75; + depth = -3.75; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1200,7 +1226,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacebox) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(-1.25, 0, 0)); contact << -1.875, 0, 0; - depth = 1.25; + depth = -1.25; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1208,7 +1234,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacebox) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(-1.25, 0, 0)); contact = transform.transform(Vec3f(-1.875, 0, 0)); - depth = 1.25; + depth = -1.25; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1216,7 +1242,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacebox) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(2.51, 0, 0)); contact << 0.005, 0, 0; - depth = 5.01; + depth = -5.01; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1224,7 +1250,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacebox) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(2.51, 0, 0)); contact = transform.transform(Vec3f(0.005, 0, 0)); - depth = 5.01; + depth = -5.01; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1261,48 +1287,53 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planebox) { tf1 = Transform3f(); tf2 = Transform3f(); - contact << 0, 0, 0; - depth = 2.5; + Vec3f p1(2.5, 0, 0); + Vec3f p2(0, 0, 0); + contact << (p1 + p2) / 2; + depth = -2.5; normal << 1, 0, 0; // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform; - contact = transform.transform(Vec3f(0, 0, 0)); - depth = 2.5; + contact = transform.transform((p1 + p2) / 2); + depth = -2.5; normal = transform.getRotation() * Vec3f(1, 0, 0); // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = Transform3f(); tf2 = Transform3f(Vec3f(1.25, 0, 0)); - contact << 1.25, 0, 0; - depth = 1.25; + p2 << 1.25, 0, 0; + contact << (p1 + p2) / 2; + depth = -1.25; normal << 1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(1.25, 0, 0)); - contact = transform.transform(Vec3f(1.25, 0, 0)); - depth = 1.25; + contact = transform.transform((p1 + p2) / 2); + depth = -1.25; normal = transform.getRotation() * Vec3f(1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = Transform3f(); tf2 = Transform3f(Vec3f(-1.25, 0, 0)); - contact << -1.25, 0, 0; - depth = 1.25; + p1 << -2.5, 0, 0; + p2 << -1.25, 0, 0; + contact << (p1 + p2) / 2; + depth = -1.25; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(-1.25, 0, 0)); - contact = transform.transform(Vec3f(-1.25, 0, 0)); - depth = 1.25; + contact = transform.transform((p1 + p2) / 2); + depth = -1.25; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1350,7 +1381,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = Transform3f(); tf2 = Transform3f(); contact << -2.5, 0, 0; - depth = 5; + depth = -5; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1358,7 +1389,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = transform; tf2 = transform; contact = transform.transform(Vec3f(-2.5, 0, 0)); - depth = 5; + depth = -5; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1366,7 +1397,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(2.5, 0, 0)); contact << -1.25, 0, 0; - depth = 7.5; + depth = -7.5; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1374,7 +1405,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(2.5, 0, 0)); contact = transform.transform(Vec3f(-1.25, 0, 0)); - depth = 7.5; + depth = -7.5; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1382,7 +1413,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(-2.5, 0, 0)); contact << -3.75, 0, 0; - depth = 2.5; + depth = -2.5; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1390,7 +1421,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(-2.5, 0, 0)); contact = transform.transform(Vec3f(-3.75, 0, 0)); - depth = 2.5; + depth = -2.5; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1398,7 +1429,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(5.1, 0, 0)); contact << 0.05, 0, 0; - depth = 10.1; + depth = -10.1; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1406,7 +1437,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(5.1, 0, 0)); contact = transform.transform(Vec3f(0.05, 0, 0)); - depth = 10.1; + depth = -10.1; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1426,7 +1457,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = Transform3f(); tf2 = Transform3f(); contact << 0, -2.5, 0; - depth = 5; + depth = -5; normal << 0, -1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1434,7 +1465,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = transform; tf2 = transform; contact = transform.transform(Vec3f(0, -2.5, 0)); - depth = 5; + depth = -5; normal = transform.getRotation() * Vec3f(0, -1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1442,7 +1473,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 2.5, 0)); contact << 0, -1.25, 0; - depth = 7.5; + depth = -7.5; normal << 0, -1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1450,7 +1481,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 2.5, 0)); contact = transform.transform(Vec3f(0, -1.25, 0)); - depth = 7.5; + depth = -7.5; normal = transform.getRotation() * Vec3f(0, -1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1458,7 +1489,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, -2.5, 0)); contact << 0, -3.75, 0; - depth = 2.5; + depth = -2.5; normal << 0, -1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1466,7 +1497,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, -2.5, 0)); contact = transform.transform(Vec3f(0, -3.75, 0)); - depth = 2.5; + depth = -2.5; normal = transform.getRotation() * Vec3f(0, -1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1474,7 +1505,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 5.1, 0)); contact << 0, 0.05, 0; - depth = 10.1; + depth = -10.1; normal << 0, -1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1482,7 +1513,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 5.1, 0)); contact = transform.transform(Vec3f(0, 0.05, 0)); - depth = 10.1; + depth = -10.1; normal = transform.getRotation() * Vec3f(0, -1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1502,7 +1533,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = Transform3f(); tf2 = Transform3f(); contact << 0, 0, -5; - depth = 10; + depth = -10; normal << 0, 0, -1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1510,7 +1541,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = transform; tf2 = transform; contact = transform.transform(Vec3f(0, 0, -5)); - depth = 10; + depth = -10; normal = transform.getRotation() * Vec3f(0, 0, -1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1518,7 +1549,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 0, 2.5)); contact << 0, 0, -3.75; - depth = 12.5; + depth = -12.5; normal << 0, 0, -1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1526,7 +1557,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 0, 2.5)); contact = transform.transform(Vec3f(0, 0, -3.75)); - depth = 12.5; + depth = -12.5; normal = transform.getRotation() * Vec3f(0, 0, -1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1534,7 +1565,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 0, -2.5)); contact << 0, 0, -6.25; - depth = 7.5; + depth = -7.5; normal << 0, 0, -1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1542,7 +1573,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 0, -2.5)); contact = transform.transform(Vec3f(0, 0, -6.25)); - depth = 7.5; + depth = -7.5; normal = transform.getRotation() * Vec3f(0, 0, -1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1550,7 +1581,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 0, 10.1)); contact << 0, 0, 0.05; - depth = 20.1; + depth = -20.1; normal << 0, 0, -1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1558,7 +1589,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecapsule) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 0, 10.1)); contact = transform.transform(Vec3f(0, 0, 0.05)); - depth = 20.1; + depth = -20.1; normal = transform.getRotation() * Vec3f(0, 0, -1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1591,7 +1622,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = Transform3f(); tf2 = Transform3f(); contact << 0, 0, 0; - depth = 5; + depth = -5; normal << 1, 0, 0; // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, 0x0, 0x0, true); @@ -1599,7 +1630,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = transform; tf2 = transform; contact = transform.transform(Vec3f(0, 0, 0)); - depth = 5; + depth = -5; normal = transform.getRotation() * Vec3f(1, 0, 0); // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, 0x0, 0x0, true); @@ -1607,7 +1638,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(2.5, 0, 0)); contact << 2.5, 0, 0; - depth = 2.5; + depth = -2.5; normal << 1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, &depth, &normal); @@ -1615,7 +1646,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(2.5, 0, 0)); contact = transform.transform(Vec3f(2.5, 0, 0)); - depth = 2.5; + depth = -2.5; normal = transform.getRotation() * Vec3f(1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, &depth, &normal); @@ -1623,7 +1654,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(-2.5, 0, 0)); contact << -2.5, 0, 0; - depth = 2.5; + depth = -2.5; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, &depth, &normal); @@ -1631,7 +1662,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(-2.5, 0, 0)); contact = transform.transform(Vec3f(-2.5, 0, 0)); - depth = 2.5; + depth = -2.5; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, &depth, &normal); @@ -1661,7 +1692,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = Transform3f(); tf2 = Transform3f(); contact << 0, 0, 0; - depth = 5; + depth = -5; normal << 0, 1, 0; // (0, 1, 0) or (0, -1, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, &depth, 0x0, true); @@ -1669,7 +1700,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = transform; tf2 = transform; contact = transform.transform(Vec3f(0, 0, 0)); - depth = 5; + depth = -5; normal = transform.getRotation() * Vec3f(0, 1, 0); // (0, 1, 0) or (0, -1, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, &depth, &normal, true); @@ -1677,7 +1708,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 2.5, 0)); contact << 0, 2.5, 0; - depth = 2.5; + depth = -2.5; normal << 0, 1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, &depth, 0x0); @@ -1685,7 +1716,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 2.5, 0)); contact = transform.transform(Vec3f(0, 2.5, 0)); - depth = 2.5; + depth = -2.5; normal = transform.getRotation() * Vec3f(0, 1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, &depth, 0x0); @@ -1693,7 +1724,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, -2.5, 0)); contact << 0, -2.5, 0; - depth = 2.5; + depth = -2.5; normal << 0, -1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, &depth, &normal); @@ -1701,7 +1732,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, -2.5, 0)); contact = transform.transform(Vec3f(0, -2.5, 0)); - depth = 2.5; + depth = -2.5; normal = transform.getRotation() * Vec3f(0, -1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, &depth, &normal); @@ -1731,7 +1762,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = Transform3f(); tf2 = Transform3f(); contact << 0, 0, 0; - depth = 10; + depth = -10; normal << 0, 0, 1; // (0, 0, 1) or (0, 0, -1) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, 0x0, 0x0, true); @@ -1739,7 +1770,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = transform; tf2 = transform; contact = transform.transform(Vec3f(0, 0, 0)); - depth = 10; + depth = -10; normal = transform.getRotation() * Vec3f(0, 0, 1); // (0, 0, 1) or (0, 0, -1) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, &depth, 0x0, true); @@ -1747,7 +1778,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 0, 2.5)); contact << 0, 0, 2.5; - depth = 7.5; + depth = -7.5; normal << 0, 0, 1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, &depth, 0x0); @@ -1755,7 +1786,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 0, 2.5)); contact = transform.transform(Vec3f(0, 0, 2.5)); - depth = 7.5; + depth = -7.5; normal = transform.getRotation() * Vec3f(0, 0, 1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, &depth, 0x0); @@ -1763,7 +1794,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 0, -2.5)); contact << 0, 0, -2.5; - depth = 7.5; + depth = -7.5; normal << 0, 0, -1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, &depth, 0x0); @@ -1771,7 +1802,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecapsule) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 0, -2.5)); contact = transform.transform(Vec3f(0, 0, -2.5)); - depth = 7.5; + depth = -7.5; normal = transform.getRotation() * Vec3f(0, 0, -1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, 0x0, &depth, 0x0); @@ -1814,7 +1845,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = Transform3f(); tf2 = Transform3f(); contact << -2.5, 0, 0; - depth = 5; + depth = -5; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1822,7 +1853,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = transform; tf2 = transform; contact = transform.transform(Vec3f(-2.5, 0, 0)); - depth = 5; + depth = -5; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1830,7 +1861,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(2.5, 0, 0)); contact << -1.25, 0, 0; - depth = 7.5; + depth = -7.5; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1838,7 +1869,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(2.5, 0, 0)); contact = transform.transform(Vec3f(-1.25, 0, 0)); - depth = 7.5; + depth = -7.5; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1846,7 +1877,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(-2.5, 0, 0)); contact << -3.75, 0, 0; - depth = 2.5; + depth = -2.5; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1854,7 +1885,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(-2.5, 0, 0)); contact = transform.transform(Vec3f(-3.75, 0, 0)); - depth = 2.5; + depth = -2.5; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1862,7 +1893,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(5.1, 0, 0)); contact << 0.05, 0, 0; - depth = 10.1; + depth = -10.1; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1870,7 +1901,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(5.1, 0, 0)); contact = transform.transform(Vec3f(0.05, 0, 0)); - depth = 10.1; + depth = -10.1; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1890,7 +1921,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = Transform3f(); tf2 = Transform3f(); contact << 0, -2.5, 0; - depth = 5; + depth = -5; normal << 0, -1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1898,7 +1929,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = transform; tf2 = transform; contact = transform.transform(Vec3f(0, -2.5, 0)); - depth = 5; + depth = -5; normal = transform.getRotation() * Vec3f(0, -1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1906,7 +1937,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 2.5, 0)); contact << 0, -1.25, 0; - depth = 7.5; + depth = -7.5; normal << 0, -1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1914,7 +1945,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 2.5, 0)); contact = transform.transform(Vec3f(0, -1.25, 0)); - depth = 7.5; + depth = -7.5; normal = transform.getRotation() * Vec3f(0, -1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1922,7 +1953,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, -2.5, 0)); contact << 0, -3.75, 0; - depth = 2.5; + depth = -2.5; normal << 0, -1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1930,7 +1961,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, -2.5, 0)); contact = transform.transform(Vec3f(0, -3.75, 0)); - depth = 2.5; + depth = -2.5; normal = transform.getRotation() * Vec3f(0, -1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1938,7 +1969,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 5.1, 0)); contact << 0, 0.05, 0; - depth = 10.1; + depth = -10.1; normal << 0, -1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1946,7 +1977,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 5.1, 0)); contact = transform.transform(Vec3f(0, 0.05, 0)); - depth = 10.1; + depth = -10.1; normal = transform.getRotation() * Vec3f(0, -1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1966,7 +1997,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = Transform3f(); tf2 = Transform3f(); contact << 0, 0, -2.5; - depth = 5; + depth = -5; normal << 0, 0, -1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1974,7 +2005,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = transform; tf2 = transform; contact = transform.transform(Vec3f(0, 0, -2.5)); - depth = 5; + depth = -5; normal = transform.getRotation() * Vec3f(0, 0, -1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1982,7 +2013,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 0, 2.5)); contact << 0, 0, -1.25; - depth = 7.5; + depth = -7.5; normal << 0, 0, -1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1990,7 +2021,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 0, 2.5)); contact = transform.transform(Vec3f(0, 0, -1.25)); - depth = 7.5; + depth = -7.5; normal = transform.getRotation() * Vec3f(0, 0, -1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -1998,7 +2029,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 0, -2.5)); contact << 0, 0, -3.75; - depth = 2.5; + depth = -2.5; normal << 0, 0, -1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2006,7 +2037,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 0, -2.5)); contact = transform.transform(Vec3f(0, 0, -3.75)); - depth = 2.5; + depth = -2.5; normal = transform.getRotation() * Vec3f(0, 0, -1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2014,7 +2045,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 0, 5.1)); contact << 0, 0, 0.05; - depth = 10.1; + depth = -10.1; normal << 0, 0, -1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2022,7 +2053,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecylinder) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 0, 5.1)); contact = transform.transform(Vec3f(0, 0, 0.05)); - depth = 10.1; + depth = -10.1; normal = transform.getRotation() * Vec3f(0, 0, -1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2051,51 +2082,80 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecylinder) { Vec3f contact; FCL_REAL depth; Vec3f normal; + Vec3f p1, p2; - tf1 = Transform3f(); + FCL_REAL eps = 1e-6; + tf1 = Transform3f(Vec3f(eps, 0, 0)); tf2 = Transform3f(); - contact << 0, 0, 0; - depth = 5; - normal << 1, 0, 0; // (1, 0, 0) or (-1, 0, 0) + p1 << -5 + eps, 0, 0; + p2 << 0, 0, 0; + contact << (p1 + p2) / 2; + depth = -5 + eps; + normal << -1, 0, 0; // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); - tf1 = transform; + tf1 = transform * tf1; tf2 = transform; - contact = transform.transform(Vec3f(0, 0, 0)); - depth = 5; - normal = transform.getRotation() * Vec3f(1, 0, 0); // (1, 0, 0) or (-1, 0, 0) + contact = transform.transform((p1 + p2) / 2); + depth = -5 + eps; + normal = + transform.getRotation() * Vec3f(-1, 0, 0); // (1, 0, 0) or (-1, 0, 0) + SET_LINE; + testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); + + eps = -1e-6; + tf1 = Transform3f(Vec3f(eps, 0, 0)); + tf2 = Transform3f(); + p1 << 5 + eps, 0, 0; + p2 << 0, 0, 0; + contact << (p1 + p2) / 2; + depth = -5 - eps; + normal << -1, 0, 0; // (1, 0, 0) or (-1, 0, 0) + SET_LINE; + testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); + + tf1 = transform * tf1; + tf2 = transform; + contact = transform.transform((p1 + p2) / 2); + depth = -5 - eps; + normal = + transform.getRotation() * Vec3f(-1, 0, 0); // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); tf1 = Transform3f(); tf2 = Transform3f(Vec3f(2.5, 0, 0)); - contact << 2.5, 0, 0; - depth = 2.5; + p1 << 5, 0, 0; + p2 << 2.5, 0, 0; + contact << (p1 + p2) / 2; + depth = -2.5; normal << 1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(2.5, 0, 0)); - contact = transform.transform(Vec3f(2.5, 0, 0)); - depth = 2.5; + contact = transform.transform((p1 + p2) / 2); + depth = -2.5; normal = transform.getRotation() * Vec3f(1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = Transform3f(); tf2 = Transform3f(Vec3f(-2.5, 0, 0)); - contact << -2.5, 0, 0; - depth = 2.5; + p1 << -5, 0, 0; + p2 << -2.5, 0, 0; + contact << (p1 + p2) / 2; + depth = -2.5; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(-2.5, 0, 0)); - contact = transform.transform(Vec3f(-2.5, 0, 0)); - depth = 2.5; + contact = transform.transform((p1 + p2) / 2); + depth = -2.5; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2122,50 +2182,76 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecylinder) { hs = Plane(Vec3f(0, 1, 0), 0); - tf1 = Transform3f(); + eps = 1e-6; + tf1 = Transform3f(Vec3f(0, eps, 0)); tf2 = Transform3f(); - contact << 0, 0, 0; - depth = 5; + p1 << 0, -5 + eps, 0; + p2 << 0, 0, 0; + contact << (p1 + p2) / 2; + depth = -5 + eps; normal << 0, 1, 0; // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); - tf1 = transform; + tf1 = transform * tf1; tf2 = transform; - contact = transform.transform(Vec3f(0, 0, 0)); - depth = 5; + contact = transform.transform((p1 + p2) / 2); + depth = -5 + eps; + normal = transform.getRotation() * Vec3f(0, 1, 0); // (1, 0, 0) or (-1, 0, 0) + SET_LINE; + testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); + + eps = -1e-6; + tf1 = Transform3f(Vec3f(0, eps, 0)); + tf2 = Transform3f(); + p1 << 0, 5 + eps, 0; + p2 << 0, 0, 0; + contact << (p1 + p2) / 2; + depth = -5 - eps; + normal << 0, 1, 0; // (1, 0, 0) or (-1, 0, 0) + SET_LINE; + testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); + + tf1 = transform * tf1; + tf2 = transform; + contact = transform.transform((p1 + p2) / 2); + depth = -5 - eps; normal = transform.getRotation() * Vec3f(0, 1, 0); // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 2.5, 0)); - contact << 0, 2.5, 0; - depth = 2.5; + p1 << 0, 5, 0; + p2 << 0, 2.5, 0; + contact << (p1 + p2) / 2; + depth = -2.5; normal << 0, 1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 2.5, 0)); - contact = transform.transform(Vec3f(0, 2.5, 0)); - depth = 2.5; + contact = transform.transform((p1 + p2) / 2); + depth = -2.5; normal = transform.getRotation() * Vec3f(0, 1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, -2.5, 0)); - contact << 0, -2.5, 0; - depth = 2.5; + p1 << 0, -5, 0; + p2 << 0, -2.5, 0; + contact << (p1 + p2) / 2; + depth = -2.5; normal << 0, -1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, -2.5, 0)); - contact = transform.transform(Vec3f(0, -2.5, 0)); - depth = 2.5; + contact = transform.transform((p1 + p2) / 2); + depth = -2.5; normal = transform.getRotation() * Vec3f(0, -1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2192,50 +2278,76 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecylinder) { hs = Plane(Vec3f(0, 0, 1), 0); - tf1 = Transform3f(); + eps = 1e-6; + tf1 = Transform3f(Vec3f(0, 0, eps)); tf2 = Transform3f(); - contact << 0, 0, 0; - depth = 5; + p1 << 0, 0, -5 + eps; + p2 << 0, 0, 0; + contact << (p1 + p2) / 2; + depth = -5 + eps; normal << 0, 0, 1; // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); - tf1 = transform; + tf1 = transform * tf1; tf2 = transform; - contact = transform.transform(Vec3f(0, 0, 0)); - depth = 5; + contact = transform.transform((p1 + p2) / 2); + depth = -5 + eps; + normal = transform.getRotation() * Vec3f(0, 0, 1); // (1, 0, 0) or (-1, 0, 0) + SET_LINE; + testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); + + eps = -1e-6; + tf1 = Transform3f(Vec3f(0, 0, eps)); + tf2 = Transform3f(); + p1 << 0, 0, 5 + eps; + p2 << 0, 0, 0; + contact << (p1 + p2) / 2; + depth = -5 - eps; + normal << 0, 0, 1; // (1, 0, 0) or (-1, 0, 0) + SET_LINE; + testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); + + tf1 = transform * tf1; + tf2 = transform; + contact = transform.transform((p1 + p2) / 2); + depth = -5 - eps; normal = transform.getRotation() * Vec3f(0, 0, 1); // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 0, 2.5)); - contact << 0, 0, 2.5; - depth = 2.5; + p1 << 0, 0, 5; + p2 << 0, 0, 2.5; + contact << (p1 + p2) / 2; + depth = -2.5; normal << 0, 0, 1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 0, 2.5)); - contact = transform.transform(Vec3f(0, 0, 2.5)); - depth = 2.5; + contact = transform.transform((p1 + p2) / 2); + depth = -2.5; normal = transform.getRotation() * Vec3f(0, 0, 1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 0, -2.5)); - contact << 0, 0, -2.5; - depth = 2.5; + p1 << 0, 0, -5.; + p2 << 0, 0, -2.5; + contact << (p1 + p2) / 2; + depth = -2.5; normal << 0, 0, -1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 0, -2.5)); - contact = transform.transform(Vec3f(0, 0, -2.5)); - depth = 2.5; + contact = transform.transform((p1 + p2) / 2); + depth = -2.5; normal = transform.getRotation() * Vec3f(0, 0, -1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2278,7 +2390,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = Transform3f(); tf2 = Transform3f(); contact << -2.5, 0, -5; - depth = 5; + depth = -5; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2286,7 +2398,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = transform; tf2 = transform; contact = transform.transform(Vec3f(-2.5, 0, -5)); - depth = 5; + depth = -5; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2294,7 +2406,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(2.5, 0, 0)); contact << -1.25, 0, -5; - depth = 7.5; + depth = -7.5; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2302,7 +2414,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(2.5, 0, 0)); contact = transform.transform(Vec3f(-1.25, 0, -5)); - depth = 7.5; + depth = -7.5; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2310,7 +2422,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(-2.5, 0, 0)); contact << -3.75, 0, -5; - depth = 2.5; + depth = -2.5; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2318,7 +2430,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(-2.5, 0, 0)); contact = transform.transform(Vec3f(-3.75, 0, -5)); - depth = 2.5; + depth = -2.5; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2326,7 +2438,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(5.1, 0, 0)); contact << 0.05, 0, -5; - depth = 10.1; + depth = -10.1; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2334,7 +2446,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(5.1, 0, 0)); contact = transform.transform(Vec3f(0.05, 0, -5)); - depth = 10.1; + depth = -10.1; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2354,7 +2466,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = Transform3f(); tf2 = Transform3f(); contact << 0, -2.5, -5; - depth = 5; + depth = -5; normal << 0, -1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2362,7 +2474,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = transform; tf2 = transform; contact = transform.transform(Vec3f(0, -2.5, -5)); - depth = 5; + depth = -5; normal = transform.getRotation() * Vec3f(0, -1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2370,7 +2482,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 2.5, 0)); contact << 0, -1.25, -5; - depth = 7.5; + depth = -7.5; normal << 0, -1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2378,7 +2490,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 2.5, 0)); contact = transform.transform(Vec3f(0, -1.25, -5)); - depth = 7.5; + depth = -7.5; normal = transform.getRotation() * Vec3f(0, -1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2386,7 +2498,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, -2.5, 0)); contact << 0, -3.75, -5; - depth = 2.5; + depth = -2.5; normal << 0, -1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2394,7 +2506,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, -2.5, 0)); contact = transform.transform(Vec3f(0, -3.75, -5)); - depth = 2.5; + depth = -2.5; normal = transform.getRotation() * Vec3f(0, -1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2402,7 +2514,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 5.1, 0)); contact << 0, 0.05, -5; - depth = 10.1; + depth = -10.1; normal << 0, -1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2410,7 +2522,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 5.1, 0)); contact = transform.transform(Vec3f(0, 0.05, -5)); - depth = 10.1; + depth = -10.1; normal = transform.getRotation() * Vec3f(0, -1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2430,7 +2542,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = Transform3f(); tf2 = Transform3f(); contact << 0, 0, -2.5; - depth = 5; + depth = -5; normal << 0, 0, -1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2438,7 +2550,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = transform; tf2 = transform; contact = transform.transform(Vec3f(0, 0, -2.5)); - depth = 5; + depth = -5; normal = transform.getRotation() * Vec3f(0, 0, -1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2446,7 +2558,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 0, 2.5)); contact << 0, 0, -1.25; - depth = 7.5; + depth = -7.5; normal << 0, 0, -1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2454,7 +2566,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 0, 2.5)); contact = transform.transform(Vec3f(0, 0, -1.25)); - depth = 7.5; + depth = -7.5; normal = transform.getRotation() * Vec3f(0, 0, -1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2462,7 +2574,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 0, -2.5)); contact << 0, 0, -3.75; - depth = 2.5; + depth = -2.5; normal << 0, 0, -1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2470,7 +2582,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 0, -2.5)); contact = transform.transform(Vec3f(0, 0, -3.75)); - depth = 2.5; + depth = -2.5; normal = transform.getRotation() * Vec3f(0, 0, -1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2478,7 +2590,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 0, 5.1)); contact << 0, 0, 0.05; - depth = 10.1; + depth = -10.1; normal << 0, 0, -1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2486,7 +2598,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_halfspacecone) { tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 0, 5.1)); contact = transform.transform(Vec3f(0, 0, 0.05)); - depth = 10.1; + depth = -10.1; normal = transform.getRotation() * Vec3f(0, 0, -1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2515,19 +2627,43 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecone) { Vec3f contact; FCL_REAL depth; Vec3f normal; + Vec3f p1, p2; - tf1 = Transform3f(); + FCL_REAL eps = 1e-6; + tf1 = Transform3f(Vec3f(eps, 0, 0)); tf2 = Transform3f(); - contact << 0, 0, 0; - depth = 5; + p1 << -5 + eps, 0, -5; + p2 << 0, 0, -5; + contact << (p1 + p2) / 2; + depth = -5 + eps; normal << 1, 0, 0; // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); - tf1 = transform; + tf1 = transform * tf1; tf2 = transform; - contact = transform.transform(Vec3f(0, 0, 0)); - depth = 5; + contact = transform.transform((p1 + p2) / 2); + depth = -5 + eps; + normal = + transform.getRotation() * Vec3f(-1, 0, 0); // (1, 0, 0) or (-1, 0, 0) + SET_LINE; + testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); + + eps = -1e-6; + tf1 = Transform3f(Vec3f(eps, 0, 0)); + tf2 = Transform3f(); + p1 << 5 + eps, 0, -5; + p2 << 0, 0, -5; + contact << (p1 + p2) / 2; + depth = -5 - eps; + normal << 1, 0, 0; // (1, 0, 0) or (-1, 0, 0) + SET_LINE; + testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); + + tf1 = transform * tf1; + tf2 = transform; + contact = transform.transform((p1 + p2) / 2); + depth = -5 - eps; normal = transform.getRotation() * Vec3f(-1, 0, 0); // (1, 0, 0) or (-1, 0, 0) SET_LINE; @@ -2535,32 +2671,36 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecone) { tf1 = Transform3f(); tf2 = Transform3f(Vec3f(2.5, 0, 0)); - contact << 2.5, 0, -2.5; - depth = 2.5; + p1 << 5, 0, -5; + p2 << 2.5, 0, -5; + contact << (p1 + p2) / 2; + depth = -2.5; normal << 1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(2.5, 0, 0)); - contact = transform.transform(Vec3f(2.5, 0, -2.5)); - depth = 2.5; + contact = transform.transform((p1 + p2) / 2); + depth = -2.5; normal = transform.getRotation() * Vec3f(1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = Transform3f(); tf2 = Transform3f(Vec3f(-2.5, 0, 0)); - contact << -2.5, 0, -2.5; - depth = 2.5; + p1 << -5, 0, -5; + p2 << -2.5, 0, -5; + contact << (p1 + p2) / 2; + depth = -2.5; normal << -1, 0, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(-2.5, 0, 0)); - contact = transform.transform(Vec3f(-2.5, 0, -2.5)); - depth = 2.5; + contact = transform.transform((p1 + p2) / 2); + depth = -2.5; normal = transform.getRotation() * Vec3f(-1, 0, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2587,50 +2727,76 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecone) { hs = Plane(Vec3f(0, 1, 0), 0); - tf1 = Transform3f(); + eps = 1e-6; + tf1 = Transform3f(Vec3f(0, eps, 0)); tf2 = Transform3f(); - contact << 0, 0, 0; - depth = 5; + p1 << 0, -5 + eps, -5; + p2 << 0, 0, -5; + contact << (p1 + p2) / 2; + depth = -5 + eps; normal << 0, 1, 0; // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); - tf1 = transform; + tf1 = transform * tf1; tf2 = transform; - contact = transform.transform(Vec3f(0, 0, 0)); - depth = 5; + contact = transform.transform((p1 + p2) / 2); + depth = -5 + eps; + normal = transform.getRotation() * Vec3f(0, 1, 0); // (1, 0, 0) or (-1, 0, 0) + SET_LINE; + testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); + + eps = -1e-6; + tf1 = Transform3f(Vec3f(0, eps, 0)); + tf2 = Transform3f(); + p1 << 0, 5 + eps, -5; + p2 << 0, 0, -5; + contact << (p1 + p2) / 2; + depth = -5 - eps; + normal << 0, 1, 0; // (1, 0, 0) or (-1, 0, 0) + SET_LINE; + testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); + + tf1 = transform * tf1; + tf2 = transform; + contact = transform.transform((p1 + p2) / 2); + depth = -5 - eps; normal = transform.getRotation() * Vec3f(0, 1, 0); // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 2.5, 0)); - contact << 0, 2.5, -2.5; - depth = 2.5; + p1 << 0, 5, -5; + p2 << 0, 2.5, -5; + contact << (p1 + p2) / 2; + depth = -2.5; normal << 0, 1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 2.5, 0)); - contact = transform.transform(Vec3f(0, 2.5, -2.5)); - depth = 2.5; + contact = transform.transform((p1 + p2) / 2); + depth = -2.5; normal = transform.getRotation() * Vec3f(0, 1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, -2.5, 0)); - contact << 0, -2.5, -2.5; - depth = 2.5; + p1 << 0, -5, -5; + p2 << 0, -2.5, -5; + contact << (p1 + p2) / 2; + depth = -2.5; normal << 0, -1, 0; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, -2.5, 0)); - contact = transform.transform(Vec3f(0, -2.5, -2.5)); - depth = 2.5; + contact = transform.transform((p1 + p2) / 2); + depth = -2.5; normal = transform.getRotation() * Vec3f(0, -1, 0); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2657,50 +2823,76 @@ BOOST_AUTO_TEST_CASE(shapeIntersection_planecone) { hs = Plane(Vec3f(0, 0, 1), 0); - tf1 = Transform3f(); + eps = 1e-6; + tf1 = Transform3f(Vec3f(0, 0, eps)); tf2 = Transform3f(); - contact << 0, 0, 0; - depth = 5; + p1 << 0, 0, -5 + eps; + p2 << 0, 0, 0; + contact << (p1 + p2) / 2; + depth = -5 + eps; normal << 0, 0, 1; // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); - tf1 = transform; + tf1 = transform * tf1; tf2 = transform; - contact = transform.transform(Vec3f(0, 0, 0)); - depth = 5; + contact = transform.transform((p1 + p2) / 2); + depth = -5 + eps; + normal = transform.getRotation() * Vec3f(0, 0, 1); // (1, 0, 0) or (-1, 0, 0) + SET_LINE; + testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); + + eps = -1e-6; + tf1 = Transform3f(Vec3f(0, 0, eps)); + tf2 = Transform3f(); + p1 << 0, 0, 5 + eps; + p2 << 0, 0, 0; + contact << (p1 + p2) / 2; + depth = -5 - eps; + normal << 0, 0, 1; // (1, 0, 0) or (-1, 0, 0) + SET_LINE; + testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); + + tf1 = transform * tf1; + tf2 = transform; + contact = transform.transform((p1 + p2) / 2); + depth = -5 - eps; normal = transform.getRotation() * Vec3f(0, 0, 1); // (1, 0, 0) or (-1, 0, 0) SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal, true); tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 0, 2.5)); - contact << 0, 0, 2.5; - depth = 2.5; + p1 << 0, 0, 5; + p2 << 0, 0, 2.5; + contact << (p1 + p2) / 2; + depth = -2.5; normal << 0, 0, 1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 0, 2.5)); - contact = transform.transform(Vec3f(0, 0, 2.5)); - depth = 2.5; + contact = transform.transform((p1 + p2) / 2); + depth = -2.5; normal = transform.getRotation() * Vec3f(0, 0, 1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = Transform3f(); tf2 = Transform3f(Vec3f(0, 0, -2.5)); - contact << 0, 0, -2.5; - depth = 2.5; + p1 << 0, 0, -5; + p2 << 0, 0, -2.5; + contact << (p1 + p2) / 2; + depth = -2.5; normal << 0, 0, -1; SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); tf1 = transform; tf2 = transform * Transform3f(Vec3f(0, 0, -2.5)); - contact = transform.transform(Vec3f(0, 0, -2.5)); - depth = 2.5; + contact = transform.transform((p1 + p2) / 2); + depth = -2.5; normal = transform.getRotation() * Vec3f(0, 0, -1); SET_LINE; testShapeIntersection(s, tf1, hs, tf2, true, &contact, &depth, &normal); @@ -2823,12 +3015,12 @@ BOOST_AUTO_TEST_CASE(shapeDistance_boxbox) { res = solver1.shapeDistance(s1, Transform3f(), s2, Transform3f(), dist, closest_p1, closest_p2, normal); BOOST_CHECK(dist <= 0); - BOOST_CHECK_FALSE(res); + BOOST_CHECK(res); res = solver1.shapeDistance(s1, transform, s2, transform, dist, closest_p1, closest_p2, normal); BOOST_CHECK(dist <= 0); - BOOST_CHECK_FALSE(res); + BOOST_CHECK(res); res = solver1.shapeDistance(s2, Transform3f(), s2, Transform3f(Vec3f(10.1, 0, 0)), dist, closest_p1, @@ -2975,12 +3167,12 @@ BOOST_AUTO_TEST_CASE(shapeDistance_cylindercylinder) { res = solver1.shapeDistance(s1, Transform3f(), s2, Transform3f(), dist, closest_p1, closest_p2, normal); BOOST_CHECK(dist <= 0); - BOOST_CHECK_FALSE(res); + BOOST_CHECK(res); res = solver1.shapeDistance(s1, transform, s2, transform, dist, closest_p1, closest_p2, normal); BOOST_CHECK(dist <= 0); - BOOST_CHECK_FALSE(res); + BOOST_CHECK(res); res = solver1.shapeDistance(s1, Transform3f(), s2, Transform3f(Vec3f(10.1, 0, 0)), dist, closest_p1, @@ -3021,12 +3213,12 @@ BOOST_AUTO_TEST_CASE(shapeDistance_conecone) { res = solver1.shapeDistance(s1, Transform3f(), s2, Transform3f(), dist, closest_p1, closest_p2, normal); BOOST_CHECK(dist <= 0); - BOOST_CHECK_FALSE(res); + BOOST_CHECK(res); res = solver1.shapeDistance(s1, transform, s2, transform, dist, closest_p1, closest_p2, normal); BOOST_CHECK(dist <= 0); - BOOST_CHECK_FALSE(res); + BOOST_CHECK(res); res = solver1.shapeDistance(s1, Transform3f(), s2, Transform3f(Vec3f(10.1, 0, 0)), dist, closest_p1, @@ -3067,12 +3259,12 @@ BOOST_AUTO_TEST_CASE(shapeDistance_conecylinder) { res = solver1.shapeDistance(s1, Transform3f(), s2, Transform3f(), dist, closest_p1, closest_p2, normal); BOOST_CHECK(dist <= 0); - BOOST_CHECK_FALSE(res); + BOOST_CHECK(res); res = solver1.shapeDistance(s1, transform, s2, transform, dist, closest_p1, closest_p2, normal); BOOST_CHECK(dist <= 0); - BOOST_CHECK_FALSE(res); + BOOST_CHECK(res); res = solver1.shapeDistance(s1, Transform3f(), s2, Transform3f(Vec3f(10.1, 0, 0)), dist, closest_p1, @@ -3210,7 +3402,7 @@ BOOST_AUTO_TEST_CASE(shapeIntersectionGJK_boxbox) { // FCL_REAL depth; Vec3f normal; - Quaternion3f q; + Quatf q; q = AngleAxis((FCL_REAL)3.140 / 6, UnitZ); tf1 = Transform3f(); @@ -3807,12 +3999,12 @@ BOOST_AUTO_TEST_CASE(boxbox) { res = solver2.shapeDistance(s1, Transform3f(), s2, Transform3f(), dist, closest_p1, closest_p2, normal); BOOST_CHECK(dist <= 0); - BOOST_CHECK_FALSE(res); + BOOST_CHECK(res); res = solver2.shapeDistance(s1, transform, s2, transform, dist, closest_p1, closest_p2, normal); BOOST_CHECK(dist <= 0); - BOOST_CHECK_FALSE(res); + BOOST_CHECK(res); res = solver2.shapeDistance(s1, Transform3f(), s2, Transform3f(Vec3f(15.1, 0, 0)), dist, closest_p1, @@ -3899,12 +4091,12 @@ BOOST_AUTO_TEST_CASE(cylindercylinder) { res = solver2.shapeDistance(s1, Transform3f(), s2, Transform3f(), dist, closest_p1, closest_p2, normal); BOOST_CHECK(dist <= 0); - BOOST_CHECK_FALSE(res); + BOOST_CHECK(res); res = solver2.shapeDistance(s1, transform, s2, transform, dist, closest_p1, closest_p2, normal); BOOST_CHECK(dist <= 0); - BOOST_CHECK_FALSE(res); + BOOST_CHECK(res); res = solver2.shapeDistance(s1, Transform3f(), s2, Transform3f(Vec3f(10.1, 0, 0)), dist, closest_p1, @@ -3945,12 +4137,12 @@ BOOST_AUTO_TEST_CASE(conecone) { res = solver2.shapeDistance(s1, Transform3f(), s2, Transform3f(), dist, closest_p1, closest_p2, normal); BOOST_CHECK(dist <= 0); - BOOST_CHECK_FALSE(res); + BOOST_CHECK(res); res = solver2.shapeDistance(s1, transform, s2, transform, dist, closest_p1, closest_p2, normal); BOOST_CHECK(dist <= 0); - BOOST_CHECK_FALSE(res); + BOOST_CHECK(res); res = solver2.shapeDistance(s1, Transform3f(), s2, Transform3f(Vec3f(10.1, 0, 0)), dist, closest_p1, diff --git a/test/gjk.cpp b/test/gjk.cpp index 32b0ae4c022fea885103c908932d289428551957..2b6f50b7c6f014916b8dbb7de04688e4f5548125 100644 --- a/test/gjk.cpp +++ b/test/gjk.cpp @@ -49,7 +49,7 @@ using hpp::fcl::FCL_REAL; using hpp::fcl::GJKSolver; using hpp::fcl::GJKVariant; using hpp::fcl::Matrix3f; -using hpp::fcl::Quaternion3f; +using hpp::fcl::Quatf; using hpp::fcl::Transform3f; using hpp::fcl::TriangleP; using hpp::fcl::Vec3f; @@ -103,8 +103,8 @@ void test_gjk_distance_triangle_triangle( Q2_loc = Vec3f(-10.926, -1.284259033203125, 3.7281499023437501); Q3_loc = Vec3f(-10.926, -1.2866180419921875, 3.72335400390625); Transform3f tf( - Quaternion3f(-0.42437287410898855, -0.26862477561450587, - -0.46249645019513175, 0.73064726592483387), + Quatf(-0.42437287410898855, -0.26862477561450587, + -0.46249645019513175, 0.73064726592483387), Vec3f(-12.824601270753471, -1.6840516940066426, 3.8914453043793844)); tf1 = tf; } else if (i == 1) { @@ -323,8 +323,8 @@ void test_gjk_unit_sphere(FCL_REAL center_distance, Vec3f ray, Sphere sphere(1.); typedef Eigen::Matrix<FCL_REAL, 4, 1> Vec4f; - Transform3f tf0(Quaternion3f(Vec4f::Random().normalized()), Vec3f::Zero()), - tf1(Quaternion3f(Vec4f::Random().normalized()), center_distance * ray); + Transform3f tf0(Quatf(Vec4f::Random().normalized()), Vec3f::Zero()), + tf1(Quatf(Vec4f::Random().normalized()), center_distance * ray); details::MinkowskiDiff shape; shape.set(&sphere, &sphere, tf0, tf1); diff --git a/test/gjk_convergence_criterion.cpp b/test/gjk_convergence_criterion.cpp index cb239d90f8a4c520afe5a142d496bfaa71384fab..844a327b60ddaa2cad0ae3c0e49f41088db53a03 100644 --- a/test/gjk_convergence_criterion.cpp +++ b/test/gjk_convergence_criterion.cpp @@ -136,21 +136,21 @@ void test_gjk_cv_criterion(const ShapeBase& shape0, const ShapeBase& shape1, mink_diff.set(&shape0, &shape1, identity, transforms[i]); GJK::Status res1 = gjk1.evaluate(mink_diff, init_guess, init_support_guess); - BOOST_CHECK(gjk1.getIterations() <= max_iterations); + BOOST_CHECK(gjk1.iterations <= max_iterations); Vec3f ray1 = gjk1.ray; res1 = gjk1.evaluate(mink_diff, init_guess, init_support_guess); BOOST_CHECK(res1 != GJK::Status::Failed); EIGEN_VECTOR_IS_APPROX(gjk1.ray, ray1, 1e-8); GJK::Status res2 = gjk2.evaluate(mink_diff, init_guess, init_support_guess); - BOOST_CHECK(gjk2.getIterations() <= max_iterations); + BOOST_CHECK(gjk2.iterations <= max_iterations); Vec3f ray2 = gjk2.ray; res2 = gjk2.evaluate(mink_diff, init_guess, init_support_guess); BOOST_CHECK(res2 != GJK::Status::Failed); EIGEN_VECTOR_IS_APPROX(gjk2.ray, ray2, 1e-8); GJK::Status res3 = gjk3.evaluate(mink_diff, init_guess, init_support_guess); - BOOST_CHECK(gjk3.getIterations() <= max_iterations); + BOOST_CHECK(gjk3.iterations <= max_iterations); Vec3f ray3 = gjk3.ray; res3 = gjk3.evaluate(mink_diff, init_guess, init_support_guess); BOOST_CHECK(res3 != GJK::Status::Failed); diff --git a/test/math.cpp b/test/math.cpp index f26ebb3415a5bbd7aaa49063f14bcd5d53ea10f8..bdbed85c635e7afe36a2f9c950477d88962c945c 100644 --- a/test/math.cpp +++ b/test/math.cpp @@ -111,7 +111,7 @@ Vec3f rotate(Vec3f input, FCL_REAL w, Vec3f vec) { } BOOST_AUTO_TEST_CASE(quaternion) { - Quaternion3f q1(Quaternion3f::Identity()), q2, q3; + Quatf q1(Quatf::Identity()), q2, q3; q2 = fromAxisAngle(Vec3f(0, 0, 1), M_PI / 2); q3 = q2.inverse(); @@ -124,7 +124,7 @@ BOOST_AUTO_TEST_CASE(quaternion) { } BOOST_AUTO_TEST_CASE(transform) { - Quaternion3f q = fromAxisAngle(Vec3f(0, 0, 1), M_PI / 2); + Quatf q = fromAxisAngle(Vec3f(0, 0, 1), M_PI / 2); Vec3f T(0, 1, 2); Transform3f tf(q, T); diff --git a/test/normal_and_nearest_points.cpp b/test/normal_and_nearest_points.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bd0b6cff5939fd8b62dae4fbc7444b056aaa2fb9 --- /dev/null +++ b/test/normal_and_nearest_points.cpp @@ -0,0 +1,536 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2022, INRIA + * 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 name of Willow Garage, Inc. 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 OWNER 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. + */ + +/** \author Louis Montaut */ + +#define BOOST_TEST_MODULE NORMAL_AND_NEAREST_POINTS +#include <boost/test/included/unit_test.hpp> + +#include <hpp/fcl/fwd.hh> +#include <hpp/fcl/shape/geometric_shapes.h> +#include <hpp/fcl/collision_data.h> +#include <hpp/fcl/BV/OBBRSS.h> +#include <hpp/fcl/BVH/BVH_model.h> +#include <hpp/fcl/shape/geometric_shape_to_BVH_model.h> + +#include "utility.h" + +using hpp::fcl::Box; +using hpp::fcl::Capsule; +using hpp::fcl::CollisionRequest; +using hpp::fcl::CollisionResult; +using hpp::fcl::Cone; +using hpp::fcl::constructPolytopeFromEllipsoid; +using hpp::fcl::Contact; +using hpp::fcl::Convex; +using hpp::fcl::Cylinder; +using hpp::fcl::DistanceRequest; +using hpp::fcl::DistanceResult; +using hpp::fcl::Ellipsoid; +using hpp::fcl::FCL_REAL; +using hpp::fcl::Halfspace; +using hpp::fcl::Plane; +using hpp::fcl::shared_ptr; +using hpp::fcl::Sphere; +using hpp::fcl::Transform3f; +using hpp::fcl::Triangle; +using hpp::fcl::Vec3f; + +// This test is designed to check if the normal and the nearest points +// are properly handled as defined in DistanceResult::normal. +// Regardless of wether or not the two shapes are in intersection, regardless of +// wether `collide` or `distance` is called: +// --> we denote `dist` the (signed) distance that separates the two shapes +// --> we denote `p1` and `p2` their nearest_points (witness points) +// Thus: +// --> if o1 and o2 are not in collision, translating o2 by vector (abs(dist) - +// eps) * normal should bring them in collision. +// --> if o1 and o2 are in collision, translating o1 by vector (dist + eps) +// * normal should separate them. +// --> finally, if abs(dist) > 0, we should have normal = sign(dist) * (p2 - +// p1).normalized() and p1 = p2 - dist * normal +template <typename ShapeType1, typename ShapeType2> +void test_normal_and_nearest_points(const ShapeType1& o1, + const ShapeType2& o2) { + // Generate random poses for o2 +#ifndef NDEBUG // if debug mode + std::size_t n = 1; +#else + size_t n = 10000; +#endif + FCL_REAL extents[] = {-2., -2., -2., 2., 2., 2.}; + std::vector<Transform3f> transforms; + generateRandomTransforms(extents, transforms, n); + Transform3f tf1 = Transform3f::Identity(); + + CollisionRequest colreq; + colreq.distance_upper_bound = std::numeric_limits<FCL_REAL>::max(); + CollisionResult colres; + DistanceRequest distreq; + DistanceResult distres; + + for (size_t i = 0; i < n; i++) { + // Run both `distance` and `collide`. + // Since CollisionRequest::distance_lower_bound is set to infinity, + // these functions should agree on the results regardless of collision or + // not. + Transform3f tf2 = transforms[i]; + colres.clear(); + distres.clear(); + size_t col = collide(&o1, tf1, &o2, tf2, colreq, colres); + FCL_REAL dist = distance(&o1, tf1, &o2, tf2, distreq, distres); + + if (col) { + BOOST_CHECK(dist <= 0.); + BOOST_CHECK_CLOSE(dist, distres.min_distance, 1e-6); + Contact contact = colres.getContact(0); + BOOST_CHECK_CLOSE(dist, contact.penetration_depth, 1e-6); + + Vec3f cp1 = contact.nearest_points[0]; + EIGEN_VECTOR_IS_APPROX(cp1, distres.nearest_points[0], 1e-6); + + Vec3f cp2 = contact.nearest_points[1]; + EIGEN_VECTOR_IS_APPROX(cp2, distres.nearest_points[1], 1e-6); + BOOST_CHECK_CLOSE(contact.penetration_depth, -(cp2 - cp1).norm(), 1e-6); + EIGEN_VECTOR_IS_APPROX(cp1, cp2 - dist * distres.normal, 1e-6); + + Vec3f separation_vector = contact.penetration_depth * contact.normal; + EIGEN_VECTOR_IS_APPROX(separation_vector, cp2 - cp1, 1e-6); + + if (dist < 0) { + EIGEN_VECTOR_IS_APPROX(contact.normal, -(cp2 - cp1).normalized(), 1e-6); + } + + // Separate the shapes + Vec3f t = tf1.getTranslation(); + FCL_REAL eps = 1e-2; + tf1.setTranslation(t + separation_vector - eps * contact.normal); + colres.clear(); + distres.clear(); + col = collide(&o1, tf1, &o2, tf2, colreq, colres); + dist = distance(&o1, tf1, &o2, tf2, distreq, distres); + BOOST_CHECK(dist > 0); + BOOST_CHECK(!col); + BOOST_CHECK_CLOSE(colres.distance_lower_bound, dist, 1e-6); + BOOST_CHECK(fabs(colres.distance_lower_bound - eps) <= 1e-2); + cp1 = distres.nearest_points[0]; + cp2 = distres.nearest_points[1]; + BOOST_CHECK_CLOSE(dist, (cp1 - cp2).norm(), 1e-6); + EIGEN_VECTOR_IS_APPROX(cp1, cp2 - dist * distres.normal, 1e-6); + + separation_vector = dist * distres.normal; + EIGEN_VECTOR_IS_APPROX(separation_vector, cp2 - cp1, 1e-6); + + if (dist > 0) { + EIGEN_VECTOR_IS_APPROX(distres.normal, (cp2 - cp1).normalized(), 1e-6); + } + } else { + BOOST_CHECK(dist >= 0.); + BOOST_CHECK_CLOSE(distres.min_distance, dist, 1e-6); + BOOST_CHECK_CLOSE(dist, colres.distance_lower_bound, 1e-6); + + Vec3f cp1 = distres.nearest_points[0]; + Vec3f cp2 = distres.nearest_points[1]; + BOOST_CHECK_CLOSE(dist, (cp1 - cp2).norm(), 1e-6); + EIGEN_VECTOR_IS_APPROX(cp1, cp2 - dist * distres.normal, 1e-6); + + Vec3f separation_vector = dist * distres.normal; + EIGEN_VECTOR_IS_APPROX(separation_vector, cp2 - cp1, 1e-6); + + if (dist > 0) { + EIGEN_VECTOR_IS_APPROX(distres.normal, (cp2 - cp1).normalized(), 1e-6); + } + + // Bring the shapes in collision + Vec3f t = tf1.getTranslation(); + FCL_REAL eps = 1e-2; + tf1.setTranslation(t + separation_vector + eps * distres.normal); + colres.clear(); + distres.clear(); + col = collide(&o1, tf1, &o2, tf2, colreq, colres); + dist = distance(&o1, tf1, &o2, tf2, distreq, distres); + BOOST_CHECK(dist < 0); + BOOST_CHECK(col); + // Contrary to Contact::penetration_depth, + // CollisionResult::distance_lower_bound is a signed distance like + // DistanceResult::min_distance + BOOST_CHECK_CLOSE(colres.distance_lower_bound, dist, 1e-6); + BOOST_CHECK(fabs(colres.distance_lower_bound - -eps) <= 1e-2); + Contact contact = colres.getContact(0); + cp1 = contact.nearest_points[0]; + EIGEN_VECTOR_IS_APPROX(cp1, distres.nearest_points[0], 1e-6); + + cp2 = contact.nearest_points[1]; + EIGEN_VECTOR_IS_APPROX(cp2, distres.nearest_points[1], 1e-6); + BOOST_CHECK_CLOSE(contact.penetration_depth, -(cp2 - cp1).norm(), 1e-6); + EIGEN_VECTOR_IS_APPROX(cp1, cp2 - dist * distres.normal, 1e-6); + + separation_vector = contact.penetration_depth * contact.normal; + EIGEN_VECTOR_IS_APPROX(separation_vector, cp2 - cp1, 1e-6); + + if (dist < 0) { + EIGEN_VECTOR_IS_APPROX(contact.normal, -(cp2 - cp1).normalized(), 1e-6); + } + } + } +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_sphere_sphere) { + FCL_REAL r = 0.5; + shared_ptr<Sphere> o1(new Sphere(r)); + shared_ptr<Sphere> o2(new Sphere(r)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_sphere_capsule) { + FCL_REAL r = 0.5; + FCL_REAL h = 1.; + shared_ptr<Sphere> o1(new Sphere(r)); + shared_ptr<Capsule> o2(new Capsule(r, h)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_sphere_box) { + FCL_REAL r = 0.5; + FCL_REAL rbox = 2 * 0.5; + shared_ptr<Box> o1(new Box(rbox, rbox, rbox)); + shared_ptr<Sphere> o2(new Sphere(r)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_mesh_mesh) { + FCL_REAL r = 0.5; + Convex<Triangle> o1_ = constructPolytopeFromEllipsoid(Ellipsoid(r, r, r)); + shared_ptr<Convex<Triangle>> o1(new Convex<Triangle>( + o1_.points, o1_.num_points, o1_.polygons, o1_.num_polygons)); + Convex<Triangle> o2_ = constructPolytopeFromEllipsoid(Ellipsoid(r, r, r)); + shared_ptr<Convex<Triangle>> o2(new Convex<Triangle>( + o2_.points, o2_.num_points, o2_.polygons, o2_.num_polygons)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_mesh_box) { + FCL_REAL r = 0.5; + FCL_REAL rbox = 2 * 0.5; + Convex<Triangle> o1_ = constructPolytopeFromEllipsoid(Ellipsoid(r, r, r)); + shared_ptr<Convex<Triangle>> o1(new Convex<Triangle>( + o1_.points, o1_.num_points, o1_.polygons, o1_.num_polygons)); + shared_ptr<Box> o2(new Box(rbox, rbox, rbox)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_mesh_ellipsoid) { + FCL_REAL r = 0.5; + Convex<Triangle> o1_ = constructPolytopeFromEllipsoid(Ellipsoid(r, r, r)); + shared_ptr<Convex<Triangle>> o1(new Convex<Triangle>( + o1_.points, o1_.num_points, o1_.polygons, o1_.num_polygons)); + shared_ptr<Ellipsoid> o2(new Ellipsoid(0.5 * r, 1.3 * r, 0.8 * r)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_box_plane) { + FCL_REAL rbox = 1; + shared_ptr<Box> o1(new Box(rbox, rbox, rbox)); + FCL_REAL offset = 0.1; + Vec3f n = Vec3f::Random(); + n.normalize(); + shared_ptr<Plane> o2(new Plane(n, offset)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_box_halfspace) { + FCL_REAL rbox = 1; + shared_ptr<Box> o1(new Box(rbox, rbox, rbox)); + FCL_REAL offset = 0.1; + Vec3f n = Vec3f::Random(); + n.normalize(); + shared_ptr<Halfspace> o2(new Halfspace(n, offset)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_capsule_halfspace) { + FCL_REAL r = 0.5; + FCL_REAL d = 1.; + shared_ptr<Capsule> o1(new Capsule(r, d)); + FCL_REAL offset = 0.1; + Vec3f n = Vec3f::Random(); + n.normalize(); + shared_ptr<Halfspace> o2(new Halfspace(n, offset)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_sphere_halfspace) { + FCL_REAL r = 0.5; + shared_ptr<Sphere> o1(new Sphere(r)); + FCL_REAL offset = 0.1; + Vec3f n = Vec3f::Random(); + n.normalize(); + shared_ptr<Halfspace> o2(new Halfspace(n, offset)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_sphere_plane) { + FCL_REAL r = 0.5; + shared_ptr<Sphere> o1(new Sphere(r)); + FCL_REAL offset = 0.1; + Vec3f n = Vec3f::Random(); + n.normalize(); + shared_ptr<Plane> o2(new Plane(n, offset)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_mesh_halfspace) { + FCL_REAL r = 0.5; + Convex<Triangle> o1_ = constructPolytopeFromEllipsoid(Ellipsoid(r, r, r)); + shared_ptr<Convex<Triangle>> o1(new Convex<Triangle>( + o1_.points, o1_.num_points, o1_.polygons, o1_.num_polygons)); + FCL_REAL offset = 0.1; + Vec3f n = Vec3f::Random(); + n.normalize(); + shared_ptr<Halfspace> o2(new Halfspace(n, offset)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_cone_halfspace) { + FCL_REAL r = 0.5; + FCL_REAL h = 1.; + shared_ptr<Cone> o1(new Cone(r, h)); + FCL_REAL offset = 0.1; + Vec3f n = Vec3f::Random(); + n.normalize(); + shared_ptr<Halfspace> o2(new Halfspace(n, offset)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_cylinder_halfspace) { + FCL_REAL r = 0.5; + FCL_REAL h = 1.; + shared_ptr<Cylinder> o1(new Cylinder(r, h)); + FCL_REAL offset = 0.1; + Vec3f n = Vec3f::Random(); + n.normalize(); + shared_ptr<Halfspace> o2(new Halfspace(n, offset)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_cone_plane) { + FCL_REAL r = 0.5; + FCL_REAL h = 1.; + shared_ptr<Cone> o1(new Cone(r, h)); + FCL_REAL offset = 0.1; + Vec3f n = Vec3f::Random(); + n.normalize(); + shared_ptr<Plane> o2(new Plane(n, offset)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_cylinder_plane) { + FCL_REAL r = 0.5; + FCL_REAL h = 1.; + shared_ptr<Cylinder> o1(new Cylinder(r, h)); + FCL_REAL offset = 0.1; + Vec3f n = Vec3f::Random(); + n.normalize(); + shared_ptr<Plane> o2(new Plane(n, offset)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_capsule_plane) { + FCL_REAL r = 0.5; + FCL_REAL h = 1.; + shared_ptr<Capsule> o1(new Capsule(r, h)); + FCL_REAL offset = 0.1; + Vec3f n = Vec3f::Random(); + n.normalize(); + shared_ptr<Plane> o2(new Plane(n, offset)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_capsule_capsule) { + FCL_REAL r = 0.5; + FCL_REAL h = 1.; + shared_ptr<Capsule> o1(new Capsule(r, h)); + shared_ptr<Capsule> o2(new Capsule(r, h)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_sphere_cylinder) { + FCL_REAL r = 0.5; + FCL_REAL h = 1.; + shared_ptr<Sphere> o1(new Sphere(r)); + shared_ptr<Cylinder> o2(new Cylinder(r, h)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_ellipsoid_halfspace) { + Vec3f radii(0.3, 0.5, 0.2); + FCL_REAL offset = 0.1; + Vec3f n = Vec3f::Random(); + n.normalize(); + shared_ptr<Ellipsoid> o1(new Ellipsoid(radii)); + shared_ptr<Halfspace> o2(new Halfspace(n, offset)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_ellispoid_plane) { + Vec3f radii(0.3, 0.5, 0.4); + FCL_REAL offset = 0.1; + Vec3f n = Vec3f::Random(); + n.normalize(); + shared_ptr<Ellipsoid> o1(new Ellipsoid(radii)); + shared_ptr<Plane> o2(new Plane(n, offset)); + + test_normal_and_nearest_points(*o1.get(), *o2.get()); + test_normal_and_nearest_points(*o2.get(), *o1.get()); +} + +using hpp::fcl::BVHModel; +using hpp::fcl::OBBRSS; + +void test_normal_and_nearest_points(const BVHModel<OBBRSS>& o1, + const Halfspace& o2) { + // Generate random poses for o2 +#ifndef NDEBUG // if debug mode + std::size_t n = 1; +#else + size_t n = 10000; +#endif + FCL_REAL extents[] = {-2., -2., -2., 2., 2., 2.}; + std::vector<Transform3f> transforms; + generateRandomTransforms(extents, transforms, n); + Transform3f tf1 = Transform3f::Identity(); + transforms[0] = Transform3f::Identity(); + + CollisionRequest colreq; + colreq.distance_upper_bound = std::numeric_limits<FCL_REAL>::max(); + colreq.num_max_contacts = 100; + CollisionResult colres; + DistanceRequest distreq; + DistanceResult distres; + + for (size_t i = 0; i < n; i++) { + Transform3f tf2 = transforms[i]; + colres.clear(); + distres.clear(); + size_t col = collide(&o1, tf1, &o2, tf2, colreq, colres); + FCL_REAL dist = distance(&o1, tf1, &o2, tf2, distreq, distres); + + if (col) { + BOOST_CHECK(dist <= 0.); + BOOST_CHECK_CLOSE(dist, distres.min_distance, 1e-6); + for (size_t c = 0; c < colres.numContacts(); c++) { + Contact contact = colres.getContact(c); + BOOST_CHECK(contact.penetration_depth <= 0); + BOOST_CHECK(contact.penetration_depth >= colres.distance_lower_bound); + + Vec3f cp1 = contact.nearest_points[0]; + Vec3f cp2 = contact.nearest_points[1]; + BOOST_CHECK_CLOSE(contact.penetration_depth, -(cp2 - cp1).norm(), 1e-6); + EIGEN_VECTOR_IS_APPROX( + cp1, cp2 - contact.penetration_depth * contact.normal, 1e-6); + + Vec3f separation_vector = contact.penetration_depth * contact.normal; + EIGEN_VECTOR_IS_APPROX(separation_vector, cp2 - cp1, 1e-6); + + if (dist < 0) { + EIGEN_VECTOR_IS_APPROX(contact.normal, -(cp2 - cp1).normalized(), + 1e-6); + } + } + } else { + BOOST_CHECK(dist >= 0.); + BOOST_CHECK_CLOSE(distres.min_distance, dist, 1e-6); + BOOST_CHECK_CLOSE(dist, colres.distance_lower_bound, 1e-6); + } + } +} + +void test_normal_and_nearest_points(const Halfspace& o1, + const BVHModel<OBBRSS>& o2) { + test_normal_and_nearest_points(o2, o1); +} + +BOOST_AUTO_TEST_CASE(test_normal_and_nearest_points_bvh_halfspace) { + Box* box_ptr = new hpp::fcl::Box(1, 1, 1); + hpp::fcl::CollisionGeometryPtr_t b1(box_ptr); + BVHModel<hpp::fcl::OBBRSS> o1 = BVHModel<OBBRSS>(); + generateBVHModel(o1, *box_ptr, Transform3f()); + o1.buildConvexRepresentation(false); + + FCL_REAL offset = 0.1; + Vec3f n = Vec3f::Random(); + n.normalize(); + shared_ptr<Halfspace> o2(new Halfspace(n, offset)); + + test_normal_and_nearest_points(o1, *o2.get()); + test_normal_and_nearest_points(*o2.get(), o1); +} diff --git a/test/obb.cpp b/test/obb.cpp index cad2c8c5a24425fcb7b066a6beb7591c2923de92..5c5b8acf82d1a3c1014c65cadaee563dea24c0c3 100644 --- a/test/obb.cpp +++ b/test/obb.cpp @@ -70,7 +70,7 @@ void randomTransform(Matrix3f& B, Vec3f& T, const Vec3f& a, const Vec3f& b, T = (Vec3f::Random() / sqrt(3)) * 1.5 * N; // T.setZero(); - Quaternion3f q; + Quatf q; q.coeffs().setRandom(); q.normalize(); B = q; @@ -1204,7 +1204,7 @@ BenchmarkResult benchmark_obb_case(const Matrix3f& B, const Vec3f& T, result.failure = false; } if (result.failure) { - std::cerr << "\nR = " << Quaternion3f(B).coeffs().transpose().format(py_fmt) + std::cerr << "\nR = " << Quatf(B).coeffs().transpose().format(py_fmt) << "\nT = " << T.transpose().format(py_fmt) << "\na = " << a.transpose().format(py_fmt) << "\nb = " << b.transpose().format(py_fmt) diff --git a/test/octree.cpp b/test/octree.cpp index 222cf5f3418df598786022397f2c817c459e6442..566fb6dd504dd0967a65f3ab8c18f91259c879a6 100644 --- a/test/octree.cpp +++ b/test/octree.cpp @@ -2,6 +2,7 @@ * Software License Agreement (BSD License) * * Copyright (c) 2019, CNRS-LAAS. + * Copyright (c) 2023, INRIA. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,7 +43,9 @@ #include <hpp/fcl/BVH/BVH_model.h> #include <hpp/fcl/collision.h> #include <hpp/fcl/distance.h> +#include <hpp/fcl/hfield.h> #include <hpp/fcl/shape/geometric_shapes.h> +#include <hpp/fcl/shape/geometric_shapes_utility.h> #include <hpp/fcl/internal/BV_splitter.h> #include "utility.h" @@ -50,16 +53,7 @@ namespace utf = boost::unit_test::framework; -using hpp::fcl::BVHModel; -using hpp::fcl::BVSplitter; -using hpp::fcl::CollisionRequest; -using hpp::fcl::CollisionResult; -using hpp::fcl::FCL_REAL; -using hpp::fcl::OBBRSS; -using hpp::fcl::OcTree; -using hpp::fcl::Transform3f; -using hpp::fcl::Triangle; -using hpp::fcl::Vec3f; +using namespace hpp::fcl; void makeMesh(const std::vector<Vec3f>& vertices, const std::vector<Triangle>& triangles, BVHModel<OBBRSS>& model) { @@ -108,7 +102,7 @@ hpp::fcl::OcTree makeOctree(const BVHModel<OBBRSS>& mesh, return OcTree(octree); } -BOOST_AUTO_TEST_CASE(OCTREE) { +BOOST_AUTO_TEST_CASE(octree_mesh) { Eigen::IOFormat tuple(Eigen::FullPrecision, Eigen::DontAlignCols, "", ", ", "", "", "(", ")"); FCL_REAL resolution(10.); @@ -130,6 +124,22 @@ BOOST_AUTO_TEST_CASE(OCTREE) { std::cout << "Finished loading octree." << std::endl; + // Test operator== + { + BOOST_CHECK(envOctree == envOctree); + BOOST_CHECK(envOctree == OcTree(envOctree)); + + const OcTree envOctree_from_tree(envOctree.getTree()); + BOOST_CHECK(envOctree == envOctree_from_tree); + } + + // Test tobytes() + { + const std::vector<uint8_t> bytes = envOctree.tobytes(); + BOOST_CHECK(bytes.size() > 0 && bytes.size() <= envOctree.toBoxes().size() * + 3 * sizeof(FCL_REAL)); + } + std::vector<Transform3f> transforms; FCL_REAL extents[] = {-2000, -2000, 0, 2000, 2000, 2000}; #ifndef NDEBUG // if debug mode @@ -166,3 +176,76 @@ BOOST_AUTO_TEST_CASE(OCTREE) { } } } + +BOOST_AUTO_TEST_CASE(octree_height_field) { + Eigen::IOFormat tuple(Eigen::FullPrecision, Eigen::DontAlignCols, "", ", ", + "", "", "(", ")"); + FCL_REAL resolution(10.); + std::vector<Vec3f> pEnv; + std::vector<Triangle> tEnv; + boost::filesystem::path path(TEST_RESOURCES_DIR); + loadOBJFile((path / "env.obj").string().c_str(), pEnv, tEnv); + + BVHModel<OBBRSS> envMesh; + // Build meshes with robot and environment + makeMesh(pEnv, tEnv, envMesh); + // Build octomap with environment + envMesh.computeLocalAABB(); + // Load octree built from envMesh by makeOctree(envMesh, resolution) + OcTree envOctree( + hpp::fcl::loadOctreeFile((path / "env.octree").string(), resolution)); + + std::cout << "Finished loading octree." << std::endl; + + // Building hfield + const FCL_REAL x_dim = 10, y_dim = 20; + const int nx = 100, ny = 100; + const FCL_REAL max_altitude = 1., min_altitude = 0.; + const MatrixXf heights = MatrixXf::Constant(ny, nx, max_altitude); + + HeightField<AABB> hfield(x_dim, y_dim, heights, min_altitude); + hfield.computeLocalAABB(); + + std::vector<Transform3f> transforms; + FCL_REAL extents[] = {-2000, -2000, 0, 2000, 2000, 2000}; +#ifndef NDEBUG // if debug mode + std::size_t N = 1000; +#else + std::size_t N = 100000; +#endif + N = hpp::fcl::getNbRun(utf::master_test_suite().argc, + utf::master_test_suite().argv, N); + + generateRandomTransforms(extents, transforms, 2 * N); + + CollisionRequest request(hpp::fcl::CONTACT | hpp::fcl::DISTANCE_LOWER_BOUND, + 1); + for (std::size_t i = 0; i < N; ++i) { + CollisionResult resultBox; + CollisionResult resultHfield1, resultHfield2; + Transform3f tf1(transforms[2 * i]); + Transform3f tf2(transforms[2 * i + 1]); + + Box box; + Transform3f box_tf; + constructBox(hfield.aabb_local, tf2, box, box_tf); + + // Test collision between octree and equivalent box. + hpp::fcl::collide(&envOctree, tf1, &box, box_tf, request, resultBox); + // Test collision between octree and hfield. + hpp::fcl::collide(&envOctree, tf1, &hfield, tf2, request, resultHfield1); + hpp::fcl::collide(&hfield, tf2, &envOctree, tf1, request, resultHfield2); + + bool resBox(resultBox.isCollision()); + bool resHfield(resultHfield1.isCollision()); + BOOST_CHECK(resBox == resHfield); + BOOST_CHECK(resultHfield1.isCollision() == resultHfield2.isCollision()); + if (!resBox && resHfield) { + hpp::fcl::DistanceRequest dreq; + hpp::fcl::DistanceResult dres; + hpp::fcl::distance(&envMesh, tf1, &box, box_tf, dreq, dres); + std::cout << "distance mesh box: " << dres.min_distance << std::endl; + BOOST_CHECK(dres.min_distance < sqrt(2.) * resolution); + } + } +} diff --git a/test/python_unit/geometric_shapes.py b/test/python_unit/geometric_shapes.py index b64850e5996c1a20eea9cbee33820434cd3cb728..0b4d52a278a7d8f279f865cc6a1db2b11f6a67e6 100644 --- a/test/python_unit/geometric_shapes.py +++ b/test/python_unit/geometric_shapes.py @@ -156,6 +156,11 @@ class TestGeometricShapes(TestCase): Ic_ref = np.diag([Icx_ref, Icx_ref, Iz_ref]) self.assertApprox(Ic, Ic_ref) + def test_BVH(self): + bvh = hppfcl.BVHModelOBBRSS() + self.assertEqual(bvh.num_vertices, 0) + self.assertEqual(bvh.vertices().shape, (0, 3)) + def test_convex(self): verts = hppfcl.StdVec_Vec3f() faces = hppfcl.StdVec_Triangle() @@ -174,8 +179,8 @@ class TestGeometricShapes(TestCase): hppfcl.Convex.convexHull(verts, False, None) qhullAvailable = True except Exception as e: - self.assertEqual( - str(e), "Library built without qhull. Cannot build object of this type." + self.assertIn( + "Library built without qhull. Cannot build object of this type.", str(e) ) qhullAvailable = False @@ -187,7 +192,7 @@ class TestGeometricShapes(TestCase): hppfcl.Convex.convexHull(verts[:3], False, None) except Exception as e: self.assertIn( - str(e), "You shouldn't use this function with less than 4 points." + "You shouldn't use this function with less than 4 points.", str(e) ) diff --git a/test/security_margin.cpp b/test/security_margin.cpp index 9232f9c4e03c75fcfb620fb9dcbb3301764bb6ba..1fd584a7b28c47d0ac15cf36a88baef41755b6ce 100644 --- a/test/security_margin.cpp +++ b/test/security_margin.cpp @@ -194,7 +194,7 @@ BOOST_AUTO_TEST_CASE(sphere_sphere) { collisionResult); BOOST_CHECK(collisionResult.isCollision()); BOOST_CHECK_CLOSE(collisionResult.distance_lower_bound, 0, 1e-8); - BOOST_CHECK_SMALL(-collisionResult.getContact(0).penetration_depth, 1e-8); + BOOST_CHECK_SMALL(collisionResult.getContact(0).penetration_depth, 1e-8); } // No security margin - no collision @@ -216,14 +216,13 @@ BOOST_AUTO_TEST_CASE(sphere_sphere) { CollisionResult collisionResult; const double distance = 0.01; collisionRequest.security_margin = distance; - Transform3f tf2_no_collision( + Transform3f tf2( Vec3f(tf2_collision.getTranslation() + Vec3f(0, 0, distance))); - collide(s1.get(), tf1, s2.get(), tf2_no_collision, collisionRequest, - collisionResult); + collide(s1.get(), tf1, s2.get(), tf2, collisionRequest, collisionResult); BOOST_CHECK(collisionResult.isCollision()); BOOST_CHECK_CLOSE(collisionResult.distance_lower_bound, 0, 1e-8); - BOOST_CHECK_CLOSE(-collisionResult.getContact(0).penetration_depth, - distance, 1e-8); + BOOST_CHECK_CLOSE(collisionResult.getContact(0).penetration_depth, distance, + 1e-8); } // Negative security margin - collion because the two spheres are in contact @@ -237,8 +236,8 @@ BOOST_AUTO_TEST_CASE(sphere_sphere) { collide(s1.get(), tf1, s2.get(), tf2, collisionRequest, collisionResult); BOOST_CHECK(collisionResult.isCollision()); BOOST_CHECK_SMALL(collisionResult.distance_lower_bound, 1e-8); - BOOST_CHECK_CLOSE(-collisionResult.getContact(0).penetration_depth, - distance, 1e-8); + BOOST_CHECK_CLOSE(collisionResult.getContact(0).penetration_depth, distance, + 1e-8); } // Negative security margin - no collision @@ -269,7 +268,7 @@ BOOST_AUTO_TEST_CASE(capsule_capsule) { collisionResult); BOOST_CHECK(collisionResult.isCollision()); BOOST_CHECK_SMALL(collisionResult.distance_lower_bound, 1e-8); - BOOST_CHECK_SMALL(-collisionResult.getContact(0).penetration_depth, 1e-8); + BOOST_CHECK_SMALL(collisionResult.getContact(0).penetration_depth, 1e-8); } // No security margin - no collision @@ -297,8 +296,8 @@ BOOST_AUTO_TEST_CASE(capsule_capsule) { collisionResult); BOOST_CHECK(collisionResult.isCollision()); BOOST_CHECK_SMALL(collisionResult.distance_lower_bound, 1e-8); - BOOST_CHECK_CLOSE(-collisionResult.getContact(0).penetration_depth, - distance, 1e-8); + BOOST_CHECK_CLOSE(collisionResult.getContact(0).penetration_depth, distance, + 1e-8); } // Negative security margin - collion because the two capsules are in contact @@ -312,8 +311,8 @@ BOOST_AUTO_TEST_CASE(capsule_capsule) { collide(c1.get(), tf1, c2.get(), tf2, collisionRequest, collisionResult); BOOST_CHECK(collisionResult.isCollision()); BOOST_CHECK_SMALL(collisionResult.distance_lower_bound, 1e-8); - BOOST_CHECK_CLOSE(-collisionResult.getContact(0).penetration_depth, - distance, 1e-8); + BOOST_CHECK_CLOSE(collisionResult.getContact(0).penetration_depth, distance, + 1e-8); } // Negative security margin - no collision @@ -345,7 +344,7 @@ BOOST_AUTO_TEST_CASE(box_box) { collisionResult); BOOST_CHECK(collisionResult.isCollision()); BOOST_CHECK_SMALL(collisionResult.distance_lower_bound, tol); - BOOST_CHECK_SMALL(-collisionResult.getContact(0).penetration_depth, 1e-8); + BOOST_CHECK_SMALL(collisionResult.getContact(0).penetration_depth, 1e-8); } // No security margin - no collision @@ -373,7 +372,7 @@ BOOST_AUTO_TEST_CASE(box_box) { BOOST_CHECK(collisionResult.isCollision()); BOOST_CHECK_CLOSE(collisionResult.distance_lower_bound, -collisionRequest.security_margin, tol); - BOOST_CHECK_SMALL(-collisionResult.getContact(0).penetration_depth, 1e-8); + BOOST_CHECK_SMALL(collisionResult.getContact(0).penetration_depth, 1e-8); } // Negative security margin - no collision @@ -402,8 +401,8 @@ BOOST_AUTO_TEST_CASE(box_box) { collide(b1.get(), tf1, b2.get(), tf2, collisionRequest, collisionResult); BOOST_CHECK(collisionResult.isCollision()); BOOST_CHECK_SMALL(collisionResult.distance_lower_bound, tol); - BOOST_CHECK_CLOSE(-collisionResult.getContact(0).penetration_depth, - distance, tol); + BOOST_CHECK_CLOSE(collisionResult.getContact(0).penetration_depth, distance, + tol); } } @@ -419,7 +418,7 @@ void test_shape_shape(const ShapeType1& shape1, const Transform3f& tf1, collisionResult); BOOST_CHECK(collisionResult.isCollision()); BOOST_CHECK_SMALL(collisionResult.distance_lower_bound, tol); - BOOST_CHECK_SMALL(-collisionResult.getContact(0).penetration_depth, 1e-8); + BOOST_CHECK_SMALL(collisionResult.getContact(0).penetration_depth, 1e-8); } // No security margin - no collision @@ -447,7 +446,7 @@ void test_shape_shape(const ShapeType1& shape1, const Transform3f& tf1, BOOST_CHECK(collisionResult.isCollision()); BOOST_CHECK_CLOSE(collisionResult.distance_lower_bound, -collisionRequest.security_margin, tol); - BOOST_CHECK_SMALL(-collisionResult.getContact(0).penetration_depth, 1e-8); + BOOST_CHECK_SMALL(collisionResult.getContact(0).penetration_depth, 1e-8); } // Negative security margin - no collision @@ -479,8 +478,8 @@ void test_shape_shape(const ShapeType1& shape1, const Transform3f& tf1, collide(&shape1, tf1, &shape2, tf2, collisionRequest, collisionResult); BOOST_CHECK(collisionResult.isCollision()); BOOST_CHECK_SMALL(collisionResult.distance_lower_bound, tol); - BOOST_CHECK_CLOSE(-collisionResult.getContact(0).penetration_depth, - distance, tol); + BOOST_CHECK_CLOSE(collisionResult.getContact(0).penetration_depth, distance, + tol); } } diff --git a/test/serialization.cpp b/test/serialization.cpp index 14bbf33fb3a67e31d614def9ccaefbcc16beb52c..c4d3fe51bdc157990eaf75f6f07892237a5e3ee7 100644 --- a/test/serialization.cpp +++ b/test/serialization.cpp @@ -1,7 +1,7 @@ /* * Software License Agreement (BSD License) * - * Copyright (c) 2021-2022 INRIA. + * Copyright (c) 2021-2023 INRIA. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -49,6 +49,10 @@ #include <hpp/fcl/serialization/convex.h> #include <hpp/fcl/serialization/memory.h> +#ifdef HPP_FCL_HAS_OCTOMAP +#include <hpp/fcl/serialization/octree.h> +#endif + #include "utility.h" #include "fcl_resources/config.h" @@ -84,15 +88,80 @@ bool check(const T& value, const T& other) { return value == other; } +template <typename T> +bool check_ptr(const T* value, const T* other) { + return *value == *other; +} + enum SerializationMode { TXT = 1, XML = 2, BIN = 4, STREAM = 8 }; +template <typename T> +void test_serialization(const T* value, T& other_value, + const int mode = TXT | XML | BIN | STREAM) { + test_serialization(*value, other_value, mode); +} + +template <typename T, + bool is_base = std::is_base_of<T, CollisionGeometry>::value> +struct test_pointer_serialization_impl { + static void run(const T&, T&, const int) {} +}; + +template <typename T> +struct test_pointer_serialization_impl<T, true> { + static void run(const T& value, T& other_value, const int mode) { + const CollisionGeometry* ptr = &value; + CollisionGeometry* other_ptr = &other_value; + + const boost::filesystem::path tmp_path(boost::archive::tmpdir()); + const boost::filesystem::path txt_path("file.txt"); + const boost::filesystem::path txt_ptr_path("ptr_file.txt"); + const boost::filesystem::path xml_path("file.xml"); + const boost::filesystem::path bin_path("file.bin"); + const boost::filesystem::path txt_filename(tmp_path / txt_path); + const boost::filesystem::path xml_filename(tmp_path / xml_path); + const boost::filesystem::path bin_filename(tmp_path / bin_path); + + // TXT + if (mode & 0x1) { + { + std::ofstream ofs(txt_filename.c_str()); + + boost::archive::text_oarchive oa(ofs); + oa << ptr; + } + BOOST_CHECK(check(*reinterpret_cast<const CollisionGeometry*>(ptr), + *reinterpret_cast<const CollisionGeometry*>(ptr))); + + { + std::ifstream ifs(txt_filename.c_str()); + boost::archive::text_iarchive ia(ifs); + + ia >> other_ptr; + } + BOOST_CHECK( + check(*reinterpret_cast<const CollisionGeometry*>(ptr), + *reinterpret_cast<const CollisionGeometry*>(other_ptr))); + } + } +}; + +template <typename T> +void test_pointer_serialization(const T& value, T& other_value, + const int mode = TXT | XML | BIN | STREAM) { + test_pointer_serialization_impl<T>::run(value, other_value, mode); +} + template <typename T> void test_serialization(const T& value, T& other_value, const int mode = TXT | XML | BIN | STREAM) { const boost::filesystem::path tmp_path(boost::archive::tmpdir()); const boost::filesystem::path txt_path("file.txt"); + const boost::filesystem::path txt_ptr_path("ptr_file.txt"); + const boost::filesystem::path xml_path("file.xml"); const boost::filesystem::path bin_path("file.bin"); const boost::filesystem::path txt_filename(tmp_path / txt_path); + const boost::filesystem::path xml_filename(tmp_path / xml_path); const boost::filesystem::path bin_filename(tmp_path / bin_path); // TXT @@ -114,6 +183,24 @@ void test_serialization(const T& value, T& other_value, BOOST_CHECK(check(value, other_value)); } + // XML + if (mode & 0x2) { + { + std::ofstream ofs(xml_filename.c_str()); + boost::archive::xml_oarchive oa(ofs); + oa << boost::serialization::make_nvp("value", value); + } + BOOST_CHECK(check(value, value)); + + { + std::ifstream ifs(xml_filename.c_str()); + boost::archive::xml_iarchive ia(ifs, boost::archive::no_codecvt); + + ia >> boost::serialization::make_nvp("value", other_value); + } + BOOST_CHECK(check(value, other_value)); + } + // BIN if (mode & 0x4) { { @@ -141,6 +228,31 @@ void test_serialization(const T& value, T& other_value, loadFromBinary(other_value, buffer); BOOST_CHECK(check(value, other_value)); } + + // Test std::shared_ptr<T> + { + const boost::filesystem::path txt_ptr_filename(tmp_path / txt_ptr_path); + std::shared_ptr<T> ptr = std::make_shared<T>(value); + + { + std::ofstream ofs(txt_ptr_filename.c_str()); + + boost::archive::text_oarchive oa(ofs); + oa << ptr; + } + BOOST_CHECK(check_ptr(ptr.get(), ptr.get())); + + std::shared_ptr<T> other_ptr = nullptr; + { + std::ifstream ifs(txt_ptr_filename.c_str()); + boost::archive::text_iarchive ia(ifs); + + ia >> other_ptr; + } + BOOST_CHECK(check_ptr(ptr.get(), other_ptr.get())); + } + + test_pointer_serialization(value, other_value); } template <typename T> @@ -166,6 +278,9 @@ BOOST_AUTO_TEST_CASE(test_collision_data) { collision_result.addContact(contact); collision_result.addContact(contact); collision_result.distance_lower_bound = 0.1; + collision_result.normal.setOnes(); + collision_result.nearest_points[0].setRandom(); + collision_result.nearest_points[1].setRandom(); test_serialization(collision_result); DistanceRequest distance_request(true, 1., 2.); @@ -178,6 +293,16 @@ BOOST_AUTO_TEST_CASE(test_collision_data) { test_serialization(distance_result); } +template <typename T> +void checkEqualStdVector(const std::vector<T>& v1, const std::vector<T>& v2) { + BOOST_CHECK(v1.size() == v2.size()); + if (v1.size() == v2.size()) { + for (size_t i = 0; i < v1.size(); i++) { + BOOST_CHECK(v1[i] == v2[i]); + } + } +} + BOOST_AUTO_TEST_CASE(test_BVHModel) { std::vector<Vec3f> p1, p2; std::vector<Triangle> t1, t2; @@ -191,11 +316,19 @@ BOOST_AUTO_TEST_CASE(test_BVHModel) { m1.beginModel(); m1.addSubModel(p1, t1); m1.endModel(); + BOOST_CHECK(m1.num_vertices == p1.size()); + BOOST_CHECK(m1.num_tris == t1.size()); + checkEqualStdVector(*m1.vertices, p1); + checkEqualStdVector(*m1.tri_indices, t1); BOOST_CHECK(m1 == m1); m2.beginModel(); m2.addSubModel(p2, t2); m2.endModel(); + BOOST_CHECK(m2.num_vertices == p2.size()); + BOOST_CHECK(m2.num_tris == t2.size()); + checkEqualStdVector(*m2.vertices, p2); + checkEqualStdVector(*m2.tri_indices, t2); BOOST_CHECK(m2 == m2); BOOST_CHECK(m1 != m2); @@ -233,6 +366,36 @@ BOOST_AUTO_TEST_CASE(test_Convex) { Convex<Triangle> convex_copy; test_serialization(convex, convex_copy); } + + // Test std::shared_ptr<CollisionGeometry> + { + const boost::filesystem::path tmp_dir(boost::archive::tmpdir()); + const boost::filesystem::path txt_filename = tmp_dir / "file.txt"; + const boost::filesystem::path bin_filename = tmp_dir / "file.bin"; + Convex<Triangle> convex_copy; + + std::shared_ptr<CollisionGeometry> ptr = + std::make_shared<Convex<Triangle>>(convex); + BOOST_CHECK(ptr.get()); + { + std::ofstream ofs(txt_filename.c_str()); + + boost::archive::text_oarchive oa(ofs); + oa << ptr; + } + BOOST_CHECK(check(*reinterpret_cast<Convex<Triangle>*>(ptr.get()), convex)); + + std::shared_ptr<CollisionGeometry> other_ptr = nullptr; + BOOST_CHECK(!other_ptr.get()); + { + std::ifstream ifs(txt_filename.c_str()); + boost::archive::text_iarchive ia(ifs); + + ia >> other_ptr; + } + BOOST_CHECK( + check(convex, *reinterpret_cast<Convex<Triangle>*>(other_ptr.get()))); + } } #endif @@ -303,6 +466,45 @@ BOOST_AUTO_TEST_CASE(test_shapes) { } } +#ifdef HPP_FCL_HAS_OCTOMAP +BOOST_AUTO_TEST_CASE(test_octree) { + const FCL_REAL resolution = 1e-2; + const Matrixx3f points = Matrixx3f::Random(1000, 3); + OcTreePtr_t octree_ptr = makeOctree(points, resolution); + const OcTree& octree = *octree_ptr.get(); + + const boost::filesystem::path tmp_dir(boost::archive::tmpdir()); + const boost::filesystem::path txt_filename = tmp_dir / "file.txt"; + const boost::filesystem::path bin_filename = tmp_dir / "file.bin"; + + { + std::ofstream ofs(bin_filename.c_str(), std::ios::binary); + boost::archive::binary_oarchive oa(ofs); + oa << octree; + } + + OcTree octree_value(1.); + { + std::ifstream ifs(bin_filename.c_str(), + std::fstream::binary | std::fstream::in); + boost::archive::binary_iarchive ia(ifs); + + ia >> octree_value; + } + + BOOST_CHECK(octree.getTree() == octree.getTree()); + BOOST_CHECK(octree_value.getTree() == octree_value.getTree()); + // BOOST_CHECK(octree.getTree() == octree_value.getTree()); + BOOST_CHECK(octree.getResolution() == octree_value.getResolution()); + BOOST_CHECK(octree.getTree()->size() == octree_value.getTree()->size()); + BOOST_CHECK(octree.toBoxes().size() == octree_value.toBoxes().size()); + BOOST_CHECK(octree == octree_value); + + OcTree octree_copy(1.); + test_serialization(octree, octree_copy); +} +#endif + BOOST_AUTO_TEST_CASE(test_memory_footprint) { Sphere sphere(1.); BOOST_CHECK(sizeof(Sphere) == computeMemoryFootprint(sphere)); diff --git a/test/utility.cpp b/test/utility.cpp index d00f9f6924d43f73b867ed310725d4d43143c11f..b343de07925c588ccc7e5b716743a06ce76d20c5 100644 --- a/test/utility.cpp +++ b/test/utility.cpp @@ -365,8 +365,8 @@ std::string getNodeTypeName(NODE_TYPE node_type) { return std::string("invalid"); } -Quaternion3f makeQuat(FCL_REAL w, FCL_REAL x, FCL_REAL y, FCL_REAL z) { - Quaternion3f q; +Quatf makeQuat(FCL_REAL w, FCL_REAL x, FCL_REAL y, FCL_REAL z) { + Quatf q; q.w() = w; q.x() = x; q.y() = y; @@ -478,53 +478,54 @@ Convex<Triangle> constructPolytopeFromEllipsoid(const Ellipsoid& ellipsoid) { FCL_REAL PHI = (1 + std::sqrt(5)) / 2; // vertices - Vec3f* pts = new Vec3f[12]; - pts[0] = Vec3f(-1, PHI, 0); - pts[1] = Vec3f(1, PHI, 0); - pts[2] = Vec3f(-1, -PHI, 0); - pts[3] = Vec3f(1, -PHI, 0); - - pts[4] = Vec3f(0, -1, PHI); - pts[5] = Vec3f(0, 1, PHI); - pts[6] = Vec3f(0, -1, -PHI); - pts[7] = Vec3f(0, 1, -PHI); - - pts[8] = Vec3f(PHI, 0, -1); - pts[9] = Vec3f(PHI, 0, 1); - pts[10] = Vec3f(-PHI, 0, -1); - pts[11] = Vec3f(-PHI, 0, 1); - - for (int i = 0; i < 12; ++i) { - toEllipsoid(pts[i], ellipsoid); + std::shared_ptr<std::vector<Vec3f>> pts(new std::vector<Vec3f>({ + Vec3f(-1, PHI, 0), + Vec3f(1, PHI, 0), + Vec3f(-1, -PHI, 0), + Vec3f(1, -PHI, 0), + + Vec3f(0, -1, PHI), + Vec3f(0, 1, PHI), + Vec3f(0, -1, -PHI), + Vec3f(0, 1, -PHI), + + Vec3f(PHI, 0, -1), + Vec3f(PHI, 0, 1), + Vec3f(-PHI, 0, -1), + Vec3f(-PHI, 0, 1), + })); + + std::vector<Vec3f>& pts_ = *pts; + for (size_t i = 0; i < 12; ++i) { + toEllipsoid(pts_[i], ellipsoid); } // faces - Triangle* tris = new Triangle[20]; - tris[0].set(0, 11, 5); - tris[1].set(0, 5, 1); - tris[2].set(0, 1, 7); - tris[3].set(0, 7, 10); - tris[4].set(0, 10, 11); - - tris[5].set(1, 5, 9); - tris[6].set(5, 11, 4); - tris[7].set(11, 10, 2); - tris[8].set(10, 7, 6); - tris[9].set(7, 1, 8); - - tris[10].set(3, 9, 4); - tris[11].set(3, 4, 2); - tris[12].set(3, 2, 6); - tris[13].set(3, 6, 8); - tris[14].set(3, 8, 9); - - tris[15].set(4, 9, 5); - tris[16].set(2, 4, 11); - tris[17].set(6, 2, 10); - tris[18].set(8, 6, 7); - tris[19].set(9, 8, 1); - return Convex<Triangle>(true, - pts, // points + std::shared_ptr<std::vector<Triangle>> tris(new std::vector<Triangle>(20)); + (*tris)[0].set(0, 11, 5); + (*tris)[1].set(0, 5, 1); + (*tris)[2].set(0, 1, 7); + (*tris)[3].set(0, 7, 10); + (*tris)[4].set(0, 10, 11); + + (*tris)[5].set(1, 5, 9); + (*tris)[6].set(5, 11, 4); + (*tris)[7].set(11, 10, 2); + (*tris)[8].set(10, 7, 6); + (*tris)[9].set(7, 1, 8); + + (*tris)[10].set(3, 9, 4); + (*tris)[11].set(3, 4, 2); + (*tris)[12].set(3, 2, 6); + (*tris)[13].set(3, 6, 8); + (*tris)[14].set(3, 8, 9); + + (*tris)[15].set(4, 9, 5); + (*tris)[16].set(2, 4, 11); + (*tris)[17].set(6, 2, 10); + (*tris)[18].set(8, 6, 7); + (*tris)[19].set(9, 8, 1); + return Convex<Triangle>(pts, // points 12, // num_points tris, // triangles 20 // number of triangles diff --git a/test/utility.h b/test/utility.h index 370dbf391cc320ffd56b6d1827b4ecec9a53f788..667023883994fdfef1a9b042918b789dfb89fe8b 100644 --- a/test/utility.h +++ b/test/utility.h @@ -179,7 +179,7 @@ bool defaultDistanceFunction(CollisionObject* o1, CollisionObject* o2, std::string getNodeTypeName(NODE_TYPE node_type); -Quaternion3f makeQuat(FCL_REAL w, FCL_REAL x, FCL_REAL y, FCL_REAL z); +Quatf makeQuat(FCL_REAL w, FCL_REAL x, FCL_REAL y, FCL_REAL z); std::ostream& operator<<(std::ostream& os, const Transform3f& tf);