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 }