1 /** 2 * This module contains the `Vec` templated struct representing vectors and 3 * their operations. 4 */ 5 module dvec.vector; 6 7 import std.traits : isNumeric, isFloatingPoint; 8 9 /** 10 * Generic struct that represents a vector holding `size` elements of type `T`. 11 * A vector must contain at least 1 element, and has no upper-bound on the size 12 * beyond restrictions imposed by the system. 13 */ 14 struct Vec(T, size_t size) if (isNumeric!T && size > 0) { 15 /** 16 * A static contstant that can be used to get the size of the vector. 17 */ 18 public static const size_t SIZE = size; 19 20 /** 21 * The internal static array storing the elements of this vector. 22 */ 23 public T[size] data; 24 25 /** 26 * Constructs a vector from an array of elements. 27 * Params: 28 * elements = The elements to put in the vector. 29 */ 30 public this(T[size] elements) @nogc { 31 static foreach (i; 0 .. size) data[i] = elements[i]; 32 } 33 unittest { 34 import dvec.vector_types; 35 Vec3f v = Vec3f([1f, 2f, 3f]); 36 assert(v.data == [1f, 2f, 3f]); 37 } 38 39 /** 40 * Constructs a vector from a variadic list of elements. 41 * Params: 42 * elements = The elements to put in the vector. 43 */ 44 public this(T[] elements...) @nogc { 45 if (elements.length != size) assert(false, "Invalid number of elements provided to Vec constructor."); 46 static foreach (i; 0 .. size) data[i] = elements[i]; 47 } 48 unittest { 49 import dvec.vector_types; 50 Vec3i v = Vec3i(5, 4, 3); 51 assert(v.data == [5, 4, 3]); 52 } 53 54 /** 55 * Constructs a vector where all elements have the given value. 56 * Params: 57 * value = The value to assign to all elements in the vector. 58 */ 59 public this(T value) @nogc { 60 static foreach (i; 0 .. size) data[i] = value; 61 } 62 unittest { 63 import dvec.vector_types; 64 Vec2f v = Vec2f(1f); 65 assert(v.data == [1f, 1f]); 66 Vec!(float, 25) v2 = Vec!(float, 25)(3f); 67 foreach (value; v2.data) { 68 assert(value == 3f); 69 } 70 } 71 72 /** 73 * Constructs a vector as a copy of the other. 74 * Params: 75 * other = The vector to copy. 76 */ 77 public this(Vec!(T, size) other) @nogc { 78 this(other.data); 79 } 80 unittest { 81 import dvec.vector_types; 82 Vec2f vOriginal = Vec2f(5f, 16f); 83 Vec2f vCopy = Vec2f(vOriginal); 84 assert(vCopy.data == [5f, 16f]); 85 } 86 87 /** 88 * Constructs a vector containing all 0's. 89 * Returns: An vector containing all 0's. 90 */ 91 public static Vec!(T, size) empty() @nogc { 92 Vec!(T, size) v; 93 static foreach (i; 0 .. size) v[i] = 0; 94 return v; 95 } 96 unittest { 97 import dvec.vector_types; 98 Vec4f v = Vec4f.empty(); 99 assert(v.data == [0f, 0f, 0f, 0f]); 100 } 101 102 /** 103 * Computes the sum of a given array of vectors. 104 * Params: 105 * vectors = The list of vectors to compute the sum of. 106 * Returns: The sum of all vectors. 107 */ 108 public static Vec!(T, size) sum(Vec!(T, size)[] vectors) @nogc { 109 Vec!(T, size) v = Vec!(T, size)(0); 110 foreach (vector; vectors) v.add(vector); 111 return v; 112 } 113 unittest { 114 import dvec.vector_types; 115 Vec2i v1 = Vec2i(1, 1); 116 Vec2i v2 = Vec2i(2, 2); 117 Vec2i v3 = Vec2i(3, 3); 118 Vec2i v4 = Vec2i(-1, -4); 119 assert(Vec2i.sum([v1, v2]) == v3); 120 assert(Vec2i.sum([v1, v2, v3]) == Vec2i(6, 6)); 121 assert(Vec2i.sum([v3, v4]) == Vec2i(2, -1)); 122 } 123 124 /** 125 * Gets a copy of this vector. 126 * Returns: A copy of this vector. 127 */ 128 public Vec!(T, size) copy() const @nogc { 129 return Vec!(T, size)(this); 130 } 131 unittest { 132 import dvec.vector_types; 133 Vec3f v = Vec3f(0.5f, 1.0f, 0.75f); 134 Vec3f vCopy = v.copy(); 135 assert(v.data == vCopy.data); 136 v.data[0] = -0.5f; 137 assert(vCopy.data[0] == 0.5f); 138 } 139 140 /** 141 * Sets all elements of the vector to those in the specified array. 142 * Params: 143 * elements = The elements to set. 144 * Returns: A reference to this vector. 145 */ 146 public ref Vec!(T, size) set(T[size] elements) @nogc { 147 static foreach (i; 0 .. size) data[i] = elements[i]; 148 return this; 149 } 150 unittest { 151 import dvec.vector_types; 152 assert(Vec2i(1, 2).set([3, 4]).data == [3, 4]); 153 } 154 155 /** 156 * Sets all the elements of the vector to those in the specified variadic 157 * array. Note that if the given list of elements is shorter than the 158 * vector's size, only the first `elements.length` elements will be set, 159 * and if the list of elements is larger than the vector's size, any extra 160 * elements will be ignored. 161 * Returns: A reference to this vector. 162 */ 163 public ref Vec!(T, size) set(T[] elements...) @nogc { 164 import std.algorithm : min; 165 foreach (i; 0 .. min(size, elements.length)) data[i] = elements[i]; 166 return this; 167 } 168 unittest { 169 import dvec.vector_types; 170 assert(Vec2i(1, 2).set(3, 4).data == [3, 4]); 171 } 172 173 /** 174 * Adds the given vector to this one. 175 * Params: 176 * other = The vector to add to this one. 177 * Returns: A reference to this vector, for method chaining. 178 */ 179 public ref Vec!(T, size) add(V)(Vec!(V, size) other) @nogc if (isNumeric!V) { 180 static foreach (i; 0 .. size) data[i] += other[i]; 181 return this; 182 } 183 unittest { 184 import dvec.vector_types; 185 Vec3i v1 = Vec3i(1); 186 Vec3i v2 = Vec3i(2); 187 v1.add(v2); 188 assert(v1.data == [3, 3, 3]); 189 assert(v2.data == [2, 2, 2]); 190 } 191 192 /** 193 * Adds the given scalar value to this vector. 194 * Params: 195 * scalar = The scalar value to add to this vector. 196 * Returns: A reference to this vector. 197 */ 198 public ref Vec!(T, size) add(V)(V scalar) @nogc if (isNumeric!V) { 199 static foreach (i; 0 .. size) data[i] += scalar; 200 return this; 201 } 202 unittest { 203 import dvec.vector_types; 204 Vec3i v = Vec3i(-2, 4, 3); 205 v.add(2); 206 assert(v.data == [0, 6, 5]); 207 } 208 209 /** 210 * Subtracts the given vector from this one. 211 * Params: 212 * other = The vector to subtract from this one. 213 * Returns: A reference to this vector, for method chaining. 214 */ 215 public ref Vec!(T, size) sub(V)(Vec!(V, size) other) @nogc if (isNumeric!V) { 216 static foreach (i; 0 .. size) data[i] -= other[i]; 217 return this; 218 } 219 unittest { 220 import dvec.vector_types; 221 Vec3i v1 = Vec3i(1); 222 Vec3i v2 = Vec3i(2); 223 v1.sub(v2); 224 assert(v1.data == [-1, -1, -1]); 225 assert(v2.data == [2, 2, 2]); 226 } 227 228 /** 229 * Subtracts the given scalar value from this vector. 230 * Params: 231 * scalar = The scalar value to subtract from this vector. 232 * Returns: A reference to this vector. 233 */ 234 public ref Vec!(T, size) sub(V)(V scalar) @nogc if (isNumeric!V) { 235 static foreach (i; 0 .. size) data[i] -= scalar; 236 return this; 237 } 238 unittest { 239 import dvec.vector_types; 240 Vec3i v = Vec3i(0, 3, -1); 241 v.sub(-2); 242 assert(v.data == [2, 5, 1]); 243 } 244 245 /** 246 * Multiplies this vector by a factor, element-wise. 247 * Params: 248 * factor = The factor to multiply by. 249 * Returns: A reference to this vector, for method chaining. 250 */ 251 public ref Vec!(T, size) mul(V)(V factor) @nogc if (isNumeric!V) { 252 static foreach (i; 0 .. size) data[i] *= factor; 253 return this; 254 } 255 unittest { 256 import dvec.vector_types; 257 assert(Vec2i(2).mul(2).data == [4, 4]); 258 } 259 260 /** 261 * Multiplies this vector by another vector, element-wise. 262 * Params: 263 * other = The other vector to multiply with. 264 * Returns: A reference to this vector, for method chaining. 265 */ 266 public ref Vec!(T, size) mul(V)(Vec!(V, size) other) @nogc if (isNumeric!V) { 267 static foreach (i; 0 .. size) data[i] *= other[i]; 268 return this; 269 } 270 unittest { 271 import dvec.vector_types; 272 assert(Vec2i(1, 2).mul(Vec2i(3, 2)).data == [3, 4]); 273 } 274 275 /** 276 * Divides this vector by a factor, element-wise. 277 * Params: 278 * factor = The factor to divide by. 279 * Returns: A reference to this vector, for method chaining. 280 */ 281 public ref Vec!(T, size) div(V)(V factor) @nogc if (isNumeric!V) { 282 static foreach (i; 0 .. size) data[i] /= factor; 283 return this; 284 } 285 unittest { 286 import dvec.vector_types; 287 assert(Vec2i(8).div(2).data == [4, 4]); 288 } 289 290 /** 291 * Divides this vector by another vector, element-wise. 292 * Params: 293 * other = The other vector to divide with. 294 * Returns: A reference to this vector, for method chaining. 295 */ 296 public ref Vec!(T, size) div(V)(Vec!(V, size) other) @nogc if (isNumeric!V) { 297 static foreach (i; 0 .. size) data[i] /= other[i]; 298 return this; 299 } 300 unittest { 301 import dvec.vector_types; 302 assert(Vec2i(8).div(Vec2i(2, 4)).data == [4, 2]); 303 } 304 305 /** 306 * Determines the squared magnitude of this vector. 307 * Returns: The squared magnitude of this vector. 308 */ 309 public double mag2() const @nogc { 310 double sum = 0; 311 static foreach (i; 0 .. size) sum += data[i] * data[i]; 312 return sum; 313 } 314 unittest { 315 import dvec.vector_types; 316 assert(Vec2i(2).mag2() == 8); 317 assert(Vec2f(3f, 4f).mag2() == 5f * 5f); 318 } 319 320 /** 321 * Determines the magnitude of this vector. 322 * Returns: The magnitude of this vector. 323 */ 324 public double mag() const @nogc { 325 import std.math : sqrt; 326 return sqrt(mag2()); 327 } 328 unittest { 329 import dvec.vector_types; 330 import std.math : sqrt; 331 assert(Vec2i(1, 0).mag() == 1); 332 assert(Vec2f(3f, 4f).mag() == 5f); 333 assert(Vec2i(1, 1).mag() == sqrt(2.0)); 334 } 335 336 /** 337 * Determines the [dot product](https://en.wikipedia.org/wiki/Dot_product) 338 * of this vector and another vector. 339 * Params: 340 * other = The other vector. 341 * Returns: The dot product of the vectors. 342 */ 343 public T dot(Vec!(T, size) other) const @nogc { 344 T sum = 0; 345 static foreach (i; 0 .. size) sum += data[i] * other[i]; 346 return sum; 347 } 348 unittest { 349 import dvec.vector_types; 350 assert(Vec3i(1, 3, -5).dot(Vec3i(4, -2, -1)) == 3); 351 assert(Vec2f(1.0f, 0.0f).dot(Vec2f(0.0f, 1.0f)) == 0f); 352 assert(Vec2f(1.0f, 0.0f).dot(Vec2f(1.0f, 0.0f)) == 1f); 353 assert(Vec2f(1.0f, 0.0f).dot(Vec2f(-1.0f, 0.0f)) == -1f); 354 } 355 356 /** 357 * Implements the basic unary operators on a vector. This supports: 358 * - Negation `-v`: Negates all a vector's components. 359 * - Incrementation `++v`: Increments all a vector's components by 1. 360 * - Decrementation `--v`: Decrements all a vector's components by 1. 361 * Returns: A new vector with the operation applied. 362 */ 363 public Vec!(T, size) opUnary(string op)() const @nogc { 364 Vec!(T, size) result = copy(); 365 static if (op == "-") { 366 result.mul(-1); 367 } else static if (op == "+") { 368 // Skip 369 } else static if (op == "++") { 370 result.add(1); 371 } else static if (op == "--") { 372 result.sub(1); 373 } else static assert(false, "Operator " ~ op ~ " is not implemented."); 374 return result; 375 } 376 unittest { 377 import dvec.vector_types; 378 assert(-Vec2i(3) == Vec2i(-3)); 379 assert(+Vec2i(2) == Vec2i(2)); 380 assert(++Vec3f(0.5f) == Vec3f(1.5f)); 381 assert(--Vec3f(0.5f) == Vec3f(-0.5f)); 382 } 383 384 /** 385 * Implements the basic binary operators between two vectors. It supports 386 * element-wise addition, subtraction, multiplication, and division. 387 * Params: 388 * other = The other vector operand. 389 * Returns: A new vector that is the result of applying this vector and 390 * the other to the given operation. 391 */ 392 public Vec!(T, size) opBinary(string op, V)(Vec!(V, size) other) const @nogc if (isNumeric!V) { 393 Vec!(T, size) result = copy(); 394 static if (op == "+") { 395 result.add(other); 396 } else static if (op == "-") { 397 result.sub(other); 398 } else static if (op == "*") { 399 result.mul(other); 400 } else static if (op == "/") { 401 result.div(other); 402 } else static assert(false, "Operator " ~ op ~ " is not implemented."); 403 return result; 404 } 405 unittest { 406 import dvec.vector_types; 407 assert(Vec2i(2) + Vec2i(1) == Vec2i(3)); 408 assert(Vec2d(0.5, 0.25) + Vec2i(-1, 2) == Vec2d(-0.5, 2.25)); 409 assert(Vec2i(1, 2) * Vec2i(3, 4) == Vec2i(3, 8)); 410 assert(Vec2d(0.5, 0.25) / Vec2d(0.25, 0.1) == Vec2d(2.0, 2.5)); 411 } 412 413 /** 414 * Implements the basic binary operators between a vector and a scalar 415 * value. It supports element-wise addition, subtraction multiplication, 416 * and division. 417 * Params: 418 * scalar = The scalar value to apply to each element of the vector. 419 * Returns: A new vector that is the result of applying the scalar value 420 * to this vector's elements, using the given operation. 421 */ 422 public Vec!(T, size) opBinary(string op, V)(V scalar) const @nogc if (isNumeric!V) { 423 Vec!(T, size) result = copy(); 424 static if (op == "+") { 425 result.add(scalar); 426 } else static if (op == "-") { 427 result.sub(scalar); 428 } else static if (op == "*") { 429 result.mul(scalar); 430 } else static if (op == "/") { 431 result.div(scalar); 432 } else static assert(false, "Operator " ~ op ~ " not implemented."); 433 return result; 434 } 435 unittest { 436 // TODO 437 } 438 439 /** 440 * Right-hand binary operator implementation. See `opBinary` above for more info. 441 * Params: 442 * scalar = The scalar value to apply to each element of the vector. 443 * Returns: A new vector that is the result of applying the scalar value 444 * to this vector's elements, using the given operation. 445 */ 446 public Vec!(T, size) opBinaryRight(string op, V)(V scalar) const @nogc if (isNumeric!V) { 447 return opBinary!(op, V)(scalar); 448 } 449 unittest { 450 import dvec.vector_types; 451 assert(3 + Vec2i(1) == Vec2i(4)); 452 // TODO: add more tests. 453 } 454 455 /** 456 * Gets the element at a specified index. 457 * Params: 458 * i = The index of the element. 459 * Returns: The element at the specified index. 460 */ 461 public T opIndex(size_t i) const @nogc { 462 return data[i]; 463 } 464 unittest { 465 import dvec.vector_types; 466 Vec3f v = Vec3f(1f, 2f, 3f); 467 assert(v[0] == 1f); 468 assert(v[1] == 2f); 469 assert(v[2] == 3f); 470 } 471 472 /** 473 * Inserts an element at the specified index. 474 * Params: 475 * value = The value to assign. 476 * i = The index of the element. 477 */ 478 public void opIndexAssign(T value, size_t i) @nogc { 479 data[i] = value; 480 } 481 unittest { 482 import dvec.vector_types; 483 Vec3i v; 484 assert(v[0] == 0); 485 v[0] = 42; 486 assert(v[0] == 42); 487 } 488 489 /** 490 * Implements op-assignments for indexed values of the vector, so you can 491 * do things like `v[2] *= 10;`. Supports addition, subtraction, 492 * multiplication, and division. 493 * Params: 494 * value = The value to apply. 495 * i = The index in the vector's array. 496 */ 497 public void opIndexOpAssign(string op, V)(V value, size_t i) @nogc if (isNumeric!V) { 498 static if (op == "+") { 499 data[i] += value; 500 } else static if (op == "-") { 501 data[i] -= value; 502 } else static if (op == "*") { 503 data[i] *= value; 504 } else static if (op == "/") { 505 data[i] /= value; 506 } else static assert(false, "Operator " ~ op ~ " not implemented."); 507 } 508 unittest { 509 import dvec.vector_types; 510 Vec3i v = Vec3i(1); 511 v[0] += 2; 512 assert(v[0] == 3); 513 v[1] -= 3; 514 assert(v[1] == -2); 515 v[0] *= 2; 516 assert(v[0] == 6); 517 } 518 519 /** 520 * Named accessor for common vector fields, which allows you to get the 521 * value of specific elements in the vector according to conventional 522 * names. 523 * - `x` for the first element. 524 * - `y` for the second element. 525 * - `z` for the third element. 526 * - `w` for the fourth element. 527 * Returns: The value of the specified element. 528 */ 529 public T opDispatch(string s)() const @nogc { 530 static if (s == "x" && size >= 1) { 531 return data[0]; 532 } else static if (s == "y" && size >= 2) { 533 return data[1]; 534 } else static if (s == "z" && size >= 3) { 535 return data[2]; 536 } else static if (s == "w" && size >= 4) { 537 return data[3]; 538 } else { 539 static assert(false, "Invalid vector named accessor: " ~ s); 540 } 541 } 542 unittest { 543 import dvec.vector_types; 544 Vec4i v = Vec4i(1, 2, 3, 4); 545 assert(v.x == 1); 546 assert(v.y == 2); 547 assert(v.z == 3); 548 assert(v.w == 4); 549 } 550 551 /** 552 * Named setter for common vector fields, which allows you to set the value 553 * of specific elements in the vector according to conventional names. 554 * - `x` for the first element. 555 * - `y` for the second element. 556 * - `z` for the third element. 557 * - `w` for the fourth element. 558 * Params: 559 * value = The value to set. It can be any numeric value, but it'll be 560 * cast to the vector's type. 561 */ 562 public void opDispatch(string s, V)(V value) @nogc if (isNumeric!V) { 563 static if (s == "x" && size >= 1) { 564 data[0] = cast(T) value; 565 } else static if (s == "y" && size >= 2) { 566 data[1] = cast(T) value; 567 } else static if (s == "z" && size >= 3) { 568 data[2] = cast(T) value; 569 } else static if (s == "w" && size >= 4) { 570 data[3] = cast(T) value; 571 } else { 572 static assert(false, "Invalid vector named setter: " ~ s); 573 } 574 // TODO: Find some way to make `v.x += 1` work. 575 } 576 unittest { 577 import dvec.vector_types; 578 Vec4i v; 579 v.x = 1; 580 v.y = 2; 581 v.z = 3; 582 v.w = 4; 583 assert(v.data == [1, 2, 3, 4]); 584 } 585 586 /** 587 * Implements explicit casting between vector types. You may cast to a 588 * type with a different `T` element type, or a different size, or both. 589 * Casting to a different element type will call a normal `cast(newType) oldValue` 590 * for each value. When casting to a smaller vector size, extra elements 591 * will be truncated, and when casting to a larger size, missing elements 592 * are initialized to zero. 593 * 594 * Keep in mind that casting floating-point vectors to integer vectors is 595 * possible, and that casting in general can lead to a loss of data. 596 * 597 * Returns: The resulting vector of the new type. 598 */ 599 public NewVecType opCast(NewVecType : Vec!(newType, newSize), newType, size_t newSize)() const @nogc { 600 NewVecType v; 601 static foreach (i; 0 .. newSize) { 602 static if (i < size) { 603 v.data[i] = cast(newType) this.data[i]; 604 } else { 605 v.data[i] = 0; 606 } 607 } 608 return v; 609 } 610 unittest { 611 import dvec.vector_types; 612 Vec2f v1 = Vec2f(0.5f, 3.1f); 613 Vec3i v2 = cast(Vec3i) v1; 614 assert(v2.data == [0, 3, 0]); 615 auto v3 = cast(Vec!(byte, 1)) v1; 616 assert(v3.data == [0]); 617 } 618 619 /** 620 * Compares this vector to another, based on their magnitudes. 621 * Params: 622 * other = The vector to compare to. 623 * Returns: 0 if the vectors have equal magnitude, 1 if this vector's 624 * magnitude is bigger, and -1 if the other's is bigger. 625 */ 626 public int opCmp(Vec!(T, size) other) const @nogc { 627 const double a = this.mag2(); 628 const double b = other.mag2(); 629 if (a == b) return 0; 630 if (a < b) return -1; 631 return 1; 632 } 633 unittest { 634 import dvec.vector_types; 635 Vec3i v1 = Vec3i(1); 636 Vec3i v2 = Vec3i(2); 637 Vec3i v3 = Vec3i(3); 638 assert(v1 < v2); 639 assert(v2 > v1); 640 assert(v3 > v2); 641 } 642 643 /** 644 * Determines if two vectors are equal. Vectors are considered equal when 645 * all of their components are equal. 646 * Params: 647 * other = The vector to check equality against. 648 * Returns: True if the vectors are equal, or false otherwise. 649 */ 650 public bool opEquals(Vec!(T, size) other) const @nogc { 651 return this.data == other.data; 652 } 653 unittest { 654 import dvec.vector_types; 655 Vec3f v1 = Vec3f(0.5f, 1.0f, 1.5f); 656 assert(v1 == v1); 657 Vec3f v2 = Vec3f(1f, 2f, 3f); 658 assert(v2 != v1); 659 assert(v1 == v2 / 2f); 660 } 661 662 /** 663 * Gets a simple string representation of this vector. 664 * Returns: The string representation of this vector. 665 */ 666 public string toString() const { 667 import std.array : appender; 668 import std.conv : to; 669 auto s = appender!string; 670 s ~= "["; 671 static foreach (i; 0 .. size - 1) { 672 s ~= data[i].to!string; 673 s ~= ", "; 674 } 675 s ~= data[size - 1].to!string; 676 s ~= "]"; 677 return s[]; 678 } 679 680 static if (isFloatingPoint!T) { 681 /** 682 * [Normalizes](https://en.wikipedia.org/wiki/Unit_vector) this vector, 683 * such that it will have a magnitude of 1. 684 * Returns: A reference to this vector, for method chaining. 685 */ 686 public ref Vec!(T, size) norm() @nogc { 687 const double mag = mag(); 688 static foreach (i; 0 .. size) { 689 data[i] /= mag; 690 } 691 return this; 692 } 693 } 694 695 static if (isFloatingPoint!T && size == 2) { 696 /** 697 * Converts this 2-dimensional vector from [Cartesian](https://en.wikipedia.org/wiki/Cartesian_coordinate_system) 698 * to [Polar](https://en.wikipedia.org/wiki/Polar_coordinate_system) 699 * coordinates. It is assumed that the first element is the **x** 700 * coordinate, and the second element is the **y** coordinate. The 701 * first element becomes the **radius** and the second becomes the 702 * angle **theta**. 703 * - The angle is normalized to be within the range [0, 2*PI). 704 * Returns: A reference to this vector. 705 */ 706 public ref Vec!(T, size) toPolar() @nogc { 707 import std.math : atan2, PI; 708 T radius = mag(); 709 T angle = atan2(data[1], data[0]); 710 if (angle < 0) { 711 angle += 2 * PI; 712 } else if (angle >= 2 * PI) { 713 angle -= 2 * PI; 714 } 715 data[0] = radius; 716 data[1] = angle; 717 return this; 718 } 719 unittest { 720 import dvec.vector_types; 721 import std.math; 722 assert(Vec2f(1f, 0f).toPolar() == Vec2f(1f, 0f)); 723 assert(Vec2f(0f, 1f).toPolar() == Vec2f(1f, PI_2)); 724 assert(Vec2f(-1f, 0f).toPolar() == Vec2f(1f, PI)); 725 assert(Vec2f(0f, -1f).toPolar() == Vec2f(1f, 3f * PI / 2f)); 726 assert(Vec2f(1f, 1f).toPolar() == Vec2f(SQRT2, PI_4)); 727 assert(Vec2f(-50f, -50f).toPolar() == Vec2f(10f * sqrt(50f), 5 * PI_4)); 728 } 729 730 /** 731 * Converts this 2-dimensional vector from [Polar](https://en.wikipedia.org/wiki/Polar_coordinate_system) 732 * to [Cartesian](https://en.wikipedia.org/wiki/Cartesian_coordinate_system) 733 * coordinates. It is assumed that the first element is the **radius** 734 * and the second element is the angle **theta**. The first element 735 * becomes the **x** coordinate, and the second becomes the **y** 736 * coordinate. 737 * Returns: A reference to this vector. 738 */ 739 public ref Vec!(T, size) toCartesian() @nogc { 740 import std.math : cos, sin; 741 T x = data[0] * cos(data[1]); 742 T y = data[0] * sin(data[1]); 743 data[0] = x; 744 data[1] = y; 745 return this; 746 } 747 } 748 749 static if (isFloatingPoint!T && size == 3) { 750 /** 751 * Computes the [cross product](https://en.wikipedia.org/wiki/Cross_product) of this vector and another, and stores 752 * the result in this vector. 753 * Params: 754 * other = The other vector. 755 * Returns: A reference to this vector, for method chaining. 756 */ 757 public ref Vec!(T, size) cross(Vec!(T, size) other) @nogc { 758 Vec!(T, size) tmp; 759 tmp[0] = data[1] * other[2] - data[2] * other[1]; 760 tmp[1] = data[2] * other[0] - data[0] * other[2]; 761 tmp[2] = data[0] * other[1] - data[1] * other[0]; 762 data[0] = tmp[0]; 763 data[1] = tmp[1]; 764 data[2] = tmp[2]; 765 return this; 766 } 767 } 768 } 769 770 unittest { 771 import std.stdio; 772 import std.math; 773 import dvec.vector_types; 774 775 void assertFP(double actual, double expected, double delta = 1e-06, string msg = "Assertion failed.") { 776 double lowBound = expected - delta; 777 double highBound = expected + delta; 778 assert(actual > lowBound && actual < highBound, msg); 779 } 780 781 // Test floating-point specific methods. 782 auto v6 = Vec2f(3, 3); 783 v6.norm(); 784 assertFP(v6.mag, 1); 785 assertFP(v6[0], sqrt(2f) / 2f); 786 v6 = Vec2f(1, 0); 787 auto v6Copy = Vec2f(v6); 788 v6.norm(); 789 assert(v6.mag == 1); 790 assert(v6.data == v6Copy.data); 791 792 // Test toPolar 793 auto vCart = Vec2d(1, 0); 794 vCart.toPolar(); 795 assert(vCart.data == [1.0, 0.0]); 796 vCart.toCartesian(); 797 assert(vCart.data == [1.0, 0.0]); 798 vCart = Vec2d(1, 1); 799 vCart.toPolar(); 800 assertFP(vCart[0], sqrt(2f)); 801 assertFP(vCart[1], PI_4); 802 vCart.toCartesian(); 803 assertFP(vCart[0], 1); 804 assertFP(vCart[1], 1); 805 vCart = Vec2d(-1, 1); 806 vCart.toPolar(); 807 assertFP(vCart[0], sqrt(2f)); 808 assertFP(vCart[1], 3 * PI_4); 809 }