function [rout,g,b] = imrotate(varargin)
%IMROTATE Rotate image.
%   B = IMROTATE(A,ANGLE,'method') rotates the image A by ANGLE
%   degrees.  The image returned B will, in general, be larger 
%   than A.  Invalid values on the periphery are set to one
%   for indexed images or zero for all other image types. Possible 
%   interpolation methods are 'nearest' (nearest neighbor),
%   'bilinear' or 'bicubic'.   'nearest' is the default for all 
%   images.   For indexed images, only nearest-neighbor interpolation 
%   should be used.   A can also be an M x N x 3 RGB truecolor image.
%
%   B = IMROTATE(A,ANGLE,'crop') or IMROTATE(A,ANGLE,'method','crop')
%   crops B to be the same size as A.
%
%   Without output arguments, IMROTATE(...) displays the rotated
%   image in the current axis.  
%
%   See also IMRESIZE, IMCROP, ROT90.

%   Clay M. Thompson 8-4-92
%   Copyright (c) 1992 by The MathWorks, Inc.
%   $Revision: 5.4 $  $Date: 1996/10/23 19:34:48 $

[Image,Angle,Method,ClassIn,DoCrop] = parse_inputs(varargin{:});

threeD = (ndims(Image)==3); % Determine if input includes a 3-D array

if threeD,
   r = rotateImage(Image(:,:,1),Angle,Method,DoCrop);
   g = rotateImage(Image(:,:,2),Angle,Method,DoCrop);
   b = rotateImage(Image(:,:,3),Angle,Method,DoCrop);
   if nargout==0, 
      imshow(r,g,b);
      return;
   elseif nargout==1,
      if strcmp(ClassIn,'uint8');
         rout = repmat(uint8(0),[size(r),3]);
         rout(:,:,1) = uint8(round(r*255));
         rout(:,:,2) = uint8(round(g*255));
         rout(:,:,3) = uint8(round(b*255));
      else
         rout = zeros([size(r),3]);
         rout(:,:,1) = r;
         rout(:,:,2) = g;
         rout(:,:,3) = b;
      end
   else % nargout==3
      if strcmp(ClassIn,'uint8')
         rout = uint8(round(r*255)); 
         g = uint8(round(g*255)); 
         b = uint8(round(b*255)); 
      else
         rout = r;        % g,b are already defined correctly above
      end
   end
else 
   r = rotateImage(Image,Angle,Method,DoCrop);
   if nargout==0,
      imshow(r);
      return;
   end
   if strcmp(ClassIn,'uint8')
      r = uint8(round(r*255)); 
   end
   rout = r;
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function: rotateImage
%

function b = rotateImage(A,ang,method,crop)
% Inputs:
%         A       Input Image
%         ang     Angle to rotate image by
%         method  'nearest','bilinear', or 'bicubic'
%         crop    1 to crop image, 0 will output entire rotated image

% Catch and speed up 90 degree rotations
if rem(ang,90)==0 & nargin<4,
   phi = round(ang - 360*floor(ang/360));
   if phi==90,
      b = rot90(A);
   elseif phi==180,
      b = rot90(A,2);
   elseif phi==270,
      b = rot90(A,-1);
   else
      b = A;
   end
   if nargout==0, imshow(b), else bout = b; end
   return
end

phi = ang*pi/180; % Convert to radians

% Rotation matrix
T = [cos(phi) -sin(phi); sin(phi) cos(phi)];

% Coordinates from center of A
[m,n,o] = size(A);
if ~crop, % Determine limits for rotated image
   siz = ceil(max(abs([(n-1)/2 -(m-1)/2;(n-1)/2 (m-1)/2]*T))/2)*2;
   uu = -siz(1):siz(1); vv = -siz(2):siz(2);
else % Cropped image
   uu = (1:n)-(n+1)/2; vv = (1:m)-(m+1)/2;
end
nu = length(uu); nv = length(vv);

blk = bestblk([nv nu]);
nblks = floor([nv nu]./blk); nrem = [nv nu] - nblks.*blk;
mblocks = nblks(1); nblocks = nblks(2);
mb = blk(1); nb = blk(2);

rows = 1:blk(1); b = zeros(nv,nu);
for i=0:mblocks,
   if i==mblocks, rows = (1:nrem(1)); end
   for j=0:nblocks,
      if j==0, cols = 1:blk(2); elseif j==nblocks, cols=(1:nrem(2)); end
      if ~isempty(rows) & ~isempty(cols)
         [u,v] = meshgrid(uu(j*nb+cols),vv(i*mb+rows));
         % Rotate points
         uv = [u(:) v(:)]*T'; % Rotate points
         u(:) = uv(:,1)+(n+1)/2; v(:) = uv(:,2)+(m+1)/2;
         if method(1)=='n', % Nearest neighbor interpolation
            b(i*mb+rows,j*nb+cols) = interp2(A,u,v,'*nearest');
         elseif all(method=='bil'), % Bilinear interpolation
            b(i*mb+rows,j*nb+cols) = interp2(A,u,v,'*linear');
         elseif all(method=='bic'), % Bicubic interpolation
            b(i*mb+rows,j*nb+cols) = interp2(A,u,v,'*cubic');
         else
            error(['Unknown interpolation method: ',method]);
         end
      end
   end
end

d = find(isnan(b));
if length(d)>0, 
   % The next line is a hack so we won't have to do the whole isind check if
   % we can see from the first 10 pixels that it is not indexed.
   if (isind(A(1:min(10,end))) & isind(A)),
      b(d) = 1; 
   else 
      b(d) = 0; 
   end
end

if isgray(A)   % This should always be true
   b = max(0,min(b,1));  
end



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Function: parse_inputs
%

function [A,Angle,Method,Class,CropIt] = parse_inputs(varargin)
% Outputs:  A       the input image
%           Angle   the angle by which to rotate the input image
%           Method  interpolation method (nearest,bilinear,bicubic)
%           Class   storage class of A

A = varargin{1};
Angle = varargin{2};
Class = class(A);

switch nargin
case 2,             % imrotate        
   Method = 'nea'; 
   CropIt = 0;
case 3,
   if isstr(varargin{3}),
      Method = [lower(varargin{3}),'   ']; % Protect against short method
      Method = Method(1:3);
      if Method(1)=='c', % Crop string
         Method = 'nea';
         CropIt = 1;
      else
         CropIt = 0;
      end
   else
      error('''METHOD'' must be a string of at least three characters.');
   end
case 4,
   if isstr(varargin{3}),
      Method = [lower(varargin{3}),'   ']; % Protect against short method
      Method = Method(1:3);
   else
      error('''METHOD'' must be a string of at least three characters.');
   end
   CropIt = 1;
otherwise,
   error('Invalid input arguments');
end

if isa(A, 'uint8'),     % Convert A to Double grayscale for filtering & interpolation
   A = double(A)/255;
end