14 using hrleIndex = viennahrle::Index<D>;
29 SmartPointer<MaterialMap> materialMap =
nullptr;
32 struct SharpCornerNode {
34 Vec3D<NumericType> position;
49 int findNearestLowerCorner(
50 unsigned l,
const Vec3D<NumericType> &pos,
51 const std::unordered_map<
unsigned, std::vector<SharpCornerNode>>
53 const hrleIndex &activeGridIdx,
NumericType lsLAtActive) {
58 for (
unsigned m = 0; m < l; ++m) {
59 auto it = sharpCornerNodes.find(m);
60 if (it == sharpCornerNodes.end())
64 viennahrle::ConstSparseIterator<hrleDomainType> mIt(
66 mIt.goToIndices(activeGridIdx);
74 lsMAtActive - lsLAtActive > layerThreshold)
76 for (
const auto &scn : it->second) {
78 for (
int i = 0; i <
D; ++i) {
82 if (dist2 < minDist2) {
93 void splitPreviousMaterialEdge(
unsigned l,
unsigned cornerNodeId,
94 const Vec3D<NumericType> &cornerPos,
95 size_t meshSizeBefore) {
101 for (
size_t lineIdx = 0; lineIdx <
mesh->lines.size(); ++lineIdx) {
106 auto &line =
mesh->lines[lineIdx];
107 unsigned node0 = line[0];
108 unsigned node1 = line[1];
110 auto const &pos0 =
mesh->nodes[node0];
111 auto const &pos1 =
mesh->nodes[node1];
112 auto lineVec = pos1 - pos0;
116 for (
int i = 0; i <
D; ++i) {
117 lineLen2 += lineVec[i] * lineVec[i];
118 dot += (cornerPos[i] - pos0[i]) * lineVec[i];
130 for (
int i = 0; i <
D; ++i) {
131 NumericType d = cornerPos[i] - (pos0[i] + t * lineVec[i]);
143 line[1] = cornerNodeId;
144 mesh->lines.push_back({cornerNodeId, node1});
150 for (
size_t k = meshSizeBefore; k <
mesh->lines.size();) {
156 const auto &rl =
mesh->lines[k];
157 bool isSeg1 = (rl[0] == node0 && rl[1] == cornerNodeId) ||
158 (rl[0] == cornerNodeId && rl[1] == node0);
159 bool isSeg2 = (rl[0] == cornerNodeId && rl[1] == node1) ||
160 (rl[0] == node1 && rl[1] == cornerNodeId);
162 if (isSeg1 || isSeg2) {
163 mesh->lines.erase(
mesh->lines.begin() + k);
175 bool getSegmentIntersection(
const Vec3D<NumericType> &p1,
176 const Vec3D<NumericType> &p2,
177 const Vec3D<NumericType> &p3,
178 const Vec3D<NumericType> &p4,
179 Vec3D<NumericType> &intersection,
186 NumericType denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
190 NumericType ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom;
191 NumericType ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom;
195 intersection[0] = x1 + ua * (x2 - x1);
196 intersection[1] = y1 + ua * (y2 - y1);
197 if constexpr (
D == 3)
213 void handleCellCornerCrossings(
215 const std::unordered_map<
unsigned, std::vector<SharpCornerNode>>
217 unsigned l,
const hrleIndex &cellIndices) {
218 if constexpr (
D != 2)
221 unsigned pNode0 =
mesh->lines[pIdx][0];
222 unsigned pNode1 =
mesh->lines[pIdx][1];
223 Vec3D<NumericType> pp0 =
mesh->nodes[pNode0];
224 Vec3D<NumericType> pp1 =
mesh->nodes[pNode1];
226 for (
unsigned m = 0; m < l; ++m) {
227 auto cit = sharpCornerNodes.find(m);
228 if (cit == sharpCornerNodes.end())
231 for (
const auto &mscn : cit->second) {
234 for (
int i = 0; i <
D; ++i) {
245 size_t edge1Idx = SIZE_MAX, edge2Idx = SIZE_MAX;
246 for (
size_t eIdx = 0; eIdx < pIdx; ++eIdx) {
249 if (
mesh->lines[eIdx][0] != mscn.nodeId &&
250 mesh->lines[eIdx][1] != mscn.nodeId)
252 if (edge1Idx == SIZE_MAX)
254 else if (edge2Idx == SIZE_MAX) {
259 if (edge1Idx == SIZE_MAX || edge2Idx == SIZE_MAX)
263 Vec3D<NumericType> int1{}, int2{};
265 bool cross1 = getSegmentIntersection(
266 pp0, pp1,
mesh->nodes[
mesh->lines[edge1Idx][0]],
267 mesh->nodes[
mesh->lines[edge1Idx][1]], int1, t1);
268 bool cross2 = getSegmentIntersection(
269 pp0, pp1,
mesh->nodes[
mesh->lines[edge2Idx][0]],
270 mesh->nodes[
mesh->lines[edge2Idx][1]], int2, t2);
272 if (!cross1 || !cross2)
278 std::swap(int1, int2);
279 std::swap(edge1Idx, edge2Idx);
293 unsigned e1n0 =
mesh->lines[edge1Idx][0];
294 unsigned e1n1 =
mesh->lines[edge1Idx][1];
295 unsigned e2n0 =
mesh->lines[edge2Idx][0];
296 unsigned e2n1 =
mesh->lines[edge2Idx][1];
300 mesh->lines[edge1Idx][1] = intNode1;
302 mesh->lines.push_back({intNode1, e1n1});
309 mesh->lines[edge2Idx][1] = intNode2;
311 mesh->lines.push_back({intNode2, e2n1});
321 mesh->lines[pIdx][1] = intNode1;
323 mesh->lines.push_back({intNode2, pNode1});
342 void handleEdgeCornerInteractions(
344 const std::unordered_map<
unsigned, std::vector<SharpCornerNode>>
346 unsigned l,
const hrleIndex &cellIndices) {
347 if constexpr (
D != 2)
349 if (l == 0 || lineIdx >=
mesh->lines.size())
352 unsigned node0 =
mesh->lines[lineIdx][0];
353 unsigned node1 =
mesh->lines[lineIdx][1];
355 Vec3D<NumericType> pos0 =
mesh->nodes[node0];
356 Vec3D<NumericType> pos1 =
mesh->nodes[node1];
357 auto lineVec = pos1 - pos0;
360 for (
int i = 0; i <
D; ++i)
361 lineLen2 += lineVec[i] * lineVec[i];
365 for (
unsigned m = 0; m < l; ++m) {
366 auto it = sharpCornerNodes.find(m);
367 if (it == sharpCornerNodes.end())
370 for (
const auto &scn : it->second) {
373 if (scn.nodeId != node0 && scn.nodeId != node1) {
375 for (
int i = 0; i <
D; ++i)
376 dot += (scn.position[i] - pos0[i]) * lineVec[i];
380 for (
int i = 0; i <
D; ++i) {
381 NumericType d = scn.position[i] - (pos0[i] + t * lineVec[i]);
385 unsigned cId = scn.nodeId;
391 auto &line =
mesh->lines[lineIdx];
392 if (keep0 && keep1) {
394 mesh->lines.push_back({cId, node1});
406 mesh->lines.erase(
mesh->lines.begin() + lineIdx);
417 for (
int i = 0; i <
D; ++i) {
427 size_t edge1Idx = SIZE_MAX, edge2Idx = SIZE_MAX;
428 for (
size_t eIdx = 0; eIdx < lineIdx; ++eIdx) {
431 if (
mesh->lines[eIdx][0] != scn.nodeId &&
432 mesh->lines[eIdx][1] != scn.nodeId)
434 if (edge1Idx == SIZE_MAX)
436 else if (edge2Idx == SIZE_MAX) {
441 if (edge1Idx == SIZE_MAX || edge2Idx == SIZE_MAX)
444 Vec3D<NumericType> int1{}, int2{};
446 bool cross1 = getSegmentIntersection(
447 pos0, pos1,
mesh->nodes[
mesh->lines[edge1Idx][0]],
448 mesh->nodes[
mesh->lines[edge1Idx][1]], int1, t1);
449 bool cross2 = getSegmentIntersection(
450 pos0, pos1,
mesh->nodes[
mesh->lines[edge2Idx][0]],
451 mesh->nodes[
mesh->lines[edge2Idx][1]], int2, t2);
452 if (!cross1 || !cross2)
457 std::swap(int1, int2);
458 std::swap(edge1Idx, edge2Idx);
470 unsigned e1n0 =
mesh->lines[edge1Idx][0],
471 e1n1 =
mesh->lines[edge1Idx][1];
472 unsigned e2n0 =
mesh->lines[edge2Idx][0],
473 e2n1 =
mesh->lines[edge2Idx][1];
476 mesh->lines[edge1Idx][1] = intNode1;
478 mesh->lines.push_back({intNode1, e1n1});
484 mesh->lines[edge2Idx][1] = intNode2;
486 mesh->lines.push_back({intNode2, e2n1});
492 mesh->lines[lineIdx][1] = intNode1;
494 mesh->lines.push_back({intNode2, node1});
510 snapSharpCorners(
unsigned l,
size_t meshSizeBefore,
511 std::unordered_map<
unsigned, std::vector<SharpCornerNode>>
513 const hrleIndex &activeGridIdx,
NumericType lsLAtActive,
514 const hrleIndex &cellIndices) {
516 unsigned cornerNodeId = cornerPair.first;
517 Vec3D<NumericType> cornerPos = cornerPair.second;
520 (l > 0) ? findNearestLowerCorner(l, cornerPos, sharpCornerNodes,
521 activeGridIdx, lsLAtActive)
524 if (snapToNodeId >= 0) {
526 if (snapToNodeId != (
int)cornerNodeId) {
527 if constexpr (
D == 2) {
529 for (
size_t i =
mesh->lines.size(); i-- > meshSizeBefore;) {
530 for (
int j = 0; j < 2; ++j) {
531 if (
mesh->lines[i][j] == cornerNodeId)
532 mesh->lines[i][j] = snapToNodeId;
535 unsigned n0 =
mesh->lines[i][0];
536 unsigned n1 =
mesh->lines[i][1];
540 if (n0 == n1 || isDuplicate) {
541 mesh->lines.erase(
mesh->lines.begin() + i);
550 sharpCornerNodes[l].push_back(
551 {
static_cast<unsigned>(snapToNodeId), cornerPos});
554 sharpCornerNodes[l].push_back({cornerNodeId, cornerPos});
555 splitPreviousMaterialEdge(l, cornerNodeId, cornerPos, meshSizeBefore);
562 size_t sizeNow =
mesh->lines.size();
563 std::vector<std::pair<unsigned, Vec3D<NumericType>>> endpointsToSplit;
564 for (
size_t pIdx = meshSizeBefore; pIdx < sizeNow; ++pIdx) {
567 unsigned n0 =
mesh->lines[pIdx][0];
568 unsigned n1 =
mesh->lines[pIdx][1];
569 if (n0 != cornerNodeId)
570 endpointsToSplit.push_back({n0,
mesh->nodes[n0]});
571 if (n1 != cornerNodeId)
572 endpointsToSplit.push_back({n1,
mesh->nodes[n1]});
574 for (
auto &[nodeId, pos] : endpointsToSplit)
575 splitPreviousMaterialEdge(l, nodeId, pos, meshSizeBefore);
577 sizeNow =
mesh->lines.size();
578 for (
size_t pIdx = meshSizeBefore; pIdx < sizeNow; ++pIdx) {
581 handleCellCornerCrossings(pIdx, sharpCornerNodes, l, cellIndices);
593 double minNodeDistFactor = 0.01,
double eps = 1e-12)
595 minNodeDistFactor, eps) {}
598 std::vector<SmartPointer<lsDomainType>>
const &passedLevelSets,
600 double minNodeDistFactor = 0.01,
double eps = 1e-12)
607 double minNodeDistFactor = 0.01,
double eps = 1e-12)
619 materialMap = passedMaterialMap;
624 Logger::getInstance()
625 .addError(
"No level set was passed to ToMultiSurfaceMesh.")
629 if (
mesh ==
nullptr) {
630 Logger::getInstance()
631 .addError(
"No mesh was passed to ToMultiSurfaceMesh.")
637 for (
unsigned i = 0; i <
D; ++i) {
638 mesh->minimumExtent[i] = std::numeric_limits<NumericType>::max();
639 mesh->maximumExtent[i] = std::numeric_limits<NumericType>::lowest();
642 using nodeContainerType = std::map<viennahrle::Index<D>,
unsigned>;
644 nodeContainerType nodes[
D];
645 nodeContainerType faceNodes[
D];
646 nodeContainerType cornerNodes;
653 const bool useMaterialMap = materialMap !=
nullptr;
656 if (sharpCorners &&
D == 3) {
657 Logger::getInstance()
658 .addWarning(
"Sharp corner generation in 3D is experimental and may "
659 "produce suboptimal meshes. Use with caution.")
664 std::vector<viennahrle::ConstSparseCellIterator<hrleDomainType>> cellIts;
666 cellIts.emplace_back(
ls->getDomain());
669 std::unordered_map<unsigned, std::vector<SharpCornerNode>> sharpCornerNodes;
671 for (
unsigned l = 0; l <
levelSets.size(); l++) {
682 normalCalculator.
setMaxValue(std::numeric_limits<NumericType>::max());
683 normalCalculator.
apply();
688 viennahrle::ConstSparseIterator<hrleDomainType> valueIt(
692 for (
auto cellIt = cellIts[l]; !cellIt.isFinished(); cellIt.next()) {
693 for (
int u = 0; u <
D; u++) {
694 while (!nodes[u].empty() &&
695 nodes[u].begin()->first <
696 viennahrle::Index<D>(cellIt.getIndices()))
697 nodes[u].erase(nodes[u].begin());
699 if constexpr (
D == 3) {
700 while (!faceNodes[u].empty() &&
701 faceNodes[u].begin()->first <
702 viennahrle::Index<D>(cellIt.getIndices()))
703 faceNodes[u].erase(faceNodes[u].begin());
706 while (!cornerNodes.empty() &&
707 cornerNodes.begin()->first <
708 viennahrle::Index<D>(cellIt.getIndices()))
709 cornerNodes.erase(cornerNodes.begin());
715 for (
int i = 0; i < (1 <<
D); i++) {
716 if (cellIt.getCorner(i).getValue() >=
NumericType(0))
723 if (signs == (1 << (1 <<
D)) - 1)
728 bool atMaterialBoundary =
false;
729 unsigned touchingMaterial = 0;
730 if (sharpCorners && l > 0) {
731 touchingMaterial = l - 1;
732 viennahrle::ConstSparseIterator<hrleDomainType> prevIt(
733 levelSets[touchingMaterial]->getDomain());
734 for (
int i = 0; i < (1 <<
D); ++i) {
735 hrleIndex cornerIdx =
736 cellIt.getIndices() + viennahrle::BitMaskToIndex<D>(i);
737 prevIt.goToIndices(cornerIdx);
738 if (prevIt.getValue() <=
740 atMaterialBoundary =
true;
747 bool perfectCornerFound =
false;
753 for (
int i = 0; i < (1 <<
D); ++i) {
776 size_t meshSizeBefore = 0;
777 if constexpr (
D == 2) {
778 meshSizeBefore =
mesh->lines.size();
781 posMask, nodes,
nullptr, valueIt);
782 }
else if constexpr (
D == 3) {
783 if (countNeg == 2 || countPos == 2) {
785 cellIt, countNeg, countPos, negMask, posMask, nodes,
786 faceNodes,
nullptr, valueIt);
787 }
else if (countNeg == 1 || countPos == 1) {
789 cellIt, countNeg, countPos, negMask, posMask, nodes,
790 cornerNodes, faceNodes,
nullptr, valueIt);
791 }
else if (countNeg == 3 || countPos == 3) {
793 cellIt, countNeg, countPos, negMask, posMask, nodes,
794 faceNodes,
nullptr, valueIt);
806 for (
int i = 0; i < (1 <<
D); ++i)
807 if ((negMask >> i) & 1) {
811 }
else if (countPos == 1) {
812 for (
int i = 0; i < (1 <<
D); ++i)
813 if ((posMask >> i) & 1) {
818 hrleIndex activeGridIdx = cellIt.getIndices();
820 if (activeBit >= 0) {
821 activeGridIdx += viennahrle::BitMaskToIndex<D>(activeBit);
822 lsLAtActive = cellIt.getCorner(activeBit).getValue();
824 snapSharpCorners(l, meshSizeBefore, sharpCornerNodes, activeGridIdx,
825 lsLAtActive, cellIt.getIndices());
829 if (perfectCornerFound)
832 if constexpr (
D == 3) {
834 for (
int axis = 0; axis < 3; ++axis) {
835 for (
int d = 0; d < 2; ++d) {
836 viennahrle::Index<D> faceKey(cellIt.getIndices());
840 auto it = faceNodes[axis].find(faceKey);
841 if (it != faceNodes[axis].end()) {
842 const unsigned faceNodeId = it->second;
843 const int *Triangles =
846 for (; Triangles[0] != -1; Triangles += 3) {
847 std::vector<unsigned> face_edge_nodes;
848 for (
int i = 0; i < 3; ++i) {
849 int edge = Triangles[i];
853 (((c0 >> axis) & 1) == d) && (((c1 >> axis) & 1) == d);
855 face_edge_nodes.push_back(
856 this->
getNode(cellIt, edge, nodes,
nullptr));
859 if (face_edge_nodes.size() == 2) {
861 face_edge_nodes[0], face_edge_nodes[1], faceNodeId});
870 const int *Triangles;
871 if constexpr (
D == 2) {
877 for (; Triangles[0] != -1; Triangles +=
D) {
878 std::array<unsigned, D> nodeNumbers;
881 for (
int n = 0; n <
D; n++) {
882 const int edge = Triangles[n];
884 unsigned p0 = this->
corner0[edge];
890 viennahrle::Index<D> d(cellIt.getIndices());
891 auto p0B = viennahrle::BitMaskToIndex<D>(p0);
894 auto nodeIt = nodes[dir].find(d);
895 if (nodeIt != nodes[dir].end()) {
896 nodeNumbers[n] = nodeIt->second;
900 if (sharpCorners && l > 0 && atMaterialBoundary) {
901 unsigned cachedNodeId = nodeIt->second;
903 auto it = sharpCornerNodes.find(touchingMaterial);
904 if (it != sharpCornerNodes.end()) {
905 for (
const auto &scn : it->second) {
906 if (scn.nodeId == cachedNodeId) {
908 auto &lCorners = sharpCornerNodes[l];
909 bool alreadyInherited =
false;
910 for (
const auto &existing : lCorners) {
911 if (existing.nodeId == cachedNodeId) {
912 alreadyInherited =
true;
916 if (!alreadyInherited)
917 lCorners.push_back({cachedNodeId, scn.position});
927 Vec3D<NumericType> snapPos{};
928 if (sharpCorners && l > 0 && atMaterialBoundary) {
929 auto tmIt = sharpCornerNodes.find(touchingMaterial);
930 if (tmIt != sharpCornerNodes.end() && !tmIt->second.empty()) {
931 auto [nodePos, pointId] =
936 for (
const auto &scn : tmIt->second) {
938 for (
int i = 0; i <
D; ++i) {
942 if (dist2 < minDist2) {
944 snapNodeId = scn.nodeId;
945 snapPos = scn.position;
951 if (snapNodeId >= 0) {
952 nodeNumbers[n] = snapNodeId;
953 nodes[dir][d] = snapNodeId;
955 sharpCornerNodes[l].push_back(
956 {
static_cast<unsigned>(snapNodeId), snapPos});
958 nodeNumbers[n] = this->
getNode(cellIt, edge, nodes,
nullptr);
966 if (sharpCorners && l > 0) {
967 handleEdgeCornerInteractions(
mesh->lines.size() - 1,
969 cellIt.getIndices());
976 this->
scaleMesh(levelSets.front()->getGrid().getGridDelta());
982 mesh->triangles.shrink_to_fit();
983 mesh->nodes.shrink_to_fit();
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:27