Skip to content

Commit 52517ab

Browse files
author
Teseo Schneider
committed
distance query and 2d/3d support
1 parent e1a9313 commit 52517ab

File tree

3 files changed

+360
-75
lines changed

3 files changed

+360
-75
lines changed

src/SimpleBVH/BVH.cpp

Lines changed: 221 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,94 @@
44
#include <iostream>
55

66
namespace SimpleBVH {
7+
8+
void point_segment_squared_distance(
9+
const VectorMax3d& point,
10+
const std::array<VectorMax3d, 2>& f,
11+
VectorMax3d& closest_point,
12+
double& dist)
13+
{
14+
const double l2 = (f[0] - f[1]).norm();
15+
const double t = (point - f[0]).dot(f[1] - f[0]);
16+
if (t <= 0.0 || l2 == 0.0) {
17+
closest_point = f[0];
18+
} else if (t > l2) {
19+
closest_point = f[1];
20+
} else {
21+
const double lambda1 = t / l2;
22+
const double lambda0 = 1.0 - lambda1;
23+
closest_point = lambda0 * f[0] + lambda1 * f[1];
24+
}
25+
dist = (point - closest_point).squaredNorm();
26+
}
27+
728
namespace {
829
bool box_box_intersection(
9-
const Eigen::Vector3d& min1,
10-
const Eigen::Vector3d& max1,
11-
const Eigen::Vector3d& min2,
12-
const Eigen::Vector3d& max2)
30+
const VectorMax3d& min1,
31+
const VectorMax3d& max1,
32+
const VectorMax3d& min2,
33+
const VectorMax3d& max2)
34+
{
35+
if (min1.size() == 3) {
36+
if (max1[0] < min2[0] || max1[1] < min2[1] || max1[2] < min2[2])
37+
return 0;
38+
if (max2[0] < min1[0] || max2[1] < min1[1] || max2[2] < min1[2])
39+
return 0;
40+
return 1;
41+
} else {
42+
if (max1[0] < min2[0] || max1[1] < min2[1])
43+
return 0;
44+
if (max2[0] < min1[0] || max2[1] < min1[1])
45+
return 0;
46+
return 1;
47+
}
48+
}
49+
50+
double point_box_center_squared_distance(
51+
const VectorMax3d& p, const std::array<VectorMax3d, 2>& B)
52+
{
53+
return (p - (B[0] + B[1]) / 2).norm();
54+
}
55+
56+
double inner_point_box_squared_distance(
57+
const VectorMax3d& p, const std::array<VectorMax3d, 2>& B)
58+
{
59+
assert(p.size() == B[0].size());
60+
61+
double result = std::pow(p[0] - B[0][0], 2);
62+
result = std::min(result, std::pow(p[0] - B[1][0], 2));
63+
for (int c = 1; c < p.size(); ++c) {
64+
result = std::min(result, std::pow(p[c] - B[0][c], 2));
65+
result = std::min(result, std::pow(p[c] - B[1][c], 2));
66+
}
67+
return result;
68+
}
69+
70+
double point_box_signed_squared_distance(
71+
const VectorMax3d& p, const std::array<VectorMax3d, 2>& B)
1372
{
14-
if (max1[0] < min2[0] || max1[1] < min2[1] || max1[2] < min2[2])
15-
return 0;
16-
if (max2[0] < min1[0] || max2[1] < min1[1] || max2[2] < min1[2])
17-
return 0;
18-
return 1;
73+
assert(p.size() == B[0].size());
74+
75+
bool inside = true;
76+
double result = 0.0;
77+
for (int c = 0; c < p.size(); c++) {
78+
if (p[c] < B[0][c]) {
79+
inside = false;
80+
result += std::pow(p[c] - B[0][c], 2);
81+
} else if (p[c] > B[1][c]) {
82+
inside = false;
83+
result += std::pow(p[c] - B[1][c], 2);
84+
}
85+
}
86+
if (inside) {
87+
result = -inner_point_box_squared_distance(p, B);
88+
}
89+
return result;
1990
}
2091
} // namespace
2192

