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