001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.geometry.spherical.oned; 018 019import org.apache.commons.geometry.core.Transform; 020 021/** Implementation of the {@link Transform} interface for spherical 1D points. 022 * 023 * <p>Similar to the Euclidean 1D 024 * {@link org.apache.commons.geometry.euclidean.oned.AffineTransformMatrix1D AffineTransformMatrix1D}, 025 * this class performs transformations using an internal 1D affine transformation matrix. In the 026 * Euclidean case, the matrix contains a scale factor and a translation. Here, the matrix contains 027 * a scale/negation factor that takes the values -1 or +1, and a rotation value. This restriction on 028 * the allowed values in the matrix is required in order to fulfill the geometric requirements 029 * of the {@link Transform} interface. For example, if arbitrary scaling is allowed, the point {@code 0.5pi} 030 * could be scaled by 4 to {@code 2pi}, which is equivalent to {@code 0pi}. However, if the inverse scaling 031 * of {@code 1/4} is applied to {@code 0pi}, the result is {@code 0pi} and not {@code 0.5pi}. This breaks 032 * the {@link Transform} requirement that transforms be inversible. 033 * </p> 034 * 035 * <p>Instances of this class are guaranteed to be immutable.</p> 036 */ 037public final class Transform1S implements Transform<Point1S> { 038 /** Static instance representing the identity transform. */ 039 private static final Transform1S IDENTITY = new Transform1S(1, 0); 040 041 /** Static instance that negates azimuth values. */ 042 private static final Transform1S NEGATION = new Transform1S(-1, 0); 043 044 /** Value to scale the point azimuth by. This will only be +1/-1. */ 045 private final double scale; 046 047 /** Value to rotate the point azimuth by. */ 048 private final double rotate; 049 050 /** Construct a new instance from its transform components. 051 * @param scale scale value for the transform; must only be +1 or -1 052 * @param rotate rotation value 053 */ 054 private Transform1S(final double scale, final double rotate) { 055 this.scale = scale; 056 this.rotate = rotate; 057 } 058 059 /** Return true if the transform negates the azimuth values of transformed 060 * points, regardless of any rotation applied subsequently. 061 * @return true if the transform negates the azimuth values of transformed 062 * points 063 * @see #preservesOrientation() 064 */ 065 public boolean isNegation() { 066 return scale <= 0; 067 } 068 069 /** Get the rotation value applied by this instance, in radians. 070 * @return the rotation value applied by this instance, in radians. 071 */ 072 public double getRotation() { 073 return rotate; 074 } 075 076 /** {@inheritDoc} */ 077 @Override 078 public Point1S apply(final Point1S pt) { 079 final double az = pt.getAzimuth(); 080 final double resultAz = (az * scale) + rotate; 081 082 return Point1S.of(resultAz); 083 } 084 085 /** {@inheritDoc} */ 086 @Override 087 public boolean preservesOrientation() { 088 return !isNegation(); 089 } 090 091 /** Return a new transform created by pre-multiplying this instance by a transform 092 * producing a rotation with the given angle. 093 * @param angle angle to rotate, in radians 094 * @return a new transform created by pre-multiplying this instance by a transform 095 * producing a rotation with the given angle 096 * @see #createRotation(double) 097 */ 098 public Transform1S rotate(final double angle) { 099 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}