36 using hrleIndexType = viennahrle::IndexType;
38 SmartPointer<Domain<T, D>> levelSetTarget =
nullptr;
39 SmartPointer<Domain<T, D>> levelSetSample =
nullptr;
49 std::vector<RangeSpec> rangeSpecs;
52 struct CriticalDimensionResult {
63 std::vector<CriticalDimensionResult> results;
66 SmartPointer<Mesh<T>> outputMesh =
nullptr;
69 if (levelSetTarget ==
nullptr || levelSetSample ==
nullptr) {
71 .addWarning(
"Missing level set in SampleCriticalDimensions.")
77 const auto &gridTarget = levelSetTarget->getGrid();
78 const auto &gridSample = levelSetSample->getGrid();
80 if (gridTarget.getGridDelta() != gridSample.getGridDelta()) {
82 .addWarning(
"Grid delta mismatch in CompareCriticalDimensions. The "
83 "grid deltas of the two level sets must be equal.")
88 if (rangeSpecs.empty()) {
90 .addWarning(
"No ranges specified in CompareCriticalDimensions.")
100 std::vector<T> findSurfaceCrossings(SmartPointer<
Mesh<T>> surfaceMesh,
101 bool isXRange,
T scanMin,
T scanMax,
102 T perpMin,
T perpMax) {
103 std::vector<T> crossings;
106 for (
const auto &node : surfaceMesh->nodes) {
111 bool inRange =
false;
114 inRange = (xCoord >= scanMin && xCoord <= scanMax);
117 inRange = (yCoord >= scanMin && yCoord <= scanMax);
122 T perpCoord = isXRange ? yCoord : xCoord;
125 if (perpCoord >= perpMin && perpCoord <= perpMax) {
126 crossings.push_back(perpCoord);
135 std::pair<bool, T> findCriticalDimension(
const std::vector<T> &crossings,
137 if (crossings.empty()) {
142 return {
true, *std::max_element(crossings.begin(), crossings.end())};
144 return {
true, *std::min_element(crossings.begin(), crossings.end())};
150 static_assert(
D == 2 &&
"CompareCriticalDimensions is currently only "
151 "implemented for 2D level sets.");
156 : levelSetTarget(passedLevelSetTarget),
157 levelSetSample(passedLevelSetSample) {
158 static_assert(
D == 2 &&
"CompareCriticalDimensions is currently only "
159 "implemented for 2D level sets.");
163 levelSetTarget = passedLevelSet;
167 levelSetSample = passedLevelSet;
173 spec.isXRange =
true;
174 spec.rangeMin = minX;
175 spec.rangeMax = maxX;
176 spec.findMaximum = findMaximum;
177 rangeSpecs.push_back(spec);
183 spec.isXRange =
false;
184 spec.rangeMin = minY;
185 spec.rangeMax = maxY;
186 spec.findMaximum = findMaximum;
187 rangeSpecs.push_back(spec);
195 outputMesh = passedMesh;
202 if (!checkInputs()) {
206 const auto &grid = levelSetTarget->getGrid();
207 const T gridDelta = grid.getGridDelta();
210 auto surfaceMeshRef = SmartPointer<Mesh<T>>::New();
211 auto surfaceMeshCmp = SmartPointer<Mesh<T>>::New();
219 T xMin = std::numeric_limits<T>::max();
220 T xMax = std::numeric_limits<T>::lowest();
221 T yMin = std::numeric_limits<T>::max();
222 T yMax = std::numeric_limits<T>::lowest();
224 for (
const auto &node : surfaceMeshRef->nodes) {
225 xMin = std::min(xMin, node[0]);
226 xMax = std::max(xMax, node[0]);
227 yMin = std::min(yMin, node[1]);
228 yMax = std::max(yMax, node[1]);
230 for (
const auto &node : surfaceMeshCmp->nodes) {
231 xMin = std::min(xMin, node[0]);
232 xMax = std::max(xMax, node[0]);
233 yMin = std::min(yMin, node[1]);
234 yMax = std::max(yMax, node[1]);
238 results.resize(rangeSpecs.size());
241#pragma omp parallel for
242 for (
size_t specIdx = 0; specIdx < rangeSpecs.size(); ++specIdx) {
243 const auto &spec = rangeSpecs[specIdx];
244 CriticalDimensionResult result;
245 result.isXRange = spec.isXRange;
246 result.rangeMin = spec.rangeMin;
247 result.rangeMax = spec.rangeMax;
248 result.findMaximum = spec.findMaximum;
249 result.valid =
false;
252 T scanMin, scanMax, perpMin, perpMax;
255 scanMin = spec.rangeMin;
256 scanMax = spec.rangeMax;
261 scanMin = spec.rangeMin;
262 scanMax = spec.rangeMax;
268 auto crossingsRef = findSurfaceCrossings(
269 surfaceMeshRef, spec.isXRange, scanMin, scanMax, perpMin, perpMax);
270 auto crossingsCmp = findSurfaceCrossings(
271 surfaceMeshCmp, spec.isXRange, scanMin, scanMax, perpMin, perpMax);
274 auto [validRef, cdRef] =
275 findCriticalDimension(crossingsRef, spec.findMaximum);
276 auto [validCmp, cdCmp] =
277 findCriticalDimension(crossingsCmp, spec.findMaximum);
279 if (validRef && validCmp) {
281 result.positionTarget = cdRef;
282 result.positionSample = cdCmp;
283 result.difference = std::abs(cdRef - cdCmp);
287 results[specIdx] = result;
291 if (outputMesh !=
nullptr) {
301 T &positionSample,
T &difference)
const {
302 if (index >= results.size() || !results[index].valid) {
305 positionTarget = results[index].positionTarget;
306 positionSample = results[index].positionSample;
307 difference = results[index].difference;
315 for (
const auto &result : results) {
317 sum += result.difference;
321 return count > 0 ? sum / count : std::numeric_limits<T>::quiet_NaN();
327 for (
const auto &result : results) {
329 maxDiff = std::max(maxDiff, result.difference);
339 for (
const auto &result : results) {
341 sumSquared += result.difference * result.difference;
345 return count > 0 ? std::sqrt(sumSquared / count)
346 : std::numeric_limits<T>::quiet_NaN();
351 std::vector<T> differences;
352 for (
const auto &result : results) {
354 differences.push_back(result.difference);
361 void generateMesh() {
364 std::vector<Vec3D<T>> nodeCoordinates;
365 std::vector<std::array<unsigned, 1>> vertexIndices;
366 std::vector<T> differenceValues;
367 std::vector<T> targetValues;
368 std::vector<T> sampleValues;
370 for (
unsigned i = 0; i <
D; ++i) {
371 outputMesh->minimumExtent[i] = std::numeric_limits<T>::max();
372 outputMesh->maximumExtent[i] = std::numeric_limits<T>::lowest();
375 unsigned pointId = 0;
376 for (
const auto &result : results) {
381 Vec3D<T> coordTarget, coordSample;
383 if (result.isXRange) {
385 T xMid = (result.rangeMin + result.rangeMax) / 2.0;
386 coordTarget = {xMid, result.positionTarget, 0.0};
387 coordSample = {xMid, result.positionSample, 0.0};
390 T yMid = (result.rangeMin + result.rangeMax) / 2.0;
391 coordTarget = {result.positionTarget, yMid, 0.0};
392 coordSample = {result.positionSample, yMid, 0.0};
396 nodeCoordinates.push_back(coordTarget);
397 vertexIndices.push_back({pointId++});
398 differenceValues.push_back(result.difference);
399 targetValues.push_back(result.positionTarget);
400 sampleValues.push_back(result.positionSample);
403 nodeCoordinates.push_back(coordSample);
404 vertexIndices.push_back({pointId++});
405 differenceValues.push_back(result.difference);
406 targetValues.push_back(result.positionTarget);
407 sampleValues.push_back(result.positionSample);
410 for (
unsigned i = 0; i <
D; ++i) {
411 outputMesh->minimumExtent[i] = std::min(
412 {outputMesh->minimumExtent[i], coordTarget[i], coordSample[i]});
413 outputMesh->maximumExtent[i] = std::max(
414 {outputMesh->maximumExtent[i], coordTarget[i], coordSample[i]});
418 if (!nodeCoordinates.empty()) {
419 outputMesh->nodes = std::move(nodeCoordinates);
420 outputMesh->vertices = std::move(vertexIndices);
421 outputMesh->pointData.insertNextScalarData(std::move(differenceValues),
423 outputMesh->pointData.insertNextScalarData(std::move(targetValues),
425 outputMesh->pointData.insertNextScalarData(std::move(sampleValues),
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:27