1 module polyplex.math.simplemath.vectors; 2 import polyplex.utils.strutils; 3 import std.math, std.traits, std.string; 4 import polyplex.math.simplemath; 5 static import Mathf = polyplex.math.mathf; 6 7 /** 8 A 2 dimensional vector. 9 */ 10 public struct Vector2T(T) if (isNumeric!(T)) { 11 private alias GVector = typeof(this); 12 public alias Type = T; 13 enum Dimensions = 2; 14 15 /** 16 Due to https://issues.dlang.org/show_bug.cgi?id=8006 this has to be flipped like this 17 18 TOOD: Replace with getter/setter structure once https://github.com/dlang/dmd/pull/7079 is merged. 19 **/ 20 private @safe nothrow @property T* data() { 21 return this.ptr; 22 } 23 24 private @property void data(T[Dimensions] data) { 25 X = data[0]; 26 Y = data[1]; 27 } 28 29 /// The X component 30 public T X = 0; 31 32 /// The Y component 33 public T Y = 0; 34 35 /// Constructor 36 this(T x) { 37 this.X = x; 38 this.Y = x; 39 } 40 41 /// Constructor 42 this(T x, T y) { 43 this.X = x; 44 this.Y = y; 45 } 46 47 this(Vector3T!T vec) { 48 this.X = vec.X; 49 this.Y = vec.Y; 50 } 51 52 this(Vector4T!T vec) { 53 this.X = vec.X; 54 this.Y = vec.Y; 55 } 56 57 /** 58 Pointer to the underlying array data. 59 */ 60 public T* ptr() { return &X; } 61 62 // Binary actions. 63 public @trusted nothrow GVector opBinary(string op, T2)(T2 other) if (IsVector!T2) { 64 // Operation on these, (due to being smallest size) can be done faster this way. 65 mixin(q{ 66 return GVector( 67 this.X {0} other.X, 68 this.Y {0} other.Y); 69 }.Format(op) 70 ); 71 } 72 73 // Binary actions numeric. 74 public @trusted nothrow GVector opBinary(string op, T2)(T2 other) if (isNumeric!(T2)) { 75 mixin(q{ 76 return GVector( 77 this.X {0} other, 78 this.Y {0} other); 79 }.Format(op) 80 ); 81 } 82 83 // Binary Action w/ assignment 84 public @trusted nothrow void opOpAssign(string op, T2)(T2 other) if (isNumeric!(T2) || IsVector!(T2)) { 85 mixin(q{this = this {0} other;}.Format(op)); 86 } 87 88 /** 89 Returns: 90 The difference between the 2 vectors. 91 **/ 92 public @trusted nothrow GVector Difference(T2)(T2 other) if (IsVector!T2) { 93 return other-this; 94 } 95 96 /** 97 Returns: 98 The distance between this and another vector. 99 **/ 100 public @trusted nothrow T Distance(T2)(T2 other) if (IsVector!T2) { 101 return (other-this).Length; 102 } 103 104 /** 105 Returns: 106 The length/magnitude of this vector. 107 **/ 108 public @trusted nothrow T Length() { 109 T len = (X*X)+(Y*Y); 110 return cast(T)Mathf.Sqrt(cast(float)len); 111 } 112 113 /** 114 Returns: 115 A normalized version of this vector. 116 **/ 117 public @trusted nothrow GVector Normalize() { 118 GVector o; 119 T len = Length(); 120 o.X = this.X/len; 121 o.Y = this.Y/len; 122 return o; 123 } 124 125 /// Dot product of a vector. 126 public @trusted nothrow T Dot(GVector other) { 127 return (this.X*other.X)+(this.Y*other.Y); 128 } 129 130 /** 131 Returns: 132 Initial (zero) state of this vector. 133 **/ 134 public static GVector Zero() { 135 return GVector(0, 0); 136 } 137 138 /** 139 Returns: 140 Initial (one) state of this vector. 141 **/ 142 public static GVector One() { 143 return GVector(1, 1); 144 } 145 146 /** 147 Returns: 148 Up unit vector 149 **/ 150 public static GVector Up() { 151 return GVector(0, 1); 152 } 153 154 /** 155 Returns: 156 Down unit vector 157 **/ 158 public static GVector Down() { 159 return GVector(0, -1); 160 } 161 162 /** 163 Returns: 164 Left unit vector 165 **/ 166 public static GVector Left() { 167 return GVector(0, -1); 168 } 169 170 /** 171 Returns: 172 Right unit vector 173 **/ 174 public static GVector Right() { 175 return GVector(0, 1); 176 } 177 178 /** 179 Returns: 180 String representation of the array. 181 **/ 182 public string ToString() { 183 string o = "<"; 184 static foreach(i; 0 .. Dimensions) { 185 switch(i) { 186 case (Dimensions-1): 187 o~= "{0}".Format(this.data[i]); 188 break; 189 default: 190 o ~= "{0}, ".Format(this.data[i]); 191 break; 192 } 193 } 194 return o~">"; 195 } 196 197 /// Backwards compatiblity with glmath. 198 alias toString = ToString; 199 } 200 201 public struct Vector3T(T) if (isNumeric!T) { 202 private alias GVector = typeof(this); 203 public alias Type = T; 204 enum Dimensions = 3; 205 206 /** 207 Due to https://issues.dlang.org/show_bug.cgi?id=8006 this has to be flipped like this 208 209 TOOD: Replace with getter/setter structure once https://github.com/dlang/dmd/pull/7079 is merged. 210 **/ 211 private @safe nothrow @property T* data() { 212 return this.ptr; 213 } 214 215 private @property void data(T[Dimensions] data) { 216 X = data[0]; 217 Y = data[1]; 218 Z = data[2]; 219 } 220 221 /// The X component 222 public T X = 0; 223 224 /// The Y component 225 public T Y = 0; 226 227 // The Z component 228 public T Z = 0; 229 230 /// Constructor 231 this(T x) { 232 this.X = x; 233 this.Y = x; 234 this.Z = 0; 235 } 236 237 /// Constructor 238 this(T x, T y) { 239 this.X = x; 240 this.Y = y; 241 this.Z = 0; 242 } 243 244 /// Constructor 245 this(T x, T y, T z) { 246 this.X = x; 247 this.Y = y; 248 this.Z = z; 249 } 250 251 252 this(Vector2T!T vec) { 253 this.X = vec.X; 254 this.Y = vec.Y; 255 this.Z = 0; 256 } 257 258 this(Vector4T!T vec) { 259 this.X = vec.X; 260 this.Y = vec.Y; 261 this.Z = vec.Z; 262 } 263 264 /** 265 Pointer to the underlying array data. 266 */ 267 public T* ptr() { return &X; } 268 269 /** 270 Generic opBinary operation for Vectors. 271 */ 272 public @trusted nothrow GVector opBinary(string op, T2)(T2 other) if (IsVector!T2) { 273 GVector o; 274 // Get the length of the smallest vector 275 276 static if (other.Dimensions < this.Dimensions) enum len = other.Dimensions; 277 else enum len = this.Dimensions; 278 279 // Do the operations. 280 static foreach(i; 0 .. len) { 281 mixin(q{o.data[i] = this.data[i] {0} other.data[i];}.Format(op)); 282 } 283 return o; 284 } 285 286 // Binary actions numeric. 287 public @trusted nothrow GVector opBinary(string op)(T other) if (isNumeric!(T)) { 288 mixin(q{ 289 return GVector( 290 this.X {0} other, 291 this.Y {0} other, 292 this.Z {0} other); 293 }.Format(op) 294 ); 295 } 296 297 // Binary Action w/ assignment 298 public @trusted nothrow void opOpAssign(string op)(T other) if (isNumeric!(T) || IsVector!(T)) { 299 mixin(q{this = this {0} other;}.Format(op)); 300 } 301 302 /** 303 Returns: 304 The difference between the 2 vectors. 305 **/ 306 public @trusted nothrow GVector Difference(T2)(T2 other) if (IsVector!T2) { 307 return other-this; 308 } 309 310 /** 311 Returns: 312 The distance between this and another vector. 313 **/ 314 public @trusted nothrow T Distance(T2)(T2 other) if (IsVector!T2) { 315 return (other-this).Length; 316 } 317 318 /** 319 Returns: 320 The length/magnitude of this vector. 321 **/ 322 public @trusted nothrow T Length() { 323 T len = (X*X)+(Y*Y)+(Z*Z); 324 return cast(T)Mathf.Sqrt(cast(float)len); 325 } 326 327 /** 328 Returns: 329 A normalized version of this vector. 330 **/ 331 public @trusted nothrow GVector Normalize() { 332 GVector o; 333 T len = Length(); 334 static foreach(i; 0 .. Dimensions) { 335 o.data[i] = this.data[i]/len; 336 } 337 return o; 338 } 339 340 /// Dot product of a vector. 341 public @trusted nothrow T Dot(GVector other) { 342 return (this.X*other.X)+(this.Y*other.Y)+(this.Z*other.Z); 343 } 344 345 /// Cross product of a vector. 346 public @trusted nothrow GVector Cross(GVector other) { 347 return GVector( 348 (this.Y*other.Z)-(this.Z*other.Y), 349 (this.Z*other.X)-(this.X*other.Z), 350 (this.X*other.Y)-(this.Y*other.X)); 351 } 352 353 /** 354 Returns: 355 Initial (zero) state of this vector. 356 **/ 357 public static Vector3T!(T) Zero() { 358 return Vector3T!(T)(0, 0, 0); 359 } 360 361 /** 362 Returns: 363 Initial (one) state of this vector. 364 **/ 365 public static GVector One() { 366 return GVector(1, 1, 1); 367 } 368 369 /** 370 Returns: 371 Up unit vector 372 **/ 373 public static GVector Up() { 374 return GVector(0, -1, 0); 375 } 376 377 /** 378 Returns: 379 Down unit vector 380 **/ 381 public static GVector Down() { 382 return GVector(0, 1, 0); 383 } 384 385 /** 386 Returns: 387 Left unit vector 388 **/ 389 public static GVector Left() { 390 return GVector(0, -1, 0); 391 } 392 393 /** 394 Returns: 395 Right unit vector 396 **/ 397 public static GVector Right() { 398 return GVector(0, 1, 0); 399 } 400 401 /** 402 Returns: 403 Left unit vector 404 **/ 405 public static GVector Forward() { 406 return GVector(0, 0, -1); 407 } 408 409 /** 410 Returns: 411 Right unit vector 412 **/ 413 public static GVector Back() { 414 return GVector(0, 0, 1); 415 } 416 417 /** 418 Returns: 419 String representation of the array. 420 **/ 421 public string ToString() { 422 string o = "<"; 423 static foreach(i; 0 .. Dimensions) { 424 switch(i) { 425 case (Dimensions-1): 426 o~= "{0}".Format(this.data[i]); 427 break; 428 default: 429 o ~= "{0}, ".Format(this.data[i]); 430 break; 431 } 432 } 433 return o~">"; 434 } 435 436 /// Backwards compatiblity with glmath. 437 alias toString = ToString; 438 } 439 440 public struct Vector4T(T) if (isNumeric!(T)) { 441 private alias GVector = typeof(this); 442 public alias Type = T; 443 enum Dimensions = 4; 444 445 /** 446 Due to https://issues.dlang.org/show_bug.cgi?id=8006 this has to be flipped like this 447 448 TOOD: Replace with getter/setter structure once https://github.com/dlang/dmd/pull/7079 is merged. 449 **/ 450 private @safe nothrow @property T* data() { 451 return this.ptr; 452 } 453 454 private @property void data(T[Dimensions] data) { 455 X = data[0]; 456 Y = data[1]; 457 Z = data[2]; 458 W = data[3]; 459 } 460 461 /// The X component 462 public T X = 0; 463 464 /// The Y component 465 public T Y = 0; 466 467 // The Z component 468 public T Z = 0; 469 470 // The W component 471 public T W = 0; 472 473 /// Constructor 474 this(T x) { 475 this.X = x; 476 this.Y = x; 477 this.Z = 0; 478 this.W = 0; 479 } 480 481 /// Constructor 482 this(T x, T y) { 483 this.X = x; 484 this.Y = y; 485 this.Z = 0; 486 this.W = 0; 487 } 488 489 /// Constructor 490 this(T x, T y, T z) { 491 this.X = x; 492 this.Y = y; 493 this.Z = z; 494 this.W = 0; 495 } 496 497 /// Constructor 498 this(T x, T y, T z, T w) { 499 this.X = x; 500 this.Y = y; 501 this.Z = z; 502 this.W = w; 503 } 504 505 this(Vector2T!T vec) { 506 this.X = vec.X; 507 this.Y = vec.Y; 508 this.Z = 0; 509 this.W = 0; 510 } 511 512 this(Vector3T!T vec) { 513 this.X = vec.X; 514 this.Y = vec.Y; 515 this.Z = vec.Z; 516 this.W = 0; 517 } 518 519 /** 520 Pointer to the underlying array data. 521 */ 522 public T* ptr() { return &X; } 523 524 /** 525 Generic opBinary operation for Vectors. 526 */ 527 public @trusted nothrow GVector opBinary(string op, T2)(T2 other) if (IsVector!(T2)) { 528 GVector o; 529 // Get the length of the smallest vector 530 531 static if (other.Dimensions < this.Dimensions) enum len = other.Dimensions; 532 else enum len = this.Dimensions; 533 534 // Do the operations. 535 static foreach(i; 0 .. len) { 536 mixin(q{o.data[i] = this.data[i] {0} other.data[i];}.Format(op)); 537 } 538 return o; 539 } 540 541 // Binary actions numeric. 542 public @trusted nothrow GVector opBinary(string op)(T other) if (isNumeric!(T)) { 543 mixin(q{ 544 return GVector( 545 this.X {1} other, 546 this.Y {1} other, 547 this.Z {1} other, 548 this.W {1} other); 549 }.Format(T.stringof, op) 550 ); 551 } 552 553 // Binary Action w/ assignment 554 public @trusted nothrow void opOpAssign(string op, T2)(T2 other) if (isNumeric!(T2) || IsVector!(T2)) { 555 mixin(q{this = this {0} other;}.Format(op)); 556 } 557 558 /** 559 Returns: 560 The difference between the 2 vectors. 561 **/ 562 public @trusted nothrow GVector Difference(T2)(T2 other) if (IsVector!T2){ 563 return other-this; 564 } 565 566 /** 567 Returns: 568 The distance between this and another vector. 569 **/ 570 public @trusted nothrow T Distance(T2)(T2 other) if (IsVector!T2) { 571 return (other-this).Length; 572 } 573 574 /** 575 Returns: 576 The length/magnitude of this vector. 577 **/ 578 public @trusted nothrow T Length() { 579 T len = (X*X)+(Y*Y)+(Z*Z)+(W*W); 580 return cast(T)Mathf.Sqrt(cast(float)len); 581 } 582 583 /** 584 Returns: 585 A normalized version of this vector. 586 **/ 587 public @trusted nothrow GVector Normalize() { 588 GVector o; 589 T len = Length(); 590 static foreach(i; 0 .. Dimensions) { 591 o.data[i] = this.data[i]/len; 592 } 593 return o; 594 } 595 596 /// Dot product of a vector. 597 public @trusted nothrow T Dot(GVector other) { 598 return (this.X*other.X)+(this.Y*other.Y)+(this.Z*other.Z)+(this.W*other.W); 599 } 600 601 /** 602 Returns: 603 Initial (zero) state of this vector. 604 **/ 605 public static GVector Zero() { 606 return GVector(0, 0, 0, 0); 607 } 608 609 /** 610 Returns: 611 Initial (one) state of this vector. 612 **/ 613 public static GVector One() { 614 return GVector(1, 1, 1, 1); 615 } 616 617 /** 618 Returns: 619 String representation of the array. 620 **/ 621 public string ToString() { 622 string o = "<"; 623 static foreach(i; 0 .. Dimensions) { 624 switch(i) { 625 case (Dimensions-1): 626 o~= "{0}".Format(this.data[i]); 627 break; 628 default: 629 o ~= "{0}, ".Format(this.data[i]); 630 break; 631 } 632 } 633 return o~">"; 634 } 635 636 /// Backwards compatiblity with glmath. 637 alias toString = ToString; 638 } 639 640 // Copied from the GLMath implementation, checks if T is a Vector2T 641 enum IsVector2T(T) = is(T : Vector2T!U, U...); 642 643 // Copied from the GLMath implementation, checks if T is a Vector3T 644 enum IsVector3T(T) = is(T : Vector3T!U, U...); 645 646 // Copied from the GLMath implementation, checks if T is a Vector4T 647 enum IsVector4T(T) = is(T : Vector4T!U, U...); 648 649 //Combination of all IsVector 650 enum IsVector(T) = (IsVector2T!T || IsVector3T!T || IsVector4T!T); 651 652 alias Vector2 = Vector2T!float; 653 alias Vector2i = Vector2T!int; 654 alias Vector3 = Vector3T!float; 655 alias Vector3i = Vector3T!int; 656 alias Vector4 = Vector4T!float; 657 alias Vector4i = Vector4T!int;