Loading...
Searching...
No Matches
levelSetSegmentationObject.cpp
1/*---------------------------------------------------------------------------*\
2 *
3 * bitpit
4 *
5 * Copyright (C) 2015-2021 OPTIMAD engineering Srl
6 *
7 * -------------------------------------------------------------------------
8 * License
9 * This file is part of bitpit.
10 *
11 * bitpit is free software: you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License v3 (LGPL)
13 * as published by the Free Software Foundation.
14 *
15 * bitpit is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
18 * License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with bitpit. If not, see <http://www.gnu.org/licenses/>.
22 *
23\*---------------------------------------------------------------------------*/
24
25#include "levelSetSegmentationObject.hpp"
26
27#include "bitpit_CG.hpp"
28#include "levelSetObject.hpp"
29
30namespace bitpit {
31
37
44
49 : m_surface(nullptr),
50 m_featureAngle(0),
51 m_surfaceSmoothing(LevelSetSurfaceSmoothing::LOW_ORDER)
52{
53}
54
59 : m_surface(other.m_surface),
60 m_featureAngle(other.m_featureAngle),
61 m_surfaceSmoothing(other.m_surfaceSmoothing),
62 m_segmentVertexOffset(other.m_segmentVertexOffset),
63 m_segmentNormalsValid(other.m_segmentNormalsValid),
64 m_segmentNormalsStorage(other.m_segmentNormalsStorage),
65 m_unlimitedVertexNormalsValid(other.m_unlimitedVertexNormalsValid),
66 m_unlimitedVertexNormalsStorage(other.m_unlimitedVertexNormalsStorage),
67 m_limitedSegmentVertexNormalValid(other.m_limitedSegmentVertexNormalValid),
68 m_limitedSegmentVertexNormalStorage(other.m_limitedSegmentVertexNormalStorage)
69{
70 if (other.m_ownedSurface) {
71 m_ownedSurface = std::unique_ptr<SurfUnstructured>(new SurfUnstructured(*(other.m_ownedSurface)));
72 } else {
73 m_ownedSurface = nullptr;
74 }
75
76 if (other.m_searchTree) {
77 m_searchTree = std::unique_ptr<SurfaceSkdTree>(new SurfaceSkdTree(m_surface));
78 m_searchTree->build();
79 } else {
80 m_searchTree = nullptr;
81 }
82}
83
90 : m_surface(nullptr),
91 m_featureAngle(0),
92 m_surfaceSmoothing(surfaceSmoothing)
93{
94}
95
103LevelSetSegmentationSurfaceInfo::LevelSetSegmentationSurfaceInfo(std::unique_ptr<const SurfUnstructured> &&surface, double featureAngle, LevelSetSurfaceSmoothing surfaceSmoothing)
104 : m_surfaceSmoothing(surfaceSmoothing)
105{
106 setSurface(std::move(surface), featureAngle);
107}
108
117{
118 setSurface(surface, featureAngle, surfaceSmoothing);
119}
120
126 return *m_surface;
127}
128
134void LevelSetSegmentationSurfaceInfo::setSurface(std::unique_ptr<const SurfUnstructured> &&surface, double featureAngle){
135
136 m_ownedSurface = std::move(surface);
137 setSurface(m_ownedSurface.get(), featureAngle);
138}
139
146void LevelSetSegmentationSurfaceInfo::setSurface(const SurfUnstructured *surface, double featureAngle, LevelSetSurfaceSmoothing surfaceSmoothing){
147
148 // Check if adjacencies are built
149 if (surface->getAdjacenciesBuildStrategy() == SurfUnstructured::ADJACENCIES_NONE) {
150 throw std::runtime_error ("Segmentation needs adjacencies!") ;
151 }
152
153 // Surface information
154 m_surface = surface;
155 m_featureAngle = featureAngle;
156 m_surfaceSmoothing = surfaceSmoothing;
157
158 // Segment vertices information
159 m_segmentVertexOffset.unsetKernel();
160 m_segmentVertexOffset.setStaticKernel(&m_surface->getCells());
161
162 std::size_t nTotalSegmentVertices = 0;
163 for (auto itr = m_segmentVertexOffset.begin(); itr != m_segmentVertexOffset.end(); ++itr) {
164 *itr = nTotalSegmentVertices;
165 nTotalSegmentVertices += m_surface->getCells().rawAt(itr.getRawIndex()).getVertexCount();
166 }
167
168 // Normals
169 m_segmentNormalsValid.unsetKernel();
170 m_segmentNormalsValid.setStaticKernel(&m_surface->getCells());
171 m_segmentNormalsValid.fill(false);
172 m_segmentNormalsStorage.unsetKernel();
173 m_segmentNormalsStorage.setStaticKernel(&m_surface->getCells());
174
175 m_unlimitedVertexNormalsValid.unsetKernel();
176 m_unlimitedVertexNormalsValid.setStaticKernel(&m_surface->getVertices());
177 m_unlimitedVertexNormalsValid.fill(false);
178 m_unlimitedVertexNormalsStorage.unsetKernel();
179 m_unlimitedVertexNormalsStorage.setStaticKernel(&m_surface->getVertices());
180
181 m_limitedSegmentVertexNormalValid.assign(nTotalSegmentVertices, false);
182
183 // Initialize search tree
184 m_searchTree = std::unique_ptr<SurfaceSkdTree>(new SurfaceSkdTree(surface));
185 m_searchTree->build();
186}
187
193void LevelSetSegmentationSurfaceInfo::setSurface(const SurfUnstructured *surface, double featureAngle){
194 setSurface(surface, featureAngle, LevelSetSurfaceSmoothing::LOW_ORDER);
195}
196
202 return *m_searchTree;
203}
204
210 return m_featureAngle;
211}
212
221
231double LevelSetSegmentationSurfaceInfo::evalDistance(const std::array<double, 3> &point,
232 const SegmentConstIterator &segmentItr,
233 bool signedDistance,
234 std::array<double, 3> *distanceVector) const
235{
236 // Project the point on the surface and evaluate the point-projection vector
237 int nSegmentVertices = segmentItr->getVertexCount();
238 BITPIT_CREATE_WORKSPACE(lambda, double, nSegmentVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
239 std::array<double, 3> pointProjection = evalProjection(point, segmentItr, lambda);
240 (*distanceVector) = point - pointProjection;
241
242 // Evaluate unsigned distance
243 double unsignedDistance = norm2(*distanceVector);
244 if (!signedDistance) {
245 return unsignedDistance;
246 }
247
248 // Signed distance
249 //
250 // If the sign is null and the point doesn't lie on the segmentation, it lies on the normal
251 // plane. This case is not supported, because it would require to evaluate the sign taking
252 // into account the the curvature of the surface.
253 std::array<double, 3> pseudoNormal = computePseudoNormal(segmentItr, lambda);
254 double pointProjectionNormalComponent = dotProduct(*distanceVector, pseudoNormal);
255
256 double distanceTolerance = m_surface->getTol();
257 if (utils::DoubleFloatingEqual()(pointProjectionNormalComponent, 0., distanceTolerance, distanceTolerance)) {
258 bool pointOnSegmentation = utils::DoubleFloatingEqual()(unsignedDistance, 0., distanceTolerance, distanceTolerance);
259 if (!pointOnSegmentation) {
260 throw std::runtime_error("Unable to evaluate point sign: the point lies on the normal plane!");
261 }
262 }
263
264 return sign(pointProjectionNormalComponent) * unsignedDistance;
265}
266
275double LevelSetSegmentationSurfaceInfo::evalDistance(const std::array<double, 3> &point,
276 const SegmentConstIterator &segmentItr,
277 bool signedDistance) const
278{
279 std::array<double, 3> distanceVector;
280 return evalDistance(point, segmentItr, signedDistance, &distanceVector);
281}
282
290std::array<double, 3> LevelSetSegmentationSurfaceInfo::evalDistanceVector(const std::array<double, 3> &point,
291 const SegmentConstIterator &segmentItr) const
292{
293 int nSegmentVertices = segmentItr->getVertexCount();
294 BITPIT_CREATE_WORKSPACE(lambda, double, nSegmentVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
295 std::array<double, 3> pointProjection = evalProjection(point, segmentItr, lambda);
296
297 return (point - pointProjection);
298}
299
307std::array<double, 3> LevelSetSegmentationSurfaceInfo::evalNormal(const std::array<double, 3> &point,
308 const SegmentConstIterator &segmentItr) const
309{
310 // Project the point on the surface and evaluate the point-projection vector
311 std::array<double, 3> projectionPoint;
312 std::array<double, 3> projectionNormal;
313 evalProjection(point, segmentItr, &projectionPoint, &projectionNormal);
314
315 return projectionNormal;
316}
317
325std::array<double,3> LevelSetSegmentationSurfaceInfo::evalPseudoNormal(const SegmentConstIterator &segmentItr,
326 const double *lambda ) const
327{
328 return computePseudoNormal(segmentItr, lambda);
329}
330
345void LevelSetSegmentationSurfaceInfo::evalProjectionOnVertex(const std::array<double,3> &point,
346 const SegmentConstIterator &segmentItr,
347 std::array<double, 3> *projectionPoint,
348 std::array<double, 3> *projectionNormal) const
349{
350 BITPIT_UNUSED(point);
351
352 // Get segment
353 const Cell &segment = *segmentItr;
354 assert(segment.getType() == ElementType::VERTEX);
355
356 // Get vertex id
357 ConstProxyVector<long> segmentVertexIds = segment.getVertexIds();
358 long id = segmentVertexIds[0];
359
360 // Compute projection point and normal
361 (*projectionPoint) = m_surface->getVertexCoords(id);
362 (*projectionNormal) = {0., 0., 0.};
363}
364
415void LevelSetSegmentationSurfaceInfo::evalHighOrderProjectionOnLine(const std::array<double,3> &point,
416 const SegmentConstIterator &segmentItr,
417 std::array<double, 3> *projectionPoint,
418 std::array<double, 3> *projectionNormal) const
419{
420 // Get segment
421 const Cell &segment = *segmentItr;
422 assert(segment.getType() == ElementType::LINE);
423
424 // Get segment vertices
425 ConstProxyVector<long> segmentVertexIds = segment.getVertexIds();
426
427 const std::array<double,3> &point0 = m_surface->getVertexCoords(segmentVertexIds[0]);
428 const std::array<double,3> &point1 = m_surface->getVertexCoords(segmentVertexIds[1]);
429
430 // Get projection point on segment
431 std::array<double,2> t;
432 std::array<double, 3> point_s = CGElem::projectPointSegment(point, point0, point1, &(t[0]));
433
434 // Early return if point_s consides with segment's node
435 double distanceTolerance = m_surface->getTol();
436 for (int i = 0; i < 2; ++i) {
437 if (utils::DoubleFloatingEqual()(t[i], 1., distanceTolerance, distanceTolerance)) {
438 (*projectionPoint) = m_surface->getVertexCoords(segmentVertexIds[i]);
439 (*projectionNormal) = computeSegmentVertexNormal(segmentItr, i, true);
440 return;
441 }
442 }
443
444 // Get normal on segment
445 std::array<double, 3> facetNormal = computeSegmentNormal(segmentItr);
446 std::array<double, 3> normal_s = point - point_s;
447
448 double distance = norm2(normal_s);
449 if (utils::DoubleFloatingEqual()(distance, 0., distanceTolerance, distanceTolerance)) {
450 normal_s = facetNormal;
451 } else {
452 normal_s /= distance;
453 if (dotProduct(normal_s, facetNormal) < 0.0) {
454 normal_s *= -1.0;
455 }
456 }
457
458 // Get normals on segment's vertices
459 std::array<double, 3> normal0 = computeSegmentVertexNormal(segmentItr, 0, true);
460 std::array<double, 3> normal1 = computeSegmentVertexNormal(segmentItr, 1, true);
461
462 // Compute lambda coefficients
463 std::array<double, 3> edge = point1 - point0;
464 double lambda01 = -dotProduct(edge, normal0) / dotProduct(normal_s, normal0);
465 double lambda10 = dotProduct(edge, normal1) / dotProduct(normal_s, normal1);
466
467 // Eval polynomial parameterizing the curve
468 double product1 = t[0] * t[1];
469 double product2 = lambda01 * t[0] + lambda10 * t[1];
470 double f = product1 * product2;
471 double df = product1 * (lambda01 - lambda10) + product2 * (t[1] - t[0]);
472
473 // Eval projection point on curve
474 (*projectionPoint) = point_s + f * normal_s;
475
476 // Eval normal vector on curve
477 double length = norm2(edge);
478 (*projectionNormal) = df * edge + length * length * normal_s;
479 (*projectionNormal) /= norm2((*projectionNormal));
480
481 if (dotProduct(normal_s, (*projectionNormal)) < 0.0) {
482 (*projectionNormal) *= -1.0;
483 }
484}
485
616void LevelSetSegmentationSurfaceInfo::evalHighOrderProjectionOnTriangle(const std::array<double,3> &point,
617 const SegmentConstIterator &segmentItr,
618 std::array<double, 3> *projectionPoint,
619 std::array<double, 3> *projectionNormal) const
620{
621 // Get segment
622 const Cell &segment = *segmentItr;
623 assert(segment.getType() == ElementType::TRIANGLE);
624
625 // Get segment vertices
626 ConstProxyVector<long> segmentVertexIds = segment.getVertexIds();
627
628 const std::array<double,3> &point0 = m_surface->getVertexCoords(segmentVertexIds[0]);
629 const std::array<double,3> &point1 = m_surface->getVertexCoords(segmentVertexIds[1]);
630 const std::array<double,3> &point2 = m_surface->getVertexCoords(segmentVertexIds[2]);
631
632 // Get projection point on segment
633 std::array<double,3> tau;
634 std::array<double, 3> point_s = CGElem::projectPointTriangle(point, point0, point1, point2, &(tau[0]));
635 std::array<double, 3> d0_point_s = point0 - point2;
636 std::array<double, 3> d1_point_s = point1 - point2;
637
638 // Early return if point_s consides with segment's node
639 double distanceTolerance = m_surface->getTol();
640 for (int i = 0; i < 3; ++i) {
641 if (utils::DoubleFloatingEqual()(tau[i], 1., distanceTolerance, distanceTolerance)) {
642 (*projectionPoint) = m_surface->getVertexCoords(segmentVertexIds[i]);
643 (*projectionNormal) = computeSegmentVertexNormal(segmentItr, i, true);
644 return;
645 }
646 }
647
648 // Get normal on segment
649 std::array<double, 3> facetNormal = computeSegmentNormal(segmentItr);
650 std::array<double, 3> normal_s = point - point_s;
651
652 double distance = norm2(normal_s);
653 if (utils::DoubleFloatingEqual()(distance, 0., distanceTolerance, distanceTolerance)) {
654 normal_s = facetNormal;
655 } else {
656 normal_s /= distance;
657 if (dotProduct(normal_s, facetNormal) < 0.0) {
658 normal_s *= -1.0;
659 }
660 }
661
662 // Compute projection point and normal at each edge "e"
663 std::array<std::array<double, 3>,3 > edgePoint_s;
664 std::array< std::array<double, 3>, 3 > d0_edgePoint_s;
665 std::array< std::array<double, 3>, 3 > d1_edgePoint_s;
666 std::array< std::array<double, 3>, 3 > edgePoint_p;
667 std::array< std::array<double, 3>, 3 > d0_edgePoint_p;
668 std::array< std::array<double, 3>, 3 > d1_edgePoint_p;
669 std::array< std::array<double, 3>, 3 > edgeNormal_p;
670 std::array< std::array<double, 3>, 3 > d0_edgeNormal_p;
671 std::array< std::array<double, 3>, 3 > d1_edgeNormal_p;
672 for (int e = 0; e < 3; ++e) {
673 // Get segment
674 const Cell &segment = *segmentItr;
675
676 // Get edge's local and global node ids
677 ConstProxyVector<int> edgeLocalVertexIds = segment.getFaceLocalVertexIds(e);
678 int localId0 = edgeLocalVertexIds[0];
679 int localId1 = edgeLocalVertexIds[1];
680 int id0 = segment.getVertexId(localId0);
681 int id1 = segment.getVertexId(localId1);
682
683 // Get vertex information
684 const std::array<double,3> &node0 = m_surface->getVertexCoords(id0);
685 const std::array<double,3> &node1 = m_surface->getVertexCoords(id1);
686
687 std::array<double, 3> normal0 = computeSegmentVertexNormal(segmentItr, localId0, true);
688 std::array<double, 3> normal1 = computeSegmentVertexNormal(segmentItr, localId1, true);
689
690 // Get edge normal
691 std::array<double, 3> edgeNormal_s = computeSegmentEdgeNormal(segmentItr, e, true);
692
693 // Compute projection coefficients
694 std::array<double, 3> edge = node1 - node0;
695 double p01 = dotProduct(point_s - node0, edge) / dotProduct(edge, edge);
696 double d0_p01;
697 double d1_p01;
698 if (p01 > 1.0) {
699 p01 = 1.0;
700 d0_p01 = 0.0;
701 d1_p01 = 0.0;
702 } else if (p01 < 0.0) {
703 p01 = 0.0;
704 d0_p01 = 0.0;
705 d1_p01 = 0.0;
706 } else {
707 d0_p01 = dotProduct(d0_point_s - node0, edge) / dotProduct(edge, edge);
708 d1_p01 = dotProduct(d1_point_s - node0, edge) / dotProduct(edge, edge);
709 }
710 double p10 = 1.0 - p01;
711 double d0_p10 = - d0_p01;
712 double d1_p10 = - d1_p01;
713
714 // Compute lambda coefficients
715 double lambda01 = -dotProduct(edge, normal0) / dotProduct(edgeNormal_s, normal0);
716 double lambda10 = dotProduct(edge, normal1) / dotProduct(edgeNormal_s, normal1);
717
718 // Edge projection point
719 double product1 = p10 * p01;
720 double d_product1 = p01 - p10; // derivative wrt p10
721
722 double product2 = lambda01 * p10 + lambda10 * p01;
723 double d_product2 = lambda01 - lambda10;
724
725 double f01 = product1 * product2;
726 double d_f01 = d_product1 * product2 + product1 * d_product2;
727 double d_d_f01 = 2.0 * (d_product1 * d_product2 - product2);
728
729 edgePoint_s[e] = p10 * node0 + p01 * node1;
730 std::array<double, 3> d_edgePoint_s = node0 - node1;
731
732 d0_edgePoint_s[e] = d_edgePoint_s * d0_p10;
733 d1_edgePoint_s[e] = d_edgePoint_s * d1_p10;
734
735 edgePoint_p[e] = edgePoint_s[e] + f01 * edgeNormal_s;
736 std::array<double, 3> d_edgePoint_p = d_edgePoint_s + d_f01 * edgeNormal_s;
737
738 d0_edgePoint_p[e] = d_edgePoint_p * d0_p10;
739 d1_edgePoint_p[e] = d_edgePoint_p * d1_p10;
740
741 // Edge surface normal
742 double length = norm2(edge);
743 edgeNormal_p[e] = d_f01 * edge + length * length * edgeNormal_s;
744 std::array<double, 3> d_edgeNormal_p = d_d_f01 * edge;
745
746 double norm = norm2(edgeNormal_p[e]);
747 double d_norm = dotProduct(edgeNormal_p[e], d_edgeNormal_p) / norm;
748 edgeNormal_p[e] /= norm;
749 d_edgeNormal_p = (d_edgeNormal_p - edgeNormal_p[e] * d_norm) / norm;
750
751 d0_edgeNormal_p[e] = d_edgeNormal_p * d0_p10;
752 d1_edgeNormal_p[e] = d_edgeNormal_p * d1_p10;
753 }
754
755 // Project surface point to the intermediator polygon
756 double sum_t = 0.0;
757 double d0_sum_t = 0.0;
758 double d1_sum_t = 0.0;
759 std::array<double, 3> t;
760 std::array<double, 3> d0_t;
761 std::array<double, 3> d1_t;
762 for (int i = 0; i < 3; i ++) { // loop in vertices
763 int j = (i + 1) % 3;
764 int k = (i + 2) % 3;
765 std::array<double, 3> v1 = edgePoint_s[j] - point_s;
766 std::array<double, 3> d0_v1 = d0_edgePoint_s[j]- d0_point_s;
767 std::array<double, 3> d1_v1 = d1_edgePoint_s[j]- d1_point_s;
768
769 std::array<double, 3> v2 = edgePoint_s[k] - point_s;
770 std::array<double, 3> d0_v2 = d0_edgePoint_s[k]- d0_point_s;
771 std::array<double, 3> d1_v2 = d1_edgePoint_s[k]- d1_point_s;
772
773 std::array<double, 3> normal = crossProduct(v1, v2);
774 std::array<double, 3> d0_normal = crossProduct(d0_v1, v2) + crossProduct(v1, d0_v2);
775 std::array<double, 3> d1_normal = crossProduct(d1_v1, v2) + crossProduct(v1, d1_v2);
776
777 t[i] = norm2(normal);
778 d0_t[i] = dotProduct(normal, d0_normal) / t[i];
779 d1_t[i] = dotProduct(normal, d1_normal) / t[i];
780 sum_t += t[i];
781 d0_sum_t += d0_t[i];
782 d1_sum_t += d1_t[i];
783 }
784
785 std::array<double, 3> point_m = {0.0, 0.0, 0.0};
786 std::array<double, 3> d0_point_m = {0.0, 0.0, 0.0};
787 std::array<double, 3> d1_point_m = {0.0, 0.0, 0.0};
788 for (int i = 0; i < 3; i ++) {
789 t[i] /= sum_t;
790 d0_t[i] = (d0_t[i] - t[i] * d0_sum_t) / sum_t;
791 d1_t[i] = (d1_t[i] - t[i] * d1_sum_t) / sum_t;
792 point_m += t[i] * edgePoint_p[i];
793 d0_point_m += d0_t[i] * edgePoint_p[i] + t[i] * d0_edgePoint_p[i];
794 d1_point_m += d1_t[i] * edgePoint_p[i] + t[i] * d1_edgePoint_p[i];
795 }
796
797 // Early return if point_s coinsides with a triangle
798 // edge (or a node of the intermediate triangle)
799 for (int i = 0; i < 3; ++i) {
800 if (utils::DoubleFloatingEqual()(t[i], 1., distanceTolerance, distanceTolerance)) {
801 (*projectionPoint) = edgePoint_p[i];
802 (*projectionNormal) = edgeNormal_p[i];
803 return;
804 }
805 }
806
807 // Compute 2D f function
808 double f = 0.0;
809 double d0_f = 0.0;
810 double d1_f = 0.0;
811 for (int e = 0; e < 3; ++e) {
812 int i = e; // previous vertex
813 int j = (e + 1) % 3; // next vertex
814
815 // Get vertex information
816 std::array<double,3> &node_i = edgePoint_p[i];
817 std::array<double,3> &d0_node_i = d0_edgePoint_p[i];
818 std::array<double,3> &d1_node_i = d1_edgePoint_p[i];
819
820 std::array<double,3> &node_j = edgePoint_p[j];
821 std::array<double, 3> &d0_node_j = d0_edgeNormal_p[j];
822 std::array<double, 3> &d1_node_j = d1_edgeNormal_p[j];
823
824 std::array<double, 3> &normal_i = edgeNormal_p[i];
825 std::array<double, 3> &d0_normal_i = d0_edgeNormal_p[i];
826 std::array<double, 3> &d1_normal_i = d1_edgeNormal_p[i];
827
828 std::array<double, 3> &normal_j = edgeNormal_p[j];
829 std::array<double, 3> &d0_normal_j = d0_edgeNormal_p[j];
830 std::array<double, 3> &d1_normal_j = d1_edgeNormal_p[j];
831
832 // Compute lambda parameters
833 std::array<double, 3> edge = node_j - node_i;
834 std::array<double, 3> d0_edge = d0_node_j - d0_node_i;
835 std::array<double, 3> d1_edge = d1_node_j - d1_node_i;
836
837 double lambda_ij = -dotProduct(edge, normal_i) / dotProduct(normal_s, normal_i);
838 double d0_lambda_ij = -(dotProduct(edge - lambda_ij * normal_s, d0_normal_i) + dotProduct(d0_edge, normal_i)) / dotProduct(normal_s, normal_i);
839 double d1_lambda_ij = -(dotProduct(edge - lambda_ij * normal_s, d1_normal_i) + dotProduct(d1_edge, normal_i)) / dotProduct(normal_s, normal_i);
840
841 double lambda_ji = dotProduct(edge, normal_j) / dotProduct(normal_s, normal_j);
842 double d0_lambda_ji = (dotProduct(edge - lambda_ji * normal_s, d0_normal_j) + dotProduct(d0_edge, normal_j)) / dotProduct(normal_s, normal_j);
843 double d1_lambda_ji = (dotProduct(edge - lambda_ji * normal_s, d1_normal_j) + dotProduct(d1_edge, normal_j)) / dotProduct(normal_s, normal_j);
844
845 // Compute contribution to the f function
846 f += t[i] * t[j] * (lambda_ij * t[i] + lambda_ji * t[j]);
847 d0_f += (d0_t[i] * t[j] + t[i] * d0_t[j]) * (lambda_ij * t[i] + lambda_ji * t[j])
848 + t[i] * t[j] * (d0_lambda_ij * t[i] + lambda_ij * d0_t[i] + d0_lambda_ji * t[j] + lambda_ji * d0_t[j]);
849 d1_f += (d1_t[i] * t[j] + t[i] * d1_t[j]) * (lambda_ij * t[i] + lambda_ji * t[j])
850 + t[i] * t[j] * (d1_lambda_ij * t[i] + lambda_ij * d1_t[i] + d1_lambda_ji * t[j] + lambda_ji * d1_t[j]);
851 }
852
853 // Compute projection point on curved surface
854 (*projectionPoint) = point_m + f * normal_s;
855
856 // Compute projection normal on curved surface
857 std::array<double, 3> d0_point_p = d0_point_m + d0_f * normal_s;
858 std::array<double, 3> d1_point_p = d1_point_m + d1_f * normal_s;
859
860 (*projectionNormal) = crossProduct(d0_point_p, d1_point_p);
861 (*projectionNormal) /= norm2((*projectionNormal));
862
863 if (dotProduct(normal_s, (*projectionNormal)) < 0.0) {
864 (*projectionNormal) *= -1.0;
865 }
866}
867
889void LevelSetSegmentationSurfaceInfo::evalHighOrderProjectionOnPolygon(const std::array<double,3> &point,
890 const SegmentConstIterator &segmentItr,
891 std::array<double, 3> *projectionPoint,
892 std::array<double, 3> *projectionNormal) const
893{
894 // Get segment
895 const Cell &segment = *segmentItr;
896
897 // Get projection point on segment
898 ConstProxyVector<long> segmentVertexIds = m_surface->getFacetOrderedVertexIds(segment);
899
900 std::size_t nSegmentVertices = segmentVertexIds.size();
901 BITPIT_CREATE_WORKSPACE(segmentVertexCoords, std::array<double BITPIT_COMMA 3>, nSegmentVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
902 m_surface->getVertexCoords(segmentVertexIds.size(), segmentVertexIds.data(), segmentVertexCoords);
903
904 BITPIT_CREATE_WORKSPACE(tau, double, nSegmentVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
905 std::array<double, 3> point_s = CGElem::projectPointPolygon(point, nSegmentVertices, segmentVertexCoords, tau);
906
907 std::array<double, 3> lowOrderProjectionNormal = tau[0] * computeSegmentVertexNormal(segmentItr, 0, true);
908 for (std::size_t i = 1; i < nSegmentVertices; ++i) {
909 lowOrderProjectionNormal += tau[i] * computeSegmentVertexNormal(segmentItr, i, true);
910 }
911
912 // Early return if point_s consides with segment's node
913 double distanceTolerance = m_surface->getTol();
914 for (std::size_t i = 0; i < nSegmentVertices; ++i) {
915 if (utils::DoubleFloatingEqual()(tau[i], 1., distanceTolerance, distanceTolerance)) {
916 (*projectionPoint) = segmentVertexCoords[i];
917 (*projectionNormal) = computeSegmentVertexNormal(segmentItr, i, true);
918 return;
919 }
920 }
921
922 std::array<double, 3> d0_point_s;
923 std::array<double, 3> d1_point_s;
924
925 // Define parameterization of point_s
926 // Definition: point_s - x_central = t0 * (x_prev - x_central) + t1 * (x_next - x_central)
927 // where x_prev, x_central, x_next are subsequent polygon vertices
928 // Vertex x_central is the one corresponding to polygon angle closer to 90 degrees
929 double cosMin = std::numeric_limits<double>::max();
930 for (std::size_t i = 0; i < nSegmentVertices; ++i) {
931 int i_p = i; // previous
932 int i_c = (i + 1) % nSegmentVertices; // central
933 int i_n = (i + 2) % nSegmentVertices; // next
934
935 const std::array<double,3> &point_p = m_surface->getVertexCoords(segmentVertexIds[i_p]);
936 const std::array<double,3> &point_c = m_surface->getVertexCoords(segmentVertexIds[i_c]);
937 const std::array<double,3> &point_n = m_surface->getVertexCoords(segmentVertexIds[i_n]);
938
939 std::array<double,3> x_p = point_p - point_c;
940 std::array<double,3> x_n = point_n - point_c;
941
942 double cos = std::abs(dotProduct(x_p, x_n)) / (norm2(x_p) * norm2(x_n));
943 if (cos < cosMin) {
944 cosMin = cos;
945 d0_point_s = x_p;
946 d1_point_s = x_n;
947 }
948 }
949
950 // Get normal on segment
951 std::array<double, 3> facetNormal = computeSegmentNormal(segmentItr);
952 std::array<double, 3> normal_s = point - point_s;
953 double distance = norm2(normal_s);
954 if (utils::DoubleFloatingEqual()(distance, 0., distanceTolerance, distanceTolerance)) {
955 normal_s = facetNormal;
956 } else {
957 normal_s /= distance;
958 if (dotProduct(normal_s, facetNormal) < 0.0) {
959 normal_s *= -1.0;
960 }
961 }
962
963 // Compute projection point and normal at each edge "e"
964 int nSegmentEdges = nSegmentVertices;
965 BITPIT_CREATE_WORKSPACE(edgePoint_s, std::array<double BITPIT_COMMA 3>, nSegmentEdges, ReferenceElementInfo::MAX_ELEM_EDGES);
966 BITPIT_CREATE_WORKSPACE(d0_edgePoint_s, std::array<double BITPIT_COMMA 3>, nSegmentEdges, ReferenceElementInfo::MAX_ELEM_EDGES);
967 BITPIT_CREATE_WORKSPACE(d1_edgePoint_s, std::array<double BITPIT_COMMA 3>, nSegmentEdges, ReferenceElementInfo::MAX_ELEM_EDGES);
968 BITPIT_CREATE_WORKSPACE(edgePoint_p, std::array<double BITPIT_COMMA 3>, nSegmentEdges, ReferenceElementInfo::MAX_ELEM_EDGES);
969 BITPIT_CREATE_WORKSPACE(d0_edgePoint_p, std::array<double BITPIT_COMMA 3>, nSegmentEdges, ReferenceElementInfo::MAX_ELEM_EDGES);
970 BITPIT_CREATE_WORKSPACE(d1_edgePoint_p, std::array<double BITPIT_COMMA 3>, nSegmentEdges, ReferenceElementInfo::MAX_ELEM_EDGES);
971 BITPIT_CREATE_WORKSPACE(edgeNormal_p, std::array<double BITPIT_COMMA 3>, nSegmentEdges, ReferenceElementInfo::MAX_ELEM_EDGES);
972 BITPIT_CREATE_WORKSPACE(d0_edgeNormal_p, std::array<double BITPIT_COMMA 3>, nSegmentEdges, ReferenceElementInfo::MAX_ELEM_EDGES);
973 BITPIT_CREATE_WORKSPACE(d1_edgeNormal_p, std::array<double BITPIT_COMMA 3>, nSegmentEdges, ReferenceElementInfo::MAX_ELEM_EDGES);
974 for (int e = 0; e < nSegmentEdges; ++e) {
975 // Get edge's local and global node ids
976 ConstProxyVector<int> edgeLocalVertexIds = segment.getFaceLocalVertexIds(e);
977 int localId0 = edgeLocalVertexIds[0];
978 int localId1 = edgeLocalVertexIds[1];
979 int id0 = segment.getVertexId(localId0);
980 int id1 = segment.getVertexId(localId1);
981
982 // Get vertex information
983 const std::array<double,3> &node0 = m_surface->getVertexCoords(id0);
984 const std::array<double,3> &node1 = m_surface->getVertexCoords(id1);
985
986 std::array<double, 3> normal0 = computeSegmentVertexNormal(segmentItr, localId0, true);
987 std::array<double, 3> normal1 = computeSegmentVertexNormal(segmentItr, localId1, true);
988
989 // Get edge normal
990 std::array<double, 3> edgeNormal_s = computeSegmentEdgeNormal(segmentItr, e, true);
991
992 // Compute projection coefficients
993 std::array<double, 3> edge = node1 - node0;
994 double p01 = dotProduct(point_s - node0, edge) / dotProduct(edge, edge);
995 double d0_p01;
996 double d1_p01;
997 if (p01 > 1.0) {
998 p01 = 1.0;
999 d0_p01 = 0.0;
1000 d1_p01 = 0.0;
1001 } else if (p01 < 0.0) {
1002 p01 = 0.0;
1003 d0_p01 = 0.0;
1004 d1_p01 = 0.0;
1005 } else {
1006 d0_p01 = dotProduct(d0_point_s - node0, edge) / dotProduct(edge, edge);
1007 d1_p01 = dotProduct(d1_point_s - node0, edge) / dotProduct(edge, edge);
1008 }
1009 double p10 = 1.0 - p01;
1010 double d0_p10 = - d0_p01;
1011 double d1_p10 = - d1_p01;
1012
1013 // Compute lambda coefficients
1014 double lambda01 = -dotProduct(edge, normal0) / dotProduct(edgeNormal_s, normal0);
1015 double lambda10 = dotProduct(edge, normal1) / dotProduct(edgeNormal_s, normal1);
1016
1017 // Edge projection point
1018 double product1 = p10 * p01;
1019 double d_product1 = p01 - p10; // derivative wrt p10
1020
1021 double product2 = lambda01 * p10 + lambda10 * p01;
1022 double d_product2 = lambda01 - lambda10;
1023
1024 double f01 = product1 * product2;
1025 double d_f01 = d_product1 * product2 + product1 * d_product2;
1026 double d_d_f01 = 2.0 * (d_product1 * d_product2 - product2);
1027
1028 edgePoint_s[e] = p10 * node0 + p01 * node1;
1029 std::array<double, 3> d_edgePoint_s = node0 - node1;
1030
1031 d0_edgePoint_s[e] = d_edgePoint_s * d0_p10;
1032 d1_edgePoint_s[e] = d_edgePoint_s * d1_p10;
1033
1034 edgePoint_p[e] = edgePoint_s[e] + f01 * edgeNormal_s;
1035 std::array<double, 3> d_edgePoint_p = d_edgePoint_s + d_f01 * edgeNormal_s;
1036
1037 d0_edgePoint_p[e] = d_edgePoint_p * d0_p10;
1038 d1_edgePoint_p[e] = d_edgePoint_p * d1_p10;
1039
1040 // Edge surface normal
1041 double length = norm2(edge);
1042 edgeNormal_p[e] = d_f01 * edge + length * length * edgeNormal_s;
1043 std::array<double, 3> d_edgeNormal_p = d_d_f01 * edge;
1044
1045 double norm = norm2(edgeNormal_p[e]);
1046 double d_norm = dotProduct(edgeNormal_p[e], d_edgeNormal_p) / norm;
1047 edgeNormal_p[e] /= norm;
1048 d_edgeNormal_p = (d_edgeNormal_p - edgeNormal_p[e] * d_norm) / norm;
1049
1050 d0_edgeNormal_p[e] = d_edgeNormal_p * d0_p10;
1051 d1_edgeNormal_p[e] = d_edgeNormal_p * d1_p10;
1052 }
1053
1054 // Early return if point_s coinsides with a polygon's
1055 // edge (or a node of the intermediate polygon)
1056 for (int e = 0; e < nSegmentEdges; ++e) {
1057 double distance = norm2(edgePoint_s[e] - point_s);
1058 if (utils::DoubleFloatingEqual()(distance, 0.0, distanceTolerance, distanceTolerance)) {
1059 (*projectionPoint) = edgePoint_p[e];
1060 (*projectionNormal) = edgeNormal_p[e];
1061 return;
1062 }
1063 }
1064
1065 // Project surface point to the intermediator polygon
1066 // The projection is done by following formula:
1067 // point_m - point_s = sum i=0,vertices-1 { t[i] * (edgePoint_p[i] - edgePoint_s[i]) }
1068 // After various numerical experiments it is concluded that a quite smooth interpolation is
1069 // resulted from the following choice of the interpolation weights "t":
1070 // t[i] = w[i] / sum j=0,vertices-1 { w[j] }, where
1071 // w[i] = product j=0,vertices-1, j!=i { d[j]^4 }, where
1072 // d[i] is the distance between point_s and edgePoint_s[i]
1073 BITPIT_CREATE_WORKSPACE(d, double, nSegmentVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
1074 BITPIT_CREATE_WORKSPACE(d0_d, double, nSegmentVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
1075 BITPIT_CREATE_WORKSPACE(d1_d, double, nSegmentVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
1076 for (std::size_t i = 0; i < nSegmentVertices; ++i) {
1077 d[i] = (edgePoint_s[i][0] - point_s[0]) * (edgePoint_s[i][0] - point_s[0])
1078 + (edgePoint_s[i][1] - point_s[1]) * (edgePoint_s[i][1] - point_s[1])
1079 + (edgePoint_s[i][2] - point_s[2]) * (edgePoint_s[i][2] - point_s[2]);
1080
1081 d[i] = d[i] * d[i];
1082
1083 d0_d[i] = 4.0 * d[i] * dotProduct((edgePoint_s[i] - point_s), (d0_edgePoint_s[i] - d0_point_s));
1084 d1_d[i] = 4.0 * d[i] * dotProduct((edgePoint_s[i] - point_s), (d1_edgePoint_s[i] - d1_point_s));
1085 }
1086
1087 BITPIT_CREATE_WORKSPACE(t, double, nSegmentVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
1088 BITPIT_CREATE_WORKSPACE(d0_t, double, nSegmentVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
1089 BITPIT_CREATE_WORKSPACE(d1_t, double, nSegmentVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
1090 double sum = 0.0;
1091 double d0_sum = 0.0;
1092 double d1_sum = 0.0;
1093 for (std::size_t i = 0; i < nSegmentVertices; ++i) {
1094 t[i] = 1.0;
1095 d0_t[i] = 0.0;
1096 d1_t[i] = 0.0;
1097 for (std::size_t j = 0; j < nSegmentVertices; ++j) {
1098 if (i != j) {
1099 t[i] *= d[j];
1100
1101 double product = 1.0;
1102 for (std::size_t k = 0; k < nSegmentVertices; ++k) {
1103 if (j != k) {
1104 product *= d[k];
1105 }
1106 }
1107 d0_t[i] += d0_d[j] * product;
1108 d1_t[i] += d1_d[j] * product;
1109 }
1110 }
1111 sum += t[i];
1112 d0_sum += d0_t[i];
1113 d1_sum += d1_t[i];
1114 }
1115 for (std::size_t i = 0; i < nSegmentVertices; ++i) {
1116 t[i] /= sum;
1117 d0_t[i] = (d0_t[i] - t[i] * d0_sum) / sum;
1118 d1_t[i] = (d1_t[i] - t[i] * d1_sum) / sum;
1119 }
1120
1121 std::array<double, 3> point_m = {0.0, 0.0, 0.0};
1122 std::array<double, 3> d0_point_m = {0.0, 0.0, 0.0};
1123 std::array<double, 3> d1_point_m = {0.0, 0.0, 0.0};
1124 for (std::size_t i = 0; i < nSegmentVertices; ++i) {
1125 point_m += (edgePoint_p[i] - edgePoint_s[i]) * t[i];
1126 d0_point_m += d0_t[i] * (edgePoint_p[i]- edgePoint_s[i]) + t[i] * (d0_edgePoint_p[i] - d0_edgePoint_s[i]);
1127 d1_point_m += d1_t[i] * (edgePoint_p[i]- edgePoint_s[i]) + t[i] * (d1_edgePoint_p[i] - d1_edgePoint_s[i]);
1128 }
1129 point_m += point_s;
1130 d0_point_m += d0_point_s;
1131 d1_point_m += d1_point_s;
1132
1133 // Compute 2D f function
1134 double f = 0.0;
1135 double d0_f = 0.0;
1136 double d1_f = 0.0;
1137 for (int e = 0; e < nSegmentEdges; ++e) {
1138 int i = e; // previous vertex
1139 int j = (e + 1) % nSegmentEdges; // next vertex
1140
1141 // Get vertex information
1142 std::array<double,3> &node_i = edgePoint_p[i];
1143 std::array<double,3> &d0_node_i = d0_edgePoint_p[i];
1144 std::array<double,3> &d1_node_i = d1_edgePoint_p[i];
1145
1146 std::array<double, 3> &node_j = edgePoint_p[j];
1147 std::array<double, 3> &d0_node_j = d0_edgeNormal_p[j];
1148 std::array<double, 3> &d1_node_j = d1_edgeNormal_p[j];
1149
1150 std::array<double, 3> &normal_i = edgeNormal_p[i];
1151 std::array<double, 3> &d0_normal_i = d0_edgeNormal_p[i];
1152 std::array<double, 3> &d1_normal_i = d1_edgeNormal_p[i];
1153
1154 std::array<double, 3> &normal_j = edgeNormal_p[j];
1155 std::array<double, 3> &d0_normal_j = d0_edgeNormal_p[j];
1156 std::array<double, 3> &d1_normal_j = d1_edgeNormal_p[j];
1157
1158 // Compute lambda parameters
1159 std::array<double, 3> edge = node_j - node_i;
1160 std::array<double, 3> d0_edge = d0_node_j - d0_node_i;
1161 std::array<double, 3> d1_edge = d1_node_j - d1_node_i;
1162
1163 double lambda_ij = -dotProduct(edge, normal_i) / dotProduct(normal_s, normal_i);
1164 double d0_lambda_ij = -(dotProduct(edge - lambda_ij * normal_s, d0_normal_i) + dotProduct(d0_edge, normal_i)) / dotProduct(normal_s, normal_i);
1165 double d1_lambda_ij = -(dotProduct(edge - lambda_ij * normal_s, d1_normal_i) + dotProduct(d1_edge, normal_i)) / dotProduct(normal_s, normal_i);
1166
1167 double lambda_ji = dotProduct(edge, normal_j) / dotProduct(normal_s, normal_j);
1168 double d0_lambda_ji = (dotProduct(edge - lambda_ji * normal_s, d0_normal_j) + dotProduct(d0_edge, normal_j)) / dotProduct(normal_s, normal_j);
1169 double d1_lambda_ji = (dotProduct(edge - lambda_ji * normal_s, d1_normal_j) + dotProduct(d1_edge, normal_j)) / dotProduct(normal_s, normal_j);
1170
1171 // Compute contribution to the f function
1172 f += t[i] * t[j] * (lambda_ij * t[i] + lambda_ji * t[j]);
1173 d0_f += (d0_t[i] * t[j] + t[i] * d0_t[j]) * (lambda_ij * t[i] + lambda_ji * t[j])
1174 + t[i] * t[j] * (d0_lambda_ij * t[i] + lambda_ij * d0_t[i] + d0_lambda_ji * t[j] + lambda_ji * d0_t[j]);
1175 d1_f += (d1_t[i] * t[j] + t[i] * d1_t[j]) * (lambda_ij * t[i] + lambda_ji * t[j])
1176 + t[i] * t[j] * (d1_lambda_ij * t[i] + lambda_ij * d1_t[i] + d1_lambda_ji * t[j] + lambda_ji * d1_t[j]);
1177 }
1178
1179 // Compute projection point on curved surface
1180 (*projectionPoint) = point_m + f * normal_s;
1181
1182 // Compute projection normal on curved surface
1183 std::array<double, 3> d0_point_p = d0_point_m + d0_f * normal_s;
1184 std::array<double, 3> d1_point_p = d1_point_m + d1_f * normal_s;
1185
1186 (*projectionNormal) = crossProduct(d0_point_p, d1_point_p);
1187 (*projectionNormal) /= norm2((*projectionNormal));
1188
1189 if (dotProduct(normal_s, (*projectionNormal)) < 0.0) {
1190 (*projectionNormal) *= -1.0;
1191 }
1192}
1193
1213void LevelSetSegmentationSurfaceInfo::evalHighOrderProjection(const std::array<double,3> &point,
1214 const SegmentConstIterator &segmentItr,
1215 std::array<double, 3> *projectionPoint,
1216 std::array<double, 3> *projectionNormal) const
1217{
1218
1219 const Cell &segment = *segmentItr;
1220 ElementType segmentType = segment.getType();
1221 switch (segmentType) {
1222
1223 case ElementType::VERTEX:
1224 {
1225 evalProjectionOnVertex(point, segmentItr, projectionPoint, projectionNormal);
1226 return;
1227 }
1228
1229 case ElementType::LINE:
1230 {
1231 evalHighOrderProjectionOnLine(point, segmentItr, projectionPoint, projectionNormal);
1232 return;
1233 }
1234
1235 case ElementType::TRIANGLE:
1236 {
1237 evalHighOrderProjectionOnTriangle(point, segmentItr, projectionPoint, projectionNormal);
1238 return;
1239 }
1240
1241 default:
1242 {
1243 evalHighOrderProjectionOnPolygon(point, segmentItr, projectionPoint, projectionNormal);
1244 return;
1245 }
1246
1247 }
1248}
1249
1258void LevelSetSegmentationSurfaceInfo::evalLowOrderProjectionOnLine(const std::array<double, 3> &point,
1259 const SegmentConstIterator &segmentItr,
1260 std::array<double, 3> *projectionPoint,
1261 std::array<double, 3> *projectionNormal) const
1262{
1263 const Cell &segment = *segmentItr;
1264 assert(segment.getType() == ElementType::LINE);
1265
1266 ConstProxyVector<long> segmentVertexIds = segment.getVertexIds();
1267
1268 const std::array<double,3> &point0 = m_surface->getVertexCoords(segmentVertexIds[0]);
1269 const std::array<double,3> &point1 = m_surface->getVertexCoords(segmentVertexIds[1]);
1270
1271 int nSegmentVertices = segment.getVertexCount();
1272 BITPIT_CREATE_WORKSPACE(lambda, double, nSegmentVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
1273 (*projectionPoint) = CGElem::projectPointSegment(point, point0, point1, lambda);
1274
1275 std::array<double, 3> normal0 = computeSegmentVertexNormal(segmentItr, 0, true);
1276 std::array<double, 3> normal1 = computeSegmentVertexNormal(segmentItr, 1, true);
1277
1278 (*projectionNormal) = lambda[0] * normal0 + lambda[1] * normal1;
1279 (*projectionNormal) /= norm2((*projectionNormal));
1280}
1281
1290void LevelSetSegmentationSurfaceInfo::evalLowOrderProjectionOnTriangle(const std::array<double, 3> &point,
1291 const SegmentConstIterator &segmentItr,
1292 std::array<double, 3> *projectionPoint,
1293 std::array<double, 3> *projectionNormal) const
1294{
1295 const Cell &segment = *segmentItr;
1296 assert(segment.getType() == ElementType::TRIANGLE);
1297
1298 ConstProxyVector<long> segmentVertexIds = segment.getVertexIds();
1299
1300 const std::array<double,3> &point0 = m_surface->getVertexCoords(segmentVertexIds[0]);
1301 const std::array<double,3> &point1 = m_surface->getVertexCoords(segmentVertexIds[1]);
1302 const std::array<double,3> &point2 = m_surface->getVertexCoords(segmentVertexIds[2]);
1303
1304 int nSegmentVertices = segment.getVertexCount();
1305 BITPIT_CREATE_WORKSPACE(lambda, double, nSegmentVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
1306 (*projectionPoint) = CGElem::projectPointTriangle(point, point0, point1, point2, lambda);
1307
1308 std::array<double, 3> normal0 = computeSegmentVertexNormal(segmentItr, 0, true);
1309 std::array<double, 3> normal1 = computeSegmentVertexNormal(segmentItr, 1, true);
1310 std::array<double, 3> normal2 = computeSegmentVertexNormal(segmentItr, 2, true);
1311
1312 (*projectionNormal) = lambda[0] * normal0 + lambda[1] * normal1 + lambda[2] * normal2;
1313 (*projectionNormal) /= norm2((*projectionNormal));
1314}
1315
1324void LevelSetSegmentationSurfaceInfo::evalLowOrderProjectionOnPolygon(const std::array<double, 3> &point,
1325 const SegmentConstIterator &segmentItr,
1326 std::array<double, 3> *projectionPoint,
1327 std::array<double, 3> *projectionNormal) const
1328{
1329 const Cell &segment = *segmentItr;
1330
1331 ConstProxyVector<long> segmentVertexIds = m_surface->getFacetOrderedVertexIds(segment);
1332
1333 std::size_t nSegmentVertices = segmentVertexIds.size();
1334 BITPIT_CREATE_WORKSPACE(segmentVertexCoords, std::array<double BITPIT_COMMA 3>, nSegmentVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
1335 m_surface->getVertexCoords(segmentVertexIds.size(), segmentVertexIds.data(), segmentVertexCoords);
1336
1337 BITPIT_CREATE_WORKSPACE(lambda, double, nSegmentVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
1338 (*projectionPoint) = CGElem::projectPointPolygon(point, nSegmentVertices, segmentVertexCoords, lambda);
1339
1340 (*projectionNormal) = lambda[0] * computeSegmentVertexNormal(segmentItr, 0, true);
1341 for (std::size_t i = 1; i < nSegmentVertices; ++i) {
1342 (*projectionNormal) += lambda[i] * computeSegmentVertexNormal(segmentItr, i, true);
1343 }
1344 (*projectionNormal) /= norm2(*projectionNormal);
1345}
1346
1355void LevelSetSegmentationSurfaceInfo::evalLowOrderProjection(const std::array<double, 3> &point,
1356 const SegmentConstIterator &segmentItr,
1357 std::array<double, 3> *projectionPoint,
1358 std::array<double, 3> *projectionNormal) const
1359{
1360 const Cell &segment = *segmentItr;
1361 ElementType segmentType = segment.getType();
1362 switch (segmentType) {
1363
1364 case ElementType::VERTEX:
1365 {
1366 evalProjectionOnVertex(point, segmentItr, projectionPoint, projectionNormal);
1367 return;
1368 }
1369
1370 case ElementType::LINE:
1371 {
1372 evalLowOrderProjectionOnLine(point, segmentItr, projectionPoint, projectionNormal);
1373 return;
1374 }
1375
1376 case ElementType::TRIANGLE:
1377 {
1378 evalLowOrderProjectionOnTriangle(point, segmentItr, projectionPoint, projectionNormal);
1379 return;
1380 }
1381
1382 default:
1383 {
1384 evalLowOrderProjectionOnPolygon(point, segmentItr, projectionPoint, projectionNormal);
1385 return;
1386 }
1387
1388 }
1389}
1390
1399void LevelSetSegmentationSurfaceInfo::evalProjection(const std::array<double, 3> &point,
1400 const SegmentConstIterator &segmentItr,
1401 std::array<double, 3> *projectionPoint,
1402 std::array<double, 3> *projectionNormal) const
1403{
1404 if (m_surfaceSmoothing == LevelSetSurfaceSmoothing::HIGH_ORDER) {
1405 evalHighOrderProjection(point, segmentItr, projectionPoint, projectionNormal);
1406 } else {
1407 evalLowOrderProjection(point, segmentItr, projectionPoint, projectionNormal);
1408 }
1409}
1410
1418void LevelSetSegmentationSurfaceInfo::evalProjection(const std::array<double, 3> &point,
1419 const SegmentConstIterator &segmentItr,
1420 std::array<double, 3> *projectionPoint) const
1421{
1422 std::array<double, 3> projectionNormal;
1423 evalProjection(point, segmentItr, projectionPoint, &projectionNormal);
1424
1425 BITPIT_UNUSED(projectionNormal);
1426}
1427
1436std::array<double, 3> LevelSetSegmentationSurfaceInfo::evalProjection(const std::array<double, 3> &point,
1437 const SegmentConstIterator &segmentItr,
1438 double *lambda) const
1439{
1440 std::array<double, 3> projectionPoint;
1441 evalProjection(point, segmentItr, &projectionPoint);
1442
1443 const Cell &segment = *segmentItr;
1444 m_surface->evalBarycentricCoordinates(segment.getId(), point, lambda);
1445 return projectionPoint;
1446}
1447
1470std::array<double,3> LevelSetSegmentationSurfaceInfo::computePseudoNormal(const SegmentConstIterator &segmentItr,
1471 const double *lambda ) const
1472{
1473 // Early return if the segment is a point
1474 const Cell &segment = *segmentItr;
1475 ElementType segmentType = segment.getType();
1476 if (segmentType == ElementType::VERTEX) {
1477 return {{0., 0., 0.}};
1478 }
1479
1480 // Evaluate pseudo normal
1481 int positionFlag;
1482 if (segmentType == ElementType::LINE) {
1483 positionFlag = CGElem::convertBarycentricToFlagSegment(lambda, m_surface->getTol());
1484 } else {
1485 int nSegmentVertices = segment.getVertexCount();
1486 positionFlag = CGElem::convertBarycentricToFlagPolygon(nSegmentVertices, lambda, m_surface->getTol());
1487 if (positionFlag > 0) {
1488 int polygonVertex = positionFlag - 1;
1489 int elementVertex = m_surface->getFacetOrderedLocalVertex(segment, polygonVertex);
1490
1491 positionFlag = elementVertex + 1;
1492 } else if (positionFlag < 0) {
1493 int polygonEdge = (- positionFlag) - 1;
1494 int elementEdge = m_surface->getFacetOrderedLocalEdge(segment, polygonEdge);
1495
1496 positionFlag = - (elementEdge + 1);
1497 }
1498 }
1499
1500 std::array<double,3> pseudoNormal;
1501 if (positionFlag == 0) {
1502 pseudoNormal = computeSegmentNormal(segmentItr);
1503 } else if (positionFlag > 0) {
1504 int vertex = positionFlag - 1;
1505 pseudoNormal = computeSegmentVertexNormal(segmentItr, vertex, false);
1506 } else {
1507 int edge = (- positionFlag) - 1;
1508 pseudoNormal = computeSegmentEdgeNormal(segmentItr, edge, false);
1509 }
1510
1511 return pseudoNormal;
1512}
1513
1526std::array<double,3> LevelSetSegmentationSurfaceInfo::computeSurfaceNormal(const SegmentConstIterator &segmentItr,
1527 const double *lambda ) const
1528{
1529 // Early return if the segment is a point
1530 const Cell &segment = *segmentItr;
1531 ElementType segmentType = segment.getType();
1532 if (segmentType == ElementType::VERTEX) {
1533 return {{0., 0., 0.}};
1534 }
1535
1536 // Evaluate surface normal
1537 std::size_t nSegmentVertices = segment.getVertexCount();
1538 std::array<double,3> surfaceNormal = lambda[0] * computeSegmentVertexNormal(segmentItr, 0, true);
1539 for (std::size_t i = 1; i < nSegmentVertices; ++i) {
1540 surfaceNormal += lambda[i] * computeSegmentVertexNormal(segmentItr, i, true);
1541 }
1542 surfaceNormal /= norm2(surfaceNormal);
1543
1544 return surfaceNormal;
1545}
1546
1555std::array<double,3> LevelSetSegmentationSurfaceInfo::computeSegmentNormal(const SegmentConstIterator &segmentItr ) const {
1556
1557 std::size_t segmentRawId = segmentItr.getRawIndex();
1558 std::array<double, 3> *segmentNormal = m_segmentNormalsStorage.rawData(segmentRawId);
1559 if (!m_segmentNormalsValid.rawAt(segmentRawId)) {
1560 *segmentNormal = m_surface->evalFacetNormal(segmentItr->getId());
1561 m_segmentNormalsValid.rawAt(segmentRawId) = true;
1562 }
1563
1564 return *segmentNormal;
1565}
1566
1576std::array<double,3> LevelSetSegmentationSurfaceInfo::computeSegmentEdgeNormal(const SegmentConstIterator &segmentItr, int edge, bool limited ) const {
1577
1578 long segmentId = segmentItr.getId();
1579
1580 std::array<double, 3> limitedEdgeNormal;
1581 std::array<double, 3> unlimitedEdgeNormal;
1582 m_surface->evalEdgeNormals(segmentId, edge, m_featureAngle, &unlimitedEdgeNormal, &limitedEdgeNormal) ;
1583
1584 if (limited) {
1585 return limitedEdgeNormal;
1586 }
1587
1588 return unlimitedEdgeNormal;
1589}
1590
1602std::array<double,3> LevelSetSegmentationSurfaceInfo::computeSegmentVertexNormal(const SegmentConstIterator &segmentItr, int vertex, bool limited ) const {
1603
1604 // Segment information
1605 long segmentId = segmentItr.getId();
1606 long segmentRawId = segmentItr.getRawIndex();
1607 const Cell &segment = *segmentItr;
1608
1609 // Update the cache
1610 //
1611 // Cache is updated for all the vertices of the segment.
1612 long vertexId = segment.getVertexId(vertex);
1613 std::size_t vertexRawId = m_surface->getVertices().getRawIndex(vertexId);
1614 bool hasUnlimitedNormal = m_unlimitedVertexNormalsValid.rawAt(vertexRawId);
1615
1616 bool hasLimitedNormal = m_limitedSegmentVertexNormalValid[m_segmentVertexOffset.rawAt(segmentRawId) + vertex];
1617
1618 if (!hasUnlimitedNormal || !hasLimitedNormal) {
1619 static std::vector<long> vertexNeighbours;
1620 vertexNeighbours.clear();
1621 m_surface->findCellVertexNeighs(segmentId, vertex, &vertexNeighbours);
1622
1623 std::array<double, 3> limitedVertexNormal;
1624 std::array<double, 3> unlimitedVertexNormal;
1625 if (hasUnlimitedNormal) {
1626 limitedVertexNormal = m_surface->evalLimitedVertexNormal(segmentId, vertex, vertexNeighbours.size(), vertexNeighbours.data(), m_featureAngle) ;
1627 unlimitedVertexNormal = m_unlimitedVertexNormalsStorage.rawAt(vertexRawId);
1628 } else {
1629 m_surface->evalVertexNormals(segmentId, vertex, vertexNeighbours.size(), vertexNeighbours.data(), m_featureAngle, &unlimitedVertexNormal, &limitedVertexNormal) ;
1630 }
1631
1632 // Store vertex limited normal
1633 //
1634 // Both limited and unlimited normal are evaluated, however limited
1635 // normal is only stored if its misalignment with respect to the
1636 // unlimited normal is greater than a defined tolerance.
1637 if (!hasLimitedNormal) {
1638 double misalignment = norm2(unlimitedVertexNormal - limitedVertexNormal) ;
1639 if (misalignment >= m_surface->getTol()) {
1640 std::pair<long, int> segmentVertexKey = std::make_pair(segmentId, vertex);
1641 m_limitedSegmentVertexNormalStorage.insert({segmentVertexKey, std::move(limitedVertexNormal)}) ;
1642 }
1643 m_limitedSegmentVertexNormalValid[m_segmentVertexOffset.rawAt(segmentRawId) + vertex] = true;
1644 }
1645
1646 // Store vertex unlimited normal
1647 if (!hasUnlimitedNormal ) {
1648 m_unlimitedVertexNormalsStorage.rawAt(vertexRawId) = std::move(unlimitedVertexNormal);
1649 m_unlimitedVertexNormalsValid.rawAt(vertexRawId) = true ;
1650 }
1651 }
1652
1653 // Get the normal from the cache
1654 if (limited) {
1655 std::pair<long, int> segmentVertexKey = std::make_pair(segmentId, vertex);
1656 auto limitedSegmentVertexNormal = m_limitedSegmentVertexNormalStorage.find(segmentVertexKey);
1657 if (limitedSegmentVertexNormal != m_limitedSegmentVertexNormalStorage.end()) {
1658 return limitedSegmentVertexNormal->second;
1659 }
1660 }
1661
1662 return m_unlimitedVertexNormalsStorage.rawAt(vertexRawId);
1663}
1664
1670
1678
1688{
1689 switch(field) {
1690
1692 return createFieldCellCache<long>(field, cacheId);
1693
1694 default:
1695 return LevelSetObject::createFieldCellCache(field, cacheId);
1696
1697 }
1698}
1699
1705{
1706 LevelSetFieldset supportedFields = LevelSetObject::getSupportedFields();
1707 supportedFields.push_back(LevelSetField::PART);
1708 supportedFields.push_back(LevelSetField::NORMAL);
1709 supportedFields.push_back(LevelSetField::SUPPORT);
1710
1711 return supportedFields;
1712}
1713
1721{
1722 switch (field) {
1723
1725 evalCellSupport(id);
1726 break;
1727
1728 default:
1730
1731 }
1732}
1733
1741{
1742 return _evalCellSurface(id);
1743}
1744
1752{
1753 auto evaluator = [this] (long id)
1754 {
1755 return _evalCellPart(id);
1756 };
1757
1758 auto fallback = [] (long id)
1759 {
1760 BITPIT_UNUSED(id);
1761
1763 };
1764
1766 int part = evalCellFieldCached<int>(field, id, evaluator, fallback);
1767
1768 return part;
1769}
1770
1807{
1808 // Get surface information
1809 const SurfUnstructured &surface = evalCellSurface(id);
1810
1811 // Early return if the surface is empty
1812 if (surface.empty()) {
1814 }
1815
1816 // Evaluate intersection using base class
1817 return LevelSetObject::_isCellIntersected(id, distance, mode);
1818}
1819
1841 std::array<std::array<double, 3>, 2> *intersection,
1842 std::vector<std::array<double, 3>> *polygon) const
1843{
1844
1845 // Get the tolerance used for distance comparisons
1846 double tolerance = getKernel()->getDistanceTolerance();
1847
1848 // Evaluate projection of interface centroid on surface
1849 const Interface &interface = m_kernel->getMesh()->getInterface(id);
1850 std::array<double,3> interfaceCentroid = m_kernel->getMesh()->evalElementCentroid(interface);
1851
1852 std::array<double,3> projectionPoint;
1853 std::array<double,3> projectionNormal;
1854 evalProjection(interfaceCentroid, true, &projectionPoint, &projectionNormal);
1855 if (!invert) {
1856 projectionNormal *= -1.;
1857 }
1858
1859 // Early return if projection point is not the closest point of the
1860 // plane (defined by the projection point and projection normal) to
1861 // the interface centroid
1862 long support = evalSupport(interfaceCentroid);
1863 const SurfUnstructured &surface = evalSurface(interfaceCentroid);
1864 LevelSetSegmentationSurfaceInfo::SegmentConstIterator segmentItr = surface.getCellConstIterator(support);
1865
1866 const Cell &segment = *segmentItr;
1867 ElementType segmentType = segment.getType();
1868 if ((segmentType == ElementType::VERTEX)
1869 || (m_kernel->getMesh()->getDimension() == 3 && segmentType == ElementType::LINE)) {
1871 }
1872
1873 // Detect the intersection by imposing a Newton algorithm
1874 std::array<double, 3> intersectionCentroid = interfaceCentroid;
1875 std::array<double, 3> intersectionCentroidNew;
1876
1877 double residual = tolerance + 1.0;
1878 int iter = 0;
1879 int iterMax = 5;
1880 bool isIntersected = true;
1881 while (residual > tolerance && iter < iterMax && isIntersected) {
1882 ++ iter;
1883
1884 // Eval intersection between arbitrary 2D surface and planar interface
1885 isIntersected = m_kernel->getMesh()->intersectInterfacePlane(id, projectionPoint, projectionNormal, intersection, polygon);
1886
1887 intersectionCentroidNew = 0.5 * ((*intersection)[0] + (*intersection)[1]);
1888
1889 evalProjection(intersectionCentroidNew, true, &projectionPoint, &projectionNormal);
1890 if (!invert) {
1891 projectionNormal *= -1.;
1892 }
1893
1894 residual = norm2(intersectionCentroid - intersectionCentroidNew);
1895 intersectionCentroid = intersectionCentroidNew;
1896 }
1897
1898 // Eval intersection corresponding to the latest projection point and normal
1899 if (isIntersected) {
1900 isIntersected = m_kernel->getMesh()->intersectInterfacePlane(id, projectionPoint, projectionNormal, intersection, polygon);
1901 if (isIntersected) {
1903 }
1904 }
1906}
1907
1915std::array<double,3> LevelSetSegmentationBaseObject::evalCellNormal(long id, bool signedLevelSet) const
1916{
1917 // Evaluate signed normal
1918 //
1919 // The normal stored in the cache is unsigned.
1920 auto evaluator = [this] (long id) -> std::array<double,3>
1921 {
1922 return _evalCellNormal(id, false);
1923 };
1924
1925 auto fallback = [] (long id) -> const std::array<double,3> &
1926 {
1927 BITPIT_UNUSED(id);
1928
1930 };
1931
1933 std::array<double, 3> normal = evalCellFieldCached<std::array<double, 3>>(field, id, evaluator, fallback);
1934
1935 // Evaluate the normal with the correct signdness
1936 //
1937 // If an unsigned evaluation is requested, the orientation of the surface should be discarded
1938 // and in order to have a normal that is agnostic with respect the two sides of the surface.
1939 if (signedLevelSet) {
1940 short cellSign = evalCellSign(id);
1941 if (cellSign <= 0) {
1942 normal *= static_cast<double>(cellSign);;
1943 }
1944 }
1945
1946 return normal;
1947}
1948
1963long LevelSetSegmentationBaseObject::evalCellSupport(long id, double searchRadius) const
1964{
1965 // Evaluate support
1966 //
1967 // If we are using a limited search radius, it is not possible to use the support stored
1968 // in the cache, because it may be outside the search radius.
1969 auto evaluator = [this, searchRadius] (long id)
1970 {
1971 // Evaluate the search radius for support evaluation
1972 //
1973 // For the automatic evaluation of the search range, it is possible to use the
1974 // information about the cell zone:
1975 // - cells that are inside the narrow band because their distance from the surface is
1976 // less than the narrow band size can use a search radius equal to the narrow band
1977 // size
1978 // - cells that are inside the narrow band because they intersects the surface can use
1979 // a search radius equal to their bounding radius;
1980 // - cells that are inside the narrow band because their one of their face neighbours
1981 // intersect the surface can use a search radius equal to their bounding radius plus
1982 // the bounding diameter of the smallest neighbour that intersects the surface.
1983 double supportSearchRadius;
1984 if (searchRadius == AUTOMATIC_SEARCH_RADIUS) {
1985 LevelSetCellLocation cellLocation = getCellLocation(id);
1986 if (cellLocation == LevelSetCellLocation::NARROW_BAND_DISTANCE) {
1987 supportSearchRadius = this->m_narrowBandSize;
1988 } else if (cellLocation == LevelSetCellLocation::NARROW_BAND_INTERSECTED) {
1989 supportSearchRadius = m_kernel->computeCellBoundingRadius(id);
1990 } else if (cellLocation == LevelSetCellLocation::NARROW_BAND_NEIGHBOUR) {
1991 // Evaluate the bounding diameter of the smallest intersected face neighbour
1992 supportSearchRadius = std::numeric_limits<double>::max();
1993 auto neighProcessor = [this, &supportSearchRadius](long neighId, int layer) {
1994 BITPIT_UNUSED(layer);
1995
1996 // Discard neighbours that are not intersected
1997 LevelSetCellLocation neighLocation = getCellLocation(neighId);
1998 if (neighLocation != LevelSetCellLocation::NARROW_BAND_INTERSECTED) {
1999 return false;
2000 }
2001
2002 // Consider the bounding diameter of the smallest intersected neighbour
2003 supportSearchRadius = std::min(2 * m_kernel->computeCellBoundingRadius(neighId), supportSearchRadius);
2004
2005 // Continue processing the other neighbours
2006 return false;
2007 };
2008
2009 const VolumeKernel &mesh = *(m_kernel->getMesh()) ;
2010 mesh.processCellFaceNeighbours(id, 1, neighProcessor);
2011
2012 // Ad the bounding radius of the cell
2013 supportSearchRadius += m_kernel->computeCellBoundingRadius(id);
2014 } else {
2015 supportSearchRadius = std::numeric_limits<double>::max();
2016 }
2017 } else {
2018 supportSearchRadius = searchRadius;
2019 }
2020
2021 // Evaluate cell support
2022 return _evalCellSupport(id, supportSearchRadius);
2023 };
2024
2025 auto fallback = [] (long id)
2026 {
2027 BITPIT_UNUSED(id);
2028
2030 };
2031
2033
2034 long support;
2035 if (searchRadius < std::numeric_limits<double>::max() && searchRadius != AUTOMATIC_SEARCH_RADIUS) {
2036 // Evaluate the support from scratch
2037 support = evalCellField<long>(field, id, evaluator, fallback);
2038
2039 // Update the cache if the support is valid
2040 if (support >= 0) {
2041 fillFieldCellCache(field, id, support);
2042 }
2043 } else {
2044 // Evaluate the support
2045 support = evalCellFieldCached<long>(field, id, evaluator, fallback);
2046 }
2047
2048 return support;
2049}
2050
2057const SurfUnstructured & LevelSetSegmentationBaseObject::evalSurface(const std::array<double,3> &point) const
2058{
2059 return _evalSurface(point);
2060}
2061
2068int LevelSetSegmentationBaseObject::evalPart(const std::array<double,3> &point) const
2069{
2070 return _evalPart(point);
2071}
2072
2080std::array<double,3> LevelSetSegmentationBaseObject::evalNormal(const std::array<double,3> &point, bool signedLevelSet) const
2081{
2082 // Project the point on the surface and evaluate the point-projection vector
2083 std::array<double, 3> projectionPoint;
2084 std::array<double, 3> projectionNormal;
2085 evalProjection(point, signedLevelSet, &projectionPoint, &projectionNormal);
2086
2087 return projectionNormal;
2088}
2089
2096long LevelSetSegmentationBaseObject::evalSupport(const std::array<double,3> &point) const
2097{
2098 return _evalSupport(point);
2099}
2100
2109long LevelSetSegmentationBaseObject::evalSupport(const std::array<double,3> &point, double searchRadius) const
2110{
2111 return _evalSupport(point, searchRadius);
2112}
2113
2131void LevelSetSegmentationBaseObject::evalProjection(const std::array<double,3> &point,
2132 bool signedLevelSet,
2133 std::array<double, 3> *projectionPoint,
2134 std::array<double, 3> *projectionNormal) const
2135{
2136 _evalProjection(point, signedLevelSet, projectionPoint, projectionNormal);
2137}
2138
2146{
2147 long support = evalCellSupport(id);
2148 const SurfUnstructured &surface = evalCellSurface(id);
2149 int part = surface.getCell(support).getPID();
2150
2151 return part;
2152}
2153
2160int LevelSetSegmentationBaseObject::_evalPart(const std::array<double,3> &point) const
2161{
2162 long support = evalSupport(point);
2163 const SurfUnstructured &surface = evalSurface(point);
2164 int part = surface.getCell(support).getPID();
2165
2166 return part;
2167}
2168
2175void LevelSetSegmentationBaseObject::addVTKOutputData( LevelSetField field, const std::string &objectName)
2176{
2177 VTK &vtkWriter = this->m_kernel->getMesh()->getVTK() ;
2178 std::string name = this->getVTKOutputDataName(field, objectName);
2179
2180 switch (field) {
2181
2183 vtkWriter.addData<long>( name, VTKFieldType::SCALAR, VTKLocation::CELL, this);
2184 break;
2185
2187 vtkWriter.addData<int>( name, VTKFieldType::SCALAR, VTKLocation::CELL, this);
2188 break;
2189
2191 vtkWriter.addData<double>( name, VTKFieldType::VECTOR, VTKLocation::CELL, this);
2192 break;
2193
2194 default:
2195 LevelSetObject::addVTKOutputData(field, objectName);
2196 break;
2197
2198 }
2199}
2200
2208{
2209 switch (field) {
2210
2212 return "SupportId";
2213
2215 return "PartId";
2216
2218 return "Normal";
2219
2220 default:
2222
2223 }
2224}
2225
2236 LevelSetField field) const
2237{
2238 switch (field) {
2239
2241 {
2242 auto evaluator = [this] (long id) -> long { return evalCellSupport(id); };
2243 auto fallback = [] (long id) -> long { BITPIT_UNUSED(id); return levelSetDefaults::SUPPORT; };
2244 flushVTKOutputData<double>(stream, format, field, evaluator, fallback);
2245 break;
2246 }
2247
2249 {
2250 auto evaluator = [this] (long id) -> int { return evalCellPart(id); };
2251 auto fallback = [] (long id) -> int { BITPIT_UNUSED(id); return levelSetDefaults::PART; };
2252 flushVTKOutputData<double>(stream, format, field, evaluator, fallback);
2253 break;
2254 }
2255
2257 {
2258 auto evaluator = [this] (long id) -> std::array<double,3> { return evalCellNormal(id, true); };
2259 auto fallback = [] (long id) -> const std::array<double,3> & { BITPIT_UNUSED(id); return levelSetDefaults::NORMAL; };
2260 flushVTKOutputData<double>(stream, format, field, evaluator, fallback);
2261 break;
2262 }
2263
2264 default:
2265 LevelSetObject::flushVTKOutputData(stream, format, field);
2266 break;
2267
2268 }
2269}
2270
2278{
2279 return evalCellPart(cellId);
2280}
2281
2288std::array<double,3> LevelSetSegmentationBaseObject::getNormal(long cellId) const
2289{
2290 return evalCellNormal(cellId, m_defaultSignedLevelSet);
2291}
2292
2300{
2301 return evalCellSupport(cellId);
2302}
2303
2311{
2312 long support = evalCellSupport(cellId);
2313 if (support < 0) {
2314 return (- levelSetDefaults::SIZE);
2315 }
2316
2317 const SurfUnstructured &surface = evalCellSurface(cellId);
2318
2319 return surface.evalCellSize(support);
2320}
2321
2328int LevelSetSegmentationBaseObject::getPart(const std::array<double,3> &point) const
2329{
2330 return evalPart(point);
2331}
2332
2339std::array<double,3> LevelSetSegmentationBaseObject::getNormal(const std::array<double,3> &point) const
2340{
2341 return evalNormal(point, m_defaultSignedLevelSet);
2342}
2343
2350long LevelSetSegmentationBaseObject::getSupport(const std::array<double,3> &point) const
2351{
2352 return evalSupport(point);
2353}
2354
2361double LevelSetSegmentationBaseObject::getSurfaceFeatureSize(const std::array<double,3> &point) const
2362{
2363 long support = evalSupport(point);
2364 if (support < 0) {
2365 return (- levelSetDefaults::SIZE);
2366 }
2367
2368 const SurfUnstructured &surface = evalSurface(point);
2369
2370 return surface.evalCellSize(support);
2371}
2372
2378
2384 : LevelSetSegmentationBaseObject(id),
2385 m_surfaceInfo(nullptr)
2386{
2387}
2388
2395LevelSetSegmentationObject::LevelSetSegmentationObject(int id, std::unique_ptr<const SurfUnstructured> &&surface, double featureAngle)
2396 : LevelSetSegmentationBaseObject(id)
2397{
2398 setSurface(std::move(surface), featureAngle);
2399}
2400
2408 : LevelSetSegmentationBaseObject(id)
2409{
2410 setSurface(surface, featureAngle);
2411}
2412
2421 : LevelSetSegmentationBaseObject(id)
2422{
2423 setSurface(surface, featureAngle, surfaceSmoothing);
2424}
2425
2432 : LevelSetSegmentationBaseObject(other)
2433{
2434 if (other.m_surfaceInfo) {
2435 m_surfaceInfo = std::unique_ptr<LevelSetSegmentationSurfaceInfo>(new LevelSetSegmentationSurfaceInfo(*(other.m_surfaceInfo)));
2436 } else {
2437 m_surfaceInfo = nullptr;
2438 }
2439}
2440
2447{
2448 return getSurface().empty();
2449}
2450
2458
2464 return m_surfaceInfo->getSurface();
2465}
2466
2481void LevelSetSegmentationObject::setSurface(std::unique_ptr<const SurfUnstructured> &&surface, bool force){
2483}
2484
2498void LevelSetSegmentationObject::setSurface(std::unique_ptr<const SurfUnstructured> &&surface, double featureAngle, bool force){
2499 if (m_surfaceInfo) {
2500 // Check if replacing an existing surface is allowed
2501 if (!force) {
2502 throw std::runtime_error ("The surface can only be set once.");
2503 }
2504
2505 // Replace the surface
2506 m_surfaceInfo->setSurface(std::move(surface), featureAngle);
2507 } else {
2508 // Set surface
2509 //
2510 // Since this is the first time we set the surface, there is no need
2511 // to clear the caches.
2512 m_surfaceInfo = std::unique_ptr<LevelSetSegmentationSurfaceInfo>(new LevelSetSegmentationSurfaceInfo(std::move(surface), featureAngle));
2513 }
2514}
2515
2533
2547void LevelSetSegmentationObject::setSurface(const SurfUnstructured *surface, double featureAngle, bool force){
2548 if (m_surfaceInfo) {
2549 // Check if replacing an existing surface is allowed
2550 if (!force) {
2551 throw std::runtime_error ("The surface can only be set once.");
2552 }
2553
2554 // Replace the surface
2555 m_surfaceInfo->setSurface(surface, featureAngle);
2556 } else {
2557 // Set surface
2558 //
2559 // Since this is the first time we set the surface, there is no need
2560 // to clear the caches.
2561 m_surfaceInfo = std::unique_ptr<LevelSetSegmentationSurfaceInfo>(new LevelSetSegmentationSurfaceInfo(surface, featureAngle));
2562 }
2563}
2564
2579void LevelSetSegmentationObject::setSurface(const SurfUnstructured *surface, double featureAngle, LevelSetSurfaceSmoothing surfaceSmoothing, bool force){
2580 if (m_surfaceInfo) {
2581 // Check if replacing an existing surface is allowed
2582 if (!force) {
2583 throw std::runtime_error ("The surface can only be set once.");
2584 }
2585
2586 // Replace the surface
2587 m_surfaceInfo->setSurface(surface, featureAngle, surfaceSmoothing);
2588 } else {
2589 // Set surface
2590 //
2591 // Since this is the first time we set the surface, there is no need
2592 // to clear the caches.
2593 m_surfaceInfo = std::unique_ptr<LevelSetSegmentationSurfaceInfo>(new LevelSetSegmentationSurfaceInfo(surface, featureAngle, surfaceSmoothing));
2594 }
2595}
2596
2602 return m_surfaceInfo->getSearchTree();
2603}
2604
2610 return m_surfaceInfo->getFeatureAngle();
2611}
2612
2619 return m_surfaceInfo->getSurfaceSmoothing();
2620}
2621
2633{
2634 // Cartesian patches are handled separately
2635 if (dynamic_cast<LevelSetCartesianKernel*>(m_kernel)) {
2636 fillCartesianCellZoneCache();
2637 return;
2638 }
2639
2640 // All other patches are handled with the base method.
2642}
2643
2656void LevelSetSegmentationObject::fillCellLocationCache(const std::vector<adaption::Info> &adaptionData)
2657{
2658 // Cartesian patches are handled separately
2659 //
2660 // Update is not implemented for Cartesian patches, the cells that should be inserted in
2661 // the cache will be evaluated considering all the mash not just the elements newly added.
2662 if (dynamic_cast<LevelSetCartesianKernel*>(m_kernel)) {
2663 fillCartesianCellZoneCache();
2664 return;
2665 }
2666
2667 // All other patches are handled with the base method
2669}
2670
2682void LevelSetSegmentationObject::fillCartesianCellZoneCache()
2683{
2684 // The function needs a Cartesian kernel
2685 const LevelSetCartesianKernel *cartesianKernel = dynamic_cast<LevelSetCartesianKernel*>(m_kernel);
2686 if (!dynamic_cast<LevelSetCartesianKernel*>(m_kernel)) {
2687 throw std::runtime_error("The function needs a Cartesian kernels.");
2688 }
2689
2690 // Get mesh information
2691 const VolCartesian &mesh = *(cartesianKernel->getMesh() ) ;
2692 int meshDimension = mesh.getDimension();
2693 long nCells = mesh.getCellCount();
2694
2695 std::array<double,3> meshMinPoint;
2696 std::array<double,3> meshMaxPoint;
2697 mesh.getBoundingBox(meshMinPoint, meshMaxPoint) ;
2698
2699 // Get surface information
2700 const SurfUnstructured &surface = getSurface();
2701
2702 // Initialize process list
2703 //
2704 // Process list is initialized with cells that are certainly inside the
2705 // narrow band. Those cells are the ones that contain the vertices of the
2706 // segments or the intersection between the segments and the bounding box
2707 // of the patch.
2708 std::unordered_set<long> processList;
2709
2710 std::vector<std::array<double,3>> intersectionPoints;
2711 std::vector<std::array<double,3>> segmentVertexCoords;
2712 for (const Cell &segment : surface.getCells()) {
2713 // Get segment info
2714 //
2715 // Since vertex information will be passed to the CG module, we need to get the
2716 // vertices in the proper order.
2717 ConstProxyVector<long> segmentVertexIds = surface.getFacetOrderedVertexIds(segment);
2718 std::size_t nSegmentVertices = segmentVertexIds.size();
2719
2720 // Get segment coordinates
2721 segmentVertexCoords.resize(nSegmentVertices);
2722 surface.getVertexCoords(nSegmentVertices, segmentVertexIds.data(), segmentVertexCoords.data());
2723
2724 // Add to the process list the cells that contain the vertices of the
2725 // segment or the intersection between the segment and the bounding box
2726 // of the patch.
2727 int nInnerVertices = 0;
2728 for (const std::array<double,3> &vertexPoint : segmentVertexCoords) {
2729 long cellId = mesh.locatePoint(vertexPoint);
2730 if (cellId < 0) {
2731 continue;
2732 }
2733
2734 processList.insert(cellId);
2735 ++nInnerVertices;
2736 }
2737
2738 if (nInnerVertices == 0) {
2739 if (CGElem::intersectBoxPolygon(meshMinPoint, meshMaxPoint, segmentVertexCoords, false, true, true, intersectionPoints, meshDimension)) {
2740 for (const std::array<double,3> &intersectionPoint : intersectionPoints){
2741 long cellId = mesh.locateClosestCell(intersectionPoint);
2742 processList.insert(cellId);
2743 }
2744 }
2745 }
2746 }
2747
2748 // Get cache for zone identification
2749 CellCacheCollection::ValueCache<char> *locationCache = getCellCache<char>(m_cellLocationCacheId);
2750
2751 // Cells with an unknown region are in the bulk
2752 for (long cellId = 0; cellId < nCells; ++cellId) {
2753 locationCache->insertEntry(cellId, static_cast<char>(LevelSetCellLocation::UNKNOWN));
2754 }
2755
2756 // Start filling the list of cells within the narrow band
2757 //
2758 // The initial process list is gradually expanded considering all the neighbours
2759 // inside the narrow band.
2760 std::vector<long> intersectedCellIds;
2761 std::vector<bool> alreadyProcessed(nCells, false);
2762 while (!processList.empty()) {
2763 // Get the cell to process
2764 long cellId = *(processList.begin());
2765 processList.erase(processList.begin());
2766
2767 // Skip cells already processed
2768 if (alreadyProcessed[cellId]) {
2769 continue;
2770 }
2771 alreadyProcessed[cellId] = true;
2772
2773 // Fill location cache for cells geometrically inside the narrow band
2775
2776 // Track intersected cells
2778 intersectedCellIds.push_back(cellId);
2779 }
2780
2781 // Add unprocessed neighbours of cells inside the narrow band to the process list
2782 if (cellLocation != LevelSetCellLocation::UNKNOWN) {
2783 auto neighProcessor = [&processList, &alreadyProcessed](long neighId, int layer) {
2784 BITPIT_UNUSED(layer);
2785
2786 // Skip neighbours already processed
2787 if (alreadyProcessed[neighId]) {
2788 return false;
2789 }
2790
2791 // Update the process list
2792 processList.insert(neighId);
2793
2794 // Continue processing the other neighbours
2795 return false;
2796 };
2797
2798 mesh.processCellFaceNeighbours(cellId, 1, neighProcessor);
2799 }
2800 }
2801
2802 // Identify neighbours of cells that intersect the surface
2803 for (std::size_t cellId : intersectedCellIds) {
2804 // Process face neighbours
2805 auto neighProcessor = [this, &locationCache](long neighId, int layer) {
2806 BITPIT_UNUSED(layer);
2807
2808 // Skip neighbours whose region has already been identified
2810 return false;
2811 }
2812
2813 // The neighbour is inside the narrow band
2814 locationCache->insertEntry(neighId, static_cast<char>(LevelSetCellLocation::NARROW_BAND_NEIGHBOUR));
2815
2816 // Continue processing the other neighbours
2817 return false;
2818 };
2819
2820 mesh.processCellFaceNeighbours(cellId, 1, neighProcessor);
2821 }
2822
2823 // Cells with an unknown region are in the bulk
2824 for (long cellId = 0; cellId < nCells; ++cellId) {
2825 CellCacheCollection::ValueCache<char>::Entry locationCacheEntry = locationCache->findEntry(cellId);
2826 if (*locationCacheEntry == static_cast<char>(LevelSetCellLocation::UNKNOWN)) {
2827 locationCache->insertEntry(cellId, static_cast<char>(LevelSetCellLocation::BULK));
2828 }
2829 }
2830
2831#if BITPIT_ENABLE_MPI==1
2832 // Exchange ghost data
2833 if (mesh.isPartitioned()) {
2834 std::unique_ptr<DataCommunicator> dataCommunicator = m_kernel->createDataCommunicator();
2835 startCellCacheExchange(mesh.getGhostCellExchangeSources(), m_cellLocationCacheId, dataCommunicator.get());
2836 completeCellCacheExchange(mesh.getGhostCellExchangeTargets(), m_cellLocationCacheId, dataCommunicator.get());
2837 }
2838#endif
2839}
2840
2856{
2857 // Early return if the cell is geometrically outside the narrow band
2858 double searchRadius = std::max(m_kernel->computeCellBoundingRadius(id), m_narrowBandSize);
2859 long cellSupport = evalCellSupport(id, searchRadius);
2860 if (cellSupport < 0) {
2862 }
2863
2864 // Evaluate levelset value
2865 std::array<double,3> cellCentroid = m_kernel->computeCellCentroid(id);
2866
2867 double cellCacheValue = _evalValue(cellCentroid, cellSupport, CELL_CACHE_IS_SIGNED);
2868 double cellUnsigendValue = std::abs(cellCacheValue);
2869
2870 // Update the cell location cache
2871 //
2872 // First we need to check if the cell intersects the surface, and only if it
2873 // doesn't we should check if its distance is lower than the narrow band size.
2874 //
2875 // When high order smoothing is active, there may be cells whose support is within the search
2876 // radius, but they are not intersected and their distance is less than the narrow band size.
2877 // These cells are not geometrically inside the narrow band, they are neighbours of cells
2878 // geometrically inside the narrow band and as such it's up to the caller of this function to
2879 // identify their cell location.
2883 } else if (cellUnsigendValue <= m_narrowBandSize) {
2885 }
2887
2888 if (cellLocation != LevelSetCellLocation::UNKNOWN) {
2889 CellCacheCollection::ValueCache<char> *locationCache = getCellCache<char>(m_cellLocationCacheId);
2890 locationCache->insertEntry(id, static_cast<char>(cellLocation));
2891 }
2892
2893 // Fill the cache of the evaluated fields
2894 //
2895 // Now that the cell location has been identified, we can fill the cache of the
2896 // evaluated fields.
2898 fillFieldCellCache(LevelSetField::VALUE, id, cellCacheValue);
2899
2900 // Return the location
2901 return cellLocation;
2902}
2903
2911{
2912 BITPIT_UNUSED(id);
2913
2914 return getSurface();
2915}
2916
2924{
2925 long support = evalCellSupport(id);
2926 std::array<double,3> centroid = m_kernel->computeCellCentroid(id);
2927
2928 return _evalSign(centroid, support);
2929}
2930
2938double LevelSetSegmentationObject::_evalCellValue(long id, bool signedLevelSet) const
2939{
2940 long support = evalCellSupport(id);
2941 std::array<double,3> centroid = m_kernel->computeCellCentroid(id);
2942
2943 return _evalValue(centroid, support, signedLevelSet);
2944}
2945
2953std::array<double,3> LevelSetSegmentationObject::_evalCellGradient(long id, bool signedLevelSet) const
2954{
2955 long support = evalCellSupport(id);
2956 std::array<double,3> centroid = m_kernel->computeCellCentroid(id);
2957
2958 return _evalGradient(centroid, support, signedLevelSet);
2959}
2960
2973long LevelSetSegmentationObject::_evalCellSupport(long id, double searchRadius) const
2974{
2975 std::array<double,3> centroid = m_kernel->computeCellCentroid(id);
2976
2977 return _evalSupport(centroid, searchRadius);
2978}
2979
2987std::array<double,3> LevelSetSegmentationObject::_evalCellNormal(long id, bool signedLevelSet) const
2988{
2989 long support = evalCellSupport(id);
2990 std::array<double,3> centroid = m_kernel->computeCellCentroid(id);
2991
2992 std::array<double, 3> projectionPoint;
2993 std::array<double, 3> projectionNormal;
2994 _evalProjection(centroid, support, signedLevelSet, &projectionPoint, &projectionNormal);
2995
2996 return projectionNormal;
2997}
2998
3005const SurfUnstructured & LevelSetSegmentationObject::_evalSurface(const std::array<double,3> &point) const
3006{
3007 BITPIT_UNUSED(point);
3008
3009 return getSurface();
3010}
3011
3018short LevelSetSegmentationObject::_evalSign(const std::array<double,3> &point) const
3019{
3020 long support = evalSupport(point);
3021
3022 return _evalSign(point, support);
3023}
3024
3032double LevelSetSegmentationObject::_evalValue(const std::array<double,3> &point, bool signedLevelSet) const
3033{
3034 long support = evalSupport(point);
3035
3036 return _evalValue(point, support, signedLevelSet);
3037}
3038
3046std::array<double,3> LevelSetSegmentationObject::_evalGradient(const std::array<double,3> &point, bool signedLevelSet) const
3047{
3048 long support = evalSupport(point);
3049
3050 return _evalGradient(point, support, signedLevelSet);
3051}
3052
3070void LevelSetSegmentationObject::_evalProjection(const std::array<double,3> &point,
3071 bool signedLevelSet,
3072 std::array<double, 3> *projectionPoint,
3073 std::array<double, 3> *projectionNormal) const
3074{
3075 // Get closest segment
3076 long support = evalSupport(point);
3077
3078 _evalProjection(point, support, signedLevelSet, projectionPoint, projectionNormal);
3079}
3080
3088short LevelSetSegmentationObject::_evalSign(const std::array<double,3> &point, long support) const
3089{
3090 // Throw an error if the support is not valid
3091 if (support < 0) {
3092 if (empty()) {
3094 }
3095
3096 throw std::runtime_error("Unable to evaluate the sign: the support is not valid.");
3097 }
3098
3099 LevelSetSegmentationSurfaceInfo::SegmentConstIterator supportItr = getSurface().getCellConstIterator(support);
3100
3101 return evalValueSign(m_surfaceInfo->evalDistance(point, supportItr, true));
3102}
3103
3112double LevelSetSegmentationObject::_evalValue(const std::array<double,3> &point, long support,
3113 bool signedLevelSet) const
3114{
3115 // Early return if the support is not valid
3116 //
3117 // With an invalid support, only the unsigend levelset can be evaluated.
3118 if (support < 0) {
3119 if (!signedLevelSet || empty()) {
3121 }
3122
3123 throw std::runtime_error("With an invalid support, only the unsigend levelset can be evaluated.");
3124 }
3125
3126 // Evaluate the distance of the point from the surface
3127 LevelSetSegmentationSurfaceInfo::SegmentConstIterator supportItr = getSurface().getCellConstIterator(support);
3128 double distance = m_surfaceInfo->evalDistance(point, supportItr, signedLevelSet);
3129
3130 // Early return if the point lies on the surface
3131 if (evalValueSign(distance) == 0) {
3132 return 0.;
3133 }
3134
3135 // Evaluate levelset value
3136 double value;
3137 if (signedLevelSet) {
3138 value = distance;
3139 } else {
3140 value = std::abs(distance);
3141 }
3142
3143 return value;
3144}
3145
3154std::array<double,3> LevelSetSegmentationObject::_evalGradient(const std::array<double,3> &point, long support,
3155 bool signedLevelSet) const
3156{
3157 // Early return if the support is not valid
3158 //
3159 // With an invalid support, only the unsigend levelset can be evaluated.
3160 if (support < 0) {
3161 if (!signedLevelSet || empty()) {
3163 }
3164
3165 throw std::runtime_error("With an invalid support, only the unsigend levelset can be evaluated.");
3166 }
3167
3168 // Evaluate the distance of the point from the surface
3169 LevelSetSegmentationSurfaceInfo::SegmentConstIterator supportItr = getSurface().getCellConstIterator(support);
3170 std::array<double,3> distanceVector;
3171 double distance = m_surfaceInfo->evalDistance(point, supportItr, signedLevelSet, &distanceVector);
3172
3173 // Early return if the point lies on the surface
3174 if (evalValueSign(distance) == 0) {
3175 if (signedLevelSet) {
3176 return m_surfaceInfo->evalNormal(point, supportItr);
3177 } else {
3178 return {{0., 0., 0.}};
3179 }
3180 }
3181
3182 // Evaluate levelset gradient
3183 std::array<double,3> gradient = distanceVector / distance;
3184
3185 return gradient;
3186}
3187
3194long LevelSetSegmentationObject::_evalSupport(const std::array<double,3> &point) const
3195{
3196 return _evalSupport(point, std::numeric_limits<double>::max());
3197}
3198
3207long LevelSetSegmentationObject::_evalSupport(const std::array<double,3> &point, double searchRadius) const
3208{
3209 long closestSegmentId;
3210 double closestDistance;
3211 m_surfaceInfo->getSearchTree().findPointClosestCell(point, searchRadius, &closestSegmentId, &closestDistance);
3212
3213 return closestSegmentId;
3214}
3215
3236void LevelSetSegmentationObject::_evalProjection(const std::array<double,3> &point,
3237 long support,
3238 bool signedLevelSet,
3239 std::array<double, 3> *projectionPoint,
3240 std::array<double, 3> *projectionNormal) const
3241{
3242 // Early return if projection normal and projection point pointers are null
3243 if (!(projectionNormal) && !(projectionPoint)) {
3244 return;
3245 }
3246
3247 // Early return if the support is not valid
3248 //
3249 // With an invalid support, only the unsigend levelset can be evaluated.
3250 if (support < 0) {
3251 if (!signedLevelSet || empty()) {
3252 (*projectionPoint) = levelSetDefaults::POINT;
3253 (*projectionNormal) = levelSetDefaults::NORMAL;
3254 }
3255
3256 throw std::runtime_error("With an invalid support, only the unsigend levelset can be evaluated.");
3257 }
3258
3259 // Get closest segment
3260 LevelSetSegmentationSurfaceInfo::SegmentConstIterator segmentItr = getSurface().getCellConstIterator(support);
3261
3262 // Eval projection point and normal
3263 m_surfaceInfo->evalProjection(point, segmentItr, projectionPoint, projectionNormal);
3264
3265 // Early return if a projection normal null pointer is given
3266 if (!(projectionNormal)) {
3267 return;
3268 }
3269
3270 // If an unsigned evaluation is requested, the orientation of the surface should be discarded
3271 // in order to have a normal that is agnostic with respect the two sides of the surface.
3272 if (!signedLevelSet) {
3273 short sign = evalSign(point);
3274 (*projectionNormal) *= static_cast<double>(sign);
3275 }
3276}
3277
3300 std::array<std::array<double, 3>, 2> *intersection,
3301 std::vector<std::array<double, 3>> *polygon) const
3302{
3303 // Early return if low order smoothing is chosen
3304 if (m_surfaceInfo->getSurfaceSmoothing() == LevelSetSurfaceSmoothing::LOW_ORDER) {
3305 return LevelSetObject::_isInterfaceIntersected(id, invert, intersection, polygon);
3306 }
3307 return LevelSetSegmentationBaseObject::_isInterfaceIntersected(id, invert, intersection, polygon);
3308}
3309
3317
3318 const SurfUnstructured &surface = getSurface();
3319
3320 bool minimumValid = false;
3321 double minimumSize = levelSetDefaults::SIZE;
3322 for (const Cell &cell : surface.getCells()) {
3323 double segmentSize = surface.evalCellSize(cell.getId());
3324 if (segmentSize < 0) {
3325 continue;
3326 }
3327
3328 minimumValid = true;
3329 minimumSize = std::min(segmentSize, minimumSize);
3330 }
3331
3332 if (!minimumValid) {
3333 minimumSize = - levelSetDefaults::SIZE;
3334 }
3335
3336 return minimumSize;
3337}
3338
3346
3347 const SurfUnstructured &surface = getSurface();
3348
3349 double maximumSize = - levelSetDefaults::SIZE;
3350 for (const Cell &cell : surface.getCells()) {
3351 double segmentSize = surface.evalCellSize(cell.getId());
3352 maximumSize = std::max(segmentSize, maximumSize);
3353 }
3354
3355 return maximumSize;
3356}
3357
3368
3376LevelSetBooleanObject<LevelSetSegmentationBaseObject>::LevelSetBooleanObject( int id, LevelSetBooleanOperation op, const std::vector<const LevelSetSegmentationBaseObject *> &sourceObjects )
3378}
3379
3387
3395{
3396 return getCellReferenceObject(id)->evalCellSurface(id);
3397}
3398
3406{
3407 return getCellReferenceObject(id)->evalCellSupport(id, searchRadius);
3408}
3409
3417{
3418 return getCellReferenceObject(id)->evalCellPart(id);
3419}
3420
3428std::array<double,3> LevelSetBooleanObject<LevelSetSegmentationBaseObject>::_evalCellNormal(long id, bool signedLevelSet) const
3429{
3430 return _evalCellFunction<std::array<double,3>>(id, signedLevelSet, [&id, signedLevelSet] (const LevelSetBooleanResult<LevelSetSegmentationBaseObject> &result) -> std::array<double,3>
3431 {
3432 const LevelSetSegmentationBaseObject *resultObject = result.getObject();
3433 if ( !resultObject ) {
3435 }
3436
3437 std::array<double,3> normal = resultObject->evalCellNormal(id, signedLevelSet);
3438 if (signedLevelSet) {
3439 normal *= static_cast<double>(result.getObjectSign());
3440 }
3441
3442 return normal;
3443 });
3444}
3445
3453{
3454 return getReferenceObject(point)->evalSurface(point);
3455}
3456
3464{
3465 return getReferenceObject(point)->evalSupport(point);
3466}
3467
3476long LevelSetBooleanObject<LevelSetSegmentationBaseObject>::_evalSupport(const std::array<double,3> &point, double searchRadius) const
3477{
3478 return getReferenceObject(point)->evalSupport(point, searchRadius);
3479}
3480
3499 bool signedLevelSet,
3500 std::array<double, 3> *projectionPoint,
3501 std::array<double, 3> *projectionNormal) const
3502{
3503 return _evalFunction<void>(point, signedLevelSet, [&point, projectionPoint, projectionNormal, signedLevelSet] (const LevelSetBooleanResult<LevelSetSegmentationBaseObject> &result)
3504 {
3505 const LevelSetSegmentationBaseObject *resultObject = result.getObject();
3506 if ( !resultObject ) {
3507 (*projectionNormal) = levelSetDefaults::NORMAL;
3508 (*projectionPoint) = levelSetDefaults::POINT;
3509 }
3510
3511 resultObject->evalProjection(point, signedLevelSet, projectionPoint, projectionNormal);
3512 if (signedLevelSet) {
3513 (*projectionNormal) *= static_cast<double>(result.getObjectSign());
3514 }
3515 });
3516}
3517
3525{
3526 return getReferenceObject(point)->evalPart(point);
3527}
3528
3539
3547
3555{
3556 return getSourceObject()->evalCellSurface(id);
3557}
3558
3572{
3573 return getSourceObject()->evalCellSupport(id, searchRadius);
3574}
3575
3583{
3584 return getCellReferenceObject(id)->evalCellPart(id);
3585}
3586
3594std::array<double,3> LevelSetComplementObject<LevelSetSegmentationBaseObject>::_evalCellNormal(long id, bool signedLevelSet) const
3595{
3596 std::array<double,3> normal = getSourceObject()->evalCellNormal(id, signedLevelSet);
3597 if (signedLevelSet) {
3598 normal *= -1.;
3599 }
3600
3601 return normal;
3602}
3603
3611{
3612 return getSourceObject()->evalSurface(point);
3613}
3614
3622{
3623 return getSourceObject()->evalSupport(point);
3624}
3625
3634long LevelSetComplementObject<LevelSetSegmentationBaseObject>::_evalSupport(const std::array<double,3> &point, double searchRadius) const
3635{
3636 return getSourceObject()->evalSupport(point, searchRadius);
3637}
3638
3646{
3647 return getSourceObject()->evalPart(point);
3648}
3649
3668 bool signedLevelSet,
3669 std::array<double, 3> *projectionPoint,
3670 std::array<double, 3> *projectionNormal) const
3671{
3672 getSourceObject()->evalProjection(point, signedLevelSet, projectionPoint, projectionNormal);
3673 if (signedLevelSet) {
3674 (*projectionNormal) *= -1.;
3675 }
3676}
3677
3678}
The Cell class defines the cells.
Definition cell.hpp:42
long getId() const
Definition element.cpp:510
ElementType getType() const
Definition element.cpp:551
static ConstProxyVector< long > getVertexIds(ElementType type, const long *connectivity)
Definition element.cpp:1193
int getVertexCount() const
Definition element.cpp:1152
int getPID() const
Definition element.cpp:581
The Interface class defines the interfaces among cells.
Definition interface.hpp:37
const LevelSetSegmentationBaseObject * getCellReferenceObject(long id) const override
LevelSetBooleanBaseObject(int, LevelSetBooleanOperation, const LevelSetSegmentationBaseObject *, const LevelSetSegmentationBaseObject *)
const LevelSetSegmentationBaseObject * getReferenceObject(const std::array< double, 3 > &point) const override
data_t _evalCellFunction(long id, bool signedLevelSet, const function_t &function) const
data_t _evalFunction(const std::array< double, 3 > &point, bool signedLevelSet, const function_t &function) const
LevelSetBooleanObject(int, LevelSetBooleanOperation, const LevelSetSegmentationBaseObject *, const LevelSetSegmentationBaseObject *)
Allow to evaluate the result of a boolean operation between two LevelSetObjects.
const SourceLevelSetObject * getObject() const
Implements LevelSetKernel for cartesian meshes.
VolCartesian * getMesh() const override
const LevelSetSegmentationBaseObject * getCellReferenceObject(long id) const override
virtual const LevelSetSegmentationBaseObject * getSourceObject() const
LevelSetComplementBaseObject(int id, const LevelSetSegmentationBaseObject *source)
LevelSetComplementObject(int id, const LevelSetSegmentationBaseObject *source)
double getDistanceTolerance() const
CellCacheCollection::ValueCache< value_t > * getCellCache(std::size_t cacheId) const
static const LevelSetIntersectionMode CELL_LOCATION_INTERSECTION_MODE
void fillFieldCellCache(LevelSetField field, const std::vector< long > &cellIds)
short evalValueSign(double value) const
LevelSetKernel * m_kernel
void completeCellCacheExchange(const std::unordered_map< int, std::vector< long > > &sendCellIds, std::size_t cacheIds, DataCommunicator *)
virtual const LevelSetKernel * getKernel() const
virtual void addVTKOutputData(LevelSetField field, const std::string &objectName)
static const bool CELL_CACHE_IS_SIGNED
double m_narrowBandSize
Size of narrow band.
virtual std::string getVTKOutputFieldName(LevelSetField field) const
virtual short evalSign(const std::array< double, 3 > &point) const
virtual LevelSetIntersectionStatus _isInterfaceIntersected(long id, bool positivePart, std::array< std::array< double, 3 >, 2 > *intersection, std::vector< std::array< double, 3 > > *polygon) const
virtual LevelSetFieldset getSupportedFields() const
virtual void fillCellLocationCache()
virtual short evalCellSign(long id) const
LevelSetCellLocation getCellLocation(long id) const
value_t evalCellFieldCached(LevelSetField field, long id, const evaluator_t &evaluator, const fallback_t &fallback) const
std::size_t m_cellLocationCacheId
Id of the cache that will keep track if cell zones.
virtual void flushVTKOutputData(std::fstream &stream, VTKFormat format, LevelSetField field) const
value_t evalCellField(LevelSetField field, long id, const evaluator_t &evaluator, const fallback_t &fallback) const
virtual LevelSetIntersectionStatus _isCellIntersected(long, double distance, LevelSetIntersectionMode=LevelSetIntersectionMode::FAST_FUZZY) const
virtual std::size_t createFieldCellCache(LevelSetField field, std::size_t cacheId=CellCacheCollection::NULL_CACHE_ID)
std::string getVTKOutputDataName(LevelSetField field, const std::string &objectName) const
void startCellCacheExchange(const std::unordered_map< int, std::vector< long > > &recvCellIds, std::size_t cacheIds, DataCommunicator *) const
Implements visitor pattern fo segmentated geometries.
void evalProjection(const std::array< double, 3 > &point, bool signedLevelSet, std::array< double, 3 > *projectionPoint, std::array< double, 3 > *projectionNormal) const
void fillFieldCellCache(LevelSetField field, long id) override
void flushVTKOutputData(std::fstream &stream, VTKFormat format, LevelSetField field) const override
std::array< double, 3 > evalNormal(const std::array< double, 3 > &point, bool signedLevelSet) const
const SurfUnstructured & evalSurface(const std::array< double, 3 > &point) const
LevelSetFieldset getSupportedFields() const override
std::array< double BITPIT_COMMA 3 > getNormal(long cellId) const
virtual int _evalPart(const std::array< double, 3 > &point) const
void addVTKOutputData(LevelSetField field, const std::string &objectName) override
std::array< double, 3 > evalCellNormal(long id, bool signedLevelSet) const
std::size_t createFieldCellCache(LevelSetField field, std::size_t cacheId=CellCacheCollection::NULL_CACHE_ID) override
LevelSetIntersectionStatus _isCellIntersected(long, double distance, LevelSetIntersectionMode=LevelSetIntersectionMode::FAST_FUZZY) const override
int evalPart(const std::array< double, 3 > &point) const
long evalCellSupport(long id, double searchRadius=AUTOMATIC_SEARCH_RADIUS) const
static BITPIT_PUBLIC_API const double AUTOMATIC_SEARCH_RADIUS
virtual LevelSetIntersectionStatus _isInterfaceIntersected(long id, bool invert, std::array< std::array< double, 3 >, 2 > *intersection, std::vector< std::array< double, 3 > > *polygon) const override
long evalSupport(const std::array< double, 3 > &point) const
const SurfUnstructured & evalCellSurface(long id) const
std::string getVTKOutputFieldName(LevelSetField field) const override
const SurfUnstructured & _evalSurface(const std::array< double, 3 > &point) const override
LevelSetSegmentationObject * clone() const override
long _evalCellSupport(long id, double searchRadius=AUTOMATIC_SEARCH_RADIUS) const override
LevelSetCellLocation fillCellGeometricNarrowBandLocationCache(long id) override
LevelSetIntersectionStatus _isInterfaceIntersected(long id, bool invert, std::array< std::array< double, 3 >, 2 > *intersection, std::vector< std::array< double, 3 > > *polygon) const override
long _evalSupport(const std::array< double, 3 > &point) const override
short _evalSign(const std::array< double, 3 > &point) const override
std::array< double, 3 > _evalGradient(const std::array< double, 3 > &point, bool signedLevelSet) const override
double _evalCellValue(long id, bool signedLevelSet) const override
std::array< double, 3 > _evalCellGradient(long id, bool signedLevelSet) const override
LevelSetSurfaceSmoothing getSurfaceSmoothing() const
double _evalValue(const std::array< double, 3 > &point, bool signedLevelSet) const override
void setSurface(std::unique_ptr< const SurfUnstructured > &&surface, bool force=false)
const SurfUnstructured & getSurface() const
std::array< double, 3 > _evalCellNormal(long id, bool signedLevelSet) const override
const SurfUnstructured & _evalCellSurface(long id) const override
void _evalProjection(const std::array< double, 3 > &point, bool signedLevelSet, std::array< double, 3 > *projectionPoint, std::array< double, 3 > *projectionNormal) const override
void evalProjection(const std::array< double, 3 > &point, const SegmentConstIterator &segmentItr, std::array< double, 3 > *projectionPoint, std::array< double, 3 > *projectionNormal) const
double evalDistance(const std::array< double, 3 > &point, const SegmentConstIterator &segmentItr, bool signedDistance, std::array< double, 3 > *distanceVector) const
std::array< double, 3 > evalDistanceVector(const std::array< double, 3 > &point, const SegmentConstIterator &segmentItr) const
void setSurface(std::unique_ptr< const SurfUnstructured > &&surface, double featureAngle=DEFAULT_FEATURE_ANGLE)
std::array< double, 3 > evalNormal(const std::array< double, 3 > &point, const SegmentConstIterator &segmentItr) const
static BITPIT_PUBLIC_API const double DEFAULT_FEATURE_ANGLE
LevelSetSurfaceSmoothing getSurfaceSmoothing() const
std::array< double, 3 > evalPseudoNormal(const SurfUnstructured::CellConstIterator &segmentIterator, const double *lambda) const
AdjacenciesBuildStrategy getAdjacenciesBuildStrategy() const
PiercedVector< Cell > & getCells()
bool empty(bool global=true) const
Cell & getCell(long id)
void processCellFaceNeighbours(long seedId, int nLayers, Function function) const
const std::array< double, 3 > & getVertexCoords(long id) const
CellConstIterator getCellConstIterator(long id) const
The SurfUnstructured class defines an unstructured surface triangulation.
double evalCellSize(long id) const override
The SurfaceSkdTree implements a Bounding Volume Hierarchy tree for surface patches.
A base class for VTK input output.
Definition VTK.hpp:298
VTKField & addData(VTKField &&field)
Definition VTK.cpp:281
The VolumeKernel class provides an interface for defining volume patches.
array3D projectPointTriangle(array3D const &, array3D const &, array3D const &, array3D const &)
Definition CG_elem.cpp:1050
array3D projectPointSegment(array3D const &, array3D const &, array3D const &)
Definition CG_elem.cpp:997
int convertBarycentricToFlagSegment(std::array< double, 2 > const &, double tolerance=DEFAULT_DISTANCE_TOLERANCE)
Definition CG_elem.cpp:619
int convertBarycentricToFlagPolygon(std::vector< double > const &, double tolerance=DEFAULT_DISTANCE_TOLERANCE)
Definition CG_elem.cpp:687
bool intersectBoxPolygon(array3D const &, array3D const &, std::vector< array3D > const &, int dim=3, const double distanceTolerance=DEFAULT_DISTANCE_TOLERANCE)
Definition CG_elem.cpp:2931
array3D projectPointPolygon(array3D const &, std::vector< array3D > const &)
Definition CG_elem.cpp:1215
void sum(const std::array< T, d > &x, T1 &s)
double norm(const std::array< T, d > &x, int p)
std::array< T, 3 > crossProduct(const std::array< T, 3 > &x, const std::array< T, 3 > &y)
T sign(const T &)
Definition Operators.tpp:39
T dotProduct(const std::array< T, d > &x, const std::array< T, d > &y)
double norm2(const std::array< T, d > &x)
VTKFormat
Definition VTK.hpp:92
#define BITPIT_UNUSED(variable)
Definition compiler.hpp:63
#define BITPIT_CREATE_WORKSPACE(workspace, item_type, size, stack_size)
@ NARROW_BAND_INTERSECTED
Narrow band zone, the cell intersects the surface.
const std::array< double, 3 > GRADIENT
const std::array< double, 3 > POINT
const std::array< double, 3 > NORMAL