Transform2S.java

  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. import org.apache.commons.geometry.core.Transform;
  19. import org.apache.commons.geometry.euclidean.threed.AffineTransformMatrix3D;
  20. import org.apache.commons.geometry.euclidean.threed.Vector3D;
  21. import org.apache.commons.geometry.euclidean.threed.rotation.QuaternionRotation;

  22. /** Implementation of the {@link Transform} interface for spherical 2D points.
  23.  *
  24.  * <p>This class uses an {@link AffineTransformMatrix3D} to perform spherical point transforms
  25.  * in Euclidean 3D space.</p>
  26.  *
  27.  * <p>Instances of this class are guaranteed to be immutable.</p>
  28.  */
  29. public final class Transform2S implements Transform<Point2S> {
  30.     /** Static instance representing the identity transform. */
  31.     private static final Transform2S IDENTITY = new Transform2S(AffineTransformMatrix3D.identity());

  32.     /** Static transform instance that reflects across the x-y plane. */
  33.     private static final AffineTransformMatrix3D XY_PLANE_REFLECTION = AffineTransformMatrix3D.createScale(1, 1, -1);

  34.     /** Euclidean transform matrix underlying the spherical transform. */
  35.     private final AffineTransformMatrix3D euclideanTransform;

  36.     /** Construct a new instance from its underlying Euclidean transform.
  37.      * @param euclideanTransform underlying Euclidean transform
  38.      */
  39.     private Transform2S(final AffineTransformMatrix3D euclideanTransform) {
  40.         this.euclideanTransform = euclideanTransform;
  41.     }

  42.     /** Get the Euclidean transform matrix underlying the spherical transform.
  43.      * @return the Euclidean transform matrix underlying the spherical transform
  44.      */
  45.     public AffineTransformMatrix3D getEuclideanTransform() {
  46.         return euclideanTransform;
  47.     }

  48.     /** {@inheritDoc} */
  49.     @Override
  50.     public Point2S apply(final Point2S pt) {
  51.         final Vector3D vec = pt.getVector();
  52.         return Point2S.from(euclideanTransform.apply(vec));
  53.     }

  54.     /** {@inheritDoc} */
  55.     @Override
  56.     public boolean preservesOrientation() {
  57.         return euclideanTransform.preservesOrientation();
  58.     }

  59.     /** {@inheritDoc} */
  60.     @Override
  61.     public Transform2S inverse() {
  62.         return new Transform2S(euclideanTransform.inverse());
  63.     }

  64.     /** Apply a rotation of {@code angle} radians around the given point to this instance.
  65.      * @param pt point to rotate around
  66.      * @param angle rotation angle in radians
  67.      * @return transform resulting from applying the specified rotation to this instance
  68.      */
  69.     public Transform2S rotate(final Point2S pt, final double angle) {
  70.         return premultiply(createRotation(pt, angle));
  71.     }

  72.     /** Apply a rotation of {@code angle} radians around the given 3D axis to this instance.
  73.      * @param axis 3D axis of rotation
  74.      * @param angle rotation angle in radians
  75.      * @return transform resulting from applying the specified rotation to this instance
  76.      */
  77.     public Transform2S rotate(final Vector3D axis, final double angle) {
  78.         return premultiply(createRotation(axis, angle));
  79.     }

  80.     /** Apply the given quaternion rotation to this instance.
  81.      * @param quaternion quaternion rotation to apply
  82.      * @return transform resulting from applying the specified rotation to this instance
  83.      */
  84.     public Transform2S rotate(final QuaternionRotation quaternion) {
  85.         return premultiply(createRotation(quaternion));
  86.     }

  87.     /** Apply a reflection across the equatorial plane defined by the given pole point
  88.      * to this instance.
  89.      * @param pole pole point defining the equatorial reflection plane
  90.      * @return transform resulting from applying the specified reflection to this instance
  91.      */
  92.     public Transform2S reflect(final Point2S pole) {
  93.         return premultiply(createReflection(pole));
  94.     }

  95.     /** Apply a reflection across the equatorial plane defined by the given pole vector
  96.      * to this instance.
  97.      * @param poleVector pole vector defining the equatorial reflection plane
  98.      * @return transform resulting from applying the specified reflection to this instance
  99.      */
  100.     public Transform2S reflect(final Vector3D poleVector) {
  101.         return premultiply(createReflection(poleVector));
  102.     }

  103.     /** Multiply the underlying Euclidean transform of this instance by that of the argument, eg,
  104.      * {@code other * this}. The returned transform performs the equivalent of
  105.      * {@code other} followed by {@code this}.
  106.      * @param other transform to multiply with
  107.      * @return a new transform computed by multiplying the matrix of this
  108.      *      instance by that of the argument
  109.      * @see AffineTransformMatrix3D#multiply(AffineTransformMatrix3D)
  110.      */
  111.     public Transform2S multiply(final Transform2S other) {
  112.         return multiply(this, other);
  113.     }

  114.     /** Multiply the underlying Euclidean transform matrix of the argument by that of this instance, eg,
  115.      * {@code this * other}. The returned transform performs the equivalent of {@code this}
  116.      * followed by {@code other}.
  117.      * @param other transform to multiply with
  118.      * @return a new transform computed by multiplying the matrix of the
  119.      *      argument by that of this instance
  120.      * @see AffineTransformMatrix3D#premultiply(AffineTransformMatrix3D)
  121.      */
  122.     public Transform2S premultiply(final Transform2S other) {
  123.         return multiply(other, this);
  124.     }

  125.     /** {@inheritDoc} */
  126.     @Override
  127.     public int hashCode() {
  128.         return euclideanTransform.hashCode();
  129.     }

  130.     /**
  131.      * Return true if the given object is an instance of {@link Transform2S}
  132.      * and the underlying Euclidean transform matrices are exactly equal.
  133.      * @param obj object to test for equality with the current instance
  134.      * @return true if the underlying transform matrices are exactly equal
  135.      */
  136.     @Override
  137.     public boolean equals(final Object obj) {
  138.         if (this == obj) {
  139.             return true;
  140.         }
  141.         if (!(obj instanceof Transform2S)) {
  142.             return false;
  143.         }
  144.         final Transform2S other = (Transform2S) obj;

  145.         return euclideanTransform.equals(other.euclideanTransform);
  146.     }

  147.     /** {@inheritDoc} */
  148.     @Override
  149.     public String toString() {
  150.         final StringBuilder sb = new StringBuilder();

  151.         sb.append(this.getClass().getSimpleName())
  152.             .append("[euclideanTransform= ")
  153.             .append(getEuclideanTransform())
  154.             .append(']');

  155.         return sb.toString();
  156.     }

  157.     /** Return an instance representing the identity transform. This transform is guaranteed
  158.      * to return an <em>equivalent</em> (ie, co-located) point for any input point. However, the
  159.      * points are not guaranteed to contain exactly equal coordinates. For example, at the poles, an
  160.      * infinite number of points exist that vary only in the azimuth coordinate. When one of these
  161.      * points is transformed by this identity transform, the returned point may contain a different
  162.      * azimuth value from the input, but it will still represent the same location in space.
  163.      * @return an instance representing the identity transform
  164.      */
  165.     public static Transform2S identity() {
  166.         return IDENTITY;
  167.     }

  168.     /** Create a transform that rotates the given angle around {@code pt}.
  169.      * @param pt point to rotate around
  170.      * @param angle angle of rotation in radians
  171.      * @return a transform that rotates the given angle around {@code pt}
  172.      */
  173.     public static Transform2S createRotation(final Point2S pt, final double angle) {
  174.         return createRotation(pt.getVector(), angle);
  175.     }

  176.     /** Create a transform that rotates the given angle around {@code axis}.
  177.      * @param axis 3D axis of rotation
  178.      * @param angle angle of rotation in radians
  179.      * @return a transform that rotates the given angle {@code axis}
  180.      */
  181.     public static Transform2S createRotation(final Vector3D axis, final double angle) {
  182.         return createRotation(QuaternionRotation.fromAxisAngle(axis, angle));
  183.     }

  184.     /** Create a transform that performs the given 3D rotation.
  185.      * @param quaternion quaternion instance representing the 3D rotation
  186.      * @return a transform that performs the given 3D rotation
  187.      */
  188.     public static Transform2S createRotation(final QuaternionRotation quaternion) {
  189.         return new Transform2S(quaternion.toMatrix());
  190.     }

  191.     /** Create a transform that performs a reflection across the equatorial plane
  192.      * defined by the given pole point.
  193.      * @param pole pole point defining the equatorial reflection plane
  194.      * @return a transform that performs a reflection across the equatorial plane
  195.      *      defined by the given pole point
  196.      */
  197.     public static Transform2S createReflection(final Point2S pole) {
  198.         return createReflection(pole.getVector());
  199.     }

  200.     /** Create a transform that performs a reflection across the equatorial plane
  201.      * defined by the given pole point.
  202.      * @param poleVector pole vector defining the equatorial reflection plane
  203.      * @return a transform that performs a reflection across the equatorial plane
  204.      *      defined by the given pole point
  205.      */
  206.     public static Transform2S createReflection(final Vector3D poleVector) {
  207.         final QuaternionRotation quat = QuaternionRotation.createVectorRotation(poleVector, Vector3D.Unit.PLUS_Z);

  208.         final AffineTransformMatrix3D matrix = quat.toMatrix()
  209.                 .premultiply(XY_PLANE_REFLECTION)
  210.                 .premultiply(quat.inverse().toMatrix());

  211.         return new Transform2S(matrix);
  212.     }

  213.     /** Multiply the Euclidean transform matrices of the arguments together.
  214.      * @param a first transform
  215.      * @param b second transform
  216.      * @return the transform computed as {@code a x b}
  217.      */
  218.     private static Transform2S multiply(final Transform2S a, final Transform2S b) {

  219.         return new Transform2S(a.euclideanTransform.multiply(b.euclideanTransform));
  220.     }
  221. }