content: package edu.stanford.rsl.conrad.geometry.motion;
  
  
  import edu.stanford.rsl.conrad.geometry.Axis;
  import edu.stanford.rsl.conrad.geometry.Rotations;
  import edu.stanford.rsl.conrad.geometry.transforms.AffineTransform;
  import edu.stanford.rsl.conrad.geometry.transforms.ComboTransform;
  import edu.stanford.rsl.conrad.geometry.transforms.Transform;
  import edu.stanford.rsl.conrad.geometry.transforms.Translation;
  import edu.stanford.rsl.conrad.numerics.DoubleFunction;
  import edu.stanford.rsl.conrad.numerics.SimpleMatrix;
  import edu.stanford.rsl.conrad.numerics.SimpleOperators;
  import edu.stanford.rsl.conrad.numerics.SimpleVector;
  
  
  
  
  
  /**
   * MotionField to handle affine rotation and affine modulated translational motion.
   * The motion paramters, i.e. euler angles, translation, can be given as lambda expressions or method references
   * 
   * @author berger
   *
   */
  public class AnalyticalAffineMotionFieldEuler extends AnalyticalAffineMotionField {
  	
  	/**
  	 * 
  	 */
  	private static final long serialVersionUID = -2928748290559062947L;
  	
  
  	public AnalyticalAffineMotionFieldEuler() {
  	}
  
  	DoubleFunction[] transformationCenter;
  	DoubleFunction[] eulerAngles;
  	DoubleFunction[] translation;
  
  	/**
  	 * Creates a new rotational and translation MotionField. 
  	 * It transforms points with a rotation around the given Euler angles and center and translates them afterwards. 
  	 * The translation is an affine motion field as well which gives further flexibility.
  	 * The angle is given in radians.
  	 *  
  	 * @param transformationCenter
  	 * @param axis
  	 * @param angle
  	 */
  	public AnalyticalAffineMotionFieldEuler (DoubleFunction[] transformationCenter, DoubleFunction[] eulerAngles, DoubleFunction[] translation){
  		this.transformationCenter = transformationCenter;
  		this.translation = translation;
  		this.eulerAngles = eulerAngles;
  	}
  
  	/**
  	 * Returns the interpolated transform between initial time and time.
  	 * Here the lambda expressions are evaluated and the current transform is build
  	 * 
  	 * @param initialTime
  	 * @param time
  	 * @return the transform
  	 * @throws Exception 
  	 */
  	@Override
  	public Transform getTransform(double initialTime, double time) {
  
  		// first get the current rotation center
  		SimpleVector backTrans = evaluateFunctionArray(transformationCenter,time);
  		Transform curBack = new Translation(backTrans);
  		Transform curToCenter = curBack.inverse();
  
  		// now we determine the current rotation axis and the angle
  		SimpleVector curAngles = evaluateFunctionArray(eulerAngles, time);
  
  		// finally we evaluate the current translation
  		SimpleVector curTranslation = evaluateFunctionArray(translation, time);
  
  		// lets build the full current affine transform
  		Transform curAffine = new AffineTransform(
  				Rotations.createRotationMatrix(curAngles.getElement(0),curAngles.getElement(1),curAngles.getElement(2)),
  				curTranslation);
  		return new ComboTransform(curToCenter,curAffine,curBack);
  	}
  	
  	/**
  	 * Returns the interpolated transform between initial time and time.
  	 * Here the lambda expressions are evaluated and the current transform is build
  	 * 
  	 * @param initialTime
  	 * @param time
  	 * @return the transform
  	 * @throws Exception 
  	 */
  	@Override
  	public SimpleMatrix getAffineMatrix(double initialTime, double time) {
  
  		// first get the current rotation center
  		SimpleVector transformCenter = evaluateFunctionArray(transformationCenter,time);
  
  		// now we determine the current rotation angles
  		SimpleVector curAngles = evaluateFunctionArray(eulerAngles, time);
  
  		// finally we evaluate the current translation
  		SimpleVector curTranslation = evaluateFunctionArray(translation, time);
  
  		SimpleMatrix out = new SimpleMatrix(curAngles.getLen()+1,curAngles.getLen()+1);
  		out.setElementValue(out.getRows()-1, out.getCols()-1, 1);
  		
  		SimpleMatrix rot = Rotations.createRotationMatrix(curAngles.getElement(0),curAngles.getElement(1),curAngles.getElement(2));
  		out.setSubMatrixValue(0, 0, rot);	
  		
  		// Translation is build by: currentTranslation + transformCenter - Rotation*transformCenter
  		// Computed directly to avoid matrix multiplications!
  		// Validity is easily tested by plugging i the transform Center, as the result should be the transform center
  		curTranslation.add(transformCenter);
  		curTranslation.subtract(SimpleOperators.multiply(rot, transformCenter));
  		out.setSubColValue(0, out.getCols()-1, curTranslation);
  		return out;
  	}
  	
  	public SimpleVector getEulerAngles(double initialTime, double time) {
  		return evaluateFunctionArray(eulerAngles, time);
  	}
  	
  	public SimpleVector getTranslation(double initialTime, double time) {
  		return evaluateFunctionArray(translation, time);
  	}
  
  	public DoubleFunction[] getTransformationCenter() {
  		return transformationCenter;
  	}
  
  	public void setTransformationCenter(DoubleFunction[] transformationCenter) {
  		this.transformationCenter = transformationCenter;
  	}
  
  	public DoubleFunction[] getEulerAngles() {
  		return eulerAngles;
  	}
  
  	public void setEulerAngles(DoubleFunction[] eulerAngles) {
  		this.eulerAngles = eulerAngles;
  	}
  
  	public DoubleFunction[] getTranslation() {
  		return translation;
  	}
  
  	public void setTranslation(DoubleFunction[] translation) {
  		this.translation = translation;
  	}
  
  
  
  
  }
  /*
   * Copyright (C) 2010-2014 Martin Berger 
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
   */