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