1 module daque.math.quaternion;
2 
3 import std.math;
4 
5 import daque.math.geometry;
6 import daque.math.linear;
7 
8 struct Quaternion(R)
9 {
10 	public R scalar;
11 	public R[3] vector;
12 
13 	public this(R[4] components)
14 	{
15 		scalar = components[0];
16 		vector[] = components[1 .. 4];
17 	}
18 
19 	public this(R scalar, R[3] vector)
20 	{
21 		this.scalar = scalar;
22 		this.vector = vector;
23 	}
24 
25 	static public Quaternion!R getRotation(R[3] axis, R amount)
26 	{
27 		Quaternion quaternion = Quaternion([0, 0, 0, 0]);
28 		quaternion.scalar = sin(amount / 2.0f);
29 		quaternion.vector[] = cos(amount / 2.0f) * axis[];
30 		return quaternion;
31 	}
32 
33 	Matrix!(R, 3, 3) getRotationMatrix()
34 	{
35 		auto m = Matrix!(R, 3, 3).Identity();
36 
37 		for(uint j; j < 3; j++)
38 		{
39 			Quaternion columnQuaternion = Quaternion([0, 0, 0, 0]);
40 			columnQuaternion.scalar = 0;
41 			for(uint i; i < 3; i++)
42 				columnQuaternion.vector[i] = m[i, j];
43 
44 			columnQuaternion = columnQuaternion * this;
45 			for(uint i; i < 3; i++)
46 				m[i, j] = columnQuaternion.vector[i];
47 		}
48 
49 		return m;
50 	}
51 
52 	public Quaternion opBinary(string op)(Quaternion rhs)
53 	{
54 		Quaternion result;
55 		static if (op == "+")
56 		{
57 			result.scalar = this.scalar + rhs.scalar;
58 			result.vector[] = this.vector[] + rhs.vector[];
59 		}
60 		else static if (op == "*")
61 		{
62 			result.scalar = this.scalar * rhs.scalar - dot(this.vector, rhs.vector);
63 			result.vector[] = this.scalar * rhs.vector[] + this.vector[] * rhs.scalar + cross(this.vector, rhs.vector)[];
64 		}
65 		else 
66 		{
67 			static assert(0, "Quaternion: Unsupported operation: " ~ op);
68 		}
69 		return result;
70 	}
71 
72 	public Quaternion conjugate()
73 	{
74 		Quaternion conjugateQuaternion;
75 		conjugateQuaternion.scalar = this.scalar; 
76 		conjugateQuaternion.vector[] = -1 * this.vector[]; 
77 		return conjugateQuaternion;
78 	}
79 
80 	public Quaternion inverse()
81 	{
82 		Quaternion invSquareAbs = Quaternion(1.0 / squareAbs(), [0, 0, 0]);
83 		return conjugate() * invSquareAbs;
84 	}
85 
86 	public real abs()
87 	{
88 		return sqrt(squareAbs);
89 	}
90 
91 	public real squareAbs()
92 	{
93 		return (this * this.conjugate).scalar;
94 	}
95 }