function [mat, dim] = spm_get_matdim(img, vx, bb)
% Voxel-to-world matrix and image dimensions from image or bbox and vox-dim
%
% FORMAT [mat, dim] = spm_get_matdim(img, vx, bb)
%
% img - filename of image to use as reference (defaults to SPM's TPM.nii)
% vx  - [1 x 3] vector of voxel dimensions (mm).
% bb  - [2 x 3] array of the min and max X, Y, and Z coordinates (mm),
%       i.e. bb = [minX minY minZ; maxX maxY maxZ].
%
% mat - [4 x 4] matrix mapping voxel coordinates to world (mm) coordinates
% dim - [1 x 3] vector of image dimensions (number of voxels)
%       (both as in output from spm_vol)
%
%       Note that the output mat will correspond to the same orientation
%       as SPM's canonical templates (transverse and vx(1) forced negative)
%       if either or both bb and vx are specified (finite), but otherwise
%       will keep the orientation of the reference image.
%__________________________________________________________________________

% Ged Ridgway
% Copyright (C) 2013-2022 Wellcome Centre for Human Neuroimaging


if nargin < 3, vx  = nan(1, 3); end
if nargin < 2, bb  = nan(2, 3); end
if nargin < 1, img = '';        end

% Use MNI space by default, based on tissue priors
try
    vol = spm_vol(img);
    if isempty(vol), error('Failed to read volume %s', img), end
catch
    vol = spm_vol(fullfile(spm('dir'), 'tpm', 'TPM.nii'));
end
mat = vol(1).mat;
dim = vol(1).dim;

valid_bb = all(isfinite(bb(:)));
valid_vx = all(isfinite(vx));
if ~valid_bb && ~valid_vx
    return
end

% User has specified one or both of bb or vx, over-ride appropriately
[BB VX] = spm_get_bbox(vol(1));
if ~valid_bb, bb = BB; end
if ~valid_vx, vx = VX; end

% Determine mat and dim from bb and vx, assuming "canonical" orientation
% (i.e. transverse with negative first voxel dimension)
vx  = [-1 1 1] .* abs(vx);
mn  = vx .* min(bb ./ repmat(vx, 2, 1)); % "first" voxel's mm coordinates
mx  = vx .* round(max(bb ./ repmat(vx, 2, 1))); % "last voxel's mm coords
% matrix that maps voxel [1 1 1] to mn
mat = spm_matrix([mn 0 0 0 vx]) * spm_matrix([-1 -1 -1]);
% dim such that mat * [dim 1]' == [mx 1]'
dim = mat \ [mx 1]';
dim = round(dim(1:3)');
