1 module polyplex.math.simplemath.quaternion;
2 import polyplex.math.simplemath.matrix4x4;
3 import polyplex.math.simplemath.vectors;
4 
5 public struct Quaternion {
6 	private float[4] data;
7 	
8 	/**
9 		Creates a new Quaternion, in which initial values are X, Y, Z and W.
10 	*/
11 	public this(float x, float y, float z, float w) {
12 		data[0] = x;
13 		data[1] = y;
14 		data[2] = z;
15 		data[3] = w;
16 	}	
17 	
18 	/**
19 		Creates a new Quaternion, in which initial values are X, Y, Z and 0.
20 	*/
21 	public this(float x, float y, float z) {
22 		data[0] = x;
23 		data[1] = y;
24 		data[2] = z;
25 		data[3] = 0;
26 	}	
27 
28 	/**
29 		Creates a new Quaternion, in which initial values are X, Y, 0 and 0.
30 	*/
31 	public this(float x, float y) {
32 		data[0] = x;
33 		data[1] = y;
34 		data[2] = 0;
35 		data[3] = 0;
36 	}
37 
38 	/**
39 		Creates a new Quaternion, in which all initial values are X.
40 	*/
41 	public this(float x) {
42 		data[0] = x;
43 		data[1] = x;
44 		data[2] = x;
45 		data[3] = x;
46 	}
47 
48 	/**
49 		The X component.	
50 	*/
51 	public float X() { return data[0]; }
52 	public void X(float value) { data[0] = value; }
53 		
54 	/**
55 		The Y component.	
56 	*/
57 	public float Y() { return data[1]; }
58 	public void Y(float value) { data[1] = value; }
59 
60 	/**
61 		The Z component.	
62 	*/
63 	public float Z() { return data[2]; }
64 	public void Z(float value) { data[2] = value; }
65 
66 	/**
67 		The W component.	
68 	*/
69 	public float W() { return data[3]; }
70 	public void W(float value) { data[3] = value; }
71 
72 	public void Reorient(Vector3 axis, float angle) {
73 		this.X = axis.X;
74 		this.Y = axis.Y;
75 		this.Z = axis.Z;
76 		this.W = angle;
77 	}
78 
79 	public Vector3 ForwardDirection() {
80 		return Vector3(
81 			2 * (X*Z + W*Y), 
82 			2 * (Y*Z - W*X),
83 			1 - 2 * (X*X + Y*Y));
84 	}
85 
86 	public Vector3 UpDirection() {
87 		return Vector3(
88 			2 * (X*Y + W*Z), 
89 			1 - 2 * (X*X + Z*Z),
90 			2 * (Y*Z - W*X));
91 	}
92 
93 	public Vector3 LeftDirection() {
94 		return Vector3(
95 			1 - 2 * (Y*Y + Z*Z), 
96 			2 * (X*Y + W*Z),
97 			2 * (X*Z - W*Y));
98 	}
99 
100 	public void Rotate(T)(Vector3 axis, T theta) {
101 		X = Mathf.Sin(cast(real)theta/2) * axis.X;
102 		Y = Mathf.Sin(cast(real)theta/2) * axis.Y;
103 		Z = Mathf.Sin(cast(real)theta/2) * axis.Z;
104 		W = Mathf.Cos(cast(real)theta/2);
105 	}
106 
107 	public static Quaternion Rotated(T)(Vector3 axis, T theta) {
108 		Quaternion q;
109 		q.Rotate(axis, theta);
110 		return q;
111 	}
112 
113 	/*public Vector3 Rotate(T)(Vector3 axis, T angle) {
114 		Vector3 vec;
115 		vec.X = X;
116 		vec.Y = Y;
117 		vec.Z = Z;
118 
119 		float vd = 2.0f * vec.Dot(axis);
120 		Vector3 vdv = Vector3(vec.X * vd, vec.Y * vd, vec.Z * vd);
121 
122 		float ad = W*W - vec.Dot(vec);
123 		Vector3 adv = Vector3(axis.X * ad, axis.Y * ad, axis.Z * ad);
124 
125 		float vc = 2.0f * W;
126 		Vector3 vcv = vec.Cross(axis);
127 		vcv.X *= vc;
128 		vcv.Y *= vc;
129 		vcv.Z *= vc;
130 
131 		return vdv * adv + vcv ;
132 	}*/
133 
134 	public static Quaternion EulerToQuaternion(Vector3 euler) {
135 		Quaternion quat;
136 
137 		// pitch
138 		double cp = Mathf.Cos(euler.X * 0.5);
139 		double sp = Mathf.Cos(euler.X * 0.5);
140 
141 		// roll
142 		double cr = Mathf.Cos(euler.Y * 0.5);
143 		double sr = Mathf.Cos(euler.Y * 0.5);
144 
145 		// yaw
146 		double cy = Mathf.Cos(euler.Z * 0.5);
147 		double sy = Mathf.Cos(euler.Z * 0.5);
148 
149 
150 		// convert to quaternion.
151 		quat.X = cy * sr * cp - sy * cr * sp;
152 		quat.Y = cy * cr * sp + sy * sr * cp;
153 		quat.Z = sy * cr * cp - cy * sr * sp;
154 		quat.W = cy * cr * cp + sy * sr * sp;
155 		return quat;
156 	}
157 
158 	public static Quaternion FromMatrix(Matrix4x4 mat) {
159 		Quaternion qt = Quaternion.Identity;
160 		float tr = mat[0,0] + mat[1,1] + mat[2,2];
161 
162 		if (tr > 0) {
163 			float S =  Mathf.Sqrt(tr+1.0) * 2;
164 			qt.X = (mat[2,1] - mat[1,2]) / S;
165 			qt.Y = (mat[0,2] - mat[2,0]) / S;
166 			qt.Z = (mat[1,0] - mat[0,1]) / S;
167 			qt.W = 0.25 * S;
168 			return qt;
169 		}
170 
171 		if ((mat[0,0] > mat[1,1])&(mat[0,0] > mat[2,2])) {
172 			float S = Mathf.Sqrt(1.0 + mat[0,0] - mat[1,1] - mat[2,2]) * 2;
173 			qt.X = 0.25 * S;
174 			qt.Y = (mat[0,1] + mat[1,0]) / S;
175 			qt.Z = (mat[0,2] + mat[2,0]) / S;
176 			qt.W = (mat[2,1] - mat[1,2]) / S;
177 			return qt;
178 		}
179 
180 		if (mat[1,1] > mat[2,2]) {
181 			float S = Mathf.Sqrt(1.0 + mat[1,1] - mat[0,0] - mat[2,2]) * 2;
182 			qt.X = (mat[0,1] + mat[1,0]) / S;
183 			qt.Y = 0.25 * S;
184 			qt.Z = (mat[1,2] - mat[2,1]) / S;
185 			qt.W = (mat[0,2] + mat[2,0]) / S;
186 			return qt;
187 		}
188 
189 		float S = Mathf.Sqrt(1.0 + mat[2,2] - mat[0,0] - mat[1,1]) * 2;
190 		qt.X = (mat[0,2] + mat[2,0]) / S;
191 		qt.Y = (mat[1,2] - mat[2,1]) / S;
192 		qt.Z = 0.25 * S;
193 		qt.W = (mat[1,0] + mat[0,1]) / S;
194 		return qt;
195 	}
196 
197 	// Binary actions.
198 	public @trusted nothrow Quaternion opBinary(string op, T2)(T2 other) if (is (T2 : Quaternion)) { 
199 		// Operation on these, (due to being smallest size) can be done faster this way.
200 		mixin(q{
201 			return Quaternion(
202 				this.X * other.W + this.Y * other.Z + this.Z * other.Y - this.W * other.X,
203 				-this.X * this.Z - this.Y * other.W + this.Z * other.X + this.W * other.Y,
204 				this.X * this.Y - this.Y * other.X + this.Z * other.W + this.W * other.Z,
205 				-this.X * this.X - this.Y * other.Y + this.Z * other.Z + this.W * other.W);
206 			}.Format(op)
207 		); 
208 	}
209 
210 	public @trusted Quaternion Normalized() {
211 		import polyplex.math;
212 		double n = Mathf.Sqrt(X*X+Y*Y+Z*Z+W*W);
213 		return Quaternion(X/n, Y/n, Z/n, W/n);
214 	}
215 
216 	public static Quaternion Identity() {
217 		return Quaternion(0f, 0f, 0f, 0f);
218 	}
219 
220 	public string toString() {
221 		import polyplex.utils.strutils;
222 		return "<{0}, {1}, {2}, {3}>".Format(X, Y, Z, W);
223 	}
224 }