/*
	Copyright (C) 2005-2007 Feeling Software Inc.
	Portions of the code are:
	Copyright (C) 2005-2007 Sony Computer Entertainment America
	
	MIT License: http://www.opensource.org/licenses/mit-license.php
*/

#include "StdAfx.h"
#include "FMQuaternion.h"
#include "FUtils/FUTestBed.h"

//
// IMPORTANT: Many of the comparison numbers below were gotten using Maya.
//

TESTSUITE_START(FMQuaternion)

TESTSUITE_TEST(0, SimpleOperations)
	// Simple constructor.
	FMQuaternion q(0.1f, 0.2f, 0.5f, 0.6f);
	PassIf(IsEquivalent(0.1f, q.x));
	PassIf(IsEquivalent(0.2f, q.y));
	PassIf(IsEquivalent(0.5f, q.z));
	PassIf(IsEquivalent(0.6f, q.w));

	// Copy constructor.
	FMQuaternion q2 = q;
	PassIf(IsEquivalent(q2, q));
	PassIf(IsEquivalent(q2.x, 0.1f));
	PassIf(IsEquivalent(q2.y, 0.2f));
	PassIf(IsEquivalent(q2.z, 0.5f));
	PassIf(IsEquivalent(q2.w, 0.6f));

	// Basic arithmetic
	PassIf(IsEquivalent(q.Length(), 0.8124f));
	PassIf(IsEquivalent(q.LengthSquared(), 0.66f));
	q.NormalizeIt();
	PassIf(IsEquivalent(q.Length(), 1.0f));
	PassIf(IsEquivalent(q.LengthSquared(), 1.0f));

TESTSUITE_TEST(1, AngleAxis)
	// Angle-axis constructor.
	FMQuaternion aa(FMVector3::XAxis, 0.0f);
	PassIf(IsEquivalent(aa, FMQuaternion(0.0f, 0.0f, 0.0f, 1.0f)));
	FMQuaternion aa2(FMVector3(0.5f, -0.51f, 0.7f), FMath::DegToRad(30.0f));
	PassIf(IsEquivalent(aa2, FMQuaternion(0.1294f, -0.13199f, 0.181164f, 0.965926f)));
	FMQuaternion aa3(FMVector3(0.8f, 0.35f, 0.48734f), FMath::DegToRad(-45.0f));
	PassIf(IsEquivalent(aa3, FMQuaternion(-0.3061467f, -0.133939f, -0.1864969f, 0.9238795f)));

	// Angle-axis extraction.
	FMVector3 axis; float angle;
	aa.ToAngleAxis(axis, angle);
	PassIf(IsEquivalent(angle, 0.0f)); // Axis is irrelevant, since no rotation.
	aa2.ToAngleAxis(axis, angle);
	PassIf((IsEquivalent(axis, FMVector3(0.5f, -0.51f, 0.7f)) && IsEquivalent(angle, FMath::DegToRad(30.0f)))
		|| (IsEquivalent(axis, FMVector3(-0.5f, 0.51f, -0.7f)) && IsEquivalent(angle, FMath::DegToRad(-30.0f))));
	aa3.ToAngleAxis(axis, angle);
	PassIf((IsEquivalent(axis, FMVector3(0.8f, 0.35f, 0.48734f)) && IsEquivalent(angle, FMath::DegToRad(-45.0f)))
		|| (IsEquivalent(axis, FMVector3(-0.8f, -0.35f, -0.48734f)) && IsEquivalent(angle, FMath::DegToRad(45.0f))));

