| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.geometry.euclidean.oned; |
| |
| import org.apache.commons.geometry.core.internal.DoubleFunction1N; |
| import org.apache.commons.geometry.euclidean.AbstractAffineTransformMatrix; |
| import org.apache.commons.geometry.euclidean.exception.NonInvertibleTransformException; |
| import org.apache.commons.geometry.euclidean.internal.Vectors; |
| import org.apache.commons.numbers.core.Precision; |
| |
| /** Class using a matrix to represent affine transformations in 1 dimensional Euclidean space. |
| * |
| * <p>Instances of this class use a 2x2 matrix for all transform operations. |
| * The last row of this matrix is always set to the values <code>[0 1]</code> and so |
| * is not stored. Hence, the methods in this class that accept or return arrays always |
| * use arrays containing 2 elements, instead of 4. |
| * </p> |
| */ |
| public final class AffineTransformMatrix1D extends AbstractAffineTransformMatrix<Vector1D> |
| implements Transform1D { |
| /** The number of internal matrix elements. */ |
| private static final int NUM_ELEMENTS = 2; |
| |
| /** String used to start the transform matrix string representation. */ |
| private static final String MATRIX_START = "[ "; |
| |
| /** String used to end the transform matrix string representation. */ |
| private static final String MATRIX_END = " ]"; |
| |
| /** String used to separate elements in the matrix string representation. */ |
| private static final String ELEMENT_SEPARATOR = ", "; |
| |
| /** Shared transform set to the identity matrix. */ |
| private static final AffineTransformMatrix1D IDENTITY_INSTANCE = new AffineTransformMatrix1D(1, 0); |
| |
| /** Transform matrix entry <code>m<sub>0,0</sub></code>. */ |
| private final double m00; |
| /** Transform matrix entry <code>m<sub>0,1</sub></code>. */ |
| private final double m01; |
| |
| /** |
| * Simple constructor; sets all internal matrix elements. |
| * @param m00 matrix entry <code>m<sub>0,0</sub></code> |
| * @param m01 matrix entry <code>m<sub>0,1</sub></code> |
| */ |
| private AffineTransformMatrix1D(final double m00, final double m01) { |
| this.m00 = m00; |
| this.m01 = m01; |
| } |
| |
| /** Return a 2 element array containing the variable elements from the |
| * internal transformation matrix. The elements are in row-major order. |
| * The array indices map to the internal matrix as follows: |
| * <pre> |
| * [ |
| * arr[0], arr[1], |
| * 0 1 |
| * ] |
| * </pre> |
| * @return 2 element array containing the variable elements from the |
| * internal transformation matrix |
| */ |
| public double[] toArray() { |
| return new double[] { |
| m00, m01 |
| }; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public Vector1D apply(final Vector1D vec) { |
| final double x = vec.getX(); |
| |
| final double resultX = (m00 * x) + m01; |
| |
| return Vector1D.of(resultX); |
| } |
| |
| /** {@inheritDoc} |
| * @see #applyDirection(Vector1D) |
| */ |
| @Override |
| public Vector1D applyVector(final Vector1D vec) { |
| return applyVector(vec, Vector1D::of); |
| } |
| |
| /** {@inheritDoc} |
| * @see #applyVector(Vector1D) |
| */ |
| @Override |
| public Vector1D.Unit applyDirection(final Vector1D vec) { |
| return applyVector(vec, Vector1D.Unit::from); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public double determinant() { |
| return m00; |
| } |
| |
| /** {@inheritDoc} |
| * |
| * <p>This simply returns the current instance.</p> |
| */ |
| @Override |
| public AffineTransformMatrix1D toMatrix() { |
| return this; |
| } |
| |
| /** Get a new transform containing the result of applying a translation logically after |
| * the transformation represented by the current instance. This is achieved by |
| * creating a new translation transform and pre-multiplying it with the current |
| * instance. In other words, the returned transform contains the matrix |
| * <code>B * A</code>, where <code>A</code> is the current matrix and <code>B</code> |
| * is the matrix representing the given translation. |
| * @param translation vector containing the translation values for each axis |
| * @return a new transform containing the result of applying a translation to |
| * the current instance |
| */ |
| public AffineTransformMatrix1D translate(final Vector1D translation) { |
| return translate(translation.getX()); |
| } |
| |
| /** Get a new transform containing the result of applying a translation logically after |
| * the transformation represented by the current instance. This is achieved by |
| * creating a new translation transform and pre-multiplying it with the current |
| * instance. In other words, the returned transform contains the matrix |
| * <code>B * A</code>, where <code>A</code> is the current matrix and <code>B</code> |
| * is the matrix representing the given translation. |
| * @param x translation in the x direction |
| * @return a new transform containing the result of applying a translation to |
| * the current instance |
| */ |
| public AffineTransformMatrix1D translate(final double x) { |
| return new AffineTransformMatrix1D(m00, m01 + x); |
| } |
| |
| /** Get a new transform containing the result of applying a scale operation |
| * logically after the transformation represented by the current instance. |
| * This is achieved by creating a new scale transform and pre-multiplying it with the current |
| * instance. In other words, the returned transform contains the matrix |
| * <code>B * A</code>, where <code>A</code> is the current matrix and <code>B</code> |
| * is the matrix representing the given scale operation. |
| * @param scaleFactor vector containing scale factors for each axis |
| * @return a new transform containing the result of applying a scale operation to |
| * the current instance |
| */ |
| public AffineTransformMatrix1D scale(final Vector1D scaleFactor) { |
| return scale(scaleFactor.getX()); |
| } |
| |
| /** Get a new transform containing the result of applying a scale operation |
| * logically after the transformation represented by the current instance. |
| * This is achieved by creating a new scale transform and pre-multiplying it with the current |
| * instance. In other words, the returned transform contains the matrix |
| * <code>B * A</code>, where <code>A</code> is the current matrix and <code>B</code> |
| * is the matrix representing the given scale operation. |
| * @param x scale factor |
| * @return a new transform containing the result of applying a scale operation to |
| * the current instance |
| */ |
| public AffineTransformMatrix1D scale(final double x) { |
| return new AffineTransformMatrix1D(m00 * x, m01 * x); |
| } |
| |
| /** Get a new transform created by multiplying this instance by the argument. |
| * This is equivalent to the expression {@code A * M} where {@code A} is the |
| * current transform matrix and {@code M} is the given transform matrix. In |
| * terms of transformations, applying the returned matrix is equivalent to |
| * applying {@code M} and <em>then</em> applying {@code A}. In other words, |
| * the rightmost transform is applied first. |
| * |
| * @param m the transform to multiply with |
| * @return the result of multiplying the current instance by the given |
| * transform matrix |
| */ |
| public AffineTransformMatrix1D multiply(final AffineTransformMatrix1D m) { |
| return multiply(this, m); |
| } |
| |
| /** Get a new transform created by multiplying the argument by this instance. |
| * This is equivalent to the expression {@code M * A} where {@code A} is the |
| * current transform matrix and {@code M} is the given transform matrix. In |
| * terms of transformations, applying the returned matrix is equivalent to |
| * applying {@code A} and <em>then</em> applying {@code M}. In other words, |
| * the rightmost transform is applied first. |
| * |
| * @param m the transform to multiply with |
| * @return the result of multiplying the given transform matrix by the current |
| * instance |
| */ |
| public AffineTransformMatrix1D premultiply(final AffineTransformMatrix1D m) { |
| return multiply(m, this); |
| } |
| |
| /** Get a new transform representing the inverse of the current instance. |
| * @return inverse transform |
| * @throws NonInvertibleTransformException if the transform matrix cannot be inverted |
| */ |
| public AffineTransformMatrix1D inverse() { |
| |
| final double det = this.m00; |
| |
| if (!Vectors.isRealNonZero(det)) { |
| throw new NonInvertibleTransformException("Transform is not invertible; matrix determinant is " + det); |
| } |
| |
| validateElementForInverse(m01); |
| |
| final double invDet = 1.0 / det; |
| |
| final double c00 = invDet; |
| final double c01 = -(this.m01 * invDet); |
| |
| return new AffineTransformMatrix1D(c00, c01); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| |
| result = (result * prime) + Double.hashCode(m00); |
| result = (result * prime) + Double.hashCode(m01); |
| |
| return result; |
| } |
| |
| /** |
| * Return true if the given object is an instance of {@link AffineTransformMatrix1D} |
| * and all matrix element values are exactly equal. |
| * @param obj object to test for equality with the current instance |
| * @return true if all transform matrix elements are exactly equal; otherwise false |
| */ |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (!(obj instanceof AffineTransformMatrix1D)) { |
| return false; |
| } |
| final AffineTransformMatrix1D other = (AffineTransformMatrix1D) obj; |
| |
| return Precision.equals(this.m00, other.m00) && |
| Precision.equals(this.m01, other.m01); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public String toString() { |
| final StringBuilder sb = new StringBuilder(); |
| |
| sb.append(MATRIX_START) |
| |
| .append(m00) |
| .append(ELEMENT_SEPARATOR) |
| .append(m01) |
| |
| .append(MATRIX_END); |
| |
| return sb.toString(); |
| } |
| |
| /** Multiplies the given vector by the scaling component of this transform. |
| * The computed coordinate is passed to the given factory function. |
| * @param <T> factory output type |
| * @param vec the vector to transform |
| * @param factory the factory instance that will be passed the transformed coordinate |
| * @return the factory return value |
| */ |
| private <T> T applyVector(final Vector1D vec, final DoubleFunction1N<T> factory) { |
| final double resultX = m00 * vec.getX(); |
| |
| return factory.apply(resultX); |
| } |
| |
| /** Get a new transform with the given matrix elements. The array must contain 2 elements. |
| * The first element in the array represents the scale factor for the transform and the |
| * second represents the translation. |
| * @param arr 2-element array containing values for the variable entries in the |
| * transform matrix |
| * @return a new transform initialized with the given matrix values |
| * @throws IllegalArgumentException if the array does not have 2 elements |
| */ |
| public static AffineTransformMatrix1D of(final double... arr) { |
| if (arr.length != NUM_ELEMENTS) { |
| throw new IllegalArgumentException("Dimension mismatch: " + arr.length + " != " + NUM_ELEMENTS); |
| } |
| |
| return new AffineTransformMatrix1D(arr[0], arr[1]); |
| } |
| |
| /** Get the transform representing the identity matrix. This transform does not |
| * modify point or vector values when applied. |
| * @return transform representing the identity matrix |
| */ |
| public static AffineTransformMatrix1D identity() { |
| return IDENTITY_INSTANCE; |
| } |
| |
| /** Get a transform representing the given translation. |
| * @param translation vector containing translation values for each axis |
| * @return a new transform representing the given translation |
| */ |
| public static AffineTransformMatrix1D createTranslation(final Vector1D translation) { |
| return createTranslation(translation.getX()); |
| } |
| |
| /** Get a transform representing the given translation. |
| * @param x translation in the x direction |
| * @return a new transform representing the given translation |
| */ |
| public static AffineTransformMatrix1D createTranslation(final double x) { |
| return new AffineTransformMatrix1D(1, x); |
| } |
| |
| /** Get a transform representing a scale operation. |
| * @param factor vector containing the scale factor |
| * @return a new transform representing a scale operation |
| */ |
| public static AffineTransformMatrix1D createScale(final Vector1D factor) { |
| return createScale(factor.getX()); |
| } |
| |
| /** Get a transform representing a scale operation. |
| * @param factor scale factor |
| * @return a new transform representing a scale operation |
| */ |
| public static AffineTransformMatrix1D createScale(final double factor) { |
| return new AffineTransformMatrix1D(factor, 0); |
| } |
| |
| /** Multiply two transform matrices together. |
| * @param a first transform |
| * @param b second transform |
| * @return the transform computed as {@code a x b} |
| */ |
| private static AffineTransformMatrix1D multiply(final AffineTransformMatrix1D a, |
| final AffineTransformMatrix1D b) { |
| |
| // calculate the matrix elements |
| final double c00 = a.m00 * b.m00; |
| final double c01 = (a.m00 * b.m01) + a.m01; |
| |
| return new AffineTransformMatrix1D(c00, c01); |
| } |
| |
| /** Checks that the given matrix element is valid for use in calculation of |
| * a matrix inverse. Throws a {@link NonInvertibleTransformException} if not. |
| * @param element matrix entry to check |
| * @throws NonInvertibleTransformException if the element is not valid for use |
| * in calculating a matrix inverse, ie if it is NaN or infinite. |
| */ |
| private static void validateElementForInverse(final double element) { |
| if (!Double.isFinite(element)) { |
| throw new NonInvertibleTransformException("Transform is not invertible; invalid matrix element: " + |
| element); |
| } |
| } |
| } |