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 }