2293
void BVH::init_boxes_recursive(
23-
const std::vector<std::array<Eigen::Vector3d, 2>>& cornerlist,
94+
const std::vector<std::array<VectorMax3d, 2>>& cornerlist,
2495
int node_index,
2596
int b,
2697
int e)
@@ -44,7 +115,7 @@ void BVH::init_boxes_recursive(
44115

45116
assert(childl < boxlist.size());
46117
assert(childr < boxlist.size());
47-
for (int c = 0; c < 3; ++c) {
118+
for (int c = 0; c < cornerlist[0].size(); ++c) {
48119
boxlist[node_index][0][c] =
49120
std::min(boxlist[childl][0][c], boxlist[childr][0][c]);
50121
boxlist[node_index][1][c] =
@@ -53,8 +124,8 @@ void BVH::init_boxes_recursive(
53124
}
54125

55126
void BVH::box_search_recursive(
56-
const Eigen::Vector3d& bbd0,
57-
const Eigen::Vector3d& bbd1,
127+
const VectorMax3d& bbd0,
128+
const VectorMax3d& bbd1,
58129
std::vector<unsigned int>& list,
59130
int n,
60131
int b,
@@ -99,25 +170,25 @@ int BVH::max_node_index(int node_index, int b, int e)
99170
return std::max(max_node_index(childl, b, m), max_node_index(childr, m, e));
100171
}
101172

102-
void BVH::init(const std::vector<std::array<Eigen::Vector3d, 2>>& cornerlist)
173+
void BVH::init(const std::vector<std::array<VectorMax3d, 2>>& cornerlist)
103174
{
104175
n_corners = cornerlist.size();
105176

106-
Eigen::MatrixXd box_centers(n_corners, 3);
177+
Eigen::MatrixXd box_centers(n_corners, cornerlist[0][0].size());
107178
for (int i = 0; i < n_corners; ++i) {
108179
box_centers.row(i) = (cornerlist[i][0] + cornerlist[i][1]) / 2;
109180
}
110181

111-
const Eigen::RowVector3d vmin = box_centers.colwise().minCoeff();
112-
const Eigen::RowVector3d vmax = box_centers.colwise().maxCoeff();
113-
const Eigen::RowVector3d center = (vmin + vmax) / 2;
182+
const VectorMax3d vmin = box_centers.colwise().minCoeff();
183+
const VectorMax3d vmax = box_centers.colwise().maxCoeff();
184+
const VectorMax3d center = (vmin + vmax) / 2;
114185
for (int i = 0; i < n_corners; i++) {
115186
// make box centered at origin
116187
box_centers.row(i) -= center;
117188
}
118189

119190
// after placing box at origin, vmax and vmin are symetric.
120-
const Eigen::Vector3d scale_point = vmax - center;
191+
const VectorMax3d scale_point = vmax - center;
121192
const double scale = scale_point.lpNorm<Eigen::Infinity>();
122193
// if the box is too big, resize it
123194
if (scale > 100) {
@@ -135,8 +206,8 @@ void BVH::init(const std::vector<std::array<Eigen::Vector3d, 2>>& cornerlist)
135206
for (int i = 0; i < n_corners; i++) {
136207
const Eigen::MatrixXd tmp = box_centers.row(i) * multi;
137208

138-
list[i].morton =
139-
Resorting::MortonCode64(int(tmp(0)), int(tmp(1)), int(tmp(2)));
209+
list[i].morton = Resorting::MortonCode64(
210+
int(tmp(0)), int(tmp(1)), tmp.size() == 3 ? int(tmp(2)) : 0);
140211
list[i].order = i;
141212
}
142213

@@ -150,7 +221,7 @@ void BVH::init(const std::vector<std::array<Eigen::Vector3d, 2>>& cornerlist)
150221
new2old[i] = list[i].order;
151222
}
152223

153-
std::vector<std::array<Eigen::Vector3d, 2>> sorted_cornerlist(n_corners);
224+
std::vector<std::array<VectorMax3d, 2>> sorted_cornerlist(n_corners);
154225

155226
for (int i = 0; i < n_corners; i++) {
156227
sorted_cornerlist[i] = cornerlist[list[i].order];
@@ -160,45 +231,151 @@ void BVH::init(const std::vector<std::array<Eigen::Vector3d, 2>>& cornerlist)
160231
max_node_index(1, 0, n_corners)
161232
+ 1 // <-- this is because size == max_index + 1 !!!
162233
);
234+
for (auto& b : boxlist) {
235+
b[0].resize(cornerlist[0][0].size());
236+
b[1].resize(cornerlist[0][0].size());
237+
}
163238

164239
init_boxes_recursive(sorted_cornerlist, 1, 0, n_corners);
165240
}
166241

167242
void BVH::init(
168243
const Eigen::MatrixXd& V, const Eigen::MatrixXi& F, const double tol)
169244
{
170-
assert(F.cols() == 3);
171-
assert(V.cols() == 3);
172-
173-
std::vector<std::array<Eigen::Vector3d, 2>> cornerlist(F.rows());
174-
175-
for (int i = 0; i < F.rows(); i++) {
176-
const Eigen::RowVector3i face = F.row(i);
177-
const Eigen::RowVector3d v0 = V.row(face(0));
178-
const Eigen::RowVector3d v1 = V.row(face(1));
179-
const Eigen::RowVector3d v2 = V.row(face(2));
180-
181-
Eigen::Matrix3d tmp;
182-
tmp.row(0) = v0;
183-
tmp.row(1) = v1;
184-
tmp.row(2) = v2;
185-
186-
const Eigen::RowVector3d min = tmp.colwise().minCoeff().array() - tol;
187-
const Eigen::RowVector3d max = tmp.colwise().maxCoeff().array() + tol;
188-
189-
cornerlist[i][0] = min.transpose();
190-
cornerlist[i][1] = max.transpose();
245+
assert(F.cols() == 3 || F.cols() == 2);
246+
assert(V.cols() == 3 || V.cols() == 2);
247+
248+
std::vector<std::array<VectorMax3d, 2>> cornerlist(F.rows());
249+
if (F.cols() == 3) {
250+
for (int i = 0; i < F.rows(); i++) {
251+
const Eigen::RowVector3i face = F.row(i);
252+
const VectorMax3d v0 = V.row(face(0));
253+
const VectorMax3d v1 = V.row(face(1));
254+
const VectorMax3d v2 = V.row(face(2));
255+
256+
Eigen::MatrixXd tmp(3, v0.size());
257+
tmp.row(0) = v0.transpose();
258+
tmp.row(1) = v1.transpose();
259+
tmp.row(2) = v2.transpose();
260+
261+
const VectorMax3d min = tmp.colwise().minCoeff().array() - tol;
262+
const VectorMax3d max = tmp.colwise().maxCoeff().array() + tol;
263+
264+
cornerlist[i][0] = min.transpose();
265+
cornerlist[i][1] = max.transpose();
266+
}
267+
} else if (F.cols() == 2) {
268+
for (int i = 0; i < F.rows(); i++) {
269+
const Eigen::RowVector2i face = F.row(i);
270+
const VectorMax3d v0 = V.row(face(0));
271+
const VectorMax3d v1 = V.row(face(1));
272+
273+
Eigen::MatrixXd tmp(2, v0.size());
274+
tmp.row(0) = v0.transpose();
275+
tmp.row(1) = v1.transpose();
276+
277+
const VectorMax3d min = tmp.colwise().minCoeff().array() - tol;
278+
const VectorMax3d max = tmp.colwise().maxCoeff().array() + tol;
279+
280+
cornerlist[i][0] = min.transpose();
281+
cornerlist[i][1] = max.transpose();
282+
}
191283
}
192284

193285
init(cornerlist);
194286
}
195287

196288
bool BVH::box_intersects_box(
197-
const Eigen::Vector3d& bbd0, const Eigen::Vector3d& bbd1, int index) const
289+
const VectorMax3d& bbd0, const VectorMax3d& bbd1, int index) const
198290
{
199291
const auto& bmin = boxlist[index][0];
200292
const auto& bmax = boxlist[index][1];
201293

202294
return box_box_intersection(bbd0, bbd1, bmin, bmax);
203295
}
296+
297+
void BVH::get_nearest_facet_hint(
298+
const VectorMax3d& p,
299+
int& nearest_f,
300+
VectorMax3d& nearest_point,
301+
double& sq_dist) const
302+
{
303+
int b = 0;
304+
int e = n_corners;
305+
int n = 1;
306+
while (e != b + 1) {
307+
int m = b + (e - b) / 2;
308+
int childl = 2 * n;
309+
int childr = 2 * n + 1;
310+
if (point_box_center_squared_distance(p, boxlist[childl])
311+
< point_box_center_squared_distance(p, boxlist[childr])) {
312+
e = m;
313+
n = childl;
314+
} else {
315+
b = m;
316+
n = childr;
317+
}
318+
}
319+
nearest_f = b;
320+
321+
nearest_point = getPoint(nearest_f);
322+
sq_dist = (p - nearest_point).norm();
323+
}
324+
325+
void BVH::nearest_facet_recursive(
326+
const VectorMax3d& p,
327+
int& nearest_f,
328+
VectorMax3d& nearest_point,
329+
double& sq_dist,
330+
int n,
331+
int b,
332+
int e) const
333+
{
334+
assert(e > b);
335+
336+
// If node is a leaf: compute point-facet distance
337+
// and replace current if nearer
338+
if (b + 1 == e) {
339+
VectorMax3d cur_nearest_point;
340+
double cur_sq_dist;
341+
leafCallback(p, new2old[b], cur_nearest_point, cur_sq_dist);
342+
if (cur_sq_dist < sq_dist) {
343+
nearest_f = b;
344+
nearest_point = cur_nearest_point;
345+
sq_dist = cur_sq_dist;
346+
}
347+
return;
348+
}
349+
int m = b + (e - b) / 2;
350+
int childl = 2 * n;
351+
int childr = 2 * n + 1;
352+
353+
assert(childl < boxlist.size());
354+
assert(childr < boxlist.size());
355+
356+
double dl = point_box_signed_squared_distance(p, boxlist[childl]);
357+
double dr = point_box_signed_squared_distance(p, boxlist[childr]);
358+
359+
// Traverse the "nearest" child first, so that it has more chances
360+
// to prune the traversal of the other child.
361+
if (dl < dr) {
362+
if (dl < sq_dist) {
363+
nearest_facet_recursive(
364+
p, nearest_f, nearest_point, sq_dist, childl, b, m);
365+
}
366+
if (dr < sq_dist) {
367+
nearest_facet_recursive(
368+
p, nearest_f, nearest_point, sq_dist, childr, m, e);
369+
}
370+
} else {
371+
if (dr < sq_dist) {
372+
nearest_facet_recursive(
373+
p, nearest_f, nearest_point, sq_dist, childr, m, e);
374+
}
375+
if (dl < sq_dist) {
376+
nearest_facet_recursive(
377+
p, nearest_f, nearest_point, sq_dist, childl, b, m);
378+
}
379+
}
380+
}
204381
} // namespace SimpleBVH

0 commit comments

Comments
 (0)