├── .github └── workflows │ └── d.yml ├── .gitignore ├── LICENSE ├── README.md ├── dub.json ├── dub.selections.json └── source └── dath ├── core.d ├── matrix.d ├── package.d ├── rect.d └── vector.d /.github/workflows/d.yml: -------------------------------------------------------------------------------- 1 | name: D 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: dlang-community/setup-dlang@4c99aa991ce7d19dd3064de0a4f2f6b2f152e2d7 17 | 18 | - name: 'Test' 19 | run: | 20 | dub test --compiler=$DC 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /dath 6 | dath.so 7 | dath.dylib 8 | dath.dll 9 | dath.a 10 | dath.lib 11 | dath-test-* 12 | libdath.a 13 | *.exe 14 | *.o 15 | *.obj 16 | *.lst 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 CosmoMyst 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dath 2 | 3 | [![DUB](https://img.shields.io/dub/v/dath)](https://code.dlang.org/packages/dath) 4 | 5 | A simple math library intended for games, written in D. 6 | 7 | The reason for making this library is for personal projects, learning, and it simply being fun to make. As such, this library is a work in progress, the API might change, and the implementation of math functions is definitely a naive one, and performance isn't a huge factor as it's intended for small 2D games, though I will still try and improve performance when I can. 8 | 9 | Inspiration and ideas from: [gfm](https://code.dlang.org/packages/gfm) and [gl3n](https://github.com/Dav1dde/gl3n). 10 | 11 | All functions are `nogc`. 12 | 13 | ## Usage 14 | 15 | Add to your project: `dub add dath`. 16 | 17 | Import: `import dath;`. 18 | 19 | ## Documentation 20 | 21 | As the library is pretty small, all the documentation will be provided in this README file. You can also look in the unittests of each module for usage. 22 | 23 | ### `dath.core` 24 | 25 | Some basic math functions. 26 | 27 | --- 28 | 29 | ```d 30 | const double PI = 3.14159265358979323846; 31 | ``` 32 | 33 | --- 34 | 35 | Convert degrees to radians. 36 | 37 | ```d 38 | float rad(float deg) @nogc pure nothrow; 39 | ``` 40 | 41 | --- 42 | 43 | Convert radians to degrees. 44 | 45 | ```d 46 | float deg(float rad) @nogc pure nothrow; 47 | ``` 48 | 49 | ### `dath.rect` 50 | 51 | ```d 52 | alias Rectf = Rect!float; 53 | alias Rectd = Rect!double; 54 | alias Recti = Rect!int; 55 | alias Rectu = Rect!uint; 56 | ``` 57 | 58 | --- 59 | 60 | Rectangle defined by position and size. Origin at upper left. 61 | 62 | ```d 63 | struct Rect(T) if (isNumeric!T); 64 | ``` 65 | 66 | Fields: 67 | 68 | `T x` - X position 69 | 70 | `T y` - Y position 71 | 72 | `T w` - Width 73 | 74 | `T h` - Height 75 | 76 | --- 77 | 78 | Checks if two rectangles intersect. 79 | 80 | ```d 81 | bool intersects(T)(const Rect!T a, const Rect!T b) @nogc pure nothrow; 82 | ``` 83 | 84 | ### `core.vector` 85 | 86 | ```d 87 | alias Vec2f = Vec!(float, 2); 88 | alias Vec3f = Vec!(float, 3); 89 | alias Vec4f = Vec!(float, 4); 90 | 91 | alias Vec2 = Vec2f; 92 | alias Vec3 = Vec3f; 93 | alias Vec4 = Vec4f; 94 | 95 | alias Vec2d = Vec!(double, 2); 96 | alias Vec3d = Vec!(double, 3); 97 | alias Vec4d = Vec!(double, 4); 98 | 99 | alias Vec2i = Vec!(int, 2); 100 | alias Vec3i = Vec!(int, 3); 101 | alias Vec4i = Vec!(int, 4); 102 | 103 | alias Vec2u = Vec!(uint, 2); 104 | alias Vec3u = Vec!(uint, 3); 105 | alias Vec4u = Vec!(uint, 4); 106 | ``` 107 | 108 | --- 109 | 110 | Numeric vector type with an optional amount of components. T can be any numeric type. 111 | 112 | ```d 113 | struct Vec(T, ulong n) if (n >= 1 && isNumeric!T); 114 | ``` 115 | 116 | Constructors: 117 | 118 | Accepts an array of components. 119 | 120 | ```d 121 | this(U)(U[] elements) @nogc pure nothrow; 122 | 123 | const a = Vec2([1f, 2f]); 124 | ``` 125 | 126 | Accepts a variadict list of components, or a single component (all components will have the same value). 127 | 128 | ```d 129 | this(U...)(U args) @nogc pure nothrow; 130 | 131 | const a = Vec2(1f, 2f); 132 | const b = Vec2(1f); 133 | ``` 134 | 135 | Functions: 136 | 137 | Vector magnitude 138 | 139 | ```d 140 | real magnitude() @nogc pure nothrow const; 141 | ``` 142 | 143 | Normalizes the vector. Changes the current struct. 144 | 145 | ```d 146 | void normalize() @nogc pure nothrow; 147 | ``` 148 | 149 | Returns the normalized vector. Doesn't change the current struct. 150 | 151 | ```d 152 | Vec!(T, n) normalized() @nogc pure nothrow const; 153 | ``` 154 | 155 | Returns the negated vector. 156 | 157 | ```d 158 | Vec!(T, n) opUnary(string s)() @nogc pure nothrow const if (s == "-"); 159 | 160 | const a = Vec2(1f); 161 | const b = -a; 162 | ``` 163 | 164 | Returns the multiplication of this vector and a scalar. 165 | 166 | ```d 167 | Vec!(T, n) opBinary(string s) (const float scalar) @nogc pure nothrow const if (s == "*"); 168 | 169 | const a = Vec2(1f, 2f); 170 | auto b = a * 2f; 171 | b *= 3f; 172 | ``` 173 | 174 | Returns the division of this vector and a scalar. 175 | 176 | ```d 177 | Vec!(T, n) opBinary(string s) (in float scalar) @nogc pure nothrow const if (s == "/"); 178 | 179 | const a = Vec2(1f, 2f); 180 | auto b = a / 2f; 181 | b /= 3f; 182 | ``` 183 | 184 | Returns the sum of this vector and a scalar. 185 | 186 | ```d 187 | Vec!(T, n) opBinary(string s) (const float scalar) @nogc pure nothrow const if (s == "+"); 188 | 189 | const a = Vec2(1f, 2f); 190 | auto b = a + 2f; 191 | b += 3f; 192 | ``` 193 | 194 | Returns the subtraction of this vector and a scalar. 195 | 196 | ```d 197 | Vec!(T, n) opBinary(string s) (const float scalar) @nogc pure nothrow const if (s == "-"); 198 | 199 | const a = Vec2(1f, 2f); 200 | auto b = a - 2f; 201 | b -= 3f; 202 | ``` 203 | 204 | Returns the multiplication of 2 vectors. Element wise product `(a, b) * (c, d) = (a*c, b*d)`. 205 | 206 | ```d 207 | Vec!(T, n) opBinary(string s) (const Vec!(T, n) other) @nogc pure nothrow const if (s == "*"); 208 | 209 | const a = Vec2(1f, 2f); 210 | const b = Vec2(3f, 4f); 211 | 212 | auto c = a * b; 213 | c *= b; 214 | ``` 215 | 216 | Returns the division of 2 vectors. Element wise quotient `(a, b) / (c, d) = (a/c, b/d)`. 217 | 218 | ```d 219 | Vec!(T, n) opBinary(string s) (const Vec!(T, n) other) @nogc pure nothrow const if (s == "/"); 220 | 221 | const a = Vec2(1f, 2f); 222 | const b = Vec2(3f, 4f); 223 | 224 | auto c = a / b; 225 | c /= b; 226 | ``` 227 | 228 | Get and set the N-th component. 229 | 230 | ```d 231 | T opIndex(int n) @nogc pure const nothrow; 232 | T opIndexAssign(T value, int n) @nogc pure nothrow; 233 | 234 | auto a = Vec2(1f, 2f); 235 | const b = a[0]; 236 | a[1] = 3; 237 | ``` 238 | 239 | Internal data as a pointer, can be used for sending data to shaders. 240 | 241 | ```d 242 | auto ptr() @nogc pure nothrow const; 243 | ``` 244 | 245 | Returns the internal array. 246 | 247 | ```d 248 | T[n] opIndex() @nogc pure const nothrow; 249 | 250 | const a = Vec2(1f, 2f); 251 | const b = a[]; 252 | ``` 253 | 254 | Cast to a vector of a different type but same size. 255 | 256 | ```d 257 | U opCast(U)() pure nothrow const if (is(U : Vec!R, R...) && (U._n == n)); 258 | 259 | const a = Vec2(1f, 2f); 260 | const b = cast(Vec2i) a; 261 | ``` 262 | 263 | Swizzling, returns a new vector. 264 | 265 | ```d 266 | Vec!(T, swizzle.length) opDispatch(const string swizzle)() @nogc const pure nothrow; 267 | 268 | const a = Vec3(1f, 2f, 3f); 269 | Vec!(5, float) b = a.xxzzy; 270 | b == Vec!(5, float)(1f, 1f, 3f, 3f, 2f); 271 | ``` 272 | 273 | --- 274 | 275 | Returns the dot product of 2 vectors. 276 | 277 | ```d 278 | real dot(T, ulong n)(Vec!(T, n) a, Vec!(T, n) b) @nogc pure nothrow; 279 | 280 | const a = Vec3(1f, 2f, 3f); 281 | const b = Vec3(4f, 5f, 6f); 282 | const c = dot(a, b); 283 | ``` 284 | 285 | --- 286 | 287 | Returns the cross product of 2 `Vec3`. The result is always a `Vec3f`. 288 | 289 | ```d 290 | Vec!(float, 3) cross(T)(Vec!(T, 3) a, Vec!(T, 3) b) @nogc pure nothrow; 291 | 292 | const a = Vec3(1f, 2f, 3f); 293 | const b = Vec3(4f, 5f, 6f); 294 | const c cross(a, b); 295 | ``` 296 | 297 | ### `dath.matrix` 298 | 299 | ```d 300 | public alias Mat2f = Mat!(float, 2); 301 | alias Mat3f = Mat!(float, 3); 302 | alias Mat4f = Mat!(float, 4); 303 | 304 | alias Mat2 = Mat2f; 305 | alias Mat3 = Mat3f; 306 | alias Mat4 = Mat4f; 307 | 308 | alias Mat2d = Mat!(double, 2); 309 | alias Mat3d = Mat!(double, 3); 310 | alias Mat4d = Mat!(double, 4); 311 | 312 | alias Mat2i = Mat!(int, 2); 313 | alias Mat3i = Mat!(int, 3); 314 | alias Mat4i = Mat!(int, 4); 315 | 316 | alias Mat2u = Mat!(uint, 2); 317 | alias Mat3u = Mat!(uint, 3); 318 | alias Mat4u = Mat!(uint, 4); 319 | ``` 320 | 321 | --- 322 | 323 | A square matrix. Supports any numeric type. 324 | 325 | ```d 326 | struct Mat(T, ulong n) if (n >= 2 && isNumeric!T); 327 | ``` 328 | 329 | Constructors: 330 | 331 | Variadic list of values or a single value (all elements will have the same value). 332 | 333 | ```d 334 | this(T...)(T args) @nogc pure nothrow; 335 | 336 | const a = Mat2(1f, 2f, 3f, 4f); 337 | const b = Mat2(0f); 338 | ``` 339 | 340 | Functions: 341 | 342 | Get and set the value at `[i, j]`. 343 | 344 | ```d 345 | T opIndex(int i, int j) @nogc pure const nothrow; 346 | T opIndexAssign(T value, int i, int j) @nogc pure nothrow; 347 | 348 | auto a = Mat2(1f, 2f, 3f, 4f); 349 | const b = a[0, 1]; 350 | a[0, 0] = 5f; 351 | ``` 352 | 353 | Returns the multiplication of this matrix with a scalar. 354 | 355 | ```d 356 | auto opBinary(string s) (const float scalar) @nogc pure const nothrow if (s == "*"); 357 | 358 | const a = Mat2(1f, 2f, 3f, 4f); 359 | auto b = a * 2f; 360 | b *= 3f; 361 | ``` 362 | 363 | Returns the multiplication of this matrix with a vector. 364 | 365 | ```d 366 | auto opBinary(string s) (const Vec!(T, n) vector) @nogc pure const nothrow if (s == "*"); 367 | 368 | const a = Mat2(1f, 2f, 3f, 4f); 369 | const v = Vec2(2f); 370 | const b = a * v; 371 | ``` 372 | 373 | Returns the multiplication of this matrix and another matrix. 374 | 375 | ```d 376 | auto opBinary(string s) (const Mat!(T, n) other) @nogc pure const nothrow if (s == "*"); 377 | 378 | const a = Mat2(1f, 2f, 3f, 4f); 379 | const b = Mat2(5f, 6f, 7f, 8f); 380 | const c = a * b; 381 | ``` 382 | 383 | Returns the subtraction of addition this matrix and another matrix. 384 | 385 | ```d 386 | auto opBinary(string s) (const Mat!(T, n) other) @nogc pure const nothrow if (s == "+" || s == "-"); 387 | 388 | const a = Mat2(1f, 2f, 3f, 4f); 389 | const b = Mat2(5f, 6f, 7f, 8f); 390 | const c = a + b; 391 | const d = a - b; 392 | ``` 393 | 394 | Returns the internal data as a pointer, can be used for sending data to shaders. 395 | 396 | ```d 397 | auto ptr() @nogc pure const nothrow; 398 | ``` 399 | 400 | --- 401 | 402 | Creates an identity matrix. 403 | 404 | ```d 405 | Mat!(T, n) identity(T, ulong n)() @nogc pure nothrow; 406 | 407 | const a = identity!(float, 2); 408 | ``` 409 | 410 | --- 411 | 412 | Creates a scaling matrix. 413 | 414 | ```d 415 | Mat!(T, n) scaling(T, ulong n)(Vec!(T, n-1) v) @nogc pure nothrow; 416 | 417 | const a = scaling!(float, 2)(Vec!(float, 1)(3f)); 418 | ``` 419 | 420 | --- 421 | 422 | Creates a rotation matrix, angle in radians. 423 | 424 | ```d 425 | auto rotation(T)(float angle, Vec!(T, 3) axis) @nogc pure nothrow; 426 | 427 | const a = rotation!(float, 2)(1, Vec3(0f, 1f, 0f)); 428 | ``` 429 | 430 | --- 431 | 432 | Creates a translation matrix. 433 | 434 | ```d 435 | auto translation(T, ulong n)(Vec!(T, n-1) v) @nogc pure nothrow; 436 | 437 | const a = translation!(float, 3)(Vec2(4f)); 438 | ``` 439 | 440 | --- 441 | 442 | Creates a look-at matrix. 443 | 444 | ```d 445 | auto lookAt(Vec3 eye, Vec3 target, Vec3 up) @nogc pure nothrow; 446 | ``` 447 | 448 | --- 449 | 450 | Creates an orthographic projection matrix. 451 | 452 | ```d 453 | auto orthographic(float left, float right, float bottom, float top, float near, float far) @nogc pure nothrow; 454 | ``` 455 | 456 | --- 457 | 458 | Creates a perspective projection matrix. 459 | 460 | ```d 461 | auto perspective(float fov_in_radians, float aspect, float near, float far) @nogc pure nothrow; 462 | ``` 463 | 464 | --- 465 | 466 | Returns the inverse of a `Mat4`. If no inverse can be found it returns a `Mat4(inf)`. 467 | 468 | ```d 469 | Mat4 inverse(const Mat4 a) @nogc pure nothrow; 470 | 471 | const a = Mat4(5f, 6f, 6f, 8f, 2f, 2f, 2f, 8f, 6f, 6f, 2f, 8f, 2f, 3f, 6f, 7f); 472 | (inverse(a) * a) == identity!(float , 4); 473 | ``` 474 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "authors": [ 3 | "CodeMyst" 4 | ], 5 | "copyright": "Copyright © 2022, codemyst", 6 | "dependencies": { 7 | "fluent-asserts": "~>0.13.3", 8 | "silly": "~>1.1.1" 9 | }, 10 | "description": "Math lib intended for games. Still very much a work in progress.", 11 | "license": "MIT", 12 | "name": "dath", 13 | "targetType": "library" 14 | } -------------------------------------------------------------------------------- /dub.selections.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileVersion": 1, 3 | "versions": { 4 | "ddmp": "0.0.1-0.dev.3", 5 | "dshould": "1.5.0", 6 | "fluent-asserts": "0.13.3", 7 | "intel-intrinsics": "1.2.5", 8 | "libdparse": "0.14.0", 9 | "prettyprint": "1.0.7", 10 | "silly": "1.1.1", 11 | "stdx-allocator": "2.77.5", 12 | "unit-threaded": "2.0.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /source/dath/core.d: -------------------------------------------------------------------------------- 1 | module dath.core; 2 | 3 | public const double PI = 3.14159265358979323846; 4 | 5 | /++ 6 | + Converts deg to rad. 7 | +/ 8 | public float rad(float deg) @nogc pure nothrow { 9 | return deg * (PI / 180); 10 | } 11 | 12 | /++ 13 | + Converts rad to deg. 14 | +/ 15 | public float deg(float rad) @nogc pure nothrow { 16 | return rad * 180 / PI; 17 | } 18 | -------------------------------------------------------------------------------- /source/dath/matrix.d: -------------------------------------------------------------------------------- 1 | module dath.matrix; 2 | 3 | version(unittest) import fluent.asserts; 4 | 5 | import std.traits; 6 | import dath.core; 7 | import dath.vector; 8 | 9 | public alias Mat2f = Mat!(float, 2); 10 | public alias Mat3f = Mat!(float, 3); 11 | public alias Mat4f = Mat!(float, 4); 12 | 13 | public alias Mat2 = Mat2f; 14 | public alias Mat3 = Mat3f; 15 | public alias Mat4 = Mat4f; 16 | 17 | public alias Mat2d = Mat!(double, 2); 18 | public alias Mat3d = Mat!(double, 3); 19 | public alias Mat4d = Mat!(double, 4); 20 | 21 | public alias Mat2i = Mat!(int, 2); 22 | public alias Mat3i = Mat!(int, 3); 23 | public alias Mat4i = Mat!(int, 4); 24 | 25 | public alias Mat2u = Mat!(uint, 2); 26 | public alias Mat3u = Mat!(uint, 3); 27 | public alias Mat4u = Mat!(uint, 4); 28 | 29 | /++ 30 | + A square NxN matrix. Supports any numeric type. 31 | +/ 32 | public struct Mat(T, ulong n) if (n >= 2 && isNumeric!T) 33 | { 34 | union 35 | { 36 | T[n*n] v; // all values 37 | T[n][n] c; // all components 38 | } 39 | 40 | public this(T...)(T args) @nogc pure nothrow 41 | { 42 | static foreach (arg; args) 43 | { 44 | static assert(isNumeric!(typeof(arg)), "All values must be numeric."); 45 | } 46 | 47 | static assert(args.length > 0, "No args provided."); 48 | 49 | static assert(args.length == 1 || args.length == n*n, "Number of args must be either 1 or N*N."); 50 | 51 | static if (args.length == 1) 52 | { 53 | v[] = args[0]; 54 | } 55 | else 56 | { 57 | v = [args]; 58 | } 59 | } 60 | 61 | /++ 62 | + Get the value at [i, j] 63 | +/ 64 | public T opIndex(int i, int j) @nogc pure const nothrow 65 | { 66 | return c[i][j]; 67 | } 68 | 69 | /++ 70 | + Set the value at [i, j] 71 | +/ 72 | public T opIndexAssign(T value, int i, int j) @nogc pure nothrow 73 | { 74 | return c[i][j] = value; 75 | } 76 | 77 | /++ 78 | + Returns this matrix * scalar 79 | +/ 80 | public auto opBinary(string s) (const float scalar) @nogc pure const nothrow if (s == "*") 81 | { 82 | Mat!(T, n) res; 83 | res.v = v[] * scalar; 84 | return res; 85 | } 86 | 87 | /++ 88 | + Returns this matrix * scalar 89 | +/ 90 | public void opOpAssign(string s) (const float scalar) @nogc pure nothrow if (s == "*") 91 | { 92 | v[] *= scalar; 93 | } 94 | 95 | /++ 96 | + Returns this matrix * vector 97 | +/ 98 | public auto opBinary(string s) (const Vec!(T, n) vector) @nogc pure const nothrow if (s == "*") 99 | { 100 | Vec!(T, n) res; 101 | 102 | for (int i = 0; i < n; i++) 103 | { 104 | float sum = 0f; 105 | for (int j = 0; j < n; j++) 106 | { 107 | sum += this[i, j] * vector[j]; 108 | } 109 | res[i] = sum; 110 | } 111 | 112 | return res; 113 | } 114 | 115 | /++ 116 | + Returns this matrix * matrix 117 | +/ 118 | public auto opBinary(string s) (const Mat!(T, n) other) @nogc pure const nothrow if (s == "*") 119 | { 120 | Mat!(T, n) res; 121 | 122 | for (int i = 0; i < n; i++) 123 | { 124 | for (int j = 0; j < n; j++) 125 | { 126 | float sum = 0f; 127 | for (int k = 0; k < n; k++) 128 | { 129 | sum += this[i, k] * other[k, j]; 130 | } 131 | res[i, j] = sum; 132 | } 133 | } 134 | 135 | return res; 136 | } 137 | 138 | /++ 139 | + Returns this matrix * matrix 140 | +/ 141 | public void opOpAssign (string s) (const Mat!(T, n) other) @nogc pure nothrow if (s == "*") 142 | { 143 | auto res = this * other; 144 | this.v = res.v; 145 | } 146 | 147 | /++ 148 | + Returns sum or sub of two matrices 149 | +/ 150 | public auto opBinary(string s)(const Mat!(T, n) other) @nogc pure const nothrow if (s == "+" || s == "-") 151 | { 152 | Mat!(T, n) res; 153 | 154 | for (int i = 0; i < n; i++) 155 | { 156 | for (int j = 0; j < n; j++) 157 | { 158 | mixin("res[i, j] = this[i, j] " ~ s ~ " other[i, j];"); 159 | } 160 | } 161 | 162 | return res; 163 | } 164 | 165 | /++ 166 | + Internal data as a pointer, use for sending data to shaders. 167 | +/ 168 | public auto ptr() @nogc pure const nothrow 169 | { 170 | return v.ptr; 171 | } 172 | } 173 | 174 | /++ 175 | + creates an identity matrix 176 | +/ 177 | public Mat!(T, n) identity(T, ulong n)() @nogc pure nothrow 178 | { 179 | Mat!(T, n) res; 180 | for (int i = 0; i < n; i++) 181 | { 182 | for (int j = 0; j < n; j++) 183 | { 184 | res[i, j] = i == j ? 1 : 0; 185 | } 186 | } 187 | return res; 188 | } 189 | 190 | /++ 191 | + Creates a scaling matrix 192 | +/ 193 | public Mat!(T, n) scaling(T, ulong n)(Vec!(T, n-1) v) @nogc pure nothrow 194 | { 195 | auto res = identity!(T, n); 196 | for (int i = 0; i + 1 < n; i++) 197 | { 198 | res[i, i] = v[i]; 199 | } 200 | return res; 201 | } 202 | 203 | /++ 204 | + Creates a rotation matrix, angle in radians 205 | +/ 206 | public auto rotation(T)(float angle, Vec!(T, 3) axis) @nogc pure nothrow 207 | { 208 | import std.math : sin, cos; 209 | 210 | auto res = identity!(T, 4)(); 211 | float c = cos(angle); 212 | float c1 = 1 - c; 213 | float s = sin(angle); 214 | 215 | auto a = axis.normalized(); 216 | 217 | res[0, 0] = a.x * a.x * c1 + c; 218 | res[0, 1] = a.x * a.y * c1 - a.z * s; 219 | res[0, 2] = a.x * a.z * c1 + a.y * s; 220 | res[1, 0] = a.y * a.x * c1 + a.z * s; 221 | res[1, 1] = a.y * a.y * c1 + c; 222 | res[1, 2] = a.y * a.z * c1 - a.x * s; 223 | res[2, 0] = a.z * a.x * c1 - a.y * s; 224 | res[2, 1] = a.z * a.y * c1 + a.x * s; 225 | res[2, 2] = a.z * a.z * c1 + c; 226 | 227 | return res; 228 | } 229 | 230 | /++ 231 | + Creates a translation matrix 232 | +/ 233 | public auto translation(T, ulong n)(Vec!(T, n-1) v) @nogc pure nothrow 234 | { 235 | auto res = identity!(T, n)(); 236 | for (int i = 0; i + 1 < n; i++) 237 | { 238 | res[i, n-1] = res[i, n-1] + v[i]; 239 | } 240 | return res; 241 | } 242 | 243 | /++ 244 | + Creates a look-at matrix 245 | +/ 246 | public auto lookAt(Vec3 eye, Vec3 target, Vec3 up) @nogc pure nothrow 247 | { 248 | Vec3 z = (eye - target).normalized(); 249 | Vec3 x = cross(-up, z).normalized(); 250 | Vec3 y = cross(z, -x); 251 | 252 | return Mat4(-x.x, -x.y, -x.z, dot(x, eye), 253 | y.x, y.y, y.z, -dot(y, eye), 254 | z.x, z.y, z.z, -dot(z, eye), 255 | 0, 0, 0, 1); 256 | } 257 | 258 | /++ 259 | + Creates an orthographic projection matrix 260 | +/ 261 | public auto orthographic(float left, float right, float bottom, float top, float near, float far) @nogc pure nothrow 262 | { 263 | float dx = right - left; 264 | float dy = top - bottom; 265 | float dz = far - near; 266 | 267 | float tx = -(right + left) / dx; 268 | float ty = -(top + bottom) / dy; 269 | float tz = -(far + near) / dz; 270 | 271 | return Mat4(2/dx, 0f, 0f, tx, 272 | 0f, 2/dy, 0f, ty, 273 | 0f, 0f, -2/dz, tz, 274 | 0f, 0f, 0f, 1f); 275 | } 276 | 277 | /++ 278 | + Creates a perspective projection matrix 279 | +/ 280 | public auto perspective(float fov_in_radians, float aspect, float near, float far) @nogc pure nothrow 281 | { 282 | import core.stdc.math : tan; 283 | 284 | float f = 1 / tan(fov_in_radians / 2); 285 | float d = 1 / (near - far); 286 | 287 | return Mat4(f / aspect, 0f, 0f, 0f, 288 | 0f, f, 0f, 0f, 289 | 0f, 0f, (far + near) * d, 2 * d * far * near, 290 | 0f, 0f, -1f, 0f); 291 | } 292 | 293 | /++ 294 | + Returns the inverse of the provided matrix, if no inverse can be found it returns a `Mat4(inf)` 295 | +/ 296 | public Mat4 inverse(const Mat4 a) @nogc pure nothrow 297 | { 298 | Mat4 t = a; 299 | 300 | float det2_01_01 = t[0, 0] * t[1, 1] - t[0, 1] * t[1, 0]; 301 | float det2_01_02 = t[0, 0] * t[1, 2] - t[0, 2] * t[1, 0]; 302 | float det2_01_03 = t[0, 0] * t[1, 3] - t[0, 3] * t[1, 0]; 303 | float det2_01_12 = t[0, 1] * t[1, 2] - t[0, 2] * t[1, 1]; 304 | float det2_01_13 = t[0, 1] * t[1, 3] - t[0, 3] * t[1, 1]; 305 | float det2_01_23 = t[0, 2] * t[1, 3] - t[0, 3] * t[1, 2]; 306 | 307 | float det3_201_012 = t[2, 0] * det2_01_12 - t[2, 1] * det2_01_02 + t[2, 2] * det2_01_01; 308 | float det3_201_013 = t[2, 0] * det2_01_13 - t[2, 1] * det2_01_03 + t[2, 3] * det2_01_01; 309 | float det3_201_023 = t[2, 0] * det2_01_23 - t[2, 2] * det2_01_03 + t[2, 3] * det2_01_02; 310 | float det3_201_123 = t[2, 1] * det2_01_23 - t[2, 2] * det2_01_13 + t[2, 3] * det2_01_12; 311 | 312 | float det = - det3_201_123 * t[3, 0] + det3_201_023 * t[3, 1] - det3_201_013 * t[3, 2] + det3_201_012 * t[3, 3]; 313 | float invDet = 1 / det; 314 | 315 | float det2_03_01 = t[0, 0] * t[3, 1] - t[0, 1] * t[3, 0]; 316 | float det2_03_02 = t[0, 0] * t[3, 2] - t[0, 2] * t[3, 0]; 317 | float det2_03_03 = t[0, 0] * t[3, 3] - t[0, 3] * t[3, 0]; 318 | float det2_03_12 = t[0, 1] * t[3, 2] - t[0, 2] * t[3, 1]; 319 | float det2_03_13 = t[0, 1] * t[3, 3] - t[0, 3] * t[3, 1]; 320 | float det2_03_23 = t[0, 2] * t[3, 3] - t[0, 3] * t[3, 2]; 321 | float det2_13_01 = t[1, 0] * t[3, 1] - t[1, 1] * t[3, 0]; 322 | float det2_13_02 = t[1, 0] * t[3, 2] - t[1, 2] * t[3, 0]; 323 | float det2_13_03 = t[1, 0] * t[3, 3] - t[1, 3] * t[3, 0]; 324 | float det2_13_12 = t[1, 1] * t[3, 2] - t[1, 2] * t[3, 1]; 325 | float det2_13_13 = t[1, 1] * t[3, 3] - t[1, 3] * t[3, 1]; 326 | float det2_13_23 = t[1, 2] * t[3, 3] - t[1, 3] * t[3, 2]; 327 | 328 | float det3_203_012 = t[2, 0] * det2_03_12 - t[2, 1] * det2_03_02 + t[2, 2] * det2_03_01; 329 | float det3_203_013 = t[2, 0] * det2_03_13 - t[2, 1] * det2_03_03 + t[2, 3] * det2_03_01; 330 | float det3_203_023 = t[2, 0] * det2_03_23 - t[2, 2] * det2_03_03 + t[2, 3] * det2_03_02; 331 | float det3_203_123 = t[2, 1] * det2_03_23 - t[2, 2] * det2_03_13 + t[2, 3] * det2_03_12; 332 | 333 | float det3_213_012 = t[2, 0] * det2_13_12 - t[2, 1] * det2_13_02 + t[2, 2] * det2_13_01; 334 | float det3_213_013 = t[2, 0] * det2_13_13 - t[2, 1] * det2_13_03 + t[2, 3] * det2_13_01; 335 | float det3_213_023 = t[2, 0] * det2_13_23 - t[2, 2] * det2_13_03 + t[2, 3] * det2_13_02; 336 | float det3_213_123 = t[2, 1] * det2_13_23 - t[2, 2] * det2_13_13 + t[2, 3] * det2_13_12; 337 | 338 | float det3_301_012 = t[3, 0] * det2_01_12 - t[3, 1] * det2_01_02 + t[3, 2] * det2_01_01; 339 | float det3_301_013 = t[3, 0] * det2_01_13 - t[3, 1] * det2_01_03 + t[3, 3] * det2_01_01; 340 | float det3_301_023 = t[3, 0] * det2_01_23 - t[3, 2] * det2_01_03 + t[3, 3] * det2_01_02; 341 | float det3_301_123 = t[3, 1] * det2_01_23 - t[3, 2] * det2_01_13 + t[3, 3] * det2_01_12; 342 | 343 | Mat4 res; 344 | 345 | res[0, 0] = - det3_213_123 * invDet; 346 | res[1, 0] = + det3_213_023 * invDet; 347 | res[2, 0] = - det3_213_013 * invDet; 348 | res[3, 0] = + det3_213_012 * invDet; 349 | 350 | res[0, 1] = + det3_203_123 * invDet; 351 | res[1, 1] = - det3_203_023 * invDet; 352 | res[2, 1] = + det3_203_013 * invDet; 353 | res[3, 1] = - det3_203_012 * invDet; 354 | 355 | res[0, 2] = + det3_301_123 * invDet; 356 | res[1, 2] = - det3_301_023 * invDet; 357 | res[2, 2] = + det3_301_013 * invDet; 358 | res[3, 2] = - det3_301_012 * invDet; 359 | 360 | res[0, 3] = - det3_201_123 * invDet; 361 | res[1, 3] = + det3_201_023 * invDet; 362 | res[2, 3] = - det3_201_013 * invDet; 363 | res[3, 3] = + det3_201_012 * invDet; 364 | 365 | return res; 366 | } 367 | 368 | @("Creating matrices") 369 | unittest 370 | { 371 | auto t1 = Mat2(2f); 372 | const t2 = Mat2(1f, 2f, 3f, 4f); 373 | 374 | t1[0, 0].should.equal(2f); 375 | t1[0, 1].should.equal(2f); 376 | t1[1, 0].should.equal(2f); 377 | t1[1, 1].should.equal(2f); 378 | 379 | t2[0, 0].should.equal(1f); 380 | t2[0, 1].should.equal(2f); 381 | t2[1, 0].should.equal(3f); 382 | t2[1, 1].should.equal(4f); 383 | 384 | t1[0, 0] = 5f; 385 | t1[0, 0].should.equal(5f); 386 | } 387 | 388 | @("Matrix multiplication by scalar") 389 | unittest 390 | { 391 | const t1 = Mat2(1f, 2f, 3f, 4f); 392 | (t1 * 2f).should.equal(Mat2(2, 4, 6, 8)); 393 | } 394 | 395 | @("Matrix multiplication by vector") 396 | unittest 397 | { 398 | const m1 = Mat2(1f, 2f, 3f, 4f); 399 | const v1 = Vec2(4f, 6f); 400 | (m1 * v1).should.equal(Vec2(16f, 36f)); 401 | } 402 | 403 | @("Matrix multiplication by matrix") 404 | unittest 405 | { 406 | auto m1 = Mat2(1f, 2f, 3f, 4f); 407 | const m2 = Mat2(5f, 6f, 7f, 8f); 408 | (m1 * m2).should.equal(Mat2(19, 22, 43, 50)); 409 | m1 *= m2; 410 | m1.should.equal(Mat2(19, 22, 43, 50)); 411 | } 412 | 413 | @("Adding 2 matrices") 414 | unittest 415 | { 416 | const m1 = Mat2(1f, 2f, 3f, 4f); 417 | const m2 = Mat2(5f, 6f, 7f, 8f); 418 | (m1 + m2).should.equal(Mat2(6, 8, 10, 12)); 419 | } 420 | 421 | @("Subtracting 2 matrices") 422 | unittest 423 | { 424 | const m1 = Mat2(5f, 6f, 7f, 8f); 425 | const m2 = Mat2(1f, 2f, 3f, 4f); 426 | (m1 - m2).should.equal(Mat2(4)); 427 | } 428 | 429 | @("Identity matrix") 430 | unittest 431 | { 432 | const m1 = identity!(float, 2)(); 433 | m1.should.equal(Mat2(1, 0, 0, 1)); 434 | } 435 | 436 | @("Scaling matrix") 437 | unittest 438 | { 439 | const scaling = scaling!(float, 2)(Vec!(float, 1)(3f)); 440 | scaling.should.equal(Mat2(3, 0, 0, 1)); 441 | 442 | const m1 = Mat2(1f, 2f, 3f, 4f); 443 | (m1 * scaling).should.equal(Mat2(3, 2, 9, 4)); 444 | } 445 | 446 | @("Translation matrix") 447 | unittest 448 | { 449 | const trans = translation!(float, 3)(Vec2(4f)); 450 | trans.should.equal(Mat3(1, 0, 4, 0, 1, 4, 0, 0, 1)); 451 | 452 | const m1 = Mat3(3f); 453 | (m1 * trans).should.equal(Mat3(3, 3, 27, 3, 3, 27, 3, 3, 27)); 454 | } 455 | 456 | @("Matrix inverse") 457 | unittest 458 | { 459 | const m1 = Mat4(5f, 6f, 6f, 8f, 2f, 2f, 2f, 8f, 6f, 6f, 2f, 8f, 2f, 3f, 6f, 7f); 460 | (inverse(m1) * m1).should.equal(identity!(float, 4)); 461 | } 462 | -------------------------------------------------------------------------------- /source/dath/package.d: -------------------------------------------------------------------------------- 1 | module dath; 2 | 3 | public import dath.core; 4 | public import dath.vector; 5 | public import dath.matrix; 6 | public import dath.rect; 7 | -------------------------------------------------------------------------------- /source/dath/rect.d: -------------------------------------------------------------------------------- 1 | module dath.rect; 2 | 3 | version(unittest) import fluent.asserts; 4 | 5 | import std.traits; 6 | import dath.vector; 7 | 8 | public alias Rectf = Rect!float; 9 | public alias Rectd = Rect!double; 10 | public alias Recti = Rect!int; 11 | public alias Rectu = Rect!uint; 12 | 13 | /++ 14 | + Rectangle definition, origin at upper left. 15 | +/ 16 | public struct Rect(T) if (isNumeric!T) 17 | { 18 | union 19 | { 20 | T[4] v; 21 | 22 | struct 23 | { 24 | T x; 25 | T y; 26 | T w; 27 | T h; 28 | } 29 | 30 | struct 31 | { 32 | Vec!(T, 2) position; 33 | Vec!(T, 2) size; 34 | } 35 | } 36 | 37 | public this(T value) @nogc pure nothrow 38 | { 39 | v[] = value; 40 | } 41 | 42 | public this(T x, T y, T w, T h) @nogc pure nothrow 43 | { 44 | this.x = x; 45 | this.y = y; 46 | this.w = w; 47 | this.h = h; 48 | } 49 | 50 | public this(T x, T y, Vec!(T, 2) size) @nogc pure nothrow 51 | { 52 | this.x = x; 53 | this.y = y; 54 | this.size = size; 55 | } 56 | 57 | public this (Vec!(T, 2) position, T w, T h) @nogc pure nothrow 58 | { 59 | this.position = position; 60 | this.w = w; 61 | this.h = h; 62 | } 63 | 64 | public this (Vec!(T, 2) position, Vec!(T, 2) size) @nogc pure nothrow 65 | { 66 | this.position = position; 67 | this.size = size; 68 | } 69 | } 70 | 71 | /++ 72 | + Checks if two rectangles intersect. 73 | +/ 74 | public bool intersects(T)(const Rect!T a, const Rect!T b) @nogc pure nothrow 75 | { 76 | if (a.x < b.x + b.w && 77 | a.x + a.w > b.x && 78 | a.y < b.y + b.h && 79 | a.y + a.h > b.y) 80 | { 81 | return true; 82 | } 83 | 84 | return false; 85 | } 86 | 87 | @("Rect creation and intersection") 88 | unittest 89 | { 90 | const a = Rectf(0, 0, 10, 10); 91 | const b = Rectf(5, 5, 10, 10); 92 | 93 | intersects(a, b).should.equal(true); 94 | 95 | const r1 = Rectf(Vec2(10, 10), Vec2(5, 5)); 96 | 97 | r1.should.equal(Rectf(10, 10, 5, 5)); 98 | } 99 | -------------------------------------------------------------------------------- /source/dath/vector.d: -------------------------------------------------------------------------------- 1 | module dath.vector; 2 | 3 | version(unittest) import fluent.asserts; 4 | 5 | import std.traits; 6 | 7 | public alias Vec2f = Vec!(float, 2); 8 | public alias Vec3f = Vec!(float, 3); 9 | public alias Vec4f = Vec!(float, 4); 10 | 11 | public alias Vec2 = Vec2f; 12 | public alias Vec3 = Vec3f; 13 | public alias Vec4 = Vec4f; 14 | 15 | public alias Vec2d = Vec!(double, 2); 16 | public alias Vec3d = Vec!(double, 3); 17 | public alias Vec4d = Vec!(double, 4); 18 | 19 | public alias Vec2i = Vec!(int, 2); 20 | public alias Vec3i = Vec!(int, 3); 21 | public alias Vec4i = Vec!(int, 4); 22 | 23 | public alias Vec2u = Vec!(uint, 2); 24 | public alias Vec3u = Vec!(uint, 3); 25 | public alias Vec4u = Vec!(uint, 4); 26 | 27 | /++ 28 | + Numeric Vector type with an optional amount of components. 29 | +/ 30 | public struct Vec(T, ulong n) if (n >= 1 && isNumeric!T) 31 | { 32 | union { 33 | /++ 34 | + Internal data. 35 | +/ 36 | T[n] v; 37 | 38 | struct 39 | { 40 | // Predefined component names. 41 | 42 | static if (n >= 1) 43 | { 44 | T x; 45 | } 46 | 47 | static if (n >= 2) 48 | { 49 | T y; 50 | } 51 | 52 | static if (n >= 3) 53 | { 54 | T z; 55 | } 56 | 57 | static if (n >= 4) 58 | { 59 | T w; 60 | } 61 | } 62 | 63 | struct 64 | { 65 | // Predefined components names for colors. 66 | 67 | static if (n >= 1) 68 | { 69 | T r; 70 | } 71 | 72 | static if (n >= 2) 73 | { 74 | T g; 75 | } 76 | 77 | static if (n >= 3) 78 | { 79 | T b; 80 | } 81 | 82 | static if (n >= 4) 83 | { 84 | T a; 85 | } 86 | } 87 | } 88 | 89 | private alias _t = T; 90 | private enum _n = n; 91 | 92 | public this(U)(U[] elements) @nogc pure nothrow 93 | { 94 | assert(isNumeric!U, "All components must be numeric."); 95 | 96 | assert(elements.length > 0, "No components provided."); 97 | 98 | assert(elements.length == 1 || elements.length == n, 99 | "Number of components must be either 1 or the number of components the vector holds."); 100 | 101 | v = elements; 102 | } 103 | 104 | public this(U...)(U args) @nogc pure nothrow 105 | { 106 | static foreach (arg; args) 107 | { 108 | static assert(isNumeric!(typeof(arg)), "All components must be numeric"); 109 | } 110 | 111 | static assert(args.length > 0, "No components provided."); 112 | 113 | static assert(args.length == 1 || args.length == n, 114 | "Number of components must be either 1 or the number of components the vector holds."); 115 | 116 | static if (args.length == 1) 117 | { 118 | static if (!is(T == typeof(args[0]))) 119 | { 120 | v[] = cast(T) args[0]; 121 | } 122 | else 123 | { 124 | v[] = args[0]; 125 | } 126 | } 127 | else 128 | { 129 | static if (!is(T == typeof(args[0]))) 130 | { 131 | v = cast(T[]) [args]; 132 | } 133 | else 134 | { 135 | v = [args]; 136 | } 137 | } 138 | } 139 | 140 | /++ 141 | + Internal data as a pointer, use for sending data to shaders. 142 | +/ 143 | public auto ptr() @nogc pure nothrow const 144 | { 145 | return v.ptr; 146 | } 147 | 148 | /++ 149 | + Vector magnitude. 150 | +/ 151 | public real magnitude() @nogc pure nothrow const 152 | { 153 | import std.math : sqrt; 154 | 155 | real sum = 0; 156 | for (int i = 0; i < n; i++) 157 | { 158 | sum += v[i] * v[i]; 159 | } 160 | 161 | return sqrt(sum); 162 | } 163 | 164 | /++ 165 | + Normalizes the vectors. Changes the current struct! 166 | +/ 167 | public void normalize() @nogc pure nothrow 168 | { 169 | this = normalized(); 170 | } 171 | 172 | /++ 173 | + Returns the normalized vector. Doesn't change the current struct! 174 | +/ 175 | public Vec!(T, n) normalized() @nogc pure nothrow const 176 | { 177 | real mag = magnitude(); 178 | 179 | if (mag == 0) 180 | { 181 | return Vec!(T, n)(0); 182 | } 183 | 184 | return this / mag; 185 | } 186 | 187 | /++ 188 | + Returns the negated vector. 189 | +/ 190 | public Vec!(T, n) opUnary(string s)() @nogc pure nothrow const if (s == "-") 191 | { 192 | Vec!(T, n) res; 193 | res.v = -v[]; 194 | return res; 195 | } 196 | 197 | /++ 198 | + Returns the mul of this * scalar. 199 | +/ 200 | public Vec!(T, n) opBinary(string s) (const float scalar) @nogc pure nothrow const if (s == "*") 201 | { 202 | Vec!(T, n) res; 203 | res.v = v[] * scalar; 204 | return res; 205 | } 206 | 207 | /++ 208 | + Returns the mul of this * scalar. 209 | +/ 210 | public void opOpAssign(string s) (const float scalar) @nogc pure nothrow if (s == "*") 211 | { 212 | v[] *= scalar; 213 | } 214 | 215 | /++ 216 | + Returns the sum of this + scalar. 217 | +/ 218 | public Vec!(T, n) opBinary(string s) (const float scalar) @nogc pure nothrow const if (s == "+") 219 | { 220 | Vec!(T, n) res; 221 | res.v = v[] + scalar; 222 | return res; 223 | } 224 | 225 | /++ 226 | + Returns the sum of this + scalar. 227 | +/ 228 | public void opOpAssign(string s) (const float scalar) @nogc pure nothrow if (s == "+") 229 | { 230 | v[] += scalar; 231 | } 232 | 233 | /++ 234 | + Returns the sub of this - scalar. 235 | +/ 236 | public Vec!(T, n) opBinary(string s) (const float scalar) @nogc pure nothrow const if (s == "-") 237 | { 238 | Vec!(T, n) res; 239 | res.v = v[] - scalar; 240 | return res; 241 | } 242 | 243 | /++ 244 | + Returns the sub of this - scalar. 245 | +/ 246 | public void opOpAssign(string s) (const float scalar) @nogc pure nothrow if (s == "-") 247 | { 248 | v[] -= scalar; 249 | } 250 | 251 | /++ 252 | + Returns the div of this / scalar. 253 | +/ 254 | public Vec!(T, n) opBinary(string s) (in float scalar) @nogc pure nothrow const if (s == "/") 255 | { 256 | Vec!(T, n) res; 257 | res.v = v[] / cast(T) scalar; 258 | return res; 259 | } 260 | 261 | /++ 262 | + Returns the div of this / scalar. 263 | +/ 264 | public void opOpAssign(string s) (in float scalar) @nogc pure nothrow if (s == "/") 265 | { 266 | v[] /= scalar; 267 | } 268 | 269 | /++ 270 | + Returns the sum of 2 vectors. 271 | +/ 272 | public Vec!(T, n) opBinary(string s) (const Vec!(T, n) other) @nogc pure nothrow const if (s == "+") 273 | { 274 | Vec!(T, n) res; 275 | res.v = v[] + other.v[]; 276 | return res; 277 | } 278 | 279 | /++ 280 | + Returns the sum of 2 vectors. 281 | +/ 282 | public void opOpAssign(string s) (const Vec!(T, n) other) @nogc pure nothrow if (s == "+") 283 | { 284 | v[] += other.v[]; 285 | } 286 | 287 | /++ 288 | + Returns the sub of 2 vectors. 289 | +/ 290 | public Vec!(T, n) opBinary(string s) (const Vec!(T, n) other) @nogc pure nothrow const if (s == "-") 291 | { 292 | Vec!(T, n) res; 293 | res.v = v[] - other.v[]; 294 | return res; 295 | } 296 | 297 | /++ 298 | + Returns the sub of 2 vectors. 299 | +/ 300 | public void opOpAssign(string s) (const Vec!(T, n) other) @nogc pure nothrow if (s == "-") 301 | { 302 | v[] -= other.v[]; 303 | } 304 | 305 | /++ 306 | + Returns the mul of 2 vectors. 307 | +/ 308 | public Vec!(T, n) opBinary(string s) (const Vec!(T, n) other) @nogc pure nothrow const if (s == "*") 309 | { 310 | Vec!(T, n) res; 311 | res.v = v[] * other.v[]; 312 | return res; 313 | } 314 | 315 | /++ 316 | + Returns the mul of 2 vectors. 317 | +/ 318 | public void opOpAssign(string s) (const Vec!(T, n) other) @nogc pure nothrow if (s == "*") 319 | { 320 | v[] *= other.v[]; 321 | } 322 | 323 | /++ 324 | + Returns the div of 2 vectors. 325 | +/ 326 | public Vec!(T, n) opBinary(string s) (const Vec!(T, n) other) @nogc pure nothrow const if (s == "/") 327 | { 328 | Vec!(T, n) res; 329 | res.v = v[] / other.v[]; 330 | return res; 331 | } 332 | 333 | /++ 334 | + Returns the div of 2 vectors. 335 | +/ 336 | public void opOpAssign(string s) (const Vec!(T, n) other) @nogc pure nothrow if (s == "/") 337 | { 338 | v[] /= other.v[]; 339 | } 340 | 341 | /++ 342 | + Get the N-th component. 343 | +/ 344 | public T opIndex(const ulong n) @nogc pure const nothrow 345 | { 346 | return v[n]; 347 | } 348 | 349 | /++ 350 | + Get the whole internal array. 351 | +/ 352 | public T[n] opIndex() @nogc pure const nothrow 353 | { 354 | return v[]; 355 | } 356 | 357 | /++ 358 | + Set the nth component 359 | +/ 360 | public T opIndexAssign(const T value, const ulong n) @nogc pure nothrow 361 | { 362 | return v[n] = value; 363 | } 364 | 365 | /++ 366 | + Cast to a vector of a different type. 367 | +/ 368 | public U opCast(U)() pure nothrow const if (is(U : Vec!R, R...) && (U._n == n)) 369 | { 370 | U res; 371 | foreach (i, el; v) 372 | { 373 | res.v[i] = cast(U._t) v[i]; 374 | } 375 | return res; 376 | } 377 | 378 | /++ 379 | + Swizzling. 380 | +/ 381 | public Vec!(T, swizzle.length) opDispatch(const string swizzle)() @nogc pure nothrow const 382 | { 383 | T[swizzle.length] arr; 384 | 385 | static foreach (i, c; swizzle) 386 | { 387 | static assert(coordToIdx!(c) <= n-1, 388 | "Trying to swizzle the " ~ c ~ " component, but this vector is too small."); 389 | 390 | arr[i] = v[coordToIdx!(c)]; 391 | } 392 | 393 | Vec!(T, swizzle.length) res; 394 | res.v = arr; 395 | return res; 396 | } 397 | 398 | public static Vec!(T, n) zero = Vec!(T, n)(0); 399 | public static Vec!(T, n) one = Vec!(T, n)(1); 400 | 401 | private template coordToIdx(char c) 402 | { 403 | static if (c == 'x') enum coordToIdx = 0; 404 | else static if (c == 'r') enum coordToIdx = 0; 405 | else static if (c == 'y') enum coordToIdx = 1; 406 | else static if (c == 'g') enum coordToIdx = 1; 407 | else static if (c == 'z') enum coordToIdx = 2; 408 | else static if (c == 'b') enum coordToIdx = 1; 409 | else static if (c == 'w') enum coordToIdx = 3; 410 | else static if (c == 'a') enum coordToIdx = 3; 411 | else static assert(false, "Unknown vector component " ~ c); 412 | } 413 | } 414 | 415 | /++ 416 | + Returns the dot product of 2 vectors. 417 | +/ 418 | public real dot(T, ulong n)(Vec!(T, n) a, Vec!(T, n) b) @nogc pure nothrow 419 | { 420 | real res = 0f; 421 | static foreach (i; 0..n) 422 | { 423 | res += a.v[i] * b.v[i]; 424 | } 425 | return res; 426 | } 427 | 428 | /++ 429 | + Returns the cross product of 2 Vec3. The result is always a float Vec3. 430 | +/ 431 | public Vec!(float, 3) cross(T)(Vec!(T, 3) a, Vec!(T, 3) b) @nogc pure nothrow 432 | { 433 | return Vec!(float, 3)(a.y * b.z - a.z * b.y, 434 | a.z * b.x - a.x * b.z, 435 | a.x * b.y - a.y * b.x); 436 | } 437 | 438 | /++ 439 | + Clamps a vector between a min and max value. Compares components and not the vector length. 440 | +/ 441 | public Vec!(T, n) clamp(T, ulong n)(const Vec!(T, n) value, const Vec!(T, n) min, const Vec!(T, n) max) 442 | @nogc pure nothrow 443 | { 444 | import std.algorithm : clamp; 445 | 446 | Vec!(T, n) res; 447 | foreach (i, el; value[]) 448 | { 449 | res[i] = clamp(el, min[i], max[i]); 450 | } 451 | 452 | return res; 453 | } 454 | 455 | @("Creating vectors") 456 | unittest 457 | { 458 | const t1 = Vec2(2f, 3f); 459 | 460 | t1.x.should.equal(2f); 461 | t1.y.should.equal(3f); 462 | 463 | t1.r.should.equal(2f); 464 | t1.g.should.equal(3f); 465 | 466 | const t2 = Vec2(2f); 467 | 468 | t2.x.should.equal(2f); 469 | t2.y.should.equal(2f); 470 | 471 | const t3 = Vec4d(1f, 2f, 3f, 4f); 472 | 473 | t3.x.should.equal(1f); 474 | t3.y.should.equal(2f); 475 | t3.z.should.equal(3f); 476 | t3.w.should.equal(4f); 477 | 478 | const t4 = Vec2i(5, 6); 479 | 480 | t4.x.should.equal(5); 481 | t4.y.should.equal(6); 482 | 483 | const float[] arr = [1f, 2f]; 484 | const t5 = Vec2(arr); 485 | 486 | t5.x.should.equal(1); 487 | t5.y.should.equal(2); 488 | } 489 | 490 | @("Vector magnitude") 491 | unittest 492 | { 493 | const t1 = Vec3(5, 6, 8); 494 | 495 | t1.magnitude().should.be.approximately(11.180, 0.01); 496 | 497 | const t2 = Vec2d(65, 76); 498 | 499 | t2.magnitude().should.be.approximately(100, 0.01); 500 | } 501 | 502 | @("Vector normalization") 503 | unittest 504 | { 505 | const t1 = Vec3(75, 64, 23); 506 | const t2 = t1.normalized(); 507 | 508 | t2.x.should.be.approximately(0.74, 0.01); 509 | t2.y.should.be.approximately(0.63, 0.01); 510 | t2.z.should.be.approximately(0.22, 0.01); 511 | } 512 | 513 | @("Vector scalar operations") 514 | unittest 515 | { 516 | auto t1 = Vec3(63, 75, 38); 517 | 518 | // negation 519 | (-t1).should.equal(Vec3(-63, -75, -38)); 520 | 521 | // multiplication with a scalar 522 | (t1 * 2).should.equal(Vec3(126, 150, 76)); 523 | t1 *= 3; 524 | t1.should.equal(Vec3(189, 225, 114)); 525 | 526 | // sum with a scalar 527 | (t1 + 2).should.equal(Vec3(191, 227, 116)); 528 | t1 += 3; 529 | t1.should.equal(Vec3(192, 228, 117)); 530 | 531 | // sub with a scalar 532 | (t1 - 2).should.equal(Vec3(190, 226, 115)); 533 | t1 -= 3; 534 | t1.should.equal(Vec3(189, 225, 114)); 535 | 536 | // division with a scalar 537 | (t1 / 2).should.equal(Vec3(94.5, 112.5, 57)); 538 | t1 /= 3; 539 | t1.should.equal(Vec3(63, 75, 38)); 540 | } 541 | 542 | @("Vector operations with other vectors") 543 | unittest 544 | { 545 | auto t1 = Vec3(63, 75, 38); 546 | auto t2 = Vec3(37, 98, 100); 547 | 548 | // sum of 2 vectors 549 | (t1 + t2).should.equal(Vec3(100, 173, 138)); 550 | t1 += t2; 551 | t1.should.equal(Vec3(100, 173, 138)); 552 | 553 | // sub of 2 vectors 554 | (t1 - t2).should.equal(Vec3(63, 75, 38)); 555 | t1 -= t2; 556 | t1.should.equal(Vec3(63, 75, 38)); 557 | 558 | // multiplication of 2 vectors 559 | (t1 * t2).should.equal(Vec3(2331, 7350, 3800)); 560 | t1 *= t2; 561 | t1.should.equal(Vec3(2331, 7350, 3800)); 562 | 563 | // division of 2 vectors 564 | (t1 / t2).should.equal(Vec3(63, 75, 38)); 565 | t1 /= t2; 566 | t1.should.equal(Vec3(63, 75, 38)); 567 | } 568 | 569 | @("Vector components and casting") 570 | unittest 571 | { 572 | auto t1 = Vec3(63, 75, 38); 573 | 574 | t1[0].should.equal(63); 575 | 576 | t1[1] = 100; 577 | t1[1].should.equal(100); 578 | t1.y.should.equal(100); 579 | 580 | auto t2 = cast(Vec3d) t1; 581 | t2.should.equal(Vec3d(63, 100, 38)); 582 | 583 | auto t3 = cast(Vec3i) t2; 584 | t3.should.equal(Vec3i(63, 100, 38)); 585 | } 586 | 587 | @("Vector dot and cross product") 588 | unittest 589 | { 590 | const t1 = Vec3(63, 75, 38); 591 | const t2 = Vec3(37, 98, 100); 592 | 593 | t1.dot(t2).should.equal(13_481); 594 | 595 | t1.cross(t2).should.equal(Vec3(3776, -4894, 3399)); 596 | } 597 | 598 | @("Vector swizzling") 599 | unittest 600 | { 601 | const t1 = Vec3(4, 5, 6); 602 | const t2 = t1.xz; 603 | 604 | t2.should.equal(Vec2(4, 6)); 605 | 606 | const t3 = Vec4(1f, 2f, 3f, 4f); 607 | const t4 = t3.xxyyww; 608 | 609 | t4.should.equal(Vec!(float, 6)(1f, 1f, 2f, 2f, 4f, 4f)); 610 | 611 | // rgb swizzling because why not 612 | const t5 = t3.rrr; 613 | t5.should.equal(Vec3(1f)); 614 | 615 | // rgba mixed with xyzw because why not 616 | const t6 = t3.rrzz; 617 | t6.should.equal(Vec4(1f, 1f, 3f, 3f)); 618 | } 619 | 620 | @("Clamping") 621 | unittest 622 | { 623 | const t1 = Vec2(0.5, 0.5); 624 | const t2 = t1.clamp(Vec2.zero, Vec2.one); 625 | 626 | t2.should.equal(t1); 627 | 628 | const t3 = Vec2(1.5, 0.5); 629 | const t4 = t3.clamp(Vec2.zero, Vec2.one); 630 | 631 | t4.should.equal(Vec2(1, 0.5)); 632 | 633 | const t5 = Vec2(10, 10); 634 | const t6 = t5.clamp(Vec2.zero, Vec2.one); 635 | 636 | t6.should.equal(Vec2(1, 1)); 637 | } 638 | --------------------------------------------------------------------------------