001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.math3.geometry.euclidean.twod; 018 019import java.text.NumberFormat; 020 021import org.apache.commons.math3.exception.DimensionMismatchException; 022import org.apache.commons.math3.exception.MathArithmeticException; 023import org.apache.commons.math3.exception.util.LocalizedFormats; 024import org.apache.commons.math3.geometry.Point; 025import org.apache.commons.math3.geometry.Space; 026import org.apache.commons.math3.geometry.Vector; 027import org.apache.commons.math3.util.FastMath; 028import org.apache.commons.math3.util.MathArrays; 029import org.apache.commons.math3.util.MathUtils; 030 031/** This class represents a 2D vector. 032 * <p>Instances of this class are guaranteed to be immutable.</p> 033 * @since 3.0 034 */ 035public class Vector2D implements Vector<Euclidean2D> { 036 037 /** Origin (coordinates: 0, 0). */ 038 public static final Vector2D ZERO = new Vector2D(0, 0); 039 040 // CHECKSTYLE: stop ConstantName 041 /** A vector with all coordinates set to NaN. */ 042 public static final Vector2D NaN = new Vector2D(Double.NaN, Double.NaN); 043 // CHECKSTYLE: resume ConstantName 044 045 /** A vector with all coordinates set to positive infinity. */ 046 public static final Vector2D POSITIVE_INFINITY = 047 new Vector2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); 048 049 /** A vector with all coordinates set to negative infinity. */ 050 public static final Vector2D NEGATIVE_INFINITY = 051 new Vector2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); 052 053 /** Serializable UID. */ 054 private static final long serialVersionUID = 266938651998679754L; 055 056 /** Abscissa. */ 057 private final double x; 058 059 /** Ordinate. */ 060 private final double y; 061 062 /** Simple constructor. 063 * Build a vector from its coordinates 064 * @param x abscissa 065 * @param y ordinate 066 * @see #getX() 067 * @see #getY() 068 */ 069 public Vector2D(double x, double y) { 070 this.x = x; 071 this.y = y; 072 } 073 074 /** Simple constructor. 075 * Build a vector from its coordinates 076 * @param v coordinates array 077 * @exception DimensionMismatchException if array does not have 2 elements 078 * @see #toArray() 079 */ 080 public Vector2D(double[] v) throws DimensionMismatchException { 081 if (v.length != 2) { 082 throw new DimensionMismatchException(v.length, 2); 083 } 084 this.x = v[0]; 085 this.y = v[1]; 086 } 087 088 /** Multiplicative constructor 089 * Build a vector from another one and a scale factor. 090 * The vector built will be a * u 091 * @param a scale factor 092 * @param u base (unscaled) vector 093 */ 094 public Vector2D(double a, Vector2D u) { 095 this.x = a * u.x; 096 this.y = a * u.y; 097 } 098 099 /** Linear constructor 100 * Build a vector from two other ones and corresponding scale factors. 101 * The vector built will be a1 * u1 + a2 * u2 102 * @param a1 first scale factor 103 * @param u1 first base (unscaled) vector 104 * @param a2 second scale factor 105 * @param u2 second base (unscaled) vector 106 */ 107 public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2) { 108 this.x = a1 * u1.x + a2 * u2.x; 109 this.y = a1 * u1.y + a2 * u2.y; 110 } 111 112 /** Linear constructor 113 * Build a vector from three other ones and corresponding scale factors. 114 * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 115 * @param a1 first scale factor 116 * @param u1 first base (unscaled) vector 117 * @param a2 second scale factor 118 * @param u2 second base (unscaled) vector 119 * @param a3 third scale factor 120 * @param u3 third base (unscaled) vector 121 */ 122 public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2, 123 double a3, Vector2D u3) { 124 this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x; 125 this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y; 126 } 127 128 /** Linear constructor 129 * Build a vector from four other ones and corresponding scale factors. 130 * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4 131 * @param a1 first scale factor 132 * @param u1 first base (unscaled) vector 133 * @param a2 second scale factor 134 * @param u2 second base (unscaled) vector 135 * @param a3 third scale factor 136 * @param u3 third base (unscaled) vector 137 * @param a4 fourth scale factor 138 * @param u4 fourth base (unscaled) vector 139 */ 140 public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2, 141 double a3, Vector2D u3, double a4, Vector2D u4) { 142 this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x + a4 * u4.x; 143 this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y + a4 * u4.y; 144 } 145 146 /** Get the abscissa of the vector. 147 * @return abscissa of the vector 148 * @see #Vector2D(double, double) 149 */ 150 public double getX() { 151 return x; 152 } 153 154 /** Get the ordinate of the vector. 155 * @return ordinate of the vector 156 * @see #Vector2D(double, double) 157 */ 158 public double getY() { 159 return y; 160 } 161 162 /** Get the vector coordinates as a dimension 2 array. 163 * @return vector coordinates 164 * @see #Vector2D(double[]) 165 */ 166 public double[] toArray() { 167 return new double[] { x, y }; 168 } 169 170 /** {@inheritDoc} */ 171 public Space getSpace() { 172 return Euclidean2D.getInstance(); 173 } 174 175 /** {@inheritDoc} */ 176 public Vector2D getZero() { 177 return ZERO; 178 } 179 180 /** {@inheritDoc} */ 181 public double getNorm1() { 182 return FastMath.abs(x) + FastMath.abs(y); 183 } 184 185 /** {@inheritDoc} */ 186 public double getNorm() { 187 return FastMath.sqrt (x * x + y * y); 188 } 189 190 /** {@inheritDoc} */ 191 public double getNormSq() { 192 return x * x + y * y; 193 } 194 195 /** {@inheritDoc} */ 196 public double getNormInf() { 197 return FastMath.max(FastMath.abs(x), FastMath.abs(y)); 198 } 199 200 /** {@inheritDoc} */ 201 public Vector2D add(Vector<Euclidean2D> v) { 202 Vector2D v2 = (Vector2D) v; 203 return new Vector2D(x + v2.getX(), y + v2.getY()); 204 } 205 206 /** {@inheritDoc} */ 207 public Vector2D add(double factor, Vector<Euclidean2D> v) { 208 Vector2D v2 = (Vector2D) v; 209 return new Vector2D(x + factor * v2.getX(), y + factor * v2.getY()); 210 } 211 212 /** {@inheritDoc} */ 213 public Vector2D subtract(Vector<Euclidean2D> p) { 214 Vector2D p3 = (Vector2D) p; 215 return new Vector2D(x - p3.x, y - p3.y); 216 } 217 218 /** {@inheritDoc} */ 219 public Vector2D subtract(double factor, Vector<Euclidean2D> v) { 220 Vector2D v2 = (Vector2D) v; 221 return new Vector2D(x - factor * v2.getX(), y - factor * v2.getY()); 222 } 223 224 /** {@inheritDoc} */ 225 public Vector2D normalize() throws MathArithmeticException { 226 double s = getNorm(); 227 if (s == 0) { 228 throw new MathArithmeticException(LocalizedFormats.CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR); 229 } 230 return scalarMultiply(1 / s); 231 } 232 233 /** Compute the angular separation between two vectors. 234 * <p>This method computes the angular separation between two 235 * vectors using the dot product for well separated vectors and the 236 * cross product for almost aligned vectors. This allows to have a 237 * good accuracy in all cases, even for vectors very close to each 238 * other.</p> 239 * @param v1 first vector 240 * @param v2 second vector 241 * @return angular separation between v1 and v2 242 * @exception MathArithmeticException if either vector has a null norm 243 */ 244 public static double angle(Vector2D v1, Vector2D v2) throws MathArithmeticException { 245 246 double normProduct = v1.getNorm() * v2.getNorm(); 247 if (normProduct == 0) { 248 throw new MathArithmeticException(LocalizedFormats.ZERO_NORM); 249 } 250 251 double dot = v1.dotProduct(v2); 252 double threshold = normProduct * 0.9999; 253 if ((dot < -threshold) || (dot > threshold)) { 254 // the vectors are almost aligned, compute using the sine 255 final double n = FastMath.abs(MathArrays.linearCombination(v1.x, v2.y, -v1.y, v2.x)); 256 if (dot >= 0) { 257 return FastMath.asin(n / normProduct); 258 } 259 return FastMath.PI - FastMath.asin(n / normProduct); 260 } 261 262 // the vectors are sufficiently separated to use the cosine 263 return FastMath.acos(dot / normProduct); 264 265 } 266 267 /** {@inheritDoc} */ 268 public Vector2D negate() { 269 return new Vector2D(-x, -y); 270 } 271 272 /** {@inheritDoc} */ 273 public Vector2D scalarMultiply(double a) { 274 return new Vector2D(a * x, a * y); 275 } 276 277 /** {@inheritDoc} */ 278 public boolean isNaN() { 279 return Double.isNaN(x) || Double.isNaN(y); 280 } 281 282 /** {@inheritDoc} */ 283 public boolean isInfinite() { 284 return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y)); 285 } 286 287 /** {@inheritDoc} */ 288 public double distance1(Vector<Euclidean2D> p) { 289 Vector2D p3 = (Vector2D) p; 290 final double dx = FastMath.abs(p3.x - x); 291 final double dy = FastMath.abs(p3.y - y); 292 return dx + dy; 293 } 294 295 /** {@inheritDoc} 296 */ 297 public double distance(Vector<Euclidean2D> p) { 298 return distance((Point<Euclidean2D>) p); 299 } 300 301 /** {@inheritDoc} */ 302 public double distance(Point<Euclidean2D> p) { 303 Vector2D p3 = (Vector2D) p; 304 final double dx = p3.x - x; 305 final double dy = p3.y - y; 306 return FastMath.sqrt(dx * dx + dy * dy); 307 } 308 309 /** {@inheritDoc} */ 310 public double distanceInf(Vector<Euclidean2D> p) { 311 Vector2D p3 = (Vector2D) p; 312 final double dx = FastMath.abs(p3.x - x); 313 final double dy = FastMath.abs(p3.y - y); 314 return FastMath.max(dx, dy); 315 } 316 317 /** {@inheritDoc} */ 318 public double distanceSq(Vector<Euclidean2D> p) { 319 Vector2D p3 = (Vector2D) p; 320 final double dx = p3.x - x; 321 final double dy = p3.y - y; 322 return dx * dx + dy * dy; 323 } 324 325 /** {@inheritDoc} */ 326 public double dotProduct(final Vector<Euclidean2D> v) { 327 final Vector2D v2 = (Vector2D) v; 328 return MathArrays.linearCombination(x, v2.x, y, v2.y); 329 } 330 331 /** 332 * Compute the cross-product of the instance and the given points. 333 * <p> 334 * The cross product can be used to determine the location of a point 335 * with regard to the line formed by (p1, p2) and is calculated as: 336 * \[ 337 * P = (x_2 - x_1)(y_3 - y_1) - (y_2 - y_1)(x_3 - x_1) 338 * \] 339 * with \(p3 = (x_3, y_3)\) being this instance. 340 * <p> 341 * If the result is 0, the points are collinear, i.e. lie on a single straight line L; 342 * if it is positive, this point lies to the left, otherwise to the right of the line 343 * formed by (p1, p2). 344 * 345 * @param p1 first point of the line 346 * @param p2 second point of the line 347 * @return the cross-product 348 * 349 * @see <a href="http://en.wikipedia.org/wiki/Cross_product">Cross product (Wikipedia)</a> 350 */ 351 public double crossProduct(final Vector2D p1, final Vector2D p2) { 352 final double x1 = p2.getX() - p1.getX(); 353 final double y1 = getY() - p1.getY(); 354 final double x2 = getX() - p1.getX(); 355 final double y2 = p2.getY() - p1.getY(); 356 return MathArrays.linearCombination(x1, y1, -x2, y2); 357 } 358 359 /** Compute the distance between two vectors according to the L<sub>2</sub> norm. 360 * <p>Calling this method is equivalent to calling: 361 * <code>p1.subtract(p2).getNorm()</code> except that no intermediate 362 * vector is built</p> 363 * @param p1 first vector 364 * @param p2 second vector 365 * @return the distance between p1 and p2 according to the L<sub>2</sub> norm 366 */ 367 public static double distance(Vector2D p1, Vector2D p2) { 368 return p1.distance(p2); 369 } 370 371 /** Compute the distance between two vectors according to the L<sub>∞</sub> norm. 372 * <p>Calling this method is equivalent to calling: 373 * <code>p1.subtract(p2).getNormInf()</code> except that no intermediate 374 * vector is built</p> 375 * @param p1 first vector 376 * @param p2 second vector 377 * @return the distance between p1 and p2 according to the L<sub>∞</sub> norm 378 */ 379 public static double distanceInf(Vector2D p1, Vector2D p2) { 380 return p1.distanceInf(p2); 381 } 382 383 /** Compute the square of the distance between two vectors. 384 * <p>Calling this method is equivalent to calling: 385 * <code>p1.subtract(p2).getNormSq()</code> except that no intermediate 386 * vector is built</p> 387 * @param p1 first vector 388 * @param p2 second vector 389 * @return the square of the distance between p1 and p2 390 */ 391 public static double distanceSq(Vector2D p1, Vector2D p2) { 392 return p1.distanceSq(p2); 393 } 394 395 /** 396 * Test for the equality of two 2D vectors. 397 * <p> 398 * If all coordinates of two 2D vectors are exactly the same, and none are 399 * <code>Double.NaN</code>, the two 2D vectors are considered to be equal. 400 * </p> 401 * <p> 402 * <code>NaN</code> coordinates are considered to affect globally the vector 403 * and be equals to each other - i.e, if either (or all) coordinates of the 404 * 2D vector are equal to <code>Double.NaN</code>, the 2D vector is equal to 405 * {@link #NaN}. 406 * </p> 407 * 408 * @param other Object to test for equality to this 409 * @return true if two 2D vector objects are equal, false if 410 * object is null, not an instance of Vector2D, or 411 * not equal to this Vector2D instance 412 * 413 */ 414 @Override 415 public boolean equals(Object other) { 416 417 if (this == other) { 418 return true; 419 } 420 421 if (other instanceof Vector2D) { 422 final Vector2D rhs = (Vector2D)other; 423 if (rhs.isNaN()) { 424 return this.isNaN(); 425 } 426 427 return (x == rhs.x) && (y == rhs.y); 428 } 429 return false; 430 } 431 432 /** 433 * Get a hashCode for the 2D vector. 434 * <p> 435 * All NaN values have the same hash code.</p> 436 * 437 * @return a hash code value for this object 438 */ 439 @Override 440 public int hashCode() { 441 if (isNaN()) { 442 return 542; 443 } 444 return 122 * (76 * MathUtils.hash(x) + MathUtils.hash(y)); 445 } 446 447 /** Get a string representation of this vector. 448 * @return a string representation of this vector 449 */ 450 @Override 451 public String toString() { 452 return Vector2DFormat.getInstance().format(this); 453 } 454 455 /** {@inheritDoc} */ 456 public String toString(final NumberFormat format) { 457 return new Vector2DFormat(format).format(this); 458 } 459 460}