45 if(triangulation !=
nullptr) {
54 if(triangulation !=
nullptr) {
64 template <
typename triangulationType0,
typename triangulationType1>
65 int execute(
float *
const outputCoords,
66 const float *
const inputCoords,
67 const char *
const mask,
70 const triangulationType0 &triangulationToSmooth,
71 const triangulationType1 &triangulationSurface)
const;
73 struct Point :
public std::array<float, 3> {
76 res[0] = (*this)[0] + other[0];
77 res[1] = (*this)[1] + other[1];
78 res[2] = (*this)[2] + other[2];
83 res[0] = (*this)[0] * scalar;
84 res[1] = (*this)[1] * scalar;
85 res[2] = (*this)[2] * scalar;
89 return *
this + other * (-1);
92 return (*
this * (1.0F / scalar));
95 return os <<
'(' << pt[0] <<
" " << pt[1] <<
" " << pt[2] <<
')';
100 template <
typename triangulationType0,
typename triangulationType1>
102 std::vector<Point> &tmpStorage,
103 std::vector<SimplexId> &nearestVertexId,
104 std::vector<bool> &trianglesTested,
105 std::vector<SimplexId> &visitedTriangles,
106 std::vector<float> &dists,
107 const char *
const mask,
108 const triangulationType0 &triangulationToSmooth,
109 const triangulationType1 &triangulationSurface)
const;
139 template <
typename triangulationType0,
typename triangulationType1>
143 std::vector<float> &dists,
144 std::stack<SimplexId> &trianglesToTest,
145 const bool reverseProjection,
146 const triangulationType0 &triangulationToSmooth,
147 const triangulationType1 &triangulationSurface)
const;
157 template <
typename triangulationType>
160 std::vector<ttk::SurfaceGeometrySmoother::Point> &outputPoints,
161 const triangulationType &triangulationToSmooth)
const {
162 Point relaxed{outputPoints[a]};
163 const auto nneigh{triangulationToSmooth.getVertexNeighborNumber(a)};
166 triangulationToSmooth.getVertexNeighbor(a, i, neigh);
167 relaxed = relaxed + outputPoints[neigh];
169 return relaxed * (1.0F /
static_cast<float>(nneigh + 1));
182 const Point &normTri)
const {
183 const auto ap{p - a};
197 const auto ab{b - a};
198 const auto ap{p - a};
212 template <
typename triangulationType>
215 std::vector<float> &dists,
216 const triangulationType &triangulation)
const {
217 for(
SimplexId i = 0; i < triangulation.getNumberOfVertices(); ++i) {
219 triangulation.getVertexPoint(i, pv[0], pv[1], pv[2]);
222 return std::min_element(dists.begin(), dists.end()) - dists.begin();
239 const Point ab = b - a;
240 const Point ac = c - a;
246 if(mag > powf(10, -FLT_DIG)) {
264 template <
typename triangulationType>
267 const triangulationType &triangulation)
const;
275 const SimplexId a,
const triangulationType &triangulation)
const {
280 std::vector<std::array<SimplexId, 2>> edges{};
282 const auto nStar{triangulation.getVertexStarNumber(a)};
284 if(triangulation.getCellVertexNumber(0) == 3) {
288 triangulation.getVertexStar(a, i, c);
290 triangulation.getCellVertex(c, 0, v0);
292 triangulation.getCellVertex(c, 1, v0);
294 triangulation.getCellVertex(c, 1, v1);
296 triangulation.getCellVertex(c, 2, v1);
299 edges.emplace_back(std::array<SimplexId, 2>{v0, v1});
301 }
else if(triangulation.getCellVertexNumber(0) == 4) {
305 triangulation.getVertexStar(a, i, c);
306 std::array<SimplexId, 4> q{};
307 triangulation.getCellVertex(c, 0, q[0]);
308 triangulation.getCellVertex(c, 1, q[1]);
309 triangulation.getCellVertex(c, 2, q[2]);
310 triangulation.getCellVertex(c, 3, q[3]);
312 edges.emplace_back(std::array<SimplexId, 2>{
316 }
else if(q[1] == a) {
317 edges.emplace_back(std::array<SimplexId, 2>{
321 }
else if(q[2] == a) {
322 edges.emplace_back(std::array<SimplexId, 2>{
326 }
else if(q[3] == a) {
327 edges.emplace_back(std::array<SimplexId, 2>{
337 triangulation.getVertexPoint(a, pa[0], pa[1], pa[2]);
340 std::vector<Point> normals{};
341 normals.reserve(nStar);
343 for(
auto &e : edges) {
345 triangulation.getVertexPoint(e[0], pb[0], pb[1], pb[2]);
346 triangulation.getVertexPoint(e[1], pc[0], pc[1], pc[2]);
350 if(normal[0] != -1.0F && normal[1] != -1.0F && normal[2] != -1.0F) {
352 normals.emplace_back(normal);
356 if(!normals.empty()) {
359 for(
size_t i = 1; i < normals.size(); ++i) {
363 normals[i] = normals[i] * -1.0F;
368 res = std::accumulate(
369 normals.begin(), normals.end(),
Point{}, std::plus<Point>());
370 res = res /
static_cast<float>(normals.size());
386 std::vector<float> &dists,
387 std::stack<SimplexId> &trianglesToTest,
388 const bool reverseProjection,
389 const triangulationType0 &triangulationToSmooth,
390 const triangulationType1 &triangulation)
const {
394 Point surfaceNormal{};
395 if(reverseProjection) {
397 = this->computeSurfaceNormalAtPoint(pi.
id, triangulationToSmooth);
398 res.reverseProjection = !std::isnan(surfaceNormal[0]);
402 while(!trianglesToTest.empty()) {
403 trianglesToTest.pop();
407 if(triangulation.getVertexTriangleNumber(res.nearestVertex) > 0) {
409 triangulation.getVertexTriangle(res.nearestVertex, 0, next);
410 trianglesToTest.push(next);
413 while(!trianglesToTest.empty()) {
414 const auto curr = trianglesToTest.top();
415 trianglesToTest.pop();
423 std::array<SimplexId, 3> tverts{};
424 triangulation.getTriangleVertex(curr, 0, tverts[0]);
425 triangulation.getTriangleVertex(curr, 1, tverts[1]);
426 triangulation.getTriangleVertex(curr, 2, tverts[2]);
429 std::array<Point, 3> mno{};
430 triangulation.getVertexPoint(tverts[0], mno[0][0], mno[0][1], mno[0][2]);
431 triangulation.getVertexPoint(tverts[1], mno[1][0], mno[1][1], mno[1][2]);
432 triangulation.getVertexPoint(tverts[2], mno[2][0], mno[2][1], mno[2][2]);
434 const auto normTri{this->computeTriangleNormal(mno[0], mno[1], mno[2])};
436 static const float PREC_FLT{powf(10, -FLT_DIG)};
438 if(res.reverseProjection) {
444 if(std::abs(denom) < PREC_FLT) {
446 for(
auto &vert : tverts) {
447 auto ntr = triangulation.getVertexTriangleNumber(vert);
450 triangulation.getVertexTriangle(vert, j, tid);
452 trianglesToTest.push(tid);
461 auto tmp = mno[0] - pi.
pt;
463 res.pt = pi.
pt + surfaceNormal * alpha;
466 res.pt = this->projectOnTrianglePlane(pi.
pt, mno[0], normTri);
472 mno[0].data(), mno[1].data(), mno[2].data(), res.pt.data(), baryCoords);
475 bool inTriangle =
true;
477 for(
auto &coord : baryCoords) {
478 if(coord < -PREC_FLT) {
481 if(coord > 1 + PREC_FLT) {
487 trianglesTested.
insert(curr);
488 res.trianglesChecked++;
491 res.projSuccess =
true;
498 = std::minmax_element(baryCoords.begin(), baryCoords.end());
502 std::array<SimplexId, 2> vid{
503 static_cast<SimplexId>(extrema.second - baryCoords.begin()), 0};
504 for(
size_t j = 0; j < baryCoords.size(); j++) {
505 if(j !=
static_cast<size_t>(extrema.first - baryCoords.begin())
506 && j !=
static_cast<size_t>(extrema.second - baryCoords.begin())) {
513 res.nearestVertex = tverts[vid[0]];
514 const auto secondNearVert{tverts[vid[1]]};
518 const auto nEdges{triangulation.getVertexEdgeNumber(res.nearestVertex)};
520 triangulation.getVertexEdge(res.nearestVertex, i, edge);
522 triangulation.getEdgeVertex(edge, 0, v);
523 if(v == res.nearestVertex) {
524 triangulation.getEdgeVertex(edge, 1, v);
526 if(v == secondNearVert) {
533 const auto nTri{triangulation.getEdgeTriangleNumber(edge)};
535 triangulation.getEdgeTriangle(edge, i, next);
541 const auto nVisited{trianglesTested.
visitedIds_.size()};
542 if(nVisited > 1 && next == trianglesTested.
visitedIds_[nVisited - 2]) {
545 res.pt = this->projectOnEdge(pi.
pt, mno[vid[0]], mno[vid[1]]);
548 res.pt.data(), mno[vid[0]].data(), mno[vid[1]].data());
549 if(!res.projSuccess) {
551 triangulation.getVertexPoint(
552 tverts[vid[0]], res.pt[0], res.pt[1], res.pt[2]);
553 res.projSuccess =
true;
559 trianglesToTest.push(next);
563 const size_t maxTrChecked = 100;
565 if(res.projSuccess && res.trianglesChecked > maxTrChecked) {
566 res.projSuccess =
false;
569 Point nearestCoords{};
570 triangulation.getVertexPoint(
571 pi.
nearestVertex, nearestCoords[0], nearestCoords[1], nearestCoords[2]);
574 res.projSuccess =
false;
575 if(triangulationToSmooth.getDimensionality() == 2 && !reverseProjection) {
581 return this->findProjection(pi, trianglesTested, dists, trianglesToTest,
582 true, triangulationToSmooth, triangulation);
586 if(!res.projSuccess) {
589 = this->getNearestSurfaceVertex(pi.
pt, dists, triangulation);
590 triangulation.getVertexPoint(
591 res.nearestVertex, res.pt[0], res.pt[1], res.pt[2]);
599 std::vector<ttk::SurfaceGeometrySmoother::Point> &outputPoints,
600 std::vector<ttk::SurfaceGeometrySmoother::Point> &tmpStorage,
601 std::vector<SimplexId> &nearestVertexId,
602 std::vector<bool> &trianglesTested,
603 std::vector<SimplexId> &visitedTriangles,
604 std::vector<float> &dists,
605 const char *
const mask,
606 const triangulationType0 &triangulationToSmooth,
607 const triangulationType1 &triangulationSurface)
const {
610 std::stack<SimplexId> trianglesToTest{};
613#ifdef TTK_ENABLE_OPENMP4
614#pragma omp parallel for num_threads(threadNumber_) \
615 firstprivate(trianglesTested, visitedTriangles, dists, trianglesToTest)
617 for(
size_t i = 0; i < outputPoints.size(); i++) {
620 if(mask !=
nullptr && mask[i] == 0) {
621 tmpStorage[i] = outputPoints[i];
624 tmpStorage[i] = this->relax(i, outputPoints, triangulationToSmooth);
629 const auto res = this->findProjection(
631 trianglesToTest,
false, triangulationToSmooth, triangulationSurface);
633 tmpStorage[i] = res.pt;
634 nearestVertexId[i] = res.nearestVertex;
637 std::swap(outputPoints, tmpStorage);
648 float *
const outputCoords,
649 const float *
const inputCoords,
650 const char *
const mask,
653 const triangulationType0 &triangulationToSmooth,
654 const triangulationType1 &triangulationSurface)
const {
656 const auto nPoints{triangulationToSmooth.getNumberOfVertices()};
657 if(triangulationSurface.getDimensionality() != 2) {
658 this->printErr(
"Can only project onto a surface");
662 if(triangulationToSmooth.getDimensionality() < 1
663 || triangulationToSmooth.getDimensionality() > 2) {
664 this->printErr(
"Can only project a 1D or a 2D triangulated object");
674 std::vector<bool> trianglesTested(
675 triangulationSurface.getNumberOfTriangles(),
false);
676 std::vector<SimplexId> visitedTriangles{};
678 std::vector<float> dists(triangulationSurface.getNumberOfVertices());
681 std::vector<ttk::SurfaceGeometrySmoother::Point> outputPoints(nPoints),
683 std::vector<SimplexId> nearestVertexId(nPoints);
686#ifdef TTK_ENABLE_OPENMP
687#pragma omp parallel for num_threads(threadNumber_)
690 outputPoints[i][0] = inputCoords[3 * i + 0];
691 outputPoints[i][1] = inputCoords[3 * i + 1];
692 outputPoints[i][2] = inputCoords[3 * i + 2];
697 if(vertsId !=
nullptr) {
698#ifdef TTK_ENABLE_OPENMP
699#pragma omp parallel for num_threads(threadNumber_)
702 nearestVertexId[i] = vertsId[i];
711#ifdef TTK_ENABLE_OPENMP
712#pragma omp parallel for num_threads(threadNumber_) firstprivate(dists)
715 nearestVertexId[i] = this->getNearestSurfaceVertex(
716 outputPoints[i], dists, triangulationSurface);
718 this->
printMsg(
"Computed nearest vertices", 1.0, tm_nv.getElapsedTime(),
719 this->threadNumber_);
722 for(
int i = 0; i < nIter; ++i) {
723 this->relaxProject(outputPoints, tmpStorage, nearestVertexId,
724 trianglesTested, visitedTriangles, dists, mask,
725 triangulationToSmooth, triangulationSurface);
729#ifdef TTK_ENABLE_OPENMP
730#pragma omp parallel for num_threads(threadNumber_)
733 outputCoords[3 * i + 0] = outputPoints[i][0];
734 outputCoords[3 * i + 1] = outputPoints[i][1];
735 outputCoords[3 * i + 2] = outputPoints[i][2];
739 tm.getElapsedTime(), this->threadNumber_);