001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.geometry.euclidean.oned;
018
019import java.util.function.UnaryOperator;
020
021import org.apache.commons.geometry.euclidean.AbstractAffineTransformMatrix;
022import org.apache.commons.geometry.euclidean.internal.Matrices;
023import org.apache.commons.geometry.euclidean.internal.Vectors;
024
025/** Class using a matrix to represent affine transformations in 1 dimensional Euclidean space.
026*
027* <p>Instances of this class use a 2x2 matrix for all transform operations.
028* The last row of this matrix is always set to the values <code>[0 1]</code> and so
029* is not stored. Hence, the methods in this class that accept or return arrays always
030* use arrays containing 2 elements, instead of 4.
031* </p>
032*/
033public final class AffineTransformMatrix1D extends AbstractAffineTransformMatrix<Vector1D, AffineTransformMatrix1D> {
034    /** The number of internal matrix elements. */
035    private static final int NUM_ELEMENTS = 2;
036
037    /** String used to start the transform matrix string representation. */
038    private static final String MATRIX_START = "[ ";
039
040    /** String used to end the transform matrix string representation. */
041    private static final String MATRIX_END = " ]";
042
043    /** String used to separate elements in the matrix string representation. */
044    private static final String ELEMENT_SEPARATOR = ", ";
045
046    /** Shared transform set to the identity matrix. */
047    private static final AffineTransformMatrix1D IDENTITY_INSTANCE = new AffineTransformMatrix1D(1, 0);
048
049    /** Transform matrix entry <code>m<sub>0,0</sub></code>. */
050    private final double m00;
051    /** Transform matrix entry <code>m<sub>0,1</sub></code>. */
052    private final double m01;
053
054    /**
055     * Simple constructor; sets all internal matrix elements.
056     * @param m00 matrix entry <code>m<sub>0,0</sub></code>
057     * @param m01 matrix entry <code>m<sub>0,1</sub></code>
058     */
059    private AffineTransformMatrix1D(final double m00, final double m01) {
060        this.m00 = m00;
061        this.m01 = m01;
062    }
063
064    /** Return a 2 element array containing the variable elements from the
065     * internal transformation matrix. The elements are in row-major order.
066     * The array indices map to the internal matrix as follows:
067     * <pre>
068     *      [
069     *          arr[0],   arr[1],
070     *          0         1
071     *      ]
072     * </pre>
073     * @return 2 element array containing the variable elements from the
074     *      internal transformation matrix
075     */
076    public double[] toArray() {
077        return new double[] {
078            m00, m01
079        };
080    }
081
082    /** {@inheritDoc} */
083    @Override
084    public Vector1D apply(final Vector1D vec) {
085        return Vector1D.of(applyX(vec.getX()));
086    }
087
088    /** Apply this transform to the given point coordinate and return the transformed
089     * x value. The return value is equal to <code>(x * m<sub>00</sub>) + m<sub>01</sub></code>.
090     * @param x x coordinate value
091     * @return transformed x coordinate value
092     * @see #apply(Vector1D)
093     */
094    public double applyX(final double x) {
095        return applyVectorX(x) + m01;
096    }
097
098    /** {@inheritDoc}
099     * @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}