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 @@
+
+
+
+
+
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)) {