14 using hrleIndex = viennahrle::Index<D>;
30 SmartPointer<MaterialMap> materialMap =
nullptr;
33 struct SharpCornerNode {
35 Vec3D<NumericType> position;
50 int findNearestLowerCorner(
51 unsigned l,
const Vec3D<NumericType> &pos,
52 const std::unordered_map<
unsigned, std::vector<SharpCornerNode>>
54 const hrleIndex &activeGridIdx,
NumericType lsLAtActive) {
59 for (
unsigned m = 0; m < l; ++m) {
60 auto it = sharpCornerNodes.find(m);
61 if (it == sharpCornerNodes.end())
65 viennahrle::ConstSparseIterator<hrleDomainType> mIt(
67 mIt.goToIndices(activeGridIdx);
75 lsMAtActive - lsLAtActive > layerThreshold)
77 for (
const auto &scn : it->second) {
79 for (
int i = 0; i <
D; ++i) {
83 if (dist2 < minDist2) {
94 void splitPreviousMaterialEdge(
unsigned l,
unsigned cornerNodeId,
95 const Vec3D<NumericType> &cornerPos,
96 size_t meshSizeBefore) {
102 for (
size_t lineIdx = 0; lineIdx <
mesh->lines.size(); ++lineIdx) {
107 auto &line =
mesh->lines[lineIdx];
108 unsigned node0 = line[0];
109 unsigned node1 = line[1];
111 auto const &pos0 =
mesh->nodes[node0];
112 auto const &pos1 =
mesh->nodes[node1];
113 auto lineVec = pos1 - pos0;
117 for (
int i = 0; i <
D; ++i) {
118 lineLen2 += lineVec[i] * lineVec[i];
119 dot += (cornerPos[i] - pos0[i]) * lineVec[i];
131 for (
int i = 0; i <
D; ++i) {
132 NumericType d = cornerPos[i] - (pos0[i] + t * lineVec[i]);
144 line[1] = cornerNodeId;
145 mesh->lines.push_back({cornerNodeId, node1});
151 for (
size_t k = meshSizeBefore; k <
mesh->lines.size();) {
157 const auto &rl =
mesh->lines[k];
158 bool isSeg1 = (rl[0] == node0 && rl[1] == cornerNodeId) ||
159 (rl[0] == cornerNodeId && rl[1] == node0);
160 bool isSeg2 = (rl[0] == cornerNodeId && rl[1] == node1) ||
161 (rl[0] == node1 && rl[1] == cornerNodeId);
163 if (isSeg1 || isSeg2) {
164 mesh->lines.erase(
mesh->lines.begin() + k);
176 bool getSegmentIntersection(
const Vec3D<NumericType> &p1,
177 const Vec3D<NumericType> &p2,
178 const Vec3D<NumericType> &p3,
179 const Vec3D<NumericType> &p4,
180 Vec3D<NumericType> &intersection,
187 NumericType denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
191 NumericType ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom;
192 NumericType ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom;
196 intersection[0] = x1 + ua * (x2 - x1);
197 intersection[1] = y1 + ua * (y2 - y1);
198 if constexpr (
D == 3)
214 void handleCellCornerCrossings(
216 const std::unordered_map<
unsigned, std::vector<SharpCornerNode>>
218 unsigned l,
const hrleIndex &cellIndices) {
219 if constexpr (
D != 2)
222 unsigned pNode0 =
mesh->lines[pIdx][0];
223 unsigned pNode1 =
mesh->lines[pIdx][1];
224 Vec3D<NumericType> pp0 =
mesh->nodes[pNode0];
225 Vec3D<NumericType> pp1 =
mesh->nodes[pNode1];
227 for (
unsigned m = 0; m < l; ++m) {
228 auto cit = sharpCornerNodes.find(m);
229 if (cit == sharpCornerNodes.end())
232 for (
const auto &mscn : cit->second) {
235 for (
int i = 0; i <
D; ++i) {
246 size_t edge1Idx = SIZE_MAX, edge2Idx = SIZE_MAX;
247 for (
size_t eIdx = 0; eIdx < pIdx; ++eIdx) {
250 if (
mesh->lines[eIdx][0] != mscn.nodeId &&
251 mesh->lines[eIdx][1] != mscn.nodeId)
253 if (edge1Idx == SIZE_MAX)
255 else if (edge2Idx == SIZE_MAX) {
260 if (edge1Idx == SIZE_MAX || edge2Idx == SIZE_MAX)
264 Vec3D<NumericType> int1{}, int2{};
266 bool cross1 = getSegmentIntersection(
267 pp0, pp1,
mesh->nodes[
mesh->lines[edge1Idx][0]],
268 mesh->nodes[
mesh->lines[edge1Idx][1]], int1, t1);
269 bool cross2 = getSegmentIntersection(
270 pp0, pp1,
mesh->nodes[
mesh->lines[edge2Idx][0]],
271 mesh->nodes[
mesh->lines[edge2Idx][1]], int2, t2);
273 if (!cross1 || !cross2)
279 std::swap(int1, int2);
280 std::swap(edge1Idx, edge2Idx);
294 unsigned e1n0 =
mesh->lines[edge1Idx][0];
295 unsigned e1n1 =
mesh->lines[edge1Idx][1];
296 unsigned e2n0 =
mesh->lines[edge2Idx][0];
297 unsigned e2n1 =
mesh->lines[edge2Idx][1];
301 mesh->lines[edge1Idx][1] = intNode1;
303 mesh->lines.push_back({intNode1, e1n1});
310 mesh->lines[edge2Idx][1] = intNode2;
312 mesh->lines.push_back({intNode2, e2n1});
322 mesh->lines[pIdx][1] = intNode1;
324 mesh->lines.push_back({intNode2, pNode1});
343 void handleEdgeCornerInteractions(
345 const std::unordered_map<
unsigned, std::vector<SharpCornerNode>>
347 unsigned l,
const hrleIndex &cellIndices) {
348 if constexpr (
D != 2)
350 if (l == 0 || lineIdx >=
mesh->lines.size())
353 unsigned node0 =
mesh->lines[lineIdx][0];
354 unsigned node1 =
mesh->lines[lineIdx][1];
356 Vec3D<NumericType> pos0 =
mesh->nodes[node0];
357 Vec3D<NumericType> pos1 =
mesh->nodes[node1];
358 auto lineVec = pos1 - pos0;
361 for (
int i = 0; i <
D; ++i)
362 lineLen2 += lineVec[i] * lineVec[i];
366 for (
unsigned m = 0; m < l; ++m) {
367 auto it = sharpCornerNodes.find(m);
368 if (it == sharpCornerNodes.end())
371 for (
const auto &scn : it->second) {
374 if (scn.nodeId != node0 && scn.nodeId != node1) {
376 for (
int i = 0; i <
D; ++i)
377 dot += (scn.position[i] - pos0[i]) * lineVec[i];
381 for (
int i = 0; i <
D; ++i) {
382 NumericType d = scn.position[i] - (pos0[i] + t * lineVec[i]);
386 unsigned cId = scn.nodeId;
392 auto &line =
mesh->lines[lineIdx];
393 if (keep0 && keep1) {
395 mesh->lines.push_back({cId, node1});
407 mesh->lines.erase(
mesh->lines.begin() + lineIdx);
418 for (
int i = 0; i <
D; ++i) {
428 size_t edge1Idx = SIZE_MAX, edge2Idx = SIZE_MAX;
429 for (
size_t eIdx = 0; eIdx < lineIdx; ++eIdx) {
432 if (
mesh->lines[eIdx][0] != scn.nodeId &&
433 mesh->lines[eIdx][1] != scn.nodeId)
435 if (edge1Idx == SIZE_MAX)
437 else if (edge2Idx == SIZE_MAX) {
442 if (edge1Idx == SIZE_MAX || edge2Idx == SIZE_MAX)
445 Vec3D<NumericType> int1{}, int2{};
447 bool cross1 = getSegmentIntersection(
448 pos0, pos1,
mesh->nodes[
mesh->lines[edge1Idx][0]],
449 mesh->nodes[
mesh->lines[edge1Idx][1]], int1, t1);
450 bool cross2 = getSegmentIntersection(
451 pos0, pos1,
mesh->nodes[
mesh->lines[edge2Idx][0]],
452 mesh->nodes[
mesh->lines[edge2Idx][1]], int2, t2);
453 if (!cross1 || !cross2)
458 std::swap(int1, int2);
459 std::swap(edge1Idx, edge2Idx);
471 unsigned e1n0 =
mesh->lines[edge1Idx][0],
472 e1n1 =
mesh->lines[edge1Idx][1];
473 unsigned e2n0 =
mesh->lines[edge2Idx][0],
474 e2n1 =
mesh->lines[edge2Idx][1];
477 mesh->lines[edge1Idx][1] = intNode1;
479 mesh->lines.push_back({intNode1, e1n1});
485 mesh->lines[edge2Idx][1] = intNode2;
487 mesh->lines.push_back({intNode2, e2n1});
493 mesh->lines[lineIdx][1] = intNode1;
495 mesh->lines.push_back({intNode2, node1});
511 snapSharpCorners(
unsigned l,
size_t meshSizeBefore,
512 std::unordered_map<
unsigned, std::vector<SharpCornerNode>>
514 const hrleIndex &activeGridIdx,
NumericType lsLAtActive,
515 const hrleIndex &cellIndices) {
517 unsigned cornerNodeId = cornerPair.first;
518 Vec3D<NumericType> cornerPos = cornerPair.second;
521 (l > 0) ? findNearestLowerCorner(l, cornerPos, sharpCornerNodes,
522 activeGridIdx, lsLAtActive)
525 if (snapToNodeId >= 0) {
527 if (snapToNodeId != (
int)cornerNodeId) {
528 if constexpr (
D == 2) {
530 for (
size_t i =
mesh->lines.size(); i-- > meshSizeBefore;) {
531 for (
int j = 0; j < 2; ++j) {
532 if (
mesh->lines[i][j] == cornerNodeId)
533 mesh->lines[i][j] = snapToNodeId;
536 unsigned n0 =
mesh->lines[i][0];
537 unsigned n1 =
mesh->lines[i][1];
541 if (n0 == n1 || isDuplicate) {
542 mesh->lines.erase(
mesh->lines.begin() + i);
551 sharpCornerNodes[l].push_back(
552 {
static_cast<unsigned>(snapToNodeId), cornerPos});
555 sharpCornerNodes[l].push_back({cornerNodeId, cornerPos});
556 splitPreviousMaterialEdge(l, cornerNodeId, cornerPos, meshSizeBefore);
563 size_t sizeNow =
mesh->lines.size();
564 std::vector<std::pair<unsigned, Vec3D<NumericType>>> endpointsToSplit;
565 for (
size_t pIdx = meshSizeBefore; pIdx < sizeNow; ++pIdx) {
568 unsigned n0 =
mesh->lines[pIdx][0];
569 unsigned n1 =
mesh->lines[pIdx][1];
570 if (n0 != cornerNodeId)
571 endpointsToSplit.push_back({n0,
mesh->nodes[n0]});
572 if (n1 != cornerNodeId)
573 endpointsToSplit.push_back({n1,
mesh->nodes[n1]});
575 for (
auto &[nodeId, pos] : endpointsToSplit)
576 splitPreviousMaterialEdge(l, nodeId, pos, meshSizeBefore);
578 sizeNow =
mesh->lines.size();
579 for (
size_t pIdx = meshSizeBefore; pIdx < sizeNow; ++pIdx) {
582 handleCellCornerCrossings(pIdx, sharpCornerNodes, l, cellIndices);
594 double minNodeDistFactor = 0.01,
double eps = 1e-12)
596 minNodeDistFactor, eps) {}
599 std::vector<SmartPointer<lsDomainType>>
const &passedLevelSets,
601 double minNodeDistFactor = 0.01,
double eps = 1e-12)
608 double minNodeDistFactor = 0.01,
double eps = 1e-12)
620 materialMap = passedMaterialMap;
625 Logger::getInstance()
626 .addError(
"No level set was passed to ToMultiSurfaceMesh.")
630 if (
mesh ==
nullptr) {
631 Logger::getInstance()
632 .addError(
"No mesh was passed to ToMultiSurfaceMesh.")
639 for (
unsigned i = 0; i <
D; ++i) {
640 mesh->minimumExtent[i] = std::numeric_limits<NumericType>::max();
641 mesh->maximumExtent[i] = std::numeric_limits<NumericType>::lowest();
644 using nodeContainerType = std::map<viennahrle::Index<D>,
unsigned>;
646 nodeContainerType nodes[
D];
647 nodeContainerType faceNodes[
D];
648 nodeContainerType cornerNodes;
655 const bool useMaterialMap = materialMap !=
nullptr;
658 if (sharpCorners &&
D == 3) {
659 Logger::getInstance()
660 .addWarning(
"Sharp corner generation in 3D is experimental and may "
661 "produce suboptimal meshes. Use with caution.")
666 std::vector<viennahrle::ConstSparseCellIterator<hrleDomainType>> cellIts;
668 cellIts.emplace_back(
ls->getDomain());
671 std::unordered_map<unsigned, std::vector<SharpCornerNode>> sharpCornerNodes;
673 for (
unsigned l = 0; l <
levelSets.size(); l++) {
675 if (useMaterialMap) {
687 normalCalculator.
setMaxValue(std::numeric_limits<NumericType>::max());
688 normalCalculator.
apply();
693 viennahrle::ConstSparseIterator<hrleDomainType> valueIt(
697 for (
auto cellIt = cellIts[l]; !cellIt.isFinished(); cellIt.next()) {
698 for (
int u = 0; u <
D; u++) {
699 while (!nodes[u].empty() &&
700 nodes[u].begin()->first <
701 viennahrle::Index<D>(cellIt.getIndices()))
702 nodes[u].erase(nodes[u].begin());
704 if constexpr (
D == 3) {
705 while (!faceNodes[u].empty() &&
706 faceNodes[u].begin()->first <
707 viennahrle::Index<D>(cellIt.getIndices()))
708 faceNodes[u].erase(faceNodes[u].begin());
711 while (!cornerNodes.empty() &&
712 cornerNodes.begin()->first <
713 viennahrle::Index<D>(cellIt.getIndices()))
714 cornerNodes.erase(cornerNodes.begin());
720 for (
int i = 0; i < (1 <<
D); i++) {
721 if (cellIt.getCorner(i).getValue() >=
NumericType(0))
728 if (signs == (1 << (1 <<
D)) - 1)
733 bool atMaterialBoundary =
false;
734 unsigned touchingMaterial = 0;
735 if (sharpCorners && l > 0) {
736 touchingMaterial = l - 1;
737 viennahrle::ConstSparseIterator<hrleDomainType> prevIt(
738 levelSets[touchingMaterial]->getDomain());
739 for (
int i = 0; i < (1 <<
D); ++i) {
740 hrleIndex cornerIdx =
741 cellIt.getIndices() + viennahrle::BitMaskToIndex<D>(i);
742 prevIt.goToIndices(cornerIdx);
743 if (prevIt.getValue() <=
745 atMaterialBoundary =
true;
752 bool perfectCornerFound =
false;
758 for (
int i = 0; i < (1 <<
D); ++i) {
781 size_t meshSizeBefore = 0;
782 if constexpr (
D == 2) {
783 meshSizeBefore =
mesh->lines.size();
786 posMask, nodes,
nullptr, valueIt);
787 }
else if constexpr (
D == 3) {
788 if (countNeg == 2 || countPos == 2) {
790 cellIt, countNeg, countPos, negMask, posMask, nodes,
791 faceNodes,
nullptr, valueIt);
792 }
else if (countNeg == 1 || countPos == 1) {
794 cellIt, countNeg, countPos, negMask, posMask, nodes,
795 cornerNodes, faceNodes,
nullptr, valueIt);
796 }
else if (countNeg == 3 || countPos == 3) {
798 cellIt, countNeg, countPos, negMask, posMask, nodes,
799 faceNodes,
nullptr, valueIt);
811 for (
int i = 0; i < (1 <<
D); ++i)
812 if ((negMask >> i) & 1) {
816 }
else if (countPos == 1) {
817 for (
int i = 0; i < (1 <<
D); ++i)
818 if ((posMask >> i) & 1) {
823 hrleIndex activeGridIdx = cellIt.getIndices();
825 if (activeBit >= 0) {
826 activeGridIdx += viennahrle::BitMaskToIndex<D>(activeBit);
827 lsLAtActive = cellIt.getCorner(activeBit).getValue();
829 snapSharpCorners(l, meshSizeBefore, sharpCornerNodes, activeGridIdx,
830 lsLAtActive, cellIt.getIndices());
834 if (perfectCornerFound)
837 if constexpr (
D == 3) {
839 for (
int axis = 0; axis < 3; ++axis) {
840 for (
int d = 0; d < 2; ++d) {
841 viennahrle::Index<D> faceKey(cellIt.getIndices());
845 auto it = faceNodes[axis].find(faceKey);
846 if (it != faceNodes[axis].end()) {
847 const unsigned faceNodeId = it->second;
848 const int *Triangles =
851 for (; Triangles[0] != -1; Triangles += 3) {
852 std::vector<unsigned> face_edge_nodes;
853 for (
int i = 0; i < 3; ++i) {
854 int edge = Triangles[i];
858 (((c0 >> axis) & 1) == d) && (((c1 >> axis) & 1) == d);
860 face_edge_nodes.push_back(
861 this->
getNode(cellIt, edge, nodes,
nullptr));
864 if (face_edge_nodes.size() == 2) {
866 face_edge_nodes[0], face_edge_nodes[1], faceNodeId});
875 const int *Triangles;
876 if constexpr (
D == 2) {
882 for (; Triangles[0] != -1; Triangles +=
D) {
883 std::array<unsigned, D> nodeNumbers;
886 for (
int n = 0; n <
D; n++) {
887 const int edge = Triangles[n];
889 unsigned p0 = this->
corner0[edge];
895 viennahrle::Index<D> d(cellIt.getIndices());
896 auto p0B = viennahrle::BitMaskToIndex<D>(p0);
899 auto nodeIt = nodes[dir].find(d);
900 if (nodeIt != nodes[dir].end()) {
901 nodeNumbers[n] = nodeIt->second;
905 if (sharpCorners && l > 0 && atMaterialBoundary) {
906 unsigned cachedNodeId = nodeIt->second;
908 auto it = sharpCornerNodes.find(touchingMaterial);
909 if (it != sharpCornerNodes.end()) {
910 for (
const auto &scn : it->second) {
911 if (scn.nodeId == cachedNodeId) {
913 auto &lCorners = sharpCornerNodes[l];
914 bool alreadyInherited =
false;
915 for (
const auto &existing : lCorners) {
916 if (existing.nodeId == cachedNodeId) {
917 alreadyInherited =
true;
921 if (!alreadyInherited)
922 lCorners.push_back({cachedNodeId, scn.position});
932 Vec3D<NumericType> snapPos{};
933 if (sharpCorners && l > 0 && atMaterialBoundary) {
934 auto tmIt = sharpCornerNodes.find(touchingMaterial);
935 if (tmIt != sharpCornerNodes.end() && !tmIt->second.empty()) {
936 Vec3D<NumericType> nodePos =
941 for (
const auto &scn : tmIt->second) {
943 for (
int i = 0; i <
D; ++i) {
947 if (dist2 < minDist2) {
949 snapNodeId = scn.nodeId;
950 snapPos = scn.position;
956 if (snapNodeId >= 0) {
957 nodeNumbers[n] = snapNodeId;
958 nodes[dir][d] = snapNodeId;
960 sharpCornerNodes[l].push_back(
961 {
static_cast<unsigned>(snapNodeId), snapPos});
963 nodeNumbers[n] = this->
getNode(cellIt, edge, nodes,
nullptr);
969 bool isDuplicate =
false;
970 if constexpr (
D == 2) {
972 if (
uniqueElements.find({(int)nodeNumbers[1], (int)nodeNumbers[0],
979 if (sharpCorners && l > 0)
980 handleEdgeCornerInteractions(
mesh->lines.size() - 1,
982 cellIt.getIndices());
992 mesh->triangles.shrink_to_fit();
993 mesh->nodes.shrink_to_fit();
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:28