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 }