ViennaLS
Loading...
Searching...
No Matches
lsToSurfaceMesh.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <iostream>
6#include <map>
7
8#include <hrleSparseCellIterator.hpp>
9#include <lsDomain.hpp>
10#include <lsMarchingCubes.hpp>
11#include <lsMesh.hpp>
12
13namespace viennals {
14
15using namespace viennacore;
16
20template <class T, int D> class ToSurfaceMesh {
21 typedef typename Domain<T, D>::DomainType hrleDomainType;
22
23 SmartPointer<Domain<T, D>> levelSet = nullptr;
24 SmartPointer<Mesh<T>> mesh = nullptr;
25 // std::vector<hrleIndexType> meshNodeToPointIdMapping;
26 const T epsilon;
27 bool updatePointData = true;
28
29public:
30 ToSurfaceMesh(double eps = 1e-12) : epsilon(eps) {}
31
32 ToSurfaceMesh(const SmartPointer<Domain<T, D>> passedLevelSet,
33 SmartPointer<Mesh<T>> passedMesh, double eps = 1e-12)
34 : levelSet(passedLevelSet), mesh(passedMesh), epsilon(eps) {}
35
36 void setLevelSet(SmartPointer<Domain<T, D>> passedlsDomain) {
37 levelSet = passedlsDomain;
38 }
39
40 void setMesh(SmartPointer<Mesh<T>> passedMesh) { mesh = passedMesh; }
41
42 void setUpdatePointData(bool update) { updatePointData = update; }
43
44 void apply() {
45 if (levelSet == nullptr) {
46 Logger::getInstance()
47 .addWarning("No level set was passed to ToSurfaceMesh.")
48 .print();
49 return;
50 }
51 if (mesh == nullptr) {
52 Logger::getInstance()
53 .addWarning("No mesh was passed to ToSurfaceMesh.")
54 .print();
55 return;
56 }
57
58 if (levelSet->getNumberOfPoints() == 0) {
59 return;
60 }
61
62 mesh->clear();
63 const unsigned int corner0[12] = {0, 1, 2, 0, 4, 5, 6, 4, 0, 1, 3, 2};
64 const unsigned int corner1[12] = {1, 3, 3, 2, 5, 7, 7, 6, 4, 5, 7, 6};
65 const unsigned int direction[12] = {0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2};
66
67 // test if level set function consists of at least 2 layers of
68 // defined grid points
69 if (levelSet->getLevelSetWidth() < 2) {
70 Logger::getInstance()
71 .addWarning("Levelset is less than 2 layers wide. Export might fail!")
72 .print();
73 }
74
75 typedef typename std::map<hrleVectorType<hrleIndexType, D>, unsigned>
76 nodeContainerType;
77
78 nodeContainerType nodes[D];
79
80 typename nodeContainerType::iterator nodeIt;
81
82 lsInternal::MarchingCubes marchingCubes;
83
84 using DomainType = Domain<T, D>;
85 using ScalarDataType = typename DomainType::PointDataType::ScalarDataType;
86 using VectorDataType = typename DomainType::PointDataType::VectorDataType;
87
88 const bool updateData = updatePointData;
89
90 // save how data should be transferred to new level set
91 // list of indices into the old pointData vector
92 std::vector<std::vector<unsigned>> newDataSourceIds;
93 // there is no multithreading here, so just use 1
94 if (updateData)
95 newDataSourceIds.resize(1);
96
97 // iterate over all active points
98 for (hrleConstSparseCellIterator<hrleDomainType> cellIt(
99 levelSet->getDomain());
100 !cellIt.isFinished(); cellIt.next()) {
101
102 for (int u = 0; u < D; u++) {
103 while (!nodes[u].empty() &&
104 nodes[u].begin()->first <
105 hrleVectorType<hrleIndexType, D>(cellIt.getIndices()))
106 nodes[u].erase(nodes[u].begin());
107 }
108
109 unsigned signs = 0;
110 for (int i = 0; i < (1 << D); i++) {
111 if (cellIt.getCorner(i).getValue() >= T(0))
112 signs |= (1 << i);
113 }
114
115 // all corners have the same sign, so no surface here
116 if (signs == 0)
117 continue;
118 if (signs == (1 << (1 << D)) - 1)
119 continue;
120
121 // for each element
122 const int *Triangles = (D == 2) ? marchingCubes.polygonize2d(signs)
123 : marchingCubes.polygonize3d(signs);
124
125 for (; Triangles[0] != -1; Triangles += D) {
126 std::array<unsigned, D> nod_numbers;
127
128 // for each node
129 for (int n = 0; n < D; n++) {
130 const int edge = Triangles[n];
131
132 unsigned p0 = corner0[edge];
133 unsigned p1 = corner1[edge];
134
135 // determine direction of edge
136 int dir = direction[edge];
137
138 // look for existing surface node
139 hrleVectorType<hrleIndexType, D> d(cellIt.getIndices());
140 d += BitMaskToVector<D, hrleIndexType>(p0);
141
142 nodeIt = nodes[dir].find(d);
143 if (nodeIt != nodes[dir].end()) {
144 nod_numbers[n] = nodeIt->second;
145 } else { // if node does not exist yet
146
147 // calculate coordinate of new node
148 std::array<T, 3> cc{}; // initialise with zeros
149 std::size_t currentPointId = 0;
150 for (int z = 0; z < D; z++) {
151 if (z != dir) {
152 // TODO might not need BitMaskToVector here, just check if z bit
153 // is set
154 cc[z] = double(cellIt.getIndices(z) +
155 BitMaskToVector<D, hrleIndexType>(p0)[z]);
156 } else {
157 T d0, d1;
158
159 d0 = cellIt.getCorner(p0).getValue();
160 d1 = cellIt.getCorner(p1).getValue();
161
162 // calculate the surface-grid intersection point
163 if (d0 == -d1) { // includes case where d0=d1=0
164 currentPointId = cellIt.getCorner(p0).getPointId();
165 cc[z] = static_cast<T>(cellIt.getIndices(z)) + 0.5;
166 } else {
167 if (std::abs(d0) <= std::abs(d1)) {
168 currentPointId = cellIt.getCorner(p0).getPointId();
169 cc[z] =
170 static_cast<T>(cellIt.getIndices(z)) + (d0 / (d0 - d1));
171 } else {
172 currentPointId = cellIt.getCorner(p1).getPointId();
173 cc[z] = static_cast<T>(cellIt.getIndices(z) + 1) -
174 (d1 / (d1 - d0));
175 }
176 }
177 cc[z] = std::max(cc[z], cellIt.getIndices(z) + epsilon);
178 cc[z] = std::min(cc[z], (cellIt.getIndices(z) + 1) - epsilon);
179 }
180 cc[z] = levelSet->getGrid().getGridDelta() * cc[z];
181 }
182
183 // insert new node
184 nod_numbers[n] =
185 mesh->insertNextNode(cc); // insert new surface node
186 nodes[dir][d] = nod_numbers[n];
187
188 if (updateData)
189 newDataSourceIds[0].push_back(currentPointId);
190 }
191 }
192
193 mesh->insertNextElement(nod_numbers); // insert new surface element
194 }
195 }
196
197 // now copy old data into new level set
198 if (updateData) {
199 mesh->getPointData().translateFromMultiData(levelSet->getPointData(),
200 newDataSourceIds);
201 }
202 }
203};
204
205// add all template specialisations for this class
207
208} // namespace viennals
Helper class for lsToSurfaceMesh. Should not be used directly.
Definition lsMarchingCubes.hpp:6
const int * polygonize3d(unsigned int signs)
signs = signs of the corners in lexicographic order (1bit per corner)
Definition lsMarchingCubes.hpp:324
const int * polygonize2d(unsigned int signs)
signs = signs of the corners in lexicographic order (1bit per corner)
Definition lsMarchingCubes.hpp:312
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:28
hrleDomain< 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:21
Extract an explicit Mesh<> instance from an lsDomain. The interface is then described by explciit sur...
Definition lsToSurfaceMesh.hpp:20
void setMesh(SmartPointer< Mesh< T > > passedMesh)
Definition lsToSurfaceMesh.hpp:40
ToSurfaceMesh(const SmartPointer< Domain< T, D > > passedLevelSet, SmartPointer< Mesh< T > > passedMesh, double eps=1e-12)
Definition lsToSurfaceMesh.hpp:32
void apply()
Definition lsToSurfaceMesh.hpp:44
void setUpdatePointData(bool update)
Definition lsToSurfaceMesh.hpp:42
void setLevelSet(SmartPointer< Domain< T, D > > passedlsDomain)
Definition lsToSurfaceMesh.hpp:36
ToSurfaceMesh(double eps=1e-12)
Definition lsToSurfaceMesh.hpp:30
#define PRECOMPILE_PRECISION_DIMENSION(className)
Definition lsPreCompileMacros.hpp:24
Definition lsAdvect.hpp:46
constexpr int D
Definition pyWrap.cpp:65
double T
Definition pyWrap.cpp:63