1 /** 2 * N-dimensional small vector math. 3 * 4 * Copyright: Copyright Guillaume Piolat 2021. 5 * Copyright Chance Snow 2021. 6 * Copyright Aleksandr Druzhinin 2018. 7 * Copyright Nathan Sashihara 2018. 8 * Copyright Ryan Roden-Corrent 2016. 9 * Copyright Steven Dwy 2015. 10 * Copyright Martin Nowak 2015. 11 * Copyright Tanel Tagaväli 2015. 12 * 13 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 14 */ 15 module dplug.math.vector; 16 17 18 import std.traits, 19 std.math, 20 std.array; 21 22 import inteli.emmintrin; 23 24 /** 25 * Generic 1D small vector. 26 * Params: 27 * N = number of elements 28 * T = type of elements 29 */ 30 struct Vector(T, int N) 31 { 32 nothrow: 33 public 34 { 35 static assert(N >= 1); 36 37 // fields definition 38 union 39 { 40 T[N] v; 41 struct 42 { 43 static if (N >= 1) 44 { 45 T x; 46 alias x r; 47 } 48 static if (N >= 2) 49 { 50 T y; 51 alias y g; 52 } 53 static if (N >= 3) 54 { 55 T z; 56 alias z b; 57 } 58 static if (N >= 4) 59 { 60 T w; 61 alias w a; 62 } 63 } 64 } 65 66 /// Construct a Vector with a `T[]` or the values as arguments 67 @nogc this(Args...)(Args args) pure nothrow 68 { 69 static if (args.length == 1) 70 { 71 // Construct a Vector from a single value. 72 opAssign!(Args[0])(args[0]); 73 } 74 else 75 { 76 // validate the total argument count across scalars and vectors 77 template argCount(T...) { 78 static if(T.length == 0) 79 enum argCount = 0; // done recursing 80 else static if(isVector!(T[0])) 81 enum argCount = T[0]._N + argCount!(T[1..$]); 82 else 83 enum argCount = 1 + argCount!(T[1..$]); 84 } 85 86 static assert(argCount!Args <= N, "Too many arguments in vector constructor"); 87 88 int index = 0; 89 foreach(arg; args) 90 { 91 static if (isAssignable!(T, typeof(arg))) 92 { 93 v[index] = arg; 94 index++; // has to be on its own line (DMD 2.068) 95 } 96 else static if (isVector!(typeof(arg)) && isAssignable!(T, arg._T)) 97 { 98 mixin(generateLoopCode!("v[index + @] = arg[@];", arg._N)()); 99 index += arg._N; 100 } 101 else 102 static assert(false, "Unrecognized argument in Vector constructor"); 103 } 104 assert(index == N, "Bad arguments in Vector constructor"); 105 } 106 } 107 108 size_t toHash() const nothrow @safe 109 { 110 size_t hash = 0; 111 foreach (elem; v) { 112 hash = elem.hashOf(hash); 113 } 114 return hash; 115 } 116 117 /// Assign a Vector from a compatible type. 118 @nogc ref Vector opAssign(U)(U x) pure nothrow if (isAssignable!(T, U)) 119 { 120 mixin(generateLoopCode!("v[@] = x;", N)()); // copy to each component 121 return this; 122 } 123 124 /// Assign a Vector with a static array type. 125 @nogc ref Vector opAssign(U)(U arr) pure nothrow if ((isStaticArray!(U) && isAssignable!(T, typeof(arr[0])) && (arr.length == N))) 126 { 127 mixin(generateLoopCode!("v[@] = arr[@];", N)()); 128 return this; 129 } 130 131 /// Assign with a dynamic array. 132 /// Size is checked in debug-mode. 133 @nogc ref Vector opAssign(U)(U arr) pure nothrow if (isDynamicArray!(U) && isAssignable!(T, typeof(arr[0]))) 134 { 135 assert(arr.length == N); 136 mixin(generateLoopCode!("v[@] = arr[@];", N)()); 137 return this; 138 } 139 140 /// Assign from a samey Vector. 141 @nogc ref Vector opAssign(U)(U u) pure nothrow if (is(U : Vector)) 142 { 143 v[] = u.v[]; 144 return this; 145 } 146 147 /// Assign from other vectors types (same size, compatible type). 148 @nogc ref Vector opAssign(U)(U x) pure nothrow if (isVector!U 149 && isAssignable!(T, U._T) 150 && (!is(U: Vector)) 151 && (U._N == _N)) 152 { 153 mixin(generateLoopCode!("v[@] = x.v[@];", N)()); 154 return this; 155 } 156 157 /// Returns: a pointer to content. 158 @nogc inout(T)* ptr() pure inout nothrow @property 159 { 160 return v.ptr; 161 } 162 163 @nogc bool opEquals(U)(U other) pure const nothrow 164 if (is(U : Vector)) 165 { 166 for (int i = 0; i < N; ++i) 167 { 168 if (v[i] != other.v[i]) 169 { 170 return false; 171 } 172 } 173 return true; 174 } 175 176 @nogc bool opEquals(U)(U other) pure const nothrow 177 if (isConvertible!U) 178 { 179 Vector conv = other; 180 return opEquals(conv); 181 } 182 183 @nogc Vector opUnary(string op)() pure const nothrow 184 if (op == "+" || op == "-" || op == "~" || op == "!") 185 { 186 Vector res = void; 187 mixin(generateLoopCode!("res.v[@] = " ~ op ~ " v[@];", N)()); 188 return res; 189 } 190 191 @nogc ref Vector opOpAssign(string op, U)(U operand) pure nothrow 192 if (is(U : Vector)) 193 { 194 mixin(generateLoopCode!("v[@] " ~ op ~ "= operand.v[@];", N)()); 195 return this; 196 } 197 198 @nogc ref Vector opOpAssign(string op, U)(U operand) pure nothrow if (isConvertible!U) 199 { 200 Vector conv = operand; 201 return opOpAssign!op(conv); 202 } 203 204 @nogc Vector opBinary(string op, U)(U operand) pure const nothrow 205 if (is(U: Vector) || (isConvertible!U)) 206 { 207 Vector result = void; 208 static if (is(U: T)) 209 mixin(generateLoopCode!("result.v[@] = cast(T)(v[@] " ~ op ~ " operand);", N)()); 210 else 211 { 212 Vector other = operand; 213 mixin(generateLoopCode!("result.v[@] = cast(T)(v[@] " ~ op ~ " other.v[@]);", N)()); 214 } 215 return result; 216 } 217 218 @nogc Vector opBinaryRight(string op, U)(U operand) pure const nothrow if (isConvertible!U) 219 { 220 Vector result = void; 221 static if (is(U: T)) 222 mixin(generateLoopCode!("result.v[@] = cast(T)(operand " ~ op ~ " v[@]);", N)()); 223 else 224 { 225 Vector other = operand; 226 mixin(generateLoopCode!("result.v[@] = cast(T)(other.v[@] " ~ op ~ " v[@]);", N)()); 227 } 228 return result; 229 } 230 231 @nogc ref T opIndex(size_t i) pure nothrow 232 { 233 return v[i]; 234 } 235 236 @nogc ref const(T) opIndex(size_t i) pure const nothrow 237 { 238 return v[i]; 239 } 240 241 @nogc T opIndexAssign(U : T)(U x, size_t i) pure nothrow 242 { 243 return v[i] = x; 244 } 245 246 247 /// Implements swizzling. 248 /// 249 /// Example: 250 /// --- 251 /// vec4i vi = [4, 1, 83, 10]; 252 /// assert(vi.zxxyw == [83, 4, 4, 1, 10]); 253 /// --- 254 @nogc @property auto opDispatch(string op, U = void)() pure const nothrow if (isValidSwizzle!(op)) 255 { 256 alias Vector!(T, op.length) returnType; 257 returnType res = void; 258 enum indexTuple = swizzleTuple!op; 259 foreach(i, index; indexTuple) 260 res.v[i] = v[index]; 261 return res; 262 } 263 264 /// Support swizzling assignment like in shader languages. 265 /// 266 /// Example: 267 /// --- 268 /// vec3f v = [0, 1, 2]; 269 /// v.yz = v.zx; 270 /// assert(v == [0, 2, 0]); 271 /// --- 272 @nogc @property void opDispatch(string op, U)(U x) pure 273 if ((op.length >= 2) 274 && (isValidSwizzleUnique!op) // v.xyy will be rejected 275 && is(typeof(Vector!(T, op.length)(x)))) // can be converted to a small vector of the right size 276 { 277 Vector!(T, op.length) conv = x; 278 enum indexTuple = swizzleTuple!op; 279 foreach(i, index; indexTuple) 280 v[index] = conv[i]; 281 } 282 283 /// Casting to small vectors of the same size. 284 /// Example: 285 /// --- 286 /// vec4f vf; 287 /// vec4d vd = cast!(vec4d)vf; 288 /// --- 289 @nogc U opCast(U)() pure const nothrow if (isVector!U && (U._N == _N)) 290 { 291 U res = void; 292 mixin(generateLoopCode!("res.v[@] = cast(U._T)v[@];", N)()); 293 return res; 294 } 295 296 /// Implement slices operator overloading. 297 /// Allows to go back to slice world. 298 /// Returns: length. 299 @nogc int opDollar() pure const nothrow 300 { 301 return N; 302 } 303 304 /// Slice containing vector values 305 /// Returns: a slice which covers the whole Vector. 306 @nogc T[] opSlice() pure nothrow 307 { 308 return v[]; 309 } 310 311 /// vec[a..b] 312 @nogc T[] opSlice(int a, int b) pure nothrow 313 { 314 return v[a..b]; 315 } 316 317 /// Squared Euclidean length of the Vector 318 /// Returns: squared length. 319 @nogc T squaredMagnitude() pure const nothrow 320 { 321 T sumSquares = 0; 322 mixin(generateLoopCode!("sumSquares += v[@] * v[@];", N)()); 323 return sumSquares; 324 } 325 326 /// Squared Euclidean distance between this vector and another one 327 /// Returns: squared Euclidean distance. 328 @nogc T squaredDistanceTo(Vector v) pure const nothrow 329 { 330 return (v - this).squaredMagnitude(); 331 } 332 333 static if (isFloatingPoint!T) 334 { 335 /// Euclidean length of the vector 336 /// Returns: Euclidean length 337 @nogc T magnitude() pure const nothrow 338 { 339 return sqrt(squaredMagnitude()); 340 } 341 342 /// Inverse Euclidean length of the vector 343 /// Returns: Inverse of Euclidean length. 344 @nogc T inverseMagnitude() pure const nothrow 345 { 346 return 1 / sqrt(squaredMagnitude()); 347 } 348 349 alias fastInverseLength = fastInverseMagnitude; 350 /// Faster but less accurate inverse of Euclidean length. 351 /// Returns: Inverse of Euclidean length. 352 @nogc T fastInverseMagnitude() pure const nothrow 353 { 354 return inverseSqrt(squaredMagnitude()); 355 } 356 357 /// Euclidean distance between this vector and another one 358 /// Returns: Euclidean distance between this and other. 359 @nogc T distanceTo(Vector other) pure const nothrow 360 { 361 return (other - this).magnitude(); 362 } 363 364 /// In-place normalization. 365 @nogc void normalize() pure nothrow 366 { 367 auto invMag = inverseMagnitude(); 368 mixin(generateLoopCode!("v[@] *= invMag;", N)()); 369 } 370 371 /// Returns a normalized copy of this Vector 372 /// Returns: Normalized vector. 373 @nogc Vector normalized() pure const nothrow 374 { 375 Vector res = this; 376 res.normalize(); 377 return res; 378 } 379 380 /// Faster but less accurate in-place normalization. 381 @nogc void fastNormalize() pure nothrow 382 { 383 auto invLength = fastInverseMagnitude(); 384 mixin(generateLoopCode!("v[@] *= invLength;", N)()); 385 } 386 387 /// Faster but less accurate vector normalization. 388 /// Returns: Normalized vector. 389 @nogc Vector fastNormalized() pure const nothrow 390 { 391 Vector res = this; 392 res.fastNormalize(); 393 return res; 394 } 395 396 static if (N == 3) 397 { 398 /// Gets an orthogonal vector from a 3-dimensional vector. 399 /// Doesn’t normalize the output. 400 /// Authors: Sam Hocevar 401 /// See_also: Source at $(WEB lolengine.net/blog/2013/09/21/picking-orthogonal-vector-combing-coconuts). 402 @nogc Vector getOrthogonalVector() pure const nothrow 403 { 404 return abs(x) > abs(z) ? Vector(-y, x, 0.0) : Vector(0.0, -z, y); 405 } 406 } 407 } 408 } 409 410 private 411 { 412 enum _N = N; 413 alias T _T; 414 415 // define types that can be converted to this, but are not the same type 416 template isConvertible(T) 417 { 418 enum bool isConvertible = (!is(T : Vector)) 419 && is(typeof( 420 { 421 T x; 422 Vector v = x; 423 }())); 424 } 425 426 // define types that can't be converted to this 427 template isForeign(T) 428 { 429 enum bool isForeign = (!isConvertible!T) && (!is(T: Vector)); 430 } 431 432 template isValidSwizzle(string op, int lastSwizzleClass = -1) 433 { 434 static if (op.length == 0) 435 enum bool isValidSwizzle = true; 436 else 437 { 438 enum len = op.length; 439 enum int swizzleClass = swizzleClassify!(op[0]); 440 enum bool swizzleClassValid = (lastSwizzleClass == -1 || (swizzleClass == lastSwizzleClass)); 441 enum bool isValidSwizzle = (swizzleIndex!(op[0]) != -1) 442 && swizzleClassValid 443 && isValidSwizzle!(op[1..len], swizzleClass); 444 } 445 } 446 447 template searchElement(char c, string s) 448 { 449 static if (s.length == 0) 450 { 451 enum bool result = false; 452 } 453 else 454 { 455 enum string tail = s[1..s.length]; 456 enum bool result = (s[0] == c) || searchElement!(c, tail).result; 457 } 458 } 459 460 template hasNoDuplicates(string s) 461 { 462 static if (s.length == 1) 463 { 464 enum bool result = true; 465 } 466 else 467 { 468 enum tail = s[1..s.length]; 469 enum bool result = !(searchElement!(s[0], tail).result) && hasNoDuplicates!(tail).result; 470 } 471 } 472 473 // true if the swizzle has at the maximum one time each letter 474 template isValidSwizzleUnique(string op) 475 { 476 static if (isValidSwizzle!op) 477 enum isValidSwizzleUnique = hasNoDuplicates!op.result; 478 else 479 enum bool isValidSwizzleUnique = false; 480 } 481 482 template swizzleIndex(char c) 483 { 484 static if((c == 'x' || c == 'r') && N >= 1) 485 enum swizzleIndex = 0; 486 else static if((c == 'y' || c == 'g') && N >= 2) 487 enum swizzleIndex = 1; 488 else static if((c == 'z' || c == 'b') && N >= 3) 489 enum swizzleIndex = 2; 490 else static if ((c == 'w' || c == 'a') && N >= 4) 491 enum swizzleIndex = 3; 492 else 493 enum swizzleIndex = -1; 494 } 495 496 template swizzleClassify(char c) 497 { 498 static if(c == 'x' || c == 'y' || c == 'z' || c == 'w') 499 enum swizzleClassify = 0; 500 else static if(c == 'r' || c == 'g' || c == 'b' || c == 'a') 501 enum swizzleClassify = 1; 502 else 503 enum swizzleClassify = -1; 504 } 505 506 template swizzleTuple(string op) 507 { 508 enum opLength = op.length; 509 static if (op.length == 0) 510 enum swizzleTuple = []; 511 else 512 enum swizzleTuple = [ swizzleIndex!(op[0]) ] ~ swizzleTuple!(op[1..op.length]); 513 } 514 } 515 } 516 517 /// True if `T` is some kind of `Vector` 518 enum isVector(T) = is(T : Vector!U, U...); 519 520 /// 521 unittest 522 { 523 static assert(isVector!vec2f); 524 static assert(isVector!vec3d); 525 static assert(isVector!(vec4!real)); 526 static assert(!isVector!float); 527 } 528 529 /// Get the numeric type used to measure a vectors's coordinates. 530 alias DimensionType(T : Vector!U, U...) = U[0]; 531 532 /// 533 unittest 534 { 535 static assert(is(DimensionType!vec2f == float)); 536 static assert(is(DimensionType!vec3d == double)); 537 } 538 539 /// 540 template vec2(T) { alias Vector!(T, 2) vec2; } 541 /// 542 template vec3(T) { alias Vector!(T, 3) vec3; } 543 /// 544 template vec4(T) { alias Vector!(T, 4) vec4; } 545 546 alias vec2!int vec2i; /// 547 alias vec2!float vec2f; /// 548 alias vec2!double vec2d; /// 549 550 alias vec3!int vec3i; /// 551 alias vec3!float vec3f; /// 552 alias vec3!double vec3d; /// 553 554 alias vec4!int vec4i; /// 555 alias vec4!float vec4f; /// 556 alias vec4!double vec4d; /// 557 558 559 /// Element-wise minimum. 560 @nogc Vector!(T, N) minByElem(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow 561 { 562 import std.algorithm: min; 563 Vector!(T, N) res = void; 564 mixin(generateLoopCode!("res.v[@] = min(a.v[@], b.v[@]);", N)()); 565 return res; 566 } 567 568 /// Element-wise maximum. 569 @nogc Vector!(T, N) maxByElem(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow 570 { 571 import std.algorithm: max; 572 Vector!(T, N) res = void; 573 mixin(generateLoopCode!("res.v[@] = max(a.v[@], b.v[@]);", N)()); 574 return res; 575 } 576 577 /// Element-wise absolute value. 578 @nogc Vector!(T, N) absByElem(T, int N)(const Vector!(T, N) a) pure nothrow 579 { 580 Vector!(T, N) res = void; 581 mixin(generateLoopCode!("res.v[@] = abs(a.v[@]);", N)()); 582 return res; 583 } 584 585 /// Dot product of two vectors 586 /// Returns: Dot product. 587 @nogc T dot(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow 588 { 589 T sum = 0; 590 mixin(generateLoopCode!("sum += a.v[@] * b.v[@];", N)()); 591 return sum; 592 } 593 594 /// Cross product of two 3D vectors 595 /// Returns: 3D cross product. 596 /// Thanks to vuaru for corrections. 597 @nogc Vector!(T, 3) cross(T)(const Vector!(T, 3) a, const Vector!(T, 3) b) pure nothrow 598 { 599 return Vector!(T, 3)(a.y * b.z - a.z * b.y, 600 a.z * b.x - a.x * b.z, 601 a.x * b.y - a.y * b.x); 602 } 603 604 /// 3D reflect, like the GLSL function. 605 /// Returns: a reflected by normal b. 606 @nogc Vector!(T, N) reflect(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow 607 { 608 return a - (2 * dot(b, a)) * b; 609 } 610 611 /// 612 @nogc unittest 613 { 614 // reflect a 2D vector across the x axis (the normal points along the y axis) 615 assert(vec2f(1,1).reflect(vec2f(0,1)) == vec2f(1,-1)); 616 assert(vec2f(1,1).reflect(vec2f(0,-1)) == vec2f(1,-1)); 617 618 // note that the normal must be, well, normalized: 619 assert(vec2f(1,1).reflect(vec2f(0,20)) != vec2f(1,-1)); 620 621 // think of this like a ball hitting a flat floor at an angle. 622 // the x and y components remain unchanged, and the z inverts 623 assert(vec3f(2,3,-0.5).reflect(vec3f(0,0,1)) == vec3f(2,3,0.5)); 624 } 625 626 /// Angle between two vectors 627 /// Returns: angle between vectors. 628 /// See_also: "The Right Way to Calculate Stuff" at $(WEB www.plunk.org/~hatch/rightway.php) 629 @nogc T angleBetween(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow 630 { 631 auto aN = a.normalized(); 632 auto bN = b.normalized(); 633 auto dp = dot(aN, bN); 634 635 if (dp < 0) 636 return T(PI) - 2 * asin((-bN-aN).magnitude / 2); 637 else 638 return 2 * asin((bN-aN).magnitude / 2); 639 } 640 641 static assert(vec2f.sizeof == 8); 642 static assert(vec3d.sizeof == 24); 643 static assert(vec4i.sizeof == 16); 644 645 unittest 646 { 647 static assert(vec2i.isValidSwizzle!"xyx"); 648 static assert(!vec2i.isValidSwizzle!"xyz"); 649 static assert(vec4i.isValidSwizzle!"brra"); 650 static assert(!vec4i.isValidSwizzle!"rgyz"); 651 static assert(vec2i.isValidSwizzleUnique!"xy"); 652 static assert(vec2i.isValidSwizzleUnique!"yx"); 653 static assert(!vec2i.isValidSwizzleUnique!"xx"); 654 655 alias vec2l = vec2!long; 656 alias vec3ui = vec3!uint; 657 alias vec4ub = vec4!ubyte; 658 659 assert(vec2l(0, 1) == vec2i(0, 1)); 660 661 int[2] arr = [0, 1]; 662 int[] arr2 = new int[2]; 663 arr2[] = arr[]; 664 vec2i a = vec2i([0, 1]); 665 vec2i a2 = vec2i(0, 1); 666 immutable vec2i b = vec2i(0); 667 assert(b[0] == 0 && b[1] == 0); 668 vec2i c = arr; 669 vec2l d = arr2; 670 assert(a == a2); 671 assert(a == c); 672 assert(vec2l(a) == vec2l(a)); 673 assert(vec2l(a) == d); 674 675 int[vec2i] hashMap; 676 hashMap[a] = (c - a).squaredMagnitude; 677 assert(hashMap[a] == (c - a).squaredMagnitude); 678 679 vec4i x = [4, 5, 6, 7]; 680 assert(x == x); 681 --x[0]; 682 assert(x[0] == 3); 683 ++x[0]; 684 assert(x[0] == 4); 685 x[1] &= 1; 686 x[2] = 77 + x[2]; 687 x[3] += 3; 688 assert(x == [4, 1, 83, 10]); 689 assert(x.xxywz == [4, 4, 1, 10, 83]); 690 assert(x.xxxxxxx == [4, 4, 4, 4, 4, 4, 4]); 691 assert(x.abgr == [10, 83, 1, 4]); 692 assert(a != b); 693 x = vec4i(x.xyz, 166); 694 assert(x == [4, 1, 83, 166]); 695 696 vec2l e = a; 697 vec2l f = a + b; 698 assert(f == vec2l(a)); 699 700 vec3ui g = vec3i(78,9,4); 701 g ^= vec3i(78,9,4); 702 assert(g == vec3ui(0)); 703 //g[0..2] = 1u; 704 //assert(g == [2, 1, 0]); 705 706 assert(vec2i(4, 5) + 1 == vec2i(5,6)); 707 assert(vec2i(4, 5) - 1 == vec2i(3,4)); 708 assert(1 + vec2i(4, 5) == vec2i(5,6)); 709 assert(vec3f(1,1,1) * 0 == 0); 710 assert(1.0 * vec3d(4,5,6) == vec3f(4,5.0f,6.0)); 711 712 auto dx = vec2i(1,2); 713 auto dy = vec2i(4,5); 714 auto dp = dot(dx, dy); 715 assert(dp == 14 ); 716 717 vec3i h = cast(vec3i)(vec3d(0.5, 1.1, -2.2)); 718 assert(h == [0, 1, -2]); 719 assert(h[] == [0, 1, -2]); 720 assert(h[1..3] == [1, -2]); 721 assert(h.zyx == [-2, 1, 0]); 722 723 h.yx = vec2i(5, 2); // swizzle assignment 724 725 assert(h.xy == [2, 5]); 726 assert(-h[1] == -5); 727 assert(++h[0] == 3); 728 729 //assert(h == [-2, 1, 0]); 730 assert(!__traits(compiles, h.xx = h.yy)); 731 vec4ub j; 732 733 // larger vectors 734 alias Vector!(float, 5) vec5f; 735 vec5f l = vec5f(1, 2.0f, 3.0, 4u, 5.0L); 736 l = vec5f(l.xyz, vec2i(1, 2)); 737 738 // the ctor should not compile if given too many arguments 739 static assert(!is(typeof(vec2f(1, 2, 3)))); 740 static assert(!is(typeof(vec2f(vec2f(1, 2), 3)))); 741 static assert( is(typeof(vec3f(vec2f(1, 2), 3)))); 742 static assert( is(typeof(vec3f(1, 2, 3)))); 743 744 assert(absByElem(vec3i(-1, 0, 2)) == vec3i(1, 0, 2)); 745 } 746 747 private: 748 749 /// SSE approximation of reciprocal square root. 750 @nogc T inverseSqrt(T)(T x) pure nothrow if (isFloatingPoint!T) 751 { 752 static if (is(T == float)) 753 { 754 __m128 V = _mm_set_ss(x); 755 V = _mm_rsqrt_ss(V); 756 return _mm_cvtss_f32(V); 757 } 758 else 759 { 760 return 1 / sqrt(x); 761 } 762 } 763 764 765 package 766 { 767 // This generates small loops for Vector, Matrix, and Box. 768 // Time has shown such sort of manually unrolled code works best on both DMD and LDC. 769 770 static string generateLoopCode(string formatString, int N)() pure nothrow 771 { 772 string result; 773 for (int i = 0; i < N; ++i) 774 { 775 string index = ctIntToString(i); 776 // replace all @ by indices 777 778 int after = 0; 779 int cur = 0; 780 for (; cur < formatString.length; ++cur) 781 { 782 char ch = formatString[cur]; 783 if (ch == '@') 784 { 785 if (cur > after) 786 result ~= formatString[after..cur]; 787 result ~= index; 788 after = cur+1; 789 } 790 } 791 if (cur > after) 792 result ~= formatString[after..cur]; 793 } 794 return result; 795 } 796 797 // Speed-up CTFE conversions, replacement for std.conv 798 // Doesn't do the negatives. 799 static string ctIntToString(int n) pure nothrow 800 { 801 static immutable string[16] table = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; 802 if (n < 10) 803 return table[n]; 804 else 805 { 806 char[10] r; 807 for (int k = 0; k < 10; ++k) 808 { 809 r[9-k] = cast(char)('0' + n % 10); 810 n /= 10; 811 if (n == 0) 812 return r[9-k..$].idup; 813 } 814 return r.idup; 815 } 816 } 817 } 818 819 unittest 820 { 821 assert(ctIntToString(132) == "132"); 822 assert(ctIntToString(2147483647) == "2147483647"); 823 }