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