ViennaLS
Loading...
Searching...
No Matches
lsBooleanOperation.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <hrleSparseStarIterator.hpp>
6
7#include <lsDomain.hpp>
8#include <lsPrune.hpp>
9
10#include <vcLogger.hpp>
11#include <vcSmartPointer.hpp>
12#include <vcVectorType.hpp>
13
14#include <functional>
15
16namespace viennals {
17
18using namespace viennacore;
19
27enum struct BooleanOperationEnum : unsigned {
29 UNION = 1,
31 INVERT = 3,
33};
34
45template <class T, int D> class BooleanOperation {
46public:
48 std::function<std::pair<T, bool>(const T &, const T &)>;
49
50private:
51 typedef typename Domain<T, D>::DomainType hrleDomainType;
52 SmartPointer<Domain<T, D>> levelSetA = nullptr;
53 SmartPointer<Domain<T, D>> levelSetB = nullptr;
55 ComparatorType operationComp = nullptr;
56 bool updatePointData = true;
57 bool pruneResult = true;
58
59 void booleanOpInternal(ComparatorType comp) {
60 auto &grid = levelSetA->getGrid();
61 auto newlsDomain = SmartPointer<Domain<T, D>>::New(grid);
62 typename Domain<T, D>::DomainType &newDomain = newlsDomain->getDomain();
63 typename Domain<T, D>::DomainType &domain = levelSetA->getDomain();
64
65 newDomain.initialize(domain.getNewSegmentation(), domain.getAllocation());
66
67 const bool updateData = updatePointData;
68 // save how data should be transferred to new level set
69 // list of indices into the old pointData vector
70 std::vector<std::vector<unsigned>> newDataSourceIds;
71 std::vector<std::vector<bool>> newDataLS;
72 if (updateData) {
73 newDataSourceIds.resize(newDomain.getNumberOfSegments());
74 newDataLS.resize(newDataSourceIds.size());
75 }
76
77#pragma omp parallel num_threads(newDomain.getNumberOfSegments())
78 {
79 int p = 0;
80#ifdef _OPENMP
81 p = omp_get_thread_num();
82#endif
83
84 auto &domainSegment = newDomain.getDomainSegment(p);
85
86 viennahrle::Index<D> currentVector =
87 (p == 0) ? grid.getMinGridPoint()
88 : newDomain.getSegmentation()[p - 1];
89
90 viennahrle::Index<D> const endVector =
91 (p != static_cast<int>(newDomain.getNumberOfSegments() - 1))
92 ? newDomain.getSegmentation()[p]
93 : grid.incrementIndices(grid.getMaxGridPoint());
94
95 viennahrle::ConstSparseIterator<hrleDomainType> itA(
96 levelSetA->getDomain(), currentVector);
97 viennahrle::ConstSparseIterator<hrleDomainType> itB(
98 levelSetB->getDomain(), currentVector);
99
100 while (currentVector < endVector) {
101 const auto &comparison = comp(itA.getValue(), itB.getValue());
102 const auto &currentValue = comparison.first;
103
104 if (currentValue != Domain<T, D>::NEG_VALUE &&
105 currentValue != Domain<T, D>::POS_VALUE) {
106 domainSegment.insertNextDefinedPoint(currentVector, currentValue);
107 if (updateData) {
108 // if taken from A, set to true
109 const bool originLS = comparison.second;
110 newDataLS[p].push_back(originLS);
111 const auto originPointId =
112 (originLS) ? itA.getPointId() : itB.getPointId();
113 newDataSourceIds[p].push_back(originPointId);
114 }
115 } else {
116 domainSegment.insertNextUndefinedPoint(
117 currentVector, (currentValue < 0) ? Domain<T, D>::NEG_VALUE
119 }
120
121 switch (Compare(itA.getEndIndices(), itB.getEndIndices())) {
122 case -1:
123 itA.next();
124 break;
125 case 0:
126 itA.next();
127 default:
128 itB.next();
129 }
130 currentVector =
131 Max(itA.getStartIndices().get(), itB.getStartIndices().get());
132 }
133 }
134
135 // merge data
136 for (unsigned i = 1; i < newDataLS.size(); ++i) {
137 newDataLS[0].insert(newDataLS[0].end(), newDataLS[i].begin(),
138 newDataLS[i].end());
139
140 newDataSourceIds[0].insert(newDataSourceIds[0].end(),
141 newDataSourceIds[i].begin(),
142 newDataSourceIds[i].end());
143 }
144
145 // transfer data from the old LSs to new LS
146 // Only do so if the same data exists in both LSs
147 // If this is not the case, the data is invalid
148 // and therefore not needed anyway.
149 if (updateData) {
150 const auto &AData = levelSetA->getPointData();
151 const auto &BData = levelSetB->getPointData();
152 auto &newData = newlsDomain->getPointData();
153
154 // scalars — preserve A's fields even when B doesn't have them.
155 // Previously, fields present only on A (e.g. OxVelocity on the oxide
156 // in a RELATIVE_COMPLEMENT with the mask) were silently dropped because
157 // the mask has no matching field. For A-sourced result points we always
158 // use A's value; for B-sourced points without a matching B field we use
159 // 0.
160 for (unsigned i = 0; i < AData.getScalarDataSize(); ++i) {
161 auto scalarDataLabel = AData.getScalarDataLabel(i);
162 auto APointer = AData.getScalarData(i);
163 auto BPointer =
164 BData.getScalarData(scalarDataLabel, true); // may be nullptr
166 scalars.resize(newlsDomain->getNumberOfPoints(), T(0));
167 for (unsigned j = 0; j < newlsDomain->getNumberOfPoints(); ++j) {
168 if (newDataLS[0][j])
169 scalars[j] = APointer->at(newDataSourceIds[0][j]);
170 else if (BPointer != nullptr)
171 scalars[j] = BPointer->at(newDataSourceIds[0][j]);
172 // else: B-sourced point with no matching B field → stays 0
173 }
174 newData.insertNextScalarData(scalars, scalarDataLabel);
175 }
176
177 // vectors — same asymmetric treatment as scalars above.
178 for (unsigned i = 0; i < AData.getVectorDataSize(); ++i) {
179 auto vectorDataLabel = AData.getVectorDataLabel(i);
180 auto APointer = AData.getVectorData(i);
181 auto BPointer =
182 BData.getVectorData(vectorDataLabel, true); // may be nullptr
183 using Vec =
186 vectors.resize(newlsDomain->getNumberOfPoints(), Vec{});
187 for (unsigned j = 0; j < newlsDomain->getNumberOfPoints(); ++j) {
188 if (newDataLS[0][j])
189 vectors[j] = APointer->at(newDataSourceIds[0][j]);
190 else if (BPointer != nullptr)
191 vectors[j] = BPointer->at(newDataSourceIds[0][j]);
192 // else: B-sourced point with no matching B field → stays
193 // zero-initialised
194 }
195 newData.insertNextVectorData(vectors, vectorDataLabel);
196 }
197 }
198
199 newDomain.finalize();
200 newDomain.segment();
201 newlsDomain->setLevelSetWidth(levelSetA->getLevelSetWidth());
202
203 if (pruneResult) {
204 auto pruner = Prune<T, D>(newlsDomain);
205 pruner.setRemoveStrayZeros(true);
206 pruner.apply();
207
208 // now we need to prune, to remove stray defined points
209 Prune<T, D>(newlsDomain).apply();
210 }
211
212 levelSetA->deepCopy(newlsDomain);
213 }
214
215 void invert() {
216 auto &hrleDomain = levelSetA->getDomain();
217#pragma omp parallel num_threads(hrleDomain.getNumberOfSegments())
218 {
219 int p = 0;
220#ifdef _OPENMP
221 p = omp_get_thread_num();
222#endif
223 auto &domainSegment = hrleDomain.getDomainSegment(p);
224
225 // change all defined values
226 for (unsigned i = 0; i < domainSegment.definedValues.size(); ++i) {
227 domainSegment.definedValues[i] = -domainSegment.definedValues[i];
228 }
229
230 // add undefined values if missing
231 if (domainSegment.undefinedValues.size() < 1) {
232 domainSegment.undefinedValues.push_back(T(Domain<T, D>::NEG_VALUE));
233 }
234 if (domainSegment.undefinedValues.size() < 2) {
235 if (domainSegment.undefinedValues[0] == Domain<T, D>::NEG_VALUE) {
236 domainSegment.undefinedValues.push_back(T(Domain<T, D>::POS_VALUE));
237 } else {
238 domainSegment.undefinedValues.push_back(T(Domain<T, D>::NEG_VALUE));
239 }
240 }
241
242 // change all runTypes
243 // there are only two undefined runs: negative undefined (UNDEF_PT)
244 // and positive undefined (UNDEF_PT+1)
245 for (unsigned dim = 0; dim < D; ++dim) {
246 for (unsigned c = 0; c < domainSegment.runTypes[dim].size(); ++c) {
247 if (domainSegment.runTypes[dim][c] ==
248 viennahrle::RunTypeValues::UNDEF_PT) {
249 domainSegment.runTypes[dim][c] =
250 viennahrle::RunTypeValues::UNDEF_PT + 1;
251 } else if (domainSegment.runTypes[dim][c] ==
252 viennahrle::RunTypeValues::UNDEF_PT + 1) {
253 domainSegment.runTypes[dim][c] =
254 viennahrle::RunTypeValues::UNDEF_PT;
255 }
256 }
257 }
258 }
259 levelSetA->finalize();
260 }
261
262 static std::pair<T, bool> minComp(const T &a, const T &b) {
263 bool AIsSmaller = a < b;
264 if (AIsSmaller)
265 return std::make_pair(a, true);
266 else
267 return std::make_pair(b, false);
268 }
269
270 static std::pair<T, bool> maxComp(const T &a, const T &b) {
271 bool AIsLarger = a > b;
272 if (AIsLarger)
273 return std::make_pair(a, true);
274 else
275 return std::make_pair(b, false);
276 }
277
278 static std::pair<T, bool> relativeComplementComp(const T &a, const T &b) {
279 return maxComp(a, -b);
280 }
281
282public:
283 BooleanOperation() = default;
284
286 SmartPointer<Domain<T, D>> passedlsDomain,
288 : levelSetA(passedlsDomain), operation(passedOperation) {}
289
291 SmartPointer<Domain<T, D>> passedlsDomainA,
292 SmartPointer<Domain<T, D>> passedlsDomainB,
294 : levelSetA(passedlsDomainA), levelSetB(passedlsDomainB),
295 operation(passedOperation){};
296
298 void setLevelSet(SmartPointer<Domain<T, D>> passedlsDomain) {
299 levelSetA = passedlsDomain;
300 }
301
304 void setSecondLevelSet(SmartPointer<Domain<T, D>> passedlsDomain) {
305 levelSetB = passedlsDomain;
306 }
307
310 operation = passedOperation;
311 }
312
316 operationComp = passedOperationComp;
317 }
318
321 void setUpdatePointData(bool update) { updatePointData = update; }
322
324 void setPruneResult(bool pR) { pruneResult = pR; }
325
327 void apply() {
328 if (levelSetA == nullptr) {
329 VIENNACORE_LOG_ERROR("No level set was passed to BooleanOperation.");
330 return;
331 }
332
333 if (static_cast<unsigned>(operation) < 3) {
334 if (levelSetB == nullptr) {
335 VIENNACORE_LOG_ERROR(
336 "Only one level set was passed to BooleanOperation, "
337 "although two were required.");
338 return;
339 }
340 }
341
342 switch (operation) {
344 booleanOpInternal(&BooleanOperation::maxComp);
345 break;
347 booleanOpInternal(&BooleanOperation::minComp);
348 break;
350 booleanOpInternal(&BooleanOperation::relativeComplementComp);
351 break;
353 invert();
354 break;
356 if (operationComp == nullptr) {
357 VIENNACORE_LOG_ERROR(
358 "No comparator supplied to custom BooleanOperation.");
359 return;
360 }
361 booleanOpInternal(operationComp);
362 }
363 }
364};
365
366// add all template specialisations for this class
367PRECOMPILE_PRECISION_DIMENSION(BooleanOperation)
368
369} // namespace viennals
constexpr int D
Definition Epitaxy.cpp:12
double T
Definition Epitaxy.cpp:13
void setBooleanOperationComparator(ComparatorType passedOperationComp)
Set the comparator to be used when the BooleanOperation is set to CUSTOM.
Definition lsBooleanOperation.hpp:315
void setUpdatePointData(bool update)
Set whether to update the point data stored in the LS during this algorithm. Defaults to true.
Definition lsBooleanOperation.hpp:321
void setPruneResult(bool pR)
Set whether the resulting level set should be pruned. Defaults to true.
Definition lsBooleanOperation.hpp:324
void setLevelSet(SmartPointer< Domain< T, D > > passedlsDomain)
Set which level set to perform the boolean operation on.
Definition lsBooleanOperation.hpp:298
BooleanOperation(SmartPointer< Domain< T, D > > passedlsDomain, BooleanOperationEnum passedOperation=BooleanOperationEnum::INVERT)
Definition lsBooleanOperation.hpp:285
std::function< std::pair< T, bool >(const T &, const T &)> ComparatorType
Definition lsBooleanOperation.hpp:47
void setSecondLevelSet(SmartPointer< Domain< T, D > > passedlsDomain)
Set the level set which will be used to modify the first level set.
Definition lsBooleanOperation.hpp:304
void setBooleanOperation(BooleanOperationEnum passedOperation)
Set which of the operations of BooleanOperationEnum to perform.
Definition lsBooleanOperation.hpp:309
void apply()
Perform operation.
Definition lsBooleanOperation.hpp:327
BooleanOperation(SmartPointer< Domain< T, D > > passedlsDomainA, SmartPointer< Domain< T, D > > passedlsDomainB, BooleanOperationEnum passedOperation=BooleanOperationEnum::INTERSECT)
Definition lsBooleanOperation.hpp:290
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:27
viennahrle::Domain< T, D > DomainType
Definition lsDomain.hpp:32
static constexpr T NEG_VALUE
Definition lsDomain.hpp:52
unsigned getNumberOfSegments() const
returns the number of segments, the levelset is split into. This is useful for algorithm parallelisat...
Definition lsDomain.hpp:153
void finalize(int newWidth)
this function sets a new levelset width and finalizes the levelset, so it is ready for use by other a...
Definition lsDomain.hpp:117
static constexpr T POS_VALUE
Definition lsDomain.hpp:51
void setLevelSetWidth(int width)
Definition lsDomain.hpp:160
Removes all level set points, which do not have at least one oppositely signed neighbour (Meaning the...
Definition lsPrune.hpp:17
void apply()
removes all grid points, which do not have at least one opposite signed neighbour returns the number ...
Definition lsPrune.hpp:74
#define PRECOMPILE_PRECISION_DIMENSION(className)
Definition lsPreCompileMacros.hpp:24
Definition lsAdvect.hpp:41
BooleanOperationEnum
Enumeration for the different types of boolean operations which are supported. When INVERT,...
Definition lsBooleanOperation.hpp:27
@ INTERSECT
Definition lsBooleanOperation.hpp:28
@ CUSTOM
Definition lsBooleanOperation.hpp:32
@ INVERT
Definition lsBooleanOperation.hpp:31
@ RELATIVE_COMPLEMENT
Definition lsBooleanOperation.hpp:30
@ UNION
Definition lsBooleanOperation.hpp:29