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