AffineTransformMatrix1D.java

  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. import java.util.function.UnaryOperator;

  19. import org.apache.commons.geometry.euclidean.AbstractAffineTransformMatrix;
  20. import org.apache.commons.geometry.euclidean.internal.Matrices;
  21. import org.apache.commons.geometry.euclidean.internal.Vectors;

  22. /** Class using a matrix to represent affine transformations in 1 dimensional Euclidean space.
  23. *
  24. * <p>Instances of this class use a 2x2 matrix for all transform operations.
  25. * The last row of this matrix is always set to the values <code>[0 1]</code> and so
  26. * is not stored. Hence, the methods in this class that accept or return arrays always
  27. * use arrays containing 2 elements, instead of 4.
  28. * </p>
  29. */
  30. public final class AffineTransformMatrix1D extends AbstractAffineTransformMatrix<Vector1D, AffineTransformMatrix1D> {
  31.     /** The number of internal matrix elements. */
  32.     private static final int NUM_ELEMENTS = 2;

  33.     /** String used to start the transform matrix string representation. */
  34.     private static final String MATRIX_START = "[ ";

  35.     /** String used to end the transform matrix string representation. */
  36.     private static final String MATRIX_END = " ]";

  37.     /** String used to separate elements in the matrix string representation. */
  38.     private static final String ELEMENT_SEPARATOR = ", ";

  39.     /** Shared transform set to the identity matrix. */
  40.     private static final AffineTransformMatrix1D IDENTITY_INSTANCE = new AffineTransformMatrix1D(1, 0);

  41.     /** Transform matrix entry <code>m<sub>0,0</sub></code>. */
  42.     private final double m00;
  43.     /** Transform matrix entry <code>m<sub>0,1</sub></code>. */
  44.     private final double m01;

  45.     /**
  46.      * Simple constructor; sets all internal matrix elements.
  47.      * @param m00 matrix entry <code>m<sub>0,0</sub></code>
  48.      * @param m01 matrix entry <code>m<sub>0,1</sub></code>
  49.      */
  50.     private AffineTransformMatrix1D(final double m00, final double m01) {
  51.         this.m00 = m00;
  52.         this.m01 = m01;
  53.     }

  54.     /** Return a 2 element array containing the variable elements from the
  55.      * internal transformation matrix. The elements are in row-major order.
  56.      * The array indices map to the internal matrix as follows:
  57.      * <pre>
  58.      *      [
  59.      *          arr[0],   arr[1],
  60.      *          0         1
  61.      *      ]
  62.      * </pre>
  63.      * @return 2 element array containing the variable elements from the
  64.      *      internal transformation matrix
  65.      */
  66.     public double[] toArray() {
  67.         return new double[] {
  68.             m00, m01
  69.         };
  70.     }

  71.     /** {@inheritDoc} */
  72.     @Override
  73.     public Vector1D apply(final Vector1D vec) {
  74.         return Vector1D.of(applyX(vec.getX()));
  75.     }

  76.     /** Apply this transform to the given point coordinate and return the transformed
  77.      * x value. The return value is equal to <code>(x * m<sub>00</sub>) + m<sub>01</sub></code>.
  78.      * @param x x coordinate value
  79.      * @return transformed x coordinate value
  80.      * @see #apply(Vector1D)
  81.      */
  82.     public double applyX(final double x) {
  83.         return applyVectorX(x) + m01;
  84.     }

  85.     /** {@inheritDoc}
  86.      * @see #applyDirection(Vector1D)
  87.      */
  88.     @Override
  89.     public Vector1D applyVector(final Vector1D vec) {
  90.         return Vector1D.of(applyVectorX(vec.getX()));
  91.     }

  92.     /** Apply this transform to the given vector coordinate, ignoring translations, and
  93.      * return the transformed x value. The return value is equal to <code>x * m<sub>00</sub></code>.
  94.      * @param x x coordinate value
  95.      * @return transformed x coordinate value
  96.      * @see #applyVector(Vector1D)
  97.      */
  98.     public double applyVectorX(final double x) {
  99.         return x * m00;
  100.     }

  101.     /** {@inheritDoc}
  102.      * @see #applyVector(Vector1D)
  103.      */
  104.     @Override
  105.     public Vector1D.Unit applyDirection(final Vector1D vec) {
  106.         return Vector1D.Unit.from(applyVectorX(vec.getX()));
  107.     }

  108.     /** {@inheritDoc} */
  109.     @Override
  110.     public double determinant() {
  111.         return m00;
  112.     }

  113.     /** {@inheritDoc}
  114.      *
  115.      * <p><strong>Example</strong>
  116.      * <pre>
  117.      *      [ a, b ]   [ a, 0 ]
  118.      *      [ 0, 1 ] &rarr; [ 0, 1 ]
  119.      * </pre>
  120.      */
  121.     @Override
  122.     public AffineTransformMatrix1D linear() {
  123.         return new AffineTransformMatrix1D(m00, 0.0);
  124.     }

  125.     /** {@inheritDoc}
  126.      *
  127.      * <p>In the one dimensional case, this is exactly the same as {@link #linear()}.</p>
  128.      *
  129.      * <p><strong>Example</strong>
  130.      * <pre>
  131.      *      [ a, b ]   [ a, 0 ]
  132.      *      [ 0, 1 ] &rarr; [ 0, 1 ]
  133.      * </pre>
  134.      */
  135.     @Override
  136.     public AffineTransformMatrix1D linearTranspose() {
  137.         return linear();
  138.     }

  139.     /** Get a new transform containing the result of applying a translation logically after
  140.      * the transformation represented by the current instance. This is achieved by
  141.      * creating a new translation transform and pre-multiplying it with the current
  142.      * instance. In other words, the returned transform contains the matrix
  143.      * <code>B * A</code>, where <code>A</code> is the current matrix and <code>B</code>
  144.      * is the matrix representing the given translation.
  145.      * @param translation vector containing the translation values for each axis
  146.      * @return a new transform containing the result of applying a translation to
  147.      *      the current instance
  148.      */
  149.     public AffineTransformMatrix1D translate(final Vector1D translation) {
  150.         return translate(translation.getX());
  151.     }

  152.     /** Get a new transform containing the result of applying a translation logically after
  153.      * the transformation represented by the current instance. This is achieved by
  154.      * creating a new translation transform and pre-multiplying it with the current
  155.      * instance. In other words, the returned transform contains the matrix
  156.      * <code>B * A</code>, where <code>A</code> is the current matrix and <code>B</code>
  157.      * is the matrix representing the given translation.
  158.      * @param x translation in the x direction
  159.      * @return a new transform containing the result of applying a translation to
  160.      *      the current instance
  161.      */
  162.     public AffineTransformMatrix1D translate(final double x) {
  163.         return new AffineTransformMatrix1D(m00, m01 + x);
  164.     }

  165.     /** Get a new transform containing the result of applying a scale operation
  166.      * logically after the transformation represented by the current instance.
  167.      * This is achieved by creating a new scale transform and pre-multiplying it with the current
  168.      * instance. In other words, the returned transform contains the matrix
  169.      * <code>B * A</code>, where <code>A</code> is the current matrix and <code>B</code>
  170.      * is the matrix representing the given scale operation.
  171.      * @param scaleFactor vector containing scale factors for each axis
  172.      * @return a new transform containing the result of applying a scale operation to
  173.      *      the current instance
  174.      */
  175.     public AffineTransformMatrix1D scale(final Vector1D scaleFactor) {
  176.         return scale(scaleFactor.getX());
  177.     }

  178.     /** Get a new transform containing the result of applying a scale operation
  179.      * logically after the transformation represented by the current instance.
  180.      * This is achieved by creating a new scale transform and pre-multiplying it with the current
  181.      * instance. In other words, the returned transform contains the matrix
  182.      * <code>B * A</code>, where <code>A</code> is the current matrix and <code>B</code>
  183.      * is the matrix representing the given scale operation.
  184.      * @param x scale factor
  185.      * @return a new transform containing the result of applying a scale operation to
  186.      *      the current instance
  187.      */
  188.     public AffineTransformMatrix1D scale(final double x) {
  189.         return new AffineTransformMatrix1D(m00 * x, m01 * x);
  190.     }

  191.     /** Get a new transform created by multiplying this instance by the argument.
  192.      * This is equivalent to the expression {@code A * M} where {@code A} is the
  193.      * current transform matrix and {@code M} is the given transform matrix. In
  194.      * terms of transformations, applying the returned matrix is equivalent to
  195.      * applying {@code M} and <em>then</em> applying {@code A}. In other words,
  196.      * the rightmost transform is applied first.
  197.      *
  198.      * @param m the transform to multiply with
  199.      * @return the result of multiplying the current instance by the given
  200.      *      transform matrix
  201.      */
  202.     public AffineTransformMatrix1D multiply(final AffineTransformMatrix1D m) {
  203.         return multiply(this, m);
  204.     }

  205.     /** Get a new transform created by multiplying the argument by this instance.
  206.      * This is equivalent to the expression {@code M * A} where {@code A} is the
  207.      * current transform matrix and {@code M} is the given transform matrix. In
  208.      * terms of transformations, applying the returned matrix is equivalent to
  209.      * applying {@code A} and <em>then</em> applying {@code M}. In other words,
  210.      * the rightmost transform is applied first.
  211.      *
  212.      * @param m the transform to multiply with
  213.      * @return the result of multiplying the given transform matrix by the current
  214.      *      instance
  215.      */
  216.     public AffineTransformMatrix1D premultiply(final AffineTransformMatrix1D m) {
  217.         return multiply(m, this);
  218.     }

  219.     /** {@inheritDoc}
  220.      *
  221.      * @throws IllegalStateException if the matrix cannot be inverted
  222.      */
  223.     @Override
  224.     public AffineTransformMatrix1D inverse() {

  225.         final double det = Matrices.checkDeterminantForInverse(determinant());

  226.         Matrices.checkElementForInverse(m01);

  227.         final double invDet = 1.0 / det;

  228.         final double c01 = -(this.m01 * invDet);

  229.         return new AffineTransformMatrix1D(invDet, c01);
  230.     }

  231.     /** {@inheritDoc} */
  232.     @Override
  233.     public int hashCode() {
  234.         final int prime = 31;
  235.         int result = 1;

  236.         result = (result * prime) + Double.hashCode(m00);
  237.         result = (result * prime) + Double.hashCode(m01);

  238.         return result;
  239.     }

  240.     /**
  241.      * Return true if the given object is an instance of {@link AffineTransformMatrix1D}
  242.      * and all matrix element values are exactly equal.
  243.      * @param obj object to test for equality with the current instance
  244.      * @return true if all transform matrix elements are exactly equal; otherwise false
  245.      */
  246.     @Override
  247.     public boolean equals(final Object obj) {
  248.         if (this == obj) {
  249.             return true;
  250.         }
  251.         if (!(obj instanceof AffineTransformMatrix1D)) {
  252.             return false;
  253.         }
  254.         final AffineTransformMatrix1D other = (AffineTransformMatrix1D) obj;

  255.         return Double.compare(this.m00, other.m00) == 0 &&
  256.                 Double.compare(this.m01, other.m01) == 0;
  257.     }

  258.     /** {@inheritDoc} */
  259.     @Override
  260.     public String toString() {
  261.         final StringBuilder sb = new StringBuilder();

  262.         sb.append(MATRIX_START)

  263.             .append(m00)
  264.             .append(ELEMENT_SEPARATOR)
  265.             .append(m01)

  266.             .append(MATRIX_END);

  267.         return sb.toString();
  268.     }

  269.     /** Get a new transform with the given matrix elements. The array must contain 2 elements.
  270.      * The first element in the array represents the scale factor for the transform and the
  271.      * second represents the translation.
  272.      * @param arr 2-element array containing values for the variable entries in the
  273.      *      transform matrix
  274.      * @return a new transform initialized with the given matrix values
  275.      * @throws IllegalArgumentException if the array does not have 2 elements
  276.      */
  277.     public static AffineTransformMatrix1D of(final double... arr) {
  278.         if (arr.length != NUM_ELEMENTS) {
  279.             throw new IllegalArgumentException("Dimension mismatch: " + arr.length + " != " + NUM_ELEMENTS);
  280.         }

  281.         return new AffineTransformMatrix1D(arr[0], arr[1]);
  282.     }

  283.     /** Construct a new transform representing the given function. The function is sampled at
  284.      * the points zero and one and a matrix is created to perform the transformation.
  285.      * @param fn function to create a transform matrix from
  286.      * @return a transform matrix representing the given function
  287.      * @throws IllegalArgumentException if the given function does not represent a valid
  288.      *      affine transform
  289.      */
  290.     public static AffineTransformMatrix1D from(final UnaryOperator<Vector1D> fn) {
  291.         final Vector1D tOne = fn.apply(Vector1D.Unit.PLUS);
  292.         final Vector1D tZero = fn.apply(Vector1D.ZERO);

  293.         final double scale = tOne.subtract(tZero).getX();
  294.         final double translate = tZero.getX();

  295.         final AffineTransformMatrix1D mat =  AffineTransformMatrix1D.of(scale, translate);

  296.         final double det = mat.determinant();
  297.         if (!Vectors.isRealNonZero(det)) {
  298.             throw new IllegalArgumentException("Transform function is invalid: matrix determinant is " + det);
  299.         }

  300.         return mat;
  301.     }

  302.     /** Get the transform representing the identity matrix. This transform does not
  303.      * modify point or vector values when applied.
  304.      * @return transform representing the identity matrix
  305.      */
  306.     public static AffineTransformMatrix1D identity() {
  307.         return IDENTITY_INSTANCE;
  308.     }

  309.     /** Get a transform representing the given translation.
  310.      * @param translation vector containing translation values for each axis
  311.      * @return a new transform representing the given translation
  312.      */
  313.     public static AffineTransformMatrix1D createTranslation(final Vector1D translation) {
  314.         return createTranslation(translation.getX());
  315.     }

  316.     /** Get a transform representing the given translation.
  317.      * @param x translation in the x direction
  318.      * @return a new transform representing the given translation
  319.      */
  320.     public static AffineTransformMatrix1D createTranslation(final double x) {
  321.         return new AffineTransformMatrix1D(1, x);
  322.     }

  323.     /** Get a transform representing a scale operation.
  324.      * @param factor vector containing the scale factor
  325.      * @return a new transform representing a scale operation
  326.      */
  327.     public static AffineTransformMatrix1D createScale(final Vector1D factor) {
  328.         return createScale(factor.getX());
  329.     }

  330.     /** Get a transform representing a scale operation.
  331.      * @param factor scale factor
  332.      * @return a new transform representing a scale operation
  333.      */
  334.     public static AffineTransformMatrix1D createScale(final double factor) {
  335.         return new AffineTransformMatrix1D(factor, 0);
  336.     }

  337.     /** Multiply two transform matrices together.
  338.      * @param a first transform
  339.      * @param b second transform
  340.      * @return the transform computed as {@code a x b}
  341.      */
  342.     private static AffineTransformMatrix1D multiply(final AffineTransformMatrix1D a,
  343.             final AffineTransformMatrix1D b) {

  344.         // calculate the matrix elements
  345.         final double c00 = a.m00 * b.m00;
  346.         final double c01 = (a.m00 * b.m01) + a.m01;

  347.         return new AffineTransformMatrix1D(c00, c01);
  348.     }
  349. }