ViennaLS
Loading...
Searching...
No Matches
lsToHullMesh.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <hrleDenseIterator.hpp>
4#include <map>
5#include <tuple>
6
7#include <lsDomain.hpp>
8#include <lsMaterialMap.hpp>
11#include <unordered_map>
12#include <utility>
13
14namespace viennals {
15
16using namespace viennacore;
17
22template <class T, int D> class ToHullMesh {
23 typedef typename Domain<T, D>::DomainType hrleDomainType;
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;
31
35 void generateHull() {
36 if (levelSets.empty()) {
37 VIENNACORE_LOG_WARNING(
38 "ToHullMesh: No level sets provided! Hull mesh will be empty.");
39 return;
40 }
41
42 if (multiMesh == nullptr) {
43 VIENNACORE_LOG_WARNING("ToHullMesh: No mesh provided!");
44 return;
45 }
46
47 auto &grid = levelSets[0]->getGrid();
48 double gridDelta = grid.getGridDelta();
49
50 int totalMinimum[D];
51 int totalMaximum[D];
52 for (int i = 0; i < D; ++i) {
53 totalMinimum[i] = std::numeric_limits<int>::max();
54 totalMaximum[i] = std::numeric_limits<int>::lowest();
55 }
56
57 for (auto &ls : levelSets) {
58 if (ls->getNumberOfPoints() == 0)
59 continue;
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;
71 }
72 }
73
74 // Generate multi-surface mesh with sharp corners
75 ToMultiSurfaceMesh<T, D> converter(minNodeDistanceFactor, 1e-12);
76 for (auto &ls : levelSets)
77 converter.insertNextLevelSet(ls);
78 if (materialMap)
79 converter.setMaterialMap(materialMap);
80 converter.setMesh(multiMesh);
81 converter.setSharpCorners(generateSharpCorners);
82 converter.apply();
83
84 // remove normals from cell data
85 multiMesh->cellData.eraseVectorData(0);
86
87 // Add closed boundary caps
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;
94
95 for (unsigned i = 0; i < multiMesh->nodes.size(); ++i) {
96 if (std::abs(multiMesh->nodes[i][boundaryAxis] - fixedValue) <
97 tolerance) {
98 if (multiMesh->nodes[i][varyingAxis] >= rangeMin - tolerance &&
99 multiMesh->nodes[i][varyingAxis] <= rangeMax + tolerance) {
100 boundaryNodes.push_back({multiMesh->nodes[i][varyingAxis], i});
101 }
102 }
103 }
104
105 auto addCorner = [&](double val) {
106 for (const auto &bn : boundaryNodes)
107 if (std::abs(bn.first - val) < tolerance)
108 return;
109
110 if (levelSets.back()->getNumberOfPoints() == 0)
111 return;
112
113 viennahrle::Index<D> idx;
114 idx[boundaryAxis] = std::round(fixedValue / gridDelta);
115 idx[varyingAxis] = std::round(val / gridDelta);
116
117 auto &grid = levelSets.back()->getGrid();
118 if (grid.isOutsideOfDomain(idx))
119 idx = grid.globalIndices2LocalIndices(idx);
120
121 // Clamp to grid bounds to ensure iterator safety
122 auto minGrid = grid.getMinGridPoint();
123 auto maxGrid = grid.getMaxGridPoint();
124 for (int i = 0; i < D; ++i) {
125 if (idx[i] < minGrid[i])
126 idx[i] = minGrid[i];
127 if (idx[i] > maxGrid[i])
128 idx[i] = maxGrid[i];
129 }
130
131 viennahrle::ConstDenseIterator<hrleDomainType> it(
132 levelSets.back()->getDomain());
133 it.goToIndices(idx);
134 if (it.getValue() > 0.)
135 return;
136
137 Vec3D<T> pos;
138 pos[boundaryAxis] = fixedValue;
139 pos[varyingAxis] = val;
140 pos[2] = 0;
141 unsigned id = multiMesh->insertNextNode(pos);
142 boundaryNodes.push_back({val, id});
143 };
144 addCorner(rangeMin);
145 addCorner(rangeMax);
146
147 std::sort(boundaryNodes.begin(), boundaryNodes.end());
148
149 if (boundaryNodes.size() < 2)
150 return;
151
152 auto matIds = multiMesh->cellData.getScalarData("MaterialIds");
153 for (size_t i = 0; i < boundaryNodes.size() - 1; ++i) {
154 double mid =
155 (boundaryNodes[i].first + boundaryNodes[i + 1].first) * 0.5;
156
157 int matId = -1;
158 int boundaryCandidate = -1;
159 viennahrle::Index<D> queryIdx;
160 queryIdx[boundaryAxis] = std::round(fixedValue / gridDelta);
161 queryIdx[varyingAxis] = std::round(mid / gridDelta);
162
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;
170 break;
171 }
172 if (val <= 1e-6 * gridDelta && boundaryCandidate == -1) {
173 boundaryCandidate =
174 (materialMap) ? materialMap->getMaterialId(l) : (int)l;
175 }
176 }
177 if (matId == -1)
178 matId = boundaryCandidate;
179
180 if (matId != -1) {
181 multiMesh->insertNextLine(
182 {static_cast<unsigned>(boundaryNodes[i].second),
183 static_cast<unsigned>(boundaryNodes[i + 1].second)});
184 if (matIds)
185 matIds->push_back(matId);
186 }
187 }
188 };
189
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;
194
195 capBoundary(0, minX, minY, maxY);
196 capBoundary(0, maxX, minY, maxY);
197 capBoundary(1, minY, minX, maxX);
198 capBoundary(1, maxY, minX, maxX);
199
200 } else {
201 // 3D implementation: Marching Squares on boundary faces
202
203 // Map to find existing nodes on grid edges/corners
204 // EdgeKey: (dir, i, j, k) where dir is edge direction (0=x, 1=y, 2=z)
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;
209
210 for (unsigned i = 0; i < multiMesh->nodes.size(); ++i) {
211 const auto &pos = multiMesh->nodes[i];
212 int intCoords[3];
213 bool isInt[3];
214 int intCount = 0;
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;
219 if (isInt[d])
220 intCount++;
221 }
222
223 if (intCount == 3) {
224 existingGridNodes[{intCoords[0], intCoords[1], intCoords[2]}] = i;
225 } else if (intCount == 2) {
226 int dir = -1;
227 for (int d = 0; d < 3; ++d)
228 if (!isInt[d])
229 dir = d;
230 int start[3];
231 for (int d = 0; d < 3; ++d) {
232 if (d == dir)
233 start[d] = std::floor(pos[d] * invGridDelta);
234 else
235 start[d] = intCoords[d];
236 }
237 edgeToNode[{dir, start[0], start[1], start[2]}] = i;
238 }
239 }
240
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())
246 return it->second;
247 auto it2 = existingGridNodes.find(key);
248 if (it2 != existingGridNodes.end())
249 return it2->second;
250
251 Vec3D<T> pos;
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;
257 return id;
258 };
259
260 // Marching Squares Cases (Triangles)
261 // 0-3: Corners (BL, BR, TR, TL), 4-7: Edges (B, R, T, L)
262 const std::vector<std::vector<int>> msTris = {{},
263 {0, 4, 7},
264 {1, 5, 4},
265 {0, 1, 5, 0, 5, 7},
266 {2, 6, 5},
267 {0, 4, 7, 2, 6, 5},
268 {1, 2, 6, 1, 6, 4},
269 {0, 1, 2, 0, 2, 6, 0, 6, 7},
270 {3, 7, 6},
271 {0, 4, 6, 0, 6, 3},
272 {1, 5, 4, 3, 7, 6},
273 {0, 1, 5, 0, 5, 6, 0, 6, 3},
274 {3, 2, 5, 3, 5, 7},
275 {0, 4, 5, 0, 5, 2, 0, 2, 3},
276 {1, 2, 3, 1, 3, 7, 1, 7, 4},
277 {0, 1, 2, 0, 2, 3}};
278
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];
282 int u = (d + 1) % D;
283 int v = (d + 2) % D;
284 if (D == 3 && u > v)
285 std::swap(u, v);
286
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;
291
292 bool flip = (side == 0);
293
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};
298
299 int mask = 0;
300 int matId = -1;
301
302 for (int k = 0; k < 4; ++k) {
303 viennahrle::Index<D> idx;
304 idx[d] = boundaryCoord;
305 idx[u] = cI[k];
306 idx[v] = cJ[k];
307
308 for (unsigned l = 0; l < levelSets.size(); ++l) {
309 viennahrle::ConstDenseIterator<hrleDomainType> it(
310 levelSets[l]->getDomain());
311 it.goToIndices(idx);
312 if (it.getValue() <= 0) {
313 mask |= (1 << k);
314 if (matId == -1)
315 matId = (materialMap) ? materialMap->getMaterialId(l)
316 : (int)l;
317 break;
318 }
319 }
320 }
321
322 if (mask == 0)
323 continue;
324 if (matId == -1)
325 matId = 0;
326
327 auto &tris = msTris[mask];
328 for (size_t t = 0; t < tris.size(); t += 3) {
329 unsigned nodes[3];
330 for (int k = 0; k < 3; ++k) {
331 int pt = tris[t + k];
332 if (pt < 4) {
333 int coords[3];
334 coords[d] = boundaryCoord;
335 coords[u] = cI[pt];
336 coords[v] = cJ[pt];
337 nodes[k] = getGridNode(coords[0], coords[1], coords[2]);
338 } else {
339 int edgeDir, ei;
340 ei = boundaryCoord;
341 int s[3];
342 s[d] = ei;
343
344 if (pt == 4) { // Bottom
345 edgeDir = u;
346 s[u] = i;
347 s[v] = j;
348 } else if (pt == 5) { // Right
349 edgeDir = v;
350 s[u] = i + 1;
351 s[v] = j;
352 } else if (pt == 6) { // Top
353 edgeDir = u;
354 s[u] = i;
355 s[v] = j + 1;
356 } else { // Left
357 edgeDir = v;
358 s[u] = i;
359 s[v] = j;
360 }
361 nodes[k] = edgeToNode[{edgeDir, s[0], s[1], s[2]}];
362
363 if (nodes[k] == 0 &&
364 edgeToNode.find({0, 0, 0, 0}) == edgeToNode.end()) {
365 // Fallback: create midpoint if node missing
366 Vec3D<T> pos;
367 pos[d] = ei * gridDelta;
368 if (pt == 4) {
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;
377 } else {
378 pos[u] = i * gridDelta;
379 pos[v] = (j + 0.5) * gridDelta;
380 }
381 nodes[k] = multiMesh->insertNextNode(pos);
382 }
383 }
384 }
385
386 if (flip)
387 std::swap(nodes[1], nodes[2]);
388
389 multiMesh->insertNextTriangle({nodes[0], nodes[1], nodes[2]});
390 auto matIds = multiMesh->cellData.getScalarData("MaterialIds");
391 if (matIds)
392 matIds->push_back(matId);
393 }
394 }
395 }
396 }
397 }
398 }
399 }
400
401public:
402 ToHullMesh() = default;
403
404 ToHullMesh(SmartPointer<Mesh<T>> passedMesh) { multiMesh = passedMesh; }
405
406 ToHullMesh(SmartPointer<Mesh<T>> passedMesh,
407 SmartPointer<Domain<T, D>> levelSet) {
408 multiMesh = passedMesh;
409 levelSets.push_back(levelSet);
410 }
411
412 ToHullMesh(SmartPointer<Mesh<T>> passedMesh,
413 std::vector<SmartPointer<Domain<T, D>>> const &levelSetsVector) {
414 multiMesh = passedMesh;
415 levelSets = levelSetsVector;
416 }
417
418 void setMesh(SmartPointer<Mesh<T>> passedMesh) { multiMesh = passedMesh; }
419
421 void insertNextLevelSet(SmartPointer<Domain<T, D>> levelSet) {
422 levelSets.push_back(levelSet);
423 }
424
425 void clearLevelSets() { levelSets.clear(); }
426
427 void setSharpCorners(bool passedSharpCorners) {
428 generateSharpCorners = passedSharpCorners;
429 }
430
431 void setMaterialMap(SmartPointer<MaterialMap> passedMaterialMap) {
432 materialMap = passedMaterialMap;
433 }
434
435 void setBottomExtension(T extension) {
436 if (D == 3) {
437 VIENNACORE_LOG_WARNING(
438 "ToHullMesh: Bottom extension is only applicable for 2D meshes. "
439 "Ignoring value.");
440 return;
441 }
442 if (extension < 0) {
443 VIENNACORE_LOG_WARNING("ToHullMesh: Negative bottom extension is not "
444 "allowed. Setting to 0.");
445 bottomExtension = 0;
446 } else {
447 bottomExtension = extension;
448 }
449 }
450
451 void setMinNodeDistanceFactor(double factor) {
452 if (factor <= 0) {
453 VIENNACORE_LOG_WARNING("ToHullMesh: minNodeDistanceFactor must be "
454 "positive. Ignoring value.");
455 return;
456 }
457 minNodeDistanceFactor = factor;
458 }
459
460 void apply() { generateHull(); }
461};
462
463// add all template specialisations for this class
465
466} // namespace viennals
constexpr int D
Definition Epitaxy.cpp:11
double T
Definition Epitaxy.cpp:12
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:28
viennahrle::Domain< T, D > DomainType
Definition lsDomain.hpp:33
This class holds an explicit mesh, which is always given in 3 dimensions. If it describes a 2D mesh,...
Definition lsMesh.hpp:22
ToHullMesh(SmartPointer< Mesh< T > > passedMesh, SmartPointer< Domain< T, D > > levelSet)
Definition lsToHullMesh.hpp:406
void setMaterialMap(SmartPointer< MaterialMap > passedMaterialMap)
Definition lsToHullMesh.hpp:431
void setMesh(SmartPointer< Mesh< T > > passedMesh)
Definition lsToHullMesh.hpp:418
void setMinNodeDistanceFactor(double factor)
Definition lsToHullMesh.hpp:451
ToHullMesh(SmartPointer< Mesh< T > > passedMesh, std::vector< SmartPointer< Domain< T, D > > > const &levelSetsVector)
Definition lsToHullMesh.hpp:412
void clearLevelSets()
Definition lsToHullMesh.hpp:425
ToHullMesh(SmartPointer< Mesh< T > > passedMesh)
Definition lsToHullMesh.hpp:404
void insertNextLevelSet(SmartPointer< Domain< T, D > > levelSet)
Level sets wrapping other level sets have to be inserted last.
Definition lsToHullMesh.hpp:421
void apply()
Definition lsToHullMesh.hpp:460
void setSharpCorners(bool passedSharpCorners)
Definition lsToHullMesh.hpp:427
void setBottomExtension(T extension)
Definition lsToHullMesh.hpp:435
Definition lsToMultiSurfaceMesh.hpp:11
void apply() override
Definition lsToMultiSurfaceMesh.hpp:623
void setMaterialMap(SmartPointer< MaterialMap > passedMaterialMap)
Definition lsToMultiSurfaceMesh.hpp:619
void insertNextLevelSet(SmartPointer< lsDomainType > passedLevelSet)
Definition lsToMultiSurfaceMesh.hpp:613
void setMesh(SmartPointer< Mesh< T > > passedMesh)
Definition lsToSurfaceMesh.hpp:95
void setSharpCorners(bool check)
Definition lsToSurfaceMesh.hpp:99
#define PRECOMPILE_PRECISION_DIMENSION(className)
Definition lsPreCompileMacros.hpp:24
Definition lsAdvect.hpp:41