% [dx,dy,dz,a,e] = DotOffset(r,d,ndots)
% computes DX, DY and DZ offsets to move a point in a direction specified
% by R. Output for NDOTS offsets are computed independently. Only azimuthal
% direction can be specified or both azimuth and elevation, in which case a
% number of different randomizations are possible (see third form of R
% below). D is the distance by with dots are to be offset and should be
% either scalar or have length NDOTS.
%
% all angles are in degrees
%
% Outputs A and E are the azimuth and elevation angles used by DotOffset
% for each offset. If only one output is requested, a 3xNDOTS output matrix
% with the x, y and z offset on the respective rows is returned.
% 
% R can take four forms:
% - scalar: direction (in degrees) of offset in azimuth (0 is translation
%   in z direction, 90 in x direction towards 3 o'clock), used for all
%   outputted offsets
% - vector with length NDOTS: direction (in degrees) of offset in azimuth
%   specified in input for each outputted offset
% - a matrix of size NDOTSx2: direction (in degrees) of offset in azimuth
%   (first column) and elevation (second column) specified in input for
%   each outputted offset
% - 2x2 cellmatrix: the first row specifies azimuth direction, the second
%   the elevation angle. A number of different inputs for the azimuth and
%   the elevation row are possible and lead to the following outputs:
%   - first column is a vector with length NDOTS: direction (in degrees) of
%     offset specified for each outputted offset. Second column is ignored.
%   - first column is a vector with length other than NDOTS: for each
%     outputted offset, offset direction is randomly selected from the
%     values in the vector (see 'help RandSel'). Second column is ignored.
%   - scalar in the first column and NaN in the second column (e.g.
%     {45,NaN;67,NaN} or {45,90;67,NaN}). Direction specified in the first
%     column is used for all outputted offsets.
%   - scalar in the first and second column (both non-NaN) (e.g.
%     {45,90;67,NaN} or {45,90;0,360}). For each offset, a random direction
%     is chosen within the specified limits. Angles are randomly chosen
%     between lower (1st column) and upper (2nd column) boundaries (see
%     'help RandLim').
%
% Function can also be used to compute points laying on (part of) a Circle
% or sphere with radius D:
% [X,~,Z] = DotOffset(linspace(0,360),D,100);

% 2008-07-29 DN  wrote it.
% 2008-08-06 DN  now included all possible inputs I could think of
% 2011-07-20 DN  now supports specified azimuth and elevation for each dot
%                without the complicated cell input syntax

function [dx,dy,dz,a,e] = DotOffset(r,d,ndots)

psychassert(length(d)==1 || length(d)==ndots,'Input D should be scalar or have length NDOTS');

if isscalar(r) && ~iscell(r)
    e   = 0;
    a   = r;
elseif isvector(r) && length(r)==ndots && ~iscell(r)
    a   = r(:).';   % assure row vector
    e   = 0;
elseif ~iscell(r) && all(size(r)==[ndots,2])
    a   = r(:,1).';
    e   = r(:,2).';
elseif iscell(r) && size(r,1)==2 && size(r,2)==2
    % azimuth
    if isvector(r{1,1}) && length(r{1,1})==ndots
        % direction for each offset specified
        a   = r{1,1}(:).';   % assure row vector
    elseif isvector(r{1,1}) && length(r{1,1})>1
        % a set of directions to randomly draw from
        a   = RandSel(r{1,1},ndots);
    elseif isnan(r{1,2}) || r{1,1}==r{1,2}
        % scalar, same direction for all offsets
        a   = r{1,1};
    else
        % randomly compute direction betwee lower and upper boundaries
        a   = RandLim([1,ndots],r{1,1},r{1,2});
    end
    % elevation
    if isvector(r{2,1}) && length(r{2,1})==ndots
        e   = r{2,1}(:).';   % assure row vector
    elseif isvector(r{2,1}) && length(r{2,1})>1
        e   = RandSel(r{2,1},ndots);
    elseif isnan(r{2,2}) || r{2,1}==r{2,2}
        e   = r{2,1};
    else
        e   = RandLim([1,ndots],r{2,1},r{2,2});
    end
else
    error('Input not recognized: R must be scalar, a vector of length NDOTS, a NDOTSx2 matrix or a 2x2 cellmatrix')
end

% a is azimuth angle, e is elevation angle
dx = d .* cosd(e) .* sind(a);
dy = d .* sind(e);
dz = d .* cosd(e) .* cosd(a);

if isscalar(dx)
    dx = dx*ones(1,ndots);
end
if isscalar(dy)
    dy = dy*ones(1,ndots);
end
if isscalar(dz)
    dz = dz*ones(1,ndots);
end

if nargout==1
    % make a 3 x NDOTS output matrix
    dx = [dx; dy; dz];
end
if isscalar(a) && nargout>=4
    a = a*ones(1,ndots);
end
if isscalar(e) && nargout>=5
    e = e*ones(1,ndots);
end
