Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6c831e1
ENH: Pole figure pipeline overhaul for MTEX compatibility
imikejackson Apr 24, 2026
b48ba4d
ENH: Squash-merge topic/color_palettes
imikejackson Apr 24, 2026
374e4d2
ENH: Squash-merge topic/add_inverse_pole_figure
imikejackson Apr 24, 2026
017ff29
TEST: IPF corner-probe + MTEX IPF legend comparison tool
imikejackson Apr 24, 2026
00188bc
TEST: Emit both TSL and Nolze-Hielscher IPF legends for MTEX comparison
imikejackson Apr 25, 2026
ecb649f
TEST: Emit gridded Nolze-Hielscher IPF legend variant for MTEX matching
imikejackson Apr 25, 2026
c9b922c
TEST: Emit gridded TSL IPF legend variant for MTEX matching
imikejackson Apr 25, 2026
873e61c
BUG: Fix stray vertical column in 622 IPF legend rendering
imikejackson Apr 25, 2026
2c20533
BUG: GriddedColorKey 3-arg direction2Color must honor angleLimits
imikejackson Apr 25, 2026
89aca99
BUG: GriddedColorKey snap must not push (eta, chi) outside SST
imikejackson Apr 25, 2026
b3aafc4
BUG: Drop eta clamp in GriddedColorKey, only clamp chi
imikejackson Apr 25, 2026
7c6f8ea
ENH: Switch IPF/PF outputs from TIFF to PNG via STB
imikejackson Apr 25, 2026
fb570fc
DOC: Validate EbsdLib TSL against EDAX_TSL_IPF.bmp reference
imikejackson Apr 25, 2026
7b576b6
ENH: Add PUCMColorKey (perceptually uniform IPF coloring)
imikejackson Apr 25, 2026
d010158
TEST: Emit PUCM IPF legend (per-pixel and gridded) per Laue class
imikejackson Apr 26, 2026
843ac04
Phase-0 Design commits
imikejackson Apr 30, 2026
ce53bd4
ENH: HexagonalOps SymOps struct + convention-aware sym op tables (PR 2a)
imikejackson Apr 30, 2026
b9dc42c
ENH: HexagonalOps generateSphereCoordsFromEulers honors HexConvention…
imikejackson Apr 30, 2026
e3ed1c3
ENH: HexagonalOps IPF color path honors HexConvention (PR 2c)
imikejackson May 1, 2026
47ff437
ENH: Propagate SymOps + HexConvention dispatch to HexLow / TrigHigh /…
imikejackson May 1, 2026
8a66c55
DOC: Document canonical=X||a* decision and v2->v3 ordering finding (P…
imikejackson May 1, 2026
ee11472
ENH: render_ebsd CLI driver + smoke test for PF/IPF/legend matrix (PR…
imikejackson May 1, 2026
9395592
BUG: PoleFigureCompositor was dropping config.hexConvention (PR 2g)
imikejackson May 1, 2026
58972ed
ENH: IPF legend labels honor HexConvention (PR 2h)
imikejackson May 1, 2026
869564d
ENH: getDefaultPoleFigureNames honors HexConvention (PR 2i)
imikejackson May 5, 2026
7ff3759
ENH: render_ebsd CLI rejects missing-output-dir and bad positional args
imikejackson May 5, 2026
3641a48
STY: clang-format reflow + ODF bin clarifications
imikejackson May 5, 2026
6b338b0
ENH: Crop IPF triangle legend to content (PR 2j)
imikejackson May 7, 2026
82fec52
ENH: InversePoleFigureConfiguration_t carries HexConvention (PR 2k)
imikejackson May 7, 2026
ed272c8
ENH: ColorKeyKind dispatch on LaueOps; drop HexConvention from IPF color
imikejackson May 12, 2026
6084a50
BUG: PUCMColorKey thread-races wlenthe lazy lookup-table init
imikejackson May 12, 2026
d930916
TEST: Fix PoleFigure generation tests with new exemplars.
imikejackson May 12, 2026
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
46 changes: 46 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"permissions": {
"allow": [
"Bash(ls:*)",
"Bash(cmake --build:*)",
"Bash(ctest:*)",
"Bash(cmake:*)",
"Bash(python3:*)",
"Bash(ninja -t targets:*)",
"Bash(git rm:*)",
"Bash(tar:*)",
"Bash(test:*)",
"Bash(git commit:*)",
"Bash(git push:*)",
"Bash(/Users/mjackson/Workspace5/DREAM3D-Build/NX-Com-Qt69-Vtk95-Rel/Bin/SimplnxCoreUnitTest:*)",
"Bash(/opt/local/cmake-3.30.3-macos-universal/CMake.app/Contents/bin/cmake:*)",
"Bash(for f in segment_features_neighbor_scheme_test.tar.gz segment_features_test_data.tar.gz)",
"Bash(do /opt/local/cmake-3.30.3-macos-universal/CMake.app/Contents/bin/cmake -E tar xzf \"$f\")",
"Bash(done)",
"Bash(for f in 6_5_test_data_1_v2.tar.gz segment_features_test_data.tar.gz 6_6_ebsd_segment_features.tar.gz segment_features_neighbor_scheme_test.tar.gz)",
"Bash(do echo \"Extracting $f...\")",
"Bash(echo:*)",
"Bash(for f in 6_5_test_data_1_v2.tar.gz segment_features_test_data.tar.gz segment_features_neighbor_scheme_test.tar.gz)",
"Bash(do tar -xzf \"$f\")",
"Bash(/Users/mjackson/Workspace5/DREAM3D-Build/NX-Com-Qt69-Vtk95-Rel/Bin/OrientationAnalysisUnitTest:*)",
"Bash(xargs sed:*)",
"Bash(grep:*)",
"Bash(git status:*)",
"Bash(git add:*)",
"Bash(git push:*)",
"Bash(tee:*)",
"Bash(git mv:*)",
"Bash(perl -pe:*)",
"Bash(find:*)",
"Bash(perl -pi -e:*)",
"Bash(perl -pi -e 's/k_TypeName = \"\"AbstractNodeGeometry0D\"\"/k_TypeName = \"\"INodeGeometry0D\"\"/g':*)",
"Bash(gh pr view:*)",
"Bash(/opt/local/bin/gh pr view:*)",
"WebFetch(domain:github.com)",
"Bash(git checkout:*)"
],
"deny": [
"Bash(rm -rf *)"
]
}
}
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ option(EbsdLib_BUILD_H5SUPPORT "Build H5Support Library" OFF)


# set project's name
project(EbsdLibProj VERSION 2.4.0)
project(EbsdLibProj VERSION 3.0.0)


# Request C++17 standard, using new CMake variables.
Expand Down
420 changes: 420 additions & 0 deletions Code_Review/coloring_schemes_vs_mtex.md

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions Code_Review/compare_ipf_legends_all_laue.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
% compare_ipf_legends_all_laue.m
%
% Companion to EbsdLib's IPFLegendTest::MTEXCompare_AllLaueClasses.
%
% For every Laue class in IPFComparison/, writes two MTEX legends so the
% pairs can be compared apples-to-apples against the EbsdLib outputs:
% ebsdlib_ipf_legend_tsl.png vs mtex_ipf_legend_tsl.png
% ebsdlib_ipf_legend_nh.png vs mtex_ipf_legend_hsv.png
% The TSL pair uses MTEX's ipfTSLKey; the NH pair uses MTEX's ipfHSVKey,
% which is the Nolze-Hielscher-style HSV scheme that EbsdLib's
% NolzeHielscherColorKey is modeled on.
%
% Usage:
% 1. Build and run the EbsdLib unit test first:
% cd .../DREAM3D-Build/ebsdlib-Release && \
% Bin/EbsdLibUnitTest "ebsdlib::IPFLegendTest::MTEXCompare_AllLaueClasses"
% 2. Edit `baseDir` below to point at the IPFComparison directory
% 3. Run this script in MATLAB (MTEX must be on the path: startup_mtex)


baseDir = '/Users/mjackson/Workspace7/DREAM3D-Build/ebsdlib-Release/Testing/Temporary/IPFComparison';

if ~exist(baseDir, 'dir')
error('baseDir does not exist: %s\nRun the IPFLegendTest MTEXCompare first.', baseDir);
end

setMTEXpref('xAxisDirection', 'east');
setMTEXpref('zAxisDirection', 'outOfPlane');

% Map EbsdLib rotation point group -> MTEX crystalSymmetry.
% Hexagonal/trigonal use X||a to match EbsdLib's X||a* after the 30-degree
% reciprocal-basis reinterpretation. (MTEX's default is X||a*; using X||a
% here rotates MTEX 30 degrees to match EbsdLib.)
laueMap = containers.Map();
laueMap('432') = crystalSymmetry('m-3m');
laueMap('23') = crystalSymmetry('m-3');
laueMap('622') = crystalSymmetry('6/mmm', [1 1 1.6], 'X||a', 'Z||c*');
laueMap('6') = crystalSymmetry('6/m', [1 1 1.6], 'X||a', 'Z||c*');
laueMap('422') = crystalSymmetry('4/mmm');
laueMap('4') = crystalSymmetry('4/m');
laueMap('32') = crystalSymmetry('-3m', [1 1 1.6], 'X||a', 'Z||c*');
laueMap('3') = crystalSymmetry('-3', [1 1 1.6], 'X||a', 'Z||c*');
laueMap('222') = crystalSymmetry('mmm');
laueMap('2') = crystalSymmetry('2/m');
laueMap('1') = crystalSymmetry('-1');

entries = dir(baseDir);
for e = 1:numel(entries)
if ~entries(e).isdir, continue; end
name = entries(e).name;
if name(1) == '.', continue; end
if ~isKey(laueMap, name)
fprintf('skipping %s (not in laueMap)\n', name);
continue;
end

classDir = fullfile(baseDir, name);
cs = laueMap(name);

% --- TSL pair (compare ebsdlib_ipf_legend_tsl.png vs mtex_ipf_legend_tsl.png) ---
try
keyTSL = ipfTSLKey(cs);
catch
warning('ipfTSLKey not available for %s; falling back to ipfHKLKey', name);
keyTSL = ipfHKLKey(cs);
end
fT = figure('Visible', 'off', 'Position', [100 100 600 600]);
plot(keyTSL);
title(sprintf('MTEX ipfTSLKey %s', name));
outTSL = fullfile(classDir, 'tsl_gridded_mtex_ipf_legend.png');
outDir = fileparts(outTSL);
if ~isfolder(outDir)
mkdir(outDir);
end
%exportgraphics(outTSL, outTSL, 'Resolution', 72);
saveas(fT, outTSL);
close(fT);
fprintf('wrote %s\n', outTSL);

% --- NH/HSV pair (compare ebsdlib_ipf_legend_nh.png vs mtex_ipf_legend_hsv.png) ---
keyHSV = ipfHSVKey(cs);
fH = figure('Visible', 'off', 'Position', [100 100 600 600]);
plot(keyHSV);
title(sprintf('MTEX ipfHSVKey %s', name));
outHSV = fullfile(classDir, 'nh_gridded_mtex_ipf_legend.png');
%exportgraphics(outHSV, outHSV, 'Resolution', 72);
saveas(fH, outHSV);
close(fH);
fprintf('wrote %s\n', outHSV);
end

fprintf('\nDone. For each Laue class directory there should now be:\n');
fprintf(' <class>/ebsdlib_ipf_legend_tsl.png EbsdLib TSL legend\n');
fprintf(' <class>/ebsdlib_ipf_legend_nh.png EbsdLib Nolze-Hielscher legend\n');
fprintf(' <class>/mtex_ipf_legend_tsl.png MTEX ipfTSLKey legend\n');
fprintf(' <class>/mtex_ipf_legend_hsv.png MTEX ipfHSVKey legend\n');
fprintf('Compare each pair (TSL <-> TSL, NH <-> HSV) side-by-side.\n');
57 changes: 57 additions & 0 deletions Code_Review/compare_pole_figure_mtex.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
% compare_pole_figure_mtex.m
%
% Compares the pole figure produced by EbsdLib's ODFTest against MTEX.
% Expects a CSV of Bunge Euler angles (phi1, Phi, phi2) in degrees.
%
% Usage: edit csvPath below to point at the file written by ODFTest, then run.
%
% The ODFTest writes the CSV to:
% <EbsdLib build dir>/Testing/Temporary/ODFTest_Eulers_deg.csv
% e.g. /Users/mjackson/Workspace7/DREAM3D-Build/ebsdlib-Release/Testing/Temporary/ODFTest_Eulers_deg.csv

csvPath = '/Users/mjackson/Workspace7/DREAM3D-Build/ebsdlib-Release/Testing/Temporary/ODFTest_Eulers_deg.csv';

% Load Euler angles
T = readtable(csvPath);
eulers_deg = [T.phi1, T.Phi, T.phi2];
fprintf('Loaded %d Euler triples from %s\n', size(eulers_deg,1), csvPath);
fprintf('First 3 rows (deg):\n');
disp(eulers_deg(1:min(3,end), :));

% Crystal symmetry: hexagonal high (6/mmm).
% Use X||a (real-space a-axis along X) to match EbsdLib's hexagonal
% direction convention in HexagonalOps.cpp. EbsdLib's (10-10) pole uses
% direction (sqrt(3)/2, 1/2, 0) and (2-1-10) uses (1, 0, 0), which is the
% X||a convention. MTEX's default is X||a* (reciprocal), which swaps the
% labels of the two prismatic pole figures — that is why EbsdLib's (10-10)
% looks like MTEX's (2-1-10) and vice versa. Switching MTEX to X||a here
% aligns the labels.
cs = crystalSymmetry('6/mmm', [1 1 1.6], 'X||a', 'Z||c*');
% Specimen symmetry: triclinic (no sample symmetry)
ss = specimenSymmetry('1');

% Build orientations (Bunge convention is MTEX default)
ori = orientation.byEuler(eulers_deg(:,1)*degree, eulers_deg(:,2)*degree, eulers_deg(:,3)*degree, cs, ss);

% MTEX default plotting: X east (right), Y north (up), Z outOfPlane.
% These match EbsdLib's pole figure convention after this fix.
setMTEXpref('xAxisDirection', 'east');
setMTEXpref('zAxisDirection', 'outOfPlane');

% Miller indices for the pole figures EbsdLib shows: (0001), (10-10), (2-1-10)
h = [ ...
Miller(0, 0, 0, 1, cs), ...
Miller(1, 0,-1, 0, cs), ...
Miller(2,-1,-1, 0, cs)];

% Plot pole figures as scatter of individual orientations (like EbsdLib discrete PF)
figure('Name', 'MTEX pole figures for EbsdLib ODFTest Euler sample');
plotPDF(ori, h, 'MarkerSize', 3, 'upper', 'projection', 'eangle', 'complete');
title('MTEX: (0001), (10-10), (2-1-10) for EbsdLib ODFTest sample');

% Print a short diagnostic
fprintf('\n');
fprintf('Expected for Bunge (180, 90, 0):\n');
fprintf(' c-axis [0001] in sample frame = +Y, so (0001) cluster at 12:00 (top).\n');
fprintf(' Centrosymmetric 6/mmm: antipodal [000-1] at 6:00 (bottom) also appears.\n');
fprintf(' The (0001) pole figure should show clusters at 12:00 and 6:00.\n');
118 changes: 118 additions & 0 deletions Code_Review/compare_pole_figures_all_laue.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
% compare_pole_figures_all_laue.m
%
% Companion to EbsdLib's PoleFigureLaueComparisonTest (Source/Test/PoleFigureLaueComparisonTest.cpp).
%
% For every Laue class listed in manifest.txt, reads eulers.csv, reconstructs
% the orientations in MTEX with the matching crystal symmetry, and saves
% a pole figure PNG named mtex.png next to EbsdLib's ebsdlib.png — so the two
% images can be compared side-by-side per Laue class.
%
% Usage:
% 1. Run the EbsdLib test first:
% cd .../DREAM3D-Build/ebsdlib-Release && \
% Bin/EbsdLibUnitTest "ebsdlib::PoleFigureLaueComparisonTest::GenerateAllLaueClasses"
% 2. Edit `baseDir` below to point at the PoleFigureComparison directory
% 3. Run this script in MATLAB (MTEX must be on the path; run `startup_mtex` first)

baseDir = '/Users/mjackson/Workspace7/DREAM3D-Build/ebsdlib-Release/Testing/Temporary/PoleFigureComparison';

if ~exist(baseDir, 'dir')
error('baseDir does not exist: %s\nRun the PoleFigureLaueComparisonTest first.', baseDir);
end

% MTEX pole-figure plotting conventions (match EbsdLib after the axis fixes)
setMTEXpref('xAxisDirection', 'east');
setMTEXpref('zAxisDirection', 'outOfPlane');

% Map EbsdLib rotation point group → MTEX crystalSymmetry Laue class string
% plus the 3 Miller-Bravais / Miller indices to plot. EbsdLib now uses
% X||a* for hexagonal/trigonal crystals (MTEX default).
laueMap = containers.Map();
% cubic
laueMap('432') = struct('cs', crystalSymmetry('m-3m'), ...
'h', {{[0 0 1], [0 1 1], [1 1 1]}});
laueMap('23') = struct('cs', crystalSymmetry('m-3'), ...
'h', {{[0 0 1], [0 1 1], [1 1 1]}});
% hexagonal
laueMap('622') = struct('cs', crystalSymmetry('6/mmm', [1 1 1.6]), ...
'h', {{[0 0 0 1], [1 0 -1 0], [2 -1 -1 0]}});
laueMap('6') = struct('cs', crystalSymmetry('6/m', [1 1 1.6]), ...
'h', {{[0 0 0 1], [1 0 -1 0], [1 1 -2 0]}});
% tetragonal
laueMap('422') = struct('cs', crystalSymmetry('4/mmm'), ...
'h', {{[0 0 1], [1 0 0], [1 1 0]}});
laueMap('4') = struct('cs', crystalSymmetry('4/m'), ...
'h', {{[0 0 1], [1 0 0], [1 1 0]}});
% trigonal
laueMap('32') = struct('cs', crystalSymmetry('-3m', [1 1 1.6]), ...
'h', {{[0 0 0 1], [0 -1 1 0], [1 -1 0 0]}});
laueMap('3') = struct('cs', crystalSymmetry('-3', [1 1 1.6]), ...
'h', {{[0 0 0 1], [-1 -1 2 0], [2 -1 -1 0]}});
% orthorhombic
laueMap('222') = struct('cs', crystalSymmetry('mmm'), ...
'h', {{[0 0 1], [1 0 0], [0 1 0]}});
% monoclinic
laueMap('2') = struct('cs', crystalSymmetry('2/m'), ...
'h', {{[0 0 1], [1 0 0], [0 1 0]}});
% triclinic
laueMap('1') = struct('cs', crystalSymmetry('-1'), ...
'h', {{[0 0 1], [1 0 0], [0 1 0]}});

% Discover all Laue class subdirectories
entries = dir(baseDir);
for e = 1:numel(entries)
if ~entries(e).isdir, continue; end
name = entries(e).name;
if name(1) == '.', continue; end
if ~isKey(laueMap, name)
fprintf('skipping %s (not in laueMap)\n', name);
continue;
end

classDir = fullfile(baseDir, name);
csvPath = fullfile(classDir, 'pole_figure_input_eulers.csv');
if ~exist(csvPath, 'file')
fprintf('skipping %s (no pole_figure_input_eulers.csv)\n', name);
continue;
end

info = laueMap(name);
cs = info.cs;
ss = specimenSymmetry('1');

T = readtable(csvPath);
eulers_deg = [T.phi1, T.Phi, T.phi2];
ori = orientation.byEuler(eulers_deg(:,1)*degree, eulers_deg(:,2)*degree, eulers_deg(:,3)*degree, cs, ss);

% Build Miller objects for the 3 pole figures
hArr = cell(1, numel(info.h));
for k = 1:numel(info.h)
idx = info.h{k};
if numel(idx) == 4
hArr{k} = Miller(idx(1), idx(2), idx(3), idx(4), cs);
else
hArr{k} = Miller(idx(1), idx(2), idx(3), cs);
end
end
h = [hArr{:}];

f = figure('Visible', 'off', 'Position', [100 100 700 250]);
plotPDF(ori, h, 'MarkerSize', 3, 'upper', 'projection', 'eangle', 'complete');
ttl = sprintf('MTEX %s — Euler %.1f, %.1f, %.1f (deg)', name, eulers_deg(1,1), eulers_deg(1,2), eulers_deg(1,3));
sgtitle(ttl);

outPath = fullfile(classDir, 'mtex_pole_figure.png');
outDir = fileparts(outPath);
if ~isfolder(outDir)
mkdir(outDir);
end
exportgraphics(f, outPath, 'Resolution', 72);
close(f);
fprintf('wrote %s\n', outPath);
end

fprintf('\nDone. For each Laue class directory there should now be:\n');
fprintf(' <class>/eulers.csv input Euler samples\n');
fprintf(' <class>/ebsdlib.png EbsdLib-rendered composite\n');
fprintf(' <class>/mtex.png MTEX-rendered composite\n');
fprintf('Compare the two image files per class to verify convention agreement.\n');
8 changes: 8 additions & 0 deletions Code_Review/ebsdlib_todo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# EbsdLib TODO Items

## PUCM

## Tighter spacing for Legend outputs.

Some legends do not take up all the canvas space. Make changes to tighten this up.

Loading
Loading