## Copyright (C) 2017 Carnë Draug <carandraug@octave.org>
## Copyright (C) 2015 Motherboard <cantfind@gmail.com>
##
## 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 -*-
## affine2d takes a transpose of the affine matrix as described in
## standard literature it performs the transformation as follows:
## v = (u*T)(1:2)
## where u = [x y 1] and T = [a b 0; c d 0; e f 1] [a b; c d] is a
## transposed rotation\shear matrix, [e f] is the translation vector,
## where e = dx, f = dy.

classdef affine2d < affine
  methods
    function this = affine2d (T = eye (3))
      if (nargin != 1)
        print_usage ()
      endif
      this@affine (T);
    endfunction

    ## Given a bounding box corner coordinated in xlims and ylims (top
    ## left, right bottom) - return the new bounding box after
    ## transformation.
    function [limitsX, limitsY] = outputLimits (this, xlims, ylims)
      xlims2 = [xlims(:); xlims(:)];
      ylims2 = [ylims(:); ylims(end:-1:1)(:)];
      temp = [xlims2 ylims2 ones(4,1)];
      temp = temp*this.T;
      limitsX = [min(temp(1:4,1)), max(temp(1:4,1))];
      limitsY = [min(temp(1:4,2)), max(temp(1:4,2))];
    endfunction
  endmethods
endclassdef

%!test
%! theta = 10;
%! A = [cosd(theta)  -sind(theta)  0
%!      sind(theta)   cosd(theta)  0
%!               0             0   1];
%! tform = affine2d (A);
%! [X, Y] = transformPointsForward (tform, 5, 10);
%! assert (X, 6.6605, e^-4)
%! assert (Y, 8.9798, e^-4)
%!
%! [U, V] = transformPointsInverse (tform, X, Y);
%! assert (U, 5, e^-10)
%! assert (V, 10, e^-10)
%! assert (isRigid (tform))
%! assert (! isTranslation (tform))
%! assert (isSimilarity (tform))

%!test
%! theta = 30;
%! tform = affine2d([ cosd(theta)  sind(theta) 0
%!                   -sind(theta)  cosd(theta) 0
%!                             0            0  1]);
%! assert (tform.T, [ 0.86603 0.5     0
%!                   -0.5     0.86603 0
%!                    0       0       1], e^-5);
%! invtform = invert(tform);
%! assert (invtform.T, [ 0.86603 -0.5     0
%!                    0.5      0.86603 0
%!                    0        0       1], e^-5);
%! assert (isRigid (tform))
%! assert (! isTranslation (tform))
%! assert (isSimilarity (tform))

%!test
%! tform = affine2d ([1 0 0; 0 1 0; 5 10 1]);
%! [X, Y] = transformPointsForward (tform, [1 2; 3 4; 5 6; 7 8]);
%! assert (round (X), [6; 8; 10; 12])
%! assert (round (Y), [12; 14; 16; 18])
%!
%! [U, V] = transformPointsInverse (tform, X, Y);
%! assert (round (U), [1; 3; 5; 7])
%! assert (round (V), [2; 4; 6; 8])
%! assert (isRigid (tform))
%! assert (isTranslation (tform))
%! assert (isSimilarity (tform))

%!test
%! tform = affine2d ([1 1e-16 0; 1e-16 1 0; 5 10 1]);
%! assert (isRigid (tform))
%! tform = affine2d ([2 1e-16 0; 1e-16 1 0; 5 10 1]);
%! assert (! isRigid (tform))

%!test
%! theta = 10;
%! A = [cosd(theta)  -sind(theta) 0
%!      sind(theta)   cosd(theta) 0
%!               0             0  1];
%! tform = affine2d (A);
%! [xlim, ylim] = outputLimits (tform, [1 240], [1 291]);
%! assert (xlim, [1.1585 286.8855], e^-4)
%! assert (ylim, [-40.6908  286.4054], e^-4)

%!test
%! A = [1  0  0
%!      0  1  0
%!      40 40 1];
%! tform = affine2d (A);
%! assert (isRigid (tform));
%! assert (isSimilarity (tform));
%! assert (isTranslation (tform));

%!test
%! a = invert (affine2d ([1 2 0; 3 4 0; 10 20 1]));
%! b = affine2d(a.T);
%! assert (b.T, [-2, 1, 0; 1.5, -0.5, 0; -10, 0, 1], e^-10)

%!assert (isTranslation (affine2d ([1, 0, 0; 0, 1, 0; 40, 40, 1])))
%!assert (! isTranslation (affine2d ([1 0 0; 0 -1 0; 0 0 1])))
%!assert (! isRigid (affine2d ([1 0 0; 0 -1 0; 0 0 1])))

%!error <must be an affine transform matrix> affine2d ([0 0 0; 0 0 0])
%!error <must be an affine transform matrix> affine2d ([0 0 0  0 0 0  0 0 1])
%!error <must be an affine transform matrix> affine2d ([0 0 0; 0 0 0; 0 0 0])
%!error <must be an affine transform matrix> affine2d ([1 0 0; 0 1 1; 0 0 1])
%!error <must not be a singular matrix> affine2d ([0 0 0; 0 0 0; 0 0 1])

%!xtest
%! tform = affine2d
%! assert (tform.T, eye (3))
%! assert (tform.Dimensionality, 2)