TESTSUITE_TEST(2, Matrix)
	// Matrix constructor.
	FMMatrix44 mx = FMMatrix44::AxisRotationMatrix(FMVector3(0.5f, -0.51f, 0.7f), FMath::DegToRad(30.0f));
	FMQuaternion aa = FMQuaternion::MatrixRotationQuaternion(mx);
	PassIf(IsEquivalent(aa, FMQuaternion(0.1294f, -0.13199f, 0.181164f, 0.965926f)));
	FMVector3 eulerAngles = FMVector3(FMath::Pif / 2.0f, FMath::Pif / 6.0f, 3.0f * FMath::Pif / 8.0f);
	FMMatrix44 mx2 = FMMatrix44::EulerRotationMatrix(eulerAngles);
	FMQuaternion aa2 = FMQuaternion::MatrixRotationQuaternion(mx2);
	FMQuaternion aaE = FMQuaternion::EulerRotationQuaternion(eulerAngles.x, eulerAngles.y, eulerAngles.z);
	PassIf(IsEquivalent(aa2, aaE));

	// Matrix extraction.
	FMMatrix44 mx3;
	mx3 = aa.ToMatrix();
	PassIf(IsEquivalent(mx3, mx));
	mx3 = aa2.ToMatrix();
	PassIf(IsEquivalent(mx3, mx2));
	FMVector3 eulerOut = aa2.ToEuler(&eulerAngles);
	PassIf(IsEquivalent(eulerOut, eulerAngles));

TESTSUITE_TEST(3, Transforms)
	FMQuaternion aa2(FMVector3(0.5f, -0.51f, 0.7f), FMath::DegToRad(30.0f));
	FMQuaternion aa3(FMVector3(0.8f, 0.35f, 0.48734f), FMath::DegToRad(-45.0f));
	FMQuaternion aa(FMVector3::XAxis, 0.0f);
	FMVector3 v(0.4f, 0.2f, 1.0f);
	FMVector3 v2(-0.5f, 0.0f, 0.6f);

	// Check identity transforms
	FMQuaternion out = aa * aa3;
	PassIf(IsEquivalent(out, aa3));
	out = aa2 * aa;
	PassIf(IsEquivalent(out, aa2));
	out = aa2 * aa3;
	PassIf(IsEquivalent(out, FMQuaternion(-0.22504311309918154f, -0.21998962990348064f, 0.044972560080678750f, 0.94812321394497479f)));
	out = aa3 * aa2;
	PassIf(IsEquivalent(out, FMQuaternion(-0.12728124831602353f, -0.28264878231341289f, -0.070509012633218196f, 0.94812321394497479f)));
	FMVector3 outV = aa * v;
	PassIf(IsEquivalent(outV, v));
	outV = aa * v2;
	PassIf(IsEquivalent(outV, v2));
	outV = aa2 * v;
	PassIf(IsEquivalent(outV, FMVector3(0.72484444514032287f, 0.22868005480411205f, 0.78886372007113525f)));
	outV = aa2 * v2;
	PassIf(IsEquivalent(outV, FMVector3(-0.26863363500894472f, 0.31336932927135464f, 0.66305027126465488f)));
	outV = aa3 * v;
	PassIf(IsEquivalent(outV, FMVector3(0.66698327793133894f, -0.19648504827654456f, 0.84647914252165402f)));
	outV = aa3 * v2;
	PassIf(IsEquivalent(outV, FMVector3(-0.23027227753990964f, -0.52274186502691655f, 0.53264964972765538f)));

TESTSUITE_TEST(4, Bugzilla455)
	double matrixData[16] = { -0.046491116, 0.00000000, -0.99891871, 0.00000000, 0.00000000, 1.0000000, 0.00000000, 0.00000000, 0.99891871, 0.00000000, -0.046491116, 0.00000000, -28652.848, 13.103017, -9445.6309, 1.0000000 };
	double quaternionData[4] = { 0.00000000, -0.72335714, 0.00000000, 0.69047403 };
	FMMatrix44 rotationMx(matrixData);
	FMQuaternion q = FMQuaternion::MatrixRotationQuaternion(rotationMx);
	PassIf(IsEquivalent(q, FMQuaternion(quaternionData)));

TESTSUITE_END
