diff --git a/.gitignore b/.gitignore index b3eaf4c4f..910c6205a 100644 --- a/.gitignore +++ b/.gitignore @@ -670,3 +670,4 @@ python/update_bindings.py # External test data tests/data +CMakeGraphVizOptions.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index f025befaf..4210e8376 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,30 +154,30 @@ target_link_libraries(ipc_toolkit PUBLIC Eigen3::Eigen) # libigl include(libigl) -target_link_libraries(ipc_toolkit PUBLIC igl::core igl::predicates) +target_link_libraries(ipc_toolkit PRIVATE igl::core igl::predicates) # TBB include(onetbb) -target_link_libraries(ipc_toolkit PUBLIC TBB::tbb) +target_link_libraries(ipc_toolkit PRIVATE TBB::tbb) # Provably conservative CCD of [Wang and Ferguson et al. 2021] include(tight_inclusion) -target_link_libraries(ipc_toolkit PUBLIC tight_inclusion::tight_inclusion) +target_link_libraries(ipc_toolkit PRIVATE tight_inclusion::tight_inclusion) # Scalable CCD (STQ broad phase and GPU Tight Inclusion) include(scalable_ccd) -target_link_libraries(ipc_toolkit PUBLIC scalable_ccd::scalable_ccd) +target_link_libraries(ipc_toolkit PRIVATE scalable_ccd::scalable_ccd) # CCD if(IPC_TOOLKIT_WITH_INEXACT_CCD) # Etienne Vouga's CTCD Library for the floating point root finding algorithm include(evouga_ccd) - target_link_libraries(ipc_toolkit PUBLIC evouga::ccd) + target_link_libraries(ipc_toolkit PRIVATE evouga::ccd) endif() # SimpleBVH include(simple_bvh) -target_link_libraries(ipc_toolkit PUBLIC simple_bvh::simple_bvh) +target_link_libraries(ipc_toolkit PRIVATE simple_bvh::simple_bvh) # Logger include(spdlog) @@ -186,7 +186,7 @@ target_link_libraries(ipc_toolkit PUBLIC spdlog::spdlog) # rational-cpp (requires GMP) if(IPC_TOOLKIT_WITH_RATIONAL_INTERSECTION) include(rational_cpp) - target_link_libraries(ipc_toolkit PUBLIC rational::rational) + target_link_libraries(ipc_toolkit PRIVATE rational::rational) endif() # Faster unordered map diff --git a/cmake/recipes/abseil.cmake b/cmake/recipes/abseil.cmake index 327d0a308..24c1b7b8f 100644 --- a/cmake/recipes/abseil.cmake +++ b/cmake/recipes/abseil.cmake @@ -6,10 +6,10 @@ endif() message(STATUS "Third-party: creating target 'absl::flat_hash_map'") -option(ABSL_PROPAGATE_CXX_STD "Use CMake C++ standard meta features (e.g. cxx_std_11) that propagate to targets that link to Abseil" ON) +option(ABSL_PROPAGATE_CXX_STD "Use CMake C++ standard meta features (e.g. cxx_std_17) that propagate to targets that link to Abseil" ON) option(ABSL_USE_SYSTEM_INCLUDES "Silence warnings in Abseil headers by marking them as SYSTEM includes" ON) option(ABSL_BUILD_TESTING "If ON, Abseil will build all of Abseil's own tests." OFF) set(ABSL_IDE_FOLDER "ThirdParty/Abseil") include(CPM) -CPMAddPackage("gh:abseil/abseil-cpp#20230125.3") \ No newline at end of file +CPMAddPackage("gh:abseil/abseil-cpp#20250512.1") \ No newline at end of file diff --git a/docs/source/_static/graphviz/dependencies.dot b/docs/source/_static/graphviz/dependencies.dot new file mode 100644 index 000000000..f9b73e872 --- /dev/null +++ b/docs/source/_static/graphviz/dependencies.dot @@ -0,0 +1,81 @@ +digraph "IPC Toolkit Dependencies" { + bgcolor = "transparent"; + splines = ortho; + layout = dot; + // nodesep = 0.1; + ranksep = 0.5; + node [fontname = "Menlo"; style = filled; penwidth = 2;]; + edge [penwidth = 2; fontname = "Menlo";]; + subgraph clusterLegend { + label = "Legend"; + color = "grey"; + fontcolor = "grey"; + fontname = "Avenir Medium"; + penwidth = 2; + node [fontname = "Avenir Light";]; + edge [fontname = "Avenir Light";]; + legendNode0 [label = "Static Library";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";]; + legendNode1 [label = "Shared Library";shape = box;style = "rounded,filled";fillcolor = "#CCE7F8";color = "#6596B2";]; + legendNode2 [label = "Interface Library";shape = box;style = "rounded,filled";fillcolor = "#FFE6CC";color = "#DAA52D";]; + legendNode0 -> legendNode1 [label = "Public"; color = "#8FB976"; fontcolor = "#8FB976";]; + legendNode2 -> legendNode0 [label = "Interface"; color = "#DAA52D"; fontcolor = "#DAA52D";]; + legendNode1 -> legendNode2 [label = "Private"; color = "#BE6562"; fontcolor = "#BE6562";]; + } + // Force ipc_toolkit to top + subgraph { + rank = "source"; + "node5"; + } + "node0" [label = "Eigen3_Eigen\n(Eigen3::Eigen)";shape = box;style = "rounded,filled";fillcolor = "#FFE6CC";color = "#DAA52D";]; + "node1" [label = "filib\n(filib::filib)";shape = box;style = "rounded,filled";fillcolor = "#CCE7F8";color = "#6596B2";]; + "node2" [label = "igl_core\n(igl::core)";shape = box;style = "rounded,filled";fillcolor = "#FFE6CC";color = "#DAA52D";]; + "node2" -> "node0" [color = "#DAA52D";]; + // igl_core -> Eigen3_Eigen + "node3" [label = "igl_predicates\n(igl::predicates)";shape = box;style = "rounded,filled";fillcolor = "#FFE6CC";color = "#DAA52D";]; + "node3" -> "node2" [color = "#DAA52D";]; + // igl_predicates -> igl_core + "node4" [label = "predicates\n(predicates::predicates)";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";]; + "node3" -> "node4" [color = "#DAA52D";]; + // igl_predicates -> predicates + "node5" [label = "ipc_toolkit\n(ipc::toolkit)";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";]; + "node5" -> "node0" [color = "#8FB976";]; + // ipc_toolkit -> Eigen3_Eigen + "node5" -> "node1" [color = "#8FB976";]; + // ipc_toolkit -> filib + "node5" -> "node2" [color = "#BE6562";]; + // ipc_toolkit -> igl_core + "node5" -> "node3" [color = "#BE6562";]; + // ipc_toolkit -> igl_predicates + "node6" [label = "robin_map\n(tsl::robin_map)";shape = box;style = "rounded,filled";fillcolor = "#FFE6CC";color = "#DAA52D";]; + "node5" -> "node6" [color = "#8FB976";]; + // ipc_toolkit -> robin_map + "node7" [label = "scalable_ccd\n(scalable_ccd::scalable_ccd)";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";]; + "node7" -> "node0" [color = "#8FB976";]; + // scalable_ccd -> Eigen3_Eigen + "node8" [label = "spdlog\n(spdlog::spdlog)";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";]; + "node7" -> "node8" [color = "#8FB976";]; + // scalable_ccd -> spdlog + "node9" [label = "tbb\n(TBB::tbb)";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";]; + "node7" -> "node9" [color = "#BE6562";]; + // scalable_ccd -> tbb + "node5" -> "node7" [color = "#BE6562";]; + // ipc_toolkit -> scalable_ccd + "node10" [label = "simple_bvh\n(simple_bvh::simple_bvh)";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";]; + "node10" -> "node0" [color = "#8FB976";]; + // simple_bvh -> Eigen3_Eigen + "node5" -> "node10" [color = "#BE6562";]; + // ipc_toolkit -> simple_bvh + "node5" -> "node8" [color = "#8FB976";]; + // ipc_toolkit -> spdlog + "node5" -> "node9" [color = "#BE6562";]; + // ipc_toolkit -> tbb + "node11" [label = "tight_inclusion\n(tight_inclusion::tight_inclusion)";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";]; + "node11" -> "node0" [color = "#8FB976";]; + // tight_inclusion -> Eigen3_Eigen + "node11" -> "node8" [color = "#8FB976";]; + // tight_inclusion -> spdlog + "node5" -> "node11" [color = "#BE6562";]; + // ipc_toolkit -> tight_inclusion + "node12" [label = "absl_hash\n(absl::hash)";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";]; + "node5" -> "node12" [color = "#8FB976";]; +} \ No newline at end of file diff --git a/docs/source/_static/graphviz/dependencies.svg b/docs/source/_static/graphviz/dependencies.svg new file mode 100644 index 000000000..a69938df1 --- /dev/null +++ b/docs/source/_static/graphviz/dependencies.svg @@ -0,0 +1,267 @@ + + + + + + +IPC Toolkit Dependencies + +clusterLegend + +Legend + + + +legendNode0 + +Static Library + + + +legendNode1 + +Shared Library + + + +legendNode0->legendNode1 + + +Public + + + +legendNode2 + +Interface Library + + + +legendNode1->legendNode2 + + +Private + + + +legendNode2->legendNode0 + + +Interface + + + +node5 + +ipc_toolkit +(ipc::toolkit) + + + +node0 + +Eigen3_Eigen +(Eigen3::Eigen) + + + +node5->node0 + + + + + +node1 + +filib +(filib::filib) + + + +node5->node1 + + + + + +node2 + +igl_core +(igl::core) + + + +node5->node2 + + + + + +node3 + +igl_predicates +(igl::predicates) + + + +node5->node3 + + + + + +node6 + +robin_map +(tsl::robin_map) + + + +node5->node6 + + + + + +node7 + +scalable_ccd +(scalable_ccd::scalable_ccd) + + + +node5->node7 + + + + + +node8 + +spdlog +(spdlog::spdlog) + + + +node5->node8 + + + + + +node9 + +tbb +(TBB::tbb) + + + +node5->node9 + + + + + +node10 + +simple_bvh +(simple_bvh::simple_bvh) + + + +node5->node10 + + + + + +node11 + +tight_inclusion +(tight_inclusion::tight_inclusion) + + + +node5->node11 + + + + + +node12 + +absl_hash +(absl::hash) + + + +node5->node12 + + + + + +node2->node0 + + + + + +node3->node2 + + + + + +node4 + +predicates +(predicates::predicates) + + + +node3->node4 + + + + + +node7->node0 + + + + + +node7->node8 + + + + + +node7->node9 + + + + + +node10->node0 + + + + + +node11->node0 + + + + + +node11->node8 + + + + + diff --git a/docs/source/about/dependencies.rst b/docs/source/about/dependencies.rst index 4094be89f..72a88e990 100644 --- a/docs/source/about/dependencies.rst +++ b/docs/source/about/dependencies.rst @@ -1,6 +1,11 @@ Dependencies ============ +.. figure:: /_static/graphviz/dependencies.svg + :align: center + + Default dependencies of the ``ipc::toolkit`` library. Excludes CUDA and Python bindings. + The IPC Toolkit depends on a handful of third-party libraries, which are used to provide various functionality. **All required dependencies are downloaded through CMake** depending on the build options, and are built automatically when you build the IPC Toolkit. You do not need to install them separately. diff --git a/docs/source/conf.py b/docs/source/conf.py index c8627096f..abea5d6ce 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -114,7 +114,7 @@ # -- GraphViz configuration ---------------------------------- graphviz_output_format = 'svg' -graphviz_dot_args = ["-Ecolor=#CE93D8", "-Kdot"] +graphviz_dot_args = ["-Ecolor=#CE93D8", "-Kdot", "-Gbgcolor=transparent", "-Nfontname=Menlo"] # python_apigen_modules = { # "ipctk": "", diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 0d350b613..c867a548e 100755 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -9,6 +9,18 @@ target_include_directories(ipctk PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") target_link_libraries(ipctk PRIVATE ipc::toolkit) +# libigl (binding igl::edges) +include(libigl) +target_link_libraries(ipctk PRIVATE igl::core igl::predicates) + +# Tight Inclusion CCD (binding tight_inclusion as ipctk.tight_inclusion) +include(tight_inclusion) +target_link_libraries(ipctk PRIVATE tight_inclusion::tight_inclusion) + +# TBB (binding tbb::global_control) +include(onetbb) +target_link_libraries(ipctk PRIVATE TBB::tbb) + if (FILIB_BUILD_SHARED_LIB AND WIN32) # Copy DLLs to the output directory add_custom_command( diff --git a/src/ipc/broad_phase/broad_phase.cpp b/src/ipc/broad_phase/broad_phase.cpp index df5a0911e..c5589c53c 100644 --- a/src/ipc/broad_phase/broad_phase.cpp +++ b/src/ipc/broad_phase/broad_phase.cpp @@ -1,12 +1,6 @@ #include "broad_phase.hpp" #include -#include -#include -#include -#include -#include -#include #include namespace ipc { diff --git a/src/ipc/broad_phase/bvh.cpp b/src/ipc/broad_phase/bvh.cpp index 1e872374c..57b5df729 100644 --- a/src/ipc/broad_phase/bvh.cpp +++ b/src/ipc/broad_phase/bvh.cpp @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -10,6 +11,16 @@ using namespace std::placeholders; namespace ipc { +BVH::BVH() + : BroadPhase() + , vertex_bvh(std::make_unique()) + , edge_bvh(std::make_unique()) + , face_bvh(std::make_unique()) +{ +} + +BVH::~BVH() = default; + void BVH::build( Eigen::ConstRef vertices, Eigen::ConstRef edges, @@ -17,9 +28,9 @@ void BVH::build( const double inflation_radius) { BroadPhase::build(vertices, edges, faces, inflation_radius); - init_bvh(vertex_boxes, vertex_bvh); - init_bvh(edge_boxes, edge_bvh); - init_bvh(face_boxes, face_bvh); + init_bvh(vertex_boxes, *vertex_bvh); + init_bvh(edge_boxes, *edge_bvh); + init_bvh(face_boxes, *face_bvh); } void BVH::build( @@ -30,9 +41,9 @@ void BVH::build( const double inflation_radius) { BroadPhase::build(vertices_t0, vertices_t1, edges, faces, inflation_radius); - init_bvh(vertex_boxes, vertex_bvh); - init_bvh(edge_boxes, edge_bvh); - init_bvh(face_boxes, face_bvh); + init_bvh(vertex_boxes, *vertex_bvh); + init_bvh(edge_boxes, *edge_bvh); + init_bvh(face_boxes, *face_bvh); } void BVH::init_bvh(const std::vector& boxes, SimpleBVH::BVH& bvh) @@ -51,9 +62,9 @@ void BVH::init_bvh(const std::vector& boxes, SimpleBVH::BVH& bvh) void BVH::clear() { - vertex_bvh.clear(); - edge_bvh.clear(); - face_bvh.clear(); + vertex_bvh->clear(); + edge_bvh->clear(); + face_bvh->clear(); } template @@ -111,7 +122,7 @@ void BVH::detect_vertex_vertex_candidates( detect_candidates< VertexVertexCandidate, /*swap_order=*/false, /*triangular=*/true>( - vertex_boxes, vertex_bvh, can_vertices_collide, candidates); + vertex_boxes, *vertex_bvh, can_vertices_collide, candidates); } void BVH::detect_edge_vertex_candidates( @@ -124,7 +135,7 @@ void BVH::detect_edge_vertex_candidates( // In 2D and for codimensional edge-vertex collisions, there are more // vertices than edges, so we want to iterate over the edges. detect_candidates( - edge_boxes, vertex_bvh, + edge_boxes, *vertex_bvh, std::bind(&BVH::can_edge_vertex_collide, this, _1, _2), candidates); } @@ -137,7 +148,7 @@ void BVH::detect_edge_edge_candidates( detect_candidates< EdgeEdgeCandidate, /*swap_order=*/false, /*triangular=*/true>( - edge_boxes, edge_bvh, std::bind(&BVH::can_edges_collide, this, _1, _2), + edge_boxes, *edge_bvh, std::bind(&BVH::can_edges_collide, this, _1, _2), candidates); } @@ -150,7 +161,7 @@ void BVH::detect_face_vertex_candidates( // The ratio vertices:faces is 1:2, so we want to iterate over the vertices. detect_candidates( - vertex_boxes, face_bvh, + vertex_boxes, *face_bvh, std::bind(&BVH::can_face_vertex_collide, this, _1, _2), candidates); } @@ -163,7 +174,7 @@ void BVH::detect_edge_face_candidates( // The ratio edges:faces is 3:2, so we want to iterate over the faces. detect_candidates( - face_boxes, edge_bvh, + face_boxes, *edge_bvh, std::bind(&BVH::can_edge_face_collide, this, _1, _2), candidates); } @@ -176,7 +187,7 @@ void BVH::detect_face_face_candidates( detect_candidates< FaceFaceCandidate, /*swap_order=*/false, /*triangular=*/true>( - face_boxes, face_bvh, std::bind(&BVH::can_faces_collide, this, _1, _2), + face_boxes, *face_bvh, std::bind(&BVH::can_faces_collide, this, _1, _2), candidates); } } // namespace ipc \ No newline at end of file diff --git a/src/ipc/broad_phase/bvh.hpp b/src/ipc/broad_phase/bvh.hpp index dd3dc80f4..0b19ec4de 100644 --- a/src/ipc/broad_phase/bvh.hpp +++ b/src/ipc/broad_phase/bvh.hpp @@ -2,14 +2,19 @@ #include -#include +#include + +namespace SimpleBVH { +class BVH; +} namespace ipc { /// @brief Bounding Volume Hierarchy (BVH) broad phase collision detection. class BVH : public BroadPhase { public: - BVH() = default; + BVH(); + ~BVH(); /// @brief Get the name of the broad phase method. /// @return The name of the broad phase method. @@ -97,11 +102,11 @@ class BVH : public BroadPhase { std::vector& candidates); /// @brief BVH containing the vertices. - SimpleBVH::BVH vertex_bvh; + std::unique_ptr vertex_bvh; /// @brief BVH containing the edges. - SimpleBVH::BVH edge_bvh; + std::unique_ptr edge_bvh; /// @brief BVH containing the faces. - SimpleBVH::BVH face_bvh; + std::unique_ptr face_bvh; }; } // namespace ipc \ No newline at end of file diff --git a/src/ipc/broad_phase/sweep_and_prune.cpp b/src/ipc/broad_phase/sweep_and_prune.cpp index 8050aed2e..4f5ae6f97 100644 --- a/src/ipc/broad_phase/sweep_and_prune.cpp +++ b/src/ipc/broad_phase/sweep_and_prune.cpp @@ -1,9 +1,24 @@ #include "sweep_and_prune.hpp" +#include #include namespace ipc { +struct SweepAndPrune::Boxes { + ~Boxes() = default; + + std::vector vertices; + std::vector edges; + std::vector faces; +}; + +SweepAndPrune::SweepAndPrune() : BroadPhase(), boxes(std::make_unique()) +{ +} + +SweepAndPrune::~SweepAndPrune() = default; + void SweepAndPrune::build( Eigen::ConstRef vertices, Eigen::ConstRef edges, @@ -15,9 +30,10 @@ void SweepAndPrune::build( clear(); - scalable_ccd::build_vertex_boxes(vertices, vertex_boxes, inflation_radius); - scalable_ccd::build_edge_boxes(vertex_boxes, edges, edge_boxes); - scalable_ccd::build_face_boxes(vertex_boxes, faces, face_boxes); + scalable_ccd::build_vertex_boxes( + vertices, boxes->vertices, inflation_radius); + scalable_ccd::build_edge_boxes(boxes->vertices, edges, boxes->edges); + scalable_ccd::build_face_boxes(boxes->vertices, faces, boxes->faces); } void SweepAndPrune::build( @@ -33,24 +49,24 @@ void SweepAndPrune::build( clear(); scalable_ccd::build_vertex_boxes( - vertices_t0, vertices_t1, vertex_boxes, inflation_radius); - scalable_ccd::build_edge_boxes(vertex_boxes, edges, edge_boxes); - scalable_ccd::build_face_boxes(vertex_boxes, faces, face_boxes); + vertices_t0, vertices_t1, boxes->vertices, inflation_radius); + scalable_ccd::build_edge_boxes(boxes->vertices, edges, boxes->edges); + scalable_ccd::build_face_boxes(boxes->vertices, faces, boxes->faces); } void SweepAndPrune::clear() { BroadPhase::clear(); - vertex_boxes.clear(); - edge_boxes.clear(); - face_boxes.clear(); + boxes->vertices.clear(); + boxes->edges.clear(); + boxes->faces.clear(); } void SweepAndPrune::detect_vertex_vertex_candidates( std::vector& candidates) const { std::vector> overlaps; - scalable_ccd::sort_and_sweep(vertex_boxes, vv_sort_axis, overlaps); + scalable_ccd::sort_and_sweep(boxes->vertices, vv_sort_axis, overlaps); for (const auto& [vai, vbi] : overlaps) { if (can_vertices_collide(vai, vbi)) { @@ -64,7 +80,7 @@ void SweepAndPrune::detect_edge_vertex_candidates( { std::vector> overlaps; scalable_ccd::sort_and_sweep( - edge_boxes, vertex_boxes, ev_sort_axis, overlaps); + boxes->edges, boxes->vertices, ev_sort_axis, overlaps); for (const auto& [ei, vi] : overlaps) { if (can_edge_vertex_collide(ei, vi)) { @@ -77,7 +93,7 @@ void SweepAndPrune::detect_edge_edge_candidates( std::vector& candidates) const { std::vector> overlaps; - scalable_ccd::sort_and_sweep(edge_boxes, ee_sort_axis, overlaps); + scalable_ccd::sort_and_sweep(boxes->edges, ee_sort_axis, overlaps); for (const auto& [eai, ebi] : overlaps) { if (can_edges_collide(eai, ebi)) { @@ -91,7 +107,7 @@ void SweepAndPrune::detect_face_vertex_candidates( { std::vector> overlaps; scalable_ccd::sort_and_sweep( - face_boxes, vertex_boxes, fv_sort_axis, overlaps); + boxes->faces, boxes->vertices, fv_sort_axis, overlaps); for (const auto& [fi, vi] : overlaps) { if (can_face_vertex_collide(fi, vi)) { @@ -105,7 +121,7 @@ void SweepAndPrune::detect_edge_face_candidates( { std::vector> overlaps; scalable_ccd::sort_and_sweep( - edge_boxes, face_boxes, ef_sort_axis, overlaps); + boxes->edges, boxes->faces, ef_sort_axis, overlaps); for (const auto& [ei, fi] : overlaps) { if (can_edge_face_collide(ei, fi)) { @@ -118,7 +134,7 @@ void SweepAndPrune::detect_face_face_candidates( std::vector& candidates) const { std::vector> overlaps; - scalable_ccd::sort_and_sweep(face_boxes, ff_sort_axis, overlaps); + scalable_ccd::sort_and_sweep(boxes->faces, ff_sort_axis, overlaps); for (const auto& [fai, fbi] : overlaps) { if (can_faces_collide(fai, fbi)) { @@ -131,7 +147,7 @@ void SweepAndPrune::detect_face_face_candidates( bool SweepAndPrune::can_edge_vertex_collide(size_t ei, size_t vi) const { - const auto& [e0i, e1i, _] = edge_boxes[ei].vertex_ids; + const auto& [e0i, e1i, _] = boxes->edges[ei].vertex_ids; // Checked by scalable_ccd::sort_and_sweep assert(vi != e0i && vi != e1i); @@ -141,8 +157,8 @@ bool SweepAndPrune::can_edge_vertex_collide(size_t ei, size_t vi) const bool SweepAndPrune::can_edges_collide(size_t eai, size_t ebi) const { - const auto& [ea0i, ea1i, _] = edge_boxes[eai].vertex_ids; - const auto& [eb0i, eb1i, __] = edge_boxes[ebi].vertex_ids; + const auto& [ea0i, ea1i, _] = boxes->edges[eai].vertex_ids; + const auto& [eb0i, eb1i, __] = boxes->edges[ebi].vertex_ids; // Checked by scalable_ccd::sort_and_sweep assert(ea0i != eb0i && ea0i != eb1i && ea1i != eb0i && ea1i != eb1i); @@ -153,7 +169,7 @@ bool SweepAndPrune::can_edges_collide(size_t eai, size_t ebi) const bool SweepAndPrune::can_face_vertex_collide(size_t fi, size_t vi) const { - const auto& [f0i, f1i, f2i] = face_boxes[fi].vertex_ids; + const auto& [f0i, f1i, f2i] = boxes->faces[fi].vertex_ids; // Checked by scalable_ccd::sort_and_sweep assert(vi != f0i && vi != f1i && vi != f2i); @@ -164,8 +180,8 @@ bool SweepAndPrune::can_face_vertex_collide(size_t fi, size_t vi) const bool SweepAndPrune::can_edge_face_collide(size_t ei, size_t fi) const { - const auto& [e0i, e1i, _] = edge_boxes[ei].vertex_ids; - const auto& [f0i, f1i, f2i] = face_boxes[fi].vertex_ids; + const auto& [e0i, e1i, _] = boxes->edges[ei].vertex_ids; + const auto& [f0i, f1i, f2i] = boxes->faces[fi].vertex_ids; // Checked by scalable_ccd::sort_and_sweep assert( @@ -179,8 +195,8 @@ bool SweepAndPrune::can_edge_face_collide(size_t ei, size_t fi) const bool SweepAndPrune::can_faces_collide(size_t fai, size_t fbi) const { - const auto& [fa0i, fa1i, fa2i] = face_boxes[fai].vertex_ids; - const auto& [fb0i, fb1i, fb2i] = face_boxes[fbi].vertex_ids; + const auto& [fa0i, fa1i, fa2i] = boxes->faces[fai].vertex_ids; + const auto& [fb0i, fb1i, fb2i] = boxes->faces[fbi].vertex_ids; // Checked by scalable_ccd::sort_and_sweep assert( diff --git a/src/ipc/broad_phase/sweep_and_prune.hpp b/src/ipc/broad_phase/sweep_and_prune.hpp index 5750af199..e157fbbcc 100644 --- a/src/ipc/broad_phase/sweep_and_prune.hpp +++ b/src/ipc/broad_phase/sweep_and_prune.hpp @@ -2,14 +2,13 @@ #include -#include - namespace ipc { /// @brief Sweep and Prune broad phase collision detection. class SweepAndPrune : public BroadPhase { public: - SweepAndPrune() = default; + SweepAndPrune(); + ~SweepAndPrune(); /// @brief Get the name of the broad phase method. /// @return The name of the broad phase method. @@ -79,9 +78,9 @@ class SweepAndPrune : public BroadPhase { bool can_edge_face_collide(size_t ei, size_t fi) const override; bool can_faces_collide(size_t fai, size_t fbi) const override; - std::vector vertex_boxes; - std::vector edge_boxes; - std::vector face_boxes; + // Pimpl pattern to hide scalable_ccd::AABB details + struct Boxes; + std::unique_ptr boxes; mutable int vv_sort_axis = 0; mutable int ev_sort_axis = 0; diff --git a/src/ipc/broad_phase/sweep_and_tiniest_queue.cpp b/src/ipc/broad_phase/sweep_and_tiniest_queue.cpp index be37c26fc..d6f198643 100644 --- a/src/ipc/broad_phase/sweep_and_tiniest_queue.cpp +++ b/src/ipc/broad_phase/sweep_and_tiniest_queue.cpp @@ -6,6 +6,22 @@ namespace ipc { +struct SweepAndTiniestQueue::Boxes { + ~Boxes() = default; + + std::vector vertices; + std::vector edges; + std::vector faces; +}; + +SweepAndTiniestQueue::SweepAndTiniestQueue() + : BroadPhase() + , boxes(std::make_unique()) +{ +} + +SweepAndTiniestQueue::~SweepAndTiniestQueue() = default; + void SweepAndTiniestQueue::build( Eigen::ConstRef _vertices, Eigen::ConstRef edges, @@ -21,9 +37,9 @@ void SweepAndTiniestQueue::build( const Eigen::MatrixXd vertices = to_X3d(_vertices); scalable_ccd::cuda::build_vertex_boxes( - vertices, vertex_boxes, inflation_radius); - scalable_ccd::cuda::build_edge_boxes(vertex_boxes, edges, edge_boxes); - scalable_ccd::cuda::build_face_boxes(vertex_boxes, faces, face_boxes); + vertices, boxes->vertices, inflation_radius); + scalable_ccd::cuda::build_edge_boxes(boxes->vertices, edges, boxes->edges); + scalable_ccd::cuda::build_face_boxes(boxes->vertices, faces, boxes->faces); } void SweepAndTiniestQueue::build( @@ -45,17 +61,17 @@ void SweepAndTiniestQueue::build( const Eigen::MatrixXd vertices_t1 = to_X3d(_vertices_t1); scalable_ccd::cuda::build_vertex_boxes( - vertices_t0, vertices_t1, vertex_boxes, inflation_radius); - scalable_ccd::cuda::build_edge_boxes(vertex_boxes, edges, edge_boxes); - scalable_ccd::cuda::build_face_boxes(vertex_boxes, faces, face_boxes); + vertices_t0, vertices_t1, boxes->vertices, inflation_radius); + scalable_ccd::cuda::build_edge_boxes(boxes->vertices, edges, boxes->edges); + scalable_ccd::cuda::build_face_boxes(boxes->vertices, faces, boxes->faces); } void SweepAndTiniestQueue::clear() { BroadPhase::clear(); - vertex_boxes.clear(); - edge_boxes.clear(); - face_boxes.clear(); + boxes->vertices.clear(); + boxes->edges.clear(); + boxes->faces.clear(); } void SweepAndTiniestQueue::detect_vertex_vertex_candidates( @@ -64,7 +80,7 @@ void SweepAndTiniestQueue::detect_vertex_vertex_candidates( scalable_ccd::cuda::BroadPhase broad_phase; // TODO: Precompute d_vertex_boxes broad_phase.build( - std::make_shared(vertex_boxes)); + std::make_shared(boxes->vertices)); for (const auto& [vai, vbi] : broad_phase.detect_overlaps()) { if (can_vertices_collide(vai, vbi)) { @@ -79,8 +95,8 @@ void SweepAndTiniestQueue::detect_edge_vertex_candidates( scalable_ccd::cuda::BroadPhase broad_phase; // TODO: Precompute d_vertex_boxes and d_edge_boxes broad_phase.build( - std::make_shared(edge_boxes), - std::make_shared(vertex_boxes)); + std::make_shared(boxes->edges), + std::make_shared(boxes->vertices)); for (const auto& [ei, vi] : broad_phase.detect_overlaps()) { if (can_edge_vertex_collide(ei, vi)) { @@ -95,7 +111,7 @@ void SweepAndTiniestQueue::detect_edge_edge_candidates( scalable_ccd::cuda::BroadPhase broad_phase; // TODO: Precompute d_edge_boxes broad_phase.build( - std::make_shared(edge_boxes)); + std::make_shared(boxes->edges)); for (const auto& [eai, ebi] : broad_phase.detect_overlaps()) { if (can_edges_collide(eai, ebi)) { @@ -110,8 +126,8 @@ void SweepAndTiniestQueue::detect_face_vertex_candidates( scalable_ccd::cuda::BroadPhase broad_phase; // TODO: Precompute d_vertex_boxes and d_face_boxes broad_phase.build( - std::make_shared(face_boxes), - std::make_shared(vertex_boxes)); + std::make_shared(boxes->faces), + std::make_shared(boxes->vertices)); for (const auto& [fi, vi] : broad_phase.detect_overlaps()) { if (can_face_vertex_collide(fi, vi)) { @@ -126,8 +142,8 @@ void SweepAndTiniestQueue::detect_edge_face_candidates( scalable_ccd::cuda::BroadPhase broad_phase; // TODO: Precompute d_face_boxes and d_edge_boxes broad_phase.build( - std::make_shared(edge_boxes), - std::make_shared(face_boxes)); + std::make_shared(boxes->edges), + std::make_shared(boxes->faces)); for (const auto& [ei, fi] : broad_phase.detect_overlaps()) { if (can_edge_face_collide(ei, fi)) { @@ -142,7 +158,7 @@ void SweepAndTiniestQueue::detect_face_face_candidates( scalable_ccd::cuda::BroadPhase broad_phase; // TODO: Precompute d_face_boxes broad_phase.build( - std::make_shared(face_boxes)); + std::make_shared(boxes->faces)); for (const auto& [fai, fbi] : broad_phase.detect_overlaps()) { if (can_faces_collide(fai, fbi)) { @@ -155,7 +171,7 @@ void SweepAndTiniestQueue::detect_face_face_candidates( bool SweepAndTiniestQueue::can_edge_vertex_collide(size_t ei, size_t vi) const { - const auto& [e0i, e1i, _] = edge_boxes[ei].vertex_ids; + const auto& [e0i, e1i, _] = boxes->edges[ei].vertex_ids; // Checked by scalable_ccd assert(vi != e0i && vi != e1i); @@ -165,8 +181,8 @@ bool SweepAndTiniestQueue::can_edge_vertex_collide(size_t ei, size_t vi) const bool SweepAndTiniestQueue::can_edges_collide(size_t eai, size_t ebi) const { - const auto& [ea0i, ea1i, _] = edge_boxes[eai].vertex_ids; - const auto& [eb0i, eb1i, __] = edge_boxes[ebi].vertex_ids; + const auto& [ea0i, ea1i, _] = boxes->edges[eai].vertex_ids; + const auto& [eb0i, eb1i, __] = boxes->edges[ebi].vertex_ids; // Checked by scalable_ccd assert(ea0i != eb0i && ea0i != eb1i && ea1i != eb0i && ea1i != eb1i); @@ -177,7 +193,7 @@ bool SweepAndTiniestQueue::can_edges_collide(size_t eai, size_t ebi) const bool SweepAndTiniestQueue::can_face_vertex_collide(size_t fi, size_t vi) const { - const auto& [f0i, f1i, f2i] = face_boxes[fi].vertex_ids; + const auto& [f0i, f1i, f2i] = boxes->faces[fi].vertex_ids; // Checked by scalable_ccd assert(vi != f0i && vi != f1i && vi != f2i); @@ -188,8 +204,8 @@ bool SweepAndTiniestQueue::can_face_vertex_collide(size_t fi, size_t vi) const bool SweepAndTiniestQueue::can_edge_face_collide(size_t ei, size_t fi) const { - const auto& [e0i, e1i, _] = edge_boxes[ei].vertex_ids; - const auto& [f0i, f1i, f2i] = face_boxes[fi].vertex_ids; + const auto& [e0i, e1i, _] = boxes->edges[ei].vertex_ids; + const auto& [f0i, f1i, f2i] = boxes->faces[fi].vertex_ids; // Checked by scalable_ccd assert( @@ -203,8 +219,8 @@ bool SweepAndTiniestQueue::can_edge_face_collide(size_t ei, size_t fi) const bool SweepAndTiniestQueue::can_faces_collide(size_t fai, size_t fbi) const { - const auto& [fa0i, fa1i, fa2i] = face_boxes[fai].vertex_ids; - const auto& [fb0i, fb1i, fb2i] = face_boxes[fbi].vertex_ids; + const auto& [fa0i, fa1i, fa2i] = boxes->faces[fai].vertex_ids; + const auto& [fb0i, fb1i, fb2i] = boxes->faces[fbi].vertex_ids; // Checked by scalable_ccd assert( diff --git a/src/ipc/broad_phase/sweep_and_tiniest_queue.hpp b/src/ipc/broad_phase/sweep_and_tiniest_queue.hpp index 5f62ffc8f..1d7e308ab 100644 --- a/src/ipc/broad_phase/sweep_and_tiniest_queue.hpp +++ b/src/ipc/broad_phase/sweep_and_tiniest_queue.hpp @@ -6,14 +6,13 @@ #include -#include - namespace ipc { /// @brief Sweep and Tiniest Queue broad phase collision detection. class SweepAndTiniestQueue : public BroadPhase { public: - SweepAndTiniestQueue() = default; + SweepAndTiniestQueue(); + ~SweepAndTiniestQueue(); /// @brief Get the name of the broad phase method. /// @return The name of the broad phase method. @@ -83,9 +82,9 @@ class SweepAndTiniestQueue : public BroadPhase { bool can_edge_face_collide(size_t ei, size_t fi) const override; bool can_faces_collide(size_t fai, size_t fbi) const override; - std::vector vertex_boxes; - std::vector edge_boxes; - std::vector face_boxes; + // Pimpl pattern to hide scalable_ccd::AABB details + struct Boxes; + std::unique_ptr boxes; }; } // namespace ipc diff --git a/src/ipc/collision_mesh.cpp b/src/ipc/collision_mesh.cpp index 482ec23b9..1a6a28792 100644 --- a/src/ipc/collision_mesh.cpp +++ b/src/ipc/collision_mesh.cpp @@ -4,7 +4,6 @@ #include #include #include -#include namespace ipc { diff --git a/src/ipc/potentials/CMakeLists.txt b/src/ipc/potentials/CMakeLists.txt index 350d9055c..d646b2e4a 100644 --- a/src/ipc/potentials/CMakeLists.txt +++ b/src/ipc/potentials/CMakeLists.txt @@ -7,8 +7,8 @@ set(SOURCES normal_adhesion_potential.hpp normal_potential.cpp normal_potential.hpp + potential.cpp potential.hpp - potential.tpp tangential_adhesion_potential.cpp tangential_adhesion_potential.hpp tangential_potential.cpp diff --git a/src/ipc/potentials/normal_potential.cpp b/src/ipc/potentials/normal_potential.cpp index e93b1e912..3744efb88 100644 --- a/src/ipc/potentials/normal_potential.cpp +++ b/src/ipc/potentials/normal_potential.cpp @@ -1,5 +1,10 @@ #include "normal_potential.hpp" +#include + +#include +#include + namespace ipc { // -- Cumulative methods ------------------------------------------------------- diff --git a/src/ipc/potentials/potential.tpp b/src/ipc/potentials/potential.cpp similarity index 95% rename from src/ipc/potentials/potential.tpp rename to src/ipc/potentials/potential.cpp index acbd03022..325cc2711 100644 --- a/src/ipc/potentials/potential.tpp +++ b/src/ipc/potentials/potential.cpp @@ -1,7 +1,7 @@ -#pragma once - #include "potential.hpp" +#include +#include #include #include @@ -134,4 +134,7 @@ Eigen::SparseMatrix Potential::hessian( const Eigen::SparseMatrix& b) { return a + b; }); } +template class Potential; +template class Potential; + } // namespace ipc \ No newline at end of file diff --git a/src/ipc/potentials/potential.hpp b/src/ipc/potentials/potential.hpp index d66cca617..23dc7744b 100644 --- a/src/ipc/potentials/potential.hpp +++ b/src/ipc/potentials/potential.hpp @@ -78,6 +78,4 @@ template class Potential { PSDProjectionMethod::NONE) const = 0; }; -} // namespace ipc - -#include "potential.tpp" \ No newline at end of file +} // namespace ipc \ No newline at end of file diff --git a/src/ipc/potentials/tangential_potential.cpp b/src/ipc/potentials/tangential_potential.cpp index be34c9e84..bda8334d0 100644 --- a/src/ipc/potentials/tangential_potential.cpp +++ b/src/ipc/potentials/tangential_potential.cpp @@ -1,5 +1,11 @@ #include "friction_potential.hpp" +#include + +#include +#include +#include + namespace ipc { // -- Cumulative methods ------------------------------------------------------- diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6e289d749..b534803f8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -48,6 +48,14 @@ source_group(TREE "${PROJECT_SOURCE_DIR}/tests" FILES ${IPC_TOOLKIT_TESTS_SOURCE target_link_libraries(ipc_toolkit_tests PRIVATE ipc::toolkit) +# libigl (use igl::edges) +include(libigl) +target_link_libraries(ipc_toolkit_tests PRIVATE igl::core) + +# OneTBB (use tbb::global_control and tbb::parallel_sort) +include(onetbb) +target_link_libraries(ipc_toolkit_tests PRIVATE TBB::tbb) + include(catch2) target_link_libraries(ipc_toolkit_tests PRIVATE Catch2::Catch2) diff --git a/tests/src/tests/candidates/test_normals.cpp b/tests/src/tests/candidates/test_normals.cpp index cbf7b9a2c..e83dd7a01 100644 --- a/tests/src/tests/candidates/test_normals.cpp +++ b/tests/src/tests/candidates/test_normals.cpp @@ -15,8 +15,7 @@ TEST_CASE("Vertex-vertex collision normal", "[vv][normal]") { const int dim = GENERATE(2, 3); - Eigen::MatrixXd V(2, dim); - V.setZero(); + Eigen::MatrixXd V = Eigen::MatrixXd::Zero(2, dim); V(0, 0) = -1; V(1, 0) = 1; @@ -41,7 +40,8 @@ TEST_CASE("Vertex-vertex collision normal", "[vv][normal]") Eigen::MatrixXd fd_jacobian; VectorMax12d x = vv.dof(V, E, F); fd::finite_jacobian( - x, [&vv](const Eigen::MatrixXd& x) { return vv.compute_normal(x); }, + x, + [&vv](const Eigen::MatrixXd& x_fd) { return vv.compute_normal(x_fd); }, fd_jacobian); CHECK(fd::compare_jacobian(jacobian, fd_jacobian)); } @@ -87,7 +87,8 @@ TEST_CASE("Edge-vertex collision normal", "[ev][normal]") Eigen::MatrixXd fd_jacobian; VectorMax12d x = ev.dof(V, E, F); fd::finite_jacobian( - x, [&ev](const Eigen::MatrixXd& x) { return ev.compute_normal(x); }, + x, + [&ev](const Eigen::MatrixXd& x_fd) { return ev.compute_normal(x_fd); }, fd_jacobian); CHECK(fd::compare_jacobian(jacobian, fd_jacobian)); if (!fd::compare_jacobian(jacobian, fd_jacobian)) { @@ -131,7 +132,8 @@ TEST_CASE("Edge-edge collision normal", "[ee][normal]") Eigen::MatrixXd fd_jacobian; VectorMax12d x = ee.dof(V, E, F); fd::finite_jacobian( - x, [&ee](const Eigen::MatrixXd& x) { return ee.compute_normal(x); }, + x, + [&ee](const Eigen::MatrixXd& x_fd) { return ee.compute_normal(x_fd); }, fd_jacobian); CHECK(fd::compare_jacobian(jacobian, fd_jacobian)); if (!fd::compare_jacobian(jacobian, fd_jacobian)) { @@ -178,7 +180,8 @@ TEST_CASE("Face-vertex collision normal", "[fv][normal]") Eigen::MatrixXd fd_jacobian; VectorMax12d x = fv.dof(V, E, F); fd::finite_jacobian( - x, [&fv](const Eigen::MatrixXd& x) { return fv.compute_normal(x); }, + x, + [&fv](const Eigen::MatrixXd& x_fd) { return fv.compute_normal(x_fd); }, fd_jacobian); CHECK(fd::compare_jacobian(jacobian, fd_jacobian)); if (!fd::compare_jacobian(jacobian, fd_jacobian)) { @@ -209,7 +212,8 @@ TEST_CASE("Plane-vertex collision normal", "[pv][normal]") Eigen::MatrixXd fd_jacobian; VectorMax12d x = pv.dof(V, E, F); fd::finite_jacobian( - x, [&pv](const Eigen::MatrixXd& x) { return pv.compute_normal(x); }, + x, + [&pv](const Eigen::MatrixXd& x_fd) { return pv.compute_normal(x_fd); }, fd_jacobian); CHECK(fd::compare_jacobian(jacobian, fd_jacobian)); if (!fd::compare_jacobian(jacobian, fd_jacobian)) {