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 }