ViennaLS
Loading...
Searching...
No Matches
lsMarkVoidPoints.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <hrleSparseStarIterator.hpp>
4
6
7#include <lsDomain.hpp>
8#include <lsGraph.hpp>
9
10namespace viennals {
11
12using namespace viennacore;
13
21enum struct VoidTopSurfaceEnum : unsigned {
22 LEX_LOWEST = 0,
23 LEX_HIGHEST = 1,
24 LARGEST = 2,
25 SMALLEST = 3
26};
27
30template <class T, int D> class MarkVoidPoints {
31 using IndexType = std::size_t;
32
33 SmartPointer<Domain<T, D>> domain = nullptr;
34 bool reverseVoidDetection = false;
35 bool saveComponents = false;
36 bool detectLargestSurface = false;
37
38 // two points are connected if they have the same sign
39 bool areConnected(const T &value1, const T &value2) {
40 return (value1 >= 0) == (value2 >= 0);
41 }
42
43 std::vector<IndexType>
44 mergeComponentCounts(const std::vector<IndexType> &components,
45 const std::vector<IndexType> &pointsPerComponent) {
46 // find number of connected components after merge
47 // TODO: the last element in the components vector is very likely
48 // the highest index, so could just set it instead of checking all values
49 unsigned highestIndex = 0;
50 for (auto it : components) {
51 if (highestIndex < it) {
52 highestIndex = it;
53 }
54 }
55
56 // merge pointsPerComponent according to the connected components
57 std::vector<IndexType> pointsPerConnected;
58 pointsPerConnected.resize(highestIndex + 1, 0);
59 for (unsigned i = 0; i < components.size(); ++i) {
60 pointsPerConnected[components[i]] += pointsPerComponent[i];
61 }
62
63 return pointsPerConnected;
64 }
65
66 IndexType calculateTopID(const std::vector<IndexType> &components,
67 const std::vector<IndexType> &pointsPerConnected) {
68 // check which component has the most points
69 IndexType topId = 0;
70 // use first component, which contains more than 0 points
71 while (topId < pointsPerConnected.size() &&
72 pointsPerConnected[topId] == 0) {
73 ++topId;
74 }
75 for (unsigned i = topId + 1; i < pointsPerConnected.size(); ++i) {
76 if ((pointsPerConnected[topId] < pointsPerConnected[i]) !=
77 reverseVoidDetection) {
78 topId = i;
79 }
80 }
81
82 return topId;
83 }
84
85public:
86 static constexpr char voidPointLabel[] = "VoidPointMarkers";
87
89
90 MarkVoidPoints(SmartPointer<Domain<T, D>> passedlsDomain,
91 bool passedReverseVoidDetection = false)
92 : domain(passedlsDomain),
93 reverseVoidDetection(passedReverseVoidDetection) {}
94
95 void setLevelSet(SmartPointer<Domain<T, D>> passedlsDomain) {
96 domain = passedlsDomain;
97 }
98
103 void setReverseVoidDetection(bool passedReverseVoidDetection) {
104 reverseVoidDetection = passedReverseVoidDetection;
105 }
106
113 void setDetectLargestSurface(bool passedDetect) {
114 detectLargestSurface = passedDetect;
115 }
116
120 switch (topSurface) {
122 reverseVoidDetection = true;
123 detectLargestSurface = false;
124 break;
126 reverseVoidDetection = false;
127 detectLargestSurface = false;
128 break;
130 reverseVoidDetection = false;
131 detectLargestSurface = true;
132 break;
134 reverseVoidDetection = true;
135 detectLargestSurface = true;
136 break;
137
138 default:
139 Logger::getInstance()
140 .addWarning("MarkVoidPoints: Invalid VoidTopSurfaceEnum set. "
141 "Using default values.")
142 .print();
143 reverseVoidDetection = false;
144 detectLargestSurface = false;
145 break;
146 }
147 }
148
152 void setSaveComponentIds(bool scid) { saveComponents = scid; }
153
154 void apply() {
155 lsInternal::Graph graph;
156
157 std::vector<std::vector<std::vector<IndexType>>> componentList(
158 domain->getNumberOfSegments());
159 for (unsigned segmentId = 0; segmentId < domain->getNumberOfSegments();
160 ++segmentId) {
161 componentList[segmentId].resize(D + 1);
162 for (int dim = -1; dim < D; ++dim) {
163 componentList[segmentId][dim + 1].resize(
164 domain->getDomain().getNumberOfRuns(segmentId, dim), -1);
165 }
166 }
167
168 std::size_t numberOfComponents = 0;
169 std::vector<IndexType> pointsPerComponent;
170
171 // cycle through and set up the graph to get connectivity information
172 for (hrleConstSparseStarIterator<typename Domain<T, D>::DomainType, 1>
173 neighborIt(domain->getDomain());
174 !neighborIt.isFinished(); neighborIt.next()) {
175 auto &center = neighborIt.getCenter();
176
177 // component id of the current run
178 auto &currentComponentId =
179 componentList[center.getSegmentId()][center.getLevel()]
180 [center.getRunTypePosition()];
181
182 // -1 means it is not set yet
183 if (currentComponentId == -1) {
184 for (int k = 0; k < 2 * D; ++k) {
185 auto &neighbor = neighborIt.getNeighbor(k);
186 const auto neighborComponentId =
187 componentList[neighbor.getSegmentId()][neighbor.getLevel()]
188 [neighbor.getRunTypePosition()];
189 // if neighbor is already defined
190 // set current component to neighbor component
191 // if they are connected
192 if (neighborComponentId != -1) {
193 if (areConnected(center.getValue(), neighbor.getValue())) {
194 currentComponentId = neighborComponentId;
195 if (center.getValue() >= 0.)
196 ++pointsPerComponent[currentComponentId];
197 break;
198 }
199 }
200 }
201 }
202
203 // it is still not set, so add new vertex
204 if (currentComponentId == -1) {
205 currentComponentId = numberOfComponents;
206 pointsPerComponent.push_back((center.getValue() > 0.) ? 1 : 0);
207 graph.insertNextVertex();
208 ++numberOfComponents;
209 }
210
211 // check if edge can be set
212 for (int k = 0; k < 2 * D; ++k) {
213 auto &neighbor = neighborIt.getNeighbor(k);
214 auto &neighborComponentId =
215 componentList[neighbor.getSegmentId()][neighbor.getLevel()]
216 [neighbor.getRunTypePosition()];
217 if (areConnected(center.getValue(), neighbor.getValue())) {
218 // if neighbor is already set
219 if (neighborComponentId != -1) {
220 // if neighbor is part of different component
221 if (currentComponentId != neighborComponentId) {
222 graph.insertNextEdge(currentComponentId, neighborComponentId);
223 }
224 } else {
225 neighborComponentId = currentComponentId;
226 if (neighbor.getValue() >= 0.)
227 ++pointsPerComponent[neighborComponentId];
228 }
229 }
230 }
231 }
232
233 auto components = graph.getConnectedComponents();
234
235 int topComponent =
236 (reverseVoidDetection) ? components[0] : components.back();
237
238 // identify which layer to keep
239 {
240 auto pointsPerConnected =
241 mergeComponentCounts(components, pointsPerComponent);
242
243 // find largest connected surface
244 if (detectLargestSurface) {
245 topComponent = calculateTopID(components, pointsPerConnected);
246 } else { // if component does not contain points, take the next one
247 while (pointsPerConnected[topComponent] == 0) {
248 if (reverseVoidDetection)
249 ++topComponent;
250 else
251 --topComponent;
252 }
253 }
254 }
255
256 std::vector<T> voidPointMarkers;
257 voidPointMarkers.resize(domain->getNumberOfPoints());
258
259 std::vector<T> componentMarkers;
260 if (saveComponents)
261 componentMarkers.resize(domain->getNumberOfPoints());
262
263 // cycle through again to set correct voidPointMarkers
264 for (hrleConstSparseStarIterator<typename Domain<T, D>::DomainType, 1>
265 neighborIt(domain->getDomain());
266 !neighborIt.isFinished(); neighborIt.next()) {
267 auto center = neighborIt.getCenter();
268
269 if (!center.isDefined())
270 continue;
271
272 // if it is positive, just check if it is part of the top component
273 if (center.getValue() >= 0) {
274 const int &oldComponentId = componentList[center.getSegmentId()][0]
275 [center.getRunTypePosition()];
276 voidPointMarkers[center.getPointId()] =
277 (components[oldComponentId] != topComponent);
278 } else {
279 // if it is negative, check all neighbours (with different sign),
280 // because they are part of of a positive component, which might
281 // be the top
282 unsigned k;
283 for (k = 0; k < 2 * D; ++k) {
284 auto &neighbor = neighborIt.getNeighbor(k);
285 if (std::signbit(neighbor.getValue()) ==
286 std::signbit(center.getValue()))
287 continue;
288 const int &oldneighborComponentId =
289 componentList[neighbor.getSegmentId()][neighbor.getLevel()]
290 [neighbor.getRunTypePosition()];
291 if (components[oldneighborComponentId] == topComponent) {
292 break;
293 }
294 }
295 voidPointMarkers[center.getPointId()] = (k == 2 * D);
296 }
297
298 if (saveComponents) {
299 const int &oldComponentId = componentList[center.getSegmentId()][0]
300 [center.getRunTypePosition()];
301 componentMarkers[center.getPointId()] = components[oldComponentId];
302 }
303 }
304
305 auto &pointData = domain->getPointData();
306 auto voidMarkersPointer = pointData.getScalarData(voidPointLabel, true);
307 // if vector data does not exist
308 if (voidMarkersPointer == nullptr) {
309 pointData.insertNextScalarData(voidPointMarkers, voidPointLabel);
310 } else {
311 *voidMarkersPointer = std::move(voidPointMarkers);
312 }
313
314 if (saveComponents) {
315 auto componentMarkersPointer =
316 pointData.getScalarData("ConnectedComponentId", true);
317 // if vector data does not exist
318 if (componentMarkersPointer == nullptr) {
319 pointData.insertNextScalarData(componentMarkers,
320 "ConnectedComponentId");
321 } else {
322 *componentMarkersPointer = std::move(componentMarkers);
323 }
324 }
325 }
326};
327
328} // namespace viennals
Definition lsGraph.hpp:13
std::size_t insertNextVertex()
add new vertex
Definition lsGraph.hpp:49
std::vector< IndexType > getConnectedComponents()
Definition lsGraph.hpp:70
void insertNextEdge(std::size_t vertex1, std::size_t vertex2)
add connection of vertex1 to vertex2
Definition lsGraph.hpp:56
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 is used to mark points of the level set which are enclosed in a void.
Definition lsMarkVoidPoints.hpp:30
void setLevelSet(SmartPointer< Domain< T, D > > passedlsDomain)
Definition lsMarkVoidPoints.hpp:95
void setReverseVoidDetection(bool passedReverseVoidDetection)
Set whether the "top" level set should be the most positive(default) connected chain of level set val...
Definition lsMarkVoidPoints.hpp:103
void apply()
Definition lsMarkVoidPoints.hpp:154
void setSaveComponentIds(bool scid)
Set whether the connected component IDs used to generate the void points should be saved....
Definition lsMarkVoidPoints.hpp:152
void setDetectLargestSurface(bool passedDetect)
Set whether the number of points of one connected surface should be used to detect void points....
Definition lsMarkVoidPoints.hpp:113
static constexpr char voidPointLabel[]
Definition lsMarkVoidPoints.hpp:86
void setVoidTopSurface(VoidTopSurfaceEnum topSurface)
Set which connected component to use as the top surface and mark all other components as void points.
Definition lsMarkVoidPoints.hpp:119
MarkVoidPoints()
Definition lsMarkVoidPoints.hpp:88
MarkVoidPoints(SmartPointer< Domain< T, D > > passedlsDomain, bool passedReverseVoidDetection=false)
Definition lsMarkVoidPoints.hpp:90
Definition lsAdvect.hpp:46
VoidTopSurfaceEnum
Enumeration describing which connected component to use as top surface during void point detection....
Definition lsMarkVoidPoints.hpp:21
constexpr int D
Definition pyWrap.cpp:65
double T
Definition pyWrap.cpp:63