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 }