1 /** 2 * Type and dimension generic Vector backed by a static array. 3 */ 4 module bettercmath.vector; 5 6 import std.algorithm : among, copy, max, min, sum; 7 import std.meta; 8 import std.range; 9 import std.traits; 10 11 import bettercmath.cmath; 12 import bettercmath.misc : FloatType; 13 /// `lerp` function for UFCS 14 public import bettercmath.misc : lerp; 15 16 @safe @nogc nothrow: 17 18 version (unittest) 19 { 20 private alias Vec1 = Vector!(float, 1); 21 private alias Vec2 = Vector!(float, 2); 22 private alias Vec2i = Vector!(int, 2); 23 private alias Vec2ub = Vector!(ubyte, 2); 24 private alias Vec3 = Vector!(float, 3); 25 private alias Vec4 = Vector!(float, 4); 26 private alias Vec4bool = Vector!(bool, 4); 27 private struct OtherVec2 28 { 29 float x; 30 float y; 31 } 32 private struct NotAVec2 33 { 34 float i; 35 string t; 36 } 37 } 38 39 private template VectorStruct(U) 40 if (is(U == struct)) 41 { 42 private alias FieldTypes = Fields!U; 43 alias ElementType = FieldTypes[0]; 44 private enum isElementType(T) = is(T == ElementType); 45 enum isVectorStruct = allSatisfy!(isElementType, FieldTypes); 46 enum fieldNames = FieldNameTuple!U; 47 enum length = fieldNames.length; 48 } 49 50 51 /** 52 * Generic Vector backed by a static array. 53 * 54 * Params: 55 * T = Element type 56 * N = Vector dimension, must be positive 57 */ 58 struct Vector(T, uint N) 59 if (N > 0) 60 { 61 /// Alias for Vector element type. 62 alias ElementType = T; 63 /// Vector dimension. 64 enum dimension = N; 65 /// Element array. 66 T[N] elements = 0; 67 alias elements this; 68 69 ref inout(T) _get(size_t i)() inout pure 70 in { assert(i < N, "Index out of bounds"); } 71 do 72 { 73 return elements[i]; 74 } 75 ref inout(T[to - from]) _slice(size_t from, size_t to)() inout pure 76 in { assert(from <= N - 1 && to <= N, "Index out of bounds"); } 77 do 78 { 79 return elements[from .. to]; 80 } 81 82 /// Get a reference to first element. 83 alias x = _get!(0); 84 /// Ditto 85 alias r = x; 86 /// Ditto 87 alias u = x; 88 /// Ditto 89 alias s = x; 90 /// Ditto 91 alias width = x; 92 93 static if (N >= 2) 94 { 95 /// Get a reference to second element. 96 alias y = _get!(1); 97 /// Ditto 98 alias g = y; 99 /// Ditto 100 alias v = y; 101 /// Ditto 102 alias t = y; 103 /// Ditto 104 alias height = y; 105 106 /// Get a reference to the first and second elements. 107 alias xy = _slice!(0, 2); 108 /// Ditto 109 alias rg = xy; 110 /// Ditto 111 alias uv = xy; 112 /// Ditto 113 alias st = xy; 114 } 115 static if (N >= 3) 116 { 117 /// Get a reference to third element. 118 alias z = _get!(2); 119 /// Ditto 120 alias b = z; 121 /// Ditto 122 alias p = z; 123 /// Ditto 124 alias depth = z; 125 126 /// Get a reference to the second and third elements. 127 alias yz = _slice!(1, 3); 128 /// Ditto 129 alias gb = yz; 130 /// Ditto 131 alias tp = yz; 132 133 /// Get a reference to the first, second and third elements. 134 alias xyz = _slice!(0, 3); 135 /// Ditto 136 alias rgb = xyz; 137 /// Ditto 138 alias stp = xyz; 139 } 140 static if (N >= 4) 141 { 142 /// Get a reference to fourth element. 143 alias w = _get!(3); 144 /// Ditto 145 alias a = w; 146 /// Ditto 147 alias q = w; 148 149 /// Get a reference to the third and fourth elements. 150 alias zw = _slice!(2, 4); 151 /// Ditto 152 alias ba = zw; 153 /// Ditto 154 alias pq = zw; 155 156 /// Get a reference to the second, third and fourth elements. 157 alias yzw = _slice!(1, 4); 158 /// Ditto 159 alias gba = yzw; 160 /// Ditto 161 alias tpq = yzw; 162 163 /// Get a reference to the first, second, third and fourth elements. 164 alias xyzw = _slice!(0, 4); 165 /// Ditto 166 alias rgba = xyzw; 167 /// Ditto 168 alias stpq = xyzw; 169 } 170 171 unittest 172 { 173 Vec2 v = [1, 2]; 174 assert(v.x == 1); 175 assert(v.x == v[0]); 176 assert(v.y == 2); 177 assert(v.y == v[1]); 178 v.x = 2; 179 assert(v.r == 2); 180 181 Vec4 v2 = [1, 2, 3, 4]; 182 assert(v2.xy == [1, 2]); 183 assert(v2.yz == [2, 3]); 184 assert(v2.zw == [3, 4]); 185 v2.xyz = 0; 186 assert(v2 == [0, 0, 0, 4]); 187 } 188 189 /// Constructs a Vector with all elements equal to `scalar` 190 this(const T scalar) pure 191 { 192 elements[] = scalar; 193 } 194 /// 195 unittest 196 { 197 alias Vec2 = Vector!(float, 2); 198 Vec2 v; 199 v = Vec2(1); 200 assert(v == [1, 1]); 201 v = Vec2(2.0); 202 assert(v == [2, 2]); 203 v = Vec2(3.0f); 204 assert(v == [3, 3]); 205 } 206 /// Constructs a Vector from static array. 207 this()(const auto ref T[N] values) pure 208 { 209 elements = values; 210 } 211 /// 212 unittest 213 { 214 alias Vec2 = Vector!(float, 2); 215 Vec2 v; 216 v = Vec2([1, 2]); 217 assert(v == [1f, 2f]); 218 v = Vec2([2.0, 3.0]); 219 assert(v == [2f, 3f]); 220 v = Vec2([3.0f, 4.0f]); 221 assert(v == [3f, 4f]); 222 } 223 /// Constructs a Vector with elements from an Input Range. 224 /// Values not provided by `range` are initialized to 0, 225 /// while additional values are ignored. 226 this(R)(auto ref R range) 227 if (isInputRange!R) 228 { 229 auto remainder = range.take(N).copy(elements[]); 230 remainder[] = 0; 231 } 232 /// 233 unittest 234 { 235 float[] values = [1, 2]; 236 assert(Vec4(values) == [1, 2, 0, 0]); 237 238 import std.range : iota, stride; 239 assert(Vec4(iota(4)) == [0, 1, 2, 3]); 240 assert(Vec4(iota(8).stride(2)) == [0, 2, 4, 6]); 241 assert(Vec4(iota(6)) == [0, 1, 2, 3]); 242 243 import std.algorithm : map; 244 auto isEven = iota(1024).map!(x => x % 2 == 0); 245 assert(Vec4bool(isEven) == [true, false, true, false]); 246 } 247 /// Constructs a Vector from a Vector-like struct. 248 this(U)(const auto ref U value) 249 if (is(U == struct) && VectorStruct!U.isVectorStruct) 250 { 251 alias vectorStruct = VectorStruct!U; 252 static foreach (i; 0 .. min(vectorStruct.length, N)) 253 { 254 elements[i] = cast(T) mixin("value." ~ vectorStruct.fieldNames[i]); 255 } 256 static if (vectorStruct.length < N) 257 { 258 elements[vectorStruct.length .. N] = 0; 259 } 260 } 261 /// 262 unittest 263 { 264 Vec2 v = OtherVec2(2, 3); 265 assert(v == Vec2(2, 3)); 266 Vec4 v4 = OtherVec2(4, 5); 267 assert(v4 == Vec4(4, 5, 0, 0)); 268 assert(!__traits(compiles, { Vec2 v = NotAVec2(); })); 269 } 270 /// Constructs a Vector with all elements initialized separately 271 this(Args...)(const auto ref Args args) 272 if (args.length > 1 && args.length <= N) 273 { 274 foreach (i, value; args) 275 { 276 elements[i] = cast(T) value; 277 } 278 static if (Args.length < N) 279 { 280 elements[Args.length .. N] = 0; 281 } 282 } 283 /// 284 unittest 285 { 286 Vec3 v; 287 v = Vec3(1, 2); 288 assert(v == [1, 2, 0]); 289 v = Vec3(1, 2, 3); 290 assert(v == [1, 2, 3]); 291 } 292 293 pure: 294 /// Vector with all zeros 295 enum Vector zeros = 0; 296 alias zeroes = zeros; 297 /// Vector with all ones 298 enum Vector ones = 1; 299 300 /// Returns a new vector with unary operator applied to all elements 301 Vector opUnary(string op)() const 302 if (op.among("-", "+", "~")) 303 { 304 Vector result; 305 mixin(q{result =} ~ op ~ q{elements[];}); 306 return result; 307 } 308 /// 309 unittest 310 { 311 assert(-Vec2(1, -2) == [-1, 2]); 312 } 313 314 /// Returns a new vector with binary operator applied to all elements and `scalar` 315 Vector opBinary(string op)(const T scalar) const 316 if (!op.among("~", "<<", ">>", ">>>")) 317 { 318 Vector result; 319 mixin(q{result = elements[]} ~ op ~ q{scalar;}); 320 return result; 321 } 322 /// Ditto 323 auto opBinary(string op, U)(const U scalar) const 324 if (!is(U : T) && !is(CommonType!(U, T) == void) && !op.among("~", "<<", ">>", ">>>")) 325 { 326 mixin(q{alias resultType = typeof(cast(T)(1) } ~ op ~ q{ cast(U)(1));}); 327 Vector!(CommonType!(T, U), N) result; 328 foreach (i; 0 .. N) 329 { 330 result[i] = mixin(q{elements[i] } ~ op ~ q{ scalar}); 331 } 332 return result; 333 } 334 /// 335 unittest 336 { 337 Vec2 a = [1, 2]; 338 assert(a + 1 == [1f + 1f, 2f + 1f]); 339 assert(a - 1 == [1f - 1f, 2f - 1f]); 340 assert(a * 2 == [1f * 2f, 2f * 2f]); 341 assert(a / 2 == [1f / 2f, 2f / 2f]); 342 assert(a % 2 == [1f % 2f, 2f % 2f]); 343 assert(a ^^ 2 == [1f ^^ 2f, 2f ^^ 2f]); 344 345 Vec2i b = [1, 2]; 346 assert((b & 1) == [1 & 1, 2 & 1]); 347 assert((b | 1) == [1 | 1, 2 | 1]); 348 assert((b ^ 1) == [1 ^ 1, 2 ^ 1]); 349 } 350 // TODO: shift operations 351 352 /// Returns a new vector with binary operator applied to all elements and `scalar` 353 Vector opBinaryRight(string op)(const T scalar) const 354 if (!op.among("~", "<<", ">>", ">>>")) 355 { 356 Vector result; 357 mixin(q{result = scalar} ~ op ~ q{elements[];}); 358 return result; 359 } 360 /// Ditto 361 auto opBinaryRight(string op, U)(const U scalar) const 362 if (!is(U : T) && !is(CommonType!(U, T) == void) && !op.among("~", "<<", ">>", ">>>")) 363 { 364 mixin(q{alias resultType = typeof(cast(U)(1) } ~ op ~ q{ cast(T)(1));}); 365 Vector!(CommonType!(T, U), N) result; 366 foreach (i; 0 .. N) 367 { 368 result[i] = mixin(q{scalar } ~ op ~ q{ elements[i]}); 369 } 370 return result; 371 } 372 /// 373 unittest 374 { 375 Vec2 a = [1, 2]; 376 assert(1 + a == [1f + 1f, 1f + 2f]); 377 assert(1 - a == [1f - 1f, 1f - 2f]); 378 assert(2 * a == [2f * 1f, 2f * 2f]); 379 assert(2 / a == [2f / 1f, 2f / 2f]); 380 assert(2 % a == [2f % 1f, 2f % 2f]); 381 assert(2 ^^ a == [2f ^^ 1f, 2f ^^ 2f]); 382 383 Vec2i b = [1, 2]; 384 assert((1 & b) == [1 & 1, 1 & 2]); 385 assert((1 | b) == [1 | 1, 1 | 2]); 386 assert((1 ^ b) == [1 ^ 1, 1 ^ 2]); 387 } 388 389 /// Returns a new vector with the results of applying operator against elements of `other`. 390 /// If operands dimensions are unequal, copies the values from greater dimension vector. 391 Vector!(T, max(N, M)) opBinary(string op, uint M)(const auto ref T[M] other) const 392 if (op != "~") 393 { 394 enum minDimension = min(N, M); 395 typeof(return) result; 396 mixin(q{result[0 .. minDimension] = elements[0 .. minDimension]} ~ op ~ q{other[0 .. minDimension];}); 397 static if (M < N) 398 { 399 result[minDimension .. N] = elements[minDimension .. N]; 400 } 401 else static if (N < M) 402 { 403 result[minDimension .. M] = other[minDimension .. M]; 404 } 405 return result; 406 } 407 /// Ditto 408 Vector!(CommonType!(U, T), max(N, M)) opBinary(string op, U, uint M)(const auto ref U[M] other) const 409 if (!is(CommonType!(U, T) == void) && op != "~") 410 { 411 enum minDimension = min(N, M); 412 typeof(return) result; 413 foreach (i; 0 .. minDimension) 414 { 415 mixin(q{result[i] = elements[i] } ~ op ~ q{ other[i];}); 416 } 417 static if (M < N) 418 { 419 result[minDimension .. N] = elements[minDimension .. N]; 420 } 421 else static if (N < M) 422 { 423 result[minDimension .. M] = other[minDimension .. M]; 424 } 425 return result; 426 } 427 /// 428 unittest 429 { 430 assert(Vec2(1, 2) + Vec2(3, 4) == [1f+3f, 2f+4f]); 431 assert(Vec2(1, 2) - Vec2(3, 4) == [1f-3f, 2f-4f]); 432 assert(Vec2(1, 2) * Vec2(3, 4) == [1f*3f, 2f*4f]); 433 assert(Vec2(1, 2) / Vec2(3, 4) == [1f/3f, 2f/4f]); 434 assert(__traits(compiles, Vec2(1, 2) + [3, 4])); 435 436 assert(Vec2(1, 2) + Vec1(3) == [1f+3f, 2f]); 437 assert(Vec2(1, 2) - Vec1(3) == [1f-3f, 2f]); 438 assert(Vec2(1, 2) * Vec1(3) == [1f*3f, 2f]); 439 assert(Vec2(1, 2) / Vec1(3) == [1f/3f, 2f]); 440 441 assert(Vec2(1, 2) + Vec3(3, 4, 5) == [1f+3f, 2f+4f, 5f]); 442 assert(Vec2(1, 2) - Vec3(3, 4, 5) == [1f-3f, 2f-4f, 5f]); 443 assert(Vec2(1, 2) * Vec3(3, 4, 5) == [1f*3f, 2f*4f, 5f]); 444 assert(Vec2(1, 2) / Vec3(3, 4, 5) == [1f/3f, 2f/4f, 5f]); 445 446 assert(Vec2i(1, 2) + Vec2(3, 4) == [1+3f, 2+4f]); 447 } 448 449 /// Returns a new vector with the results of applying operator against elements of `other`. 450 /// If operands dimensions are unequal, copies the values from greater dimension vector. 451 Vector!(CommonType!(U, T), max(N, M)) opBinaryRight(string op, U, uint M)(const auto ref U[M] other) const 452 if (!is(CommonType!(U, T) == void) && op != "~") 453 { 454 enum minDimension = min(N, M); 455 typeof(return) result; 456 foreach (i; 0 .. minDimension) 457 { 458 mixin(q{result[i] = other[i] } ~ op ~ q{ elements[i];}); 459 } 460 static if (M < N) 461 { 462 result[minDimension .. N] = elements[minDimension .. N]; 463 } 464 else static if (N < M) 465 { 466 result[minDimension .. M] = other[minDimension .. M]; 467 } 468 return result; 469 } 470 471 /// Returns a new vector of greater dimension by copying elements and appending `scalar`. 472 Vector!(T, N + 1) opBinary(string op : "~")(const T scalar) const 473 { 474 typeof(return) result; 475 result[0 .. N] = elements[]; 476 result[N] = scalar; 477 return result; 478 } 479 /// 480 unittest 481 { 482 Vec2 v = [1, 2]; 483 assert(v ~ 3 == Vec3(1, 2, 3)); 484 } 485 /// Returns a new vector of greater dimension by copying elements and prepending `scalar`. 486 Vector!(T, N + 1) opBinaryRight(string op : "~")(const T scalar) const 487 { 488 typeof(return) result; 489 result[0] = scalar; 490 result[1 .. N + 1] = elements[]; 491 return result; 492 } 493 /// 494 unittest 495 { 496 Vec2 v = [1, 2]; 497 Vec3 v2 = 0f ~ v; 498 assert(0 ~ v == Vec3(0, 1, 2)); 499 } 500 501 /// Returns a new vector of greater dimension by copying elements and appending values from `other`. 502 Vector!(T, N + M) opBinary(string op : "~", uint M)(const auto ref T[M] other) const 503 { 504 typeof(return) result = elements ~ other; 505 return result; 506 } 507 /// 508 unittest 509 { 510 Vec2 v1 = [1, 2]; 511 assert(v1 ~ [3f, 4f] == Vec4(1, 2, 3, 4)); 512 assert(v1 ~ Vec2(3f, 4f) == Vec4(1, 2, 3, 4)); 513 } 514 /// Returns a new vector of greater dimension by copying elements and prepending values from `other`. 515 Vector!(T, N + M) opBinaryRight(string op : "~", uint M)(const auto ref T[M] other) const 516 { 517 typeof(return) result = other ~ elements; 518 return result; 519 } 520 /// 521 unittest 522 { 523 Vec2 v1 = [1, 2]; 524 assert([3f, 4f] ~ v1 == Vec4(3, 4, 1, 2)); 525 assert(Vec2(3f, 4f) ~ v1 == Vec4(3, 4, 1, 2)); 526 } 527 528 /// Cast to a static array of same dimension, but different element type. 529 U opCast(U : T2[N], T2)() const 530 { 531 typeof(return) result; 532 foreach (i; 0 .. N) 533 { 534 result[i] = cast(T2) elements[i]; 535 } 536 return result; 537 } 538 /// 539 unittest 540 { 541 Vec2i intVec = [1, 2]; 542 auto floatVec = cast(Vec2) intVec; 543 assert(floatVec == Vec2(1f, 2f)); 544 assert(floatVec == intVec); 545 546 auto floatArray = cast(float[2]) intVec; 547 assert(floatArray == [1f, 2f]); 548 assert(floatArray == intVec); 549 } 550 551 /// Cast to a Vector-like struct. 552 U opCast(U)() const 553 if (is(U == struct) && VectorStruct!U.isVectorStruct) 554 { 555 alias vectorStruct = VectorStruct!U; 556 typeof(return) result; 557 static foreach (i; 0 .. min(vectorStruct.length, N)) 558 { 559 mixin("result." ~ vectorStruct.fieldNames[i]) = cast(vectorStruct.ElementType) elements[i]; 560 } 561 return result; 562 } 563 /// 564 unittest 565 { 566 immutable Vec2 five = 5; 567 OtherVec2 otherFive = cast(OtherVec2) five; 568 assert(otherFive == OtherVec2(5, 5)); 569 assert(!__traits(compiles, { auto v = cast(NotAVec2) five; })); 570 } 571 572 /// Assign result of applying operator with `scalar` to elements. 573 ref Vector opOpAssign(string op)(const T scalar) return 574 if (!op.among("~", "<<", ">>", ">>>")) 575 { 576 mixin(q{elements[] } ~ op ~ q{= scalar;}); 577 return this; 578 } 579 /// 580 unittest 581 { 582 Vec2i v = [1, 2]; 583 v += 5; 584 assert(v == [6, 7]); 585 v -= 4; 586 assert(v == [2, 3]); 587 v *= -1; 588 assert(v == [-2, -3]); 589 v %= 3; 590 assert(v == [-2, 0]); 591 v |= 1; 592 assert(v == [-1, 1]); 593 v /= 2; 594 assert(v == [0, 0]); // integer division 595 } 596 /// Assign result of applying operator with `other` to elements. 597 ref Vector opOpAssign(string op, uint M)(const auto ref T[M] other) return 598 if (!op.among("~", "<<", ">>", ">>>")) 599 { 600 enum minDimension = min(N, M); 601 mixin(q{elements[0 .. minDimension] } ~ op ~ q{= other[0 .. minDimension];}); 602 return this; 603 } 604 /// 605 unittest 606 { 607 Vec3 v = [1, 2, 3]; 608 v += Vec2(1, 2); 609 assert(v == [2, 4, 3]); 610 611 v += Vec4(1, 2, 3, 4); 612 assert(v == [3, 6, 6]); 613 } 614 615 unittest 616 { 617 assert(Vec2.sizeof == Vec2.elements.sizeof); 618 assert(Vec3.sizeof == Vec3.elements.sizeof); 619 assert(Vec4.sizeof == Vec4.elements.sizeof); 620 621 alias Vec2_100 = Vec2[100]; 622 assert(Vec2_100.sizeof == 100 * Vec2.elements.sizeof); 623 624 auto v = Vec2(5); 625 v += 3; 626 assert(v == Vec2(8)); 627 } 628 } 629 630 /// True if `T` is some kind of Vector 631 enum isVector(T) = is(T : Vector!U, U...); 632 633 /// Construct Vector directly from static array, inferring element type. 634 Vector!(T, N) vector(T, uint N)(const auto ref T[N] elements) 635 { 636 return typeof(return)(elements); 637 } 638 /// 639 unittest 640 { 641 auto v = [1, 2, 3].vector; 642 assert(v.elements == [1, 2, 3]); 643 assert(is(typeof(v) == Vector!(int, 3))); 644 } 645 646 /// Construct Vector directly from elements, inferring element type. 647 Vector!(CommonType!Args, Args.length) vector(Args...)(const auto ref Args args) 648 if (!is(CommonType!Args == void)) 649 { 650 return typeof(return)(args); 651 } 652 /// 653 unittest 654 { 655 auto v = vector(1f, 2, 3); 656 assert(is(typeof(v) == Vector!(float, 3))); 657 assert(v == [1f, 2f, 3f]); 658 } 659 660 /// Returns the dot product between two Vectors. 661 T dot(T, uint N)(const auto ref Vector!(T, N) a, const auto ref Vector!(T, N) b) pure 662 { 663 auto multiplied = a * b; 664 return multiplied[].sum; 665 } 666 667 /// Returns the cross product between two 3D Vectors. 668 Vector!(T, 3) cross(T)(const auto ref Vector!(T, 3) a, const auto ref Vector!(T, 3) b) pure 669 { 670 typeof(return) result = [ 671 a.y * b.z - a.z * b.y, 672 a.z * b.x - a.x * b.z, 673 a.x * b.y - a.y * b.x, 674 ]; 675 return result; 676 } 677 678 /// Returns a Vector that is the reflection of `vec` against `normal`. 679 Vector!(T, N) reflect(T, uint N)(const auto ref Vector!(T, N) vec, const auto ref Vector!(T, N) normal) pure 680 { 681 return vec - (2 * normal * dot(vec, normal)); 682 } 683 684 /// Returns the squared magnitude (Euclidean length) of a Vector. 685 T magnitudeSquared(T, uint N)(const auto ref Vector!(T, N) vec) pure 686 out (r) { assert(r >= 0, "Vector squared magnitude should be non-negative!"); } 687 do 688 { 689 return dot(vec, vec); 690 } 691 /// 692 unittest 693 { 694 assert(Vec2(0, 0).magnitudeSquared() == 0); 695 assert(Vec2(1, 0).magnitudeSquared() == 1); 696 assert(Vec2(0, 1).magnitudeSquared() == 1); 697 assert(Vec2(1, 1).magnitudeSquared() == 2); 698 assert(Vec2(2, 0).magnitudeSquared() == 4); 699 assert(Vec2(1, 2).magnitudeSquared() == 5); 700 } 701 702 /// Returns the magnitude (Euclidean length) of a Vector. 703 auto magnitude(T, uint N)(const auto ref Vector!(T, N) vec) 704 out (r) { assert(r >= 0, "Vector magnitude should be non-negative!"); } 705 do 706 { 707 return sqrt(vec.magnitudeSquared()); 708 } 709 /// 710 unittest 711 { 712 assert(Vec2(0, 0).magnitude() == 0); 713 assert(Vec2(1, 0).magnitude() == 1); 714 assert(Vec2(0, 1).magnitude() == 1); 715 assert(Vec2(1, 1).magnitude() == sqrt(2f)); 716 assert(Vec2(2, 0).magnitude() == 2); 717 } 718 719 /// Normalize a Vector inplace. 720 ref Vector!(T, N) normalize(T, uint N)(ref return Vector!(T, N) vec) 721 { 722 auto sqMag = vec.magnitudeSquared(); 723 if (sqMag != 0) 724 { 725 enum FloatType!T one = 1; 726 const auto inverseMag = one / sqrt(sqMag); 727 vec *= inverseMag; 728 } 729 return vec; 730 } 731 /// 732 unittest 733 { 734 Vec2 v = [5, 0]; 735 v.normalize(); 736 assert(v == Vec2(1, 0)); 737 } 738 739 /// Returns a normalized copy of Vector. 740 Vector!(T, N) normalized(T, uint N)(const auto ref Vector!(T, N) vec) 741 { 742 typeof(return) copy = vec; 743 return copy.normalize(); 744 } 745 /// 746 unittest 747 { 748 Vec2 v = [200, 0]; 749 assert(v.normalized() == Vec2(1, 0)); 750 assert(v == Vec2(200, 0)); 751 } 752 753 unittest 754 { 755 Vec2 a = [1, 1]; 756 Vec2 b = [2, 3]; 757 assert(lerp(a, b, 0) == a); 758 assert(lerp(a, b, 0.5) == Vec2(1.5, 2)); 759 assert(lerp(a, b, 1) == b); 760 761 Vec2ub c1 = [0, 0]; 762 Vec2ub c2 = [255, 255]; 763 assert(lerp(c1, c2, 0) == c1); 764 assert(lerp(c1, c2, 1) == c2); 765 assert(lerp(c1, c2, 0.5) == Vec2ub(127, 127)); 766 }