55
66#include < ipc/config.hpp>
77
8+ #include < igl/remove_unreferenced.h>
89#include < tbb/parallel_for.h>
910#include < tbb/blocked_range.h>
1011#include < shared_mutex>
@@ -19,6 +20,21 @@ namespace {
1920 return method != BroadPhaseMethod::SWEEP_AND_TINIEST_QUEUE
2021 && method != BroadPhaseMethod::SWEEP_AND_TINIEST_QUEUE_GPU;
2122 }
23+
24+ // Pad codim_edges because remove_unreferenced requires a N×3 matrix.
25+ Eigen::MatrixXi pad_edges (const Eigen::MatrixXi& E)
26+ {
27+ assert (E.cols () == 2 );
28+ Eigen::MatrixXi E_padded (E.rows (), 3 );
29+ E_padded.leftCols (2 ) = E;
30+ E_padded.col (2 ) = E.col (1 );
31+ return E_padded;
32+ }
33+
34+ Eigen::MatrixXi unpad_edges (const Eigen::MatrixXi& E_padded)
35+ {
36+ return E_padded.leftCols (2 );
37+ }
2238} // namespace
2339
2440void Candidates::build (
@@ -37,23 +53,67 @@ void Candidates::build(
3753 broad_phase->build (vertices, mesh.edges (), mesh.faces (), inflation_radius);
3854 broad_phase->detect_collision_candidates (dim, *this );
3955
40- if (mesh.num_codim_vertices ()) {
41- if (!implements_vertex_vertex (broad_phase_method)) {
42- logger ().warn (
43- " STQ broad phase does not support codim. point-point, skipping." );
44- return ;
45- }
56+ if (mesh.num_codim_vertices ()
57+ && !implements_vertex_vertex (broad_phase_method)) {
58+ // TODO: Assumes this is the same as implements_edge_vertex
59+ logger ().warn (
60+ " STQ broad phase does not support codim. point-point nor point-edge, skipping." );
61+ return ;
62+ }
4663
64+ // Codim. vertices to codim. vertices:
65+ if (mesh.num_codim_vertices ()) {
4766 broad_phase->clear ();
4867 broad_phase->build (
4968 vertices (mesh.codim_vertices (), Eigen::all), //
5069 Eigen::MatrixXi (), Eigen::MatrixXi (), inflation_radius);
70+
5171 broad_phase->detect_vertex_vertex_candidates (vv_candidates);
5272 for (auto & [vi, vj] : vv_candidates) {
5373 vi = mesh.codim_vertices ()[vi];
5474 vj = mesh.codim_vertices ()[vj];
5575 }
5676 }
77+
78+ // Codim. edges to codim. vertices:
79+ // Only need this in 3D because in 2D, the codim. edges are the same as the
80+ // edges of the boundary. Only need codim. edge to codim. vertex because
81+ // codim. edge to non-codim. vertex is the same as edge-edge or face-vertex.
82+ if (dim == 3 && mesh.num_codim_vertices () && mesh.num_codim_edges ()) {
83+ // Extract the vertices of the codim. edges
84+ Eigen::MatrixXd CE_V; // vertices of codim. edges
85+ Eigen::MatrixXi CE; // codim. edges (indices into CEV)
86+ {
87+ Eigen::VectorXi _I, _J; // unused mappings
88+ igl::remove_unreferenced (
89+ vertices,
90+ pad_edges (mesh.edges ()(mesh.codim_edges (), Eigen::all)), CE_V,
91+ CE, _I, _J);
92+ CE = unpad_edges (CE);
93+ }
94+
95+ const size_t nCV = mesh.num_codim_vertices ();
96+ Eigen::MatrixXd V (nCV + CE_V.rows (), dim);
97+ V.topRows (nCV) = vertices (mesh.codim_vertices (), Eigen::all);
98+ V.bottomRows (CE_V.rows ()) = CE_V;
99+
100+ CE.array () += nCV; // Offset indices to account for codim. vertices
101+
102+ // TODO: Can we reuse the broad phase from above?
103+ broad_phase->clear ();
104+ broad_phase->can_vertices_collide = [&](size_t vi, size_t vj) {
105+ // Ignore c-edge to c-edge and c-vertex to c-vertex
106+ return ((vi < nCV) ^ (vj < nCV)) && mesh.can_collide (vi, vj);
107+ };
108+ broad_phase->build (V, CE, Eigen::MatrixXi (), inflation_radius);
109+
110+ broad_phase->detect_edge_vertex_candidates (ev_candidates);
111+ for (auto & [ei, vi] : ev_candidates) {
112+ assert (vi < mesh.codim_vertices ().size ());
113+ ei = mesh.codim_edges ()[ei]; // Map back to mesh.edges
114+ vi = mesh.codim_vertices ()[vi]; // Map back to vertices
115+ }
116+ }
57117}
58118
59119void Candidates::build (
@@ -74,24 +134,74 @@ void Candidates::build(
74134 vertices_t0, vertices_t1, mesh.edges (), mesh.faces (), inflation_radius);
75135 broad_phase->detect_collision_candidates (dim, *this );
76136
77- if (mesh.num_codim_vertices ()) {
78- if (!implements_vertex_vertex (broad_phase_method)) {
79- logger ().warn (
80- " STQ broad phase does not support codim. point-point, skipping." );
81- return ;
82- }
137+ if (mesh.num_codim_vertices ()
138+ && !implements_vertex_vertex (broad_phase_method)) {
139+ // TODO: Assumes this is the same as implements_edge_vertex
140+ logger ().warn (
141+ " STQ broad phase does not support codim. point-point nor point-edge, skipping." );
142+ return ;
143+ }
83144
145+ // Codim. vertices to codim. vertices:
146+ if (mesh.num_codim_vertices ()) {
84147 broad_phase->clear ();
85148 broad_phase->build (
86149 vertices_t0 (mesh.codim_vertices (), Eigen::all),
87150 vertices_t1 (mesh.codim_vertices (), Eigen::all), //
88151 Eigen::MatrixXi (), Eigen::MatrixXi (), inflation_radius);
152+
89153 broad_phase->detect_vertex_vertex_candidates (vv_candidates);
90154 for (auto & [vi, vj] : vv_candidates) {
91155 vi = mesh.codim_vertices ()[vi];
92156 vj = mesh.codim_vertices ()[vj];
93157 }
94158 }
159+
160+ // Codim. edges to codim. vertices:
161+ // Only need this in 3D because in 2D, the codim. edges are the same as the
162+ // edges of the boundary. Only need codim. edge to codim. vertex because
163+ // codim. edge to non-codim. vertex is the same as edge-edge or face-vertex.
164+ if (dim == 3 && mesh.num_codim_vertices () && mesh.num_codim_edges ()) {
165+ // Extract the vertices of the codim. edges
166+ Eigen::MatrixXd CE_V_t0, CE_V_t1; // vertices of codim. edges
167+ Eigen::MatrixXi CE; // codim. edges (indices into CEV)
168+ {
169+ Eigen::VectorXi _I, J;
170+ igl::remove_unreferenced (
171+ vertices_t0,
172+ pad_edges (mesh.edges ()(mesh.codim_edges (), Eigen::all)),
173+ CE_V_t0, CE, _I, J);
174+ CE_V_t1 = vertices_t1 (J, Eigen::all);
175+ CE = unpad_edges (CE);
176+ }
177+
178+ const size_t nCV = mesh.num_codim_vertices ();
179+
180+ Eigen::MatrixXd V_t0 (nCV + CE_V_t0.rows (), dim);
181+ V_t0.topRows (nCV) = vertices_t0 (mesh.codim_vertices (), Eigen::all);
182+ V_t0.bottomRows (CE_V_t0.rows ()) = CE_V_t0;
183+
184+ Eigen::MatrixXd V_t1 (nCV + CE_V_t1.rows (), dim);
185+ V_t1.topRows (nCV) = vertices_t1 (mesh.codim_vertices (), Eigen::all);
186+ V_t1.bottomRows (CE_V_t1.rows ()) = CE_V_t1;
187+
188+ CE.array () += nCV; // Offset indices to account for codim. vertices
189+
190+ // TODO: Can we reuse the broad phase from above?
191+ broad_phase->clear ();
192+ broad_phase->can_vertices_collide = [&](size_t vi, size_t vj) {
193+ // Ignore c-edge to c-edge and c-vertex to c-vertex
194+ return ((vi < nCV) ^ (vj < nCV)) && mesh.can_collide (vi, vj);
195+ };
196+ broad_phase->build (V_t0, V_t1, CE, Eigen::MatrixXi (), inflation_radius);
197+
198+ broad_phase->detect_edge_vertex_candidates (ev_candidates);
199+ for (auto & [ei, vi] : ev_candidates) {
200+ assert (vi < mesh.codim_vertices ().size ());
201+ ei = mesh.codim_edges ()[ei]; // Map back to mesh.edges
202+ vi = mesh.codim_vertices ()[vi]; // Map back to vertices
203+ }
204+ }
95205}
96206
97207bool Candidates::is_step_collision_free (
@@ -110,8 +220,7 @@ bool Candidates::is_step_collision_free(
110220 double toi;
111221 bool is_collision = (*this )[i].ccd (
112222 vertices_t0, vertices_t1, mesh.edges (), mesh.faces (), toi,
113- min_distance,
114- /* tmax=*/ 1.0 , tolerance, max_iterations);
223+ min_distance, /* tmax=*/ 1.0 , tolerance, max_iterations);
115224
116225 if (is_collision) {
117226 return false ;
0 commit comments