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.twod;
18  
19  import org.apache.commons.geometry.core.Transform;
20  import org.apache.commons.geometry.euclidean.threed.AffineTransformMatrix3D;
21  import org.apache.commons.geometry.euclidean.threed.Vector3D;
22  import org.apache.commons.geometry.euclidean.threed.rotation.QuaternionRotation;
23  
24  /** Implementation of the {@link Transform} interface for spherical 2D points.
25   *
26   * <p>This class uses an {@link AffineTransformMatrix3D} to perform spherical point transforms
27   * in Euclidean 3D space.</p>
28   *
29   * <p>Instances of this class are guaranteed to be immutable.</p>
30   */
31  public final class Transform2S implements Transform<Point2S> {
32      /** Static instance representing the identity transform. */
33      private static final Transform2S IDENTITY = new Transform2S(AffineTransformMatrix3D.identity());
34  
35      /** Static transform instance that reflects across the x-y plane. */
36      private static final AffineTransformMatrix3D XY_PLANE_REFLECTION = AffineTransformMatrix3D.createScale(1, 1, -1);
37  
38      /** Euclidean transform matrix underlying the spherical transform. */
39      private final AffineTransformMatrix3D euclideanTransform;
40  
41      /** Construct a new instance from its underlying Euclidean transform.
42       * @param euclideanTransform underlying Euclidean transform
43       */
44      private Transform2S(final AffineTransformMatrix3D euclideanTransform) {
45          this.euclideanTransform = euclideanTransform;
46      }
47  
48      /** Get the Euclidean transform matrix underlying the spherical transform.
49       * @return the Euclidean transform matrix underlying the spherical transform
50       */
51      public AffineTransformMatrix3D getEuclideanTransform() {
52          return euclideanTransform;
53      }
54  
55      /** {@inheritDoc} */
56      @Override
57      public Point2S apply(final Point2S pt) {
58          final Vector3D vec = pt.getVector();
59          return Point2S.from(euclideanTransform.apply(vec));
60      }
61  
62      /** {@inheritDoc} */
63      @Override
64      public boolean preservesOrientation() {
65          return euclideanTransform.preservesOrientation();
66      }
67  
68      /** {@inheritDoc} */
69      @Override
70      public Transform2S inverse() {
71          return new Transform2S(euclideanTransform.inverse());
72      }
73  
74      /** Apply a rotation of {@code angle} radians around the given point to this instance.
75       * @param pt point to rotate around
76       * @param angle rotation angle in radians
77       * @return transform resulting from applying the specified rotation to this instance
78       */
79      public Transform2S rotate(final Point2S pt, final double angle) {
80          return premultiply(createRotation(pt, angle));
81      }
82  
83      /** Apply a rotation of {@code angle} radians around the given 3D axis to this instance.
84       * @param axis 3D axis of rotation
85       * @param angle rotation angle in radians
86       * @return transform resulting from applying the specified rotation to this instance
87       */
88      public Transform2S rotate(final Vector3D axis, final double angle) {
89          return premultiply(createRotation(axis, angle));
90      }
91  
92      /** Apply the given quaternion rotation to this instance.
93       * @param quaternion quaternion rotation to apply
94       * @return transform resulting from applying the specified rotation to this instance
95       */
96      public Transform2S rotate(final QuaternionRotation quaternion) {
97          return premultiply(createRotation(quaternion));
98      }
99  
100     /** Apply a reflection across the equatorial plane defined by the given pole point
101      * to this instance.
102      * @param pole pole point defining the equatorial reflection plane
103      * @return transform resulting from applying the specified reflection to this instance
104      */
105     public Transform2S reflect(final Point2S pole) {
106         return premultiply(createReflection(pole));
107     }
108 
109     /** Apply a reflection across the equatorial plane defined by the given pole vector
110      * to this instance.
111      * @param poleVector pole vector defining the equatorial reflection plane
112      * @return transform resulting from applying the specified reflection to this instance
113      */
114     public Transform2S reflect(final Vector3D poleVector) {
115         return premultiply(createReflection(poleVector));
116     }
117 
118     /** Multiply the underlying Euclidean transform of this instance by that of the argument, eg,
119      * {@code other * this}. The returned transform performs the equivalent of
120      * {@code other} followed by {@code this}.
121      * @param other transform to multiply with
122      * @return a new transform computed by multiplying the matrix of this
123      *      instance by that of the argument
124      * @see AffineTransformMatrix3D#multiply(AffineTransformMatrix3D)
125      */
126     public Transform2S multiply(final Transform2S other) {
127         return multiply(this, other);
128     }
129 
130     /** Multiply the underlying Euclidean transform matrix of the argument by that of this instance, eg,
131      * {@code this * other}. The returned transform performs the equivalent of {@code this}
132      * followed by {@code other}.
133      * @param other transform to multiply with
134      * @return a new transform computed by multiplying the matrix of the
135      *      argument by that of this instance
136      * @see AffineTransformMatrix3D#premultiply(AffineTransformMatrix3D)
137      */
138     public Transform2S premultiply(final Transform2S other) {
139         return multiply(other, this);
140     }
141 
142     /** {@inheritDoc} */
143     @Override
144     public int hashCode() {
145         return euclideanTransform.hashCode();
146     }
147 
148     /**
149      * Return true if the given object is an instance of {@link Transform2S}
150      * and the underlying Euclidean transform matrices are exactly equal.
151      * @param obj object to test for equality with the current instance
152      * @return true if the underlying transform matrices are exactly equal
153      */
154     @Override
155     public boolean equals(final Object obj) {
156         if (this == obj) {
157             return true;
158         }
159         if (!(obj instanceof Transform2S)) {
160             return false;
161         }
162         final Transform2S other = (Transform2S) obj;
163 
164         return euclideanTransform.equals(other.euclideanTransform);
165     }
166 
167     /** {@inheritDoc} */
168     @Override
169     public String toString() {
170         final StringBuilder sb = new StringBuilder();
171 
172         sb.append(this.getClass().getSimpleName())
173             .append("[euclideanTransform= ")
174             .append(getEuclideanTransform())
175             .append(']');
176 
177         return sb.toString();
178     }
179 
180     /** Return an instance representing the identity transform. This transform is guaranteed
181      * to return an <em>equivalent</em> (ie, co-located) point for any input point. However, the
182      * points are not guaranteed to contain exactly equal coordinates. For example, at the poles, an
183      * infinite number of points exist that vary only in the azimuth coordinate. When one of these
184      * points is transformed by this identity transform, the returned point may contain a different
185      * azimuth value from the input, but it will still represent the same location in space.
186      * @return an instance representing the identity transform
187      */
188     public static Transform2S identity() {
189         return IDENTITY;
190     }
191 
192     /** Create a transform that rotates the given angle around {@code pt}.
193      * @param pt point to rotate around
194      * @param angle angle of rotation in radians
195      * @return a transform that rotates the given angle around {@code pt}
196      */
197     public static Transform2S createRotation(final Point2S pt, final double angle) {
198         return createRotation(pt.getVector(), angle);
199     }
200 
201     /** Create a transform that rotates the given angle around {@code axis}.
202      * @param axis 3D axis of rotation
203      * @param angle angle of rotation in radians
204      * @return a transform that rotates the given angle {@code axis}
205      */
206     public static Transform2S createRotation(final Vector3D axis, final double angle) {
207         return createRotation(QuaternionRotation.fromAxisAngle(axis, angle));
208     }
209 
210     /** Create a transform that performs the given 3D rotation.
211      * @param quaternion quaternion instance representing the 3D rotation
212      * @return a transform that performs the given 3D rotation
213      */
214     public static Transform2S createRotation(final QuaternionRotation quaternion) {
215         return new Transform2S(quaternion.toMatrix());
216     }
217 
218     /** Create a transform that performs a reflection across the equatorial plane
219      * defined by the given pole point.
220      * @param pole pole point defining the equatorial reflection plane
221      * @return a transform that performs a reflection across the equatorial plane
222      *      defined by the given pole point
223      */
224     public static Transform2S createReflection(final Point2S pole) {
225         return createReflection(pole.getVector());
226     }
227 
228     /** Create a transform that performs a reflection across the equatorial plane
229      * defined by the given pole point.
230      * @param poleVector pole vector defining the equatorial reflection plane
231      * @return a transform that performs a reflection across the equatorial plane
232      *      defined by the given pole point
233      */
234     public static Transform2S createReflection(final Vector3D poleVector) {
235         final QuaternionRotation quat = QuaternionRotation.createVectorRotation(poleVector, Vector3D.Unit.PLUS_Z);
236 
237         final AffineTransformMatrix3D matrix = quat.toMatrix()
238                 .premultiply(XY_PLANE_REFLECTION)
239                 .premultiply(quat.inverse().toMatrix());
240 
241         return new Transform2S(matrix);
242     }
243 
244     /** Multiply the Euclidean transform matrices of the arguments together.
245      * @param a first transform
246      * @param b second transform
247      * @return the transform computed as {@code a x b}
248      */
249     private static Transform2S multiply(final Transform2S a, final Transform2S b) {
250 
251         return new Transform2S(a.euclideanTransform.multiply(b.euclideanTransform));
252     }
253 }