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 for
78 for (unsigned p = 0; p < newDomain.getNumberOfSegments(); ++p) {
79
80 auto &domainSegment = newDomain.getDomainSegment(p);
81
82 viennahrle::Index<D> currentVector =
83 (p == 0) ? grid.getMinGridPoint()
84 : newDomain.getSegmentation()[p - 1];
85
86 viennahrle::Index<D> const endVector =
87 (p != static_cast<int>(newDomain.getNumberOfSegments() - 1))
88 ? newDomain.getSegmentation()[p]
89 : grid.incrementIndices(grid.getMaxGridPoint());
90
91 viennahrle::ConstSparseIterator<hrleDomainType> itA(
92 levelSetA->getDomain(), currentVector);
93 viennahrle::ConstSparseIterator<hrleDomainType> itB(
94 levelSetB->getDomain(), currentVector);
95
96 while (currentVector < endVector) {
97 const auto &comparison = comp(itA.getValue(), itB.getValue());
98 const auto &currentValue = comparison.first;
99
100 if (currentValue != Domain<T, D>::NEG_VALUE &&
101 currentValue != Domain<T, D>::POS_VALUE) {
102 domainSegment.insertNextDefinedPoint(currentVector, currentValue);
103 if (updateData) {
104 // if taken from A, set to true
105 const bool originLS = comparison.second;
106 newDataLS[p].push_back(originLS);
107 const auto originPointId =
108 (originLS) ? itA.getPointId() : itB.getPointId();
109 newDataSourceIds[p].push_back(originPointId);
110 }
111 } else {
112 domainSegment.insertNextUndefinedPoint(
113 currentVector, (currentValue < 0) ? Domain<T, D>::NEG_VALUE
115 }
116
117 switch (Compare(itA.getEndIndices(), itB.getEndIndices())) {
118 case -1:
119 itA.next();
120 break;
121 case 0:
122 itA.next();
123 default:
124 itB.next();
125 }
126 currentVector =
127 Max(itA.getStartIndices().get(), itB.getStartIndices().get());
128 }
129 }
130
131 // merge data
132 for (unsigned i = 1; i < newDataLS.size(); ++i) {
133 newDataLS[0].insert(newDataLS[0].end(), newDataLS[i].begin(),
134 newDataLS[i].end());
135
136 newDataSourceIds[0].insert(newDataSourceIds[0].end(),
137 newDataSourceIds[i].begin(),
138 newDataSourceIds[i].end());
139 }
140
141 // transfer data from the old LSs to new LS
142 // Only do so if the same data exists in both LSs
143 // If this is not the case, the data is invalid
144 // and therefore not needed anyway.
145 if (updateData) {
146 const auto &AData = levelSetA->getPointData();
147 const auto &BData = levelSetB->getPointData();
148 auto &newData = newlsDomain->getPointData();
149
150 // scalars — preserve A's fields even when B doesn't have them.
151 // Previously, fields present only on A (e.g. OxVelocity on the oxide
152 // in a RELATIVE_COMPLEMENT with the mask) were silently dropped because
153 // the mask has no matching field. For A-sourced result points we always
154 // use A's value; for B-sourced points without a matching B field we use
155 // 0.
156 for (unsigned i = 0; i < AData.getScalarDataSize(); ++i) {
157 auto scalarDataLabel = AData.getScalarDataLabel(i);
158 auto APointer = AData.getScalarData(i);
159 auto BPointer =
160 BData.getScalarData(scalarDataLabel, true); // may be nullptr
162 scalars.resize(newlsDomain->getNumberOfPoints(), T(0));
163 for (unsigned j = 0; j < newlsDomain->getNumberOfPoints(); ++j) {
164 if (newDataLS[0][j])
165 scalars[j] = APointer->at(newDataSourceIds[0][j]);
166 else if (BPointer != nullptr)
167 scalars[j] = BPointer->at(newDataSourceIds[0][j]);
168 // else: B-sourced point with no matching B field → stays 0
169 }
170 newData.insertNextScalarData(scalars, scalarDataLabel);
171 }
172
173 // vectors — same asymmetric treatment as scalars above.
174 for (unsigned i = 0; i < AData.getVectorDataSize(); ++i) {
175 auto vectorDataLabel = AData.getVectorDataLabel(i);
176 auto APointer = AData.getVectorData(i);
177 auto BPointer =
178 BData.getVectorData(vectorDataLabel, true); // may be nullptr
179 using Vec =
182 vectors.resize(newlsDomain->getNumberOfPoints(), Vec{});
183 for (unsigned j = 0; j < newlsDomain->getNumberOfPoints(); ++j) {
184 if (newDataLS[0][j])
185 vectors[j] = APointer->at(newDataSourceIds[0][j]);
186 else if (BPointer != nullptr)
187 vectors[j] = BPointer->at(newDataSourceIds[0][j]);
188 // else: B-sourced point with no matching B field → stays
189 // zero-initialised
190 }
191 newData.insertNextVectorData(vectors, vectorDataLabel);
192 }
193 }
194
195 newDomain.finalize();
196 newDomain.segment();
197 newlsDomain->setLevelSetWidth(levelSetA->getLevelSetWidth());
198
199 if (pruneResult) {
200 auto pruner = Prune<T, D>(newlsDomain);
201 pruner.setRemoveStrayZeros(true);
202 pruner.apply();
203
204 // now we need to prune, to remove stray defined points
205 Prune<T, D>(newlsDomain).apply();
206 }
207
208 levelSetA->deepCopy(newlsDomain);
209 }
210
211 void invert() {
212 auto &hrleDomain = levelSetA->getDomain();
213#pragma omp parallel for
214 for (unsigned p = 0; p < hrleDomain.getNumberOfSegments(); ++p) {
215 auto &domainSegment = hrleDomain.getDomainSegment(p);
216
217 // change all defined values
218 for (unsigned i = 0; i < domainSegment.definedValues.size(); ++i) {
219 domainSegment.definedValues[i] = -domainSegment.definedValues[i];
220 }
221
222 // add undefined values if missing
223 if (domainSegment.undefinedValues.size() < 1) {
224 domainSegment.undefinedValues.push_back(T(Domain<T, D>::NEG_VALUE));
225 }
226 if (domainSegment.undefinedValues.size() < 2) {
227 if (domainSegment.undefinedValues[0] == Domain<T, D>::NEG_VALUE) {
228 domainSegment.undefinedValues.push_back(T(Domain<T, D>::POS_VALUE));
229 } else {
230 domainSegment.undefinedValues.push_back(T(Domain<T, D>::NEG_VALUE));
231 }
232 }
233
234 // change all runTypes
235 // there are only two undefined runs: negative undefined (UNDEF_PT)
236 // and positive undefined (UNDEF_PT+1)
237 for (unsigned dim = 0; dim < D; ++dim) {
238 for (unsigned c = 0; c < domainSegment.runTypes[dim].size(); ++c) {
239 if (domainSegment.runTypes[dim][c] ==
240 viennahrle::RunTypeValues::UNDEF_PT) {
241 domainSegment.runTypes[dim][c] =
242 viennahrle::RunTypeValues::UNDEF_PT + 1;
243 } else if (domainSegment.runTypes[dim][c] ==
244 viennahrle::RunTypeValues::UNDEF_PT + 1) {
245 domainSegment.runTypes[dim][c] =
246 viennahrle::RunTypeValues::UNDEF_PT;
247 }
248 }
249 }
250 }
251 levelSetA->finalize();
252 }
253
254 static std::pair<T, bool> minComp(const T &a, const T &b) {
255 bool AIsSmaller = a < b;
256 if (AIsSmaller)
257 return std::make_pair(a, true);
258 else
259 return std::make_pair(b, false);
260 }
261
262 static std::pair<T, bool> maxComp(const T &a, const T &b) {
263 bool AIsLarger = a > b;
264 if (AIsLarger)
265 return std::make_pair(a, true);
266 else
267 return std::make_pair(b, false);
268 }
269
270 static std::pair<T, bool> relativeComplementComp(const T &a, const T &b) {
271 return maxComp(a, -b);
272 }
273
274public:
275 BooleanOperation() = default;
276
278 SmartPointer<Domain<T, D>> passedlsDomain,
280 : levelSetA(passedlsDomain), operation(passedOperation) {}
281
283 SmartPointer<Domain<T, D>> passedlsDomainA,
284 SmartPointer<Domain<T, D>> passedlsDomainB,
286 : levelSetA(passedlsDomainA), levelSetB(passedlsDomainB),
287 operation(passedOperation){};
288
290 void setLevelSet(SmartPointer<Domain<T, D>> passedlsDomain) {
291 levelSetA = passedlsDomain;
292 }
293
296 void setSecondLevelSet(SmartPointer<Domain<T, D>> passedlsDomain) {
297 levelSetB = passedlsDomain;
298 }
299
302 operation = passedOperation;
303 }
304
308 operationComp = passedOperationComp;
309 }
310
313 void setUpdatePointData(bool update) { updatePointData = update; }
314
316 void setPruneResult(bool pR) { pruneResult = pR; }
317
319 void apply() {
320 if (levelSetA == nullptr) {
321 VIENNACORE_LOG_ERROR("No level set was passed to BooleanOperation.");
322 return;
323 }
324
325 if (static_cast<unsigned>(operation) < 3) {
326 if (levelSetB == nullptr) {
327 VIENNACORE_LOG_ERROR(
328 "Only one level set was passed to BooleanOperation, "
329 "although two were required.");
330 return;
331 }
332 }
333
334 switch (operation) {
336 booleanOpInternal(&BooleanOperation::maxComp);
337 break;
339 booleanOpInternal(&BooleanOperation::minComp);
340 break;
342 booleanOpInternal(&BooleanOperation::relativeComplementComp);
343 break;
345 invert();
346 break;
348 if (operationComp == nullptr) {
349 VIENNACORE_LOG_ERROR(
350 "No comparator supplied to custom BooleanOperation.");
351 return;
352 }
353 booleanOpInternal(operationComp);
354 }
355 }
356};
357
358// add all template specialisations for this class
359PRECOMPILE_PRECISION_DIMENSION(BooleanOperation)
360
361} // 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:307
void setUpdatePointData(bool update)
Set whether to update the point data stored in the LS during this algorithm. Defaults to true.
Definition lsBooleanOperation.hpp:313
void setPruneResult(bool pR)
Set whether the resulting level set should be pruned. Defaults to true.
Definition lsBooleanOperation.hpp:316
void setLevelSet(SmartPointer< Domain< T, D > > passedlsDomain)
Set which level set to perform the boolean operation on.
Definition lsBooleanOperation.hpp:290
BooleanOperation(SmartPointer< Domain< T, D > > passedlsDomain, BooleanOperationEnum passedOperation=BooleanOperationEnum::INVERT)
Definition lsBooleanOperation.hpp:277
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:296
void setBooleanOperation(BooleanOperationEnum passedOperation)
Set which of the operations of BooleanOperationEnum to perform.
Definition lsBooleanOperation.hpp:301
void apply()
Perform operation.
Definition lsBooleanOperation.hpp:319
BooleanOperation(SmartPointer< Domain< T, D > > passedlsDomainA, SmartPointer< Domain< T, D > > passedlsDomainB, BooleanOperationEnum passedOperation=BooleanOperationEnum::INTERSECT)
Definition lsBooleanOperation.hpp:282
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
Prune()=default
#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