Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@ not have rotated and thus serve as a better *reference orientation*.
The *Reference Orientation* parameter provides the following choices:

- **Average Feature Orientation [0]**: Uses the average orientation of the **Feature** as the reference orientation for misorientation calculations.
- **Orientation Farthest from Feature Boundary [1]**: Uses the orientation of the **Cell** that is furthest from the boundary of the **Feature** (nearest to its Euclidean center) as the reference orientation.
- **Orientation Farthest from Feature Boundary [1]**: Uses the orientation of the **Cell** that is furthest from the boundary of the **Feature** (nearest to its Euclidean center) as the reference orientation. Requires a `Boundary Euclidean Distances` array as input; that array is typically produced by the [Compute Euclidean Distance Map](../SimplnxCore/ComputeEuclideanDistMapFilter.md) filter upstream of this one.

### Output Units

The misorientation values in both output arrays (`Cell Reference Misorientations` and `Feature Average Misorientations`) are expressed in **degrees**, not radians.

### Mode 1 — Raster-order tie-break for the "farthest from boundary" voxel

When two or more voxels within a single feature share the same maximum `Boundary Euclidean Distances` value, the algorithm selects the voxel with the **latest linear (raster) index** as the feature's reference. This matches the legacy DREAM3D 6.5.171 behavior. In practice, ties are rare on real EBSD data and the choice between tied voxels has no qualitative impact on the resulting misorientation field; for synthetic inputs that deliberately tie distances, the reader should be aware that re-ordering the voxel layout would change which voxel is selected as the reference.

## IPF Colors <001> Direction

Expand All @@ -49,7 +57,14 @@ feature boundary, and use that voxel's orientation as the **reference orientatio

## Example Pipelines

+ (05) SmallIN100 Crystallographic Statistics
+ (04) Small IN100 Crystallographic Statistics

## Related Filters

- [Compute Feature Reference C-Axis Misorientations](ComputeFeatureReferenceCAxisMisorientationsFilter.md) — the C-axis variant of this filter, used for hexagonal-phase reconstructions.
- [Compute Kernel Average Misorientations](ComputeKernelAvgMisorientationsFilter.md) — computes a per-voxel kernel average misorientation; complementary to this filter for grain-boundary characterization.
- [Compute Feature Neighbor Misorientations](ComputeFeatureNeighborMisorientationsFilter.md) — computes pairwise feature-to-neighbor misorientations.
- [Compute Euclidean Distance Map](../SimplnxCore/ComputeEuclideanDistMapFilter.md) — typical upstream filter that produces the `Boundary Euclidean Distances` array required by Mode 1.

## License & Copyright

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ const std::atomic_bool& ComputeFeatureReferenceMisorientations::getCancel()
// -----------------------------------------------------------------------------
Result<> ComputeFeatureReferenceMisorientations::operator()()
{
// The ImageGeom owning this filter's cell data lives two parents above the Cell Phases array
// (geometry -> CellData attribute matrix -> Phases array). This derivation matches the standard
// ImageGeom layout produced by the SIMPLNX data structure; if the user has restructured the data
// tree, this preflight-validated path may not hold.
DataPath imageGeomPath = m_InputValues->CellPhasesArrayPath.getParent().getParent();
const ImageGeom& imageGeom = m_DataStructure.getDataRefAs<ImageGeom>(imageGeomPath);

Expand All @@ -59,11 +63,13 @@ Result<> ComputeFeatureReferenceMisorientations::operator()()
return validateNumFeatResult;
}

std::vector<ebsdlib::LaueOps::Pointer> m_OrientationOps = ebsdlib::LaueOps::GetAllOrientationOps();
std::vector<ebsdlib::LaueOps::Pointer> orientationOps = ebsdlib::LaueOps::GetAllOrientationOps();

const size_t totalVoxels = featureIds.getNumberOfTuples();

// Get the total features from the appropriate source..
// Get the total features from the appropriate source.. Mode 0 prefers the avgQuats array's tuple
// count; Mode 1 falls back to the feature attribute matrix's shape. Either resolves the same total
// for any consistent input data; the dual-source check tolerates either parameter set being unset.
size_t totalFeatures = 0;
if(featureAttrMatPtr != nullptr)
{
Expand All @@ -78,14 +84,14 @@ Result<> ComputeFeatureReferenceMisorientations::operator()()
return MakeErrorResult(-34900, "Total features was zero. The filter cannot proceed. Check either the feature attribute matrix or the average quaternions for proper size");
}

// Create local storage for the centers and center distances
std::vector<size_t> m_Centers(totalFeatures, 0);
std::vector<float> m_CenterDistances(totalFeatures, 0.0f);
// Create local storage for the centers and center distances (sized to feature count, not voxel count).
std::vector<size_t> centers(totalFeatures, 0);
std::vector<float> centerDistances(totalFeatures, 0.0f);

