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.spherical.oned; 18 19 import org.apache.commons.geometry.core.Transform; 20 21 /** Implementation of the {@link Transform} interface for spherical 1D points. 22 * 23 * <p>Similar to the Euclidean 1D 24 * {@link org.apache.commons.geometry.euclidean.oned.AffineTransformMatrix1D AffineTransformMatrix1D}, 25 * this class performs transformations using an internal 1D affine transformation matrix. In the 26 * Euclidean case, the matrix contains a scale factor and a translation. Here, the matrix contains 27 * a scale/negation factor that takes the values -1 or +1, and a rotation value. This restriction on 28 * the allowed values in the matrix is required in order to fulfill the geometric requirements 29 * of the {@link Transform} interface. For example, if arbitrary scaling is allowed, the point {@code 0.5pi} 30 * could be scaled by 4 to {@code 2pi}, which is equivalent to {@code 0pi}. However, if the inverse scaling 31 * of {@code 1/4} is applied to {@code 0pi}, the result is {@code 0pi} and not {@code 0.5pi}. This breaks 32 * the {@link Transform} requirement that transforms be inversible. 33 * </p> 34 * 35 * <p>Instances of this class are guaranteed to be immutable.</p> 36 */ 37 public final class Transform1S implements Transform<Point1S> { 38 /** Static instance representing the identity transform. */ 39 private static final Transform1S IDENTITY = new Transform1S(1, 0); 40 41 /** Static instance that negates azimuth values. */ 42 private static final Transform1S NEGATION = new Transform1S(-1, 0); 43 44 /** Value to scale the point azimuth by. This will only be +1/-1. */ 45 private final double scale; 46 47 /** Value to rotate the point azimuth by. */ 48 private final double rotate; 49 50 /** Construct a new instance from its transform components. 51 * @param scale scale value for the transform; must only be +1 or -1 52 * @param rotate rotation value 53 */ 54 private Transform1S(final double scale, final double rotate) { 55 this.scale = scale; 56 this.rotate = rotate; 57 } 58 59 /** Return true if the transform negates the azimuth values of transformed 60 * points, regardless of any rotation applied subsequently. 61 * @return true if the transform negates the azimuth values of transformed 62 * points 63 * @see #preservesOrientation() 64 */ 65 public boolean isNegation() { 66 return scale <= 0; 67 } 68 69 /** Get the rotation value applied by this instance, in radians. 70 * @return the rotation value applied by this instance, in radians. 71 */ 72 public double getRotation() { 73 return rotate; 74 } 75 76 /** {@inheritDoc} */ 77 @Override 78 public Point1S apply(final Point1S pt) { 79 final double az = pt.getAzimuth(); 80 final double resultAz = (az * scale) + rotate; 81 82 return Point1S.of(resultAz); 83 } 84 85 /** {@inheritDoc} */ 86 @Override 87 public boolean preservesOrientation() { 88 return !isNegation(); 89 } 90 91 /** Return a new transform created by pre-multiplying this instance by a transform 92 * producing a rotation with the given angle. 93 * @param angle angle to rotate, in radians 94 * @return a new transform created by pre-multiplying this instance by a transform 95 * producing a rotation with the given angle 96 * @see #createRotation(double) 97 */ 98 public Transform1S rotate(final double angle) { 99 return premultiply(createRotation(angle)); 100 } 101 102 /** Return a new transform created by pre-multiplying this instance by a transform 103 * that negates azimuth values. 104 * @return a new transform created by pre-multiplying this instance by a transform 105 * that negates azimuth values 106 */ 107 public Transform1S negate() { 108 return premultiply(createNegation()); 109 } 110 111 /** Multiply the underlying matrix of this instance by that of the argument, eg, 112 * {@code other * this}. The returned transform performs the equivalent of 113 * {@code other} followed by {@code this}. 114 * @param other transform to multiply with 115 * @return a new transform computed by multiplying the matrix of this 116 * instance by that of the argument 117 */ 118 public Transform1S multiply(final Transform1S other) { 119 return multiply(this, other); 120 } 121 122 /** Multiply the underlying matrix of the argument by that of this instance, eg, 123 * {@code this * other}. The returned transform performs the equivalent of {@code this} 124 * followed by {@code other}. 125 * @param other transform to multiply with 126 * @return a new transform computed by multiplying the matrix of the 127 * argument by that of this instance 128 */ 129 public Transform1S premultiply(final Transform1S other) { 130 return multiply(other, this); 131 } 132 133 /** {@inheritDoc} */ 134 @Override 135 public Transform1S inverse() { 136 final double invScale = 1.0 / scale; 137 138 final double resultRotate = -(rotate * invScale); 139 140 return new Transform1S(invScale, resultRotate); 141 } 142 143 /** {@inheritDoc} */ 144 @Override 145 public int hashCode() { 146 final int prime = 31; 147 int result = 1; 148 149 result = (result * prime) + Double.hashCode(scale); 150 result = (result * prime) + Double.hashCode(rotate); 151 152 return result; 153 } 154 155 /** 156 * Return true if the given object is an instance of {@link Transform1S} 157 * and all transform element values are exactly equal. 158 * @param obj object to test for equality with the current instance 159 * @return true if all transform elements are exactly equal; otherwise false 160 */ 161 @Override 162 public boolean equals(final Object obj) { 163 if (this == obj) { 164 return true; 165 } 166 if (!(obj instanceof Transform1S)) { 167 return false; 168 } 169 final Transform1S other = (Transform1S) obj; 170 171 return Double.compare(scale, other.scale) == 0 && 172 Double.compare(rotate, other.rotate) == 0; 173 } 174 175 /** {@inheritDoc} */ 176 @Override 177 public String toString() { 178 final StringBuilder sb = new StringBuilder(); 179 180 sb.append(this.getClass().getSimpleName()) 181 .append("[negate= ") 182 .append(isNegation()) 183 .append(", rotate= ") 184 .append(getRotation()) 185 .append(']'); 186 187 return sb.toString(); 188 } 189 190 /** Return a transform instance representing the identity transform. 191 * @return a transform instance representing the identity transform 192 */ 193 public static Transform1S identity() { 194 return IDENTITY; 195 } 196 197 /** Return a transform instance that negates azimuth values. 198 * @return a transform instance that negates azimuth values. 199 */ 200 public static Transform1S createNegation() { 201 return NEGATION; 202 } 203 204 /** Return a transform instance that performs a rotation with the given 205 * angle. 206 * @param angle angle of the rotation, in radians 207 * @return a transform instance that performs a rotation with the given 208 * angle 209 */ 210 public static Transform1S createRotation(final double angle) { 211 return new Transform1S(1, angle); 212 } 213 214 /** Multiply two transforms together as matrices. 215 * @param a first transform 216 * @param b second transform 217 * @return the transform computed as {@code a x b} 218 */ 219 private static Transform1S multiply(final Transform1S a, final Transform1S b) { 220 221 // calculate the matrix elements 222 final double resultScale = a.scale * b.scale; 223 final double resultRotate = (a.scale * b.rotate) + a.rotate; 224 225 return new Transform1S(resultScale, resultRotate); 226 } 227 }