1 // Written in the D programming language. 2 /* 3 NYSL Version 0.9982 4 5 A. This software is "Everyone'sWare". It means: 6 Anybody who has this software can use it as if he/she is 7 the author. 8 9 A-1. Freeware. No fee is required. 10 A-2. You can freely redistribute this software. 11 A-3. You can freely modify this software. And the source 12 may be used in any software with no limitation. 13 A-4. When you release a modified version to public, you 14 must publish it with your name. 15 16 B. The author is not responsible for any kind of damages or loss 17 while using or misusing this software, which is distributed 18 "AS IS". No warranty of any kind is expressed or implied. 19 You use AT YOUR OWN RISK. 20 21 C. Copyrighted to Kazuki KOMATSU 22 23 D. Above three clauses are applied both to source and binary 24 form of this software. 25 */ 26 27 /** 28 このモジュールでは、四元数を扱います。 29 */ 30 module carbon.quaternion; 31 32 import carbon.linear; 33 34 import std.conv, 35 std.format, 36 std.functional, 37 std.math, 38 std.traits; 39 40 41 /** 42 四元数 43 */ 44 Quaternion!(CommonType!(A, B, C, D)) quaternion(A, B, C, D)(A a, B b, C c, D d) 45 if(is(Quaternion!(CommonType!(A, B, C, D)))) 46 { 47 typeof(return) dst; 48 49 dst.a = a; 50 dst.b = b; 51 dst.c = c; 52 dst.d = d; 53 54 return dst; 55 } 56 57 58 /// ditto 59 Quaternion!A quaternion(A)(A a) 60 if(is(Quaternion!A)) 61 { 62 typeof(return) dst; 63 64 dst.a = a; 65 dst.b = 0; 66 dst.c = 0; 67 dst.d = 0; 68 69 return dst; 70 } 71 72 73 /// ditto 74 Quaternion!(ElementType!V) quaternion(V)(V v) 75 if(isVector!V) 76 { 77 typeof(return) dst; 78 dst._vec4 = v; 79 return dst; 80 } 81 82 83 /// ditto 84 Quaternion!(CommonType!(R, ElementType!V)) quaternion(R, V)(R r, V v) 85 if(is(Quaternion!(CommonType!(R, ElementType!V)))) 86 { 87 typeof(return) dst; 88 89 dst.s = r; 90 dst.v = v; 91 92 return dst; 93 } 94 95 96 /// ditto 97 Quaternion!E quaternion(E)(E[] arr) 98 if(is(Quaternion!E)) 99 in{ 100 assert(arr.length == 4); 101 } 102 body{ 103 typeof(return) dst; 104 dst._vec4.array[] = arr[]; 105 106 return dst; 107 } 108 109 110 /// ditto 111 struct Quaternion(S) 112 if(isNotVectorOrMatrix!S) 113 { 114 this(E)(in Quaternion!E q) 115 if(is(E : S)) 116 { 117 this = q; 118 } 119 120 121 this()(in SCVector!(int, 4) m) 122 { 123 this._vec4 = m; 124 } 125 126 127 /// 128 ref inout(S) opIndex(size_t i) pure nothrow @safe inout 129 in{ 130 assert(i < 4); 131 } 132 body{ 133 return _vec4[i]; 134 } 135 136 137 ref inout(S) s() pure nothrow @safe @property inout { return _vec4[0]; } 138 ref inout(S) i() pure nothrow @safe @property inout { return _vec4[1]; } 139 ref inout(S) j() pure nothrow @safe @property inout { return _vec4[2]; } 140 ref inout(S) k() pure nothrow @safe @property inout { return _vec4[3]; } 141 142 143 alias a = s; 144 alias b = i; 145 alias c = j; 146 alias d = k; 147 148 alias w = a; 149 alias x = b; 150 alias y = c; 151 alias z = d; 152 153 154 @property 155 auto v(this T)() pure nothrow @safe 156 { 157 return _vec4.pref.swizzle.bcd; 158 } 159 160 161 @property 162 void v(V)(in V v) 163 { 164 foreach(i; 0 .. 3) 165 this._vec4[i + 1] = v[i]; 166 } 167 168 169 @property 170 V asVec4(V = SCVector!(S, 4))() inout 171 { 172 V v = this._vec4; 173 return v; 174 } 175 176 177 auto opUnary(string op : "-", E)(in Quaternion!E q) const 178 { 179 return typeof(return)(typeof(typeof(return).init._vec4)(this._vec4 * -1)); 180 } 181 182 183 Quaternion!(CommonType!(S, E)) opBinary(string op : "+", E)(in Quaternion!E q) const 184 if(!is(CommonType!(S, E) == void)) 185 { 186 return typeof(return)(typeof(typeof(return).init._vec4)(this._vec4.pref + q._vec4)); 187 } 188 189 190 Quaternion!(CommonType!(S, E)) opBinary(string op : "-", E)(in Quaternion!E q) const 191 if(!is(CommonType!(S, E) == void)) 192 { 193 return typeof(return)(typeof(typeof(return).init._vec4)(this._vec4.pref - q._vec4)); 194 } 195 196 197 Quaternion!(CommonType!(S, E)) opBinary(string op : "*", E)(in Quaternion!E q) const 198 if(!is(CommonType!(S, E) == void)) 199 { 200 return quaternion(this.s * q.s - this.v.dot(q.v), (this.s * q.v) + (q.s * this.v) + (this.v.cross(q.v))); 201 } 202 203 204 auto opBinary(string op : "/", E)(in Quaternion!E q) const 205 if(isFloatingPoint!(CommonType!(S, E))) 206 { 207 return this * q.inverse; 208 } 209 210 211 Quaternion!(CommonType!(S, E)) opBinary(string op : "+", E)(in E s) const 212 if(!is(CommonType!(S, E) == void)) 213 { 214 typeof(return) dst = this; 215 dst.a += s; 216 return dst; 217 } 218 219 220 Quaternion!(CommonType!(S, E)) opBinary(string op : "-", E)(in E s) const 221 if(!is(CommonType!(S, E) == void)) 222 { 223 typeof(return) dst = this; 224 dst.a -= s; 225 return dst; 226 } 227 228 229 Quaternion!(CommonType!(S, E)) opBinary(string op : "*", E)(in E s) const 230 if(!is(CommonType!(S, E) == void)) 231 { 232 typeof(return) dst = this; 233 dst._vec4 *= s; 234 return dst; 235 } 236 237 238 Quaternion!(CommonType!(S, E)) opBinary(string op : "/", E)(in E s) const 239 if(!is(CommonType!(S, E) == void)) 240 { 241 typeof(return) dst = this; 242 dst._vec4 /= s; 243 return dst; 244 } 245 246 247 Quaternion!(Select!(isFloatingPoint!S, S, real)) opBinary(string op : "^^", Int : long)(Int n) const 248 { 249 if(n < 0) 250 return this.inverse ^^ (-n); 251 else 252 { 253 typeof(return) dst = this, memo = this; 254 255 n >>= 1; 256 while(n){ 257 memo *= memo; 258 259 if(n) 260 dst *= memo; 261 262 n >>= 1; 263 } 264 265 return dst; 266 } 267 } 268 269 270 Quaternion!(CommonType!(S, E)) opBinaryRight(string op : "+", E)(in E s) const 271 if(!is(CommonType!(S, E) == void)) 272 { 273 typeof(return) dst; 274 dst = this; 275 dst.a += s; 276 return dst; 277 } 278 279 280 Quaternion!(CommonType!(S, E)) opBinaryRight(string op : "-", E)(in E s) const 281 if(!is(CommonType!(S, E) == void)) 282 { 283 return quaternion!(CommonType!(S, E))(s) - this; 284 } 285 286 287 Quaternion!(CommonType!(S, E)) opBinaryRight(string op : "*", E)(in E s) const 288 if(!is(CommonType!(S, E) == void)) 289 { 290 typeof(return) dst; 291 dst = this; 292 dst._vec4 *= s; 293 return dst; 294 } 295 296 297 auto opBinaryRight(string op : "/", E)(in E s) const 298 if(isFloatingPoint!(CommonType!(S, E))) 299 { 300 return s / this.sumOfSquare * this.conj; 301 } 302 303 304 void opAssign(E)(in Quaternion!E q) 305 if(is(E : S)) 306 { 307 this._vec4 = q._vec4; 308 } 309 310 311 void opAssign(E)(in E s) 312 if(is(E : S)) 313 { 314 this._vec4 = 0; 315 this.a = s; 316 } 317 318 319 void opOpAssign(string op, E)(in Quaternion!E q) 320 if(!is(CommonType!(S, E) == void) && is(typeof(mixin("this " ~ op ~ " q")))) 321 { 322 this = mixin("this " ~ op ~ " q"); 323 } 324 325 326 void opOpAssign(string op, E)(in E s) 327 if(is(E : S) && is(typeof(mixin("this " ~ op ~ " s")))) 328 { 329 this = mixin("this " ~ op ~ " s"); 330 } 331 332 333 void toString(scope void delegate(const(char)[]) sink, string formatString) const 334 { 335 formattedWrite(sink, formatString, _vec4.array); 336 } 337 338 339 bool opEquals(E)(auto ref const Quaternion!E q) pure nothrow @safe const 340 { 341 foreach(i; 0 .. 4) 342 if(this[i] != q[i]) 343 return false; 344 return true; 345 } 346 347 348 private: 349 SCVector!(S, 4) _vec4 = [1, 0, 0, 0].matrix!(4, 1); 350 } 351 352 353 /// 354 unittest { 355 assert(Quaternion!int.init == quaternion(1, 0, 0, 0)); 356 // 1 = [1; (0, 0, 0)]な四元数の作成 357 auto q = quaternion(1); 358 359 // 添字によるアクセス 360 assert(q[0] == 1); 361 assert(q[1] == 0); 362 assert(q[2] == 0); 363 assert(q[3] == 0); 364 365 366 // 1 + 2i + 3j + 4k = [1; (2, 3, 4)]な四元数の作成 367 q = quaternion(1, 2, 3, 4); 368 assert(q[0] == 1); 369 assert(q[1] == 2); 370 assert(q[2] == 3); 371 assert(q[3] == 4); 372 373 // a, b, c, dによるアクセス 374 assert(q.a == 1); 375 assert(q.b == 2); 376 assert(q.c == 3); 377 assert(q.d == 4); 378 379 // スカラー部であるs, ベクトル部であるvによるアクセス 380 assert(q.s == 1); 381 assert(q.v == [2, 3, 4].matrix!(3, 1)); 382 383 // v = (i, j, k) 384 assert(q.i == 2); 385 assert(q.j == 3); 386 assert(q.k == 4); 387 388 // opIndexやa, b, c, d, i, j, k, s, vへは代入可能 389 q.s = 7; 390 assert(q[0] == 7); 391 392 // vはベクトルなので、ベクトルを代入可能 393 q.v = [4, 5, 6].matrix!(3, 1); 394 assert(q[1] == 4); 395 assert(q[2] == 5); 396 assert(q[3] == 6); 397 398 // スカラー部とベクトル部による四元数の作成 399 q = quaternion(8, [9, 10, 11].matrix!(3, 1)); 400 assert(q[0] == 8); 401 assert(q[1] == 9); 402 assert(q[2] == 10); 403 assert(q[3] == 11); 404 405 406 // 和 407 q = quaternion(1, 2, 3, 4) + quaternion(2, 2, 2, 2); 408 assert(q == quaternion(3, 4, 5, 6)); 409 410 q = q + 3; 411 assert(q == quaternion(6, 4, 5, 6)); 412 413 q = 3 + q; 414 assert(q == quaternion(9, 4, 5, 6)); 415 416 // 複合代入和 417 q += q; 418 assert(q == quaternion(18, 8, 10, 12)); 419 420 q += 3; 421 assert(q == quaternion(21, 8, 10, 12)); 422 423 424 // 差 425 q = quaternion(1, 2, 3, 4) - quaternion(2, 2, 2, 2); 426 assert(q == quaternion(-1, 0, 1, 2)); 427 428 q = q - 3; 429 assert(q == quaternion(-4, 0, 1, 2)); 430 431 q = 3 - q; 432 assert(q == quaternion(7, 0, -1, -2)); 433 434 // 複合代入和 435 q -= q; 436 assert(q == quaternion(0, 0, 0, 0)); 437 438 q -= 3; 439 assert(q == quaternion(-3, 0, 0, 0)); 440 441 442 // 積 443 q = quaternion(1, 2, 3, 4) * quaternion(7, 6, 7, 8); 444 assert(q == quaternion(-58, 16, 36, 32)); 445 446 q = quaternion(1, 2, 3, 4) * 4; 447 assert(q == quaternion(4, 8, 12, 16)); 448 449 q = 4 * quaternion(1, 2, 3, 4); 450 assert(q == quaternion(4, 8, 12, 16)); 451 452 q = quaternion(1, 2, 3, 4); 453 q *= quaternion(7, 6, 7, 8); 454 assert(q == quaternion(-58, 16, 36, 32)); 455 456 q = quaternion(1, 2, 3, 4); 457 q *= 4; 458 assert(q == quaternion(4, 8, 12, 16)); 459 460 461 // 商 462 assert((quaternion(-58.0, 16, 36, 32) / quaternion(7, 6, 7, 8)).approxEqual(quaternion(1, 2, 3, 4))); 463 assert(quaternion(4.0, 8, 12, 16) / 4 == quaternion(1, 2, 3, 4)); 464 assert((16.0 / quaternion(1.0, 2, 3, 4)).approxEqual(quaternion(16.0) / quaternion(1.0, 2, 3, 4))); 465 auto p = quaternion(-58.0, 16, 36, 32); 466 p /= quaternion(7, 6, 7, 8); 467 assert(p.approxEqual(quaternion(1, 2, 3, 4))); 468 469 p = quaternion(4.0, 8, 12, 16); 470 p /= 4; 471 assert(p.approxEqual(quaternion(1, 2, 3, 4))); 472 473 // 累乗 474 q = quaternion(1, 1, 2, 2); 475 p = q ^^ 3; 476 assert(approxEqual(p, q * q * q)); 477 478 p = q ^^ -3; 479 assert(approxEqual(p, q.inverse * q.inverse * q.inverse)); 480 assert(approxEqual(q ^^ 3 * q ^^ -3, quaternion(1, 0, 0, 0))); 481 } 482 483 484 /** 485 四元数の各要素の自乗和を返します 486 */ 487 auto sumOfSquare(E)(in Quaternion!E q) 488 { 489 return q.a ^^ 2 + q.b ^^ 2 + q.c ^^ 2 + q.d ^^ 2; 490 } 491 492 unittest 493 { 494 assert(quaternion(1, 2, 3, 4).sumOfSquare == 30); 495 } 496 497 498 /** 499 四元数の絶対値を返します 500 */ 501 auto abs(E)(in Quaternion!E q) 502 { 503 static if(isFloatingPoint!E) 504 return q.sumOfSquare.sqrt; 505 else 506 return sqrt(q.sumOfSquare.to!real); 507 } 508 509 unittest 510 { 511 assert(quaternion(2, 2, 2, 2).abs == 4); 512 assert(quaternion(1, 2, 3, 4).abs == sqrt(30.0)); 513 } 514 515 516 /** 517 四元数の共役を返します 518 */ 519 Quaternion!E conj(E)(in Quaternion!E q) pure nothrow @safe 520 { 521 typeof(return) dst; 522 dst.s = q.s; 523 dst.v = q.v * -1; 524 return dst; 525 } 526 527 unittest 528 { 529 auto q = quaternion(1, 2, 3, 4); 530 assert(q.conj == quaternion(1, -2, -3, -4)); 531 } 532 533 534 /** 535 approxEqualの四元数バージョン 536 */ 537 bool approxEqual(alias pred = std.math.approxEqual, E1, E2)(in Quaternion!E1 q1, in Quaternion!E2 q2) 538 { 539 foreach(i; 0 .. 4) 540 if(!binaryFun!pred(q1[i], q2[i])) 541 return false; 542 return true; 543 } 544 545 unittest 546 { 547 auto q = quaternion(1, 2, 3, 4); 548 assert(approxEqual(q, q)); 549 } 550 551 552 /** 553 正規化します 554 */ 555 auto normalize(E)(in Quaternion!E q) 556 { 557 return q / q.abs; 558 } 559 560 unittest 561 { 562 auto q = quaternion(1, 2, 3, 4); 563 assert(std.math.approxEqual(q.normalize.sumOfSquare, 1)); 564 } 565 import std.stdio; 566 567 /** 568 積の逆元 569 */ 570 auto inverse(E)(in Quaternion!E q) 571 { 572 static if(!isFloatingPoint!E) 573 return q.conj / q.sumOfSquare.to!real; 574 else 575 return q.conj / q.sumOfSquare; 576 } 577 578 unittest 579 { 580 import std.stdio; 581 582 auto q = quaternion(1, 2, 3, 4); 583 assert(approxEqual((q * q.inverse), quaternion(1, 0, 0, 0))); 584 }