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 }