/* 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.
 */
/*! \file Transformation.hxx */
#ifndef _Transformation_hxx
#define _Transformation_hxx
#include<ImLib3D/Interpolator3D.hpp>
#include<ImLib3D/SplineInterpolation.hpp>
#include<ImLib3D/TestPatterns.hpp>
#include<ImLib3D/Interpolator3D.hxx>



template <>
void
IP3D::TransformWithInverseField<Mask3D>(const Mask3D& imOrig, const Field3Df& field, Mask3D& _imRes);

//! Use this function to resample an image (backward transform)
/*! Use this function to resample an image imOrig at new positions as defined by field. 
WARNING: the field defines a backward transform ie.: res(P)=src(P+field(P))
this might seem unatural at first, but is the best method for using a field transform.
for int images, use temporrary float images (otherwise interpolation sucks)
*/
template <class ImageType>
void
IP3D::TransformWithInverseField(const ImageType& src, const Field3Df& field, ImageType& _res)
{
	typedef typename ImageType::value_type Im3DValue;
	// Image Args
  	ImageProcessorArgs<ImageType> args(ImArgs(src),_res);
  	args.SetDestSize(field.Size());
  	ImageType &res=args.GetDest();

	if((void *)&_res==(void *)&field){ThrowError("IP3D::TransformWithInverseField can't transform field with itself");}

	if(src.HasMask())
	{
		res.AddMask();
		IP3D::TransformWithInverseField(src.Mask(),field,res.Mask());
	}

	// If no interpolator use default
	Interpolator3D<typename ImageType::value_type> *iSpline=NULL;
	if(!src.GetInterpolator())
	{		
		string interpolName=(src.Has("DefaultInterpolatorName") ? src.template Property < string >("DefaultInterpolatorName") : "Spline 5");
		
		iSpline=Interpolator3D<typename ImageType::value_type>::Create(interpolName);
//  new SplineInterpolator3D<typename ImageType::value_type>(5);
		if(!iSpline){ThrowError("couldnt create spline interpol???????");}
		(*(ImageType *)&src).SetInterpolator(iSpline);
	}

	typename ImageType::iteratorXYZ p;
	ImageProgress tracker("CTransformWithInverseField",p);
	for(p=res.begin();p!=res.end();++p)
	{
		Vect3Df P=field(p.x,p.y,p.z)+Vect3Df(p.x,p.y,p.z);
		*p=src.Value(P.x,P.y,P.z);
	}

	// If we used default interpolator, free it
	if(iSpline)
	{
		(*(ImageType *)&src).SetInterpolator(NULL);
	}

}



//! Perform an affine transformation on an image
template <class ImageType>
void
IP3D::TransformAffine(const ImageType& src, const Affine3DTransform& transfo, ImageType& _res,const Size3D *resSize)
{
	TaskInProgress tracker("CTransformAffine");
	ImageProcessorArgs<ImageType> args(ImArgs(src),_res);
	if(resSize){args.SetDestSize(*resSize);}
	else{args.SameSize();}
	ImageType &res=args.GetDest();

	Field3Df field(res.Size());
	TransformAffineHelpers::CreateInverseField(field, transfo);
	IP3D::TransformWithInverseField(src, field, res);
}


#endif //_Transformation_hxx
