function h = ezplot3(varargin)
% EZPLOT3  Easy to use 3-d parametric curve plotter.
%   EZPLOT3(x,y,z) plots the spatial curve x = x(t), y = y(t),
%   and z = z(t) over the default domain 0 < t < 2*pi.
%
%   EZPLOT3(x,y,z,[tmin,tmax]) plots the curve x = x(t), y = y(t),
%   and z = z(t) over tmin < t < tmax.
%
%   EZPLOT3(x,y,z,'animate') or EZPLOT(x,y,z,[tminm,tmax],'animate')
%   produces an animated trace of the spatial curve.
%   
%   Examples
%     ezplot3('sin(t)','cos(t)','t')
%     ezplot3('sin(t)','cos(t)','t',[0,6*pi])
%     ezplot3('sin(3*t)*cos(t)','sin(3*t)*sin(t)','t',[0,12],'animate')
%     ezplot3('(4+cos(1.5*t))*cos(t)','(2+cos(1.5*t))*sin(t)', ...
%             'sin(1.5*t)',[0,4*pi])
%
%  See also EZPLOT, EZSURF, EZPOLAR, PLOT, PLOT3

%   Copyright (c) 1984-98 by The MathWorks, Inc.
%   $Revision: 1.2 $
%   P. J. Costa ans Z. You 

clf;
% If x,y, or z are M-files, build them as an inline function of t.
if exist(char(varargin{1})) == 2
   x = vectorize(inline([char(varargin{1}) '(t)'],'t'));
else
   x = varargin{1};
end
if exist(char(varargin{2})) == 2
   y = vectorize(inline([char(varargin{2}) '(t)'],'t'));
else
   y = varargin{2};
end
if exist(char(varargin{3})) == 2
   z = vectorize(inline([char(varargin{3}) '(t)'],'t'));
else
   z = varargin{3};
end

Aflag = 0; % Animation option.

% Determine the vairables in that define the parametrization
varsx = symvar(x);
varsy = symvar(y);
varsz = symvar(z);
vars = union(union(varsx,varsy),varsz);
% Determine the number of variables in r = (x,y,z)
N = length(vars);

Npts = 300;

% Determine the domain in t:
switch nargin
   case 3
      T =  linspace(0,2*pi,Npts);
   case 4
      if isa(varargin{4},'double')   
         T = linspace(varargin{4}(1),varargin{4}(2),Npts);
      elseif isequal(varargin{4},'animate')
         Aflag = 1;
         T =  linspace(0,2*pi,Npts);
      else
         T =  linspace(0,2*pi,Npts);
      end
   case 5
      if isa(varargin{4},'double') & isequal(varargin{5},'animate') 
         T = linspace(varargin{4}(1),varargin{4}(2),Npts);
         Aflag = 1;
      elseif isequal(varargin{4},'animate') & isa(varargin{5},'double')
         T = linspace(varargin{5}(1),varargin{5}(2),Npts);
         Aflag = 1;
      else
         T = linspace(0,2*pi,Npts);
      end
end

H = findobj(gcf,'Type','uicontrol','String','Repeat');
if ~isempty(H) & ~Aflag
   delete(H);
end

switch N
   case 1 % Spatial curve
      t = vars{1};
      if isempty(t), t = 't'; end
      % Special processing in case X, Y, or Z are 1-by-1 syms/doubles.
      X = eval(vectorize(idrep(char(x),t,'T')));
      if prod(size(X)) == 1
         X = X*ones(size(T));
      end
      Y = eval(vectorize(idrep(char(y),t,'T')));
      if prod(size(Y)) == 1
         Y = Y*ones(size(T));
      end
      Z = eval(vectorize(idrep(char(z),t,'T')));
      if prod(size(Z)) == 1
         Z = Z*ones(size(T));
      end
      % Option to Return a handle.
      if nargout ~= 0
         h = plot3(X,Y,Z);
      else
         plot3(X,Y,Z);
      end
      xlabel('x'); ylabel('y'); zlabel('z');
      title(['x = ' texlabel(x), ', y = ' texlabel(y), ', z = ' texlabel(z)]);
      grid on;
   otherwise
       error('To plot parametrized surfaces, use EZSURF')
end

if Aflag
   hold on;
   H = plot3(X(1),Y(1),Z(1),'r.','erasemode','xor','markersize',24);

   dk = ceil(length(Y)/Npts);
   for k = 1:dk:length(Y)
      set(H,'xdata',X(k),'ydata',Y(k),'zdata',Z(k));
      drawnow;
   end
   % Slow down the animation for each machine architecture.
   tic, for i = 1:1000, j = i*i; end, tm = toc;
   tm = floor(10/tm);
   % Define the userdata for the callback.
   ud.x = X; ud.y = Y; ud.z = Z; ud.dk = dk; ud.h = H; ud.tm = tm;
   set(gcf,'userdata',ud);
   % Define the callback string.
   s = ['ud = get(gcf,''userdata'');' ...
        'hold on;' ...
        'tm = ud.tm;' ...
        'for k = 1:ud.dk:length(ud.y),' ...
           'set(ud.h,''xdata'',ud.x(k),''ydata'',ud.y(k),''zdata'',ud.z(k));' ...
           'for i = 1:tm, j = i*i; end,' ...
           'drawnow;' ...
        'end,' ...
        'hold off;'];
   uicontrol('Units','normal','Position',[.02 .01 .1 .06], ...
             'String','Repeat','CallBack',s);

end

%-------------------------

function s = idrep(s1,s2,s3)
%IDREP Replaces all occurences of s2 in the string s1 by s3.
%   For example, idrep(x*exp(x*y),x,W) returns W*exp(W*y).

% Take care of trivial cases:
%   s2 > s1
%   s1 is empty
%   s2 is empty
if (length(s2) > length(s1)) | isempty(s2) | isempty(s1),
  s=s1;
  return;
end;

% Place non-identifiers at the beginning and end of the string.
s=[]; s1 = [ '#' s1 '#' ];
% Find all occurrences of s2 in s1
s2len=length(s2);
s2pos=findstr(s1,s2);

% Build resulting string
s1pos=1;
for i=1:length(s2pos),
  if ~isalphanum(s1(s2pos(i)-1)) & ~isalphanum(s1(s2pos(i)+s2len))
     s=[s,s1(s1pos:s2pos(i)-1),s3];
     s1pos=s2pos(i)+s2len;
  end
end;
s=[s,s1(s1pos:length(s1))];
% Remove the #'s at the beginning and end of s.
s(1) = []; s(end) = [];

%-------------------------

function B = isalphanum(S)
%ISALPHANUM True for alpha-numeric characters.
%   For a string S, ISALPHANUM returns 1 for alpha-numeric
%   characters (or underscores) and 0 otherwise.
%   Example:
%      S = 'x*exp(x - y) + cosh(x*z^2)';
%      isalphanum(S)  returns
%      (1,0,1,1,1,0,1,0,0,0,1,0,0,0,0,1,1,1,1,0,1,0,1,0,1,0)

B = isletter(S) | (S >= '0' & S <= '9') | (S == '_');
