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.oned;
    18
    19  import java.util.function.UnaryOperator;
    20
    21  import org.apache.commons.geometry.euclidean.AbstractAffineTransformMatrix;
    22  import org.apache.commons.geometry.euclidean.internal.Matrices;
    23  import org.apache.commons.geometry.euclidean.internal.Vectors;
    24
    25  /** Class using a matrix to represent affine transformations in 1 dimensional Euclidean space.
    26  *
    27  * <p>Instances of this class use a 2x2 matrix for all transform operations.
    28  * The last row of this matrix is always set to the values <code>[0 1]</code> and so
    29  * is not stored. Hence, the methods in this class that accept or return arrays always
    30  * use arrays containing 2 elements, instead of 4.
    31  * </p>
    32  */
    33  public final class AffineTransformMatrix1D extends AbstractAffineTransformMatrix<Vector1D, AffineTransformMatrix1D> {
    34      /** The number of internal matrix elements. */
    35      private static final int NUM_ELEMENTS = 2;
    36
    37      /** String used to start the transform matrix string representation. */
    38      private static final String MATRIX_START = "[ ";
    39
    40      /** String used to end the transform matrix string representation. */
    41      private static final String MATRIX_END = " ]";
    42
    43      /** String used to separate elements in the matrix string representation. */
    44      private static final String ELEMENT_SEPARATOR = ", ";
    45
    46      /** Shared transform set to the identity matrix. */
    47      private static final AffineTransformMatrix1D IDENTITY_INSTANCE = new AffineTransformMatrix1D(1, 0);
    48
    49      /** Transform matrix entry <code>m<sub>0,0</sub></code>. */
    50      private final double m00;
    51      /** Transform matrix entry <code>m<sub>0,1</sub></code>. */
    52      private final double m01;
    53
    54      /**
    55       * Simple constructor; sets all internal matrix elements.
    56       * @param m00 matrix entry <code>m<sub>0,0</sub></code>
    57       * @param m01 matrix entry <code>m<sub>0,1</sub></code>
    58       */
    59      private AffineTransformMatrix1D(final double m00, final double m01) {
    60          this.m00 = m00;
    61          this.m01 = m01;
    62      }
    63
    64      /** Return a 2 element array containing the variable elements from the
    65       * internal transformation matrix. The elements are in row-major order.
    66       * The array indices map to the internal matrix as follows:
    67       * <pre>
    68       *      [
    69       *          arr[0],   arr[1],
    70       *          0         1
    71       *      ]
    72       * </pre>
    73       * @return 2 element array containing the variable elements from the
    74       *      internal transformation matrix
    75       */
    76      public double[] toArray() {
    77          return new double[] {
    78              m00, m01
    79          };
    80      }
    81
    82      /** {@inheritDoc} */
    83      @Override
    84      public Vector1D apply(final Vector1D vec) {
    85          return Vector1D.of(applyX(vec.getX()));
    86      }
    87
    88      /** Apply this transform to the given point coordinate and return the transformed
    89       * x value. The return value is equal to <code>(x * m<sub>00</sub>) + m<sub>01</sub></code>.
    90       * @param x x coordinate value
    91       * @return transformed x coordinate value
    92       * @see #apply(Vector1D)
    93       */
    94      public double applyX(final double x) {
    95          return applyVectorX(x) + m01;
    96      }
    97
    98      /** {@inheritDoc}
    99       * @see #applyDirection(Vector1D)
    100      */
    101     @Override
    102     public Vector1D applyVector(final Vector1D vec) {
    103         return Vector1D.of(applyVectorX(vec.getX()));
    104     }
    105
    106     /** Apply this transform to the given vector coordinate, ignoring translations, and
    107      * return the transformed x value. The return value is equal to <code>x * m<sub>00</sub></code>.
    108      * @param x x coordinate value
    109      * @return transformed x coordinate value
    110      * @see #applyVector(Vector1D)
    111      */
    112     public double applyVectorX(final double x) {
    113         return x * m00;
    114     }
    115
    116     /** {@inheritDoc}
    117      * @see #applyVector(Vector1D)
    118      */
    119     @Override
    120     public Vector1D.Unit applyDirection(final Vector1D vec) {
    121         return Vector1D.Unit.from(applyVectorX(vec.getX()));
    122     }
    123
    124     /** {@inheritDoc} */
    125     @Override
    126     public double determinant() {
    127         return m00;
    128     }
    129
    130     /** {@inheritDoc}
    131      *
    132      * <p><strong>Example</strong>
    133      * <pre>
    134      *      [ a, b ]   [ a, 0 ]
    135      *      [ 0, 1 ] &rarr; [ 0, 1 ]
    136      * </pre>
    137      */
    138     @Override
    139     public AffineTransformMatrix1D linear() {
    140         return new AffineTransformMatrix1D(m00, 0.0);
    141     }
    142
    143     /** {@inheritDoc}
    144      *
    145      * <p>In the one dimensional case, this is exactly the same as {@link #linear()}.</p>
    146      *
    147      * <p><strong>Example</strong>
    148      * <pre>
    149      *      [ a, b ]   [ a, 0 ]
    150      *      [ 0, 1 ] &rarr; [ 0, 1 ]
    151      * </pre>
    152      */
    153     @Override
    154     public AffineTransformMatrix1D linearTranspose() {
    155         return linear();
    156     }
    157
    158     /** Get a new transform containing the result of applying a translation logically after
    159      * the transformation represented by the current instance. This is achieved by
    160      * creating a new translation transform and pre-multiplying it with the current
    161      * instance. In other words, the returned transform contains the matrix
    162      * <code>B * A</code>, where <code>A</code> is the current matrix and <code>B</code>
    163      * is the matrix representing the given translation.
    164      * @param translation vector containing the translation values for each axis
    165      * @return a new transform containing the result of applying a translation to
    166      *      the current instance
    167      */
    168     public AffineTransformMatrix1D translate(final Vector1D translation) {
    169         return translate(translation.getX());
    170     }
    171
    172     /** Get a new transform containing the result of applying a translation logically after
    173      * the transformation represented by the current instance. This is achieved by
    174      * creating a new translation transform and pre-multiplying it with the current
    175      * instance. In other words, the returned transform contains the matrix
    176      * <code>B * A</code>, where <code>A</code> is the current matrix and <code>B</code>
    177      * is the matrix representing the given translation.
    178      * @param x translation in the x direction
    179      * @return a new transform containing the result of applying a translation to
    180      *      the current instance
    181      */
    182     public AffineTransformMatrix1D translate(final double x) {
    183         return new AffineTransformMatrix1D(m00, m01 + x);
    184     }
    185
    186     /** Get a new transform containing the result of applying a scale operation
    187      * logically after the transformation represented by the current instance.
    188      * This is achieved by creating a new scale transform and pre-multiplying it with the current
    189      * instance. In other words, the returned transform contains the matrix
    190      * <code>B * A</code>, where <code>A</code> is the current matrix and <code>B</code>
    191      * is the matrix representing the given scale operation.
    192      * @param scaleFactor vector containing scale factors for each axis
    193      * @return a new transform containing the result of applying a scale operation to
    194      *      the current instance
    195      */
    196     public AffineTransformMatrix1D scale(final Vector1D scaleFactor) {
    197         return scale(scaleFactor.getX());
    198     }
    199
    200     /** Get a new transform containing the result of applying a scale operation
    201      * logically after the transformation represented by the current instance.
    202      * This is achieved by creating a new scale transform and pre-multiplying it with the current
    203      * instance. In other words, the returned transform contains the matrix
    204      * <code>B * A</code>, where <code>A</code> is the current matrix and <code>B</code>
    205      * is the matrix representing the given scale operation.
    206      * @param x scale factor
    207      * @return a new transform containing the result of applying a scale operation to
    208      *      the current instance
    209      */
    210     public AffineTransformMatrix1D scale(final double x) {
    211         return new AffineTransformMatrix1D(m00 * x, m01 * x);
    212     }
    213
    214     /** Get a new transform created by multiplying this instance by the argument.
    215      * This is equivalent to the expression {@code A * M} where {@code A} is the
    216      * current transform matrix and {@code M} is the given transform matrix. In
    217      * terms of transformations, applying the returned matrix is equivalent to
    218      * applying {@code M} and <em>then</em> applying {@code A}. In other words,
    219      * the rightmost transform is applied first.
    220      *
    221      * @param m the transform to multiply with
    222      * @return the result of multiplying the current instance by the given
    223      *      transform matrix
    224      */
    225     public AffineTransformMatrix1D multiply(final AffineTransformMatrix1D m) {
    226         return multiply(this, m);
    227     }
    228
    229     /** Get a new transform created by multiplying the argument by this instance.
    230      * This is equivalent to the expression {@code M * A} where {@code A} is the
    231      * current transform matrix and {@code M} is the given transform matrix. In
    232      * terms of transformations, applying the returned matrix is equivalent to
    233      * applying {@code A} and <em>then</em> applying {@code M}. In other words,
    234      * the rightmost transform is applied first.
    235      *
    236      * @param m the transform to multiply with
    237      * @return the result of multiplying the given transform matrix by the current
    238      *      instance
    239      */
    240     public AffineTransformMatrix1D premultiply(final AffineTransformMatrix1D m) {
    241         return multiply(m, this);
    242     }
    243
    244     /** {@inheritDoc}
    245      *
    246      * @throws IllegalStateException if the matrix cannot be inverted
    247      */
    248     @Override
    249     public AffineTransformMatrix1D inverse() {
    250
    251         final double det = Matrices.checkDeterminantForInverse(determinant());
    252
    253         Matrices.checkElementForInverse(m01);
    254
    255         final double invDet = 1.0 / det;
    256
    257         final double c01 = -(this.m01 * invDet);
    258
    259         return new AffineTransformMatrix1D(invDet, c01);
    260     }
    261
    262     /** {@inheritDoc} */
    263     @Override
    264     public int hashCode() {
    265         final int prime = 31;
    266         int result = 1;
    267
    268         result = (result * prime) + Double.hashCode(m00);
    269         result = (result * prime) + Double.hashCode(m01);
    270
    271         return result;
    272     }
    273
    274     /**
    275      * Return true if the given object is an instance of {@link AffineTransformMatrix1D}
    276      * and all matrix element values are exactly equal.
    277      * @param obj object to test for equality with the current instance
    278      * @return true if all transform matrix elements are exactly equal; otherwise false
    279      */
    280     @Override
    281     public boolean equals(final Object obj) {
    282         if (this == obj) {
    283             return true;
    284         }
    285         if (!(obj instanceof AffineTransformMatrix1D)) {
    286             return false;
    287         }
    288         final AffineTransformMatrix1D other = (AffineTransformMatrix1D) obj;
    289
    290         return Double.compare(this.m00, other.m00) == 0 &&
    291                 Double.compare(this.m01, other.m01) == 0;
    292     }
    293
    294     /** {@inheritDoc} */
    295     @Override
    296     public String toString() {
    297         final StringBuilder sb = new StringBuilder();
    298
    299         sb.append(MATRIX_START)
    300
    301             .append(m00)
    302             .append(ELEMENT_SEPARATOR)
    303             .append(m01)
    304
    305             .append(MATRIX_END);
    306
    307         return sb.toString();
    308     }
    309
    310     /** Get a new transform with the given matrix elements. The array must contain 2 elements.
    311      * The first element in the array represents the scale factor for the transform and the
    312      * second represents the translation.
    313      * @param arr 2-element array containing values for the variable entries in the
    314      *      transform matrix
    315      * @return a new transform initialized with the given matrix values
    316      * @throws IllegalArgumentException if the array does not have 2 elements
    317      */
    318     public static AffineTransformMatrix1D of(final double... arr) {
    319         if (arr.length != NUM_ELEMENTS) {
    320             throw new IllegalArgumentException("Dimension mismatch: " + arr.length + " != " + NUM_ELEMENTS);
    321         }
    322
    323         return new AffineTransformMatrix1D(arr[0], arr[1]);
    324     }
    325
    326     /** Construct a new transform representing the given function. The function is sampled at
    327      * the points zero and one and a matrix is created to perform the transformation.
    328      * @param fn function to create a transform matrix from
    329      * @return a transform matrix representing the given function
    330      * @throws IllegalArgumentException if the given function does not represent a valid
    331      *      affine transform
    332      */
    333     public static AffineTransformMatrix1D from(final UnaryOperator<Vector1D> fn) {
    334         final Vector1D tOne = fn.apply(Vector1D.Unit.PLUS);
    335         final Vector1D tZero = fn.apply(Vector1D.ZERO);
    336
    337         final double scale = tOne.subtract(tZero).getX();
    338         final double translate = tZero.getX();
    339
    340         final AffineTransformMatrix1D mat =  AffineTransformMatrix1D.of(scale, translate);
    341
    342         final double det = mat.determinant();
    343         if (!Vectors.isRealNonZero(det)) {
    344             throw new IllegalArgumentException("Transform function is invalid: matrix determinant is " + det);
    345         }
    346
    347         return mat;
    348     }
    349
    350     /** Get the transform representing the identity matrix. This transform does not
    351      * modify point or vector values when applied.
    352      * @return transform representing the identity matrix
    353      */
    354     public static AffineTransformMatrix1D identity() {
    355         return IDENTITY_INSTANCE;
    356     }
    357
    358     /** Get a transform representing the given translation.
    359      * @param translation vector containing translation values for each axis
    360      * @return a new transform representing the given translation
    361      */
    362     public static AffineTransformMatrix1D createTranslation(final Vector1D translation) {
    363         return createTranslation(translation.getX());
    364     }
    365
    366     /** Get a transform representing the given translation.
    367      * @param x translation in the x direction
    368      * @return a new transform representing the given translation
    369      */
    370     public static AffineTransformMatrix1D createTranslation(final double x) {
    371         return new AffineTransformMatrix1D(1, x);
    372     }
    373
    374     /** Get a transform representing a scale operation.
    375      * @param factor vector containing the scale factor
    376      * @return a new transform representing a scale operation
    377      */
    378     public static AffineTransformMatrix1D createScale(final Vector1D factor) {
    379         return createScale(factor.getX());
    380     }
    381
    382     /** Get a transform representing a scale operation.
    383      * @param factor scale factor
    384      * @return a new transform representing a scale operation
    385      */
    386     public static AffineTransformMatrix1D createScale(final double factor) {
    387         return new AffineTransformMatrix1D(factor, 0);
    388     }
    389
    390     /** Multiply two transform matrices together.
    391      * @param a first transform
    392      * @param b second transform
    393      * @return the transform computed as {@code a x b}
    394      */
    395     private static AffineTransformMatrix1D multiply(final AffineTransformMatrix1D a,
    396             final AffineTransformMatrix1D b) {
    397
    398         // calculate the matrix elements
    399         final double c00 = a.m00 * b.m00;
    400         final double c01 = (a.m00 * b.m01) + a.m01;
    401
    402         return new AffineTransformMatrix1D(c00, c01);
    403     }
    404 }