1 module armos.math.quaternion; 2 import armos.math; 3 import std.math; 4 /++ 5 クオータニオンを表すstructです. 6 +/ 7 struct Quaternion(T)if(__traits(isArithmetic, T)){ 8 alias Quaternion!(T) Q; 9 alias V4 = armos.math.Vector!(T, 4); 10 alias V3 = armos.math.Vector!(T, 3); 11 12 /// vec[0];x 13 /// vec[1];y 14 /// vec[2];z 15 /// vec[3];w 16 V4 vec = V4(); 17 18 /++ 19 +/ 20 this(in T x, in T y, in T z, in T w){ 21 vec[0] = x; 22 vec[1] = y; 23 vec[2] = z; 24 vec[3] = w; 25 } 26 unittest{ 27 alias N = double; 28 alias Q = Quaternion!N; 29 assert(__traits(compiles, { Q q = Q(N(1), N(2), N(3), N(4)); })); 30 } 31 unittest{ 32 assert(__traits(compiles, { auto q = Quaternion!(int)(0, 0, 0, 1); })); 33 } 34 35 36 /++ 37 +/ 38 this(in T s, in V3 v){ 39 vec[0] = v[0]; 40 vec[1] = v[1]; 41 vec[2] = v[2]; 42 vec[3] = s; 43 } 44 unittest{ 45 alias T = double; 46 alias Q = Quaternion!T; 47 alias V3 = Vector!(T, 3); 48 assert(__traits(compiles, { 49 Q q = Q(T(1), V3(2, 3, 4)); 50 })); 51 } 52 53 /++ 54 +/ 55 this(in V3 v){ 56 this(T(1), v); 57 } 58 unittest{ 59 alias T = double; 60 alias Q = Quaternion!T; 61 alias V3 = Vector!(T, 3); 62 assert(__traits(compiles, { 63 Q q = Q(V3(2, 3, 4)); 64 })); 65 } 66 67 /++ 68 +/ 69 this(in V4 v){ 70 vec[0] = v[0]; 71 vec[1] = v[1]; 72 vec[2] = v[2]; 73 vec[3] = v[3]; 74 } 75 unittest{ 76 alias T = double; 77 alias Q = Quaternion!T; 78 alias V4 = Vector!(T, 4); 79 assert(__traits(compiles, { 80 Q q = Q(V4(1, 2, 3, 4)); 81 })); 82 } 83 84 /++ 85 +/ 86 T opIndex(in int index)const{ 87 return vec[index]; 88 } 89 90 /++ 91 +/ 92 ref T opIndex(in int index){ 93 return vec[index]; 94 } 95 unittest{ 96 Quaternion!(double) q = Quaternion!(double)(1, 2, 3, 4); 97 assert(q[0]==1); 98 assert(q[1]==2); 99 assert(q[2]==3); 100 assert(q[3]==4); 101 } 102 103 /++ 104 +/ 105 static Q zero(){ 106 return Q(T( 0 ), T( 0 ), T( 0 ), T( 0 )); 107 } 108 unittest{ 109 auto e = Quaternion!(double).zero; 110 assert(e[0]==0); 111 assert(e[1]==0); 112 assert(e[2]==0); 113 assert(e[3]==0); 114 } 115 116 /++ 117 +/ 118 static Q unit(){ 119 return Q(T( 0 ), T( 0 ), T( 0 ), T( 1 )); 120 } 121 unittest{ 122 auto e = Quaternion!(double).unit; 123 assert(e[0]==0); 124 assert(e[1]==0); 125 assert(e[2]==0); 126 assert(e[3]==1); 127 } 128 129 /++ 130 +/ 131 Q opMul(in Q r_quat)const{ 132 immutable T s_l = this[3]; 133 immutable v_l = V3(this[0], this[1], this[2]); 134 135 immutable T s_r = r_quat[3]; 136 immutable v_r = V3(r_quat[0], r_quat[1], r_quat[2],); 137 138 immutable return_v = s_l*v_r + s_r*v_l + v_l.vectorProduct(v_r) ; 139 immutable return_s = ( s_l*s_r ) - ( v_l.dotProduct(v_r) ); 140 return Q(return_v[0], return_v[1], return_v[2], return_s); 141 } 142 unittest{ 143 Quaternion!(double) q1 = Quaternion!(double)(0, 0, 0, 1); 144 Quaternion!(double) q2 = Quaternion!(double)(0, 0, 0, 1); 145 Quaternion!(double) qAnswer = Quaternion!(double)(0, 0, 0, 1); 146 assert(q1*q2 == qAnswer); 147 } 148 unittest{ 149 auto va = Vector!(double, 3)(0.5, 0.1, 0.4); 150 auto v = va.normalized; 151 152 Quaternion!(double) q1 = Quaternion!(double)(v[0]*sin(0.1), v[1]*sin(0.1), v[2]*sin(0.1), cos(0.1)); 153 Quaternion!(double) q2 = Quaternion!(double)(v[0]*sin(0.2), v[1]*sin(0.2), v[2]*sin(0.2), cos(0.2)); 154 Quaternion!(double) qResult = q1 * q2; 155 Quaternion!(double) qAnswer = Quaternion!(double)(v[0]*sin(0.3), v[1]*sin(0.3), v[2]*sin(0.3), cos(0.3)); 156 foreach (int i, value; qResult.vec.elements) { 157 assert( approxEqual(value, qAnswer[i]) ); 158 } 159 } 160 161 /++ 162 +/ 163 Q opNeg()const{ 164 return Q(-this[0], -this[1], -this[2], -this[3]); 165 } 166 unittest{ 167 Quaternion!(double) q = Quaternion!(double)(1, 1, 1, 1); 168 Quaternion!(double) a = Quaternion!(double)(-1, -1, -1, -1); 169 assert(-q == a); 170 } 171 172 /++ 173 +/ 174 Q opAdd(in Q q)const{ 175 auto return_quat = Q(); 176 return_quat.vec = vec + q.vec; 177 return return_quat; 178 } 179 unittest{ 180 auto q1 = Quaternion!(double)(1, 2, 3, 4); 181 auto q2 = Quaternion!(double)(2, 3, 4, 5); 182 auto qA = Quaternion!(double)(3, 5, 7, 9); 183 assert(q1+q2 == qA); 184 } 185 186 /++ 187 +/ 188 Q opMul(in T r)const{ 189 auto return_quat = Q(); 190 return_quat.vec = vec * r; 191 return return_quat; 192 } 193 unittest{ 194 auto q = Quaternion!(double)(1, 2, 3, 4); 195 auto qA = Quaternion!(double)(3, 6, 9, 12); 196 assert(q*3 == qA); 197 } 198 199 /++ 200 +/ 201 Q opDiv(in T r)const { 202 auto return_quat = Q(); 203 return_quat.vec = vec / r; 204 return return_quat; 205 } 206 unittest{ 207 auto q = Quaternion!(double)(3, 6, 9, 12); 208 auto qA = Quaternion!(double)(1, 2, 3, 4); 209 assert(q/3 == qA); 210 } 211 212 /// 213 CastedType opCast(CastedType: Quaternion!T, T)()const{ 214 import std.conv:to; 215 return CastedType(this.vec.to!(typeof(CastedType.vec))); 216 } 217 218 unittest{ 219 import std.conv:to; 220 Quaternion!int iq = Quaternion!double.zero.to!(Quaternion!int); 221 import std.conv:to; 222 Quaternion!double dq = Quaternion!int.zero.to!(Quaternion!double); 223 } 224 225 /// 226 CastedType opCast(CastedType: Matrix!(E, 4, 4), E)()const{ 227 import std.conv:to; 228 return matrix44!E; 229 } 230 231 unittest{ 232 import std.conv:to; 233 auto q = Quaternion!double.unit.to!(Matrix!(float, 4, 4)); 234 auto m = Matrix!(float, 4, 4).identity; 235 assert(q == m); 236 } 237 238 /// 239 CastedType opCast(CastedType: Matrix!(E, 3, 3), E)()const{ 240 import std.conv:to; 241 return matrix33!E; 242 } 243 244 unittest{ 245 import std.conv:to; 246 auto q = Quaternion!double.unit.to!(Matrix!(float, 3, 3)); 247 auto m = Matrix!(float, 3, 3).identity; 248 assert(q == m); 249 } 250 251 /++ 252 Quaternionのノルムを返します. 253 +/ 254 T norm()()const if(__traits(isFloating, T)){ 255 return sqrt(this[0]^^2.0 + this[1]^^2.0 + this[2]^^2.0 + this[3]^^2.0); 256 } 257 258 unittest{ 259 auto q = Quaternion!(double)(1, 2, 3, 4); 260 assert(q.norm == sqrt(30.0)); 261 } 262 263 /++ 264 +/ 265 Q normalized()const { 266 return Q(vec.normalized); 267 } 268 unittest{ 269 // auto q = Quaternion!(double)(1, 2, 3, 4); 270 // assert(q.norm == sqrt(30.0)); 271 } 272 273 /++ 274 Quaternionの共役Quaternionを返します. 275 +/ 276 Q conjugate()const{ 277 return Q(-this[0], -this[1], -this[2], this[3]); 278 } 279 unittest{ 280 auto q = Quaternion!(double)(1, 2, 3, 4); 281 auto qA = Quaternion!(double)(-1, -2, -3, 4); 282 assert(q.conjugate == qA); 283 } 284 285 /++ 286 287 +/ 288 Q inverse()()const if(__traits(isFloating, T)){ 289 return conjugate/(this[0]^^T(2.0) + this[1]^^T(2.0) + this[2]^^T(2.0) + this[3]^^T(2.0)); 290 } 291 unittest{ 292 auto q = Quaternion!(double)(0, 1, 0, 1); 293 auto qR = q.inverse; 294 auto qA = Quaternion!(double)(0, -0.5, 0, 0.5); 295 296 foreach (int i, value; qR.vec.elements) { 297 assert( approxEqual(value, qA[i]) ); 298 } 299 } 300 301 /++ 302 自身の回転行列(4x4)を返します. 303 +/ 304 armos.math.Matrix!(E, 4, 4) matrix44(E = T)()const if(__traits(isFloating, E)){ 305 return armos.math.Matrix!(E, 4, 4)( 306 [this[3]^^2.0+this[0]^^2.0-this[1]^^2.0-this[2]^^2.0, 2.0*(this[0]*this[1]-this[3]*this[2]), 2.0*(this[0]*this[2]+this[3]*this[1]), 0], 307 [2.0*(this[0]*this[1]+this[3]*this[2]), this[3]^^2.0-this[0]^^2.0+this[1]^^2.0-this[2]^^2.0, 2.0*(this[1]*this[2]-this[3]*this[0]), 0], 308 [2.0*(this[0]*this[2]-this[3]*this[1]), 2.0*(this[1]*this[2]+this[3]*this[0]), this[3]^^2.0-this[0]^^2.0-this[1]^^2.0+this[2]^^2.0, 0], 309 [0, 0, 0, 1] 310 ); 311 } 312 unittest{ 313 } 314 315 /++ 316 自身の回転行列(3x3)を返します. 317 +/ 318 armos.math.Matrix!(E, 3, 3) matrix33(E = T)()const if(__traits(isFloating, E)){ 319 return armos.math.Matrix!(E, 3, 3)( 320 [this[3]^^2.0+this[0]^^2.0-this[1]^^2.0-this[2]^^2.0, 2.0*(this[0]*this[1]-this[3]*this[2]), 2.0*(this[0]*this[2]+this[3]*this[1]) ], 321 [2.0*(this[0]*this[1]+this[3]*this[2]), this[3]^^2.0-this[0]^^2.0+this[1]^^2.0-this[2]^^2.0, 2.0*(this[1]*this[2]-this[3]*this[0]) ], 322 [2.0*(this[0]*this[2]-this[3]*this[1]), 2.0*(this[1]*this[2]+this[3]*this[0]), this[3]^^2.0-this[0]^^2.0-this[1]^^2.0+this[2]^^2.0] 323 ); 324 } 325 326 /++ 327 指定したベクトルを自身で回転させたベクトルを返します. 328 +/ 329 V3 rotatedVector()(in V3 vec)const if(__traits(isFloating, T)){ 330 if( norm^^2.0 < T.epsilon){ 331 return vec; 332 }else{ 333 auto temp_quat = Q(vec); 334 auto return_quat= this*temp_quat*this.inverse; 335 auto return_vector = V3(return_quat[0], return_quat[1], return_quat[2]); 336 return return_vector; 337 } 338 } 339 unittest{ 340 auto v = armos.math.Vector3d(1, 0, 0); 341 double ang = PI*0.5*0.5; 342 auto q = Quaternion!(double)(0*sin(ang), 0*sin(ang), 1*sin(ang), cos(ang)); 343 auto vR = q.rotatedVector(v); 344 auto vA = armos.math.Vector3d(0, 1, 0); 345 346 foreach (int i, value; vR.elements) { 347 assert( approxEqual(value, vA[i]) ); 348 } 349 } 350 351 /++ 352 指定したベクトルを自身の逆方向に回転させたベクトルを返します. 353 +/ 354 V3 rotatedVectorInversely()(in V3 vec)const if(__traits(isFloating, T)){ 355 if( norm^^2.0 < T.epsilon){ 356 return vec; 357 }else{ 358 auto temp_quat = Q(vec); 359 auto return_quat= this.inverse*temp_quat*this; 360 auto return_vector = V3(return_quat[0], return_quat[1], return_quat[2]); 361 return return_vector; 362 } 363 } 364 unittest{ 365 auto v = armos.math.Vector3d(1, 0, 0); 366 double ang = PI*0.5*0.5; 367 auto q = Quaternion!(double)(0*sin(ang), 0*sin(ang), 1*sin(ang), cos(ang)); 368 auto vR = q.rotatedVectorInversely(v); 369 auto vA = armos.math.Vector3d(0, -1, 0); 370 371 foreach (int i, value; vR.elements) { 372 assert( approxEqual(value, vA[i]) ); 373 } 374 } 375 376 /++ 377 指定した軸で自身を回転させます. 378 Params: 379 ang = 回転角 380 axis = 回転軸 381 +/ 382 static Q angleAxis()(T ang, V3 axis) if(__traits(isFloating, T)){ 383 immutable halfAngle = ang*T(0.5); 384 return Q(axis[0]*sin(halfAngle), axis[1]*sin(halfAngle), axis[2]*sin(halfAngle), cos(halfAngle)); 385 } 386 unittest{ 387 auto v = armos.math.Vector3d(1, 0, 0); 388 auto q = Quaternion!(double).angleAxis(PI*0.5, armos.math.Vector3d(0, 0, 1)); 389 auto vR = q.rotatedVector(v); 390 auto vA = armos.math.Vector3d(0, 1, 0); 391 392 foreach (int i, value; vR.elements) { 393 assert( approxEqual(value, vA[i]) ); 394 } 395 } 396 397 Q productAngle()(T gain) if(__traits(isFloating, T)){ 398 return slerp(Q.zero, this, gain); 399 } 400 401 } 402 403 /// 回転の差分のQuaternionを返します. 404 Q rotationDifference(Q)(in Q from, in Q to){ 405 return from.inverse * to; 406 } 407 unittest{ 408 auto qBegin= Quaternion!(double).angleAxis(PI*0.2, armos.math.Vector3d(0, 0, 1)); 409 auto qEnd = Quaternion!(double).angleAxis(PI*0.5, armos.math.Vector3d(0, 0, 1)); 410 411 auto qAns = Quaternion!(double).angleAxis(PI*0.3, armos.math.Vector3d(0, 0, 1)); 412 auto qResult = rotationDifference(qBegin, qEnd); 413 414 assert(approxEqual(qResult[0], qAns[0])); 415 assert(approxEqual(qResult[1], qAns[1])); 416 assert(approxEqual(qResult[2], qAns[2])); 417 assert(approxEqual(qResult[3], qAns[3])); 418 } 419 420 /++ 421 球面線形補間(Sphercal Linear Interpolation)を行います. 422 Params: 423 from = tが0の時のQuaternion 424 to = tが1の時のQuaternion 425 t = 426 +/ 427 Q slerp(Q, T)(in Q from, in Q to, T t)if(__traits(isFloating, typeof(Q.vec[0]))){ 428 T omega, cos_omega, sin_omega, scale_from, scale_to; 429 430 Q quatTo = to; 431 cos_omega = from.vec.dotProduct(to.vec); 432 433 if (cos_omega < T( 0.0 )) { 434 cos_omega = -cos_omega; 435 quatTo = -to; 436 } 437 438 if( (T( 1.0 ) - cos_omega) > T.epsilon ){ 439 omega = acos(cos_omega); 440 sin_omega = sin(omega); 441 scale_from = sin(( T( 1.0 ) - t ) * omega) / sin_omega; 442 scale_to = sin(t * omega) / sin_omega; 443 }else{ 444 scale_from = T( 1.0 ) - t; 445 scale_to = t; 446 } 447 448 return ( from * scale_from ) + ( quatTo * scale_to ); 449 } 450 unittest{ 451 auto v = armos.math.Vector3d(1, 0, 0); 452 auto qBegin= Quaternion!(double).angleAxis(0, armos.math.Vector3d(0, 0, 1)); 453 auto qEnd = Quaternion!(double).angleAxis(PI*0.5, armos.math.Vector3d(0, 0, 1)); 454 auto qSlerped = qBegin.slerp(qEnd, 2.0); 455 auto vR = qSlerped.rotatedVector(v); 456 auto vA = armos.math.Vector3d(-1, 0, 0); 457 458 foreach (int i, value; vR.elements) { 459 assert( approxEqual(value, vA[i]) ); 460 } 461 462 } 463 464 alias Quaternion!(float) Quaternionf; 465 alias Quaternion!(double) Quaterniond;