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 ] → [ 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 ] → [ 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 }