# HG changeset patch
# User Motherboard <cantfind@gmail.com>
# Date 1449525334 -3600
#      Mon Dec 07 22:55:34 2015 +0100
# Node ID b758f947825d73d29754b70a0a4a0f893fe8f53a
# Parent  cb24795f0d8f6b41dd44d2ffc32b8173568cae4b
Added affine2d class for affine tranformations of 2d vectors

diff -r cb24795f0d8f -r b758f947825d inst/affine2d.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/inst/affine2d.m	Mon Dec 07 22:55:34 2015 +0100
@@ -0,0 +1,117 @@
+## Copyright (C) 2015 Motherboard
+## 
+## 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/>.
+
+## Author: Motherboard <cantfind@gmail.com>
+## Created: 2015-12-1
+
+## -*- 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
+  properties
+    T %Transformation matrix.
+		Dimensionality = 2;
+  end
+	methods
+  %constructor, if no argment is given, default is the identity matrix.
+    function obj = affine2d(tform)
+      if (nargin == 0)
+        tform = eye(3);
+      elseif ndims(tform) == 2 & size(tform) == [3 3] & tform(:,3) == [0; 0; 1] %check tform is a valid affine transform
+				obj.T = tform;
+			else
+				error("given transform is not affine! should be of the form [a b 0; c d 0; e f 1]")	
+			endif
+    end
+    %invert transform
+    function obj = invert(tform)    
+      obj.T = inv(tform.T);
+    end
+		function ans = isRigid(tform) %check if transform is only rotation or translation
+			submatrix = tform.T(1:2,1:2);
+			ans = abs(abs(det(submatrix))-1) < e^-10;
+		end
+		function ans = isSimilarity(tform) %check if transform is only homogineous scaling, rotation, or translation
+			submatrix = tform.T(1:2,1:2);
+			ans = all((abs(abs(submatrix*submatrix^-1) - eye(2)) < ones(2)*e^-10)(:));
+		end
+		function ans = isTranslation(tform) %check if transform is only a translation
+			submatrix = tform.T(1:2,1:2);
+			ans = all((abs(abs(submatrix) - eye(2)) < ones(2)*e^-10)(:));
+		end
+		function [limitsX limitsY] = outputLimits(tform, xlims, ylims) %given a bounding box corner coordinated in xlims and ylims (top left, right bottom) - return the new bounding box after transformation.
+			xlims2 = [xlims(:)' xlims(:)'];
+			ylims2 = [ylims(:)' ylims(end:-1:1)(:)'];
+			temp = [xlims2;ylims2;ones(1,4)];
+			ans = tform.T*temp;
+			limitsX = [min(ans(1,1:4)), max(ans(1,1:4))];
+			limitsY = [min(ans(2,1:4)), max(ans(2,1:4))];
+		end
+		function [x,y] = transformPointsForward(tform,u,v) %apply transformation on the set of u, v points (1xn vectors) or on U (2xn matrix)
+			N = size(u,1);
+			if exist("v","var")
+				u = [u(:) v(:)];
+			elseif size(u) ~= [N 2]
+				error("provided matrix to transform should be 2 by N in size");
+			endif
+			U = [u ones(N,1)];
+			x = (U*tform.T)(:,1:2);
+			if isargout(2)
+				y = x(:,2);
+				x = x(:,1);
+			endif
+		end
+		function [x,y] = transformPointsInverse(tform,u,v) %apply inverse transformation on the set of u, v points (1xn vectors) or on U (2xn matrix)
+			N = size(u,1);
+			if exist("v","var")
+				u = [u(:) v(:)];
+			elseif size(u) ~= [N 2]
+				error("provided matrix to transform should be 2 by N in size");
+			endif
+			U = [u ones(N,1)];
+			x = (U*inv(tform.T))(:,1:2);
+			if isargout(2)
+				y = x(:,2);
+				x = x(:,1);
+			endif
+		end
+  end
+end
+
+%!test
+%! theta = 10;
+%! tform = affine2d([cosd(theta) -sind(theta) 0; sind(theta) cosd(theta) 0; 0 0 1]);
+%! [X,Y] = transformPointsForward(tform,5,10);
+%! assert(abs([X Y] - [6.6605 8.9798]) < e^-4) 
+%! [U,V] = transformPointsInverse(tform,X,Y)
+%! assert(abs([U V] - [5 10]) < e^-10) 
+%! 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));
