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