/* ImLib3D
 * Copyright (c) 2001, ULP-IPB Strasbourg.
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#ifndef _Transformation_hpp
#define _Transformation_hpp
/*!
  \file   Transformation.hpp
  \brief Contains all classes concerning Transformations  

  <long description> Contains all classes concerning Transformations like rotation, scaling and so on

  See AUTHORS and COPYING files for more information
  \date   2001-01-17
*/

#include<ImLib3D/Affine3DTransform.hpp>
#include<ImLib3D/Display.hpp>
#include<ImLib3D/ImageProgress.hpp>



namespace IP3D
{
/*! #&TransformWithInverseField&#     Use a vector field to apply a deformation on an image. 
        If F is a vector field (3D image composed of 3D vectors), S the source 
        image,R the result image, and P a point, we have: R(P)=S(P+F(P)).  
        If no interpolator is present in source image, it will use a bspline 5th 
        degree interpolator. 
        If a mask is present, it will be transformed by creating seperate binary 
        images for each different intensity value in the mask. Each binary (0 or 1) 
        image is transformed using an interpolator (default bspline 5). The result 
        mask is constructed by recombining the transformed floating point binary 
        images. This gives MUCH better results than would be obtained by using 
        nearest neighbor interpolation. This is however, very slow if mask contains 
        many different intensity values. 
     <UL>Args:
      <LI>Source image 1     type:ImageType
      <LI>Deformation field  type:Image3Dlinear<Vect3Df>
      <LI>Result image       type:ImageType
     </UL>
~*/
	template <class ImageType>
	void TransformWithInverseField(const ImageType& imOrig, const Field3Df& field, ImageType& imRes);
	//! for dealing with int non interpolatable image types (byte,int)
//  	template <class ImageType>
//  	static void ApplyInt(const ImageType& imOrig, const Field3Df& field, ImageType& _imRes);
};

namespace IP3D
{
/*! #&TransformAffine&#     Perform an affine transformation on an image
        Affine transforms are described by a 3x3 matrix and a translation vector. 
        They can be stored in an ImLib3D format file (see Affine3DTransform). If no 
        interpolator is present in source image, it will use a bspline 5th degree 
        interpolator. If a mask is present, it will be transformed as described in 
        TransformWithInverseField. If optional size is not ommited, result image 
        will have same size as source image.
     <UL>Args:
      <LI>Source image 1     type:ImageType
      <LI>Affine transform   type:Affine3DTransform
      <LI>Result image       type:ImageType
      <LI>Result image size  type:Size3D
     </UL>
~*/
	template <class ImageType>
	void TransformAffine(const ImageType& imOrig, const Affine3DTransform& transfo, ImageType& imDest,const Size3D *resSize=NULL);
	namespace TransformAffineHelpers
	{
		void CreateInverseField(Field3Df &field, const Affine3DTransform& transfo);
	}
};

namespace IP3D
{
/*! #&Scale&#     Change image size using resampling
        If no interpolator is present in source image, it will use a bspline 5th 
        degree interpolator. If a mask is present, it will be transformed as 
        described in TransformWithInverseField.
     <UL>Args:
      <LI>Source image 1  type:ImageType
      <LI>New Size        type:Size3D
      <LI>Result image    type:ImageType
     </UL>
~*/
	template <class ImageType>
	void Scale(const ImageType& src, const Size3D& newSize, ImageType& res)
	{
		Affine3DTransform trf;
		trf.SetScale(newSize.width /(float)src.Width(),
					 newSize.height/(float)src.Height(),
					 newSize.depth /(float)src.Depth());

		IP3D::TransformAffine(src,trf,res,&newSize);
	}
};
namespace IP3D
{
/*! #&WrapTranslate&#     Translates an image by integer vector, and wraps around 
        Ignores masks.
     <UL>Args:
      <LI>Source image 1  type:ImageType
      <LI>dV              type:Vect3Di
      <LI>Result image    type:ImageType
     </UL>
~*/
	template <class ImageType>
	void WrapTranslate(const ImageType& src, const Vect3Di& t0, ImageType& res0)
	{
		ImageProcessorArgs<ImageType> args(ImArgs(src),res0);
		args.SameSize();
		ImageType &res=args.GetDest();

		// make sure translation is >0
		Vect3Di t=t0;
		for(uint i=0;i<3;i++)
		{
			if(t(i)<0){t(i)+=src.SizeV()(i)*(1+abs(t(i)/src.SizeV()(i)));}
		}
		// do translation
		for(typename ImageType::const_iteratorXYZ p=src.begin();p!=src.end();++p)
		{
			res((p.x+t.x)%src.Width(),
				(p.y+t.y)%src.Height(),
				(p.z+t.z)%src.Depth())  =*p;
		}
	}
};

namespace IP3D
{
/*! #&Flip&#     Flips image in one or more directions
        This is usefull when using images from various sources. Second argument 
        is a string containing directions in which to flip. example: "XY"
     <UL>Args:
      <LI>Source image  type:ImageType
      <LI>direction     type:string
      <LI>Result image  type:ImageType
     </UL>
~*/
	template <class ImageType>
	void Flip(const ImageType& src, const string &axes, ImageType& res0)
	{
		ImageProcessorArgs<ImageType> args(ImArgs(src),res0);
		args.SameSize();
		ImageType &res=args.GetDest();

		res=src;
		for(uint i=0;i<axes.size();i++)
		{
			int d=axes[i]-'X';
			if(d<0 || d>2){ThrowError("CFlip:: bad axis:\"%c\"\n",axes[i]);}
			ImageType tmp=res;
			
			for(typename ImageType::iteratorXYZ p=res.begin();p!=res.end();++p)
			{
				Vect3Di pos=p.Pos();
				pos(d)=res.SizeV()(d)-pos(d)-1;
				tmp(pos)=*p;
			}
			res=tmp;
		}
	}
};


namespace IP3D
{
/*! #&SwapAxes&#     Swaps axes
        This is usefull when using images from various sources. Second argument 
        is a string containing  axes to swap. example: "XY" will invert x and y axes
     <UL>Args:
      <LI>Source image  type:ImageType
      <LI>direction     type:string
      <LI>Result image  type:ImageType
     </UL>
~*/
	template <class ImageType>
	void SwapAxes(const ImageType& src, const string &axes, ImageType& res0)
	{
		if(axes.size()!=2){ThrowError("CSwapAxes:: axes should contain 2 axes");}
		int a=axes[0]-'X';
		if(a<0 || a>2){ThrowError("CSwapAxes:: bad axis:\"%c\"\n",axes[a]);}
		int b=axes[1]-'X';
		if(b<0 || b>2){ThrowError("CSwapAxes:: bad axis:\"%c\"\n",axes[b]);}
		Vect3Di newSize=src.SizeV();
		newSize(a)=src.SizeV()(b);
		newSize(b)=src.SizeV()(a);
		
		ImageProcessorArgs<ImageType> args(ImArgs(src),res0);
		args.SetDestSize(Size3D(newSize));
		ImageType &res=args.GetDest();
			
		for(typename ImageType::const_iteratorXYZ p=src.begin();p!=src.end();++p)
		{
			Vect3Di pos=p.Pos();
			pos(a)=p.Pos()(b);
			pos(b)=p.Pos()(a);
			res(pos)=*p;
		}
	}
};


#include<ImLib3D/Transformation.hxx>

#endif //_Transformation_hpp
