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.geometry.euclidean.twod; 018 019import java.util.Arrays; 020import java.util.Comparator; 021import java.util.Iterator; 022import java.util.function.UnaryOperator; 023 024import org.apache.commons.geometry.core.internal.DoubleFunction2N; 025import org.apache.commons.geometry.core.internal.SimpleTupleFormat; 026import org.apache.commons.geometry.euclidean.EuclideanVectorSum; 027import org.apache.commons.geometry.euclidean.MultiDimensionalEuclideanVector; 028import org.apache.commons.geometry.euclidean.internal.Vectors; 029import org.apache.commons.numbers.core.Precision; 030 031/** This class represents vectors and points in two-dimensional Euclidean space. 032 * Instances of this class are guaranteed to be immutable. 033 */ 034public class Vector2D extends MultiDimensionalEuclideanVector<Vector2D> { 035 036 /** Zero vector (coordinates: 0, 0). */ 037 public static final Vector2D ZERO = new Vector2D(0, 0); 038 039 /** A vector with all coordinates set to NaN. */ 040 public static final Vector2D NaN = new Vector2D(Double.NaN, Double.NaN); 041 042 /** A vector with all coordinates set to positive infinity. */ 043 public static final Vector2D POSITIVE_INFINITY = 044 new Vector2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); 045 046 /** A vector with all coordinates set to negative infinity. */ 047 public static final Vector2D NEGATIVE_INFINITY = 048 new Vector2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); 049 050 /** Comparator that sorts vectors in component-wise ascending order. 051 * Vectors are only considered equal if their coordinates match exactly. 052 * Null arguments are evaluated as being greater than non-null arguments. 053 */ 054 public static final Comparator<Vector2D> COORDINATE_ASCENDING_ORDER = (a, b) -> { 055 int cmp = 0; 056 057 if (a != null && b != null) { 058 cmp = Double.compare(a.getX(), b.getX()); 059 if (cmp == 0) { 060 cmp = Double.compare(a.getY(), b.getY()); 061 } 062 } else if (a != null) { 063 cmp = -1; 064 } else if (b != null) { 065 cmp = 1; 066 } 067 068 return cmp; 069 }; 070 071 /** Abscissa (first coordinate). */ 072 private final double x; 073 074 /** Ordinate (second coordinate). */ 075 private final double y; 076 077 /** Simple constructor. 078 * @param x abscissa (first coordinate) 079 * @param y ordinate (second coordinate) 080 */ 081 private Vector2D(final double x, final double y) { 082 this.x = x; 083 this.y = y; 084 } 085 086 /** Returns the abscissa (first coordinate value) of the instance. 087 * @return the abscissa 088 */ 089 public double getX() { 090 return x; 091 } 092 093 /** Returns the ordinate (second coordinate value) of the instance. 094 * @return the ordinate 095 */ 096 public double getY() { 097 return y; 098 } 099 100 /** Get the coordinates for this instance as a dimension 2 array. 101 * @return coordinates for this instance 102 */ 103 public double[] toArray() { 104 return new double[]{x, y}; 105 } 106 107 /** {@inheritDoc} */ 108 @Override 109 public int getDimension() { 110 return 2; 111 } 112 113 /** {@inheritDoc} */ 114 @Override 115 public boolean isNaN() { 116 return Double.isNaN(x) || Double.isNaN(y); 117 } 118 119 /** {@inheritDoc} */ 120 @Override 121 public boolean isInfinite() { 122 return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y)); 123 } 124 125 /** {@inheritDoc} */ 126 @Override 127 public boolean isFinite() { 128 return Double.isFinite(x) && Double.isFinite(y); 129 } 130 131 /** {@inheritDoc} */ 132 @Override 133 public Vector2D vectorTo(final Vector2D v) { 134 return v.subtract(this); 135 } 136 137 /** {@inheritDoc} */ 138 @Override 139 public Unit directionTo(final Vector2D v) { 140 return vectorTo(v).normalize(); 141 } 142 143 /** {@inheritDoc} */ 144 @Override 145 public Vector2D lerp(final Vector2D p, final double t) { 146 return Sum.create() 147 .addScaled(1.0 - t, this) 148 .addScaled(t, p).get(); 149 } 150 151 /** {@inheritDoc} */ 152 @Override 153 public Vector2D getZero() { 154 return ZERO; 155 } 156 157 /** {@inheritDoc} */ 158 @Override 159 public double norm() { 160 return Vectors.norm(x, y); 161 } 162 163 /** {@inheritDoc} */ 164 @Override 165 public double normSq() { 166 return Vectors.normSq(x, y); 167 } 168 169 /** {@inheritDoc} */ 170 @Override 171 public Vector2D withNorm(final double magnitude) { 172 final double invNorm = 1.0 / getCheckedNorm(); 173 174 return new Vector2D( 175 magnitude * x * invNorm, 176 magnitude * y * invNorm 177 ); 178 } 179 180 /** {@inheritDoc} */ 181 @Override 182 public Vector2D add(final Vector2D v) { 183 return new Vector2D(x + v.x, y + v.y); 184 } 185 186 /** {@inheritDoc} */ 187 @Override 188 public Vector2D add(final double factor, final Vector2D v) { 189 return new Vector2D(x + (factor * v.x), y + (factor * v.y)); 190 } 191 192 /** {@inheritDoc} */ 193 @Override 194 public Vector2D subtract(final Vector2D v) { 195 return new Vector2D(x - v.x, y - v.y); 196 } 197 198 /** {@inheritDoc} */ 199 @Override 200 public Vector2D subtract(final double factor, final Vector2D v) { 201 return new Vector2D(x - (factor * v.x), y - (factor * v.y)); 202 } 203 204 /** {@inheritDoc} */ 205 @Override 206 public Vector2D negate() { 207 return new Vector2D(-x, -y); 208 } 209 210 /** {@inheritDoc} */ 211 @Override 212 public Unit normalize() { 213 return Unit.from(x, y); 214 } 215 216 /** {@inheritDoc} */ 217 @Override 218 public Unit normalizeOrNull() { 219 return Unit.tryCreateNormalized(x, y, false); 220 } 221 222 /** {@inheritDoc} */ 223 @Override 224 public Vector2D multiply(final double a) { 225 return new Vector2D(a * x, a * y); 226 } 227 228 /** {@inheritDoc} */ 229 @Override 230 public double distance(final Vector2D v) { 231 return Vectors.norm(x - v.x, y - v.y); 232 } 233 234 /** {@inheritDoc} */ 235 @Override 236 public double distanceSq(final Vector2D v) { 237 return Vectors.normSq(x - v.x, y - v.y); 238 } 239 240 /** {@inheritDoc} */ 241 @Override 242 public double dot(final Vector2D v) { 243 return Vectors.linearCombination(x, v.x, y, v.y); 244 } 245 246 /** {@inheritDoc} 247 * <p>This method computes the angular separation between the two 248 * vectors using the dot product for well separated vectors and the 249 * cross product for almost aligned vectors. This allows to have a 250 * good accuracy in all cases, even for vectors very close to each 251 * other.</p> 252 */ 253 @Override 254 public double angle(final Vector2D v) { 255 final double normProduct = getCheckedNorm() * v.getCheckedNorm(); 256 257 final double dot = dot(v); 258 final double threshold = normProduct * 0.9999; 259 if ((dot < -threshold) || (dot > threshold)) { 260 // the vectors are almost aligned, compute using the sine 261 final double n = Math.abs(Vectors.linearCombination(x, v.y, -y, v.x)); 262 if (dot >= 0) { 263 return Math.asin(n / normProduct); 264 } 265 return Math.PI - Math.asin(n / normProduct); 266 } 267 268 // the vectors are sufficiently separated to use the cosine 269 return Math.acos(dot / normProduct); 270 } 271 272 /** {@inheritDoc} */ 273 @Override 274 public Vector2D project(final Vector2D base) { 275 return getComponent(base, false, Vector2D::new); 276 } 277 278 /** {@inheritDoc} */ 279 @Override 280 public Vector2D reject(final Vector2D base) { 281 return getComponent(base, true, Vector2D::new); 282 } 283 284 /** {@inheritDoc} 285 * The returned vector is computed by rotating the current instance {@code pi/2} radians 286 * counterclockwise around the origin and normalizing. For example, if this method is 287 * called on a vector pointing along the positive x-axis, then a unit vector representing 288 * the positive y-axis is returned. 289 * @return a unit vector orthogonal to the current instance 290 * @throws IllegalArgumentException if the norm of the current instance is zero, NaN, or infinite 291 */ 292 @Override 293 public Vector2D.Unit orthogonal() { 294 return Unit.from(-y, x); 295 } 296 297 /** {@inheritDoc} */ 298 @Override 299 public Vector2D.Unit orthogonal(final Vector2D dir) { 300 return dir.getComponent(this, true, Vector2D.Unit::from); 301 } 302 303 /** Compute the signed area of the parallelogram with sides formed by this instance 304 * and the given vector. 305 * 306 * <p>The parallelogram in question can be visualized by taking the current instance as the 307 * first side and placing {@code v} at the end of it to create the second. The other sides 308 * are formed by lines parallel to these two vectors. If {@code v} points to the <em>left</em> of 309 * the current instance (ie, the parallelogram is wound counter-clockwise), then the 310 * returned area is positive. If {@code v} points to the <em>right</em> of the current instance, 311 * (ie, the parallelogram is wound clockwise), then the returned area is negative. If 312 * the vectors are collinear (ie, they lie on the same line), then 0 is returned. The area of 313 * the triangle formed by the two vectors is exactly half of the returned value. 314 * @param v vector representing the second side of the constructed parallelogram 315 * @return the signed area of the parallelogram formed by this instance and the given vector 316 */ 317 public double signedArea(final Vector2D v) { 318 return Vectors.linearCombination( 319 x, v.y, 320 -y, v.x); 321 } 322 323 /** Convenience method to apply a function to this vector. This 324 * can be used to transform the vector inline with other methods. 325 * @param fn the function to apply 326 * @return the transformed vector 327 */ 328 public Vector2D transform(final UnaryOperator<Vector2D> fn) { 329 return fn.apply(this); 330 } 331 332 /** {@inheritDoc} */ 333 @Override 334 public boolean eq(final Vector2D vec, final Precision.DoubleEquivalence precision) { 335 return precision.eq(x, vec.x) && 336 precision.eq(y, vec.y); 337 } 338 339 /** 340 * Get a hashCode for the 2D coordinates. 341 * <p> 342 * All NaN values have the same hash code.</p> 343 * 344 * @return a hash code value for this object 345 */ 346 @Override 347 public int hashCode() { 348 if (isNaN()) { 349 return 542; 350 } 351 return 122 * (76 * Double.hashCode(x) + Double.hashCode(y)); 352 } 353 354 /** 355 * Test for the equality of two vector instances. 356 * <p> 357 * If all coordinates of two vectors are exactly the same, and none are 358 * <code>Double.NaN</code>, the two instances are considered to be equal. 359 * </p> 360 * <p> 361 * <code>NaN</code> coordinates are considered to globally affect the vector 362 * and be equal to each other - i.e, if either (or all) coordinates of the 363 * vector are equal to <code>Double.NaN</code>, the vector is equal to 364 * {@link #NaN}. 365 * </p> 366 * 367 * @param other Object to test for equality to this 368 * @return true if two Vector2D objects are equal, false if 369 * object is null, not an instance of Vector2D, or 370 * not equal to this Vector2D instance 371 * 372 */ 373 @Override 374 public boolean equals(final Object other) { 375 if (this == other) { 376 return true; 377 } 378 if (other instanceof Vector2D) { 379 final Vector2D rhs = (Vector2D) other; 380 if (rhs.isNaN()) { 381 return this.isNaN(); 382 } 383 384 return Double.compare(x, rhs.x) == 0 && 385 Double.compare(y, rhs.y) == 0; 386 } 387 return false; 388 } 389 390 /** {@inheritDoc} */ 391 @Override 392 public String toString() { 393 return SimpleTupleFormat.getDefault().format(x, y); 394 } 395 396 /** Returns a component of the current instance relative to the given base 397 * vector. If {@code reject} is true, the vector rejection is returned; otherwise, 398 * the projection is returned. 399 * @param base The base vector 400 * @param reject If true, the rejection of this instance from {@code base} is 401 * returned. If false, the projection of this instance onto {@code base} 402 * is returned. 403 * @param factory factory function used to build the final vector 404 * @param <T> Vector implementation type 405 * @return The projection or rejection of this instance relative to {@code base}, 406 * depending on the value of {@code reject}. 407 * @throws IllegalArgumentException if {@code base} has a zero, NaN, or infinite norm 408 */ 409 private <T extends Vector2D> T getComponent(final Vector2D base, final boolean reject, 410 final DoubleFunction2N<T> factory) { 411 final double aDotB = dot(base); 412 413 // We need to check the norm value here to ensure that it's legal. However, we don't 414 // want to incur the cost or floating point error of getting the actual norm and then 415 // multiplying it again to get the square norm. So, we'll just check the squared norm 416 // directly. This will produce the same error result as checking the actual norm since 417 // Math.sqrt(0.0) == 0.0, Math.sqrt(Double.NaN) == Double.NaN and 418 // Math.sqrt(Double.POSITIVE_INFINITY) == Double.POSITIVE_INFINITY. 419 final double baseMagSq = Vectors.checkedNorm(base.normSq()); 420 421 final double scale = aDotB / baseMagSq; 422 423 final double projX = scale * base.x; 424 final double projY = scale * base.y; 425 426 if (reject) { 427 return factory.apply(x - projX, y - projY); 428 } 429 430 return factory.apply(projX, projY); 431 } 432 433 /** Returns a vector with the given coordinate values. 434 * @param x abscissa (first coordinate value) 435 * @param y abscissa (second coordinate value) 436 * @return vector instance 437 */ 438 public static Vector2D of(final double x, final double y) { 439 return new Vector2D(x, y); 440 } 441 442 /** Creates a vector from the coordinates in the given 2-element array. 443 * @param v coordinates array 444 * @return new vector 445 * @exception IllegalArgumentException if the array does not have 2 elements 446 */ 447 public static Vector2D of(final double[] v) { 448 if (v.length != 2) { 449 throw new IllegalArgumentException("Dimension mismatch: " + v.length + " != 2"); 450 } 451 return new Vector2D(v[0], v[1]); 452 } 453 454 /** Parses the given string and returns a new vector instance. The expected string 455 * format is the same as that returned by {@link #toString()}. 456 * @param str the string to parse 457 * @return vector instance represented by the string 458 * @throws IllegalArgumentException if the given string has an invalid format 459 */ 460 public static Vector2D parse(final String str) { 461 return SimpleTupleFormat.getDefault().parse(str, Vector2D::new); 462 } 463 464 /** Return a vector containing the maximum component values from all input vectors. 465 * @param first first vector 466 * @param more additional vectors 467 * @return a vector containing the maximum component values from all input vectors 468 */ 469 public static Vector2D max(final Vector2D first, final Vector2D... more) { 470 return computeMax(first, Arrays.asList(more).iterator()); 471 } 472 473 /** Return a vector containing the maximum component values from all input vectors. 474 * @param vecs input vectors 475 * @return a vector containing the maximum component values from all input vectors 476 * @throws IllegalArgumentException if the argument does not contain any vectors 477 */ 478 public static Vector2D max(final Iterable<Vector2D> vecs) { 479 final Iterator<Vector2D> it = vecs.iterator(); 480 if (!it.hasNext()) { 481 throw new IllegalArgumentException("Cannot compute vector max: no vectors given"); 482 } 483 484 return computeMax(it.next(), it); 485 } 486 487 /** Internal method for computing a max vector. 488 * @param first first vector 489 * @param more iterator with additional vectors 490 * @return vector containing the maximum component values of all input vectors 491 */ 492 private static Vector2D computeMax(final Vector2D first, final Iterator<? extends Vector2D> more) { 493 double x = first.getX(); 494 double y = first.getY(); 495 496 Vector2D vec; 497 while (more.hasNext()) { 498 vec = more.next(); 499 500 x = Math.max(x, vec.getX()); 501 y = Math.max(y, vec.getY()); 502 } 503 504 return Vector2D.of(x, y); 505 } 506 507 /** Return a vector containing the minimum component values from all input vectors. 508 * @param first first vector 509 * @param more more vectors 510 * @return a vector containing the minimum component values from all input vectors 511 */ 512 public static Vector2D min(final Vector2D first, final Vector2D... more) { 513 return computeMin(first, Arrays.asList(more).iterator()); 514 } 515 516 /** Return a vector containing the minimum component values from all input vectors. 517 * @param vecs input vectors 518 * @return a vector containing the minimum component values from all input vectors 519 * @throws IllegalArgumentException if the argument does not contain any vectors 520 */ 521 public static Vector2D min(final Iterable<Vector2D> vecs) { 522 final Iterator<Vector2D> it = vecs.iterator(); 523 if (!it.hasNext()) { 524 throw new IllegalArgumentException("Cannot compute vector min: no vectors given"); 525 } 526 527 return computeMin(it.next(), it); 528 } 529 530 /** Internal method for computing a min vector. 531 * @param first first vector 532 * @param more iterator with additional vectors 533 * @return vector containing the minimum component values of all input vectors 534 */ 535 private static Vector2D computeMin(final Vector2D first, final Iterator<? extends Vector2D> more) { 536 double x = first.getX(); 537 double y = first.getY(); 538 539 Vector2D vec; 540 while (more.hasNext()) { 541 vec = more.next(); 542 543 x = Math.min(x, vec.getX()); 544 y = Math.min(y, vec.getY()); 545 } 546 547 return Vector2D.of(x, y); 548 } 549 550 /** Compute the centroid of the given points. The centroid is the arithmetic mean position of a set 551 * of points. 552 * @param first first point 553 * @param more additional points 554 * @return the centroid of the given points 555 */ 556 public static Vector2D centroid(final Vector2D first, final Vector2D... more) { 557 return computeCentroid(first, Arrays.asList(more).iterator()); 558 } 559 560 /** Compute the centroid of the given points. The centroid is the arithmetic mean position of a set 561 * of points. 562 * @param pts the points to compute the centroid of 563 * @return the centroid of the given points 564 * @throws IllegalArgumentException if the argument contains no points 565 */ 566 public static Vector2D centroid(final Iterable<Vector2D> pts) { 567 final Iterator<Vector2D> it = pts.iterator(); 568 if (!it.hasNext()) { 569 throw new IllegalArgumentException("Cannot compute centroid: no points given"); 570 } 571 572 return computeCentroid(it.next(), it); 573 } 574 575 /** Internal method for computing the centroid of a set of points. 576 * @param first first point 577 * @param more iterator with additional points 578 * @return the centroid of the point set 579 */ 580 private static Vector2D computeCentroid(final Vector2D first, final Iterator<? extends Vector2D> more) { 581 final Sum sum = Sum.of(first); 582 int count = 1; 583 584 while (more.hasNext()) { 585 sum.add(more.next()); 586 ++count; 587 } 588 589 return sum.get().multiply(1.0 / count); 590 } 591 592 /** 593 * Represents unit vectors. 594 * This allows optimizations for certain operations. 595 */ 596 public static final class Unit extends Vector2D { 597 /** Unit vector (coordinates: 1, 0). */ 598 public static final Unit PLUS_X = new Unit(1d, 0d); 599 /** Negation of unit vector (coordinates: -1, 0). */ 600 public static final Unit MINUS_X = new Unit(-1d, 0d); 601 /** Unit vector (coordinates: 0, 1). */ 602 public static final Unit PLUS_Y = new Unit(0d, 1d); 603 /** Negation of unit vector (coordinates: 0, -1). */ 604 public static final Unit MINUS_Y = new Unit(0d, -1d); 605 606 /** Maximum coordinate value for computing normalized vectors 607 * with raw, unscaled values. 608 */ 609 private static final double UNSCALED_MAX = 0x1.0p+500; 610 611 /** Factor used to scale up coordinate values in order to produce 612 * normalized coordinates without overflow or underflow. 613 */ 614 private static final double SCALE_UP_FACTOR = 0x1.0p+600; 615 616 /** Factor used to scale down coordinate values in order to produce 617 * normalized coordinates without overflow or underflow. 618 */ 619 private static final double SCALE_DOWN_FACTOR = 0x1.0p-600; 620 621 /** Simple constructor. Callers are responsible for ensuring that the given 622 * values represent a normalized vector. 623 * @param x abscissa (first coordinate value) 624 * @param y abscissa (second coordinate value) 625 */ 626 private Unit(final double x, final double y) { 627 super(x, y); 628 } 629 630 /** {@inheritDoc} */ 631 @Override 632 public double norm() { 633 return 1; 634 } 635 636 /** {@inheritDoc} */ 637 @Override 638 public double normSq() { 639 return 1; 640 } 641 642 /** {@inheritDoc} */ 643 @Override 644 public Unit normalize() { 645 return this; 646 } 647 648 /** {@inheritDoc} */ 649 @Override 650 public Unit normalizeOrNull() { 651 return this; 652 } 653 654 /** {@inheritDoc} */ 655 @Override 656 public Vector2D.Unit orthogonal() { 657 return new Unit(-getY(), getX()); 658 } 659 660 /** {@inheritDoc} */ 661 @Override 662 public Vector2D withNorm(final double mag) { 663 return multiply(mag); 664 } 665 666 /** {@inheritDoc} */ 667 @Override 668 public Unit negate() { 669 return new Unit(-getX(), -getY()); 670 } 671 672 /** Create a normalized vector. 673 * @param x Vector coordinate. 674 * @param y Vector coordinate. 675 * @return a vector whose norm is 1. 676 * @throws IllegalArgumentException if the norm of the given value is zero, NaN, 677 * or infinite 678 */ 679 public static Unit from(final double x, final double y) { 680 return tryCreateNormalized(x, y, true); 681 } 682 683 /** Create a normalized vector. 684 * @param v Vector. 685 * @return a vector whose norm is 1. 686 * @throws IllegalArgumentException if the norm of the given value is zero, NaN, 687 * or infinite 688 */ 689 public static Unit from(final Vector2D v) { 690 return v instanceof Unit ? 691 (Unit) v : 692 from(v.getX(), v.getY()); 693 } 694 695 /** Attempt to create a normalized vector from the given coordinate values. If {@code throwOnFailure} 696 * is true, an exception is thrown if a normalized vector cannot be created. Otherwise, null 697 * is returned. 698 * @param x x coordinate 699 * @param y y coordinate 700 * @param throwOnFailure if true, an exception will be thrown if a normalized vector cannot be created 701 * @return normalized vector or null if one cannot be created and {@code throwOnFailure} 702 * is false 703 * @throws IllegalArgumentException if the computed norm is zero, NaN, or infinite 704 */ 705 private static Unit tryCreateNormalized(final double x, final double y, final boolean throwOnFailure) { 706 707 // Compute the inverse norm directly. If the result is a non-zero real number, 708 // then we can go ahead and construct the unit vector immediately. If not, 709 // we'll do some extra work for edge cases. 710 final double norm = Vectors.norm(x, y); 711 final double normInv = 1.0 / norm; 712 if (Vectors.isRealNonZero(normInv)) { 713 return new Unit( 714 x * normInv, 715 y * normInv); 716 } 717 718 // Direct computation did not work. Try scaled versions of the coordinates 719 // to handle overflow and underflow. 720 final double scaledX; 721 final double scaledY; 722 723 final double maxCoord = Math.max(Math.abs(x), Math.abs(y)); 724 if (maxCoord > UNSCALED_MAX) { 725 scaledX = x * SCALE_DOWN_FACTOR; 726 scaledY = y * SCALE_DOWN_FACTOR; 727 } else { 728 scaledX = x * SCALE_UP_FACTOR; 729 scaledY = y * SCALE_UP_FACTOR; 730 } 731 732 final double scaledNormInv = 1.0 / Vectors.norm(scaledX, scaledY); 733 734 if (Vectors.isRealNonZero(scaledNormInv)) { 735 return new Unit( 736 scaledX * scaledNormInv, 737 scaledY * scaledNormInv); 738 } else if (throwOnFailure) { 739 throw Vectors.illegalNorm(norm); 740 } 741 return null; 742 } 743 } 744 745 /** Class used to create high-accuracy sums of vectors. Each vector component is 746 * summed using an instance of {@link org.apache.commons.numbers.core.Sum}. 747 * 748 * <p>This class is mutable and not thread-safe. 749 * @see org.apache.commons.numbers.core.Sum 750 */ 751 public static final class Sum extends EuclideanVectorSum<Vector2D> { 752 /** X component sum. */ 753 private final org.apache.commons.numbers.core.Sum xsum; 754 /** Y component sum. */ 755 private final org.apache.commons.numbers.core.Sum ysum; 756 757 /** Construct a new instance with the given initial value. 758 * @param initial initial value 759 */ 760 Sum(final Vector2D initial) { 761 this.xsum = org.apache.commons.numbers.core.Sum.of(initial.x); 762 this.ysum = org.apache.commons.numbers.core.Sum.of(initial.y); 763 } 764 765 /** {@inheritDoc} */ 766 @Override 767 public Sum add(final Vector2D vec) { 768 xsum.add(vec.x); 769 ysum.add(vec.y); 770 return this; 771 } 772 773 /** {@inheritDoc} */ 774 @Override 775 public Sum addScaled(final double scale, final Vector2D vec) { 776 xsum.addProduct(scale, vec.x); 777 ysum.addProduct(scale, vec.y); 778 return this; 779 } 780 781 /** {@inheritDoc} */ 782 @Override 783 public Vector2D get() { 784 return Vector2D.of( 785 xsum.getAsDouble(), 786 ysum.getAsDouble()); 787 } 788 789 /** Create a new instance with an initial value set to the {@link Vector2D#ZERO zero vector}. 790 * @return new instance set to zero 791 */ 792 public static Sum create() { 793 return new Sum(Vector2D.ZERO); 794 } 795 796 /** Construct a new instance with an initial value set to the argument. 797 * @param initial initial sum value 798 * @return new instance 799 */ 800 public static Sum of(final Vector2D initial) { 801 return new Sum(initial); 802 } 803 804 /** Construct a new instance from multiple values. 805 * @param first first vector 806 * @param more additional vectors 807 * @return new instance 808 */ 809 public static Sum of(final Vector2D first, final Vector2D... more) { 810 final Sum s = new Sum(first); 811 for (final Vector2D v : more) { 812 s.add(v); 813 } 814 return s; 815 } 816 } 817}