// Scilab ( https://www.scilab.org/ ) - This file is part of Scilab
// Copyright (C) INRIA
// Copyright (C) 2012 - 2024 - Scilab Enterprises
//
// This file is hereby licensed under the terms of the GNU GPL v2.0,
// pursuant to article 5.3.4 of the CeCILL v.2.1.
// This file was originally licensed under the terms of the CeCILL v2.1,
// and continues to be available under such terms.
// For more information, see the COPYING file which you should have
// received along with this program.
// 
// Improvements: Copyright Federico Miyara, 2024-10-06

function y = animate(f,x,theta,flag,rect,fps,del)
   // Animated plot of x-->f(x,t) for t=theta(1),theta(2),etc
   // 
   // Usage:
   //       animate(f,x,theta,flag,rect,fps,del)   
   //       y = animate(f,x,theta,flag,rect,fps,del)
   // where      
   //       f:      function y=f(x,t). f is a Scilab function or
   //               a dynamically linked routine (referred to as
   //               a string). It may also be a previously computed
   //               NxM matrix whose elements are f(x(i),t(k)), 
   //               i=i, ...,N, k=1, ..., M     
   //       x:      N-vector of x-values
   //       theta:  M-vector of parameters 
   //       flag:   Optional parameter. If flag = 'yes', screen 
   //               is cleared between consecutive plots). If 
   //               flag = 'no' then screen not cleared between
   //               consecutive plots).
   //       rect:   [xmin, xmax, ymin, ymax] (1 x 4 real vector),
   //               containing a priori lower and upper bounds
   //               for x and f(t,x).
   //       fps:    Frames per second. May be a single number, 
   //               a two-compoent vector, indicating the 
   //               initial and ending values of frames per 
   //               second, hence allowing to accelerate or 
   //               decelerate (slow motion <---> fast motion)
   //               or a vector with more tnan two components 
   //               to control the frame rate more acurately 
   //               during playback
   //       del:    Optional argument indicating whether the 
   //               previous content of the figure will ("yes")
   //               or wiil not ("no") be erased        
   //       y:      Optional output argument providing the whole
   //               set of y curves as a matrix, each column of
   //               which contains the y values corresponding to
   //               x values for one value of the parameter. This
   //               may be used as the first argument in subsequent
   //               playback repetitions, which is useful when
   //               the calculation of y values takes too long.  
   //
   // This function is based on the original paramfplot2d() 
   // function, with the following improvements:
   //
   // 1)  The name animate() is much more self-evident than the  
   //     original one.
   // 2)  The whole set of values of the function for all x and 
   //     all theta is computed in advance and saved into a 
   //     matrix. Even if this requires more memory (in the 
   //     original realization only a vector was created at a
   //     single time) this allows time-consuming computations
   //     to be kept outside of the playback loop, preventing
   //     artifacts. The memory requirement should not be an 
   //     issue even for quite long animations.
   // 3)  Along the same line, the function argument can now be 
   //     replaced by the whole data set, where each column 
   //     contains the curve for one value of the parameter. 
   //     This is important since in certain cases in which
   //     the data computation could take quite a long time,
   //     the original method would require a long preparation 
   //     time each time the animation is run. 
   // 4)  It is possible to select, through a new argument, the 
   //     number of frames per second (fps).
   // 5)  It is even possible to provide a transition from a 
   //     starting fps and and an ending fps, allowing thus to
   //     accelerate or decelerate playback.
   // 6)  The new version is fully commented to facilitate 
   //     interested users to understand how it works.
   // 7)  An optional output argument has been included, which
   //     offers the whole set of y values corresponding to
   //     all x and all t, in case the user may find tnem 
   //     useful for further processing.
   // 8)  Since the order and meaning of the first arguments 
   //     have not changed, it is a fully-compatible replacement 
   //     for the original paramfplot2d().
   // 9)  A new argument makes it optional to delete the content
   //     of the previous figure, allowing complementary material
   //     to be included.
   // 10) The style has been polished removing unnecessary lines. 
   //
   // Example:
   //
   //   function y = f(x,t)
   //      y = abs(cos(1.5*x+4*t)).*sin(x+10*t),
   //   endfunction
   //   x = linspace(0, 20*%pi, 500);
   //   theta = 0:0.05:5;
   //   animate(x, theta, f, "no", [0, -1, 500, 1], [10 30])
   //
   // ---------------------------------------------------   
   // Authors: INRIA, Scilab Enterprises, Federico Miyara
   // Date: 2024-10-06
   
   rhs = argn(2);
   
   // If no argument is given, run a demo 
   if ~rhs 
      deff("y=f(x,t)", "y=t*sin(x)")
      x = linspace(0,2*%pi,50);
      theta = 0:0.05:1;
      clf;
      animate(f,x,theta);
      return;
   end
   // Error message
   msg = "%s: Wrong number of input argument(s): At least %d expected.\n"
   if rhs<3 
      error(msprintf(gettext(msg), "animate", 3));
   end
   
   // Ensure x is a column vector
   x = x(:);
   // Ensure theta is a row vector
   theta = theta(:).'; 
   
   msg = "Error: First argument must be a function or a " + ..
         "length(theta) x length(x) matrix"; 
   // Compute the whole set of curves
   if type(f)==13
      // If f is a Scilab funcion, compute the whole 
      // data set as an N x M vector
      y = feval(x, theta, f);
   elseif type(f)==1
      // If f is a data matrix check whether it has
      // the right size
      if size(f)==[length(x), length(theta)]
         // f has correct size
         y = f;
      elseif size(f)==[length(theta), length(x)]
         // f has reversed size, transpose it
         y = f.';
      else
         // f has definitely an unrev¿covably wrong size
         error(msprintf(msg))
         return
      end
   else
      // f type is incompatible
      error(msprintf(msg))
      return
   end
   
   // Set default del
   if rhs<7 
      del = "yes";
   end   
   // Set default fps
   if rhs<6 
      fps = 10;
   end
   // Set default rectangle
   if rhs<5  
      // Compute the data bounds
      xmin = min(x);
      xmax = max(x);
      ymin = min(yy);
      ymax = max(yy);
      rect = [xmin, xmax, ymin, ymax];
   end
   // Set default flag
   if rhs<4 
      flag = "no";
   end
   
   // If fps is a vector, intepolate linearly to get
   // the fps corresponding to each value of the 
   // parameter
   if length(fps)>1
      m = length(fps);
      n = length(theta);
      fps = interp1(1 + (n-1)/(m-1)*(0:m-1),fps, 1:n);
   else
      fps = ones(theta)*fps; 
   end 
         
   // Compute delta times between frames
   dt = 1./fps;
   // Set realtime unit in s 
   realtimeinit(min(dt));

   // Clear existing figure or create blank one
   if del=="yes"
      clf();
   end
   
   // Get axes handle
   a = gca();
   // Convert rectangle format to data bounds
   a.data_bounds = matrix(rect,2,2);
   // Make axes visible
   a.axes_visible = "on";
   // Draw polygon
   xpoly(x, y(:,1));
   // Get the polyline handle
   p = gce();
   
   // Initialize timer
   realtime(0);
   if flag=="no" 
      for k=1:size(theta,"*")
         // Update after dt(k) seconds
         realtime(sum(dt(1:k)/min(dt)));
         // Update the ordinate data to the
         // k-th plot   
         p.data(:, 2) = y(:,k);
      end
   else
      for k=1:size(theta,"*")
         realtime(sum(dt(1:k)/min(dt)));
         // Prepare to plot a new polyline within
         // the same axes
         plot2d(x, y(:,k)) 
      end
   end

endfunction
