function Vpred = interpne(V,Xi,nodelist,method)
% interpne: Interpolates and extrapolates using n-linear interpolation (tensor product linear)
% usage: Vpred = interpne(V,Xi)
% usage: Vpred = interpne(V,Xi,nodelist)
% usage: Vpred = interpne(V,Xi,nodelist,method)
%
% Note: Extrapolating long distances outside the support of V is rarely advisable.
%
% arguments: (input)
% V - p-dimensional array to be interpolated/extrapolated at the list
% of points in the array Xi.
%
% Note: interpne will work in any number of dimensions >= 1
%
% Xi - (n by p) array of n points to interpolate/extrapolate. Each
% point is one row of the array Xi.
%
% nodelist - (OPTIONAL) cell array of nodes in each dimension.
% If nodelist is not provided, then by default I will assume:
%
% nodelist{i} = 1:size(V,i)
%
% The nodes in nodelist need not be uniformly spaced.
%
% method - (OPTIONAL) chacter string, denotes the interpolation
% method used.
%
% DEFAULT: method = 'linear'
%
% 'linear' --> n-d linear tensor product interpolation/extrapolation
% 'nearest' --> n-d nearest neighbor interpolation/extrapolation
%
% Note: in 2-d, 'linear' is equivalent to a bilinear interpolant
% in 3-d, it is commonly known as trilinear interpolation.
%
%
% arguments: (output)
% Vpred - n by 1 array of interpolated/extrapolated values
%
%
% Example 1: 2d (bilinear) case
% [x1,x2] = meshgrid(0:.2:1);
% z = exp(x1+x2);
% Xi = rand(100,2)*2-.5;
% Zi = interpne(z,Xi,{0:.2:1, 0:.2:1},'linear');
% surf(0:.2:1,0:.2:1,z)
% hold on
% plot3(Xi(:,1),Xi(:,2),Zi,'ro')
%
%
% My apology: this interface is not fully compatible with that of
% interpn. But in higher dimensions, the interpn interface is both
% a mess to use and to write.
%
%
% See also: interp1, interp2, interpn
%
% Author: John D'Errico
% e-mail address: woodchips@rochester.rr.com
% Release: 1.01
% Release date: 3/27/06
% get some sizes
vsize = size(V);
ndims = length(vsize);
[n,p] = size(Xi);
if ndims~=p
error 'Xi is not compatible in size with the array V for interpolation.'
end
% default for nodelist
if (nargin<2) || isempty(nodelist)
nodelist = cell(1,ndims);
for i=1:ndims
nodelist{i} = (1:vsize(i))';
end
end
if length(nodelist)~=ndims
error 'nodelist is incompatible with the size of V.'
end
nll = cellfun('length',nodelist);
if any(nll~=vsize)
error 'nodelist is incompatible with the size of V.'
end
% get deltax for the node spacing
dx = nodelist;
for i=1:ndims
nodelist{i} = nodelist{i}(:);
dx{i} = diff(nodelist{i});
if any(dx{i}<=0)
error 'The nodes in nodelist must be monotone increasing.'
end
end
% check for method
if (nargin<4) || isempty(method)
method = 'linear';
end
if ~ischar(method)
error 'method must be a character string if supplied.'
end
validmethod = {'linear' 'nearest'};
k = find(strncmp(method,validmethod,length(method)));
if isempty(k)
error(['No match found for method = ',method])
end
method = validmethod{k};
% Which cell of the array does each point lie in?
% This includes extrapolated points, which are also taken
% to fall in a cell. histc will do all the real work.
ind = zeros(n,ndims);
for i = 1:ndims
[junk,bin] = histc(Xi(:,i),nodelist{i});
% catch any point along the very top edge.
bin(bin==vsize(i)) = vsize(i) - 1;
ind(:,i) = bin;
k = find(bin==0);
% look for any points external to the nodes
if ~isempty(k)
% bottom end
ind(k(Xi(k,i)<nodelist{i}(1)),i) = 1;
% top end
ind(k(Xi(k,i)>nodelist{i}(end)),i) = vsize(i) - 1;
end
end % for i = 1:ndims
% where in each cell does each point fall?
t = zeros(n,ndims);
for i = 1:ndims
t(:,i) = (Xi(:,i) - nodelist{i}(ind(:,i)))./dx{i}(ind(:,i));
end
sub = cumprod([1,vsize(1:(end-1))])';
base = 1+(ind-1)*sub;
% which interpolation method do we use?
switch method
case 'nearest'
% nearest neighbor is really simple to do.
t = round(t);
t(t>1) = 1;
t(t<0) = 0;
Vpred = V(base + t*sub);
case 'linear'
% tensor product linear is not too nasty.
Vpred = zeros(n,1);
% define the 2^ndims corners of a hypercube
corners = (dec2bin(0:(2^ndims-1))== '1');
nc = size(corners,1);
for i = 1:nc
s = V(base + corners(i,:)*sub);
for j = 1:ndims
% this will work for extrapolation too
if corners(i,j) == 0
s = s.*(1-t(:,j));
else
s = s.*t(:,j);
end
end
Vpred = Vpred + s;
end
end % switch method