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