##  Copyright (C) 2014-2015  Daniel Kraft <d@domob.eu>
##  GNU Octave level-set package.
##
##  This program is free software: you can redistribute it and/or modify
##  it under the terms of the GNU General Public License as published by
##  the Free Software Foundation, either version 3 of the License, or
##  (at your option) any later version.
##
##  This program is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##  GNU General Public License for more details.
##
##  You should have received a copy of the GNU General Public License
##  along with this program.  If not, see <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn  {Function File} {@var{phi} =} ls_extract_solution (@var{t}, @var{d}, @var{phi0}, @var{f})
## 
## Calculate a level-set function of an evolving geometry from the
## result of @code{ls_solve_stationary}.  In particular, it is assumed
## that @code{@var{d} = ls_solve_stationary (@var{phi0}, @var{f}, @var{h})}.
## @var{phi} is set to a level-set function for the evolved geometry at
## time @var{t}.  The zero level-set of @var{phi} will describe the same
## geometry as the solution of the level-set equation
## @tex
## \begin{equation*}
##   \phi_t + f \left| \nabla \phi \right| = 0,
## \end{equation*}
## @end tex
## @ifnottex
##
## @example
## d/dt phi + f | grad phi | = 0,
## @end example
##
## @end ifnottex
## although @var{phi} will not be the full solution to this equation.
##
## @seealso{ls_solve_stationary, ls_time_step, ls_animate_evolution}
## @end deftypefn

function phi = ls_extract_solution (t, d, phi0, f)
  if (nargin () ~= 4)
    print_usage ();
  endif

  sz = size (phi0);
  if (~all (sz == size (f)))
    error ("PHI0 and F must be of the same size");
  endif
  if (~all (sz == size (d)))
    error ("PHI0 and D must be of the same size");
  endif

  fp = (f > 0);
  fz = (f == 0);
  fn = (f < 0);

  phi = NA (size (d));
  phi(fp) = d(fp) - t;
  phi(fn) = d(fn) + t;
  phi(fz) = phi0(fz);

  assert (all (isfinite (phi(:)) | isinf (phi(:))));
endfunction

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Tests.

% Test for error conditions.
%!error <Invalid call to>
%!  ls_extract_solution (1, 2, 3)
%!error <Invalid call to>
%!  ls_extract_solution (1, 2, 3, 4, 5)
%!error <PHI0 and F must be of the same size>
%!  ls_extract_solution (1, [1, 2], [1, 2], [3; 4]);
%!error <PHI0 and D must be of the same size>
%!  ls_extract_solution (1, [1; 2], [3, 4], [3, 4]);

% Check 0D case.
%!test
%!  assert (ls_extract_solution (1, [], [], []), []);

% For the test phi's, just check that solving works
% without any errors.
%!test
%!  ts = [0, 1, 2, 3];
%!  phis = ls_get_tests ();
%!  for i = 1 : length (phis)
%!    f = ones (size (phis{i}));
%!    for j = -1 : 1
%!      curF = j * f;
%!      d = ls_solve_stationary (phis{i}, curF);
%!      for t = ts
%!        phit = ls_extract_solution (t, d, phis{i}, curF);
%!        assert (phit(curF == 0), phis{i}(curF == 0));
%!      endfor
%!    endfor
%!  endfor

% Check vanishing of domain.
%!test
%!  n = 100;
%!  x = linspace (-1.5, 1.5, n);
%!  h = x(2) - x(1);
%!  [XX, YY] = meshgrid (x, x);
%!
%!  phi = ls_genbasic (XX, YY, "sphere", [0, 0], 1);
%!  f = -ones (size (XX));
%!
%!  d = ls_solve_stationary (phi, f, h);
%!  phiNew = ls_extract_solution (2, d, phi, f);
%!  assert (ls_isempty (phiNew));

% Test that the solution is correct for an expanding sphere.
%!test
%!  n = 50;
%!  x = linspace (-3, 3, n);
%!  h = x(2) - x(1);
%!  [XX, YY, ZZ] = ndgrid (x);
%!
%!  phi0 = ls_genbasic (XX, YY, ZZ, "sphere", [0, 0, 0], 1);
%!  phit = ls_genbasic (XX, YY, ZZ, "sphere", [0, 0, 0], 2);
%!  f = ones (size (phi0));
%!
%!  d = ls_solve_stationary (phi0, f, h);
%!  phi = ls_extract_solution (1, d, phi0, f);
%!
%!  sd1 = ls_signed_distance (phi, h);
%!  sd2 = ls_signed_distance (phit, h);
%!  diff = norm (sd1(:) - sd2(:), Inf);
%!  assert (diff, 0, h);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Demos.

% Visualise refraction and diffraction of a wave front.
%
%!demo
%!  n = 100;
%!  x = linspace (-10, 10, n);
%!  h = x(2) - x(1);
%!  [XX, YY] = meshgrid (x, x);
%!
%!  phi = ls_genbasic (XX, YY, "half", [5, -10], [1, -1]);
%!  f = sign (XX) + 2;
%!
%!  times = linspace (1, 9, 80);
%!  ls_animate_evolution (phi, f, h, times, 0.05);
%
%!demo
%!  n = 101;
%!  x = linspace (-10, 10, n);
%!  h = x(2) - x(1);
%!  [XX, YY] = meshgrid (x, x);
%!
%!  phi = ls_genbasic (XX, YY, "half", [-9, 0], [-1, 0]);
%!  f = ones (size (phi));
%!  f(XX == 0 & abs (YY) > 2) = 0;
%!
%!  times = linspace (1, 19, 80);
%!  ls_animate_evolution (phi, f, h, times, 0.05);

% Visualise evolution for more complicated case of positive and negative
%!demo
%!  n = 1000;
%!  x = linspace (-5, 5, n);
%!  h = x(2) - x(1);
%!  [XX, YY] = meshgrid (x, x);
%!
%!  F = sin (XX .* YY);
%!  phi0 = ls_genbasic (XX, YY, "sphere", [0, 0], 3);
%!  ls_animate_evolution (phi0, F, h, linspace (0, 3, 40), 0.01);
