24 using LevelSetsType = std::vector<SmartPointer<Domain<T, D>>>;
25 LevelSetsType levelSets;
26 SmartPointer<Mesh<T>> multiMesh =
nullptr;
27 SmartPointer<MaterialMap> materialMap =
nullptr;
28 bool generateSharpCorners =
false;
29 T bottomExtension = 0.0;
30 double minNodeDistanceFactor = 0.05;
36 if (levelSets.empty()) {
37 VIENNACORE_LOG_WARNING(
38 "ToHullMesh: No level sets provided! Hull mesh will be empty.");
42 if (multiMesh ==
nullptr) {
43 VIENNACORE_LOG_WARNING(
"ToHullMesh: No mesh provided!");
47 auto &grid = levelSets[0]->getGrid();
48 double gridDelta = grid.getGridDelta();
52 for (
int i = 0; i <
D; ++i) {
53 totalMinimum[i] = std::numeric_limits<int>::max();
54 totalMaximum[i] = std::numeric_limits<int>::lowest();
57 for (
auto &
ls : levelSets) {
58 if (
ls->getNumberOfPoints() == 0)
60 auto &dom =
ls->getDomain();
61 auto &grd =
ls->getGrid();
62 for (
int i = 0; i <
D; ++i) {
63 int minP = (grd.isNegBoundaryInfinite(i)) ? dom.getMinRunBreak(i) - 1
64 : grd.getMinBounds(i);
65 int maxP = (grd.isPosBoundaryInfinite(i)) ? dom.getMaxRunBreak(i) + 1
66 : grd.getMaxBounds(i);
67 if (minP < totalMinimum[i])
68 totalMinimum[i] = minP;
69 if (maxP > totalMaximum[i])
70 totalMaximum[i] = maxP;
76 for (
auto &
ls : levelSets)
85 multiMesh->cellData.eraseVectorData(0);
88 if constexpr (
D == 2) {
89 auto capBoundary = [&](
int boundaryAxis,
double fixedValue,
90 double rangeMin,
double rangeMax) {
91 int varyingAxis = 1 - boundaryAxis;
92 std::vector<std::pair<double, unsigned>> boundaryNodes;
93 const double tolerance = 1e-6 * gridDelta;
95 for (
unsigned i = 0; i < multiMesh->nodes.size(); ++i) {
96 if (std::abs(multiMesh->nodes[i][boundaryAxis] - fixedValue) <
98 if (multiMesh->nodes[i][varyingAxis] >= rangeMin - tolerance &&
99 multiMesh->nodes[i][varyingAxis] <= rangeMax + tolerance) {
100 boundaryNodes.push_back({multiMesh->nodes[i][varyingAxis], i});
105 auto addCorner = [&](
double val) {
106 for (
const auto &bn : boundaryNodes)
107 if (std::abs(bn.first - val) < tolerance)
110 if (levelSets.back()->getNumberOfPoints() == 0)
113 viennahrle::Index<D> idx;
114 idx[boundaryAxis] = std::round(fixedValue / gridDelta);
115 idx[varyingAxis] = std::round(val / gridDelta);
117 auto &grid = levelSets.back()->getGrid();
118 if (grid.isOutsideOfDomain(idx))
119 idx = grid.globalIndices2LocalIndices(idx);
122 auto minGrid = grid.getMinGridPoint();
123 auto maxGrid = grid.getMaxGridPoint();
124 for (
int i = 0; i <
D; ++i) {
125 if (idx[i] < minGrid[i])
127 if (idx[i] > maxGrid[i])
131 viennahrle::ConstDenseIterator<hrleDomainType> it(
132 levelSets.back()->getDomain());
134 if (it.getValue() > 0.)
138 pos[boundaryAxis] = fixedValue;
139 pos[varyingAxis] = val;
141 unsigned id = multiMesh->insertNextNode(pos);
142 boundaryNodes.push_back({val,
id});
147 std::sort(boundaryNodes.begin(), boundaryNodes.end());
149 if (boundaryNodes.size() < 2)
152 auto matIds = multiMesh->cellData.getScalarData(
"MaterialIds");
153 for (
size_t i = 0; i < boundaryNodes.size() - 1; ++i) {
155 (boundaryNodes[i].first + boundaryNodes[i + 1].first) * 0.5;
158 int boundaryCandidate = -1;
159 viennahrle::Index<D> queryIdx;
160 queryIdx[boundaryAxis] = std::round(fixedValue / gridDelta);
161 queryIdx[varyingAxis] = std::round(mid / gridDelta);
163 for (
unsigned l = 0; l < levelSets.size(); ++l) {
164 viennahrle::ConstDenseIterator<hrleDomainType> it(
165 levelSets[l]->getDomain());
166 it.goToIndices(queryIdx);
167 double val = it.getValue();
168 if (val < -1e-6 * gridDelta) {
169 matId = (materialMap) ? materialMap->getMaterialId(l) : (int)l;
172 if (val <= 1e-6 * gridDelta && boundaryCandidate == -1) {
174 (materialMap) ? materialMap->getMaterialId(l) : (int)l;
178 matId = boundaryCandidate;
181 multiMesh->insertNextLine(
182 {
static_cast<unsigned>(boundaryNodes[i].second),
183 static_cast<unsigned>(boundaryNodes[i + 1].second)});
185 matIds->push_back(matId);
190 double minX = totalMinimum[0] * gridDelta;
191 double maxX = totalMaximum[0] * gridDelta;
192 double minY = totalMinimum[1] * gridDelta - bottomExtension;
193 double maxY = totalMaximum[1] * gridDelta;
195 capBoundary(0, minX, minY, maxY);
196 capBoundary(0, maxX, minY, maxY);
197 capBoundary(1, minY, minX, maxX);
198 capBoundary(1, maxY, minX, maxX);
205 using EdgeKey = std::tuple<int, int, int, int>;
206 std::map<EdgeKey, unsigned> edgeToNode;
207 std::map<std::tuple<int, int, int>,
unsigned> existingGridNodes;
208 const double invGridDelta = 1.0 / gridDelta;
210 for (
unsigned i = 0; i < multiMesh->nodes.size(); ++i) {
211 const auto &pos = multiMesh->nodes[i];
215 for (
int d = 0; d < 3; ++d) {
216 double val = pos[d] * invGridDelta;
217 intCoords[d] = std::round(val);
218 isInt[d] = std::abs(val - intCoords[d]) < 1e-4;
224 existingGridNodes[{intCoords[0], intCoords[1], intCoords[2]}] = i;
225 }
else if (intCount == 2) {
227 for (
int d = 0; d < 3; ++d)
231 for (
int d = 0; d < 3; ++d) {
233 start[d] = std::floor(pos[d] * invGridDelta);
235 start[d] = intCoords[d];
237 edgeToNode[{dir, start[0], start[1], start[2]}] = i;
241 std::map<std::tuple<int, int, int>,
unsigned> gridNodeMap;
242 auto getGridNode = [&](
int i,
int j,
int k) {
243 std::tuple<int, int, int> key = {i, j, k};
244 auto it = gridNodeMap.find(key);
245 if (it != gridNodeMap.end())
247 auto it2 = existingGridNodes.find(key);
248 if (it2 != existingGridNodes.end())
252 pos[0] = i * gridDelta;
253 pos[1] = j * gridDelta;
254 pos[2] = k * gridDelta;
255 unsigned id = multiMesh->insertNextNode(pos);
256 gridNodeMap[key] = id;
262 const std::vector<std::vector<int>> msTris = {{},
269 {0, 1, 2, 0, 2, 6, 0, 6, 7},
273 {0, 1, 5, 0, 5, 6, 0, 6, 3},
275 {0, 4, 5, 0, 5, 2, 0, 2, 3},
276 {1, 2, 3, 1, 3, 7, 1, 7, 4},
279 for (
int d = 0; d <
D; ++d) {
280 for (
int side = 0; side < 2; ++side) {
281 int boundaryCoord = (side == 0) ? totalMinimum[d] : totalMaximum[d];
287 int uMin = totalMinimum[u];
288 int uMax = totalMaximum[u];
289 int vMin = (
D == 3) ? totalMinimum[v] : 0;
290 int vMax = (
D == 3) ? totalMaximum[v] : 0;
292 bool flip = (side == 0);
294 for (
int i = uMin; i < uMax; ++i) {
295 for (
int j = vMin; j < ((
D == 3) ? vMax : 1); ++j) {
296 int cI[4] = {i, i + 1, i + 1, i};
297 int cJ[4] = {j, j, j + 1, j + 1};
302 for (
int k = 0; k < 4; ++k) {
303 viennahrle::Index<D> idx;
304 idx[d] = boundaryCoord;
308 for (
unsigned l = 0; l < levelSets.size(); ++l) {
309 viennahrle::ConstDenseIterator<hrleDomainType> it(
310 levelSets[l]->getDomain());
312 if (it.getValue() <= 0) {
315 matId = (materialMap) ? materialMap->getMaterialId(l)
327 auto &tris = msTris[mask];
328 for (
size_t t = 0; t < tris.size(); t += 3) {
330 for (
int k = 0; k < 3; ++k) {
331 int pt = tris[t + k];
334 coords[d] = boundaryCoord;
337 nodes[k] = getGridNode(coords[0], coords[1], coords[2]);
348 }
else if (pt == 5) {
352 }
else if (pt == 6) {
361 nodes[k] = edgeToNode[{edgeDir, s[0], s[1], s[2]}];
364 edgeToNode.find({0, 0, 0, 0}) == edgeToNode.end()) {
367 pos[d] = ei * gridDelta;
369 pos[u] = (i + 0.5) * gridDelta;
370 pos[v] = j * gridDelta;
371 }
else if (pt == 5) {
372 pos[u] = (i + 1) * gridDelta;
373 pos[v] = (j + 0.5) * gridDelta;
374 }
else if (pt == 6) {
375 pos[u] = (i + 0.5) * gridDelta;
376 pos[v] = (j + 1) * gridDelta;
378 pos[u] = i * gridDelta;
379 pos[v] = (j + 0.5) * gridDelta;
381 nodes[k] = multiMesh->insertNextNode(pos);
387 std::swap(nodes[1], nodes[2]);
389 multiMesh->insertNextTriangle({nodes[0], nodes[1], nodes[2]});
390 auto matIds = multiMesh->cellData.getScalarData(
"MaterialIds");
392 matIds->push_back(matId);
408 multiMesh = passedMesh;
409 levelSets.push_back(levelSet);
413 std::vector<SmartPointer<
Domain<T, D>>>
const &levelSetsVector) {
414 multiMesh = passedMesh;
415 levelSets = levelSetsVector;
422 levelSets.push_back(levelSet);
428 generateSharpCorners = passedSharpCorners;
432 materialMap = passedMaterialMap;
437 VIENNACORE_LOG_WARNING(
438 "ToHullMesh: Bottom extension is only applicable for 2D meshes. "
443 VIENNACORE_LOG_WARNING(
"ToHullMesh: Negative bottom extension is not "
444 "allowed. Setting to 0.");
447 bottomExtension = extension;
453 VIENNACORE_LOG_WARNING(
"ToHullMesh: minNodeDistanceFactor must be "
454 "positive. Ignoring value.");
457 minNodeDistanceFactor = factor;