function [transformed, outside]=cosmo_vol_coordinates(ds, coords)
% convert to and from spatial (x,y,z) coordinates
%
% [transformed, outside]=cosmo_vol_coordinates(ds[, coords])
%
% Inputs:
% ds dataset struct with fields .a.fdim.{labels,values}
% (with {'i','j','k'} a subset of .a.fdim.labels) and
% .a.vol.{mat,dim}
% coords (Optional) one of:
% - 1xP matrix of feature indices
% - 3xP matrix with spatial (x,y,z) coordinates
% If omitted then it is set to 1:nfeatures, with
% nfeatures=size(ds.samples,2);
%
% Returns:
% transformed - If coords is 1xP: 3xP spatial coordinates of the
% feature indices; columns have NaN for feature ids
% not in the dataset.
% - If coords is 3xP: 1xP feature indices of the
% coordinates; an entry is 0 for coordinates not
% matching a feature.
%
% # For CoSMoMVPA's copyright information and license terms, #
% # see the COPYING file distributed with CoSMoMVPA. #
% if no coords, return coordinates of all features
if nargin<2, coords=zeros(1,0); end
check_input(ds);
% convert either from or to coordinates
one_or_three=size(coords,1);
switch one_or_three
case 1
[transformed, outside]=lin2xyz(ds, coords);
case 3
[transformed, outside]=xyz2fa_indices(ds, coords);
otherwise
error('Input must be 1xP or 3xP ');
end
function [xyz, outside]=lin2xyz(ds, coords)
nfeatures=size(ds.samples,2);
if isempty(coords)
coords=1:nfeatures;
end
% exclude invalid indices
outside=coords<1 | coords>nfeatures | round(coords)~=coords;
inside=~outside;
nfeatures=numel(outside);
% deal with valid indices
ds_inside=cosmo_slice(ds, coords(inside), 2);
% get dataset fa indices
[fa_ijk, unused, ndim]=get_fa_ijk_indices(ds_inside);
% store indices in matrix with 4 rows
ijk=zeros(ndim+1,nfeatures);
for k=1:ndim
ijk(k,:)=fa_ijk{k};
end
% set last row to 1
ijk(ndim+1,:)=1;
% convert to xyz
xyz1=ds.a.vol.mat*ijk;
% set elements inside & outside
ncoords=numel(coords);
xyz=NaN(ndim, ncoords);
xyz(:,inside)=xyz1(1:ndim,:);
function [lin2fa, outside]=xyz2fa_indices(ds, coords)
% ijk indices in dataset
[fa_ijk, dim_sizes, ndim]=get_fa_ijk_indices(ds);
% add row of ones to coordinates
nfeatures=size(coords,2);
coords1=[coords;ones(1,nfeatures)];
% convert ijk indices to coordinates
% TODO: check use of 'round'
ijk1=round(ds.a.vol.mat\coords1);
% store ijk indices in cell
coords_ijk_cell=cell(1,ndim);
outside=false(1,nfeatures);
for k=1:ndim
outside=outside | ijk1(k,:)<1 | ijk1(k,:)>dim_sizes(k);
end
for k=1:ndim
coords_ijk_cell{k}=ijk1(k,~outside);
end
% convert to linear indices
coords_lin=sub2ind(dim_sizes, coords_ijk_cell{:});
fa_lin=sub2ind(dim_sizes, fa_ijk{:});
if ~isequal(unique(fa_lin), sort(fa_lin))
unq=unique(fa_lin);
h=histc(fa_lin,unq);
idx=find(h>1,1);
pos=find(fa_lin==unq(idx),2);
error('Duplicate feature index at %d and %d', pos(1), pos(2));
end
% compute mapping from coords indices to fa indices
nfull=prod(dim_sizes);
all2fa_lin=zeros(1,nfull);
all2fa_lin(fa_lin)=1:numel(fa_lin);
% apply mapping
lin2fa=zeros(1,nfeatures);
lin2fa(~outside)=all2fa_lin(coords_lin);
outside=outside | lin2fa==0;
function [fa_ijk, dim_sizes, ndim]=get_fa_ijk_indices(ds)
% helper: return a cell with i, j, k indices
dim_labels={'i','j','k'};
ndim=numel(dim_labels);
fa_ijk=cell(1,ndim);
dim_sizes=zeros(1,ndim);
for k=1:ndim
dim_label=dim_labels{k};
[dim, dim_idx, attr_name, dim_name]=cosmo_dim_find(ds,...
dim_label,true);
if dim~=2
error('Unsupported label %s in sample dimension',dim_label);
end
dim_values=ds.a.(dim_name).values{dim_idx};
fa_ijk{k}=dim_values(ds.(attr_name).(dim_label));
dim_sizes(k)=numel(dim_values);
end
if ~isequal(dim_sizes, ds.a.vol.dim)
error('size mismatch between .a.vol.dim and .a.fdim.values');
end
function check_input(ds)
% check input dataset
cosmo_check_dataset(ds);
if isstruct(ds) && isfield(ds,'a') && ...
isfield(ds.a,'vol') && isfield(ds.a.vol,'mat')
mat=ds.a.vol.mat;
else
error('missing fields .a.vol.mat');
end
% check matrix
if ~isnumeric(mat) || ~isequal(size(mat),[4 4])
error('matrix must be 4x4');
end