function R = spm_mesh_to_grid(M, V, T)
% Non-linear interpolation of surface-based data onto a regular grid
% FORMAT R = spm_mesh_to_grid(M, V, T)
% M        - a patch structure with fields 'faces' and 'vertices'
% V        - an spm_vol structure with fields 'dim' and 'mat'
% T        - array of data to be interpolated
%
% R        - interpolated data on grid defined by V
%__________________________________________________________________________
% Copyright (C) 2010 Wellcome Trust Centre for Neuroimaging

% Karl Friston, Guillaume Flandin
% $Id: spm_mesh_to_grid.m 4079 2010-10-07 11:41:54Z guillaume $


%-Precompute interpolation kernel and voxel grid
%==========================================================================

%-Compute a densely sampled triangular mask
%--------------------------------------------------------------------------
[tx, ty]  = meshgrid(0.05:0.1:0.95, 0.05:0.1:0.95);
tx        = tx(:);
ty        = ty(:);
ti        = find(sum([tx ty],2) <= 0.9);
tx        = tx(ti); 
ty        = ty(ti); 
tz        = 1 - tx - ty;

%-Map the dense template triangle onto each face of the surface mesh
%--------------------------------------------------------------------------
P1        = M.vertices(M.faces(:,1),:);
P2        = M.vertices(M.faces(:,2),:);
P3        = M.vertices(M.faces(:,3),:);

If        = speye(size(M.faces,1));

vertDense = kron(If,tx)*P2 + kron(If,ty)*P3 + kron(If,tz)*P1;
kernel    = [tz tx ty];

%-Get voxel indices of all vertices from dense mesh within the image
%--------------------------------------------------------------------------
voxDense  = V.mat\[vertDense';ones(1,size(vertDense,1))];
voxDense  = round(voxDense(1:3,:)');
voxInd    = sub2ind(V.dim,voxDense(:,1),voxDense(:,2),voxDense(:,3));
voxInd    = reshape(voxInd,size(kernel,1),[]);


%-Interpolation
%==========================================================================
integralConservation = true;

%-Normalise vertex data by the number of faces they are in
%--------------------------------------------------------------------------
if integralConservation
    A = spm_mesh_adjacency(M);
    T = spdiags(1./sum(A,2),0,size(A,1),size(A,1)) * T;
end
    
%-Interpolate each dense face data in the voxel grid
%--------------------------------------------------------------------------
R = zeros([V.dim size(T,2)]);
if ~integralConservation, countR = zeros(size(R)); end

for i = 1:size(M.faces,1)
    %- Values at the vertices of the face
    faceVal      = T(M.faces(i,:),:);
    isData       = any(faceVal);
    if any(isData)
        %- Voxels corresponding to those vertices
        [iV,I,J] = unique(voxInd(:,i));
        for j=find(isData)
            %- Values at the vertices of the dense face
            faceValDense = kernel*faceVal(:,j);
            if integralConservation
                faceValDense = faceValDense * sum(faceVal(:,j))/sum(faceValDense);
            end
            %- Integrating values within voxels
            k = (j-1) * prod(V.dim);
            if integralConservation
                R(iV+k) = R(iV+k) + accumarray(J,faceValDense);
            else
                R(iV+k) = R(iV+k) + accumarray(J,faceValDense,[],@mean);
                countR(iV+k) = countR(iV+k) + 1;
            end
        end
    end
end

if ~integralConservation
    countR(~countR) = 1;
    R = R ./ countR;
end

if size(T,2) == 1, R = R(:,:,:,1); end
