Wildmeshing Toolkit
Loading...
Searching...
No Matches
SimplicialComplexBVH.hpp
1#pragma once
2#include <igl/AABB.h>
3#include <igl/in_element.h>
4#include <Eigen/Dense>
5#include <SimpleBVH/BVH.hpp>
6#include <wmtk/utils/Logger.hpp>
7
8
9namespace wmtk::components::topological_offset {
10
11
13{
14private:
15 // tet bvh
16 igl::AABB<MatrixXd, 3> m_tet_aabb_tree;
17 bool m_has_tets = false;
18
19 // triangle bvh
20 SimpleBVH::BVH m_tri_bvh;
21 bool m_has_tris = false;
22
23 // edge bvh
24 SimpleBVH::BVH m_edge_bvh;
25 bool m_has_edges = false;
26
27 bool m_is_3d;
28 MatrixXd m_V_T = MatrixXd(0, 3); // tet vertices
29 MatrixXi m_T_T = MatrixXi(0, 4); // tets w.r.t. V_T vertices
30 // igl::AABB<MatrixXd, 3> m_tet_aabb_tree;
31
36 std::vector<Eigen::Vector2d> m_v2;
37 std::vector<Eigen::Vector2i> m_e2;
38
39public:
52 void init(
53 const MatrixXd& V,
54 const MatrixXi& T,
55 const MatrixXi& F,
56 const MatrixXi& E,
57 const MatrixXi& P)
58 {
59 m_is_3d = (V.cols() == 3);
60 MatrixXd V_3d; // if V 2d, pad vertices to 3d (BVH needs this. should be fixed inside BVH)
61 if (!m_is_3d) {
62 V_3d.resize(V.rows(), 3);
63 V_3d.setZero();
64 V_3d.block(0, 0, V.rows(), V.cols()) = V;
65 } else {
66 V_3d = V;
67 }
68 assert(V_3d.rows() == V.rows());
69 assert(V_3d.cols() == 3);
70
71 // extract isolated faces
72 std::vector<Vector3i> faces;
73 for (int i = 0; i < F.rows(); i++) {
74 faces.push_back(F.row(i));
75 }
76
77 // complex is 3d and has tets
78 if (m_is_3d && T.rows() > 0) {
79 for (int i = 0; i < T.rows(); i++) {
80 int a = T(i, 0);
81 int b = T(i, 1);
82 int c = T(i, 2);
83 int d = T(i, 3);
84 faces.emplace_back(a, c, b);
85 faces.emplace_back(a, b, d);
86 faces.emplace_back(b, c, d);
87 faces.emplace_back(a, d, c);
88 }
89
90 m_V_T = V;
91 m_T_T = T;
92 // m_T_T.col(2).swap(m_T_T.col(3));
93 m_has_tets = true;
94 m_tet_aabb_tree.init(m_V_T, m_T_T);
95 }
96
97 // initialize triangle bvh
98 if (!faces.empty()) {
99 MatrixXi F_combo(faces.size(), 3);
100 for (size_t i = 0; i < faces.size(); i++) {
101 F_combo.row(i) = faces[i];
102 }
103 m_tri_bvh.init(V_3d, F_combo, 1e-6);
104 m_has_tris = true;
105 }
106
107 std::vector<Eigen::Vector2i> edges; // extract isolated edges
108 for (int i = 0; i < E.rows(); i++) { // actual edges
109 edges.push_back(E.row(i));
110 }
111 for (int i = 0; i < P.rows(); i++) { // pseudo edges
112 edges.emplace_back(P(i), P(i));
113 }
114
115 if (!edges.empty()) {
116 MatrixXi E_combo(edges.size(), 2);
117 for (size_t i = 0; i < edges.size(); i++) {
118 E_combo.row(i) = edges[i];
119 }
120 m_edge_bvh.init(V_3d, E_combo, 1e-6);
121 m_has_edges = true;
122 }
123
124 // The feature query's backing copies; see the members. 2D only -- in 3D the feature query
125 // runs on the input-complex envelope instead.
126 m_v2.clear();
127 m_e2.clear();
128 if (!m_is_3d) {
129 m_v2.reserve(size_t(V.rows()));
130 for (int i = 0; i < V.rows(); i++) {
131 m_v2.emplace_back(V(i, 0), V(i, 1));
132 }
133 m_e2.assign(edges.begin(), edges.end());
134 }
135 }
136
140 bool inside_any_tet(const Vector3d& p) const
141 {
142 // 2D or no tets
143 if (!m_has_tets) {
144 return false;
145 }
146
147 Eigen::MatrixXd Q(1, 3);
148 Q.row(0) = p;
149 Eigen::VectorXi I;
150 igl::in_element(m_V_T, m_T_T, Q, m_tet_aabb_tree, I);
151 return (I(0) != -1);
152 }
153
157 double squared_dist(const VectorXd& p) const
158 {
159 double min_sq_dist = std::numeric_limits<double>::max();
160
161 // pad to 3d if necessary
162 Vector3d p3;
163 if (p.size() == 2) {
164 p3 << p(0), p(1), 0.0;
165 } else {
166 p3 = p;
167 }
168
169 // inside tet check
170 if (m_has_tets) { // has any tets
171 if (inside_any_tet(p3)) {
172 return 0.0;
173 }
174 }
175
176 Vector3d closest_p;
177 double tmp_sq_dist;
178
179 if (m_has_tris) { // min dist to isolated triangles (and tet faces)
180 m_tri_bvh.nearest_facet(p3, closest_p, min_sq_dist);
181 }
182
183 if (m_has_edges) { // min dist to isolated edges (and 'pseudo'edges, ie isolated vertices)
184 m_edge_bvh.nearest_facet(p3, closest_p, tmp_sq_dist);
185 if (tmp_sq_dist < min_sq_dist) {
186 min_sq_dist = tmp_sq_dist;
187 }
188 }
189
190 return min_sq_dist;
191 }
192
193 double dist(const VectorXd& p) const { return sqrt(squared_dist(p)); }
194
200 Vector3d nearest_point(const VectorXd& p) const
201 {
202 // pad to 3d if necessary
203 Vector3d p3;
204 if (p.size() == 2) {
205 p3 << p(0), p(1), 0.0;
206 } else {
207 p3 = p;
208 }
209
210 Vector3d best_p = p3;
211 double best_sq_dist = std::numeric_limits<double>::max();
212
213 Vector3d closest_p;
214 double tmp_sq_dist;
215 if (m_has_tris) {
216 m_tri_bvh.nearest_facet(p3, closest_p, tmp_sq_dist);
217 if (tmp_sq_dist < best_sq_dist) {
218 best_sq_dist = tmp_sq_dist;
219 best_p = closest_p;
220 }
221 }
222 if (m_has_edges) {
223 m_edge_bvh.nearest_facet(p3, closest_p, tmp_sq_dist);
224 if (tmp_sq_dist < best_sq_dist) {
225 best_sq_dist = tmp_sq_dist;
226 best_p = closest_p;
227 }
228 }
229 return best_p;
230 }
231
245 const Eigen::Vector2d& p,
246 Eigen::Vector2d& result,
247 bool& on_corner,
248 Eigen::Vector2d& seg_normal,
249 int& feature_id) const
250 {
251 // Thrown, not asserted: SimpleBVH's nearest_facet walks boxlist[2] unconditionally, so
252 // querying an empty tree is a segfault and a candidate id past m_e2 is an out-of-bounds
253 // read, and release builds compile asserts out.
254 if (m_is_3d || !m_has_edges || m_v2.empty() || m_e2.empty()) {
255 log_and_throw_error(
256 "SimplicialComplexBVH::nearest_point_feature: 2D {} | edges {} | copies {}/{}",
257 !m_is_3d,
258 m_has_edges,
259 m_v2.size(),
260 m_e2.size());
261 }
262
263 const Eigen::Vector3d p3(p[0], p[1], 0.0);
264 Eigen::Vector3d nearest;
265 double sq_dist;
266 m_edge_bvh.nearest_facet(p3, nearest, sq_dist);
267
268 std::vector<unsigned int> candidates;
269 const double pad = 1e-9 + 1e-9 * std::sqrt(sq_dist);
270 // The BVH stores 2D data lifted to z = 0; query with a 3D box spanning it.
271 const Eigen::Vector3d lo(nearest[0] - pad, nearest[1] - pad, -pad);
272 const Eigen::Vector3d hi(nearest[0] + pad, nearest[1] + pad, pad);
273 m_edge_bvh.intersect_box(lo, hi, candidates);
274 if (candidates.empty()) {
275 log_and_throw_error(
276 "SimplicialComplexBVH::nearest_point_feature: no box candidate at the foot "
277 "point -- the BVH and the input copies disagree");
278 }
279
280 double best = std::numeric_limits<double>::max();
281 for (const unsigned int fid : candidates) {
282 if (fid >= m_e2.size()) {
283 log_and_throw_error(
284 "SimplicialComplexBVH::nearest_point_feature: candidate id {} out of range "
285 "({} edges)",
286 fid,
287 m_e2.size());
288 }
289 const Eigen::Vector2d a = m_v2[size_t(m_e2[fid][0])];
290 const Eigen::Vector2d b = m_v2[size_t(m_e2[fid][1])];
291 const Eigen::Vector2d ab = b - a;
292 const double len2 = ab.squaredNorm();
293 const double t = len2 > 0 ? (p - a).dot(ab) / len2 : 0.0;
294 Eigen::Vector2d foot;
295 bool corner;
296 int id;
297 if (len2 <= 0 || t <= 0) {
298 foot = a;
299 corner = true;
300 id = m_e2[fid][0];
301 } else if (t >= 1) {
302 foot = b;
303 corner = true;
304 id = m_e2[fid][1];
305 } else {
306 foot = a + t * ab;
307 corner = false;
308 id = int(fid);
309 }
310 const double d2 = (p - foot).squaredNorm();
311 if (d2 < best) {
312 best = d2;
313 result = foot;
314 on_corner = corner;
315 feature_id = id;
316 if (!corner) {
317 seg_normal = Eigen::Vector2d(-ab[1], ab[0]) / std::sqrt(len2);
318 }
319 }
320 }
321 return best;
322 }
323
324 void clear()
325 {
326 m_tri_bvh.clear();
327 m_has_tris = false;
328 m_edge_bvh.clear();
329 m_has_edges = false;
330 m_v2.clear();
331 m_e2.clear();
332 m_tet_aabb_tree = igl::AABB<MatrixXd, 3>(); // reset
333 m_has_tets = false;
334 m_V_T.resize(0, 3);
335 m_T_T.resize(0, 4);
336 }
337};
338
339
340} // namespace wmtk::components::topological_offset
std::vector< Eigen::Vector2d > m_v2
Definition SimplicialComplexBVH.hpp:36
bool inside_any_tet(const Vector3d &p) const
check if a point is inside any tet.
Definition SimplicialComplexBVH.hpp:140
double squared_dist(const VectorXd &p) const
compute distance to complex
Definition SimplicialComplexBVH.hpp:157
Vector3d nearest_point(const VectorXd &p) const
find the nearest point on the complex (triangles and edges) to p
Definition SimplicialComplexBVH.hpp:200
void init(const MatrixXd &V, const MatrixXi &T, const MatrixXi &F, const MatrixXi &E, const MatrixXi &P)
initialize BVH from "closed" simplicial complex
Definition SimplicialComplexBVH.hpp:52
double nearest_point_feature(const Eigen::Vector2d &p, Eigen::Vector2d &result, bool &on_corner, Eigen::Vector2d &seg_normal, int &feature_id) const
2D: the nearest point of the complex together with which feature it is.
Definition SimplicialComplexBVH.hpp:244