// If the user selected "Misorientation from Feature Centers"
if(m_InputValues->ReferenceOrientation == 1)
{
const auto& m_GBEuclideanDistances = m_DataStructure.getDataRefAs<Float32Array>(m_InputValues->GBEuclideanDistancesArrayPath);
const auto& gbEuclideanDistances = m_DataStructure.getDataRefAs<Float32Array>(m_InputValues->GBEuclideanDistancesArrayPath);
for(size_t voxelIdx = 0; voxelIdx < totalVoxels; voxelIdx++)
{
if(m_ShouldCancel)
Expand All @@ -94,19 +100,23 @@ Result<> ComputeFeatureReferenceMisorientations::operator()()
}

int32_t featureId = featureIds[voxelIdx];
float32 distance = m_GBEuclideanDistances[voxelIdx];
if(distance >= m_CenterDistances[featureId])
float32 distance = gbEuclideanDistances[voxelIdx];
// Tie-break: '>=' means later voxels with the same distance overwrite earlier ones. The
// selection is therefore raster-order dependent — different DataStructure layouts that
// expose the same logical voxels in a different iteration order would yield different
// centers[]. This matches the legacy DREAM3D 6.5.171 behavior intentionally.
if(distance >= centerDistances[featureId])
{
m_CenterDistances[featureId] = distance; // Save the GB Distance value
m_Centers[featureId] = voxelIdx; // Save the voxel index for that value
centerDistances[featureId] = distance; // Save the GB Distance value
centers[featureId] = voxelIdx; // Save the voxel index for that value
}
}

const auto& euclideanCellCenters = m_DataStructure.getDataAs<Float32Array>(m_InputValues->FeatureEuclideanCentersPath)->getIDataStoreAs<AbstractDataStore<float32>>();

for(size_t i = 1; i < totalFeatures; i++)
{
usize voxelIdx = m_Centers[i];
usize voxelIdx = centers[i];
auto cellCenter = imageGeom.getCoordsf(voxelIdx);
euclideanCellCenters->setTuple(i, cellCenter.data());
}
Expand Down Expand Up @@ -136,12 +146,12 @@ Result<> ComputeFeatureReferenceMisorientations::operator()()
else if(m_InputValues->ReferenceOrientation == 1) // Use the voxel's orientation that is the farthest from the grain boundary
{
auto featureId = static_cast<size_t>(featureIds[voxelIdx]);
size_t centerVoxelIdx = m_Centers[featureId];
size_t centerVoxelIdx = centers[featureId];
q2 = ebsdlib::QuatD(quats[centerVoxelIdx * 4 + 0], quats[centerVoxelIdx * 4 + 1], quats[centerVoxelIdx * 4 + 2], quats[centerVoxelIdx * 4 + 3]);
}

uint32 laueClass1 = crystalStructures[cellPhases[voxelIdx]];
ebsdlib::AxisAngleDType axisAngle = m_OrientationOps[laueClass1]->calculateMisorientation(q1, q2);
ebsdlib::AxisAngleDType axisAngle = orientationOps[laueClass1]->calculateMisorientation(q1, q2);

// Extract the misorientation, convert it to degrees, and store if for this voxel
featureReferenceMisorientations[voxelIdx] = static_cast<float>(Constants::k_RadToDegD * axisAngle[3]); // convert to degrees
Expand Down
6 changes: 5 additions & 1 deletion src/Plugins/OrientationAnalysis/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,11 @@ if(EXISTS "${DREAM3D_DATA_DIR}" AND SIMPLNX_DOWNLOAD_TEST_FILES)
download_test_data(DREAM3D_DATA_DIR ${DREAM3D_DATA_DIR} ARCHIVE_NAME so3_cubic_high_ipf_001.tar.gz SHA512 dfe4598cd4406e8b83f244302dc4fe0d4367527835c5ddd6567fe8d8ab3484d5b10ba24a8bb31db269256ec0b5272daa4340eedb5a8b397755541b32dd616b85)
download_test_data(DREAM3D_DATA_DIR ${DREAM3D_DATA_DIR} ARCHIVE_NAME write_stats_gen_odf_angle_file.tar.gz SHA512 be3f663aae1f78e5b789200421534ed9fe293187ec3514796ac8177128b34ded18bb9a98b8e838bb283f9818ac30dc4b19ec379bdd581b1a98eb36d967cdd319)
download_test_data(DREAM3D_DATA_DIR ${DREAM3D_DATA_DIR} ARCHIVE_NAME 6_5_MergeTwins.tar.gz SHA512 756da6b9a2fdc6c7f1cf611243b889b8da0bdc172c1cd184f81672c3cdf651f1f450aecff2e2e0c9b1fa367735ca1df26436d88fa342cea1825b4e5665aa7dfd)
download_test_data(DREAM3D_DATA_DIR ${DREAM3D_DATA_DIR} ARCHIVE_NAME compute_feature_reference_misorientation.tar.gz SHA512 6ea9c04ca5b0c0439573b5a14bda63592181c6badb4dd325b542fb97ff2a5d492e83d2bac1bf5999612cbdb7697ec48e321549427470f1f23ccd37921c6a95f1)
# `compute_feature_reference_misorientation.tar.gz` retired 2026-06-01 (V&V cycle for
# ComputeFeatureReferenceMisorientationsFilter). The exemplar arrays in that archive were a
# circular oracle (regenerated from pre-EbsdLib-2.4.1 SIMPLNX output). Replaced by inline
# Class 1 + Class 4 toy fixtures in ComputeFeatureReferenceMisorientationsTest.cpp. See
# src/Plugins/OrientationAnalysis/vv/provenance/ComputeFeatureReferenceMisorientationsFilter.md.

endif()

Expand Down
Loading
Loading