Lagrange
All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Modules Pages
EdgeAABBTree.impl.h
1/*
2 * Copyright 2020 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
14#include <lagrange/bvh/EdgeAABBTree.h>
15#include <lagrange/utils/assert.h>
16#include <lagrange/utils/point_on_segment.h>
17#include <lagrange/utils/point_segment_squared_distance.h>
18
19// clang-format off
20#include <lagrange/utils/warnoff.h>
21#include <igl/barycenter.h>
22#include <lagrange/utils/warnon.h>
23// clang-format on
24
25#include <limits>
26#include <numeric>
27
28namespace lagrange {
29namespace bvh {
30
32
33namespace internal {
34
35template <typename Scalar>
36Scalar sqr(Scalar x)
37{
38 return x * x;
39}
40
53template <typename Scalar, int DIM>
54Scalar inner_point_box_squared_distance(
55 const Eigen::Matrix<Scalar, DIM, 1>& p,
56 const Eigen::AlignedBox<Scalar, DIM>& B)
57{
58 assert(B.contains(p));
59 Scalar result = sqr(p[0] - B.min()[0]);
60 result = std::min(result, sqr(p[0] - B.max()[0]));
61 for (int c = 1; c < DIM; ++c) {
62 result = std::min(result, sqr(p[c] - B.min()[c]));
63 result = std::min(result, sqr(p[c] - B.max()[c]));
64 }
65 return result;
66}
67
79template <typename Scalar, int DIM>
80Scalar point_box_signed_squared_distance(
81 const Eigen::Matrix<Scalar, DIM, 1>& p,
82 const Eigen::AlignedBox<Scalar, DIM>& B)
83{
84 bool inside = true;
85 Scalar result = 0;
86 for (int c = 0; c < DIM; c++) {
87 if (p[c] < B.min()[c]) {
88 inside = false;
89 result += sqr(p[c] - B.min()[c]);
90 } else if (p[c] > B.max()[c]) {
91 inside = false;
92 result += sqr(p[c] - B.max()[c]);
93 }
94 }
95 if (inside) {
96 result = -inner_point_box_squared_distance(p, B);
97 }
98 return result;
99}
100
109template <typename Scalar, int DIM>
110Eigen::AlignedBox<Scalar, DIM> bbox_edge(
111 const Eigen::Matrix<Scalar, 1, DIM>& a,
112 const Eigen::Matrix<Scalar, 1, DIM>& b)
113{
114 Eigen::AlignedBox<Scalar, DIM> bbox;
115 bbox.extend(a.transpose());
116 bbox.extend(b.transpose());
117 return bbox;
118}
119
120} // namespace internal
121
123
124template <typename VertexArray, typename EdgeArray, int DIM>
125EdgeAABBTree<VertexArray, EdgeArray, DIM>::EdgeAABBTree(const VertexArray& V, const EdgeArray& E)
126{
127 la_runtime_assert(DIM == V.cols(), "Dimension mismatch in EdgeAABBTree!");
128 // Compute the centroids of all the edges in the input mesh
129 Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> centroids;
130 igl::barycenter(V, E, centroids);
131
132 // Top-down approach: split each set of primitives into 2 sets of roughly equal size,
133 // based on sorting the centroids along one direction or another.
134 std::vector<Index> edges(E.rows());
135 std::iota(edges.begin(), edges.end(), 0);
136
137 std::function<Index(Index, Index, Index)> top_down = [&](Index i, Index j, Index parent) {
138 // Scene is empty, so is the aabb tree
139 if (j - i == 0) {
140 return invalid<Index>();
141 }
142
143 // If there is only 1 edges left, then we are at a leaf
144 if (j - i == 1) {
145 Node node;
146 Index e = edges[i];
147 RowVectorType a = V.row(E(e, 0));
148 RowVectorType b = V.row(E(e, 1));
149 node.bbox = internal::bbox_edge<Scalar, DIM>(a, b);
150 node.parent = parent;
151 node.left = node.right = invalid<Index>();
152 node.index = e;
153 m_nodes.push_back(node);
154 return (Index)(m_nodes.size() - 1);
155 }
156
157 // Otherwise, we need to sort centroids along the longest dimension, and split recursively
158 Eigen::AlignedBox<Scalar, DIM> centroid_box;
159 for (Index k = i; k < j; ++k) {
160 Eigen::Matrix<Scalar, DIM, 1> c = centroids.row(edges[k]).transpose();
161 centroid_box.extend(c);
162 }
163 auto extent = centroid_box.diagonal();
164 int longest_dim = 0;
165 for (int dim = 1; dim < DIM; ++dim) {
166 if (extent(dim) > extent(longest_dim)) {
167 longest_dim = dim;
168 }
169 }
170 std::sort(edges.begin() + i, edges.begin() + j, [&](Index f1, Index f2) {
171 return centroids(f1, longest_dim) < centroids(f2, longest_dim);
172 });
173
174 // Then we can create a new internal node
175 Index current = (Index)m_nodes.size();
176 m_nodes.resize(current + 1);
177 Index midpoint = (i + j) / 2;
178 Index left = top_down(i, midpoint, current);
179 Index right = top_down(midpoint, j, current);
180 Node& node = m_nodes[current];
181 node.left = left;
182 node.right = right;
183 node.parent = parent;
184 node.index = invalid<Index>();
185 node.bbox = m_nodes[node.left].bbox.extend(m_nodes[node.right].bbox);
186
187 return current;
188 };
189
190 m_root = top_down(0, (Index)edges.size(), invalid<Index>());
191 m_vertices = &V;
192 m_edges = &E;
193}
194
195// -----------------------------------------------------------------------------
196
197template <typename VertexArray, typename EdgeArray, int DIM>
199 const RowVectorType& p,
200 Index element_id,
201 RowVectorType& closest_point,
202 Scalar& closest_sq_dist) const
203{
204 RowVectorType v0 = m_vertices->row((*m_edges)(element_id, 0));
205 RowVectorType v1 = m_vertices->row((*m_edges)(element_id, 1));
206 Scalar l0, l1;
207 closest_sq_dist = point_segment_squared_distance(p, v0, v1, closest_point, l0, l1);
208 if (point_on_segment(p, v0, v1)) {
209 closest_sq_dist = 0;
210 closest_point = p;
211 }
212}
213
214// -----------------------------------------------------------------------------
215
216template <typename VertexArray, typename EdgeArray, int DIM>
218 const RowVectorType& p,
219 Scalar sq_dist,
220 std::function<void(Scalar, Index, const RowVectorType&)> func) const
221{
222 foreach_element_in_radius_recursive(p, sq_dist, (Index)m_root, func);
223}
224
225template <typename VertexArray, typename EdgeArray, int DIM>
227 const RowVectorType& p,
228 Scalar sq_dist,
229 Index node_id,
230 ActionCallback func) const
231{
232 const auto& node = m_nodes[node_id];
233 if (node.is_leaf()) {
234 RowVectorType closest_point;
235 Scalar closest_sq_dist;
236 get_element_closest_point(p, node.index, closest_point, closest_sq_dist);
237 if (closest_sq_dist <= sq_dist) {
238 func(closest_sq_dist, node.index, closest_point);
239 }
240 return;
241 }
242
243 const Scalar dl =
244 internal::point_box_signed_squared_distance<Scalar, DIM>(p, m_nodes[node.left].bbox);
245 const Scalar dr =
246 internal::point_box_signed_squared_distance<Scalar, DIM>(p, m_nodes[node.right].bbox);
247
248 if (dl <= sq_dist) {
249 foreach_element_in_radius_recursive(p, sq_dist, node.left, func);
250 }
251 if (dr <= sq_dist) {
252 foreach_element_in_radius_recursive(p, sq_dist, node.right, func);
253 }
254}
255
256// -----------------------------------------------------------------------------
257
258template <typename VertexArray, typename EdgeArray, int DIM>
260 const RowVectorType& p,
261 std::function<void(Scalar, Index, const RowVectorType&)> func) const
262{
263 foreach_element_containing_recursive(p, (Index)m_root, func);
264}
265
266template <typename VertexArray, typename EdgeArray, int DIM>
268 const RowVectorType& p,
269 Index node_id,
270 ActionCallback func) const
271{
272 const auto& node = m_nodes[node_id];
273 if (node.is_leaf()) {
274 RowVectorType p0 = m_vertices->row((*m_edges)(node.index, 0));
275 RowVectorType p1 = m_vertices->row((*m_edges)(node.index, 1));
276 if (point_on_segment(p, p0, p1)) {
277 func(0, node.index, p);
278 }
279 return;
280 }
281
282 const Scalar dl =
283 internal::point_box_signed_squared_distance<Scalar, DIM>(p, m_nodes[node.left].bbox);
284 const Scalar dr =
285 internal::point_box_signed_squared_distance<Scalar, DIM>(p, m_nodes[node.right].bbox);
286
287 if (dl <= 0) {
288 foreach_element_containing_recursive(p, node.left, func);
289 }
290 if (dr <= 0) {
291 foreach_element_containing_recursive(p, node.right, func);
292 }
293}
294
295// -----------------------------------------------------------------------------
296
297template <typename VertexArray, typename EdgeArray, int DIM>
299 const RowVectorType& query_pt,
300 Index& element_id,
301 RowVectorType& closest_point,
302 Scalar& closest_sq_dist,
303 std::function<bool(Index)> filter_func) const
304{
305 la_runtime_assert(!empty());
306 element_id = invalid<Index>();
307 closest_sq_dist = std::numeric_limits<Scalar>::max();
308 closest_point.setConstant(invalid<Scalar>());
309
310 std::function<void(Index)> traverse_aabb_tree;
311 traverse_aabb_tree = [&](Index node_id) {
312 const auto& node = m_nodes[node_id];
313 if (node.is_leaf()) {
314 if (!filter_func || filter_func(node.index)) {
315 RowVectorType _closest_point;
316 Scalar _closest_sq_dist;
317 get_element_closest_point(query_pt, node.index, _closest_point, _closest_sq_dist);
318 if (_closest_sq_dist <= closest_sq_dist) {
319 closest_sq_dist = _closest_sq_dist;
320 element_id = node.index;
321 closest_point = _closest_point;
322 }
323 }
324 } else {
325 using namespace internal;
326 const Scalar dl =
327 point_box_signed_squared_distance<Scalar, DIM>(query_pt, m_nodes[node.left].bbox);
328 const Scalar dr =
329 point_box_signed_squared_distance<Scalar, DIM>(query_pt, m_nodes[node.right].bbox);
330
331 // Explore the nearest subtree first.
332 if (dl < dr) {
333 if (dl <= closest_sq_dist) {
334 traverse_aabb_tree(node.left);
335 }
336 if (dr <= closest_sq_dist) {
337 traverse_aabb_tree(node.right);
338 }
339 } else {
340 if (dr <= closest_sq_dist) {
341 traverse_aabb_tree(node.right);
342 }
343 if (dl <= closest_sq_dist) {
344 traverse_aabb_tree(node.left);
345 }
346 }
347 }
348 };
349
350 traverse_aabb_tree((Index)m_root);
351}
352
353} // namespace bvh
354} // namespace lagrange
#define la_runtime_assert(...)
Runtime assertion check.
Definition: assert.h:169
Main namespace for Lagrange.
Definition: AABBIGL.h:30
auto point_segment_squared_distance(const Eigen::MatrixBase< PointType > &point, const Eigen::MatrixBase< PointType > &V0, const Eigen::MatrixBase< PointType > &V1, Eigen::PlainObjectBase< PointType > &closest_point, ScalarOf< PointType > &lambda0, ScalarOf< PointType > &lambda1) -> ScalarOf< PointType >
Computes the point closest to a given point in a nd segment.
Definition: point_segment_squared_distance.h:73
bool point_on_segment(const Eigen::MatrixBase< PointType > &p, const Eigen::MatrixBase< PointType > &a, const Eigen::MatrixBase< PointType > &b)
Test if a point lies exactly on a segment [a,b] using exact predicates.
Definition: point_on_segment.h:43
Definition: EdgeAABBTree.h:32
Index parent
Index of the parent node (INVALID for root).
Definition: EdgeAABBTree.h:34
Index index
Edge id for the leaf (INVALID for internal nodes).
Definition: EdgeAABBTree.h:37
Index right
Index of the right child (INVALID for a leaf).
Definition: EdgeAABBTree.h:36
Index left
Index of the left child (INVALID for a leaf).
Definition: EdgeAABBTree.h:35
Definition: EdgeAABBTree.h:25
void get_element_closest_point(const RowVectorType &p, Index element_id, RowVectorType &closest_point, Scalar &closest_sq_dist) const
Gets the closest point to a given element.
Definition: EdgeAABBTree.impl.h:198
void foreach_element_containing(const RowVectorType &p, ActionCallback func) const
Iterate over edges that contain exactly a given query point.
Definition: EdgeAABBTree.impl.h:259
void foreach_element_in_radius(const RowVectorType &p, Scalar sq_dist, ActionCallback func) const
Iterate over edges within a prescribed distance from a query point.
Definition: EdgeAABBTree.impl.h:217
void get_closest_point(const RowVectorType &p, Index &element_id, RowVectorType &closest_point, Scalar &closest_sq_dist, std::function< bool(Index)> filter_func=nullptr) const
Gets the closest point to an element of the tree.
Definition: EdgeAABBTree.impl.h:298