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 }