1 module hoekjed.kern.wiskunde; 2 import hoekjed.overig; 3 import std.math : abs, cos, sin, sqrt; 4 import std.stdio; 5 6 version (HoekjeD_Double) { 7 alias nauwkeurigheid = double; 8 } else { 9 alias nauwkeurigheid = float; 10 } 11 12 alias Vec(uint grootte = 3, Soort = nauwkeurigheid) = Mat!(grootte, 1, Soort); 13 alias Mat(uint aantal = 3, Soort = nauwkeurigheid) = Mat!(aantal, aantal, Soort); 14 15 struct Mat(uint rij_aantal, uint kolom_aantal, Soort = nauwkeurigheid) 16 if (rij_aantal > 0 && kolom_aantal > 0) { 17 enum uint grootte = rij_aantal * kolom_aantal; 18 enum bool isVec = kolom_aantal == 1; 19 enum bool isMat = !isVec; 20 enum bool isVierkant = kolom_aantal == rij_aantal; 21 22 alias MatSoort = typeof(this); 23 24 union { 25 Soort[grootte] vec; 26 Soort[kolom_aantal][rij_aantal] mat; 27 static if (isVec) { 28 struct { 29 static if (grootte >= 1) 30 Soort x; 31 static if (grootte >= 2) 32 Soort y; 33 static if (grootte >= 3) 34 Soort z; 35 static if (grootte >= 4) 36 Soort w; 37 } 38 } 39 } 40 41 // VOEG TOE: constructer met ... arg lijst 42 43 // this(Soort[][] inhoud) { 44 // foreach (i; 0 .. rij_aantal) 45 // foreach (j; 0 .. kolom_aantal) 46 // this.mat[i][j] = inhoud[i][j]; 47 // } 48 49 static if (isVec) 50 alias vec this; 51 else 52 alias mat this; 53 54 static immutable auto nul = _bereken_nul(); 55 private static auto _bereken_nul() { 56 MatSoort resultaat; 57 static foreach (i; 0 .. grootte) 58 resultaat.vec[i] = 0; 59 return resultaat; 60 } 61 62 static if (isVierkant) { 63 static immutable auto identiteit = _bereken_identiteit(); 64 private static auto _bereken_identiteit() { 65 MatSoort resultaat; 66 static foreach (i; 0 .. rij_aantal) 67 static foreach (j; 0 .. kolom_aantal) 68 resultaat.vec[i + j * kolom_aantal] = (i == j ? 1 : 0); 69 return resultaat; 70 } 71 } 72 73 auto gekantelde() const { 74 Mat!(kolom_aantal, rij_aantal, Soort) resultaat; 75 static foreach (i; 0 .. rij_aantal) 76 static foreach (j; 0 .. kolom_aantal) 77 resultaat.mat[j][i] = this.mat[i][j]; 78 return resultaat; 79 } 80 81 static { 82 Mat!(4, 4, nauwkeurigheid) draaiMx(nauwkeurigheid hoek) { 83 Mat!(4, 4, nauwkeurigheid) draaiM = Mat!(4, 4, nauwkeurigheid).nul; 84 nauwkeurigheid cos = cos(hoek); 85 nauwkeurigheid sin = sin(hoek); 86 draaiM[0][0] = 1; 87 draaiM[1][1] = cos; 88 draaiM[1][2] = -sin; 89 draaiM[2][1] = sin; 90 draaiM[2][2] = cos; 91 draaiM[3][3] = 1; 92 return draaiM; 93 } 94 95 unittest { 96 import std.math : PI, PI_2; 97 98 Mat!4 draai = Mat!(4).draaiMx(0); 99 Mat!4 draai2 = Mat!(4).identiteit; 100 assert(draai == draai2); 101 102 draai = Mat!(4).draaiMx(PI_2); 103 draai2 = Mat!(4).nul; 104 draai2[0][0] = 1; 105 draai2[1][2] = -1; 106 draai2[2][1] = 1; 107 draai2[3][3] = 1; 108 float delta = 1e-5; 109 float verschil = (draai.elk(&abs!(float)) - draai2.elk(&abs!(float))).som(); 110 assert(verschil < delta); 111 112 draai = Mat!(4).draaiMx(PI); 113 draai2 = Mat!(4).identiteit; 114 draai2[1][1] = -1; 115 draai2[2][2] = -1; 116 verschil = (draai.elk(&abs!(float)) - draai2.elk(&abs!(float))).som(); 117 assert(verschil < delta); 118 } 119 120 Mat!(4, 4, nauwkeurigheid) draaiMy(nauwkeurigheid hoek) { 121 Mat!(4, 4, nauwkeurigheid) draaiM = Mat!(4, 4, nauwkeurigheid).nul; 122 nauwkeurigheid cos = cos(hoek); 123 nauwkeurigheid sin = sin(hoek); 124 draaiM[0][0] = cos; 125 draaiM[0][2] = sin; 126 draaiM[1][1] = 1; 127 draaiM[2][0] = -sin; 128 draaiM[2][2] = cos; 129 draaiM[3][3] = 1; 130 return draaiM; 131 } 132 133 unittest { 134 import std.math : PI, PI_2; 135 136 Mat!4 draai = Mat!(4).draaiMy(0); 137 Mat!4 draai2 = Mat!(4).identiteit; 138 assert(draai == draai2); 139 140 draai = Mat!(4).draaiMy(PI_2); 141 draai2 = Mat!(4).nul; 142 draai2[0][2] = 1; 143 draai2[1][1] = 1; 144 draai2[2][0] = -1; 145 draai2[3][3] = 1; 146 float delta = 1e-5; 147 float verschil = (draai.elk(&abs!(float)) - draai2.elk(&abs!(float))).som(); 148 assert(verschil < delta); 149 150 draai = Mat!(4).draaiMy(PI); 151 draai2 = Mat!(4).identiteit; 152 draai2[0][0] = -1; 153 draai2[2][2] = -1; 154 verschil = (draai.elk(&abs!(float)) - draai2.elk(&abs!(float))).som(); 155 assert(verschil < delta); 156 } 157 158 Mat!(4, 4, nauwkeurigheid) draaiMz(nauwkeurigheid hoek) { 159 Mat!(4, 4, nauwkeurigheid) draaiM = Mat!(4, 4, nauwkeurigheid).nul; 160 nauwkeurigheid cos = cos(hoek); 161 nauwkeurigheid sin = sin(hoek); 162 draaiM[0][0] = cos; 163 draaiM[0][1] = -sin; 164 draaiM[1][0] = sin; 165 draaiM[1][1] = cos; 166 draaiM[2][2] = 1; 167 draaiM[3][3] = 1; 168 return draaiM; 169 } 170 171 unittest { 172 import std.math : PI, PI_2; 173 174 Mat!4 draai = Mat!(4).draaiMz(0); 175 Mat!4 draai2 = Mat!(4).identiteit; 176 assert(draai == draai2); 177 178 draai = Mat!(4).draaiMz(PI_2); 179 draai2[0][0] = 0; 180 draai2[0][1] = -1; 181 draai2[1][0] = 1; 182 draai2[1][1] = 0; 183 float delta = 1e-5; 184 float verschil = (draai.elk(&abs!(float)) - draai2.elk(&abs!(float))).som(); 185 assert(verschil < delta); 186 187 draai = Mat!(4).draaiMz(PI); 188 draai2 = Mat!(4).identiteit; 189 draai2[0][0] = -1; 190 draai2[1][1] = -1; 191 verschil = (draai.elk(&abs!(float)) - draai2.elk(&abs!(float))).som(); 192 assert(verschil < delta); 193 } 194 } 195 196 static if (isVec) { 197 auto inp(Resultaat = Soort, R: 198 Mat!(rij_aantal, 1, T), T)(const R rechts) const { 199 Resultaat resultaat = 0; 200 static foreach (i; 0 .. grootte) 201 resultaat.vec[i] += this.vec[i] * rechts.vec[i]; 202 return resultaat; 203 } 204 205 static if (rij_aantal == 3) { 206 auto uitp(R : Mat!(rij_aantal, 1, T), T)(const R rechts) const { 207 Mat!(rij_aantal, 1, typeof(Soort.init * T.init)) resultaat; 208 resultaat.vec[0] = this.vec[1] * rechts.vec[2] - rechts.vec[1] * this.vec[2]; 209 resultaat.vec[1] = this.vec[2] * rechts.vec[0] - rechts.vec[2] * this.vec[0]; 210 resultaat.vec[2] = this.vec[0] * rechts.vec[1] - rechts.vec[0] * this.vec[1]; 211 return resultaat; 212 } 213 } 214 215 auto lengte(T = nauwkeurigheid)() const { 216 T l = 0; 217 static foreach (i; 0 .. rij_aantal) { 218 l += this.vec[i]; 219 } 220 return l; 221 } 222 223 auto normaliseer(T = nauwkeurigheid)() const { 224 Mat!(rij_aantal, kolom_aantal, T) n; 225 n.vec[] = this.vec[]; 226 n = n * cast(T)(1 / this.lengte()); 227 return n; 228 } 229 } 230 231 static if (isMat) { 232 // auto maal(T, R: 233 // Mat!(kolom_aantal, 1, T))(R rechts) { 234 // alias Onderdeel2 = typeof(Soort * T); 235 // Vec!(rij_aantal, Onderdeel2) resultaat = Vec!(R, Onderdeel2).nul; 236 // static foreach (i; 0 .. rij_aantal) 237 // static foreach (j; 0 .. rij_aantal) 238 // resultaat.vec[i] += this.mat[i][j] * rechts.vec[j]; 239 // return resultaat; 240 // } Is het zelfde als: 241 242 auto maal(T, uint K)(const T[K][kolom_aantal] rechts) const 243 if (is(Resultaat!(Soort, "*", T))) { 244 alias Onderdeel2 = Resultaat!(Soort, "*", T); 245 Mat!(rij_aantal, K, Onderdeel2) resultaat = Mat!(rij_aantal, K, Onderdeel2).nul; 246 static foreach (i; 0 .. rij_aantal) 247 static foreach (j; 0 .. K) 248 static foreach (k; 0 .. kolom_aantal) 249 resultaat.mat[i][j] += this.mat[i][k] * rechts[k][j]; 250 return resultaat; 251 } 252 253 auto maal(T)(const T[kolom_aantal] rechts) const 254 if (is(Resultaat!(Soort, "*", T))) { 255 return maal!(T, 1)(cast(T[1][kolom_aantal]) rechts); 256 } 257 258 unittest { 259 import std.math : PI_2; 260 261 Mat!4 a = Mat!4.draaiMz(PI_2); 262 Vec!4 resultaat = a.maal([1, 0, 0, 1]); 263 int[4] b = [0, 1, 0, 1]; 264 float verschil = (resultaat.elk(&abs!(float)) - b).som(); 265 float delta = 1e-5; 266 assert(verschil < delta); 267 268 Mat!(2, 3) c; 269 Mat!(3, 2) d; 270 foreach (i; 0 .. c.grootte) { 271 c.vec[i] = i + 1; 272 d.vec[i] = i + 1; 273 } 274 auto c_gevonden = c.maal(c.gekantelde); 275 assert(is(typeof(c_gevonden) == Mat!2)); 276 Mat!2 c_verwacht = Mat!2([14, 32, 32, 77]); 277 assert(c_gevonden == c_verwacht, c_gevonden.toString() ~ "\n!=\n" ~ c_verwacht.toString()); 278 279 auto d_gevonden = c.maal(d); 280 assert(is(typeof(d_gevonden) == Mat!2)); 281 Mat!2 d_verwacht = Mat!2([22, 28, 49, 64]); 282 assert(d_gevonden == d_verwacht); 283 } 284 285 unittest { 286 Mat!(2, 3, int) a; 287 Mat!(3, 5, float) b; 288 foreach (i; 0 .. 6) 289 a.vec[i] = i; 290 foreach (i; 0 .. 15) 291 b.vec[i] = -i; 292 auto c = a.maal(b); 293 scope (failure) 294 writefln("c:\n%l", c); 295 296 assert(is(typeof(c) == Mat!(2, 5, float))); 297 Mat!(2, 5, float) d; 298 foreach (i; 0 .. 5) 299 d.vec[i] = -(25 + 3 * i); 300 foreach (i; 0 .. 5) 301 d.vec[i + 5] = -(70 + 12 * i); 302 scope (failure) 303 writefln("d:\n%l", d); 304 305 assert(c == d); 306 } 307 } 308 309 auto som(T = nauwkeurigheid)() const { 310 T resultaat = 0; 311 static foreach (i; 0 .. grootte) { 312 resultaat += this.vec[i]; 313 } 314 return resultaat; 315 } 316 317 unittest { 318 Mat!2 a; 319 foreach (i; 0 .. a.grootte) 320 a.vec[i] = 2 * i; 321 assert(a.som() == 12); 322 } 323 324 auto elk(S1, S2)(S1 function(S2 onderdeel) functie) const if (!is(S1 == void)) { 325 Mat!(rij_aantal, kolom_aantal, S1) resultaat; 326 static foreach (i; 0 .. grootte) 327 resultaat.vec[i] = functie(this.vec[i]); 328 return resultaat; 329 } 330 331 void elk(S)(void function(S onderdeel) functie) const { 332 static foreach (i; 0 .. grootte) 333 functie(this.vec[i]); 334 } 335 336 unittest { 337 bool even(int getal) { 338 return getal % 2 == 0; 339 } 340 341 Mat!(2, 3, int) a; 342 foreach (i; 0 .. a.grootte) 343 a.vec[i] = i; 344 auto b = a.elk(&even); 345 assert(is(typeof(b) == Mat!(2, 3, bool))); 346 foreach (i; 0 .. b.grootte) 347 assert(b.vec[i] == (i % 2 == 0)); 348 } 349 350 auto elk(S1, S2)(S1 delegate(S2 onderdeel) functie) const if (!is(S1 == void)) { 351 Mat!(rij_aantal, kolom_aantal, S1) resultaat; 352 static foreach (i; 0 .. grootte) 353 resultaat.vec[i] = functie(this.vec[i]); 354 return resultaat; 355 } 356 357 void elk(S)(void delegate(S onderdeel) functie) const { 358 static foreach (i; 0 .. grootte) 359 functie(this.vec[i]); 360 } 361 362 unittest { 363 Vec!5 a; 364 uint i = 0; 365 void overschrijven(float getal) { 366 a.vec[i++] = getal; 367 } 368 369 Vec!5 b; 370 foreach (j; 0 .. b.grootte) 371 b.vec[j] = 5 - j; 372 Vec!5 c = b; 373 b.elk(&overschrijven); 374 assert(a == b, "a: " ~ a.toString(true) ~ "\nb: " ~ b.toString(true)); 375 assert(b == c); 376 } 377 378 auto opBinary(string op, T)(const T rechts) const 379 if (!isLijst!T) { 380 Mat!(rij_aantal, kolom_aantal, Resultaat!(Soort, op, T)) resultaat; 381 static foreach (i; 0 .. grootte) 382 mixin("resultaat.vec[i] = this.vec[i] " ~ op ~ " rechts;"); 383 return resultaat; 384 } 385 386 unittest { 387 Mat!5 a = Mat!5.nul; 388 a = (a + 2) * 3; 389 foreach (i; 0 .. a.grootte) 390 assert(a.vec[i] == 6); 391 } 392 393 static if (isVec) { 394 // PAS OP: Wegens onduidelijke reden is gebruik van letterlijke lijsten momenteel niet goed 395 // ondersdoor templates. Zie https://forum.dlang.org/post/sfgv2a$u08$1@digitalmars.com voor meer. 396 auto opBinary(string op, T: 397 U[grootte], U)(const T rechts) const 398 if (isLijst!T && !isLijst!(T, 2)) { 399 Vec!(grootte, Resultaat!(Soort, op, U)) resultaat; 400 static foreach (i; 0 .. grootte) 401 mixin("resultaat.vec[i] = this.vec[i] " ~ op ~ " rechts[i];"); 402 return resultaat; 403 } 404 } 405 406 unittest { 407 Vec!5 a = Vec!5([1, 2, 3, 4, 5]); 408 int[5] b = [5, 4, 3, 2, 1]; 409 a = a - b; 410 foreach (i; 0 .. 4) 411 assert(a[i] == -4 + 2 * i, to!string(a)); 412 } 413 414 auto opBinary(string op, T: 415 U[kolom_aantal][rij_aantal], U)(const T rechts) const 416 if (isLijst!(T, 2)) { 417 Mat!(rij_aantal, kolom_aantal, Resultaat!(Soort, op, U)) resultaat; 418 static foreach (i; 0 .. rij_aantal) 419 static foreach (j; 0 .. kolom_aantal) 420 mixin("resultaat.mat[i][j] = this.mat[i][j] " ~ op ~ " rechts[i][j];"); 421 return resultaat; 422 } 423 424 unittest { 425 int x = 0; // PAS OP: Wegens onduidelijke redenen vereist voor de leesbaarheid van a. 426 Mat!(3, 3, float) a = Mat!(3, 3, float)([1, 2, 3, 4, 5, 6, 7, 8, 9]); 427 Mat!(3, 3, double) b = Mat!(3, 3, double).identiteit; 428 429 auto c = a + b; 430 assert(c.isSoort!(Mat!(3, 3, double))); 431 foreach (i; 0 .. 9) { 432 auto verwacht = i + 1 + (i % 4 == 0); 433 assert(c.vec[i] == verwacht); 434 } 435 436 auto d = c - a; 437 assert(d.isSoort(c)); 438 assert(d == b); 439 440 auto e = a * b; 441 assert(c.isSoort(b)); 442 foreach (i; 0 .. 9) { 443 auto verwacht = (i + 1) * (i % 4 == 0); 444 assert(e.vec[i] == verwacht); 445 } 446 } 447 448 M opCast(M : Mat!(rij_aantal, kolom_aantal, T), T)() const { 449 M resultaat; 450 static foreach (i; 0 .. grootte) 451 resultaat.vec[i] = cast(T) this.vec[i]; 452 return resultaat; 453 } 454 455 unittest { 456 Mat!(2, 3, float) a = Mat!(2, 3, float)([1.0f, 2, 3, 4, 5, 6]); 457 Mat!(2, 3, int) b = cast(Mat!(2, 3, int)) a; 458 static foreach (i; 0 .. 6) 459 assert(b.vec[i] == cast(int) a.vec[i]); 460 } 461 462 import std.format : FormatSpec; 463 import std.range : put; 464 import std.conv : to; 465 466 string toString(bool mooi = false) const { 467 char[] cs; 468 cs.reserve(6 * grootte); 469 cs ~= '{'; 470 static foreach (i; 0 .. rij_aantal) { 471 cs ~= '['; 472 static foreach (j; 0 .. kolom_aantal) { 473 cs ~= this.mat[i][j].to!string; 474 static if (j != kolom_aantal - 1) 475 cs ~= ", "; 476 } 477 static if (i != rij_aantal - 1) 478 cs ~= mooi ? "],\n " : "], "; 479 else 480 cs ~= ']'; 481 } 482 cs ~= '}'; 483 return cast(string) cs[0 .. $]; 484 } 485 486 void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) const { 487 sink.put("["); 488 const string tussenvoegsel = fmt.spec == 'l' ? ",\n " : ", "; 489 static foreach (i; 0 .. rij_aantal - 1) { 490 sink.put(mat[i].to!string); 491 sink.put(tussenvoegsel); 492 } 493 static if (rij_aantal > 0) 494 sink.put(mat[rij_aantal - 1].to!string); 495 sink.put("]"); 496 } 497 498 bool opEquals(ref const MatSoort ander) const @safe pure nothrow { 499 foreach (uint i; 0 .. grootte) 500 if (this.vec[i] != ander.vec[i]) 501 return false; 502 return true; 503 } 504 505 static if (isVec) { 506 bool opEquals(const Soort[grootte] ander) const @safe pure nothrow { 507 foreach (uint i; 0 .. grootte) 508 if (this.vec[i] != ander[i]) 509 return false; 510 return true; 511 } 512 } 513 514 bool opEquals(const Soort[kolom_aantal][rij_aantal] ander) const @safe pure nothrow { 515 foreach (uint i; 0 .. rij_aantal) 516 foreach (uint j; 0 .. kolom_aantal) 517 if (this.mat[i][j] != ander[i][j]) 518 return false; 519 return true; 520 } 521 522 // Hashes vereist voor associatieve lijsten. 523 static if (is(Soort == byte) || 524 is(Soort == ubyte) || 525 is(Soort == short) || 526 is(Soort == ushort) || 527 is(Soort == int) || 528 is(Soort == uint) || 529 is(Soort == long) || 530 is(Soort == ulong)) { 531 size_t toHash() const @safe pure nothrow { 532 size_t hash = 1; 533 foreach (Soort s; this.vec) 534 hash = 31 * hash + s; 535 return hash; 536 } 537 } 538 539 static if (is(Soort == bool)) { 540 size_t toHash() const @safe pure nothrow { 541 size_t hash = 1; 542 foreach (Soort s; this.vec) 543 hash = 31 * hash + s ? 5 : 3; 544 return hash; 545 } 546 } 547 548 static if (is(Soort == float)) { 549 private static int _castFloatInt(const float f) @trusted { 550 return *cast(int*)&f; 551 } 552 553 size_t toHash() const @safe pure nothrow { 554 size_t hash = 1; 555 foreach (Soort s; this.vec) 556 hash = 31 * hash + _castFloatInt(s); // Reinterpret as int 557 return hash; 558 } 559 } 560 561 static if (is(Soort == double)) { 562 private static long _castDoubleLong(const double d) @trusted { 563 return *cast(long*)&d; 564 } 565 566 size_t toHash() const @safe pure nothrow { 567 size_t hash = 1; 568 foreach (Soort s; this.vec) 569 hash = 31 * hash + _castDoubleLong(s); // Reinterpret as long 570 return hash; 571 } 572 } 573 574 unittest { 575 auto const mat1 = Mat!(1, 2, int)([1, 2]); 576 auto const mat2 = Mat!(1, 2, int)([1, 2]); 577 auto const mat3 = Mat!(1, 2, int)([2, 1]); 578 assert(mat1 == mat1); 579 assert(mat1 == mat2); 580 assert(mat1 != mat3); 581 582 auto const mat4 = Mat!(1, 2, float)([1.0, 2.0]); 583 auto const mat5 = Mat!(2, 1, int)([1, 2]); 584 assert(!is(typeof(mat1 != mat4))); 585 assert(!is(typeof(mat1 != mat5))); 586 587 auto const vec1 = Vec!(2, float)([1.0, 2.0]); 588 auto const mat6 = Mat!(2, 1, float)([1.0, 2.0]); 589 assert(!is(typeof(mat1 == vec1))); 590 assert(vec1 == mat6); 591 592 assert(mat1 == [[1, 2]]); 593 assert(!is(typeof(mat1 == [1, 2]))); 594 assert(vec1 == [1.0f, 2.0f]); 595 assert(vec1 == [[1.0f], [2.0f]]); 596 } 597 598 static if (is(typeof(abs!Soort))) { 599 bool isOngeveer(const MatSoort ander, nauwkeurigheid delta = 1e-5) const { 600 import std.math : abs; 601 602 return (this - ander).elk(&abs!Soort).som() < delta; 603 } 604 } 605 606 unittest { 607 float delta = 1e-6; 608 Mat!3 a = Mat!(3).identiteit; 609 float[3][3] verschil = [ 610 [delta, -delta, delta], [delta, delta, -delta], 611 [-delta, -delta, delta] 612 ]; 613 Mat!3 b = a + verschil; 614 assert(a.isOngeveer(b)); 615 assert(!a.isOngeveer(b, 8 * delta)); 616 } 617 618 // Krijg de draai nodig om een vector vanaf de y as in een richting te draai. 619 // Hierbij wordt eerst de x draai toegepast gevolgd door de z draai. 620 // Er is dus geen sprake van draai om de y as. 621 // (Denk hierbij dus aan stamping gevolgd door giering, ook wel 'pitch' gevolgd door 'yaw') 622 static Vec!3 krijgDraai(Vec!3 richting) { 623 import std.math : acos, atan, PI_2, signbit; 624 625 if (richting.x == 0 && richting.y == 0) { 626 if (richting.z == 0) 627 return richting; // [0,0,0] -> [0,0,0] 628 return Vec!3([PI_2, 0, 0]); // [0,0,z] -> [PI/2,0,0] 629 } 630 nauwkeurigheid R = Vec!2([richting.x, richting.y]).lengte(); 631 // [x,y,z] -> [atan(z/sqrt(x²+y²)),0,-teken(x)*acos(y/sqrt(x²+y²))] 632 return Vec!3([ 633 atan(richting.z / R), 0, 634 -signbit(richting.x) * acos(richting.y / R) 635 ]); 636 } 637 638 // VOEG TOE: test 639 640 // Het omgekeerde van krijgDraai. 641 // Draai om de y as wordt aangenomen als 0 aangzien dit geen invloed heeft. 642 static Vec!3 krijgRichting(Vec!3 draai) { 643 import std.math : sin, cos; 644 645 return Vec!3([-sin(draai.z), cos(draai.z), sin(draai.x)]); 646 } 647 648 // VOEG TOE: test 649 }