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;
35 if (levelSets.empty()) {
36 VIENNACORE_LOG_WARNING(
37 "ToHullMesh: No level sets provided! Hull mesh will be empty.");
41 if (multiMesh ==
nullptr) {
42 VIENNACORE_LOG_WARNING(
"ToHullMesh: No mesh provided!");
46 auto &grid = levelSets[0]->getGrid();
47 double gridDelta = grid.getGridDelta();
51 for (
int i = 0; i <
D; ++i) {
52 totalMinimum[i] = std::numeric_limits<int>::max();
53 totalMaximum[i] = std::numeric_limits<int>::lowest();
56 for (
auto &
ls : levelSets) {
57 if (
ls->getNumberOfPoints() == 0)
59 auto &dom =
ls->getDomain();
60 auto &grd =
ls->getGrid();
61 for (
int i = 0; i <
D; ++i) {
62 int minP = (grd.isNegBoundaryInfinite(i)) ? dom.getMinRunBreak(i) - 1
63 : grd.getMinBounds(i);
64 int maxP = (grd.isPosBoundaryInfinite(i)) ? dom.getMaxRunBreak(i) + 1
65 : grd.getMaxBounds(i);
66 if (minP < totalMinimum[i])
67 totalMinimum[i] = minP;
68 if (maxP > totalMaximum[i])
69 totalMaximum[i] = maxP;
75 for (
auto &
ls : levelSets)
84 multiMesh->cellData.eraseVectorData(0);
87 if constexpr (
D == 2) {
88 auto capBoundary = [&](
int boundaryAxis,
double fixedValue,
89 double rangeMin,
double rangeMax) {
90 int varyingAxis = 1 - boundaryAxis;
91 std::vector<std::pair<double, unsigned>> boundaryNodes;
92 double tolerance = 1e-6 * gridDelta;
94 for (
unsigned i = 0; i < multiMesh->nodes.size(); ++i) {
95 if (std::abs(multiMesh->nodes[i][boundaryAxis] - fixedValue) <
97 if (multiMesh->nodes[i][varyingAxis] >= rangeMin - tolerance &&
98 multiMesh->nodes[i][varyingAxis] <= rangeMax + tolerance) {
99 boundaryNodes.push_back({multiMesh->nodes[i][varyingAxis], i});
104 auto addCorner = [&](
double val) {
105 for (
const auto &bn : boundaryNodes)
106 if (std::abs(bn.first - val) < tolerance)
109 if (levelSets.back()->getNumberOfPoints() == 0)
112 viennahrle::Index<D> idx;
113 idx[boundaryAxis] = std::round(fixedValue / gridDelta);
114 idx[varyingAxis] = std::round(val / gridDelta);
116 auto &grid = levelSets.back()->getGrid();
117 if (grid.isOutsideOfDomain(idx))
118 idx = grid.globalIndices2LocalIndices(idx);
121 auto minGrid = grid.getMinGridPoint();
122 auto maxGrid = grid.getMaxGridPoint();
123 for (
int i = 0; i <
D; ++i) {
124 if (idx[i] < minGrid[i])
126 if (idx[i] > maxGrid[i])
130 viennahrle::ConstDenseIterator<hrleDomainType> it(
131 levelSets.back()->getDomain());
133 if (it.getValue() > 0.)
137 pos[boundaryAxis] = fixedValue;
138 pos[varyingAxis] = val;
140 unsigned id = multiMesh->insertNextNode(pos);
141 boundaryNodes.push_back({val,
id});
146 std::sort(boundaryNodes.begin(), boundaryNodes.end());
148 if (boundaryNodes.size() < 2)
151 auto matIds = multiMesh->cellData.getScalarData(
"MaterialIds");
152 for (
size_t i = 0; i < boundaryNodes.size() - 1; ++i) {
154 (boundaryNodes[i].first + boundaryNodes[i + 1].first) * 0.5;
157 int boundaryCandidate = -1;
158 viennahrle::Index<D> queryIdx;
159 queryIdx[boundaryAxis] = std::round(fixedValue / gridDelta);
160 queryIdx[varyingAxis] = std::round(mid / gridDelta);
162 for (
unsigned l = 0; l < levelSets.size(); ++l) {
163 viennahrle::ConstDenseIterator<hrleDomainType> it(
164 levelSets[l]->getDomain());
165 it.goToIndices(queryIdx);
166 double val = it.getValue();
167 if (val < -1e-6 * gridDelta) {
168 matId = (materialMap) ? materialMap->getMaterialId(l) : (int)l;
171 if (val <= 1e-6 * gridDelta && boundaryCandidate == -1) {
173 (materialMap) ? materialMap->getMaterialId(l) : (int)l;
177 matId = boundaryCandidate;
180 multiMesh->insertNextLine(
181 {
static_cast<unsigned>(boundaryNodes[i].second),
182 static_cast<unsigned>(boundaryNodes[i + 1].second)});
184 matIds->push_back(matId);
189 double minX = totalMinimum[0] * gridDelta;
190 double maxX = totalMaximum[0] * gridDelta;
191 double minY = totalMinimum[1] * gridDelta - bottomExtension;
192 double maxY = totalMaximum[1] * gridDelta;
194 capBoundary(0, minX, minY, maxY);
195 capBoundary(0, maxX, minY, maxY);
196 capBoundary(1, minY, minX, maxX);
197 capBoundary(1, maxY, minX, maxX);
204 using EdgeKey = std::tuple<int, int, int, int>;
205 std::map<EdgeKey, unsigned> edgeToNode;
206 std::map<std::tuple<int, int, int>,
unsigned> existingGridNodes;
207 double invGridDelta = 1.0 / gridDelta;
209 for (
unsigned i = 0; i < multiMesh->nodes.size(); ++i) {
210 const auto &pos = multiMesh->nodes[i];
214 for (
int d = 0; d < 3; ++d) {
215 double val = pos[d] * invGridDelta;
216 intCoords[d] = std::round(val);
217 isInt[d] = std::abs(val - intCoords[d]) < 1e-4;
223 existingGridNodes[{intCoords[0], intCoords[1], intCoords[2]}] = i;
224 }
else if (intCount == 2) {
226 for (
int d = 0; d < 3; ++d)
230 for (
int d = 0; d < 3; ++d) {
232 start[d] = std::floor(pos[d] * invGridDelta);
234 start[d] = intCoords[d];
236 edgeToNode[{dir, start[0], start[1], start[2]}] = i;
240 std::map<std::tuple<int, int, int>,
unsigned> gridNodeMap;
241 auto getGridNode = [&](
int i,
int j,
int k) {
242 std::tuple<int, int, int> key = {i, j, k};
243 auto it = gridNodeMap.find(key);
244 if (it != gridNodeMap.end())
246 auto it2 = existingGridNodes.find(key);
247 if (it2 != existingGridNodes.end())
251 pos[0] = i * gridDelta;
252 pos[1] = j * gridDelta;
253 pos[2] = k * gridDelta;
254 unsigned id = multiMesh->insertNextNode(pos);
255 gridNodeMap[key] = id;
261 const std::vector<std::vector<int>> msTris = {{},
268 {0, 1, 2, 0, 2, 6, 0, 6, 7},
272 {0, 1, 5, 0, 5, 6, 0, 6, 3},
274 {0, 4, 5, 0, 5, 2, 0, 2, 3},
275 {1, 2, 3, 1, 3, 7, 1, 7, 4},
278 for (
int d = 0; d <
D; ++d) {
279 for (
int side = 0; side < 2; ++side) {
280 int boundaryCoord = (side == 0) ? totalMinimum[d] : totalMaximum[d];
286 int uMin = totalMinimum[u];
287 int uMax = totalMaximum[u];
288 int vMin = (
D == 3) ? totalMinimum[v] : 0;
289 int vMax = (
D == 3) ? totalMaximum[v] : 0;
291 bool flip = (side == 0);
293 for (
int i = uMin; i < uMax; ++i) {
294 for (
int j = vMin; j < ((
D == 3) ? vMax : 1); ++j) {
295 int cI[4] = {i, i + 1, i + 1, i};
296 int cJ[4] = {j, j, j + 1, j + 1};
301 for (
int k = 0; k < 4; ++k) {
302 viennahrle::Index<D> idx;
303 idx[d] = boundaryCoord;
307 for (
unsigned l = 0; l < levelSets.size(); ++l) {
308 viennahrle::ConstDenseIterator<hrleDomainType> it(
309 levelSets[l]->getDomain());
311 if (it.getValue() <= 0) {
314 matId = (materialMap) ? materialMap->getMaterialId(l)
326 auto &tris = msTris[mask];
327 for (
size_t t = 0; t < tris.size(); t += 3) {
329 for (
int k = 0; k < 3; ++k) {
330 int pt = tris[t + k];
333 coords[d] = boundaryCoord;
336 nodes[k] = getGridNode(coords[0], coords[1], coords[2]);
347 }
else if (pt == 5) {
351 }
else if (pt == 6) {
360 nodes[k] = edgeToNode[{edgeDir, s[0], s[1], s[2]}];
363 edgeToNode.find({0, 0, 0, 0}) == edgeToNode.end()) {
366 pos[d] = ei * gridDelta;
368 pos[u] = (i + 0.5) * gridDelta;
369 pos[v] = j * gridDelta;
370 }
else if (pt == 5) {
371 pos[u] = (i + 1) * gridDelta;
372 pos[v] = (j + 0.5) * gridDelta;
373 }
else if (pt == 6) {
374 pos[u] = (i + 0.5) * gridDelta;
375 pos[v] = (j + 1) * gridDelta;
377 pos[u] = i * gridDelta;
378 pos[v] = (j + 0.5) * gridDelta;
380 nodes[k] = multiMesh->insertNextNode(pos);
386 std::swap(nodes[1], nodes[2]);
388 multiMesh->insertNextTriangle({nodes[0], nodes[1], nodes[2]});
389 auto matIds = multiMesh->cellData.getScalarData(
"MaterialIds");
391 matIds->push_back(matId);
407 multiMesh = passedMesh;
408 levelSets.push_back(levelSet);
412 std::vector<SmartPointer<
Domain<T, D>>>
const &levelSetsVector) {
413 multiMesh = passedMesh;
414 levelSets = levelSetsVector;
421 levelSets.push_back(levelSet);
427 generateSharpCorners = passedSharpCorners;
431 materialMap = passedMaterialMap;
436 VIENNACORE_LOG_WARNING(
437 "ToHullMesh: Bottom extension is only applicable for 2D meshes. "
442 VIENNACORE_LOG_WARNING(
"ToHullMesh: Negative bottom extension is not "
443 "allowed. Setting to 0.");
446 bottomExtension = extension;