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 }