1 module math.linear.quaternion;
2 
3 import std.math;
4 import core.internal.traits : Unconst;
5 ////import std.traits : Unconst;
6 import std.traits;
7 
8 import math.linear.vector;
9 import math.linear.axis_rot;
10 
11 public import math.linear._qv;
12 public import math.linear._qa;
13 alias opBinaryImpl = math.linear._qv.opBinaryImpl;
14 
15 // TODO: Add tests to ensure T is a compotable type (number, etc...).
16 struct Quat(T) {
17 	union {
18 		T[4] data;
19 		struct {
20 			T dataAngle;
21 			T[3] dataAxis;
22 		}
23 		struct {
24 			T w;
25 			T x;
26 			T y;
27 			T z;
28 		}
29 	}
30 	
31 	this(T dataAngle, T[3] dataAxis ...) {
32 		this.dataAngle = dataAngle;
33 		this.dataAxis = dataAxis;
34 	}
35 	this(T[4] data) {
36 		this.data = data;
37 	}
38 	this(T[3] dataAxis, T dataAngle) {
39 		this.dataAngle = dataAngle;
40 		this.dataAxis = dataAxis;
41 	}
42 	this(typeof(this) v) {
43 		this.data = v.data;
44 	}
45 	
46 	this(AxisRot!T data) {
47 		import std.stdio;
48 		if (data.angle==0)
49 			this.data = [1,0,0,0];
50 		else {
51 			this.w = cast(T) cos(cast(real) data.angle/2);
52 			this.dataAxis[] = data.axis[] * cast(T) sin(cast(real) data.angle/2);
53 		}
54 	}
55 	
56 	const
57 	Quat!NT castType(NT)() {
58 		return Quat!NT(data.arrayCast!NT);
59 	}
60 	
61 	inout
62 	auto opBinary(string op, T)(inout T b) if (__traits(compiles, opBinaryImpl!op(this, b))){
63 		return opBinaryImpl!op(this, b);
64 	}
65 	inout
66 	auto opBinaryRight(string op, T)(inout T a) if (__traits(compiles, opBinaryImpl!op(a, this))){
67 		return opBinaryImpl!op(a,this);
68 	}
69 	auto opOpAssign(string op, T)(T b) if (__traits(compiles, opOpAssignImpl!op(this, b))){
70 		return opOpAssignImpl!op(this, b);
71 	}
72 	
73 	
74 	static const {
75 		Quat!T fromAxisRot(T)(AxisRot!T a) {
76 			return Quat!T(a);
77 		}
78 		Quat!T fromAxisRot(T)(Vec!(T,3) axis, T angle) {
79 			return Quat!T(AxisRot!T(axis,angle));
80 		}
81 		Quat!T fromAxisRot(T)(T angle, Vec!(T,3) axis) {
82 			return Quat!T(AxisRot!T(axis,angle));
83 		}
84 		Quat!T fromMagnitudeVector(T)(Vec!(T,3) a) {
85 			return Quat!T(AxisRot!T(a.magnitude,a.normalized));
86 		}
87 	}
88 	
89 	@property inout
90 	T magnitudeSquared() {
91 		return this.w^^2 + this.x^^2 + this.y^^2 + this.z^^2;
92 	}
93 	@property inout
94 	T magnitude() {
95 		return cast(T) sqrt(cast(real) this.magnitudeSquared);
96 	}
97 	void normalize() {
98 		this.data[] /= this.magnitude;
99 	}
100 	const
101 	Quat!T normalized() {
102 		Quat!T n;
103 		n.data[] = this.data[] / this.magnitude;
104 		return n;
105 	}
106 	
107 	@property {
108 		const
109 		T angle() {
110 			return 2 * cast(T) acos(cast(real) this.w);
111 		}
112 		const
113 		Vec!(T,3) axis() {
114 			Vec!(T,3) n;
115 			n.data[] = this.dataAxis[] / cast(T) sqrt(cast(real) 1 - this.w*this.w);
116 			return n;
117 		}
118 		void angle(T n) {
119 			this.w = cast(T) cos(cast(real) n/2);
120 		}
121 		void axis(Vec!(T,3) n) {
122 			this.dataAxis[] = n[] * cast(T) sin(cast(real) this.angle/2);
123 		}
124 	}
125 	
126 	
127 	static
128 	Quat!T identity() {
129 		return quat!T(1,[0,0,0]);
130 	}
131 	
132 	
133 	void invert() {
134 		this.x = -this.x;
135 		this.y = -this.y;
136 		this.z = -this.z;
137 	}
138 	alias conjugate = invert;
139 	
140 	inout
141 	Quat!T inverse() {
142 		return Quat!T(this.w, [-this.x, -this.y, -this.z]);
143 	}
144 	alias conjugated = inverse;
145 }
146 
147 auto quat(T)(T[4] data) {
148 	return Quat!T(data);
149 }
150 auto quat(T)(T dataAngle, T[3] dataAxis ...) {
151 	return Quat!T(dataAngle, dataAxis);
152 }
153 auto quat(T)(T[3] dataAxis, T dataAngle) {
154 	return Quat!T(dataAngle, dataAxis);
155 }
156 auto quat(T)(Quat!T data) {
157 	return Quat!T(data);
158 }
159 
160 auto quat(T)(AxisRot!T data) {
161 	return Quat!T(data);
162 }
163 
164 
165 
166 
167 auto opBinaryImpl(string op:"*", T,U)(const Quat!T a, const Quat!U b)
168 if (isNumeric!T && isNumeric!U)
169 {
170 	alias NT = Unconst!(typeof(rvalueOf!T*rvalueOf!U));
171 	return Quat!(NT)	(	-	a.x * b.x	-	a.y * b.y	-	a.z * b.z	+	a.w * b.w
172 		,		a.x * b.w	+	a.y * b.z	-	a.z * b.y	+	a.w * b.x
173 		,	-	a.x * b.z	+	a.y * b.w	+	a.z * b.x	+	a.w * b.y
174 		,		a.x * b.y	-	a.y * b.x	+	a.z * b.w	+	a.w * b.z
175 		);
176 }
177 
178  
179 
180 
181 
182 
183 void opOpAssignImpl(string op:"*", size_t size,T,U)(ref Quat!T a, const Quat!U b) 
184 if (isNumeric!T && isNumeric!U)
185 {
186 	a.w =	-	a.x * b.x	-	a.y * b.y	-	a.z * b.z	+	a.w * b.w	;
187 	a.x =		a.x * b.w	+	a.y * b.z	-	a.z * b.y	+	a.w * b.x	;
188 	a.y =	-	a.x * b.z	+	a.y * b.w	+	a.z * b.x	+	a.w * b.y	;
189 	a.z =		a.x * b.y	-	a.y * b.x	+	a.z * b.w	+	a.w * b.z	;
190 }
191 
192 
193 
194 
195 
196 
197 
198 
199 private {
200 	NT[] arrayCast(NT,OT)(OT[] xs) {
201 		NT[] nxs = new NT[xs.length];
202 		foreach (i,e; xs) {
203 			nxs[i] = cast(NT) e;
204 		}
205 		return nxs;
206 	}
207 	NT[L] arrayCast(NT,OT,size_t L)(OT[L] xs) {
208 		NT[L] nxs;
209 		foreach (i,e; xs) {
210 			nxs[i] = cast(NT) e;
211 		}
212 		return nxs;
213 	}
214 }
215 
216 
217 
218 
219 
220 
221 
222 unittest {
223 	void testValues(A,B)(A a1, A a2, A a3, A a4, B b1, B b2, B b3, B b4) {
224 		{
225 			Quat!A a = [a1,a2,a3,a4];
226 			Quat!B b = [b1,b2,b3,b4];
227 			static assert(is(typeof(a*b) == Quat!(typeof(a1+b1))));
228 		}
229 		{
230 			const Quat!A a = [a1,a2,a3,a4];
231 			const Quat!B b = [b1,b2,b3,b4];
232 			static assert(is(typeof(a*b) == Quat!(typeof(a1+b1))));
233 		}
234 		{
235 			const Quat!A a = [a1,a2,a3,a4];
236 			Quat!B b = [b1,b2,b3,b4];
237 			static assert(is(typeof(a*b) == Quat!(typeof(a1+b1))));
238 		}
239 		{
240 			Quat!A a = [a1,a2,a3,a4];
241 			const Quat!B b = [b1,b2,b3,b4];
242 			static assert(is(typeof(a*b) == Quat!(typeof(a1+b1))));
243 		}
244 	}
245 	testValues!(int,int)(1,2,3,4,2,3,4,5);
246 	testValues!(float,float)(1.5,2.5,3,4,2.5,3,4.5,5);
247 	testValues!(int,float)(1,2,3,4,2.5,3,4.5,5);
248 	testValues!(float,double)(1.5,2.5,3,4,2.5,3,4.5,5);
249 }
250 
251