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    *      https://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  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.geometry.euclidean.twod;
23  
24  import java.text.NumberFormat;
25  
26  import org.hipparchus.exception.LocalizedCoreFormats;
27  import org.hipparchus.exception.MathIllegalArgumentException;
28  import org.hipparchus.exception.MathRuntimeException;
29  import org.hipparchus.geometry.Point;
30  import org.hipparchus.geometry.Space;
31  import org.hipparchus.geometry.Vector;
32  import org.hipparchus.util.FastMath;
33  import org.hipparchus.util.MathArrays;
34  import org.hipparchus.util.MathUtils;
35  
36  /** This class represents a 2D vector.
37   * <p>Instances of this class are guaranteed to be immutable.</p>
38   */
39  public class Vector2D implements Vector<Euclidean2D, Vector2D> {
40  
41      /** Origin (coordinates: 0, 0). */
42      public static final Vector2D ZERO   = new Vector2D(0, 0);
43  
44      /** First canonical vector (coordinates: 1, 0).
45       * @since 1.6
46       */
47      public static final Vector2D PLUS_I = new Vector2D(1, 0);
48  
49      /** Opposite of the first canonical vector (coordinates: -1, 0).
50       * @since 1.6
51       */
52      public static final Vector2D MINUS_I = new Vector2D(-1, 0);
53  
54      /** Second canonical vector (coordinates: 0, 1).
55       * @since 1.6
56       */
57      public static final Vector2D PLUS_J = new Vector2D(0, 1);
58  
59      /** Opposite of the second canonical vector (coordinates: 0, -1).
60       * @since 1.6
61       */
62      public static final Vector2D MINUS_J = new Vector2D(0, -1);
63  
64      // CHECKSTYLE: stop ConstantName
65      /** A vector with all coordinates set to NaN. */
66      public static final Vector2D NaN = new Vector2D(Double.NaN, Double.NaN);
67      // CHECKSTYLE: resume ConstantName
68  
69      /** A vector with all coordinates set to positive infinity. */
70      public static final Vector2D POSITIVE_INFINITY =
71          new Vector2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
72  
73      /** A vector with all coordinates set to negative infinity. */
74      public static final Vector2D NEGATIVE_INFINITY =
75          new Vector2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
76  
77      /** Serializable UID. */
78      private static final long serialVersionUID = 266938651998679754L;
79  
80      /** Abscissa. */
81      private final double x;
82  
83      /** Ordinate. */
84      private final double y;
85  
86      /** Simple constructor.
87       * Build a vector from its coordinates
88       * @param x abscissa
89       * @param y ordinate
90       * @see #getX()
91       * @see #getY()
92       */
93      public Vector2D(double x, double y) {
94          this.x = x;
95          this.y = y;
96      }
97  
98      /** Simple constructor.
99       * Build a vector from its coordinates
100      * @param v coordinates array
101      * @exception MathIllegalArgumentException if array does not have 2 elements
102      * @see #toArray()
103      */
104     public Vector2D(double[] v) throws MathIllegalArgumentException {
105         if (v.length != 2) {
106             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
107                                                    v.length, 2);
108         }
109         this.x = v[0];
110         this.y = v[1];
111     }
112 
113     /** Multiplicative constructor
114      * Build a vector from another one and a scale factor.
115      * The vector built will be a * u
116      * @param a scale factor
117      * @param u base (unscaled) vector
118      */
119     public Vector2D(double a, Vector2D u) {
120         this.x = a * u.x;
121         this.y = a * u.y;
122     }
123 
124     /** Linear constructor
125      * Build a vector from two other ones and corresponding scale factors.
126      * The vector built will be a1 * u1 + a2 * u2
127      * @param a1 first scale factor
128      * @param u1 first base (unscaled) vector
129      * @param a2 second scale factor
130      * @param u2 second base (unscaled) vector
131      */
132     public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2) {
133         this.x = a1 * u1.x + a2 * u2.x;
134         this.y = a1 * u1.y + a2 * u2.y;
135     }
136 
137     /** Linear constructor
138      * Build a vector from three other ones and corresponding scale factors.
139      * The vector built will be a1 * u1 + a2 * u2 + a3 * u3
140      * @param a1 first scale factor
141      * @param u1 first base (unscaled) vector
142      * @param a2 second scale factor
143      * @param u2 second base (unscaled) vector
144      * @param a3 third scale factor
145      * @param u3 third base (unscaled) vector
146      */
147     public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2,
148                    double a3, Vector2D u3) {
149         this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x;
150         this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y;
151     }
152 
153     /** Linear constructor
154      * Build a vector from four other ones and corresponding scale factors.
155      * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4
156      * @param a1 first scale factor
157      * @param u1 first base (unscaled) vector
158      * @param a2 second scale factor
159      * @param u2 second base (unscaled) vector
160      * @param a3 third scale factor
161      * @param u3 third base (unscaled) vector
162      * @param a4 fourth scale factor
163      * @param u4 fourth base (unscaled) vector
164      */
165     public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2,
166                    double a3, Vector2D u3, double a4, Vector2D u4) {
167         this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x + a4 * u4.x;
168         this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y + a4 * u4.y;
169     }
170 
171     /** Get the abscissa of the vector.
172      * @return abscissa of the vector
173      * @see #Vector2D(double, double)
174      */
175     public double getX() {
176         return x;
177     }
178 
179     /** Get the ordinate of the vector.
180      * @return ordinate of the vector
181      * @see #Vector2D(double, double)
182      */
183     public double getY() {
184         return y;
185     }
186 
187     /** Get the vector coordinates as a dimension 2 array.
188      * @return vector coordinates
189      * @see #Vector2D(double[])
190      */
191     public double[] toArray() {
192         return new double[] { x, y };
193     }
194 
195     /** {@inheritDoc} */
196     @Override
197     public Space getSpace() {
198         return Euclidean2D.getInstance();
199     }
200 
201     /** {@inheritDoc} */
202     @Override
203     public Vector2D getZero() {
204         return ZERO;
205     }
206 
207     /** {@inheritDoc} */
208     @Override
209     public double getNorm1() {
210         return FastMath.abs(x) + FastMath.abs(y);
211     }
212 
213     /** {@inheritDoc} */
214     @Override
215     public double getNorm() {
216         return FastMath.sqrt (x * x + y * y);
217     }
218 
219     /** {@inheritDoc} */
220     @Override
221     public double getNormSq() {
222         return x * x + y * y;
223     }
224 
225     /** {@inheritDoc} */
226     @Override
227     public double getNormInf() {
228         return FastMath.max(FastMath.abs(x), FastMath.abs(y));
229     }
230 
231     /** {@inheritDoc} */
232     @Override
233     public Vector2D add(Vector<Euclidean2D, Vector2D> v) {
234         Vector2D v2 = (Vector2D) v;
235         return new Vector2D(x + v2.getX(), y + v2.getY());
236     }
237 
238     /** {@inheritDoc} */
239     @Override
240     public Vector2D add(double factor, Vector<Euclidean2D, Vector2D> v) {
241         Vector2D v2 = (Vector2D) v;
242         return new Vector2D(x + factor * v2.getX(), y + factor * v2.getY());
243     }
244 
245     /** {@inheritDoc} */
246     @Override
247     public Vector2D subtract(Vector<Euclidean2D, Vector2D> p) {
248         Vector2D p3 = (Vector2D) p;
249         return new Vector2D(x - p3.x, y - p3.y);
250     }
251 
252     /** {@inheritDoc} */
253     @Override
254     public Vector2D subtract(double factor, Vector<Euclidean2D, Vector2D> v) {
255         Vector2D v2 = (Vector2D) v;
256         return new Vector2D(x - factor * v2.getX(), y - factor * v2.getY());
257     }
258 
259     /** Compute the angular separation between two vectors.
260      * <p>This method computes the angular separation between two
261      * vectors using the dot product for well separated vectors and the
262      * cross product for almost aligned vectors. This allows to have a
263      * good accuracy in all cases, even for vectors very close to each
264      * other.</p>
265      * @param v1 first vector
266      * @param v2 second vector
267      * @return angular separation between v1 and v2
268      * @exception MathRuntimeException if either vector has a null norm
269      */
270     public static double angle(Vector2D v1, Vector2D v2) throws MathRuntimeException {
271 
272         double normProduct = v1.getNorm() * v2.getNorm();
273         if (normProduct == 0) {
274             throw new MathRuntimeException(LocalizedCoreFormats.ZERO_NORM);
275         }
276 
277         double dot = v1.dotProduct(v2);
278         double threshold = normProduct * 0.9999;
279         if (FastMath.abs(dot) > threshold) {
280             // the vectors are almost aligned, compute using the sine
281             final double n = FastMath.abs(MathArrays.linearCombination(v1.x, v2.y, -v1.y, v2.x));
282             if (dot >= 0) {
283                 return FastMath.asin(n / normProduct);
284             }
285             return FastMath.PI - FastMath.asin(n / normProduct);
286         }
287 
288         // the vectors are sufficiently separated to use the cosine
289         return FastMath.acos(dot / normProduct);
290 
291     }
292 
293     /** {@inheritDoc} */
294     @Override
295     public Vector2D negate() {
296         return new Vector2D(-x, -y);
297     }
298 
299     /** {@inheritDoc} */
300     @Override
301     public Vector2D scalarMultiply(double a) {
302         return new Vector2D(a * x, a * y);
303     }
304 
305     /** {@inheritDoc} */
306     @Override
307     public boolean isNaN() {
308         return Double.isNaN(x) || Double.isNaN(y);
309     }
310 
311     /** {@inheritDoc} */
312     @Override
313     public boolean isInfinite() {
314         return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y));
315     }
316 
317     /** {@inheritDoc} */
318     @Override
319     public double distance1(Vector<Euclidean2D, Vector2D> p) {
320         Vector2D p3 = (Vector2D) p;
321         final double dx = FastMath.abs(p3.x - x);
322         final double dy = FastMath.abs(p3.y - y);
323         return dx + dy;
324     }
325 
326     /** {@inheritDoc} */
327     @Override
328     public double distance(Point<Euclidean2D> p) {
329         Vector2D p3 = (Vector2D) p;
330         final double dx = p3.x - x;
331         final double dy = p3.y - y;
332         return FastMath.sqrt(dx * dx + dy * dy);
333     }
334 
335     /** {@inheritDoc} */
336     @Override
337     public double distanceInf(Vector<Euclidean2D, Vector2D> p) {
338         Vector2D p3 = (Vector2D) p;
339         final double dx = FastMath.abs(p3.x - x);
340         final double dy = FastMath.abs(p3.y - y);
341         return FastMath.max(dx, dy);
342     }
343 
344     /** {@inheritDoc} */
345     @Override
346     public double distanceSq(Vector<Euclidean2D, Vector2D> p) {
347         Vector2D p3 = (Vector2D) p;
348         final double dx = p3.x - x;
349         final double dy = p3.y - y;
350         return dx * dx + dy * dy;
351     }
352 
353     /** {@inheritDoc} */
354     @Override
355     public double dotProduct(final Vector<Euclidean2D, Vector2D> v) {
356         final Vector2D v2 = (Vector2D) v;
357         return MathArrays.linearCombination(x, v2.x, y, v2.y);
358     }
359 
360     /**
361      * Compute the cross-product of the instance and the given points.
362      * <p>
363      * The cross product can be used to determine the location of a point
364      * with regard to the line formed by (p1, p2) and is calculated as:
365      * \[
366      *    P = (x_2 - x_1)(y_3 - y_1) - (y_2 - y_1)(x_3 - x_1)
367      * \]
368      * with \(p3 = (x_3, y_3)\) being this instance.
369      * <p>
370      * If the result is 0, the points are collinear, i.e. lie on a single straight line L;
371      * if it is positive, this point lies to the left, otherwise to the right of the line
372      * formed by (p1, p2).
373      *
374      * @param p1 first point of the line
375      * @param p2 second point of the line
376      * @return the cross-product
377      *
378      * @see <a href="http://en.wikipedia.org/wiki/Cross_product">Cross product (Wikipedia)</a>
379      */
380     public double crossProduct(final Vector2D p1, final Vector2D p2) {
381         final double x1 = p2.getX() - p1.getX();
382         final double y1 = getY() - p1.getY();
383         final double x2 = getX() - p1.getX();
384         final double y2 = p2.getY() - p1.getY();
385         return MathArrays.linearCombination(x1, y1, -x2, y2);
386     }
387 
388     /** Compute the distance between two vectors according to the L<sub>1</sub> norm.
389      * <p>Calling this method is equivalent to calling:
390      * <code>p1.subtract(p2).getNorm1()</code> except that no intermediate
391      * vector is built</p>
392      * @param p1 first vector
393      * @param p2 second vector
394      * @return the distance between p1 and p2 according to the L<sub>1</sub> norm
395      * @since 1.6
396      */
397     public static double distance1(Vector2D p1, Vector2D p2) {
398         return p1.distance1(p2);
399     }
400 
401     /** Compute the distance between two vectors according to the L<sub>2</sub> norm.
402      * <p>Calling this method is equivalent to calling:
403      * <code>p1.subtract(p2).getNorm()</code> except that no intermediate
404      * vector is built</p>
405      * @param p1 first vector
406      * @param p2 second vector
407      * @return the distance between p1 and p2 according to the L<sub>2</sub> norm
408      */
409     public static double distance(Vector2D p1, Vector2D p2) {
410         return p1.distance(p2);
411     }
412 
413     /** Compute the distance between two vectors according to the L<sub>&infin;</sub> norm.
414      * <p>Calling this method is equivalent to calling:
415      * <code>p1.subtract(p2).getNormInf()</code> except that no intermediate
416      * vector is built</p>
417      * @param p1 first vector
418      * @param p2 second vector
419      * @return the distance between p1 and p2 according to the L<sub>&infin;</sub> norm
420      */
421     public static double distanceInf(Vector2D p1, Vector2D p2) {
422         return p1.distanceInf(p2);
423     }
424 
425     /** Compute the square of the distance between two vectors.
426      * <p>Calling this method is equivalent to calling:
427      * <code>p1.subtract(p2).getNormSq()</code> except that no intermediate
428      * vector is built</p>
429      * @param p1 first vector
430      * @param p2 second vector
431      * @return the square of the distance between p1 and p2
432      */
433     public static double distanceSq(Vector2D p1, Vector2D p2) {
434         return p1.distanceSq(p2);
435     }
436 
437     /** Compute the orientation of a triplet of points.
438      * @param p first vector of the triplet
439      * @param q second vector of the triplet
440      * @param r third vector of the triplet
441      * @return a positive value if (p, q, r) defines a counterclockwise oriented
442      * triangle, a negative value if (p, q, r) defines a clockwise oriented
443      * triangle, and 0 if (p, q, r) are collinear or some points are equal
444      * @since 1.2
445      */
446     public static double orientation(final Vector2D p, final Vector2D q, final Vector2D r) {
447         return MathArrays.linearCombination(new double[] {
448             p.getX(), -p.getX(), q.getX(), -q.getX(), r.getX(), -r.getX()
449         }, new double[] {
450             q.getY(),  r.getY(), r.getY(),  p.getY(), p.getY(),  q.getY()
451         });
452     }
453 
454     /**
455      * Test for the equality of two 2D vectors.
456      * <p>
457      * If all coordinates of two 2D vectors are exactly the same, and none are
458      * {@code Double.NaN}, the two 2D vectors are considered to be equal.
459      * </p>
460      * <p>
461      * {@code NaN} coordinates are considered to affect globally the vector
462      * and be equals to each other - i.e, if either (or all) coordinates of the
463      * 2D vector are equal to {@code Double.NaN}, the 2D vector is equal to
464      * {@link #NaN}.
465      * </p>
466      *
467      * @param other Object to test for equality to this
468      * @return true if two 2D vector objects are equal, false if
469      *         object is null, not an instance of Vector2D, or
470      *         not equal to this Vector2D instance
471      */
472     @Override
473     public boolean equals(Object other) {
474 
475         if (this == other) {
476             return true;
477         }
478 
479         if (other instanceof Vector2D) {
480             final Vector2D rhs = (Vector2D)other;
481             return x == rhs.x && y == rhs.y || isNaN() && rhs.isNaN();
482         }
483 
484         return false;
485 
486     }
487 
488     /**
489      * Test for the equality of two 2D vectors.
490      * <p>
491      * If all coordinates of two 2D vectors are exactly the same, and none are
492      * {@code NaN}, the two 2D vectors are considered to be equal.
493      * </p>
494      * <p>
495      * In compliance with IEEE754 handling, if any coordinates of any of the
496      * two vectors are {@code NaN}, then the vectors are considered different.
497      * This implies that {@link #NaN Vector2D.NaN}.equals({@link #NaN Vector2D.NaN})
498      * returns {@code false} despite the instance is checked against itself.
499      * </p>
500      *
501      * @param other Object to test for equality to this
502      * @return true if two 2D vector objects are equal, false if
503      *         object is null, not an instance of Vector2D, or
504      *         not equal to this Vector2D instance
505      * @since 2.1
506      */
507     public boolean equalsIeee754(Object other) {
508 
509         if (this == other && !isNaN()) {
510             return true;
511         }
512 
513         if (other instanceof Vector2D) {
514             final Vector2D rhs = (Vector2D) other;
515             return x == rhs.x && y == rhs.y;
516         }
517         return false;
518     }
519 
520     /**
521      * Get a hashCode for the 2D vector.
522      * <p>
523      * All NaN values have the same hash code.</p>
524      *
525      * @return a hash code value for this object
526      */
527     @Override
528     public int hashCode() {
529         if (isNaN()) {
530             return 542;
531         }
532         return 122 * (76 * MathUtils.hash(x) +  MathUtils.hash(y));
533     }
534 
535     /** Get a string representation of this vector.
536      * @return a string representation of this vector
537      */
538     @Override
539     public String toString() {
540         return Vector2DFormat.getVector2DFormat().format(this);
541     }
542 
543     /** {@inheritDoc} */
544     @Override
545     public String toString(final NumberFormat format) {
546         return new Vector2DFormat(format).format(this);
547     }
548 
549 }