View Javadoc

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