Skip to content

Commit 0aabf6d

Browse files
committed
FIX: PR review fixes for Group B face-neighbor filters
- Fix missing return before MakeErrorResult in ComputeSurfaceFeaturesDirect for 1D geometry error path (pre-existing bug). Add same error path to ComputeSurfaceFeaturesScanline. - Replace std::minmax_element full-scan validation with deferred max tracking during the main loop in ComputeFeatureNeighborsDirect and Scanline, eliminating a redundant OOC full-scan (1.4x OOC speedup). - Add Doxygen @brief comments to operator()() in all 8 new algorithm classes.
1 parent c838f62 commit 0aabf6d

10 files changed

Lines changed: 92 additions & 25 deletions

src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/BadDataNeighborOrientationCheckScanline.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ BadDataNeighborOrientationCheckScanline::BadDataNeighborOrientationCheckScanline
2727
BadDataNeighborOrientationCheckScanline::~BadDataNeighborOrientationCheckScanline() noexcept = default;
2828

2929
// -----------------------------------------------------------------------------
30+
/**
31+
* @brief Flips bad voxels to good using chunk-sequential multi-pass scanning.
32+
* OOC path: Phase 1 counts matching good neighbors using chunk-sequential iteration.
33+
* Phase 2 uses repeated chunk-sequential scans (instead of a worklist) to flip
34+
* eligible voxels, with a chunk-skip optimization that avoids loading chunks
35+
* with no eligible voxels.
36+
*/
3037
Result<> BadDataNeighborOrientationCheckScanline::operator()()
3138
{
3239
const float misorientationTolerance = m_InputValues->MisorientationTolerance * numbers::pi_v<float> / 180.0f;

src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/BadDataNeighborOrientationCheckWorklist.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ BadDataNeighborOrientationCheckWorklist::BadDataNeighborOrientationCheckWorklist
2929
BadDataNeighborOrientationCheckWorklist::~BadDataNeighborOrientationCheckWorklist() noexcept = default;
3030

3131
// -----------------------------------------------------------------------------
32+
/**
33+
* @brief Flips bad voxels to good using a worklist-based propagation algorithm.
34+
* In-core path: Phase 1 counts matching good neighbors per bad voxel.
35+
* Phase 2 iteratively flips eligible voxels using a deque worklist,
36+
* propagating new eligibility to neighbors immediately.
37+
*/
3238
Result<> BadDataNeighborOrientationCheckWorklist::operator()()
3339
{
3440
const float misorientationTolerance = m_InputValues->MisorientationTolerance * numbers::pi_v<float> / 180.0f;

src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeBoundaryCellsDirect.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ ComputeBoundaryCellsDirect::ComputeBoundaryCellsDirect(DataStructure& dataStruct
2222
ComputeBoundaryCellsDirect::~ComputeBoundaryCellsDirect() noexcept = default;
2323

2424
// -----------------------------------------------------------------------------
25+
/**
26+
* @brief Counts boundary faces per voxel using direct Z-Y-X iteration.
27+
* In-core path: iterates all voxels sequentially, checking 6 face neighbors.
28+
*/
2529
Result<> ComputeBoundaryCellsDirect::operator()()
2630
{
2731
const auto& imageGeometry = m_DataStructure.getDataRefAs<ImageGeom>(m_InputValues->ImageGeometryPath);

src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeBoundaryCellsScanline.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ ComputeBoundaryCellsScanline::ComputeBoundaryCellsScanline(DataStructure& dataSt
2222
ComputeBoundaryCellsScanline::~ComputeBoundaryCellsScanline() noexcept = default;
2323

2424
// -----------------------------------------------------------------------------
25+
/**
26+
* @brief Counts boundary faces per voxel using chunk-sequential iteration.
27+
* OOC path: iterates chunks in order via loadChunk/getChunkLowerBounds/getChunkUpperBounds,
28+
* then Z-Y-X within each chunk. Same logic as ComputeBoundaryCellsDirect.
29+
*/
2530
Result<> ComputeBoundaryCellsScanline::operator()()
2631
{
2732
const auto& imageGeometry = m_DataStructure.getDataRefAs<ImageGeom>(m_InputValues->ImageGeometryPath);

src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeFeatureNeighborsDirect.cpp

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
#include "simplnx/Utilities/MessageHelper.hpp"
1010
#include "simplnx/Utilities/NeighborUtilities.hpp"
1111

12-
#include <sstream>
13-
1412
using namespace nx::core;
1513

1614
// -----------------------------------------------------------------------------
@@ -27,6 +25,11 @@ ComputeFeatureNeighborsDirect::ComputeFeatureNeighborsDirect(DataStructure& data
2725
ComputeFeatureNeighborsDirect::~ComputeFeatureNeighborsDirect() noexcept = default;
2826

2927
// -----------------------------------------------------------------------------
28+
/**
29+
* @brief Computes feature neighbor lists using direct sequential iteration.
30+
* In-core path: Phase 1 iterates all voxels to build per-feature neighbor
31+
* counts. Phase 2 deduplicates and computes shared surface areas.
32+
*/
3033
Result<> ComputeFeatureNeighborsDirect::operator()()
3134
{
3235
auto storeBoundaryCells = m_InputValues->StoreBoundaryCells;
@@ -58,16 +61,10 @@ Result<> ComputeFeatureNeighborsDirect::operator()()
5861
usize totalPoints = featureIds.getNumberOfTuples();
5962
usize totalFeatures = numNeighbors.getNumberOfTuples();
6063

61-
/* Ensure that we will be able to work with the user selected featureId Array */
62-
const auto [minFeatureId, maxFeatureId] = std::minmax_element(featureIds.begin(), featureIds.end());
63-
if(static_cast<usize>(*maxFeatureId) >= totalFeatures)
64-
{
65-
std::stringstream out;
66-
out << "Data Array " << featureIdsPath.getTargetName() << " has a maximum value of " << *maxFeatureId << " which is greater than the "
67-
<< " number of features from array " << numNeighborsPath.getTargetName() << " which has " << totalFeatures << ". Did you select the "
68-
<< " incorrect array for the 'FeatureIds' array?";
69-
return MakeErrorResult(-24500, out.str());
70-
}
64+
// Max feature ID validation is deferred to after the main loop to avoid
65+
// a separate full scan through the data store. The loop's
66+
// `feature < neighborlist.size()` guard prevents out-of-bounds access.
67+
int32 observedMaxFeatureId = 0;
7168

7269
auto& imageGeom = m_DataStructure.getDataRefAs<ImageGeom>(imageGeomPath);
7370
SizeVec3 uDims = imageGeom.getDimensions();
@@ -123,6 +120,10 @@ Result<> ComputeFeatureNeighborsDirect::operator()()
123120

124121
onsurf = 0;
125122
feature = featureIds[voxelIndex];
123+
if(feature > observedMaxFeatureId)
124+
{
125+
observedMaxFeatureId = feature;
126+
}
126127
if(feature > 0 && feature < neighborlist.size())
127128
{
128129
int64 xIdx = voxelIndex % dims[0];
@@ -168,6 +169,15 @@ Result<> ComputeFeatureNeighborsDirect::operator()()
168169
}
169170
}
170171

172+
// Validate max feature ID (deferred from before the loop to avoid a separate full scan)
173+
if(static_cast<usize>(observedMaxFeatureId) >= totalFeatures)
174+
{
175+
return MakeErrorResult(-24500,
176+
fmt::format("Data Array {} has a maximum value of {} which is greater than the number of features from array {} which has {}. "
177+
"Did you select the incorrect array for the 'FeatureIds' array?",
178+
featureIdsPath.getTargetName(), observedMaxFeatureId, numNeighborsPath.getTargetName(), totalFeatures));
179+
}
180+
171181
FloatVec3 spacing = imageGeom.getSpacing();
172182

173183
// We do this to create new set of NeighborList objects

src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeFeatureNeighborsScanline.cpp

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
#include "simplnx/Utilities/MessageHelper.hpp"
1010
#include "simplnx/Utilities/NeighborUtilities.hpp"
1111

12-
#include <sstream>
13-
1412
using namespace nx::core;
1513

1614
// -----------------------------------------------------------------------------
@@ -27,6 +25,12 @@ ComputeFeatureNeighborsScanline::ComputeFeatureNeighborsScanline(DataStructure&
2725
ComputeFeatureNeighborsScanline::~ComputeFeatureNeighborsScanline() noexcept = default;
2826

2927
// -----------------------------------------------------------------------------
28+
/**
29+
* @brief Computes feature neighbor lists using chunk-sequential iteration.
30+
* OOC path: Phase 1 iterates chunks in order to build per-feature neighbor
31+
* counts. Phase 2 deduplicates and computes shared surface areas.
32+
* Same logic as ComputeFeatureNeighborsDirect.
33+
*/
3034
Result<> ComputeFeatureNeighborsScanline::operator()()
3135
{
3236
auto storeBoundaryCells = m_InputValues->StoreBoundaryCells;
@@ -58,16 +62,10 @@ Result<> ComputeFeatureNeighborsScanline::operator()()
5862
usize totalPoints = featureIds.getNumberOfTuples();
5963
usize totalFeatures = numNeighbors.getNumberOfTuples();
6064

61-
/* Ensure that we will be able to work with the user selected featureId Array */
62-
const auto [minFeatureId, maxFeatureId] = std::minmax_element(featureIds.begin(), featureIds.end());
63-
if(static_cast<usize>(*maxFeatureId) >= totalFeatures)
64-
{
65-
std::stringstream out;
66-
out << "Data Array " << featureIdsPath.getTargetName() << " has a maximum value of " << *maxFeatureId << " which is greater than the "
67-
<< " number of features from array " << numNeighborsPath.getTargetName() << " which has " << totalFeatures << ". Did you select the "
68-
<< " incorrect array for the 'FeatureIds' array?";
69-
return MakeErrorResult(-24500, out.str());
70-
}
65+
// Max feature ID validation is deferred to after the chunk-sequential loop
66+
// to avoid a separate full scan through OOC data. The loop's
67+
// `feature < neighborlist.size()` guard prevents out-of-bounds access.
68+
int32 observedMaxFeatureId = 0;
7169

7270
auto& imageGeom = m_DataStructure.getDataRefAs<ImageGeom>(imageGeomPath);
7371
SizeVec3 uDims = imageGeom.getDimensions();
@@ -140,6 +138,10 @@ Result<> ComputeFeatureNeighborsScanline::operator()()
140138
const int64 voxelIndex = kStride + jStride + static_cast<int64>(xIdx);
141139
onsurf = 0;
142140
feature = featureIds[voxelIndex];
141+
if(feature > observedMaxFeatureId)
142+
{
143+
observedMaxFeatureId = feature;
144+
}
143145
if(feature > 0 && feature < neighborlist.size())
144146
{
145147
const int64 ix = static_cast<int64>(xIdx);
@@ -188,6 +190,15 @@ Result<> ComputeFeatureNeighborsScanline::operator()()
188190
}
189191
}
190192

193+
// Validate max feature ID (deferred from before the loop to avoid a separate OOC scan)
194+
if(static_cast<usize>(observedMaxFeatureId) >= totalFeatures)
195+
{
196+
return MakeErrorResult(-24500,
197+
fmt::format("Data Array {} has a maximum value of {} which is greater than the number of features from array {} which has {}. "
198+
"Did you select the incorrect array for the 'FeatureIds' array?",
199+
featureIdsPath.getTargetName(), observedMaxFeatureId, numNeighborsPath.getTargetName(), totalFeatures));
200+
}
201+
191202
// Phase 2: Build NeighborList objects (operates on small feature-level data, no chunk iteration needed)
192203
FloatVec3 spacing = imageGeom.getSpacing();
193204

src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeSurfaceAreaToVolumeDirect.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ ComputeSurfaceAreaToVolumeDirect::ComputeSurfaceAreaToVolumeDirect(DataStructure
2424
ComputeSurfaceAreaToVolumeDirect::~ComputeSurfaceAreaToVolumeDirect() noexcept = default;
2525

2626
// -----------------------------------------------------------------------------
27+
/**
28+
* @brief Computes surface-area-to-volume ratio using direct Z-Y-X iteration.
29+
* In-core path: accumulates per-feature surface area from face-neighbor
30+
* comparisons, then divides by voxel volume. Optionally computes sphericity.
31+
*/
2732
Result<> ComputeSurfaceAreaToVolumeDirect::operator()()
2833
{
2934
// Input Cell Data

src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeSurfaceAreaToVolumeScanline.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ ComputeSurfaceAreaToVolumeScanline::ComputeSurfaceAreaToVolumeScanline(DataStruc
2424
ComputeSurfaceAreaToVolumeScanline::~ComputeSurfaceAreaToVolumeScanline() noexcept = default;
2525

2626
// -----------------------------------------------------------------------------
27+
/**
28+
* @brief Computes surface-area-to-volume ratio using chunk-sequential iteration.
29+
* OOC path: iterates chunks in order via loadChunk/getChunkLowerBounds/getChunkUpperBounds.
30+
* Same logic as ComputeSurfaceAreaToVolumeDirect.
31+
*/
2732
Result<> ComputeSurfaceAreaToVolumeScanline::operator()()
2833
{
2934
// Input Cell Data

src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeSurfaceFeaturesDirect.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ ComputeSurfaceFeaturesDirect::ComputeSurfaceFeaturesDirect(DataStructure& dataSt
196196
ComputeSurfaceFeaturesDirect::~ComputeSurfaceFeaturesDirect() noexcept = default;
197197

198198
// -----------------------------------------------------------------------------
199+
/**
200+
* @brief Identifies surface features using direct Z-Y-X iteration.
201+
* In-core path: delegates to findSurfaceFeatures3D or findSurfaceFeatures2D
202+
* depending on geometry dimensionality.
203+
*/
199204
Result<> ComputeSurfaceFeaturesDirect::operator()()
200205
{
201206
const auto pMarkFeature0NeighborsValue = m_InputValues->MarkFeature0Neighbors;
@@ -223,7 +228,7 @@ Result<> ComputeSurfaceFeaturesDirect::operator()()
223228
}
224229
else
225230
{
226-
MakeErrorResult(-1000, fmt::format("Image Geometry at path '{}' must be either 3D or 2D", pFeatureGeometryPathValue.toString()));
231+
return MakeErrorResult(-1000, fmt::format("Image Geometry at path '{}' must be either 3D or 2D", pFeatureGeometryPathValue.toString()));
227232
}
228233

229234
return {};

src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeSurfaceFeaturesScanline.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ ComputeSurfaceFeaturesScanline::ComputeSurfaceFeaturesScanline(DataStructure& da
110110
ComputeSurfaceFeaturesScanline::~ComputeSurfaceFeaturesScanline() noexcept = default;
111111

112112
// -----------------------------------------------------------------------------
113+
/**
114+
* @brief Identifies surface features using chunk-sequential iteration.
115+
* OOC path: iterates chunks in order, handling both 3D and 2D geometries
116+
* with coordinate remapping. Same logic as ComputeSurfaceFeaturesDirect.
117+
*/
113118
Result<> ComputeSurfaceFeaturesScanline::operator()()
114119
{
115120
const auto pMarkFeature0NeighborsValue = m_InputValues->MarkFeature0Neighbors;
@@ -216,6 +221,10 @@ Result<> ComputeSurfaceFeaturesScanline::operator()()
216221
surfaceFeatures[gNum] = 1;
217222
}
218223
}
224+
else
225+
{
226+
return MakeErrorResult(-1000, fmt::format("Image Geometry at path '{}' must be either 3D or 2D", pFeatureGeometryPathValue.toString()));
227+
}
219228
}
220229
}
221230
}

0 commit comments

Comments
 (0)