Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
7bb5097
Refactor plotting helpers and add modular utilities
AdrianCurtin Jan 24, 2026
cbc2e25
Refactor plotting functions to use flexible argument parsing
AdrianCurtin Jan 24, 2026
7fbbdfe
Add unit tests for synthetic data processing
AdrianCurtin Jan 24, 2026
bb2d606
Rename gui module to GUI for consistency
AdrianCurtin Jan 30, 2026
cf7bc64
Add Experiment API, connectivity tools, and tests
AdrianCurtin Feb 6, 2026
06842d2
Fix white mode styling in report figures - use full package path
AdrianCurtin Feb 7, 2026
65edc2e
Add connectivity analysis and plotting modules
AdrianCurtin Feb 10, 2026
1af3fc6
Add connectivity analysis and plotting modules
AdrianCurtin Feb 12, 2026
89f0513
Refactor HRF, AR-IRLS GPU support, add tests
AdrianCurtin Feb 13, 2026
d991423
Add GLM diagnostics and pipeline improvements
AdrianCurtin Feb 13, 2026
75e051e
Add GUI plotting helpers and baseline option
AdrianCurtin Feb 13, 2026
566954b
Fix unpack output and pass flags to processing
AdrianCurtin Feb 13, 2026
60ca283
Add graph metrics, HB-ICA, and pf2 updates
AdrianCurtin Feb 13, 2026
8bfb13e
Add literature references; refactor calcCBSI
AdrianCurtin Feb 13, 2026
d1ccef2
Fix scanFiles init and canonicalize rootPath
AdrianCurtin Feb 13, 2026
b085a03
Update quickSetup.m
AdrianCurtin Feb 13, 2026
87e3261
Preserve select() order and add bar error color
AdrianCurtin Feb 19, 2026
2c3202b
Add FFT-based CWT and batch wcoherence
AdrianCurtin Feb 20, 2026
d86d651
Add neural efficiency plots; enable parfor
AdrianCurtin Feb 20, 2026
e375812
Add NE outputs and per-point neTable; fix INI writer
AdrianCurtin Feb 20, 2026
eba421c
Add partialCorr and mutualInfo coupling methods
AdrianCurtin Feb 21, 2026
bad1874
Fix resample, import, QC, INI, ROI signature
AdrianCurtin Apr 9, 2026
45e6db6
Add VLines option to plotTemporal
AdrianCurtin Apr 9, 2026
d99c8d1
Resolve probe layout using MNI/Pos2D, fallback grid
AdrianCurtin Apr 9, 2026
625e2fc
Add stable signal filters and qualify lpf call
AdrianCurtin Apr 9, 2026
d23b15a
Refactor Takizawa QC into pf2.qc.takizawa
AdrianCurtin Apr 9, 2026
afca8ad
Standardize pf2 warnings and add suppression API
AdrianCurtin Apr 9, 2026
72427e9
Add statWindow, fromConfig, and stats utilities
AdrianCurtin Apr 9, 2026
8b59978
Add caching, hierarchicalAverageMulti, and tests
AdrianCurtin Apr 10, 2026
39fabc5
Refactor group preprocessing and parallel grand-avg
AdrianCurtin Apr 10, 2026
402fbb8
Parallel resampling and metadata collection
AdrianCurtin Apr 10, 2026
69e9fdc
Optimize segment table builder; parallel import
AdrianCurtin Apr 10, 2026
178d1d3
Add per-segment pp cache, optimize resample & GUI
AdrianCurtin Apr 10, 2026
dfb1d00
Plotting tweaks: styles, vline visibility, and warnings
AdrianCurtin Apr 10, 2026
5ab6b4d
Fix warning state restore and query saving
AdrianCurtin Apr 10, 2026
55e764f
Use style for legend and add Colors option
AdrianCurtin Apr 10, 2026
2de7def
Improve processing, resampling and optode handling
AdrianCurtin Apr 10, 2026
5a4ba2d
Dark-mode colors and improved timeline labels
AdrianCurtin Apr 10, 2026
fb2f5cf
Enhance plotting: escape TeX, TightLayout, VLines
AdrianCurtin Apr 11, 2026
4908ec1
Fix up grand average resampling and add padding support
AdrianCurtin Apr 13, 2026
56325ca
Fix IO/processing bugs and add ForcedTerms
AdrianCurtin Apr 17, 2026
b07ef61
Fix table merging, BH FDR, LME dummy coding
AdrianCurtin Apr 17, 2026
a50c0e3
Refactor contrast SE calc and term matching
AdrianCurtin Apr 17, 2026
285e713
Add geodesic support and refactor color projection
AdrianCurtin Apr 17, 2026
3cd7f51
Add stat projection wrappers & transparent overlay
AdrianCurtin May 1, 2026
17c4d5e
INI: split only on first '=' and recover structs
AdrianCurtin May 1, 2026
3ad4b93
Enhance ChannelCheck UI, QC, and multi-file support
AdrianCurtin May 1, 2026
0f81d35
Add methods editor, seeds, reset & tests
AdrianCurtin May 1, 2026
57ebccb
Support fNIR3000 probe and RGB vline options
AdrianCurtin May 1, 2026
6b5930b
Robust probe detection and add LD device cfg
AdrianCurtin May 2, 2026
bd07428
Remove redundant fclose calls
AdrianCurtin May 12, 2026
bd2a0dd
Update barchart_infogroup.m
AdrianCurtin May 12, 2026
48d6087
Headless QC/topo, Octave checks, pipeline tweaks
AdrianCurtin Jun 5, 2026
d37659f
Add Filename option and duplicate-ID diagnostics
AdrianCurtin Jun 5, 2026
7c1549c
Block averaging, QC tweaks, and WaveLab fixes
AdrianCurtin Jun 5, 2026
444da37
Add marker dictionary subsystem and headless channel-check gating
AdrianCurtin Jun 17, 2026
b9623fc
Add support for artinis3
AdrianCurtin Jun 18, 2026
bad957a
Fixes for addFunction and plotIntraROI
AdrianCurtin Jun 18, 2026
ccedd22
Statistics Toolbox compatibility & Device optode coordiantes / prove…
AdrianCurtin Jun 22, 2026
778e602
Harmonize errors and library usage
AdrianCurtin Jun 22, 2026
43fa020
Add montage export, sliding-window epoching, and cross-device anatomi…
AdrianCurtin Jun 22, 2026
ca3f5ff
Isolate ProcessingContext-based processing from global state
AdrianCurtin Jun 23, 2026
80fa469
Update gather.m
AdrianCurtin Jun 23, 2026
4615656
Add spatial visualizations: activation movies, parcel projection, con…
AdrianCurtin Jun 23, 2026
ad4af36
Add bids export controls
AdrianCurtin Jun 23, 2026
d5e3e9d
Add typed auxiliary signals, high-quality 3D rendering, and export re…
AdrianCurtin Jun 24, 2026
feda591
Add diffuse optical tomography, cross-brain PPI, and tidy-table import
AdrianCurtin Jun 25, 2026
3426818
Adjust docs
AdrianCurtin Jun 25, 2026
c345006
Add first-party signal-processing functions (butter, fir1, sgolay, wi…
AdrianCurtin Jun 25, 2026
728bb36
Add parcelation projectsion and other fixes
AdrianCurtin Jun 25, 2026
c04b709
Add public ProcessingContext and arg validation
AdrianCurtin Jul 12, 2026
1d30ed2
Improve plot theming and bar alignment
AdrianCurtin Jul 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
201 changes: 201 additions & 0 deletions +exploreFNIRS/+connectivity/alignMatrices.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
function [aligned, masterChannels, masterLabels, nValid] = alignMatrices(results, mode)
% ALIGNMATRICES Align connectivity results from subjects with different channels
%
% Maps each subject's/dyad's connectivity result into a common channel-
% indexed grid so that matrix entry (i,j) always represents the same
% channel pair across subjects, regardless of per-subject channel rejection.
%
% Syntax:
% [aligned, masterCh, masterLabels, nValid] = ...
% exploreFNIRS.connectivity.alignMatrices(results, 'union')
% [aligned, masterCh, masterLabels, nValid] = ...
% exploreFNIRS.connectivity.alignMatrices(results, 'intersection')
% [aligned, masterCh, masterLabels, nValid] = ...
% exploreFNIRS.connectivity.alignMatrices(results, 0.75)
%
% Inputs:
% results - Cell array of result structs. Each must have one of:
% Connectivity: .matrix [N x N] and .channels [1 x N]
% Hyperscanning 'same': .values [N x 1] and .channelsA [1 x N]
% Hyperscanning 'all': .values [Na x Nb] and .channelsA, .channelsB
% mode - Alignment mode:
% 'union' - All channels present in any subject (default)
% 'intersection' - Only channels present in every subject
% numeric 0-1 - Channels present in >= mode fraction of subjects
%
% Outputs:
% aligned - 3D array with aligned values. NaN where a subject lacks
% data for a channel. Shape: [M x M x K] for connectivity,
% [M x 1 x K] for hyperscanning 'same', [Ma x Mb x K] for 'all'.
% masterChannels - Master channel vector (or {masterA, masterB} for 'all' pairing)
% masterLabels - Cell array of labels (or {labelsA, labelsB} for 'all' pairing)
% nValid - Per-cell count of subjects contributing a non-NaN value
%
% See also: exploreFNIRS.connectivity.computeMatrix,
% exploreFNIRS.hyperscanning.computeGroup

if nargin < 2
mode = 'union';
end

K = length(results);
if K == 0
error('exploreFNIRS:connectivity:alignMatrices', 'Empty results cell array');
end

% Detect result shape
isConnectivity = isfield(results{1}, 'matrix');
isHyperAll = ~isConnectivity && isfield(results{1}, 'channelsB') && ...
isfield(results{1}, 'values') && ~isvector(results{1}.values);

if isHyperAll
[aligned, masterChannels, masterLabels, nValid] = ...
alignHyperscanningAll(results, K, mode);
elseif isConnectivity
[aligned, masterChannels, masterLabels, nValid] = ...
alignConnectivity(results, K, mode);
else
% Hyperscanning 'same' pairing (vector values)
[aligned, masterChannels, masterLabels, nValid] = ...
alignHyperscanningSame(results, K, mode);
end
end


function [aligned, masterCh, masterLabels, nValid] = alignConnectivity(results, K, mode)
% Align NxN connectivity matrices

% Collect all channel vectors
allChannels = cell(K, 1);
for k = 1:K
allChannels{k} = results{k}.channels(:)';
end

masterCh = computeMasterChannels(allChannels, K, mode);
M = length(masterCh);

% Build aligned 3D array
aligned = nan(M, M, K);
for k = 1:K
[~, masterIdx, subIdx] = intersect(masterCh, allChannels{k});
aligned(masterIdx, masterIdx, k) = results{k}.matrix(subIdx, subIdx);
end

nValid = sum(~isnan(aligned), 3);

% Build labels
masterLabels = buildLabels(results, masterCh, 'labels', 'channels');
end


function [aligned, masterCh, masterLabels, nValid] = alignHyperscanningSame(results, K, mode)
% Align Nx1 hyperscanning 'same' pairing vectors

allChannels = cell(K, 1);
for k = 1:K
allChannels{k} = results{k}.channelsA(:)';
end

masterCh = computeMasterChannels(allChannels, K, mode);
M = length(masterCh);

aligned = nan(M, 1, K);
for k = 1:K
[~, masterIdx, subIdx] = intersect(masterCh, allChannels{k});
aligned(masterIdx, 1, k) = results{k}.values(subIdx);
end

nValid = sum(~isnan(aligned), 3);

masterLabels = buildLabels(results, masterCh, 'labelsA', 'channelsA');
end


function [aligned, masterChannels, masterLabels, nValid] = alignHyperscanningAll(results, K, mode)
% Align Na x Nb hyperscanning 'all' pairing matrices

allChA = cell(K, 1);
allChB = cell(K, 1);
for k = 1:K
allChA{k} = results{k}.channelsA(:)';
allChB{k} = results{k}.channelsB(:)';
end

masterA = computeMasterChannels(allChA, K, mode);
masterB = computeMasterChannels(allChB, K, mode);
Ma = length(masterA);
Mb = length(masterB);

aligned = nan(Ma, Mb, K);
for k = 1:K
[~, mIdxA, sIdxA] = intersect(masterA, allChA{k});
[~, mIdxB, sIdxB] = intersect(masterB, allChB{k});
aligned(mIdxA, mIdxB, k) = results{k}.values(sIdxA, sIdxB);
end

nValid = sum(~isnan(aligned), 3);

labelsA = buildLabels(results, masterA, 'labelsA', 'channelsA');
labelsB = buildLabels(results, masterB, 'labelsB', 'channelsB');

masterChannels = {masterA, masterB};
masterLabels = {labelsA, labelsB};
end


function master = computeMasterChannels(allChannels, K, mode)
% Compute the master channel set based on alignment mode

if ischar(mode) || isstring(mode)
switch lower(char(mode))
case 'union'
master = allChannels{1};
for k = 2:K
master = union(master, allChannels{k});
end
case 'intersection'
master = allChannels{1};
for k = 2:K
master = intersect(master, allChannels{k});
end
otherwise
error('exploreFNIRS:connectivity:alignMatrices', ...
'Unknown alignment mode "%s". Use ''union'', ''intersection'', or a numeric threshold.', char(mode));
end
elseif isnumeric(mode) && isscalar(mode) && mode > 0 && mode <= 1
% Threshold mode: channels in >= mode fraction of subjects
all = [];
for k = 1:K
all = union(all, allChannels{k});
end
counts = zeros(size(all));
for k = 1:K
counts = counts + ismember(all, allChannels{k});
end
master = all(counts >= mode * K);
else
error('exploreFNIRS:connectivity:alignMatrices', ...
'mode must be ''union'', ''intersection'', or a numeric threshold in (0, 1].');
end

master = sort(master(:)');
end


function labels = buildLabels(results, masterCh, labelField, chField)
% Build labels for master channel set from the first result that has them

labels = arrayfun(@(c) sprintf('Ch%d', c), masterCh, 'UniformOutput', false);

for k = 1:length(results)
if isfield(results{k}, labelField) && ~isempty(results{k}.(labelField))
subCh = results{k}.(chField)(:)';
subLabels = results{k}.(labelField);
if iscell(subLabels)
[~, mIdx, sIdx] = intersect(masterCh, subCh);
labels(mIdx) = subLabels(sIdx);
end
break;
end
end
end
Loading