text: /**
  Quaternion.
  
  Copyright:
  Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.  
  Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)  
  Copyright (c) 2017-2018 Godot-D contributors  
  
  License: $(LINK2 https://opensource.org/licenses/MIT, MIT License)
  
  
  */
  module godot.core.quat;
  
  import godot.core.defs;
  import godot.core.vector3;
  import godot.core.basis;
  
  import std.math;
  
  /**
  A 4-dimensional vector representing a rotation.
  
  The vector represents a 4 dimensional complex number where multiplication of the basis elements is not commutative (multiplying i with j gives a different result than multiplying j with i).
  
  Multiplying quaternions reproduces rotation sequences. However quaternions need to be often renormalized, or else they suffer from precision issues.
  
  It can be used to perform SLERP (spherical-linear interpolation) between two rotations.
  */
  struct Quat
  {
  	@nogc nothrow:
  	
  	real_t x = 0;
  	real_t y = 0;
  	real_t z = 0;
  	real_t w = 1;
  	
  	void set(real_t p_x, real_t p_y, real_t p_z, real_t p_w)
  	{
  		x=p_x; y=p_y; z=p_z; w=p_w;
  	}
  	
  	this(real_t p_x, real_t p_y, real_t p_z, real_t p_w)
  	{
  		x=p_x; y=p_y; z=p_z; w=p_w;
  	}
  	
  	
  	real_t length() const
  	{
  		return sqrt(lengthSquared());
  	}
  	
  	void normalize()
  	{
  		this /= length();
  	}
  	
  	Quat normalized() const
  	{
  		return this / length();
  	}
  	
  	Quat inverse() const
  	{
  		return Quat( -x, -y, -z, w );
  	}
  	
  	void setEuler(in Vector3 p_euler)
  	{
  		real_t half_a1 = p_euler.x * 0.5;
  		real_t half_a2 = p_euler.y * 0.5;
  		real_t half_a3 = p_euler.z * 0.5;
  	
  		// R = X(a1).Y(a2).Z(a3) convention for Euler angles.
  		// Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-2)
  		// a3 is the angle of the first rotation, following the notation in this reference.
  	
  		real_t cos_a1 = cos(half_a1);
  		real_t sin_a1 = sin(half_a1);
  		real_t cos_a2 = cos(half_a2);
  		real_t sin_a2 = sin(half_a2);
  		real_t cos_a3 = cos(half_a3);
  		real_t sin_a3 = sin(half_a3);
  	
  		set(sin_a1*cos_a2*cos_a3 + sin_a2*sin_a3*cos_a1,
  			-sin_a1*sin_a3*cos_a2 + sin_a2*cos_a1*cos_a3,
  			sin_a1*sin_a2*cos_a3 + sin_a3*cos_a1*cos_a2,
  			-sin_a1*sin_a2*sin_a3 + cos_a1*cos_a2*cos_a3);
  	}
  	
  	Quat slerp(in Quat q, in real_t t) const
  	{
  		Quat to1;
  		real_t omega, cosom, sinom, scale0, scale1;
  		// calc cosine
  		cosom = dot(q);
  		
  		// adjust signs (if necessary)
  		if( cosom <0.0 )
  		{
  			cosom = -cosom;
  			to1.x = - q.x;
  			to1.y = - q.y;
  			to1.z = - q.z;
  			to1.w = - q.w;
  		}
  		else
  		{
  			to1.x = q.x;
  			to1.y = q.y;
  			to1.z = q.z;
  			to1.w = q.w;
  		}
  		// calculate coefficients
  		if ( (1.0 - cosom) > CMP_EPSILON )
  		{
  			// standard case (slerp)
  			omega = acos(cosom);
  			sinom = sin(omega);
  			scale0 = sin((1.0 - t) * omega) / sinom;
  			scale1 = sin(t * omega) / sinom;
  		}
  		else
  		{
  			// "from" and "to" quaternions are very close
  			//  ... so we can do a linear interpolation
  			scale0 = 1.0 - t;
  			scale1 = t;
  		}
  		// calculate final values
  		return Quat(
  			scale0 * x + scale1 * to1.x,
  			scale0 * y + scale1 * to1.y,
  			scale0 * z + scale1 * to1.z,
  			scale0 * w + scale1 * to1.w
  		);
  	}
  	
  	Quat slerpni(in Quat q, in real_t t) const
  	{
  		Quat from = this;
  	
  		real_t dot = from.dot(q);
  	
  		if (fabs(dot) > 0.9999) return from;
  	
  		real_t theta     = acos(dot),
  		       sinT      = 1.0 / sin(theta),
  		       newFactor = sin(t * theta) * sinT,
  		       invFactor = sin((1.0 - t) * theta) * sinT;
  	
  		return Quat(invFactor * from.x + newFactor * q.x,
  		            invFactor * from.y + newFactor * q.y,
  		            invFactor * from.z + newFactor * q.z,
  		            invFactor * from.w + newFactor * q.w);
  	}
  	
  	Quat cubicSlerp(in Quat q, in Quat prep, in Quat postq, in real_t t) const
  	{
  		//the only way to do slerp :|
  		real_t t2 = (1.0-t)*t*2;
  		Quat sp = this.slerp(q,t);
  		Quat sq = prep.slerpni(postq,t);
  		return sp.slerpni(sq,t2);
  	}
  	
  	void getAxisAndAngle(out Vector3 r_axis, out real_t r_angle) const
  	{
  		r_angle = 2 * acos(w);
  		r_axis.x = x / sqrt(1-w*w);
  		r_axis.y = y / sqrt(1-w*w);
  		r_axis.z = z / sqrt(1-w*w);
  	}
  	
  	
  	
  	Quat opBinary(string op : "*")(in Vector3 v) const
  	{
  		return Quat( w * v.x + y * v.z - z * v.y,
  			w * v.y + z * v.x - x * v.z,
  			w * v.z + x * v.y - y * v.x,
  			-x * v.x - y * v.y - z * v.z);
  	}
  	
  	Vector3 xform(in Vector3 v) const
  	{
  		Quat q = this * v;
  		q *= this.inverse();
  		return Vector3(q.x,q.y,q.z);
  	}
  	
  	this(in Vector3 axis, in real_t angle)
  	{
  		real_t d = axis.length();
  		if (d==0)
  			set(0,0,0,0);
  		else
  		{
  			real_t sin_angle = sin(angle * 0.5);
  			real_t cos_angle = cos(angle * 0.5);
  			real_t s = sin_angle / d;
  			set(axis.x * s, axis.y * s, axis.z * s,
  				cos_angle);
  		}
  	}
  	
  	this(in Vector3 v0, in Vector3 v1) // shortest arc
  	{
  		Vector3 c = v0.cross(v1);
  		real_t  d = v0.dot(v1);
  	
  		if (d < -1.0 + CMP_EPSILON)
  		{
  			x=0;
  			y=1;
  			z=0;
  			w=0;
  		}
  		else
  		{
  			real_t  s = sqrt((1.0 + d) * 2.0);
  			real_t rs = 1.0 / s;
  			
  			x=c.x*rs;
  			y=c.y*rs;
  			z=c.z*rs;
  			w=s * 0.5;
  		}
  	}
  	
  	
  	real_t dot(in Quat q) const
  	{
  		return x * q.x+y * q.y+z * q.z+w * q.w;
  	}
  	
  	real_t lengthSquared() const
  	{
  		return dot(this);
  	}
  	
  	void opOpAssign(string op : "+")(in Quat q)
  	{
  		x += q.x; y += q.y; z += q.z; w += q.w;
  	}
  	
  	void opOpAssign(string op : "-")(in Quat q)
  	{
  		x -= q.x; y -= q.y; z -= q.z; w -= q.w;
  	}
  	
  	void opOpAssign(string op : "*")(in Quat q)
  	{
  		x *= q.x; y *= q.y; z *= q.z; w *= q.w;
  	}
  	
  	
  	void opOpAssign(string op : "*")(in real_t s)
  	{
  		x *= s; y *= s; z *= s; w *= s;
  	}
  	
  	
  	void opOpAssign(string op : "/")(in real_t s)
  	{
  		this *= 1.0 / s;
  	}
  	
  	Quat opBinary(string op : "+")(in Quat q2) const
  	{
  		Quat q1 = this;
  		return Quat( q1.x+q2.x, q1.y+q2.y, q1.z+q2.z, q1.w+q2.w );
  	}
  	
  	Quat opBinary(string op : "-")(in Quat q2) const
  	{
  		Quat q1 = this;
  		return Quat( q1.x-q2.x, q1.y-q2.y, q1.z-q2.z, q1.w-q2.w);
  	}
  	
  	Quat opBinary(string op : "*")(in Quat q2) const
  	{
  		Quat q1 = this;
  		q1 *= q2;
  		return q1;
  	}
  	
  	
  	Quat opUnary(string op : "-")() const
  	{
  		return Quat(-x, -y, -z, -w);
  	}
  	
  	Quat opBinary(string op : "*")(in real_t s) const
  	{
  		return Quat(x * s, y * s, z * s, w * s);
  	}
  	
  	Quat opBinary(string op : "/")(in real_t s) const
  	{
  		return this * (1.0 / s);
  	}
  	
  	
  	
  	Vector3 getEuler() const
  	{
  		Basis m = Basis(this);
  		return m.getEuler();
  	}
  }
  
  