1 /* 2 Copyright (c) 2013-2021 Timur Gafarov, Martin Cejp 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 /** 30 * Matrix-based geometric transformations 31 * 32 * Copyright: Timur Gafarov 2013-2021. 33 * License: $(LINK2 boost.org/LICENSE_1_0.txt, Boost License 1.0). 34 * Authors: Timur Gafarov 35 */ 36 module dlib.math.transformation; 37 38 import std.math; 39 40 import dlib.math.utils; 41 import dlib.math.vector; 42 import dlib.math.matrix; 43 import dlib.math.quaternion; 44 45 /** 46 * Setup a rotation matrix, given Euler angles in radians 47 */ 48 Matrix!(T,4) fromEuler(T) (Vector!(T,3) v) 49 { 50 auto res = Matrix!(T,4).identity; 51 52 T cx = cos(v.x); 53 T sx = sin(v.x); 54 T cy = cos(v.y); 55 T sy = sin(v.y); 56 T cz = cos(v.z); 57 T sz = sin(v.z); 58 59 T sxsy = sx * sy; 60 T cxsy = cx * sy; 61 62 res.a11 = (cy * cz); 63 res.a12 = (sxsy * cz) + (cx * sz); 64 res.a13 = -(cxsy * cz) + (sx * sz); 65 66 res.a21 = -(cy * sz); 67 res.a22 = -(sxsy * sz) + (cx * cz); 68 res.a23 = (cxsy * sz) + (sx * cz); 69 70 res.a31 = (sy); 71 res.a32 = -(sx * cy); 72 res.a33 = (cx * cy); 73 74 return res; 75 } 76 77 /** 78 * Setup the Euler angles in radians, given a rotation matrix 79 */ 80 Vector!(T,3) toEuler(T) (Matrix!(T,4) m) 81 do 82 { 83 Vector!(T,3) v; 84 85 v.y = asin(m.a31); 86 87 T cy = cos(v.y); 88 T oneOverCosY = 1.0 / cy; 89 90 if (fabs(cy) > 0.001) 91 { 92 v.x = atan2(-m.a32 * oneOverCosY, m.a33 * oneOverCosY); 93 v.z = atan2(-m.a21 * oneOverCosY, m.a11 * oneOverCosY); 94 } 95 else 96 { 97 v.x = 0.0; 98 v.z = atan2(m.a12, m.a22); 99 } 100 101 return v; 102 } 103 104 /** 105 * Right vector of the matrix 106 */ 107 Vector!(T,3) right(T) (Matrix!(T,4) m) 108 do 109 { 110 return Vector!(T,3)(m.a11, m.a21, m.a31); 111 } 112 113 /** 114 * Up vector of the matrix 115 */ 116 Vector!(T,3) up(T) (Matrix!(T,4) m) 117 do 118 { 119 return Vector!(T,3)(m.a12, m.a22, m.a32); 120 } 121 122 /** 123 * Forward vector of the matrix 124 */ 125 Vector!(T,3) forward(T) (Matrix!(T,4) m) 126 do 127 { 128 return Vector!(T,3)(m.a13, m.a23, m.a33); 129 } 130 131 /** 132 * Translation vector of the matrix 133 */ 134 Vector!(T,3) translation(T) (Matrix!(T,4) m) 135 do 136 { 137 return Vector!(T,3)(m.a14, m.a24, m.a34); 138 } 139 140 /// 141 unittest 142 { 143 Matrix4f tm = matrixf( 144 1.0f, 0.0f, 0.0f, 3.0f, 145 0.0f, 1.0f, 0.0f, 5.0f, 146 0.0f, 0.0f, 1.0f, 2.5f, 147 0.0f, 0.0f, 0.0f, 1.0f, 148 ); 149 150 Vector3f t = translation(tm); 151 152 assert(t == Vector3f(3.0f, 5.0f, 2.5f)); 153 } 154 155 /** 156 * Scaling vector of the matrix 157 */ 158 Vector!(T,3) scaling(T) (Matrix!(T,4) m) 159 do 160 { 161 T sx = Vector!(T,3)(m.a11, m.a12, m.a13).length; 162 T sy = Vector!(T,3)(m.a21, m.a22, m.a23).length; 163 T sz = Vector!(T,3)(m.a31, m.a32, m.a33).length; 164 return Vector!(T,3)(sx, sy, sz); 165 } 166 167 /** 168 * Create a matrix to perform a rotation about a world axis 169 * (theta in radians) 170 */ 171 Matrix!(T,4) rotationMatrix(T) (uint rotaxis, T theta) 172 do 173 { 174 auto res = Matrix!(T,4).identity; 175 176 T s = sin(theta); 177 T c = cos(theta); 178 179 switch (rotaxis) 180 { 181 case Axis.x: 182 res.a11 = 1.0; res.a12 = 0.0; res.a13 = 0.0; 183 res.a21 = 0.0; res.a22 = c; res.a23 = s; 184 res.a31 = 0.0; res.a32 = -s; res.a33 = c; 185 break; 186 187 case Axis.y: 188 res.a11 = c; res.a12 = 0.0; res.a13 = -s; 189 res.a21 = 0.0; res.a22 = 1.0; res.a23 = 0.0; 190 res.a31 = s; res.a32 = 0.0; res.a33 = c; 191 break; 192 193 case Axis.z: 194 res.a11 = c; res.a12 = s; res.a13 = 0.0; 195 res.a21 = -s; res.a22 = c; res.a23 = 0.0; 196 res.a31 = 0.0; res.a32 = 0.0; res.a33 = 1.0; 197 break; 198 199 default: 200 assert(0); 201 } 202 203 return res; 204 } 205 206 /** 207 * Create a translation matrix given a translation vector 208 */ 209 Matrix!(T,4) translationMatrix(T) (Vector!(T,3) v) 210 do 211 { 212 auto res = Matrix!(T,4).identity; 213 res.a14 = v.x; 214 res.a24 = v.y; 215 res.a34 = v.z; 216 return res; 217 } 218 219 /// 220 unittest 221 { 222 Matrix4f tm = translationMatrix(Vector3f(3.0f, 5.0f, 2.5f)); 223 Vector3f t = translation(tm); 224 assert(t == Vector3f(3.0f, 5.0f, 2.5f)); 225 } 226 227 /** 228 * Create a matrix to perform scale on each axis 229 */ 230 Matrix!(T,4) scaleMatrix(T) (Vector!(T,3) v) 231 do 232 { 233 auto res = Matrix!(T,4).identity; 234 res.a11 = v.x; 235 res.a22 = v.y; 236 res.a33 = v.z; 237 return res; 238 } 239 240 /** 241 * Setup the matrix to perform scale along an arbitrary axis 242 */ 243 Matrix!(T,4) scaleAlongAxisMatrix(T) (Vector!(T,3) scaleAxis, T k) 244 in 245 { 246 assert (fabs (dot(scaleAxis, scaleAxis) - 1.0) < 0.001); 247 } 248 do 249 { 250 auto res = Matrix!(T,4).identity; 251 252 T a = k - 1.0; 253 T ax = a * scaleAxis.x; 254 T ay = a * scaleAxis.y; 255 T az = a * scaleAxis.z; 256 257 res.a11 = (ax * scaleAxis.x) + 1.0; 258 res.a22 = (ay * scaleAxis.y) + 1.0; 259 res.a33 = (az * scaleAxis.z) + 1.0; 260 261 res.a12 = res.a21 = (ax * scaleAxis.y); 262 res.a13 = res.a31 = (ax * scaleAxis.z); 263 res.a23 = res.a32 = (ay * scaleAxis.z); 264 265 return res; 266 } 267 268 /** 269 * Setup the matrix to perform a shear 270 */ 271 Matrix!(T,4) shearMatrix(T) (uint shearAxis, T s, T t) 272 do 273 { 274 // NOTE: needs test 275 auto res = Matrix!(T,4).identity; 276 277 switch (shearAxis) 278 { 279 case Axis.x: 280 res.a11 = 1.0; res.a12 = 0.0; res.a13 = 0.0; 281 res.a21 = s; res.a22 = 1.0; res.a23 = 0.0; 282 res.a31 = t; res.a32 = 0.0; res.a33 = 1.0; 283 break; 284 285 case Axis.y: 286 res.a11 = 1.0; res.a12 = s; res.a13 = 0.0; 287 res.a21 = 0.0; res.a22 = 1.0; res.a23 = 0.0; 288 res.a31 = 0.0; res.a32 = t; res.a33 = 1.0; 289 break; 290 291 case Axis.z: 292 res.a11 = 1.0; res.a12 = 0.0; res.a13 = s; 293 res.a21 = 0.0; res.a22 = 1.0; res.a23 = t; 294 res.a31 = 0.0; res.a32 = 0.0; res.a33 = 1.0; 295 break; 296 297 default: 298 assert(0); 299 } 300 301 return res; 302 } 303 304 /** 305 * Setup the matrix to perform a projection onto a plane passing 306 * through the origin. The plane is perpendicular to the 307 * unit vector n. 308 */ 309 Matrix!(T,4) projectionMatrix(T) (Vector!(T,3) n) 310 in 311 { 312 assert (fabs(dot(n, n) - 1.0) < 0.001); 313 } 314 do 315 { 316 // NOTE: needs test 317 auto res = Matrix!(T,4).identity; 318 319 res.a11 = 1.0 - (n.x * n.x); 320 res.a22 = 1.0 - (n.y * n.y); 321 res.a33 = 1.0 - (n.z * n.z); 322 323 res.a12 = res.a21 = -(n.x * n.y); 324 res.a13 = res.a31 = -(n.x * n.z); 325 res.a23 = res.a32 = -(n.y * n.z); 326 327 return res; 328 } 329 330 /** 331 * Setup the matrix to perform a reflection about a plane parallel 332 * to a cardinal plane. 333 */ 334 Matrix!(T,4) reflectionMatrix(T) (Axis reflectionAxis, T k) 335 do 336 { 337 auto res = Matrix!(T,4).identity; 338 339 switch (reflectionAxis) 340 { 341 case Axis.x: 342 res.a11 = -1.0; res.a21 = 0.0; res.a31 = 0.0; res.a41 = 2.0 * k; 343 res.a12 = 0.0; res.a22 = 1.0; res.a32 = 0.0; res.a42 = 0.0; 344 res.a13 = 0.0; res.a23 = 0.0; res.a33 = 1.0; res.a43 = 0.0; 345 break; 346 347 case Axis.y: 348 res.a11 = 1.0; res.a21 = 0.0; res.a31 = 0.0; res.a41 = 0.0; 349 res.a12 = 0.0; res.a22 = -1.0; res.a32 = 0.0; res.a42 = 2.0 * k; 350 res.a13 = 0.0; res.a23 = 0.0; res.a33 = 1.0; res.a43 = 0.0; 351 break; 352 353 case Axis.z: 354 res.a11 = 1.0; res.a21 = 0.0; res.a31 = 0.0; res.a41 = 0.0; 355 res.a12 = 0.0; res.a22 = 1.0; res.a32 = 0.0; res.a42 = 0.0; 356 res.a13 = 0.0; res.a23 = 0.0; res.a33 = -1.0; res.a43 = 2.0 * k; 357 break; 358 359 default: 360 assert(0); 361 } 362 363 return res; 364 } 365 366 /** 367 * Setup the matrix to perform a reflection about an arbitrary plane 368 * through the origin. The unit vector n is perpendicular to the plane. 369 */ 370 Matrix!(T,4) axisReflectionMatrix(T) (Vector!(T,3) n) 371 in 372 { 373 assert (fabs(dot(n, n) - 1.0) < 0.001); 374 } 375 do 376 { 377 auto res = Matrix!(T,4).identity; 378 379 T ax = -2.0 * n.x; 380 T ay = -2.0 * n.y; 381 T az = -2.0 * n.z; 382 383 res.a11 = 1.0 + (ax * n.x); 384 res.a22 = 1.0 + (ay * n.y); 385 res.a32 = 1.0 + (az * n.z); 386 387 res.a12 = res.a21 = (ax * n.y); 388 res.a13 = res.a31 = (ax * n.z); 389 res.a23 = res.a32 = (ay * n.z); 390 391 return res; 392 } 393 394 /** 395 * Setup the matrix to perform a "Look At" transformation 396 * like a first person camera 397 */ 398 Matrix!(T,4) lookAtMatrix(T) (Vector!(T,3) eye, Vector!(T,3) center, Vector!(T,3) up) 399 do 400 { 401 auto Result = Matrix!(T,4).identity; 402 403 auto f = (center - eye).normalized; 404 auto u = (up).normalized; 405 auto s = cross(f, u).normalized; 406 u = cross(s, f); 407 408 Result[0,0] = s.x; 409 Result[0,1] = s.y; 410 Result[0,2] = s.z; 411 Result[1,0] = u.x; 412 Result[1,1] = u.y; 413 Result[1,2] = u.z; 414 Result[2,0] =-f.x; 415 Result[2,1] =-f.y; 416 Result[2,2] =-f.z; 417 Result[0,3] =-dot(s, eye); 418 Result[1,3] =-dot(u, eye); 419 Result[2,3] = dot(f, eye); 420 return Result; 421 } 422 423 /** 424 * Setup a frustum matrix given the left, right, bottom, top, near, and far 425 * values for the frustum boundaries. 426 */ 427 Matrix!(T,4) frustumMatrix(T) (T l, T r, T b, T t, T n, T f) 428 in 429 { 430 assert (n >= 0.0); 431 assert (f >= 0.0); 432 } 433 do 434 { 435 auto res = Matrix!(T,4).identity; 436 437 T width = r - l; 438 T height = t - b; 439 T depth = f - n; 440 441 res.arrayof[0] = (2 * n) / width; 442 res.arrayof[1] = 0.0; 443 res.arrayof[2] = 0.0; 444 res.arrayof[3] = 0.0; 445 446 res.arrayof[4] = 0.0; 447 res.arrayof[5] = (2 * n) / height; 448 res.arrayof[6] = 0.0; 449 res.arrayof[7] = 0.0; 450 451 res.arrayof[8] = (r + l) / width; 452 res.arrayof[9] = (t + b) / height; 453 res.arrayof[10]= -(f + n) / depth; 454 res.arrayof[11]= -1.0; 455 456 res.arrayof[12]= 0.0; 457 res.arrayof[13]= 0.0; 458 res.arrayof[14]= -(2 * f * n) / depth; 459 res.arrayof[15]= 0.0; 460 461 return res; 462 } 463 464 /** 465 * Setup a perspective matrix given the field-of-view in the Y direction 466 * in degrees, the aspect ratio of Y/X, and near and far plane distances 467 */ 468 Matrix!(T,4) perspectiveMatrix(T) (T fovY, T aspect, T n, T f) 469 do 470 { 471 auto res = Matrix!(T,4).identity; 472 473 T angle; 474 T cot; 475 476 angle = fovY / 2.0; 477 angle = degtorad(angle); 478 479 cot = cos(angle) / sin(angle); 480 481 res.arrayof[0] = cot / aspect; 482 res.arrayof[1] = 0.0; 483 res.arrayof[2] = 0.0; 484 res.arrayof[3] = 0.0; 485 486 res.arrayof[4] = 0.0; 487 res.arrayof[5] = cot; 488 res.arrayof[6] = 0.0; 489 res.arrayof[7] = 0.0; 490 491 res.arrayof[8] = 0.0; 492 res.arrayof[9] = 0.0; 493 res.arrayof[10]= -(f + n) / (f - n); 494 res.arrayof[11]= -1.0f; //-(2 * f * n) / (f - n); 495 496 res.arrayof[12]= 0.0; 497 res.arrayof[13]= 0.0; 498 res.arrayof[14]= -(2 * f * n) / (f - n); //-1.0; 499 res.arrayof[15]= 0.0; 500 501 return res; 502 } 503 504 /** 505 * Setup an orthographic Matrix4x4 given the left, right, bottom, top, near, 506 * and far values for the frustum boundaries. 507 */ 508 Matrix!(T,4) orthoMatrix(T) (T l, T r, T b, T t, T n, T f) 509 do 510 { 511 auto res = Matrix!(T,4).identity; 512 513 T width = r - l; 514 T height = t - b; 515 T depth = f - n; 516 517 res.arrayof[0] = 2.0 / width; 518 res.arrayof[1] = 0.0; 519 res.arrayof[2] = 0.0; 520 res.arrayof[3] = 0.0; 521 522 res.arrayof[4] = 0.0; 523 res.arrayof[5] = 2.0 / height; 524 res.arrayof[6] = 0.0; 525 res.arrayof[7] = 0.0; 526 527 res.arrayof[8] = 0.0; 528 res.arrayof[9] = 0.0; 529 res.arrayof[10]= -2.0 / depth; 530 res.arrayof[11]= 0.0; 531 532 res.arrayof[12]= -(r + l) / width; 533 res.arrayof[13]= -(t + b) / height; 534 res.arrayof[14]= -(f + n) / depth; 535 res.arrayof[15]= 1.0; 536 537 return res; 538 } 539 540 /** 541 * Setup an orientation matrix using 3 basis normalized vectors 542 */ 543 Matrix!(T,4) orthoNormalMatrix(T) (Vector!(T,3) xdir, Vector!(T,3) ydir, Vector!(T,3) zdir) 544 do 545 { 546 auto res = Matrix!(T,4).identity; 547 548 res.arrayof[0] = xdir.x; res.arrayof[4] = ydir.x; res.arrayof[8] = zdir.x; res.arrayof[12] = 0.0; 549 res.arrayof[1] = xdir.y; res.arrayof[5] = ydir.y; res.arrayof[9] = zdir.y; res.arrayof[13] = 0.0; 550 res.arrayof[2] = xdir.z; res.arrayof[6] = ydir.z; res.arrayof[10]= zdir.z; res.arrayof[14] = 0.0; 551 res.arrayof[3] = 0.0; res.arrayof[7] = 0.0; res.arrayof[11]= 0.0; res.arrayof[15] = 1.0; 552 553 return res; 554 } 555 556 /** 557 * Setup a matrix that flattens geometry into a plane, 558 * as if it were casting a shadow from a light 559 */ 560 Matrix!(T,4) shadowMatrix(T) (Vector!(T,4) groundplane, Vector!(T,4) lightpos) 561 { 562 T d = dot(groundplane, lightpos); 563 564 auto res = Matrix!(T,4).identity; 565 566 res.a11 = d-lightpos.x * groundplane.x; 567 res.a12 = -lightpos.x * groundplane.y; 568 res.a13 = -lightpos.x * groundplane.z; 569 res.a14 = -lightpos.x * groundplane.w; 570 571 res.a21 = -lightpos.y * groundplane.x; 572 res.a22 = d-lightpos.y * groundplane.y; 573 res.a23 = -lightpos.y * groundplane.z; 574 res.a24 = -lightpos.y * groundplane.w; 575 576 res.a31 = -lightpos.z * groundplane.x; 577 res.a32 = -lightpos.z * groundplane.y; 578 res.a33 = d-lightpos.z * groundplane.z; 579 res.a34 = -lightpos.z * groundplane.w; 580 581 res.a41 = -lightpos.w * groundplane.x; 582 res.a42 = -lightpos.w * groundplane.y; 583 res.a43 = -lightpos.w * groundplane.z; 584 res.a44 = d-lightpos.w * groundplane.w; 585 586 return res; 587 } 588 589 /** 590 * Setup an orientation matrix using forward direction vector 591 */ 592 Matrix!(T,4) directionToMatrix(T) (Vector!(T,3) zdir) 593 { 594 Vector!(T,3) xdir = Vector!(T,3)(0, 0, 1); 595 Vector!(T,3) ydir; 596 float d = zdir.z; 597 598 if (d > -0.999999999 && d < 0.999999999) 599 { 600 xdir = xdir - zdir * d; 601 xdir.normalize(); 602 ydir = cross(zdir, xdir); 603 } 604 else 605 { 606 xdir = Vector!(T,3)(zdir.z, 0, -zdir.x); 607 ydir = Vector!(T,3)(0, 1, 0); 608 } 609 610 auto m = Matrix!(T,4).identity; 611 612 m.a13 = zdir.x; 613 m.a23 = zdir.y; 614 m.a33 = zdir.z; 615 616 m.a11 = xdir.x; 617 m.a21 = xdir.y; 618 m.a31 = xdir.z; 619 620 m.a12 = ydir.x; 621 m.a22 = ydir.y; 622 m.a32 = ydir.z; 623 624 return m; 625 } 626 627 /** 628 * Setup an orientation matrix that performs rotation 629 * between two vectors 630 * 631 * Currently this is just a shortcut 632 * for dlib.math.quaternion.rotationBetween 633 */ 634 Matrix!(T,4) rotationBetweenVectors(T) (Vector!(T,3) source, Vector!(T,3) target) 635 { 636 return rotationBetween(source, target).toMatrix4x4; 637 } 638 639 /// 640 unittest 641 { 642 bool isAlmostZero4(Vector4f v) 643 { 644 float e = 0.002f; 645 646 return abs(v.x) < e && 647 abs(v.y) < e && 648 abs(v.z) < e && 649 abs(v.w) < e; 650 } 651 652 // build ModelView (World to Camera) 653 vec3 center = vec3(0.0f, 0.0f, 0.0f); 654 vec3 eye = center + vec3(0.0f, 1.0f, 1.0f); 655 vec3 up = vec3(0.0f, -0.707f, 0.707f); 656 657 Matrix4f modelView = lookAtMatrix(eye, center, up); 658 659 // build Projection (Camera to Eye) 660 Matrix4f projection = perspectiveMatrix(45.0f, 16.0f / 9.0f, 1.0f, 100.0f); 661 662 // compose into one transformation 663 Matrix4f projectionModelView = projection * modelView; 664 665 vec4 positionInWorld = vec4(0.0f, 0.0f, 0.0f, 1.0f); 666 667 vec4 transformed1 = 668 positionInWorld * projectionModelView; 669 670 vec4 transformed2 = 671 (positionInWorld * modelView) * projection; 672 673 assert(isAlmostZero4(transformed1 - transformed2)); 674 } 675 676 /** 677 * Affine transformations in 2D space 678 */ 679 Vector!(T,2) affineTransform2D(T)(Vector!(T,2) v, Matrix!(T,3) m) 680 { 681 return Vector!(T,2) 682 ( 683 (v.x * m.a11) + (v.y * m.a12) + m.a13, 684 (v.x * m.a21) + (v.y * m.a22) + m.a23 685 ); 686 } 687 688 /** 689 * Translation in 2D space 690 */ 691 Matrix!(T,3) translationMatrix2D(T) (Vector!(T,2) t) 692 do 693 { 694 Matrix!(T,3) res; 695 res.a11 = 1; res.a12 = 0; res.a13 = t.x; 696 res.a21 = 0; res.a22 = 1; res.a23 = t.y; 697 res.a31 = 0; res.a32 = 0; res.a33 = 1; 698 return res; 699 } 700 701 alias translation2 = translationMatrix2D; 702 703 /** 704 * Rotation in 2D space 705 */ 706 Matrix!(T,3) rotationMatrix2D(T) (T theta) 707 do 708 { 709 Matrix!(T,3) res; 710 T s = sin(theta); 711 T c = cos(theta); 712 res.a11 = c; res.a12 = s; res.a13 = 0; 713 res.a21 = -s; res.a22 = c; res.a23 = 0; 714 res.a31 = 0; res.a32 = 0; res.a33 = 1; 715 return res; 716 } 717 718 alias rotation2 = rotationMatrix2D; 719 720 /** 721 * Scale in 2D space 722 */ 723 Matrix!(T,3) scaleMatrix2D(T) (Vector!(T,2) s) 724 do 725 { 726 Matrix!(T,3) res; 727 res.a11 = s.x; res.a12 = 0; res.a13 = 0; 728 res.a21 = 0; res.a22 = s.y; res.a23 = 0; 729 res.a31 = 0; res.a32 = 0; res.a33 = 1; 730 return res; 731 } 732 733 alias scale2 = scaleMatrix2D; 734 735 /// 736 unittest 737 { 738 bool isAlmostZero2(Vector2f v) 739 { 740 float e = 0.002f; 741 742 return abs(v.x) < e && 743 abs(v.y) < e; 744 } 745 746 vec2 v = vec2(1, 0); 747 Matrix3f tm = translationMatrix2D(vec2(2, 3)); 748 vec2 vt = affineTransform2D(v, tm); 749 assert(isAlmostZero2(vt - vec2(3, 3))); 750 751 Matrix3f rm = rotationMatrix2D(cast(float)PI); 752 vt = affineTransform2D(v, rm); 753 assert(isAlmostZero2(vt - vec2(-1, 0))); 754 755 Matrix3f sm = scaleMatrix2D(vec2(2, 2)); 756 vt = affineTransform2D(v, sm); 757 assert(isAlmostZero2(vt - vec2(2, 0))); 758 }