1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.geometry.euclidean.twod; 18 19 import java.util.function.UnaryOperator; 20 21 import org.apache.commons.geometry.core.internal.DoubleFunction2N; 22 import org.apache.commons.geometry.euclidean.AbstractAffineTransformMatrix; 23 import org.apache.commons.geometry.euclidean.internal.Matrices; 24 import org.apache.commons.geometry.euclidean.internal.Vectors; 25 import org.apache.commons.geometry.euclidean.twod.rotation.Rotation2D; 26 27 /** Class using a matrix to represent affine transformations in 2 dimensional Euclidean space. 28 * 29 * <p>Instances of this class use a 3x3 matrix for all transform operations. 30 * The last row of this matrix is always set to the values <code>[0 0 1]</code> and so 31 * is not stored. Hence, the methods in this class that accept or return arrays always 32 * use arrays containing 6 elements, instead of 9. 33 * </p> 34 */ 35 public final class AffineTransformMatrix2D extends AbstractAffineTransformMatrix<Vector2D, AffineTransformMatrix2D> { 36 /** The number of internal matrix elements. */ 37 private static final int NUM_ELEMENTS = 6; 38 39 /** String used to start the transform matrix string representation. */ 40 private static final String MATRIX_START = "[ "; 41 42 /** String used to end the transform matrix string representation. */ 43 private static final String MATRIX_END = " ]"; 44 45 /** String used to separate elements in the matrix string representation. */ 46 private static final String ELEMENT_SEPARATOR = ", "; 47 48 /** String used to separate rows in the matrix string representation. */ 49 private static final String ROW_SEPARATOR = "; "; 50 51 /** Shared transform set to the identity matrix. */ 52 private static final AffineTransformMatrix2D IDENTITY_INSTANCE = new AffineTransformMatrix2D( 53 1, 0, 0, 54 0, 1, 0 55 ); 56 57 /** Transform matrix entry <code>m<sub>0,0</sub></code>. */ 58 private final double m00; 59 /** Transform matrix entry <code>m<sub>0,1</sub></code>. */ 60 private final double m01; 61 /** Transform matrix entry <code>m<sub>0,2</sub></code>. */ 62 private final double m02; 63 64 /** Transform matrix entry <code>m<sub>1,0</sub></code>. */ 65 private final double m10; 66 /** Transform matrix entry <code>m<sub>1,1</sub></code>. */ 67 private final double m11; 68 /** Transform matrix entry <code>m<sub>1,2</sub></code>. */ 69 private final double m12; 70 71 /** 72 * Simple constructor; sets all internal matrix elements. 73 * @param m00 matrix entry <code>m<sub>0,0</sub></code> 74 * @param m01 matrix entry <code>m<sub>0,1</sub></code> 75 * @param m02 matrix entry <code>m<sub>0,2</sub></code> 76 * @param m10 matrix entry <code>m<sub>1,0</sub></code> 77 * @param m11 matrix entry <code>m<sub>1,1</sub></code> 78 * @param m12 matrix entry <code>m<sub>1,2</sub></code> 79 */ 80 private AffineTransformMatrix2D( 81 final double m00, final double m01, final double m02, 82 final double m10, final double m11, final double m12) { 83 84 this.m00 = m00; 85 this.m01 = m01; 86 this.m02 = m02; 87 88 this.m10 = m10; 89 this.m11 = m11; 90 this.m12 = m12; 91 } 92 93 /** Return a 6 element array containing the variable elements from the 94 * internal transformation matrix. The elements are in row-major order. 95 * The array indices map to the internal matrix as follows: 96 * <pre> 97 * [ 98 * arr[0], arr[1], arr[2], 99 * arr[3], arr[4], arr[5], 100 * 0 0 1 101 * ] 102 * </pre> 103 * @return 6 element array containing the variable elements from the 104 * internal transformation matrix 105 */ 106 public double[] toArray() { 107 return new double[] { 108 m00, m01, m02, 109 m10, m11, m12 110 }; 111 } 112 113 /** Apply this transform to the given point, returning the result as a new instance. 114 * 115 * <p>The transformed point is computed by creating a 3-element column vector from the 116 * coordinates in the input and setting the last element to 1. This is then multiplied with the 117 * 3x3 transform matrix to produce the transformed point. The {@code 1} in the last position 118 * is ignored. 119 * <pre> 120 * [ m00 m01 m02 ] [ x ] [ x'] 121 * [ m10 m11 m12 ] * [ y ] = [ y'] 122 * [ 0 0 1 ] [ 1 ] [ 1 ] 123 * </pre> 124 */ 125 @Override 126 public Vector2D apply(final Vector2D pt) { 127 final double x = pt.getX(); 128 final double y = pt.getY(); 129 130 return Vector2D.of( 131 applyX(x, y), 132 applyY(x, y)); 133 } 134 135 /** Apply this transform to the given point coordinates and return the transformed 136 * x value. The return value is equal to 137 * <code>(x * m<sub>00</sub>) + (y * m<sub>01</sub>) + m<sub>02</sub></code>. 138 * @param x x coordinate value 139 * @param y y coordinate value 140 * @return transformed x coordinate value 141 * @see #apply(Vector2D) 142 */ 143 public double applyX(final double x, final double y) { 144 return applyVectorX(x, y) + m02; 145 } 146 147 /** Apply this transform to the given point coordinates and return the transformed 148 * y value. The return value is equal to 149 * <code>(x * m<sub>10</sub>) + (y * m<sub>11</sub>) + m<sub>12</sub></code>. 150 * @param x x coordinate value 151 * @param y y coordinate value 152 * @return transformed y coordinate value 153 * @see #apply(Vector2D) 154 */ 155 public double applyY(final double x, final double y) { 156 return applyVectorY(x, y) + m12; 157 } 158 159 /** {@inheritDoc} 160 * 161 * <p>The transformed vector is computed by creating a 3-element column vector from the 162 * coordinates in the input and setting the last element to 0. This is then multiplied with the 163 * 3x3 transform matrix to produce the transformed vector. The {@code 0} in the last position 164 * is ignored. 165 * <pre> 166 * [ m00 m01 m02 ] [ x ] [ x'] 167 * [ m10 m11 m12 ] * [ y ] = [ y'] 168 * [ 0 0 1 ] [ 0 ] [ 0 ] 169 * </pre> 170 * 171 * @see #applyDirection(Vector2D) 172 */ 173 @Override 174 public Vector2D applyVector(final Vector2D vec) { 175 return applyVector(vec, Vector2D::of); 176 } 177 178 /** Apply this transform to the given vector coordinates, ignoring translations, and 179 * return the transformed x value. The return value is equal to 180 * <code>(x * m<sub>00</sub>) + (y * m<sub>01</sub>)</code>. 181 * @param x x coordinate value 182 * @param y y coordinate value 183 * @return transformed x coordinate value 184 * @see #applyVector(Vector2D) 185 */ 186 public double applyVectorX(final double x, final double y) { 187 return Vectors.linearCombination(m00, x, m01, y); 188 } 189 190 /** Apply this transform to the given vector coordinates, ignoring translations, and 191 * return the transformed y value. The return value is equal to 192 * <code>(x * m<sub>10</sub>) + (y * m<sub>11</sub>)</code>. 193 * @param x x coordinate value 194 * @param y y coordinate value 195 * @return transformed y coordinate value 196 * @see #applyVector(Vector2D) 197 */ 198 public double applyVectorY(final double x, final double y) { 199 return Vectors.linearCombination(m10, x, m11, y); 200 } 201 202 /** {@inheritDoc} 203 * @see #applyVector(Vector2D) 204 */ 205 @Override 206 public Vector2D.Unit applyDirection(final Vector2D vec) { 207 return applyVector(vec, Vector2D.Unit::from); 208 } 209 210 /** {@inheritDoc} */ 211 @Override 212 public double determinant() { 213 return Matrices.determinant( 214 m00, m01, 215 m10, m11 216 ); 217 } 218 219 /** {@inheritDoc} 220 * 221 * <p><strong>Example</strong> 222 * <pre> 223 * [ a, b, c ] [ a, b, 0 ] 224 * [ d, e, f ] → [ d, e, 0 ] 225 * [ 0, 0, 1 ] [ 0, 0, 1 ] 226 * </pre> 227 */ 228 @Override 229 public AffineTransformMatrix2D linear() { 230 return new AffineTransformMatrix2D( 231 m00, m01, 0.0, 232 m10, m11, 0.0); 233 } 234 235 /** {@inheritDoc} 236 * 237 * <p><strong>Example</strong> 238 * <pre> 239 * [ a, b, c ] [ a, d, 0 ] 240 * [ d, e, f ] → [ b, e, 0 ] 241 * [ 0, 0, 1 ] [ 0, 0, 1 ] 242 * </pre> 243 */ 244 @Override 245 public AffineTransformMatrix2D linearTranspose() { 246 return new AffineTransformMatrix2D( 247 m00, m10, 0.0, 248 m01, m11, 0.0); 249 } 250 251 /** Apply a translation to the current instance, returning the result as a new transform. 252 * @param translation vector containing the translation values for each axis 253 * @return a new transform containing the result of applying a translation to 254 * the current instance 255 */ 256 public AffineTransformMatrix2D translate(final Vector2D translation) { 257 return translate(translation.getX(), translation.getY()); 258 } 259 260 /** Apply a translation to the current instance, returning the result as a new transform. 261 * @param x translation in the x direction 262 * @param y translation in the y direction 263 * @return a new transform containing the result of applying a translation to 264 * the current instance 265 */ 266 public AffineTransformMatrix2D translate(final double x, final double y) { 267 return new AffineTransformMatrix2D( 268 m00, m01, m02 + x, 269 m10, m11, m12 + y 270 ); 271 } 272 273 /** Apply a scale operation to the current instance, returning the result as a new transform. 274 * @param factor the scale factor to apply to all axes 275 * @return a new transform containing the result of applying a scale operation to 276 * the current instance 277 */ 278 public AffineTransformMatrix2D scale(final double factor) { 279 return scale(factor, factor); 280 } 281 282 /** Apply a scale operation to the current instance, returning the result as a new transform. 283 * @param scaleFactors vector containing scale factors for each axis 284 * @return a new transform containing the result of applying a scale operation to 285 * the current instance 286 */ 287 public AffineTransformMatrix2D scale(final Vector2D scaleFactors) { 288 return scale(scaleFactors.getX(), scaleFactors.getY()); 289 } 290 291 /** Apply a scale operation to the current instance, returning the result as a new transform. 292 * @param x scale factor for the x axis 293 * @param y scale factor for the y axis 294 * @return a new transform containing the result of applying a scale operation to 295 * the current instance 296 */ 297 public AffineTransformMatrix2D scale(final double x, final double y) { 298 return new AffineTransformMatrix2D( 299 m00 * x, m01 * x, m02 * x, 300 m10 * y, m11 * y, m12 * y 301 ); 302 } 303 304 /** Apply a <em>counterclockwise</em> rotation to the current instance, returning the result as a 305 * new transform. 306 * @param angle the angle of counterclockwise rotation in radians 307 * @return a new transform containing the result of applying a rotation to the 308 * current instance 309 * @see Rotation2D#of(double) 310 */ 311 public AffineTransformMatrix2D rotate(final double angle) { 312 return rotate(Rotation2D.of(angle)); 313 } 314 315 /** Apply a <em>counterclockwise</em> rotation to the current instance, returning the result as a 316 * new transform. 317 * @param rotation the rotation to apply 318 * @return a new transform containing the result of applying the rotation to the 319 * current instance 320 */ 321 public AffineTransformMatrix2D rotate(final Rotation2D rotation) { 322 return multiply(rotation.toMatrix(), this); 323 } 324 325 /** Apply a <em>counterclockwise</em> rotation about the given center point to the current instance, 326 * returning the result as a new transform. This is accomplished by translating the center to the origin, 327 * applying the rotation, and then translating back. 328 * @param center the center of rotation 329 * @param angle the angle of counterclockwise rotation in radians 330 * @return a new transform containing the result of applying a rotation about the given 331 * center point to the current instance 332 */ 333 public AffineTransformMatrix2D rotate(final Vector2D center, final double angle) { 334 return multiply(createRotation(center, angle), this); 335 } 336 337 /** Apply a <em>counterclockwise</em> rotation about the given center point to the current instance, 338 * returning the result as a new transform. This is accomplished by translating the center to the origin, 339 * applying the rotation, and then translating back. 340 * @param center the center of rotation 341 * @param rotation the rotation to apply 342 * @return a new transform containing the result of applying a rotation about the given 343 * center point to the current instance 344 */ 345 public AffineTransformMatrix2D rotate(final Vector2D center, final Rotation2D rotation) { 346 // use to raw angle method to avoid matrix multiplication 347 return rotate(center, rotation.getAngle()); 348 } 349 350 /** Apply a shear to the current instance, returning the result as a new transform. 351 * @param shx multiplier by which coordinates are shifted along the positive x-axis as a factor of their 352 * y coordinate; a value of 0 indicates no shift along the x-axis 353 * @param shy multiplier by which coordinates are shifted along the positive y-axis as a factor of their 354 * x coordinate; a value of 0 indicates no shift along the y-axis 355 * @return a new transform containing the result of applying a shear to the current instance 356 */ 357 public AffineTransformMatrix2D shear(final double shx, final double shy) { 358 return multiply(createShear(shx, shy), this); 359 } 360 361 /** Get a new transform created by multiplying this instance by the argument. 362 * This is equivalent to the expression {@code A * M} where {@code A} is the 363 * current transform matrix and {@code M} is the given transform matrix. In 364 * terms of transformations, applying the returned matrix is equivalent to 365 * applying {@code M} and <em>then</em> applying {@code A}. In other words, 366 * the rightmost transform is applied first. 367 * 368 * @param m the transform to multiply with 369 * @return the result of multiplying the current instance by the given 370 * transform matrix 371 */ 372 public AffineTransformMatrix2D multiply(final AffineTransformMatrix2D m) { 373 return multiply(this, m); 374 } 375 376 /** Get a new transform created by multiplying the argument by this instance. 377 * This is equivalent to the expression {@code M * A} where {@code A} is the 378 * current transform matrix and {@code M} is the given transform matrix. In 379 * terms of transformations, applying the returned matrix is equivalent to 380 * applying {@code A} and <em>then</em> applying {@code M}. In other words, 381 * the rightmost transform is applied first. 382 * 383 * @param m the transform to multiply with 384 * @return the result of multiplying the given transform matrix by the current 385 * instance 386 */ 387 public AffineTransformMatrix2D premultiply(final AffineTransformMatrix2D m) { 388 return multiply(m, this); 389 } 390 391 /** {@inheritDoc} 392 * 393 * @throws IllegalStateException if the matrix cannot be inverted 394 */ 395 @Override 396 public AffineTransformMatrix2D inverse() { 397 398 // Our full matrix is 3x3 but we can significantly reduce the amount of computations 399 // needed here since we know that our last row is [0 0 1]. 400 401 final double det = Matrices.checkDeterminantForInverse(determinant()); 402 403 // validate the remaining matrix elements that were not part of the determinant 404 Matrices.checkElementForInverse(m02); 405 Matrices.checkElementForInverse(m12); 406 407 // compute the necessary elements of the cofactor matrix 408 // (we need all but the last column) 409 410 final double invDet = 1.0 / det; 411 412 final double c00 = invDet * m11; 413 final double c01 = -invDet * m10; 414 415 final double c10 = -invDet * m01; 416 final double c11 = invDet * m00; 417 418 final double c20 = invDet * Matrices.determinant(m01, m02, m11, m12); 419 final double c21 = -invDet * Matrices.determinant(m00, m02, m10, m12); 420 421 return new AffineTransformMatrix2D( 422 c00, c10, c20, 423 c01, c11, c21 424 ); 425 } 426 427 /** {@inheritDoc} */ 428 @Override 429 public int hashCode() { 430 final int prime = 31; 431 int result = 1; 432 433 result = (result * prime) + (Double.hashCode(m00) - Double.hashCode(m01) + Double.hashCode(m02)); 434 result = (result * prime) + (Double.hashCode(m10) - Double.hashCode(m11) + Double.hashCode(m12)); 435 436 return result; 437 } 438 439 /** 440 * Return true if the given object is an instance of {@link AffineTransformMatrix2D} 441 * and all matrix element values are exactly equal. 442 * @param obj object to test for equality with the current instance 443 * @return true if all transform matrix elements are exactly equal; otherwise false 444 */ 445 @Override 446 public boolean equals(final Object obj) { 447 if (this == obj) { 448 return true; 449 } 450 if (!(obj instanceof AffineTransformMatrix2D)) { 451 return false; 452 } 453 454 final AffineTransformMatrix2D other = (AffineTransformMatrix2D) obj; 455 456 return Double.compare(this.m00, other.m00) == 0 && 457 Double.compare(this.m01, other.m01) == 0 && 458 Double.compare(this.m02, other.m02) == 0 && 459 460 Double.compare(this.m10, other.m10) == 0 && 461 Double.compare(this.m11, other.m11) == 0 && 462 Double.compare(this.m12, other.m12) == 0; 463 } 464 465 /** {@inheritDoc} */ 466 @Override 467 public String toString() { 468 final StringBuilder sb = new StringBuilder(); 469 470 sb.append(MATRIX_START) 471 472 .append(m00) 473 .append(ELEMENT_SEPARATOR) 474 .append(m01) 475 .append(ELEMENT_SEPARATOR) 476 .append(m02) 477 .append(ROW_SEPARATOR) 478 479 .append(m10) 480 .append(ELEMENT_SEPARATOR) 481 .append(m11) 482 .append(ELEMENT_SEPARATOR) 483 .append(m12) 484 485 .append(MATRIX_END); 486 487 return sb.toString(); 488 } 489 490 /** Multiplies the given vector by the 2x2 linear transformation matrix contained in the 491 * upper-right corner of the affine transformation matrix. This applies all transformation 492 * operations except for translations. The computed coordinates are passed to the given 493 * factory function. 494 * @param <T> factory output type 495 * @param vec the vector to transform 496 * @param factory the factory instance that will be passed the transformed coordinates 497 * @return the factory return value 498 */ 499 private <T> T applyVector(final Vector2D vec, final DoubleFunction2N<T> factory) { 500 final double x = vec.getX(); 501 final double y = vec.getY(); 502 503 return factory.apply( 504 applyVectorX(x, y), 505 applyVectorY(x, y)); 506 } 507 508 /** Get a new transform with the given matrix elements. The array must contain 6 elements. 509 * @param arr 6-element array containing values for the variable entries in the 510 * transform matrix 511 * @return a new transform initialized with the given matrix values 512 * @throws IllegalArgumentException if the array does not have 6 elements 513 */ 514 public static AffineTransformMatrix2D of(final double... arr) { 515 if (arr.length != NUM_ELEMENTS) { 516 throw new IllegalArgumentException("Dimension mismatch: " + arr.length + " != " + NUM_ELEMENTS); 517 } 518 519 return new AffineTransformMatrix2D( 520 arr[0], arr[1], arr[2], 521 arr[3], arr[4], arr[5] 522 ); 523 } 524 525 /** Construct a new transform representing the given function. The function is sampled at 526 * the origin and along each axis and a matrix is created to perform the transformation. 527 * @param fn function to create a transform matrix from 528 * @return a transform matrix representing the given function 529 * @throws IllegalArgumentException if the given function does not represent a valid 530 * affine transform 531 */ 532 public static AffineTransformMatrix2D from(final UnaryOperator<Vector2D> fn) { 533 final Vector2D tPlusX = fn.apply(Vector2D.Unit.PLUS_X); 534 final Vector2D tPlusY = fn.apply(Vector2D.Unit.PLUS_Y); 535 final Vector2D tZero = fn.apply(Vector2D.ZERO); 536 537 final Vector2D u = tPlusX.subtract(tZero); 538 final Vector2D v = tPlusY.subtract(tZero); 539 540 final AffineTransformMatrix2D mat = AffineTransformMatrix2D.fromColumnVectors(u, v, tZero); 541 542 final double det = mat.determinant(); 543 if (!Vectors.isRealNonZero(det)) { 544 throw new IllegalArgumentException("Transform function is invalid: matrix determinant is " + det); 545 } 546 547 return mat; 548 } 549 550 /** Get a new transform create from the given column vectors. The returned transform 551 * does not include any translation component. 552 * @param u first column vector; this corresponds to the first basis vector 553 * in the coordinate frame 554 * @param v second column vector; this corresponds to the second basis vector 555 * in the coordinate frame 556 * @return a new transform with the given column vectors 557 */ 558 public static AffineTransformMatrix2D fromColumnVectors(final Vector2D u, final Vector2D v) { 559 return fromColumnVectors(u, v, Vector2D.ZERO); 560 } 561 562 /** Get a new transform created from the given column vectors. 563 * @param u first column vector; this corresponds to the first basis vector 564 * in the coordinate frame 565 * @param v second column vector; this corresponds to the second basis vector 566 * in the coordinate frame 567 * @param t third column vector; this corresponds to the translation of the transform 568 * @return a new transform with the given column vectors 569 */ 570 public static AffineTransformMatrix2D fromColumnVectors(final Vector2D u, final Vector2D v, final Vector2D t) { 571 return new AffineTransformMatrix2D( 572 u.getX(), v.getX(), t.getX(), 573 u.getY(), v.getY(), t.getY() 574 ); 575 } 576 577 /** Get the transform representing the identity matrix. This transform does not 578 * modify point or vector values when applied. 579 * @return transform representing the identity matrix 580 */ 581 public static AffineTransformMatrix2D identity() { 582 return IDENTITY_INSTANCE; 583 } 584 585 /** Create a transform representing the given translation. 586 * @param translation vector containing translation values for each axis 587 * @return a new transform representing the given translation 588 */ 589 public static AffineTransformMatrix2D createTranslation(final Vector2D translation) { 590 return createTranslation(translation.getX(), translation.getY()); 591 } 592 593 /** Create a transform representing the given translation. 594 * @param x translation in the x direction 595 * @param y translation in the y direction 596 * @return a new transform representing the given translation 597 */ 598 public static AffineTransformMatrix2D createTranslation(final double x, final double y) { 599 return new AffineTransformMatrix2D( 600 1, 0, x, 601 0, 1, y 602 ); 603 } 604 605 /** Create a transform representing a scale operation with the given scale factor applied to all axes. 606 * @param factor scale factor to apply to all axes 607 * @return a new transform representing a uniform scaling in all axes 608 */ 609 public static AffineTransformMatrix2D createScale(final double factor) { 610 return createScale(factor, factor); 611 } 612 613 /** Create a transform representing a scale operation. 614 * @param factors vector containing scale factors for each axis 615 * @return a new transform representing a scale operation 616 */ 617 public static AffineTransformMatrix2D createScale(final Vector2D factors) { 618 return createScale(factors.getX(), factors.getY()); 619 } 620 621 /** Create a transform representing a scale operation. 622 * @param x scale factor for the x axis 623 * @param y scale factor for the y axis 624 * @return a new transform representing a scale operation 625 */ 626 public static AffineTransformMatrix2D createScale(final double x, final double y) { 627 return new AffineTransformMatrix2D( 628 x, 0, 0, 629 0, y, 0 630 ); 631 } 632 633 /** Create a transform representing a <em>counterclockwise</em> rotation of {@code angle} 634 * radians around the origin. 635 * @param angle the angle of rotation in radians 636 * @return a new transform representing the rotation 637 * @see Rotation2D#toMatrix() 638 */ 639 public static AffineTransformMatrix2D createRotation(final double angle) { 640 return Rotation2D.of(angle).toMatrix(); 641 } 642 643 /** Create a transform representing a <em>counterclockwise</em> rotation of {@code angle} 644 * radians around the given center point. This is accomplished by translating the center point 645 * to the origin, applying the rotation, and then translating back. 646 * @param center the center of rotation 647 * @param angle the angle of rotation in radians 648 * @return a new transform representing the rotation about the given center 649 */ 650 public static AffineTransformMatrix2D createRotation(final Vector2D center, final double angle) { 651 // it's possible to do this using Rotation2D to create the rotation matrix but we 652 // can avoid the matrix multiplications by simply doing everything in-line here 653 final double x = center.getX(); 654 final double y = center.getY(); 655 656 final double sin = Math.sin(angle); 657 final double cos = Math.cos(angle); 658 659 return new AffineTransformMatrix2D( 660 cos, -sin, (-x * cos) + (y * sin) + x, 661 sin, cos, (-x * sin) - (y * cos) + y 662 ); 663 } 664 665 /** Create a transform representing a <em>counterclockwise</em> rotation around the given center point. 666 * This is accomplished by translating the center point to the origin, applying the rotation, and then 667 * translating back. 668 * @param center the center of rotation 669 * @param rotation the rotation to apply 670 * @return a new transform representing the rotation about the given center 671 */ 672 public static AffineTransformMatrix2D createRotation(final Vector2D center, final Rotation2D rotation) { 673 return createRotation(center, rotation.getAngle()); 674 } 675 676 /** Create a transform representing a shear operation. The returned instance contains the 677 * matrix values 678 * <pre> 679 * [ 1, shx, 0 ] 680 * [ shy, 1, 0 ] 681 * [ 0, 0, 0 ] 682 * </pre> 683 * @param shx multiplier by which coordinates are shifted along the positive x-axis as a factor of their 684 * y coordinate; a value of 0 indicates no shift along the x-axis 685 * @param shy multiplier by which coordinates are shifted along the positive y-axis as a factor of their 686 * x coordinate; a value of 0 indicates no shift along the y-axis 687 * @return a new transform representing the shear operation 688 */ 689 public static AffineTransformMatrix2D createShear(final double shx, final double shy) { 690 return new AffineTransformMatrix2D( 691 1, shx, 0, 692 shy, 1, 0 693 ); 694 } 695 696 /** Multiply two transform matrices together. 697 * @param a first transform 698 * @param b second transform 699 * @return the transform computed as {@code a x b} 700 */ 701 private static AffineTransformMatrix2D multiply(final AffineTransformMatrix2D a, 702 final AffineTransformMatrix2D b) { 703 704 final double c00 = Vectors.linearCombination(a.m00, b.m00, a.m01, b.m10); 705 final double c01 = Vectors.linearCombination(a.m00, b.m01, a.m01, b.m11); 706 final double c02 = Vectors.linearCombination(a.m00, b.m02, a.m01, b.m12) + a.m02; 707 708 final double c10 = Vectors.linearCombination(a.m10, b.m00, a.m11, b.m10); 709 final double c11 = Vectors.linearCombination(a.m10, b.m01, a.m11, b.m11); 710 final double c12 = Vectors.linearCombination(a.m10, b.m02, a.m11, b.m12) + a.m12; 711 712 return new AffineTransformMatrix2D( 713 c00, c01, c02, 714 c10, c11, c12 715 ); 716 } 717 }