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.euclidean.oned;
    18
    19  import java.util.Comparator;
    20  import java.util.function.UnaryOperator;
    21
    22  import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
    23  import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
    24  import org.apache.commons.geometry.euclidean.EuclideanVector;
    25  import org.apache.commons.geometry.euclidean.internal.Vectors;
    26  import org.apache.commons.numbers.angle.PlaneAngleRadians;
    27  import org.apache.commons.numbers.arrays.LinearCombination;
    28
    29  /** This class represents vectors and points in one-dimensional Euclidean space.
    30   * Instances of this class are guaranteed to be immutable.
    31   */
    32  public class Vector1D extends EuclideanVector<Vector1D> {
    33
    34      /** Zero vector (coordinates: 0). */
    35      public static final Vector1D ZERO = new Vector1D(0.0);
    36
    37      // CHECKSTYLE: stop ConstantName
    38      /** A vector with all coordinates set to NaN. */
    39      public static final Vector1D NaN = new Vector1D(Double.NaN);
    40      // CHECKSTYLE: resume ConstantName
    41
    42      /** A vector with all coordinates set to positive infinity. */
    43      public static final Vector1D POSITIVE_INFINITY =
    44          new Vector1D(Double.POSITIVE_INFINITY);
    45
    46      /** A vector with all coordinates set to negative infinity. */
    47      public static final Vector1D NEGATIVE_INFINITY =
    48          new Vector1D(Double.NEGATIVE_INFINITY);
    49
    50      /** Comparator that sorts vectors in component-wise ascending order.
    51       * Vectors are only considered equal if their coordinates match exactly.
    52       * Null arguments are evaluated as being greater than non-null arguments.
    53       */
    54      public static final Comparator<Vector1D> COORDINATE_ASCENDING_ORDER = (a, b) -> {
    55          int cmp = 0;
    56
    57          if (a != null && b != null) {
    58              cmp = Double.compare(a.getX(), b.getX());
    59          } else if (a != null) {
    60              cmp = -1;
    61          } else if (b != null) {
    62              cmp = 1;
    63          }
    64
    65          return cmp;
    66      };
    67
    68      /** Abscissa (coordinate value). */
    69      private final double x;
    70
    71      /** Simple constructor.
    72       * @param x abscissa (coordinate value)
    73       */
    74      private Vector1D(final double x) {
    75          this.x = x;
    76      }
    77
    78      /**
    79       * Returns the abscissa (coordinate value) of the instance.
    80       * @return the abscissa value
    81       */
    82      public double getX() {
    83          return x;
    84      }
    85
    86      /** {@inheritDoc} */
    87      @Override
    88      public int getDimension() {
    89          return 1;
    90      }
    91
    92      /** {@inheritDoc} */
    93      @Override
    94      public boolean isNaN() {
    95          return Double.isNaN(x);
    96      }
    97
    98      /** {@inheritDoc} */
    99      @Override
    100     public boolean isInfinite() {
    101         return !isNaN() && Double.isInfinite(x);
    102     }
    103
    104     /** {@inheritDoc} */
    105     @Override
    106     public boolean isFinite() {
    107         return Double.isFinite(x);
    108     }
    109
    110     /** {@inheritDoc} */
    111     @Override
    112     public Vector1D vectorTo(final Vector1D v) {
    113         return v.subtract(this);
    114     }
    115
    116     /** {@inheritDoc} */
    117     @Override
    118     public Unit directionTo(final Vector1D v) {
    119         return vectorTo(v).normalize();
    120     }
    121
    122     /** {@inheritDoc} */
    123     @Override
    124     public Vector1D lerp(final Vector1D p, final double t) {
    125         return linearCombination(1.0 - t, this, t, p);
    126     }
    127
    128     /** {@inheritDoc} */
    129     @Override
    130     public Vector1D getZero() {
    131         return ZERO;
    132     }
    133
    134     /** {@inheritDoc} */
    135     @Override
    136     public double norm() {
    137         return Vectors.norm(x);
    138     }
    139
    140     /** {@inheritDoc} */
    141     @Override
    142     public double normSq() {
    143         return Vectors.normSq(x);
    144     }
    145
    146     /** {@inheritDoc} */
    147     @Override
    148     public Vector1D withNorm(final double magnitude) {
    149         getCheckedNorm(); // validate our norm value
    150         return (x > 0.0) ? new Vector1D(magnitude) : new Vector1D(-magnitude);
    151     }
    152
    153     /** {@inheritDoc} */
    154     @Override
    155     public Vector1D add(final Vector1D v) {
    156         return new Vector1D(x + v.x);
    157     }
    158
    159     /** {@inheritDoc} */
    160     @Override
    161     public Vector1D add(final double factor, final Vector1D v) {
    162         return new Vector1D(x + (factor * v.x));
    163     }
    164
    165     /** {@inheritDoc} */
    166     @Override
    167     public Vector1D subtract(final Vector1D v) {
    168         return new Vector1D(x - v.x);
    169     }
    170
    171     /** {@inheritDoc} */
    172     @Override
    173     public Vector1D subtract(final double factor, final Vector1D v) {
    174         return new Vector1D(x - (factor * v.x));
    175     }
    176
    177     /** {@inheritDoc} */
    178     @Override
    179     public Vector1D negate() {
    180         return new Vector1D(-x);
    181     }
    182
    183     /** {@inheritDoc} */
    184     @Override
    185     public Unit normalize() {
    186         return Unit.from(x);
    187     }
    188
    189     /** {@inheritDoc} */
    190     @Override
    191     public Vector1D multiply(final double a) {
    192         return new Vector1D(a * x);
    193     }
    194
    195     /** {@inheritDoc} */
    196     @Override
    197     public double distance(final Vector1D v) {
    198         return Vectors.norm(x - v.x);
    199     }
    200
    201     /** {@inheritDoc} */
    202     @Override
    203     public double distanceSq(final Vector1D v) {
    204         return Vectors.normSq(x - v.x);
    205     }
    206
    207     /** {@inheritDoc} */
    208     @Override
    209     public double dot(final Vector1D v) {
    210         return x * v.x;
    211     }
    212
    213     /** {@inheritDoc}
    214      * <p>For the one-dimensional case, this method returns 0 if the vector x values have
    215      * the same sign and {@code pi} if they are opposite.</p>
    216      */
    217     @Override
    218     public double angle(final Vector1D v) {
    219         // validate the norm values
    220         getCheckedNorm();
    221         v.getCheckedNorm();
    222
    223         final double sig1 = Math.signum(x);
    224         final double sig2 = Math.signum(v.x);
    225
    226         // the angle is 0 if the x value signs are the same and pi if not
    227         return (sig1 == sig2) ? 0.0 : PlaneAngleRadians.PI;
    228     }
    229
    230     /** Convenience method to apply a function to this vector. This
    231      * can be used to transform the vector inline with other methods.
    232      * @param fn the function to apply
    233      * @return the transformed vector
    234      */
    235     public Vector1D transform(final UnaryOperator<Vector1D> fn) {
    236         return fn.apply(this);
    237     }
    238
    239     /** {@inheritDoc} */
    240     @Override
    241     public boolean eq(final Vector1D vec, final DoublePrecisionContext precision) {
    242         return precision.eq(x, vec.x);
    243     }
    244
    245     /**
    246      * Get a hashCode for the vector.
    247      * <p>All NaN values have the same hash code.</p>
    248      *
    249      * @return a hash code value for this object
    250      */
    251     @Override
    252     public int hashCode() {
    253         if (isNaN()) {
    254             return 857;
    255         }
    256         return 403 * Double.hashCode(x);
    257     }
    258
    259     /**
    260      * Test for the equality of two vectors.
    261      * <p>
    262      * If all coordinates of two vectors are exactly the same, and none are
    263      * <code>Double.NaN</code>, the two vectors are considered to be equal.
    264      * </p>
    265      * <p>
    266      * <code>NaN</code> coordinates are considered to globally affect the vector
    267      * and be equal to each other - i.e, if either (or all) coordinates of the
    268      * vector are equal to <code>Double.NaN</code>, the vector is equal to
    269      * {@link #NaN}.
    270      * </p>
    271      *
    272      * @param other Object to test for equality to this
    273      * @return true if two vector objects are equal, false if
    274      *         object is null, not an instance of Vector1D, or
    275      *         not equal to this Vector1D instance
    276      *
    277      */
    278     @Override
    279     public boolean equals(final Object other) {
    280         if (this == other) {
    281             return true;
    282         }
    283         if (other instanceof Vector1D) {
    284             final Vector1D rhs = (Vector1D) other;
    285             if (rhs.isNaN()) {
    286                 return this.isNaN();
    287             }
    288
    289             return Double.compare(x, rhs.x) == 0;
    290         }
    291         return false;
    292     }
    293
    294     /** {@inheritDoc} */
    295     @Override
    296     public String toString() {
    297         return SimpleTupleFormat.getDefault().format(x);
    298     }
    299
    300     /** Returns a vector with the given coordinate value.
    301      * @param x vector coordinate
    302      * @return vector instance
    303      */
    304     public static Vector1D of(final double x) {
    305         return new Vector1D(x);
    306     }
    307
    308     /** Parses the given string and returns a new vector instance. The expected string
    309      * format is the same as that returned by {@link #toString()}.
    310      * @param str the string to parse
    311      * @return vector instance represented by the string
    312      * @throws IllegalArgumentException if the given string has an invalid format
    313      */
    314     public static Vector1D parse(final String str) {
    315         return SimpleTupleFormat.getDefault().parse(str, Vector1D::new);
    316     }
    317
    318     /** Returns a vector consisting of the linear combination of the inputs.
    319      * <p>
    320      * A linear combination is the sum of all of the inputs multiplied by their
    321      * corresponding scale factors.
    322      * </p>
    323      *
    324      * @param a scale factor for first vector
    325      * @param c first vector
    326      * @return vector calculated by {@code a * c}
    327      */
    328     public static Vector1D linearCombination(final double a, final Vector1D c) {
    329         return new Vector1D(a * c.x);
    330     }
    331
    332     /** Returns a vector consisting of the linear combination of the inputs.
    333      * <p>
    334      * A linear combination is the sum of all of the inputs multiplied by their
    335      * corresponding scale factors.
    336      * </p>
    337      *
    338      * @param a1 scale factor for first vector
    339      * @param v1 first vector
    340      * @param a2 scale factor for second vector
    341      * @param v2 second vector
    342      * @return vector calculated by {@code (a1 * v1) + (a2 * v2)}
    343      */
    344     public static Vector1D linearCombination(final double a1, final Vector1D v1,
    345             final double a2, final Vector1D v2) {
    346
    347         return new Vector1D(
    348                 LinearCombination.value(a1, v1.x, a2, v2.x));
    349     }
    350
    351     /** Returns a vector consisting of the linear combination of the inputs.
    352      * <p>
    353      * A linear combination is the sum of all of the inputs multiplied by their
    354      * corresponding scale factors.
    355      * </p>
    356      *
    357      * @param a1 scale factor for first vector
    358      * @param v1 first vector
    359      * @param a2 scale factor for second vector
    360      * @param v2 second vector
    361      * @param a3 scale factor for third vector
    362      * @param v3 third vector
    363      * @return vector calculated by {@code (a1 * v1) + (a2 * v2) + (a3 * v3)}
    364      */
    365     public static Vector1D linearCombination(final double a1, final Vector1D v1,
    366             final double a2, final Vector1D v2,
    367             final double a3, final Vector1D v3) {
    368
    369         return new Vector1D(
    370                 LinearCombination.value(a1, v1.x, a2, v2.x, a3, v3.x));
    371     }
    372
    373     /** Returns a vector consisting of the linear combination of the inputs.
    374      * <p>
    375      * A linear combination is the sum of all of the inputs multiplied by their
    376      * corresponding scale factors.
    377      * </p>
    378      *
    379      * @param a1 scale factor for first vector
    380      * @param v1 first vector
    381      * @param a2 scale factor for second vector
    382      * @param v2 second vector
    383      * @param a3 scale factor for third vector
    384      * @param v3 third vector
    385      * @param a4 scale factor for fourth vector
    386      * @param v4 fourth vector
    387      * @return vector calculated by {@code (a1 * v1) + (a2 * v2) + (a3 * v3) + (a4 * v4)}
    388      */
    389     public static Vector1D linearCombination(final double a1, final Vector1D v1,
    390             final double a2, final Vector1D v2,
    391             final double a3, final Vector1D v3,
    392             final double a4, final Vector1D v4) {
    393
    394         return new Vector1D(
    395                 LinearCombination.value(a1, v1.x, a2, v2.x, a3, v3.x, a4, v4.x));
    396     }
    397
    398     /**
    399      * Represent unit vectors.
    400      * This allows optimizations to be performed for certain operations.
    401      */
    402     public static final class Unit extends Vector1D {
    403         /** Unit vector (coordinates: 1). */
    404         public static final Unit PLUS  = new Unit(1d);
    405         /** Negation of unit vector (coordinates: -1). */
    406         public static final Unit MINUS = new Unit(-1d);
    407
    408         /** Simple constructor. Callers are responsible for ensuring that the given
    409          * values represent a normalized vector.
    410          * @param x abscissa (first coordinate value)
    411          */
    412         private Unit(final double x) {
    413             super(x);
    414         }
    415
    416         /**
    417          * Creates a normalized vector.
    418          *
    419          * @param x Vector coordinate.
    420          * @return a vector whose norm is 1.
    421          * @throws IllegalArgumentException if the norm of the given value is zero, NaN, or infinite
    422          */
    423         public static Unit from(final double x) {
    424             Vectors.checkedNorm(Vectors.norm(x));
    425             return x > 0 ? PLUS : MINUS;
    426         }
    427
    428         /**
    429          * Creates a normalized vector.
    430          *
    431          * @param v Vector.
    432          * @return a vector whose norm is 1.
    433          * @throws IllegalArgumentException if the norm of the given value is zero, NaN, or infinite
    434          */
    435         public static Unit from(final Vector1D v) {
    436             return v instanceof Unit ?
    437                 (Unit) v :
    438                 from(v.getX());
    439         }
    440
    441         /** {@inheritDoc} */
    442         @Override
    443         public double norm() {
    444             return 1;
    445         }
    446
    447         /** {@inheritDoc} */
    448         @Override
    449         public double normSq() {
    450             return 1;
    451         }
    452
    453         /** {@inheritDoc} */
    454         @Override
    455         public Unit normalize() {
    456             return this;
    457         }
    458
    459         /** {@inheritDoc} */
    460         @Override
    461         public Vector1D withNorm(final double mag) {
    462             return multiply(mag);
    463         }
    464
    465         /** {@inheritDoc} */
    466         @Override
    467         public Vector1D negate() {
    468             return this == PLUS ? MINUS : PLUS;
    469         }
    470     }
    471 }