diff --git a/docs/source/cpp-api/broad_phase.rst b/docs/source/cpp-api/broad_phase.rst index 4735e1b35..1c4962bda 100644 --- a/docs/source/cpp-api/broad_phase.rst +++ b/docs/source/cpp-api/broad_phase.rst @@ -1,10 +1,6 @@ Broad Phase =========== -.. doxygenenum:: ipc::BroadPhaseMethod - -.. doxygenvariable:: ipc::DEFAULT_BROAD_PHASE_METHOD - Broad Phase ----------- diff --git a/docs/source/python-api/broad_phase.rst b/docs/source/python-api/broad_phase.rst index ff14fca84..d395856c8 100644 --- a/docs/source/python-api/broad_phase.rst +++ b/docs/source/python-api/broad_phase.rst @@ -1,12 +1,6 @@ Broad Phase =========== -.. autoclass:: ipctk.BroadPhaseMethod - - .. autoclasstoc:: - -.. .. autovariable:: ipctk.DEFAULT_BROAD_PHASE_METHOD - Broad Phase ----------- diff --git a/docs/source/tutorial/getting_started.rst b/docs/source/tutorial/getting_started.rst index 06d86907b..d2ec50ff7 100644 --- a/docs/source/tutorial/getting_started.rst +++ b/docs/source/tutorial/getting_started.rst @@ -508,9 +508,8 @@ The ``Candidates`` class represents the culled set of candidate pairs and is bui ipc::Candidates candidates; candidates.build( - mesh, vertices_t0, vertices_t1, - /*inflation_radius=*/0.0, - /*broad_phase_method=*/ipc::BroadPhaseMethod::HASH_GRID); + mesh, vertices_t0, vertices_t1, /*inflation_radius=*/0.0, + /*broad_phase=*/std::make_shared()); .. md-tab-item:: Python @@ -518,11 +517,10 @@ The ``Candidates`` class represents the culled set of candidate pairs and is bui candidates = ipctk.Candidates() candidates.build( - mesh, vertices_t0, vertices_t1, - broad_phase_method=ipctk.BroadPhaseMethod.HASH_GRID) + mesh, vertices_t0, vertices_t1, broad_phase=ipctk.HashGrid()) -Possible values for ``broad_phase_method`` are: ``BRUTE_FORCE`` (parallel brute force culling), ``HASH_GRID`` (default), ``SPATIAL_HASH`` (implementation from the original IPC codebase), -``BVH`` (`SimpleBVH `_), ``SWEEP_AND_PRUNE`` (method of :cite:t:`Belgrod2023Time`), or ``SWEEP_AND_TINIEST_QUEUE`` (requires CUDA). +Possible values for ``broad_phase`` are: ``BruteForce`` (parallel brute force culling), ``HashGrid`` (default), ``SpatialHash`` (implementation from the original IPC codebase), +``BVH`` (`SimpleBVH `_), ``SweepAndPrune`` (method of :cite:t:`Belgrod2023Time`), or ``SweepAndTiniestQueue`` (requires CUDA). Narrow-Phase ^^^^^^^^^^^^ @@ -628,17 +626,12 @@ To do this, we need to set the ``min_distance`` parameter when calling ``is_step .. code-block:: c++ double max_step_size = ipc::compute_collision_free_stepsize( - collision_mesh, vertices_t0, vertices_t1, - /*broad_phase_method=*/ipc::DEFAULT_BROAD_PHASE_METHOD, - /*min_distance=*/1e-4); + collision_mesh, vertices_t0, vertices_t1, /*min_distance=*/1e-4); Eigen::MatrixXd collision_free_vertices = (vertices_t1 - vertices_t0) * max_step_size + vertices_t0; assert(ipc::is_step_collision_free( - mesh, vertices_t0, collision_free_vertices, - /*broad_phase_method=*/ipc::DEFAULT_BROAD_PHASE_METHOD, - /*min_distance=*/1e-4 - )); + mesh, vertices_t0, collision_free_vertices, /*min_distance=*/1e-4)); .. md-tab-item:: Python diff --git a/python/src/broad_phase/broad_phase.cpp b/python/src/broad_phase/broad_phase.cpp index 02c9402ae..f62d5562c 100644 --- a/python/src/broad_phase/broad_phase.cpp +++ b/python/src/broad_phase/broad_phase.cpp @@ -8,37 +8,8 @@ using namespace ipc; void define_broad_phase(py::module_& m) { - py::enum_( - m, "BroadPhaseMethod", - "Enumeration of implemented broad phase methods.") - .value("BRUTE_FORCE", BroadPhaseMethod::BRUTE_FORCE, "Brute force") - .value("HASH_GRID", BroadPhaseMethod::HASH_GRID, "Hash grid") - .value("SPATIAL_HASH", BroadPhaseMethod::SPATIAL_HASH, "Spatial hash") - .value( - "BOUNDING_VOLUME_HIERARCHY", BroadPhaseMethod::BVH, - "Bounding volume hierarchy") - .value( - "SWEEP_AND_PRUNE", BroadPhaseMethod::SWEEP_AND_PRUNE, - "Sweep and prune") - .value( - "SWEEP_AND_TINIEST_QUEUE", - BroadPhaseMethod::SWEEP_AND_TINIEST_QUEUE, - "Sweep and tiniest queue (GPU)") - .export_values(); - - py::class_(m, "BroadPhase") - .def_static( - "make_broad_phase", &BroadPhase::make_broad_phase, - R"ipc_Qu8mg5v7( - Construct a registered broad phase object. - - Parameters: - method: The broad phase method to use. - - Returns: - The constructed broad phase object. - )ipc_Qu8mg5v7", - py::arg("method")) + py::class_>(m, "BroadPhase") + .def("name", &BroadPhase::name, "Get the name of the broad phase.") .def( "build", py::overload_cast< diff --git a/python/src/broad_phase/brute_force.cpp b/python/src/broad_phase/brute_force.cpp index 54a25fd86..d579eeaef 100644 --- a/python/src/broad_phase/brute_force.cpp +++ b/python/src/broad_phase/brute_force.cpp @@ -7,5 +7,7 @@ using namespace ipc; void define_brute_force(py::module_& m) { - py::class_(m, "BruteForce").def(py::init()); + py::class_>( + m, "BruteForce") + .def(py::init()); } diff --git a/python/src/broad_phase/bvh.cpp b/python/src/broad_phase/bvh.cpp index 90d7fb675..c264d60ba 100644 --- a/python/src/broad_phase/bvh.cpp +++ b/python/src/broad_phase/bvh.cpp @@ -7,5 +7,5 @@ using namespace ipc; void define_bvh(py::module_& m) { - py::class_(m, "BVH").def(py::init()); + py::class_>(m, "BVH").def(py::init()); } diff --git a/python/src/broad_phase/hash_grid.cpp b/python/src/broad_phase/hash_grid.cpp index 6af6f6191..3a79457bc 100644 --- a/python/src/broad_phase/hash_grid.cpp +++ b/python/src/broad_phase/hash_grid.cpp @@ -18,7 +18,7 @@ void define_hash_grid(py::module_& m) .def_readwrite("key", &HashItem::key, "The key of the item.") .def_readwrite("id", &HashItem::id, "The value of the item."); - py::class_(m, "HashGrid") + py::class_>(m, "HashGrid") .def(py::init()) .def_property_readonly("cell_size", &HashGrid::cell_size) .def_property_readonly( diff --git a/python/src/broad_phase/spatial_hash.cpp b/python/src/broad_phase/spatial_hash.cpp index bc8488d1c..cf6cb8dbd 100644 --- a/python/src/broad_phase/spatial_hash.cpp +++ b/python/src/broad_phase/spatial_hash.cpp @@ -7,7 +7,8 @@ using namespace ipc; void define_spatial_hash(py::module_& m) { - py::class_(m, "SpatialHash") + py::class_>( + m, "SpatialHash") .def(py::init()) .def( py::init< diff --git a/python/src/broad_phase/sweep_and_prune.cpp b/python/src/broad_phase/sweep_and_prune.cpp index 77c5c2760..cc7c19800 100644 --- a/python/src/broad_phase/sweep_and_prune.cpp +++ b/python/src/broad_phase/sweep_and_prune.cpp @@ -7,5 +7,7 @@ using namespace ipc; void define_sweep_and_prune(py::module_& m) { - py::class_(m, "SweepAndPrune").def(py::init()); + py::class_>( + m, "SweepAndPrune") + .def(py::init()); } diff --git a/python/src/broad_phase/sweep_and_tiniest_queue.cpp b/python/src/broad_phase/sweep_and_tiniest_queue.cpp index 04e0a6c12..338fa3d01 100644 --- a/python/src/broad_phase/sweep_and_tiniest_queue.cpp +++ b/python/src/broad_phase/sweep_and_tiniest_queue.cpp @@ -10,7 +10,9 @@ using namespace ipc; // not defined if IPC_TOOLKIT_WITH_CUDA is not defined void define_sweep_and_tiniest_queue(py::module_& m) { #ifdef IPC_TOOLKIT_WITH_CUDA - py::class_(m, "SweepAndTiniestQueue") + py::class_< + SweepAndTiniestQueue, BroadPhase, + std::shared_ptr>(m, "SweepAndTiniestQueue") .def(py::init()); #endif } diff --git a/python/src/candidates/candidates.cpp b/python/src/candidates/candidates.cpp index ed6535381..a53b16e10 100644 --- a/python/src/candidates/candidates.cpp +++ b/python/src/candidates/candidates.cpp @@ -13,7 +13,7 @@ void define_candidates(py::module_& m) "build", py::overload_cast< const CollisionMesh&, const Eigen::MatrixXd&, const double, - const BroadPhaseMethod>(&Candidates::build), + std::shared_ptr>(&Candidates::build), R"ipc_Qu8mg5v7( Initialize the set of discrete collision detection candidates. @@ -21,17 +21,17 @@ void define_candidates(py::module_& m) mesh: The surface of the collision mesh. vertices: Surface vertex positions (rowwise). inflation_radius: Amount to inflate the bounding boxes. - broad_phase_method: Broad phase method to use. + broad_phase: Broad phase to use. )ipc_Qu8mg5v7", py::arg("mesh"), py::arg("vertices"), py::arg("inflation_radius") = 0, - py::arg("broad_phase_method") = DEFAULT_BROAD_PHASE_METHOD) + py::arg("broad_phase") = make_default_broad_phase()) .def( "build", py::overload_cast< const CollisionMesh&, const Eigen::MatrixXd&, - const Eigen::MatrixXd&, const double, const BroadPhaseMethod>( - &Candidates::build), + const Eigen::MatrixXd&, const double, + std::shared_ptr>(&Candidates::build), R"ipc_Qu8mg5v7( Initialize the set of continuous collision detection candidates. @@ -43,11 +43,11 @@ void define_candidates(py::module_& m) vertices_t0: Surface vertex starting positions (rowwise). vertices_t1: Surface vertex ending positions (rowwise). inflation_radius: Amount to inflate the bounding boxes. - broad_phase_method: Broad phase method to use. + broad_phase: Broad phase to use. )ipc_Qu8mg5v7", py::arg("mesh"), py::arg("vertices_t0"), py::arg("vertices_t1"), py::arg("inflation_radius") = 0, - py::arg("broad_phase_method") = DEFAULT_BROAD_PHASE_METHOD) + py::arg("broad_phase") = make_default_broad_phase()) .def("__len__", &Candidates::size) .def("empty", &Candidates::empty) .def("clear", &Candidates::clear) @@ -122,13 +122,13 @@ void define_candidates(py::module_& m) vertices_t0: Surface vertex starting positions (rowwise). vertices_t1: Surface vertex ending positions (rowwise). dhat: Barrier activation distance. - min_distance: The minimum distance allowable between any two elements. - narrow_phase_ccd: The narrow phase CCD algorithm to use. + min_distance: Minimum distance allowable between any two elements. + broad_phase: Broad phase algorithm to use. + narrow_phase_ccd: Narrow phase CCD algorithm to use. )ipc_Qu8mg5v7", py::arg("mesh"), py::arg("vertices_t0"), py::arg("vertices_t1"), - py::arg("dhat"), - py::arg("broad_phase_method") = DEFAULT_BROAD_PHASE_METHOD, - py::arg("min_distance") = 0.0, + py::arg("dhat"), py::arg("min_distance") = 0.0, + py::arg("broad_phase") = make_default_broad_phase(), py::arg("narrow_phase_ccd") = DEFAULT_NARROW_PHASE_CCD) .def( "save_obj", &Candidates::save_obj, py::arg("filename"), diff --git a/python/src/collisions/normal/normal_collisions.cpp b/python/src/collisions/normal/normal_collisions.cpp index 00da3e21e..c58bbe3e2 100644 --- a/python/src/collisions/normal/normal_collisions.cpp +++ b/python/src/collisions/normal/normal_collisions.cpp @@ -13,7 +13,8 @@ void define_normal_collisions(py::module_& m) "build", py::overload_cast< const CollisionMesh&, const Eigen::MatrixXd&, const double, - const double, const BroadPhaseMethod>(&NormalCollisions::build), + const double, std::shared_ptr>( + &NormalCollisions::build), R"ipc_Qu8mg5v7( Initialize the set of collisions used to compute the barrier potential. @@ -22,11 +23,11 @@ void define_normal_collisions(py::module_& m) vertices: Vertices of the collision mesh. dhat: The activation distance of the barrier. dmin: Minimum distance. - broad_phase_method: Broad-phase method to use. + broad_phase: Broad-phase to use. )ipc_Qu8mg5v7", py::arg("mesh"), py::arg("vertices"), py::arg("dhat"), py::arg("dmin") = 0, - py::arg("broad_phase_method") = DEFAULT_BROAD_PHASE_METHOD) + py::arg("broad_phase") = make_default_broad_phase()) .def( "build", py::overload_cast< diff --git a/python/src/ipc.cpp b/python/src/ipc.cpp index 164555b23..acebef819 100644 --- a/python/src/ipc.cpp +++ b/python/src/ipc.cpp @@ -2,6 +2,7 @@ #include #include + #include namespace py = pybind11; @@ -24,7 +25,7 @@ void define_ipc(py::module_& m) vertices_t0: Surface vertex vertices at start as rows of a matrix. vertices_t1: Surface vertex vertices at end as rows of a matrix. min_distance: The minimum distance allowable between any two elements. - broad_phase_method: The broad phase method to use. + broad_phase: Broad phase to use. narrow_phase_ccd: The narrow phase CCD algorithm to use. Returns: @@ -32,7 +33,7 @@ void define_ipc(py::module_& m) )ipc_Qu8mg5v7", py::arg("mesh"), py::arg("vertices_t0"), py::arg("vertices_t1"), py::arg("min_distance") = 0.0, - py::arg("broad_phase_method") = DEFAULT_BROAD_PHASE_METHOD, + py::arg("broad_phase") = make_default_broad_phase(), py::arg("narrow_phase_ccd") = DEFAULT_NARROW_PHASE_CCD); m.def( @@ -48,7 +49,7 @@ void define_ipc(py::module_& m) vertices_t0: Vertex vertices at start as rows of a matrix. Assumes vertices_t0 is intersection free. vertices_t1: Surface vertex vertices at end as rows of a matrix. min_distance: The minimum distance allowable between any two elements. - broad_phase_method: The broad phase method to use. + broad_phase: Broad phase to use. narrow_phase_ccd: The narrow phase CCD algorithm to use. Returns: @@ -56,7 +57,7 @@ void define_ipc(py::module_& m) )ipc_Qu8mg5v7", py::arg("mesh"), py::arg("vertices_t0"), py::arg("vertices_t1"), py::arg("min_distance") = 0.0, - py::arg("broad_phase_method") = DEFAULT_BROAD_PHASE_METHOD, + py::arg("broad_phase") = make_default_broad_phase(), py::arg("narrow_phase_ccd") = DEFAULT_NARROW_PHASE_CCD); m.def( @@ -67,13 +68,13 @@ void define_ipc(py::module_& m) Parameters: mesh: The collision mesh. vertices: Vertices of the collision mesh. - broad_phase_method: The broad phase method to use. + broad_phase: Broad phase to use. Returns: A boolean for if the mesh has intersections. )ipc_Qu8mg5v7", py::arg("mesh"), py::arg("vertices"), - py::arg("broad_phase_method") = DEFAULT_BROAD_PHASE_METHOD); + py::arg("broad_phase") = make_default_broad_phase()); m.def( "edges", diff --git a/python/tests/test_ipc.py b/python/tests/test_ipc.py index d9c7756a4..8d735aac8 100644 --- a/python/tests/test_ipc.py +++ b/python/tests/test_ipc.py @@ -5,7 +5,7 @@ import utils -def check_ipc_derivatives(broad_phase_method, use_convergent_formulation, mesh_name, dhat, all_vertices_on_surface): +def check_ipc_derivatives(broad_phase, use_convergent_formulation, mesh_name, dhat, all_vertices_on_surface): vertices, edges, faces = utils.load_mesh(mesh_name) if all_vertices_on_surface: @@ -17,8 +17,7 @@ def check_ipc_derivatives(broad_phase_method, use_convergent_formulation, mesh_n collisions = ipctk.NormalCollisions() collisions.use_area_weighting = use_convergent_formulation collisions.use_improved_max_approximator = use_convergent_formulation - collisions.build(mesh, vertices, dhat, - broad_phase_method=broad_phase_method) + collisions.build(mesh, vertices, dhat, broad_phase=broad_phase) assert len(collisions) > 0 B = ipctk.BarrierPotential( @@ -40,7 +39,7 @@ def check_ipc_derivatives(broad_phase_method, use_convergent_formulation, mesh_n def test_ipc(): - for method in utils.broad_phase_methods(): + for method in utils.broad_phases(): for use_convergent_formulation in (True, False): yield check_ipc_derivatives, method, use_convergent_formulation, "cube.ply", np.sqrt(2.0), True yield check_ipc_derivatives, method, use_convergent_formulation, "two-cubes-far.ply", 1e-1, False diff --git a/python/tests/utils.py b/python/tests/utils.py index 50e04de8b..d303dbfce 100644 --- a/python/tests/utils.py +++ b/python/tests/utils.py @@ -29,12 +29,12 @@ def load_mesh(mesh_name): return mesh.points, ipctk.edges(mesh.cells_dict['triangle']), mesh.cells_dict['triangle'] -def broad_phase_methods(): - yield ipctk.BroadPhaseMethod.BRUTE_FORCE - yield ipctk.BroadPhaseMethod.HASH_GRID - yield ipctk.BroadPhaseMethod.SPATIAL_HASH - yield ipctk.BroadPhaseMethod.BOUNDING_VOLUME_HIERARCHY - yield ipctk.BroadPhaseMethod.SWEEP_AND_PRUNE +def broad_phases(): + yield ipctk.BruteForce() + yield ipctk.HashGrid() + yield ipctk.SpatialHash() + yield ipctk.BVH() + yield ipctk.SweepAndPrune() def finite_jacobian(x, f, h=1e-8): diff --git a/src/ipc/broad_phase/CMakeLists.txt b/src/ipc/broad_phase/CMakeLists.txt index 750f3f701..2f8033fda 100644 --- a/src/ipc/broad_phase/CMakeLists.txt +++ b/src/ipc/broad_phase/CMakeLists.txt @@ -7,6 +7,7 @@ set(SOURCES brute_force.hpp bvh.cpp bvh.hpp + default_broad_phase.hpp hash_grid.cpp hash_grid.hpp spatial_hash.cpp diff --git a/src/ipc/broad_phase/broad_phase.cpp b/src/ipc/broad_phase/broad_phase.cpp index e816006d5..9563b2642 100644 --- a/src/ipc/broad_phase/broad_phase.cpp +++ b/src/ipc/broad_phase/broad_phase.cpp @@ -64,34 +64,6 @@ void BroadPhase::detect_collision_candidates( // ============================================================================ -std::shared_ptr -BroadPhase::make_broad_phase(const BroadPhaseMethod method) -{ - switch (method) { - case BroadPhaseMethod::BRUTE_FORCE: - return std::make_shared(); - case BroadPhaseMethod::HASH_GRID: - return std::make_shared(); - case BroadPhaseMethod::SPATIAL_HASH: - return std::make_shared(); - case BroadPhaseMethod::SWEEP_AND_PRUNE: - return std::make_shared(); - case BroadPhaseMethod::SWEEP_AND_TINIEST_QUEUE: -#ifdef IPC_TOOLKIT_WITH_CUDA - return std::make_shared(); -#else - throw std::runtime_error("GPU Sweep and Tiniest Queue is disabled " - "because CUDA is disabled!"); -#endif - case BroadPhaseMethod::BVH: - return std::make_shared(); - default: - throw std::runtime_error("Invalid BroadPhaseMethod!"); - } -} - -// ============================================================================ - bool BroadPhase::can_edge_vertex_collide(size_t ei, size_t vi) const { const auto& [e0i, e1i, _] = edge_boxes[ei].vertex_ids; diff --git a/src/ipc/broad_phase/broad_phase.hpp b/src/ipc/broad_phase/broad_phase.hpp index 9e13bb25b..4f5d2d29d 100644 --- a/src/ipc/broad_phase/broad_phase.hpp +++ b/src/ipc/broad_phase/broad_phase.hpp @@ -13,31 +13,15 @@ namespace ipc { -/// Enumeration of implemented broad phase methods. -enum class BroadPhaseMethod { - BRUTE_FORCE = 0, - HASH_GRID, - SPATIAL_HASH, - BVH, - SWEEP_AND_PRUNE, - SWEEP_AND_TINIEST_QUEUE, // Requires CUDA - NUM_METHODS -}; - -static constexpr BroadPhaseMethod DEFAULT_BROAD_PHASE_METHOD = - BroadPhaseMethod::HASH_GRID; - class Candidates; // Forward declaration class BroadPhase { public: virtual ~BroadPhase() { clear(); } - /// @brief Construct a registered broad phase object. - /// @param method The broad phase method to use. - /// @return The constructed broad phase object. - static std::shared_ptr - make_broad_phase(const BroadPhaseMethod method); + /// @brief Get the name of the broad phase method. + /// @return The name of the broad phase method. + virtual std::string name() const = 0; /// @brief Build the broad phase for static collision detection. /// @param vertices Vertex positions diff --git a/src/ipc/broad_phase/brute_force.hpp b/src/ipc/broad_phase/brute_force.hpp index 80c2f0a3a..be97afcdb 100644 --- a/src/ipc/broad_phase/brute_force.hpp +++ b/src/ipc/broad_phase/brute_force.hpp @@ -8,6 +8,10 @@ class BruteForce : public BroadPhase { public: BruteForce() = default; + /// @brief Get the name of the broad phase method. + /// @return The name of the broad phase method. + std::string name() const override { return "BruteForce"; } + /// @brief Find the candidate vertex-vertex collisions. /// @param[out] candidates The candidate vertex-vertex collisions. void detect_vertex_vertex_candidates( diff --git a/src/ipc/broad_phase/bvh.hpp b/src/ipc/broad_phase/bvh.hpp index 5ba3cc134..2efc07bd8 100644 --- a/src/ipc/broad_phase/bvh.hpp +++ b/src/ipc/broad_phase/bvh.hpp @@ -10,6 +10,10 @@ class BVH : public BroadPhase { public: BVH() = default; + /// @brief Get the name of the broad phase method. + /// @return The name of the broad phase method. + std::string name() const override { return "BVH"; } + /// @brief Build the broad phase for static collision detection. /// @param vertices Vertex positions /// @param edges Collision mesh edges diff --git a/src/ipc/broad_phase/default_broad_phase.hpp b/src/ipc/broad_phase/default_broad_phase.hpp new file mode 100644 index 000000000..460d0e0e2 --- /dev/null +++ b/src/ipc/broad_phase/default_broad_phase.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace ipc { + +inline std::shared_ptr make_default_broad_phase() +{ + return std::make_shared(); +} + +} // namespace ipc \ No newline at end of file diff --git a/src/ipc/broad_phase/hash_grid.hpp b/src/ipc/broad_phase/hash_grid.hpp index b8ba34950..82b0e3d3c 100644 --- a/src/ipc/broad_phase/hash_grid.hpp +++ b/src/ipc/broad_phase/hash_grid.hpp @@ -28,6 +28,10 @@ class HashGrid : public BroadPhase { public: HashGrid() = default; + /// @brief Get the name of the broad phase method. + /// @return The name of the broad phase method. + std::string name() const override { return "HashGrid"; } + /// @brief Build the broad phase for static collision detection. /// @param vertices Vertex positions /// @param edges Collision mesh edges diff --git a/src/ipc/broad_phase/spatial_hash.hpp b/src/ipc/broad_phase/spatial_hash.hpp index 8ddcc094a..b554c41e3 100644 --- a/src/ipc/broad_phase/spatial_hash.hpp +++ b/src/ipc/broad_phase/spatial_hash.hpp @@ -74,6 +74,10 @@ class SpatialHash : public BroadPhase { voxel_size); } + /// @brief Get the name of the broad phase method. + /// @return The name of the broad phase method. + std::string name() const override { return "SpatialHash"; } + public: // API void build( const Eigen::MatrixXd& vertices, diff --git a/src/ipc/broad_phase/sweep_and_prune.hpp b/src/ipc/broad_phase/sweep_and_prune.hpp index 783451cfb..365dbd305 100644 --- a/src/ipc/broad_phase/sweep_and_prune.hpp +++ b/src/ipc/broad_phase/sweep_and_prune.hpp @@ -10,6 +10,10 @@ class SweepAndPrune : public BroadPhase { public: SweepAndPrune() = default; + /// @brief Get the name of the broad phase method. + /// @return The name of the broad phase method. + std::string name() const override { return "SweepAndPrune"; } + /// @brief Build the broad phase for static collision detection. /// @param vertices Vertex positions /// @param edges Collision mesh edges diff --git a/src/ipc/broad_phase/sweep_and_tiniest_queue.hpp b/src/ipc/broad_phase/sweep_and_tiniest_queue.hpp index 23fe69821..538415f93 100644 --- a/src/ipc/broad_phase/sweep_and_tiniest_queue.hpp +++ b/src/ipc/broad_phase/sweep_and_tiniest_queue.hpp @@ -14,6 +14,10 @@ class SweepAndTiniestQueue : public BroadPhase { public: SweepAndTiniestQueue() = default; + /// @brief Get the name of the broad phase method. + /// @return The name of the broad phase method. + std::string name() const override { return "SweepAndTiniestQueue"; } + /// @brief Build the broad phase for static collision detection. /// @param vertices Vertex positions /// @param edges Collision mesh edges diff --git a/src/ipc/candidates/candidates.cpp b/src/ipc/candidates/candidates.cpp index e6c207ad7..642179eb2 100644 --- a/src/ipc/candidates/candidates.cpp +++ b/src/ipc/candidates/candidates.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -35,14 +36,14 @@ void Candidates::build( const CollisionMesh& mesh, const Eigen::MatrixXd& vertices, const double inflation_radius, - const BroadPhaseMethod broad_phase_method) + const std::shared_ptr broad_phase) { + assert(broad_phase != nullptr); + const int dim = vertices.cols(); clear(); - std::shared_ptr broad_phase = - BroadPhase::make_broad_phase(broad_phase_method); broad_phase->can_vertices_collide = mesh.can_collide; broad_phase->build(vertices, mesh.edges(), mesh.faces(), inflation_radius); broad_phase->detect_collision_candidates(dim, *this); @@ -107,14 +108,14 @@ void Candidates::build( const Eigen::MatrixXd& vertices_t0, const Eigen::MatrixXd& vertices_t1, const double inflation_radius, - const BroadPhaseMethod broad_phase_method) + const std::shared_ptr broad_phase) { + assert(broad_phase != nullptr); + const int dim = vertices_t0.cols(); clear(); - std::shared_ptr broad_phase = - BroadPhase::make_broad_phase(broad_phase_method); broad_phase->can_vertices_collide = mesh.can_collide; broad_phase->build( vertices_t0, vertices_t1, mesh.edges(), mesh.faces(), inflation_radius); @@ -301,8 +302,8 @@ double Candidates::compute_cfl_stepsize( const Eigen::MatrixXd& vertices_t0, const Eigen::MatrixXd& vertices_t1, const double dhat, - const BroadPhaseMethod broad_phase_method, const double min_distance, + const std::shared_ptr broad_phase, const NarrowPhaseCCD& narrow_phase_ccd) const { assert(vertices_t0.rows() == mesh.num_vertices()); @@ -317,7 +318,7 @@ double Candidates::compute_cfl_stepsize( // If alpha_F < 0.5 * alpha_C, then we should do full CCD. if (alpha_F < 0.5 * alpha_C) { return ipc::compute_collision_free_stepsize( - mesh, vertices_t0, vertices_t1, min_distance, broad_phase_method, + mesh, vertices_t0, vertices_t1, min_distance, broad_phase, narrow_phase_ccd); } return std::min(alpha_C, alpha_F); diff --git a/src/ipc/candidates/candidates.hpp b/src/ipc/candidates/candidates.hpp index 41088cad6..38fe53de3 100644 --- a/src/ipc/candidates/candidates.hpp +++ b/src/ipc/candidates/candidates.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include #include @@ -25,7 +25,8 @@ class Candidates { const CollisionMesh& mesh, const Eigen::MatrixXd& vertices, const double inflation_radius = 0, - const BroadPhaseMethod broad_phase_method = DEFAULT_BROAD_PHASE_METHOD); + const std::shared_ptr broad_phase = + make_default_broad_phase()); /// @brief Initialize the set of continuous collision detection candidates. /// @note Assumes the trajectory is linear. @@ -39,7 +40,8 @@ class Candidates { const Eigen::MatrixXd& vertices_t0, const Eigen::MatrixXd& vertices_t1, const double inflation_radius = 0, - const BroadPhaseMethod broad_phase_method = DEFAULT_BROAD_PHASE_METHOD); + const std::shared_ptr broad_phase = + make_default_broad_phase()); size_t size() const; @@ -103,8 +105,9 @@ class Candidates { const Eigen::MatrixXd& vertices_t0, const Eigen::MatrixXd& vertices_t1, const double dhat, - const BroadPhaseMethod broad_phase_method = DEFAULT_BROAD_PHASE_METHOD, const double min_distance = 0.0, + const std::shared_ptr broad_phase = + make_default_broad_phase(), const NarrowPhaseCCD& narrow_phase_ccd = DEFAULT_NARROW_PHASE_CCD) const; diff --git a/src/ipc/ccd/CMakeLists.txt b/src/ipc/ccd/CMakeLists.txt index b341e61d2..aa19e32ef 100644 --- a/src/ipc/ccd/CMakeLists.txt +++ b/src/ipc/ccd/CMakeLists.txt @@ -4,6 +4,7 @@ set(SOURCES additive_ccd.cpp additive_ccd.hpp check_initial_distance.hpp + default_narrow_phase_ccd.cpp default_narrow_phase_ccd.hpp inexact_ccd.cpp inexact_ccd.hpp diff --git a/src/ipc/ccd/default_narrow_phase_ccd.cpp b/src/ipc/ccd/default_narrow_phase_ccd.cpp new file mode 100644 index 000000000..fc3069bfb --- /dev/null +++ b/src/ipc/ccd/default_narrow_phase_ccd.cpp @@ -0,0 +1,5 @@ +#include "default_narrow_phase_ccd.hpp" + +namespace ipc { +const TightInclusionCCD DEFAULT_NARROW_PHASE_CCD; +} \ No newline at end of file diff --git a/src/ipc/ccd/default_narrow_phase_ccd.hpp b/src/ipc/ccd/default_narrow_phase_ccd.hpp index 12195d179..4fc72e48a 100644 --- a/src/ipc/ccd/default_narrow_phase_ccd.hpp +++ b/src/ipc/ccd/default_narrow_phase_ccd.hpp @@ -3,5 +3,5 @@ #include namespace ipc { -static const TightInclusionCCD DEFAULT_NARROW_PHASE_CCD; +extern const TightInclusionCCD DEFAULT_NARROW_PHASE_CCD; } \ No newline at end of file diff --git a/src/ipc/collisions/normal/normal_collisions.cpp b/src/ipc/collisions/normal/normal_collisions.cpp index 40a33f244..9269880e6 100644 --- a/src/ipc/collisions/normal/normal_collisions.cpp +++ b/src/ipc/collisions/normal/normal_collisions.cpp @@ -145,14 +145,14 @@ void NormalCollisions::build( const Eigen::MatrixXd& vertices, const double dhat, const double dmin, - const BroadPhaseMethod broad_phase_method) + const std::shared_ptr broad_phase) { assert(vertices.rows() == mesh.num_vertices()); const double inflation_radius = 0.5 * (dhat + dmin); Candidates candidates; - candidates.build(mesh, vertices, inflation_radius, broad_phase_method); + candidates.build(mesh, vertices, inflation_radius, broad_phase); this->build(candidates, mesh, vertices, dhat, dmin); } diff --git a/src/ipc/collisions/normal/normal_collisions.hpp b/src/ipc/collisions/normal/normal_collisions.hpp index 174452158..6df7cd879 100644 --- a/src/ipc/collisions/normal/normal_collisions.hpp +++ b/src/ipc/collisions/normal/normal_collisions.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -35,7 +34,8 @@ class NormalCollisions { const Eigen::MatrixXd& vertices, const double dhat, const double dmin = 0, - const BroadPhaseMethod broad_phase_method = DEFAULT_BROAD_PHASE_METHOD); + const std::shared_ptr broad_phase = + make_default_broad_phase()); /// @brief Initialize the set of collisions used to compute the barrier potential. /// @param candidates Distance candidates from which the collision set is built. diff --git a/src/ipc/ipc.cpp b/src/ipc/ipc.cpp index a5a40df38..f4fb8f0ff 100644 --- a/src/ipc/ipc.cpp +++ b/src/ipc/ipc.cpp @@ -1,6 +1,7 @@ #include "ipc.hpp" #include +#include #include #include #include @@ -18,7 +19,7 @@ bool is_step_collision_free( const Eigen::MatrixXd& vertices_t0, const Eigen::MatrixXd& vertices_t1, const double min_distance, - const BroadPhaseMethod broad_phase_method, + const std::shared_ptr broad_phase, const NarrowPhaseCCD& narrow_phase_ccd) { assert(vertices_t0.rows() == mesh.num_vertices()); @@ -28,7 +29,7 @@ bool is_step_collision_free( Candidates candidates; candidates.build( mesh, vertices_t0, vertices_t1, - /*inflation_radius=*/0.5 * min_distance, broad_phase_method); + /*inflation_radius=*/0.5 * min_distance, broad_phase); // Narrow phase return candidates.is_step_collision_free( @@ -42,14 +43,15 @@ double compute_collision_free_stepsize( const Eigen::MatrixXd& vertices_t0, const Eigen::MatrixXd& vertices_t1, const double min_distance, - const BroadPhaseMethod broad_phase_method, + const std::shared_ptr broad_phase, const NarrowPhaseCCD& narrow_phase_ccd) { + assert(broad_phase != nullptr); assert(vertices_t0.rows() == mesh.num_vertices()); assert(vertices_t1.rows() == mesh.num_vertices()); - if (broad_phase_method == BroadPhaseMethod::SWEEP_AND_TINIEST_QUEUE) { #ifdef IPC_TOOLKIT_WITH_CUDA + if (broad_phase->name() == "SweepAndTiniestQueue") { if (vertices_t0.cols() != 3) { throw std::runtime_error( "Sweep and Tiniest Queue is only supported in 3D!"); @@ -66,17 +68,18 @@ double compute_collision_free_stepsize( return 0.8 * step_size; } return 1.0; + } #else - throw std::runtime_error( - "GPU Sweep and Tiniest Queue is disabled because CUDA is disabled!"); + // This should not be possible because SweepAndTiniestQueue is only + // available when IPC_TOOLKIT_WITH_CUDA is defined. + assert(broad_phase->name() != "SweepAndTiniestQueue"); #endif - } // Broad phase Candidates candidates; candidates.build( mesh, vertices_t0, vertices_t1, /*inflation_radius=*/0.5 * min_distance, - broad_phase_method); + broad_phase); // Narrow phase return candidates.compute_collision_free_stepsize( @@ -88,15 +91,14 @@ double compute_collision_free_stepsize( bool has_intersections( const CollisionMesh& mesh, const Eigen::MatrixXd& vertices, - const BroadPhaseMethod broad_phase_method) + const std::shared_ptr broad_phase) { + assert(broad_phase != nullptr); assert(vertices.rows() == mesh.num_vertices()); const double conservative_inflation_radius = 1e-6 * world_bbox_diagonal_length(vertices); - std::shared_ptr broad_phase = - BroadPhase::make_broad_phase(broad_phase_method); broad_phase->can_vertices_collide = mesh.can_collide; broad_phase->build( diff --git a/src/ipc/ipc.hpp b/src/ipc/ipc.hpp index 697b7c688..c3cf74d1a 100644 --- a/src/ipc/ipc.hpp +++ b/src/ipc/ipc.hpp @@ -1,7 +1,8 @@ #pragma once #include -#include +#include +#include #include #include @@ -26,7 +27,7 @@ bool is_step_collision_free( const Eigen::MatrixXd& vertices_t0, const Eigen::MatrixXd& vertices_t1, const double min_distance = 0.0, - const BroadPhaseMethod broad_phase_method = DEFAULT_BROAD_PHASE_METHOD, + const std::shared_ptr broad_phase = make_default_broad_phase(), const NarrowPhaseCCD& narrow_phase_ccd = DEFAULT_NARROW_PHASE_CCD); /// @brief Computes a maximal step size that is collision free. @@ -43,7 +44,7 @@ double compute_collision_free_stepsize( const Eigen::MatrixXd& vertices_t0, const Eigen::MatrixXd& vertices_t1, const double min_distance = 0.0, - const BroadPhaseMethod broad_phase_method = DEFAULT_BROAD_PHASE_METHOD, + const std::shared_ptr broad_phase = make_default_broad_phase(), const NarrowPhaseCCD& narrow_phase_ccd = DEFAULT_NARROW_PHASE_CCD); // ============================================================================ @@ -57,6 +58,6 @@ double compute_collision_free_stepsize( bool has_intersections( const CollisionMesh& mesh, const Eigen::MatrixXd& vertices, - const BroadPhaseMethod broad_phase_method = DEFAULT_BROAD_PHASE_METHOD); + const std::shared_ptr broad_phase = make_default_broad_phase()); } // namespace ipc diff --git a/tests/src/tests/broad_phase/benchmark_broad_phase.cpp b/tests/src/tests/broad_phase/benchmark_broad_phase.cpp index 8ad469e89..a0187d87b 100644 --- a/tests/src/tests/broad_phase/benchmark_broad_phase.cpp +++ b/tests/src/tests/broad_phase/benchmark_broad_phase.cpp @@ -75,17 +75,13 @@ TEST_CASE("Benchmark broad phase", "[!benchmark][broad_phase]") V0 = mesh.vertices(V0); V1 = mesh.vertices(V1); - const static std::vector BP_names = { - "BF", "HG", "SH", "BVH", "STQ", "GPU_STQ", + const auto broad_phase = GENERATE(tests::BroadPhaseGenerator::create()); + + BENCHMARK(fmt::format("BP {} ({})", testcase_name, broad_phase->name())) + { + Candidates candidates; + candidates.build(mesh, V0, V1, inflation_radius, broad_phase); }; - for (int i = 0; i < NUM_BROAD_PHASE_METHODS; i++) { - BroadPhaseMethod method = static_cast(i); - BENCHMARK(fmt::format("BP {} ({})", testcase_name, BP_names[i])) - { - Candidates candidates; - candidates.build(mesh, V0, V1, inflation_radius, method); - }; - } } TEST_CASE( @@ -129,15 +125,14 @@ TEST_CASE( V0 = mesh.vertices(V0); V1 = mesh.vertices(V1); - const static std::vector BP_names = { - "BF", "HG", "SH", "BVH", "STQ", "GPU_STQ", - }; - for (int i = 1; i < NUM_BROAD_PHASE_METHODS; i++) { - BroadPhaseMethod method = static_cast(i); - BENCHMARK(fmt::format("BP Real Data ({})", BP_names[i])) - { - Candidates candidates; - candidates.build(mesh, V0, V1, inflation_radius, method); - }; + const auto broad_phase = GENERATE(tests::BroadPhaseGenerator::create()); + if (broad_phase->name() == "BruteForce") { + return; // Skip brute force } + + BENCHMARK(fmt::format("BP Real Data ({})", broad_phase->name())) + { + Candidates candidates; + candidates.build(mesh, V0, V1, inflation_radius, broad_phase); + }; } diff --git a/tests/src/tests/broad_phase/brute_force_comparison.cpp b/tests/src/tests/broad_phase/brute_force_comparison.cpp index 90c46adb5..afaee3ec0 100644 --- a/tests/src/tests/broad_phase/brute_force_comparison.cpp +++ b/tests/src/tests/broad_phase/brute_force_comparison.cpp @@ -1,6 +1,7 @@ #include "brute_force_comparison.hpp" #include +#include #include @@ -25,7 +26,7 @@ void brute_force_comparison( if (cached_bf_candidates.empty() || !load_candidates(cached_bf_candidates, bf_candidates)) { bf_candidates.build( - mesh, V0, V1, inflation_radius, BroadPhaseMethod::BRUTE_FORCE); + mesh, V0, V1, inflation_radius, std::make_shared()); if (!cached_bf_candidates.empty()) { save_candidates(cached_bf_candidates, bf_candidates); } diff --git a/tests/src/tests/broad_phase/test_broad_phase.cpp b/tests/src/tests/broad_phase/test_broad_phase.cpp index 5a8d5f31a..0c63f5667 100644 --- a/tests/src/tests/broad_phase/test_broad_phase.cpp +++ b/tests/src/tests/broad_phase/test_broad_phase.cpp @@ -16,16 +16,16 @@ void test_face_face_broad_phase( const CollisionMesh& mesh, const Eigen::MatrixXd& V0, const std::optional& V1, - const BroadPhaseMethod method, + const std::shared_ptr broad_phase, double inflation_radius) { + REQUIRE(broad_phase != nullptr); + // Face-face collisions - if (mesh.num_faces() == 0 || method == BroadPhaseMethod::BRUTE_FORCE) { + if (mesh.num_faces() == 0 || broad_phase->name() == "BruteForce") { return; } - std::shared_ptr broad_phase = - BroadPhase::make_broad_phase(method); broad_phase->can_vertices_collide = mesh.can_collide; if (V1.has_value()) { broad_phase->build( @@ -54,52 +54,57 @@ void test_broad_phase( const CollisionMesh& mesh, const Eigen::MatrixXd& V0, const Eigen::MatrixXd& V1, - const BroadPhaseMethod method, + const std::shared_ptr broad_phase, const bool expect_collision = true, const std::string& cached_bf_candidates = "") { - CAPTURE(method); + REQUIRE(broad_phase != nullptr); + + CAPTURE(broad_phase->name()); REQUIRE(V0.rows() == mesh.num_vertices()); REQUIRE(V1.rows() == mesh.num_vertices()); double inflation_radius = 0; Candidates candidates; - candidates.build(mesh, V0, V1, inflation_radius, method); + candidates.build(mesh, V0, V1, inflation_radius, broad_phase); if (expect_collision) { CHECK(!candidates.is_step_collision_free(mesh, V0, V1)); } - if (method != BroadPhaseMethod::BRUTE_FORCE) { + if (broad_phase->name() != "BruteForce") { brute_force_comparison( mesh, V0, V1, candidates, inflation_radius, cached_bf_candidates); } // Face-face collisions - test_face_face_broad_phase(mesh, V0, V1, method, 0); + test_face_face_broad_phase(mesh, V0, V1, broad_phase, 0); } -Candidates test_broad_phase( +std::shared_ptr test_broad_phase( const CollisionMesh& mesh, const Eigen::MatrixXd& V, - BroadPhaseMethod method, + const std::shared_ptr broad_phase, double inflation_radius, const std::string& cached_bf_candidates = "") { - CAPTURE(method); + REQUIRE(broad_phase != nullptr); + + CAPTURE(broad_phase->name()); REQUIRE(V.rows() == mesh.num_vertices()); - Candidates candidates; - candidates.build(mesh, V, inflation_radius, method); + auto candidates = std::make_shared(); + candidates->build(mesh, V, inflation_radius, broad_phase); - if (method != BroadPhaseMethod::BRUTE_FORCE) { + if (broad_phase->name() != "BruteForce") { brute_force_comparison( - mesh, V, V, candidates, inflation_radius, cached_bf_candidates); + mesh, V, V, *candidates, inflation_radius, cached_bf_candidates); } // Face-face collisions - test_face_face_broad_phase(mesh, V, std::nullopt, method, inflation_radius); + test_face_face_broad_phase( + mesh, V, std::nullopt, broad_phase, inflation_radius); return candidates; } @@ -122,9 +127,9 @@ TEST_CASE("Vertex-Vertex Broad Phase", "[ccd][broad_phase][2D]") CollisionMesh mesh(V0, E); - const BroadPhaseMethod method = GENERATE_BROAD_PHASE_METHODS(); + const auto broad_phase = GENERATE(tests::BroadPhaseGenerator::create()); - test_broad_phase(mesh, V0, V1, method); + test_broad_phase(mesh, V0, V1, broad_phase); } #if defined(NDEBUG) || !(defined(WIN32) || defined(_WIN32) || defined(__WIN32)) @@ -149,9 +154,9 @@ TEST_CASE("Broad Phase: 2D Mesh", "[ccd][broad_phase][2D][.]") const Eigen::MatrixXd V0 = mesh.vertices(V0_full); const Eigen::MatrixXd V1 = mesh.vertices(V1_full); - const BroadPhaseMethod method = GENERATE_BROAD_PHASE_METHODS(); + const auto broad_phase = GENERATE(tests::BroadPhaseGenerator::create()); - test_broad_phase(mesh, V0, V1, method); + test_broad_phase(mesh, V0, V1, broad_phase); } TEST_CASE( @@ -169,14 +174,14 @@ TEST_CASE( CollisionMesh mesh(V_rest, E, F); CHECK(mesh.num_codim_vertices() > 0); - BroadPhaseMethod method = GENERATE_BROAD_PHASE_METHODS(); + const auto broad_phase = GENERATE(tests::BroadPhaseGenerator::create()); - CAPTURE(method); + CAPTURE(broad_phase->name()); - const Candidates candidates = test_broad_phase(mesh, V, method, dhat); + auto candidates = test_broad_phase(mesh, V, broad_phase, dhat); NormalCollisions collisions; - collisions.build(candidates, mesh, V, dhat); + collisions.build(*candidates, mesh, V, dhat); CHECK(collisions.size() != 0); } @@ -184,7 +189,7 @@ TEST_CASE("Compare BP against brute force", "[broad_phase]") { using namespace ipc; - const BroadPhaseMethod method = GENERATE_BROAD_PHASE_METHODS(); + const auto broad_phase = GENERATE(tests::BroadPhaseGenerator::create()); Eigen::MatrixXd V0, U; Eigen::MatrixXi E, F; @@ -232,7 +237,7 @@ TEST_CASE("Compare BP against brute force", "[broad_phase]") for (int i = 0; i < 2; i++) { Eigen::MatrixXd V1 = V0 + U; - test_broad_phase(mesh, V0, V1, method, false); + test_broad_phase(mesh, V0, V1, broad_phase, false); U.setRandom(); U *= 3; @@ -249,9 +254,9 @@ TEST_CASE("Cloth-Ball", "[ccd][broad_phase][cloth-ball][.]") CollisionMesh mesh(V0, E, F); - const BroadPhaseMethod method = GENERATE_BROAD_PHASE_METHODS(); + const auto broad_phase = GENERATE(tests::BroadPhaseGenerator::create()); test_broad_phase( - mesh, V0, V1, method, true, + mesh, V0, V1, broad_phase, true, (tests::DATA_DIR / "cloth_ball_bf_ccd_candidates.json").string()); } diff --git a/tests/src/tests/broad_phase/test_stq.cpp b/tests/src/tests/broad_phase/test_stq.cpp index eb804cda8..7428d0800 100644 --- a/tests/src/tests/broad_phase/test_stq.cpp +++ b/tests/src/tests/broad_phase/test_stq.cpp @@ -5,6 +5,7 @@ #include #include +#include using namespace ipc; #ifdef IPC_TOOLKIT_WITH_CUDA @@ -22,18 +23,19 @@ TEST_CASE("STQ All Cases", "[broad_phase][stq]") double inflation_radius = 0; #ifdef IPC_TOOLKIT_WITH_CUDA - const BroadPhaseMethod method = GENERATE( - BroadPhaseMethod::SWEEP_AND_PRUNE, - BroadPhaseMethod::SWEEP_AND_TINIEST_QUEUE); + const std::shared_ptr broad_phase = GENERATE( + std::static_pointer_cast(std::make_shared()), + std::static_pointer_cast( + std::make_shared())); #else - const BroadPhaseMethod method = BroadPhaseMethod::SWEEP_AND_PRUNE; + const std::shared_ptr broad_phase = + std::make_shared(); #endif - std::shared_ptr stq = BroadPhase::make_broad_phase(method); - stq->build(V0, V1, E, F, inflation_radius); + broad_phase->build(V0, V1, E, F, inflation_radius); Candidates candidates; - stq->detect_collision_candidates(V0.cols(), candidates); + broad_phase->detect_collision_candidates(V0.cols(), candidates); CHECK(candidates.size() == 6'852'873); CHECK(candidates.vv_candidates.size() == 0); @@ -42,22 +44,22 @@ TEST_CASE("STQ All Cases", "[broad_phase][stq]") CHECK(candidates.fv_candidates.size() == 1'655'541); std::vector vv_candidates; - stq->detect_vertex_vertex_candidates(vv_candidates); + broad_phase->detect_vertex_vertex_candidates(vv_candidates); CHECK(vv_candidates.size() == 84'912); std::vector ev_candidates; - stq->detect_edge_vertex_candidates(ev_candidates); + broad_phase->detect_edge_vertex_candidates(ev_candidates); CHECK(ev_candidates.size() == 1'666'926); std::vector ef_candidates; - stq->detect_edge_face_candidates(ef_candidates); + broad_phase->detect_edge_face_candidates(ef_candidates); CHECK(ef_candidates.size() == 9'248'220); std::vector ff_candidates; - stq->detect_face_face_candidates(ff_candidates); + broad_phase->detect_face_face_candidates(ff_candidates); CHECK(ff_candidates.size() == 3'975'589); - stq->clear(); + broad_phase->clear(); } #ifdef IPC_TOOLKIT_WITH_CUDA @@ -73,10 +75,10 @@ TEST_CASE("Puffer-Ball", "[ccd][broad_phase][stq][cuda]") CollisionMesh mesh(V0, E, F); - const BroadPhaseMethod method = BroadPhaseMethod::SWEEP_AND_TINIEST_QUEUE; + const auto stq = std::make_shared(); Candidates candidates; - candidates.build(mesh, V0, V1, /*inflation_radius=*/0, method); + candidates.build(mesh, V0, V1, /*inflation_radius=*/0, stq); CHECK(candidates.size() == 249'805'425); CHECK(candidates.vv_candidates.size() == 0); diff --git a/tests/src/tests/candidates/test_candidates.cpp b/tests/src/tests/candidates/test_candidates.cpp index 17ac8b9d9..350d1f67f 100644 --- a/tests/src/tests/candidates/test_candidates.cpp +++ b/tests/src/tests/candidates/test_candidates.cpp @@ -1,6 +1,8 @@ #include #include +#include +#include using namespace ipc; diff --git a/tests/src/tests/ccd/test_ccd.cpp b/tests/src/tests/ccd/test_ccd.cpp index e9228d349..59bec472a 100644 --- a/tests/src/tests/ccd/test_ccd.cpp +++ b/tests/src/tests/ccd/test_ccd.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include using namespace ipc; @@ -23,8 +25,7 @@ TEST_CASE("Repeated CCD", "[ccd][repeat][.]") constexpr long FIRST_MAX_ITER = 1'000'000, SECOND_MAX_ITER = 1'000'000; constexpr double MIN_DISTANCE = 0.0; - // BroadPhaseMethod method = GENERATE_BROAD_PHASE_METHODS(); - BroadPhaseMethod broadphase_method = BroadPhaseMethod::HASH_GRID; + auto broad_phase = std::make_shared(); double inflation_radius = 0; bool recompute_candidates = GENERATE(false, true); @@ -81,7 +82,7 @@ TEST_CASE("Repeated CCD", "[ccd][repeat][.]") V1 = mesh.vertices(V1); Candidates candidates; - candidates.build(mesh, V0, V1, inflation_radius, broadphase_method); + candidates.build(mesh, V0, V1, inflation_radius, broad_phase); bool has_collisions = !candidates.is_step_collision_free( mesh, V0, V1, MIN_DISTANCE, @@ -104,7 +105,7 @@ TEST_CASE("Repeated CCD", "[ccd][repeat][.]") // CHECK(!has_intersections(Vt, E, F)); if (recompute_candidates) { - candidates.build(mesh, V0, Vt, inflation_radius, broadphase_method); + candidates.build(mesh, V0, Vt, inflation_radius, broad_phase); } has_collisions_repeated = !candidates.is_step_collision_free( @@ -116,7 +117,7 @@ TEST_CASE("Repeated CCD", "[ccd][repeat][.]") TightInclusionCCD(SECOND_TOL, SECOND_MAX_ITER)); CAPTURE( - t0_filename, t1_filename, broadphase_method, recompute_candidates, + t0_filename, t1_filename, broad_phase->name(), recompute_candidates, has_collisions, collision_free_step_size, has_collisions_repeated, stepsize_repeated); CHECK(!has_collisions_repeated); @@ -218,12 +219,11 @@ TEST_CASE("Slow CCD", "[CCD]") CollisionMesh mesh = CollisionMesh::build_from_full_mesh(V0, E, F); const double min_distance = 1e-3; - const BroadPhaseMethod broad_phase_method = - BroadPhaseMethod::SWEEP_AND_PRUNE; + auto broad_phase = std::make_shared(); // Broad phase Candidates candidates; - candidates.build(mesh, V0, V1, min_distance / 2, broad_phase_method); + candidates.build(mesh, V0, V1, min_distance / 2, broad_phase); const TightInclusionCCD tight_inclusion( /*tolerance=*/100 * TightInclusionCCD::DEFAULT_TOLERANCE); diff --git a/tests/src/tests/ccd/test_gpu_ccd.cpp b/tests/src/tests/ccd/test_gpu_ccd.cpp index 0bde11449..d9bcaffd1 100644 --- a/tests/src/tests/ccd/test_gpu_ccd.cpp +++ b/tests/src/tests/ccd/test_gpu_ccd.cpp @@ -6,6 +6,8 @@ #include #include +#include +#include using namespace ipc; @@ -43,14 +45,14 @@ TEST_CASE("GPU CCD", "[ccd][gpu]") const double min_distance = 0; const double toi_cpu = compute_collision_free_stepsize( - mesh, V0, V1, min_distance, BroadPhaseMethod::SWEEP_AND_PRUNE, + mesh, V0, V1, min_distance, std::make_shared(), TightInclusionCCD(tolerance, max_iterations)); // Got this value from running the code CHECK(toi_cpu == Catch::Approx(4.76837158203125000e-06)); const double toi_gpu = compute_collision_free_stepsize( - mesh, V0, V1, min_distance, BroadPhaseMethod::SWEEP_AND_TINIEST_QUEUE, + mesh, V0, V1, min_distance, std::make_shared(), TightInclusionCCD(tolerance, max_iterations)); // Got this value from running the code diff --git a/tests/src/tests/collisions/test_normal_collisions.cpp b/tests/src/tests/collisions/test_normal_collisions.cpp index ec53f2add..5289a8a4b 100644 --- a/tests/src/tests/collisions/test_normal_collisions.cpp +++ b/tests/src/tests/collisions/test_normal_collisions.cpp @@ -34,8 +34,8 @@ TEST_CASE("Codim. vertex-vertex collisions", "[collisions][codim]") CHECK(mesh.num_edges() == 0); CHECK(mesh.num_faces() == 0); - const BroadPhaseMethod method = GENERATE_BROAD_PHASE_METHODS(); - CAPTURE(method); + auto broad_phase = GENERATE(tests::BroadPhaseGenerator::create()); + CAPTURE(broad_phase->name()); SECTION("Candidates") { @@ -43,7 +43,7 @@ TEST_CASE("Codim. vertex-vertex collisions", "[collisions][codim]") V1.col(1) *= 0.5; Candidates candidates; - candidates.build(mesh, vertices, V1, thickness, method); + candidates.build(mesh, vertices, V1, thickness, broad_phase); CHECK(candidates.size() > 0); CHECK(candidates.vv_candidates.size() == candidates.size()); @@ -79,7 +79,7 @@ TEST_CASE("Codim. vertex-vertex collisions", "[collisions][codim]") use_improved_max_approximator); collisions.set_enable_shape_derivatives(enable_shape_derivatives); - collisions.build(mesh, vertices, dhat, min_distance, method); + collisions.build(mesh, vertices, dhat, min_distance, broad_phase); CHECK(collisions.size() == 12); CHECK(collisions.vv_collisions.size() == 12); @@ -126,8 +126,8 @@ TEST_CASE("Codim. edge-vertex collisions", "[collisions][codim]") CHECK(mesh.num_edges() == 4); CHECK(mesh.num_faces() == 0); - const BroadPhaseMethod method = GENERATE_BROAD_PHASE_METHODS(); - CAPTURE(method); + auto broad_phase = GENERATE(tests::BroadPhaseGenerator::create()); + CAPTURE(broad_phase->name()); SECTION("Candidates") { @@ -135,7 +135,7 @@ TEST_CASE("Codim. edge-vertex collisions", "[collisions][codim]") V1.bottomRows(3).col(1).array() -= 4; // Translate the codim vertices Candidates candidates; - candidates.build(mesh, vertices, V1, thickness, method); + candidates.build(mesh, vertices, V1, thickness, broad_phase); CHECK(candidates.size() == 15); CHECK(candidates.vv_candidates.size() == 3); @@ -174,7 +174,8 @@ TEST_CASE("Codim. edge-vertex collisions", "[collisions][codim]") collisions.set_enable_shape_derivatives(enable_shape_derivatives); const double dhat = 0.25; - collisions.build(mesh, vertices, dhat, /*min_distance=*/0.8, method); + collisions.build( + mesh, vertices, dhat, /*min_distance=*/0.8, broad_phase); const int expected_num_collisions = 6 + int(use_improved_max_approximator); diff --git a/tests/src/tests/potential/test_barrier_potential.cpp b/tests/src/tests/potential/test_barrier_potential.cpp index c5951944a..e8a725768 100644 --- a/tests/src/tests/potential/test_barrier_potential.cpp +++ b/tests/src/tests/potential/test_barrier_potential.cpp @@ -20,7 +20,7 @@ TEST_CASE( "Barrier potential full gradient and hessian", "[potential][barrier_potential][gradient][hessian]") { - const BroadPhaseMethod method = GENERATE_BROAD_PHASE_METHODS(); + const auto broad_phase = GENERATE(tests::BroadPhaseGenerator::create()); const bool use_area_weighting = GENERATE(true, false); const bool use_improved_max_approximator = GENERATE(true, false); const bool use_physical_barrier = GENERATE(true, false); @@ -69,9 +69,9 @@ TEST_CASE( mesh = CollisionMesh::build_from_full_mesh(vertices, edges, faces); vertices = mesh.vertices(vertices); } - collisions.build(mesh, vertices, dhat, /*dmin=*/0, method); + collisions.build(mesh, vertices, dhat, /*dmin=*/0, broad_phase); CAPTURE( - dhat, method, all_vertices_on_surface, use_area_weighting, + dhat, broad_phase->name(), all_vertices_on_surface, use_area_weighting, use_improved_max_approximator); CHECK(collisions.size() > 0); diff --git a/tests/src/tests/test_has_intersections.cpp b/tests/src/tests/test_has_intersections.cpp index a0dce5e95..d05824f92 100644 --- a/tests/src/tests/test_has_intersections.cpp +++ b/tests/src/tests/test_has_intersections.cpp @@ -86,13 +86,13 @@ TEST_CASE("Has intersections", "[intersection]") Eigen::Matrix3d R2 = GENERATE(take(2, tests::RotationGenerator::create())); #endif - const BroadPhaseMethod broad_phase_method = GENERATE_BROAD_PHASE_METHODS(); + const auto broad_phase = GENERATE(tests::BroadPhaseGenerator::create()); Eigen::MatrixXd V; Eigen::MatrixXi E, F; bool success = combine_meshes(mesh1_name, mesh2_name, R1, R2, dim, V, E, F); REQUIRE(success); - CAPTURE(broad_phase_method); - CHECK(has_intersections(CollisionMesh(V, E, F), V, broad_phase_method)); + CAPTURE(broad_phase->name()); + CHECK(has_intersections(CollisionMesh(V, E, F), V, broad_phase)); } diff --git a/tests/src/tests/utils.cpp b/tests/src/tests/utils.cpp index 680ddf84f..ba5ab21b9 100644 --- a/tests/src/tests/utils.cpp +++ b/tests/src/tests/utils.cpp @@ -2,6 +2,15 @@ #include +#include +#include +#include +#include +#include +#ifdef IPC_TOOLKIT_WITH_CUDA +#include +#endif + #include #include @@ -11,6 +20,42 @@ namespace ipc::tests { +BroadPhaseGenerator::BroadPhaseGenerator() +{ + m_broad_phases = { { + std::make_shared(), + std::make_shared(), + std::make_shared(), + std::make_shared(), + std::make_shared(), +#ifdef IPC_TOOLKIT_WITH_CUDA + std::make_shared(), +#endif + } }; +} + +// Attempts to move the generator to the next element. +// Returns true if successful (and thus has another element that can be +// read) +bool BroadPhaseGenerator::next() { return ++m_current < m_broad_phases.size(); } + +// Precondition: +// The generator is either freshly constructed or the last call to next() +// returned true +std::shared_ptr const& BroadPhaseGenerator::get() const +{ + return m_broad_phases[m_current]; +} + +Catch::Generators::GeneratorWrapper> +BroadPhaseGenerator::create() +{ + return Catch::Generators::GeneratorWrapper>( + Catch::Detail::make_unique()); +} + +// ============================================================================ + bool load_mesh( const std::string& mesh_name, Eigen::MatrixXd& V, diff --git a/tests/src/tests/utils.hpp b/tests/src/tests/utils.hpp index 5043fe417..c6600c8fd 100644 --- a/tests/src/tests/utils.hpp +++ b/tests/src/tests/utils.hpp @@ -10,17 +10,30 @@ #include -#ifdef IPC_TOOLKIT_WITH_CUDA -#define NUM_BROAD_PHASE_METHODS static_cast(BroadPhaseMethod::NUM_METHODS) -#else -#define NUM_BROAD_PHASE_METHODS \ - (static_cast(BroadPhaseMethod::NUM_METHODS) - 1) -#endif +namespace ipc::tests { -#define GENERATE_BROAD_PHASE_METHODS() \ - static_cast(GENERATE(range(0, NUM_BROAD_PHASE_METHODS))); +class BroadPhaseGenerator + : public Catch::Generators::IGenerator> { +public: + BroadPhaseGenerator(); -namespace ipc::tests { + // Attempts to move the generator to the next element. + // Returns true if successful (and thus has another element that can be + // read) + bool next() override; + + // Precondition: + // The generator is either freshly constructed or the last call to next() + // returned true + std::shared_ptr const& get() const override; + + static Catch::Generators::GeneratorWrapper> + create(); + +private: + std::vector> m_broad_phases; + int m_current = 0; +}; // ============================================================================