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