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