View Javadoc
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 }