70 std::array<int, 3> resolution,
71 std::array<float, 3> origin,
72 std::array<float, 3> spacing) {
73 const I3 res{resolution[0], resolution[1], resolution[2]};
74 const float ox = origin[0], oy = origin[1], oz = origin[2];
75 const float sx = spacing[0], sy = spacing[1], sz = spacing[2];
76 const std::size_t n = sdf.extent(0);
77 const int max_pores = 1000000;
79 Kokkos::View<Pore*, Mem> pores(
"pores", max_pores);
80 Kokkos::View<int, Mem> counter(
"counter");
81 Kokkos::deep_copy(counter, 0);
84 using MD = Kokkos::MDRangePolicy<Exec, Kokkos::Rank<3>>;
86 "pnm::extract_pores", MD(space, {0, 0, 0}, {res.x, res.y, res.z}),
87 KOKKOS_LAMBDA(
int ix,
int iy,
int iz) {
88 const int ci =
get_idx(ix, iy, iz, res);
89 const float cv = sdf(ci);
93 for (
int dz = -1; dz <= 1 && peak; ++dz)
94 for (
int dy = -1; dy <= 1 && peak; ++dy)
95 for (
int dx = -1; dx <= 1; ++dx) {
96 if (dx == 0 && dy == 0 && dz == 0)
98 const int ni =
get_idx(ix + dx, iy + dy, iz + dz, res);
99 const float nv = sdf(ni);
100 if (nv > cv || (nv == cv && ni > ci)) {
107 float sw = 0.0f, px = 0.0f, py = 0.0f, pz = 0.0f;
108 for (
int dz = -1; dz <= 1; ++dz)
109 for (
int dy = -1; dy <= 1; ++dy)
110 for (
int dx = -1; dx <= 1; ++dx) {
111 const float v = sdf(
get_idx(ix + dx, iy + dy, iz + dz, res));
112 float w = v > 0.0f ? v : 0.0f;
119 float fx = 0, fy = 0, fz = 0;
125 const int slot = Kokkos::atomic_fetch_add(&counter(), 1);
126 if (slot < max_pores)
127 pores(slot) =
Pore{ox + (ix + fx) * sx, oy + (iy + fy) * sy, oz + (iz + fz) * sz, cv};
133 auto hc = Kokkos::create_mirror_view(counter);
134 Kokkos::deep_copy(hc, counter);
137 if (h_count > max_pores)
139 return downloadN(pores,
static_cast<std::size_t
>(h_count));
156 std::array<int, 3> resolution,
157 std::array<float, 3> spacing) {
158 const I3 res{resolution[0], resolution[1], resolution[2]};
159 const std::size_t n = sdf.extent(0);
160 const float min_sp = std::min(spacing[0], std::min(spacing[1], spacing[2]));
161 const float thr = -1.5f * min_sp;
163 Kokkos::View<int*, Mem> labels(
"labels", n), roots(
"roots", n);
164 Kokkos::View<int, Mem> changed(
"changed");
167 using MD = Kokkos::MDRangePolicy<Exec, Kokkos::Rank<3>>;
168 const auto full = MD(space, {0, 0, 0}, {res.x, res.y, res.z});
171 Kokkos::parallel_for(
172 "pnm::init_markers", full, KOKKOS_LAMBDA(
int ix,
int iy,
int iz) {
173 const int i =
get_idx(ix, iy, iz, res);
174 labels(i) = (sdf(i) < thr) ? i : -1;
180 auto flatten = [&]() {
181 Kokkos::parallel_for(
182 "pnm::flatten", Kokkos::RangePolicy<Exec>(space, 0, n), KOKKOS_LAMBDA(std::size_t idx) {
185 while (l != labels(l))
194 Kokkos::deep_copy(changed, 0);
195 Kokkos::parallel_for(
196 "pnm::merge_markers", full, KOKKOS_LAMBDA(
int ix,
int iy,
int iz) {
197 const int idx =
get_idx(ix, iy, iz, res);
198 const int my = labels(idx);
201 const int dz_l[13] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0};
202 const int dy_l[13] = {-1, -1, -1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0};
203 const int dx_l[13] = {-1, 0, 1, -1, 0, 1, -1, 0, 1, -1, 0, 1, 1};
204 for (
int k = 0; k < 13; ++k) {
205 const int ni =
get_idx(ix + dx_l[k], iy + dy_l[k], iz + dz_l[k], res);
206 const int nl = labels(ni);
207 if (nl != -1 && my != nl) {
209 while (rm != labels(rm))
212 while (rn != labels(rn))
215 const int small = rm < rn ? rm : rn, large = rm < rn ? rn : rm;
216 Kokkos::atomic_min(&labels(large), small);
224 auto hc = Kokkos::create_mirror_view(changed);
225 Kokkos::deep_copy(hc, changed);
233 Kokkos::deep_copy(changed, 0);
234 Kokkos::parallel_for(
235 "pnm::flood", full, KOKKOS_LAMBDA(
int ix,
int iy,
int iz) {
236 const int idx =
get_idx(ix, iy, iz, res);
237 if (sdf(idx) >= 0.0f)
239 if (labels(idx) != -1)
242 for (
int dz = -1; dz <= 1; ++dz)
243 for (
int dy = -1; dy <= 1; ++dy)
244 for (
int dx = -1; dx <= 1; ++dx) {
245 if (dx == 0 && dy == 0 && dz == 0)
247 const int nl = labels(
get_idx(ix + dx, iy + dy, iz + dz, res));
248 if (nl != -1 && (best == -1 || nl < best))
257 auto hc = Kokkos::create_mirror_view(changed);
258 Kokkos::deep_copy(hc, changed);
264 Kokkos::parallel_for(
265 "pnm::gradient_path", full, KOKKOS_LAMBDA(
int ix,
int iy,
int iz) {
266 const int ci =
get_idx(ix, iy, iz, res);
267 const bool ascent = (sdf(ci) > 0.0f);
269 const int MAX_STEPS = 512;
270 for (
int s = 0; s < MAX_STEPS; ++s) {
272 float bv = sdf(walker);
273 const int wx = walker % res.x, wy = (walker / res.x) % res.y,
274 wz = walker / (res.x * res.y);
275 for (
int dz = -1; dz <= 1; ++dz)
276 for (
int dy = -1; dy <= 1; ++dy)
277 for (
int dx = -1; dx <= 1; ++dx) {
278 if (dx == 0 && dy == 0 && dz == 0)
280 const int ni =
get_idx(wx + dx, wy + dy, wz + dz, res);
281 const float nv = sdf(ni);
286 }
else if (nv == bv && ni > best)
292 }
else if (nv == bv && ni > best)
310 Kokkos::View<int*, Mem> seg(
"seg", n);
311 Kokkos::View<int*, Mem> minPoreIdx(
"minPoreIdx", n), minSolidIdx(
"minSolidIdx", n);
312 Kokkos::View<int*, Mem> poreFirst(
"poreFirst", n), solidFirst(
"solidFirst", n);
313 Kokkos::View<int*, Mem> poreRank(
"poreRank", n), solidRank(
"solidRank", n);
314 Kokkos::View<int*, Mem> poreId(
"poreId", n), solidId(
"solidId", n);
315 constexpr int kBig = 0x7fffffff;
316 Kokkos::deep_copy(minPoreIdx, kBig);
317 Kokkos::deep_copy(minSolidIdx, kBig);
318 const std::size_t nn = n;
319 using R1 = Kokkos::RangePolicy<Exec>;
322 Kokkos::parallel_for(
323 "pnm::relabel_min", R1(space, 0, nn), KOKKOS_LAMBDA(std::size_t i) {
325 Kokkos::atomic_min(&minPoreIdx(roots(i)),
static_cast<int>(i));
326 else if (labels(i) != -1)
327 Kokkos::atomic_min(&minSolidIdx(labels(i)),
static_cast<int>(i));
331 Kokkos::parallel_for(
332 "pnm::relabel_first", R1(space, 0, nn), KOKKOS_LAMBDA(std::size_t i) {
333 poreFirst(i) = (sdf(i) > 0.0f && minPoreIdx(roots(i)) ==
static_cast<int>(i)) ? 1 : 0;
335 (sdf(i) <= 0.0f && labels(i) != -1 && minSolidIdx(labels(i)) ==
static_cast<int>(i))
341 Kokkos::parallel_scan(
342 "pnm::relabel_porescan", R1(space, 0, nn),
343 KOKKOS_LAMBDA(std::size_t i,
int& upd,
const bool fin) {
344 const int v = poreFirst(i);
349 Kokkos::parallel_scan(
350 "pnm::relabel_solidscan", R1(space, 0, nn),
351 KOKKOS_LAMBDA(std::size_t i,
int& upd,
const bool fin) {
352 const int v = solidFirst(i);
359 Kokkos::parallel_for(
360 "pnm::relabel_assign", R1(space, 0, nn), KOKKOS_LAMBDA(std::size_t i) {
362 poreId(roots(i)) = poreRank(i) + 1;
364 solidId(labels(i)) = -(solidRank(i) + 1);
369 Kokkos::parallel_for(
370 "pnm::relabel_seg", R1(space, 0, nn), KOKKOS_LAMBDA(std::size_t i) {
371 seg(i) = (sdf(i) > 0.0f) ? poreId(roots(i)) : (labels(i) == -1 ? 0 : solidId(labels(i)));
391 std::array<int, 3> resolution) {
392 const I3 res{resolution[0], resolution[1], resolution[2]};
393 const std::size_t n = seg.extent(0);
394 const int max_pairs = (int)(n * 3);
396 Kokkos::View<int*, Mem> pairs(
"pairs",
397 (std::size_t)max_pairs * 2);
398 Kokkos::View<int, Mem> cnt(
"cnt");
399 Kokkos::deep_copy(cnt, 0);
402 using MD = Kokkos::MDRangePolicy<Exec, Kokkos::Rank<3>>;
403 Kokkos::parallel_for(
404 "pnm::boundary_pairs", MD(space, {0, 0, 0}, {res.x, res.y, res.z}),
405 KOKKOS_LAMBDA(
int ix,
int iy,
int iz) {
406 const int idx =
get_idx(ix, iy, iz, res);
407 const int my = seg(idx);
408 const int dx_l[3] = {1, 0, 0}, dy_l[3] = {0, 1, 0}, dz_l[3] = {0, 0, 1};
409 for (
int k = 0; k < 3; ++k) {
410 const int nl = seg(
get_idx(ix + dx_l[k], iy + dy_l[k], iz + dz_l[k], res));
412 const int l1 = my < nl ? my : nl, l2 = my < nl ? nl : my;
413 const int slot = Kokkos::atomic_fetch_add(&cnt(), 1);
414 if (slot < max_pairs) {
415 pairs(2 * slot) = l1;
416 pairs(2 * slot + 1) = l2;
425 auto hc = Kokkos::create_mirror_view(cnt);
426 Kokkos::deep_copy(hc, cnt);
429 if (h_count > max_pairs)
431 std::vector<int> flat =
432 downloadN(pairs,
static_cast<std::size_t
>(2 * h_count));
433 std::vector<std::pair<int, int>> result;
434 result.reserve(h_count);
435 for (
int i = 0; i < h_count; ++i)
436 result.push_back({flat[2 * i], flat[2 * i + 1]});
437 std::sort(result.begin(), result.end());
438 result.erase(std::unique(result.begin(), result.end()), result.end());