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