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