• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  *      http://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 package org.apache.commons.math.geometry;
19 
20 import java.io.Serializable;
21 
22 import org.apache.commons.math.MathRuntimeException;
23 import org.apache.commons.math.exception.util.LocalizedFormats;
24 import org.apache.commons.math.util.MathUtils;
25 import org.apache.commons.math.util.FastMath;
26 
27 /**
28  * This class implements vectors in a three-dimensional space.
29  * <p>Instance of this class are guaranteed to be immutable.</p>
30  * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
31  * @since 1.2
32  */
33 
34 public class Vector3D
35   implements Serializable {
36 
37   /** Null vector (coordinates: 0, 0, 0). */
38   public static final Vector3D ZERO   = new Vector3D(0, 0, 0);
39 
40   /** First canonical vector (coordinates: 1, 0, 0). */
41   public static final Vector3D PLUS_I = new Vector3D(1, 0, 0);
42 
43   /** Opposite of the first canonical vector (coordinates: -1, 0, 0). */
44   public static final Vector3D MINUS_I = new Vector3D(-1, 0, 0);
45 
46   /** Second canonical vector (coordinates: 0, 1, 0). */
47   public static final Vector3D PLUS_J = new Vector3D(0, 1, 0);
48 
49   /** Opposite of the second canonical vector (coordinates: 0, -1, 0). */
50   public static final Vector3D MINUS_J = new Vector3D(0, -1, 0);
51 
52   /** Third canonical vector (coordinates: 0, 0, 1). */
53   public static final Vector3D PLUS_K = new Vector3D(0, 0, 1);
54 
55   /** Opposite of the third canonical vector (coordinates: 0, 0, -1).  */
56   public static final Vector3D MINUS_K = new Vector3D(0, 0, -1);
57 
58   // CHECKSTYLE: stop ConstantName
59   /** A vector with all coordinates set to NaN. */
60   public static final Vector3D NaN = new Vector3D(Double.NaN, Double.NaN, Double.NaN);
61   // CHECKSTYLE: resume ConstantName
62 
63   /** A vector with all coordinates set to positive infinity. */
64   public static final Vector3D POSITIVE_INFINITY =
65       new Vector3D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
66 
67   /** A vector with all coordinates set to negative infinity. */
68   public static final Vector3D NEGATIVE_INFINITY =
69       new Vector3D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
70 
71   /** Default format. */
72   private static final Vector3DFormat DEFAULT_FORMAT =
73       Vector3DFormat.getInstance();
74 
75   /** Serializable version identifier. */
76   private static final long serialVersionUID = 5133268763396045979L;
77 
78   /** Abscissa. */
79   private final double x;
80 
81   /** Ordinate. */
82   private final double y;
83 
84   /** Height. */
85   private final double z;
86 
87   /** Simple constructor.
88    * Build a vector from its coordinates
89    * @param x abscissa
90    * @param y ordinate
91    * @param z height
92    * @see #getX()
93    * @see #getY()
94    * @see #getZ()
95    */
Vector3D(double x, double y, double z)96   public Vector3D(double x, double y, double z) {
97     this.x = x;
98     this.y = y;
99     this.z = z;
100   }
101 
102   /** Simple constructor.
103    * Build a vector from its azimuthal coordinates
104    * @param alpha azimuth (&alpha;) around Z
105    *              (0 is +X, &pi;/2 is +Y, &pi; is -X and 3&pi;/2 is -Y)
106    * @param delta elevation (&delta;) above (XY) plane, from -&pi;/2 to +&pi;/2
107    * @see #getAlpha()
108    * @see #getDelta()
109    */
Vector3D(double alpha, double delta)110   public Vector3D(double alpha, double delta) {
111     double cosDelta = FastMath.cos(delta);
112     this.x = FastMath.cos(alpha) * cosDelta;
113     this.y = FastMath.sin(alpha) * cosDelta;
114     this.z = FastMath.sin(delta);
115   }
116 
117   /** Multiplicative constructor
118    * Build a vector from another one and a scale factor.
119    * The vector built will be a * u
120    * @param a scale factor
121    * @param u base (unscaled) vector
122    */
Vector3D(double a, Vector3D u)123   public Vector3D(double a, Vector3D u) {
124     this.x = a * u.x;
125     this.y = a * u.y;
126     this.z = a * u.z;
127   }
128 
129   /** Linear constructor
130    * Build a vector from two other ones and corresponding scale factors.
131    * The vector built will be a1 * u1 + a2 * u2
132    * @param a1 first scale factor
133    * @param u1 first base (unscaled) vector
134    * @param a2 second scale factor
135    * @param u2 second base (unscaled) vector
136    */
Vector3D(double a1, Vector3D u1, double a2, Vector3D u2)137   public Vector3D(double a1, Vector3D u1, double a2, Vector3D u2) {
138     this.x = a1 * u1.x + a2 * u2.x;
139     this.y = a1 * u1.y + a2 * u2.y;
140     this.z = a1 * u1.z + a2 * u2.z;
141   }
142 
143   /** Linear constructor
144    * Build a vector from three other ones and corresponding scale factors.
145    * The vector built will be a1 * u1 + a2 * u2 + a3 * u3
146    * @param a1 first scale factor
147    * @param u1 first base (unscaled) vector
148    * @param a2 second scale factor
149    * @param u2 second base (unscaled) vector
150    * @param a3 third scale factor
151    * @param u3 third base (unscaled) vector
152    */
Vector3D(double a1, Vector3D u1, double a2, Vector3D u2, double a3, Vector3D u3)153   public Vector3D(double a1, Vector3D u1, double a2, Vector3D u2,
154                   double a3, Vector3D u3) {
155     this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x;
156     this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y;
157     this.z = a1 * u1.z + a2 * u2.z + a3 * u3.z;
158   }
159 
160   /** Linear constructor
161    * Build a vector from four other ones and corresponding scale factors.
162    * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4
163    * @param a1 first scale factor
164    * @param u1 first base (unscaled) vector
165    * @param a2 second scale factor
166    * @param u2 second base (unscaled) vector
167    * @param a3 third scale factor
168    * @param u3 third base (unscaled) vector
169    * @param a4 fourth scale factor
170    * @param u4 fourth base (unscaled) vector
171    */
Vector3D(double a1, Vector3D u1, double a2, Vector3D u2, double a3, Vector3D u3, double a4, Vector3D u4)172   public Vector3D(double a1, Vector3D u1, double a2, Vector3D u2,
173                   double a3, Vector3D u3, double a4, Vector3D u4) {
174     this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x + a4 * u4.x;
175     this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y + a4 * u4.y;
176     this.z = a1 * u1.z + a2 * u2.z + a3 * u3.z + a4 * u4.z;
177   }
178 
179   /** Get the abscissa of the vector.
180    * @return abscissa of the vector
181    * @see #Vector3D(double, double, double)
182    */
getX()183   public double getX() {
184     return x;
185   }
186 
187   /** Get the ordinate of the vector.
188    * @return ordinate of the vector
189    * @see #Vector3D(double, double, double)
190    */
getY()191   public double getY() {
192     return y;
193   }
194 
195   /** Get the height of the vector.
196    * @return height of the vector
197    * @see #Vector3D(double, double, double)
198    */
getZ()199   public double getZ() {
200     return z;
201   }
202 
203   /** Get the L<sub>1</sub> norm for the vector.
204    * @return L<sub>1</sub> norm for the vector
205    */
getNorm1()206   public double getNorm1() {
207     return FastMath.abs(x) + FastMath.abs(y) + FastMath.abs(z);
208   }
209 
210   /** Get the L<sub>2</sub> norm for the vector.
211    * @return euclidian norm for the vector
212    */
getNorm()213   public double getNorm() {
214     return FastMath.sqrt (x * x + y * y + z * z);
215   }
216 
217   /** Get the square of the norm for the vector.
218    * @return square of the euclidian norm for the vector
219    */
getNormSq()220   public double getNormSq() {
221     return x * x + y * y + z * z;
222   }
223 
224   /** Get the L<sub>&infin;</sub> norm for the vector.
225    * @return L<sub>&infin;</sub> norm for the vector
226    */
getNormInf()227   public double getNormInf() {
228     return FastMath.max(FastMath.max(FastMath.abs(x), FastMath.abs(y)), FastMath.abs(z));
229   }
230 
231   /** Get the azimuth of the vector.
232    * @return azimuth (&alpha;) of the vector, between -&pi; and +&pi;
233    * @see #Vector3D(double, double)
234    */
getAlpha()235   public double getAlpha() {
236     return FastMath.atan2(y, x);
237   }
238 
239   /** Get the elevation of the vector.
240    * @return elevation (&delta;) of the vector, between -&pi;/2 and +&pi;/2
241    * @see #Vector3D(double, double)
242    */
getDelta()243   public double getDelta() {
244     return FastMath.asin(z / getNorm());
245   }
246 
247   /** Add a vector to the instance.
248    * @param v vector to add
249    * @return a new vector
250    */
add(Vector3D v)251   public Vector3D add(Vector3D v) {
252     return new Vector3D(x + v.x, y + v.y, z + v.z);
253   }
254 
255   /** Add a scaled vector to the instance.
256    * @param factor scale factor to apply to v before adding it
257    * @param v vector to add
258    * @return a new vector
259    */
add(double factor, Vector3D v)260   public Vector3D add(double factor, Vector3D v) {
261     return new Vector3D(x + factor * v.x, y + factor * v.y, z + factor * v.z);
262   }
263 
264   /** Subtract a vector from the instance.
265    * @param v vector to subtract
266    * @return a new vector
267    */
subtract(Vector3D v)268   public Vector3D subtract(Vector3D v) {
269     return new Vector3D(x - v.x, y - v.y, z - v.z);
270   }
271 
272   /** Subtract a scaled vector from the instance.
273    * @param factor scale factor to apply to v before subtracting it
274    * @param v vector to subtract
275    * @return a new vector
276    */
subtract(double factor, Vector3D v)277   public Vector3D subtract(double factor, Vector3D v) {
278     return new Vector3D(x - factor * v.x, y - factor * v.y, z - factor * v.z);
279   }
280 
281   /** Get a normalized vector aligned with the instance.
282    * @return a new normalized vector
283    * @exception ArithmeticException if the norm is zero
284    */
normalize()285   public Vector3D normalize() {
286     double s = getNorm();
287     if (s == 0) {
288       throw MathRuntimeException.createArithmeticException(LocalizedFormats.CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR);
289     }
290     return scalarMultiply(1 / s);
291   }
292 
293   /** Get a vector orthogonal to the instance.
294    * <p>There are an infinite number of normalized vectors orthogonal
295    * to the instance. This method picks up one of them almost
296    * arbitrarily. It is useful when one needs to compute a reference
297    * frame with one of the axes in a predefined direction. The
298    * following example shows how to build a frame having the k axis
299    * aligned with the known vector u :
300    * <pre><code>
301    *   Vector3D k = u.normalize();
302    *   Vector3D i = k.orthogonal();
303    *   Vector3D j = Vector3D.crossProduct(k, i);
304    * </code></pre></p>
305    * @return a new normalized vector orthogonal to the instance
306    * @exception ArithmeticException if the norm of the instance is null
307    */
orthogonal()308   public Vector3D orthogonal() {
309 
310     double threshold = 0.6 * getNorm();
311     if (threshold == 0) {
312       throw MathRuntimeException.createArithmeticException(LocalizedFormats.ZERO_NORM);
313     }
314 
315     if ((x >= -threshold) && (x <= threshold)) {
316       double inverse  = 1 / FastMath.sqrt(y * y + z * z);
317       return new Vector3D(0, inverse * z, -inverse * y);
318     } else if ((y >= -threshold) && (y <= threshold)) {
319       double inverse  = 1 / FastMath.sqrt(x * x + z * z);
320       return new Vector3D(-inverse * z, 0, inverse * x);
321     }
322     double inverse  = 1 / FastMath.sqrt(x * x + y * y);
323     return new Vector3D(inverse * y, -inverse * x, 0);
324 
325   }
326 
327   /** Compute the angular separation between two vectors.
328    * <p>This method computes the angular separation between two
329    * vectors using the dot product for well separated vectors and the
330    * cross product for almost aligned vectors. This allows to have a
331    * good accuracy in all cases, even for vectors very close to each
332    * other.</p>
333    * @param v1 first vector
334    * @param v2 second vector
335    * @return angular separation between v1 and v2
336    * @exception ArithmeticException if either vector has a null norm
337    */
angle(Vector3D v1, Vector3D v2)338   public static double angle(Vector3D v1, Vector3D v2) {
339 
340     double normProduct = v1.getNorm() * v2.getNorm();
341     if (normProduct == 0) {
342       throw MathRuntimeException.createArithmeticException(LocalizedFormats.ZERO_NORM);
343     }
344 
345     double dot = dotProduct(v1, v2);
346     double threshold = normProduct * 0.9999;
347     if ((dot < -threshold) || (dot > threshold)) {
348       // the vectors are almost aligned, compute using the sine
349       Vector3D v3 = crossProduct(v1, v2);
350       if (dot >= 0) {
351         return FastMath.asin(v3.getNorm() / normProduct);
352       }
353       return FastMath.PI - FastMath.asin(v3.getNorm() / normProduct);
354     }
355 
356     // the vectors are sufficiently separated to use the cosine
357     return FastMath.acos(dot / normProduct);
358 
359   }
360 
361   /** Get the opposite of the instance.
362    * @return a new vector which is opposite to the instance
363    */
negate()364   public Vector3D negate() {
365     return new Vector3D(-x, -y, -z);
366   }
367 
368   /** Multiply the instance by a scalar
369    * @param a scalar
370    * @return a new vector
371    */
scalarMultiply(double a)372   public Vector3D scalarMultiply(double a) {
373     return new Vector3D(a * x, a * y, a * z);
374   }
375 
376   /**
377    * Returns true if any coordinate of this vector is NaN; false otherwise
378    * @return  true if any coordinate of this vector is NaN; false otherwise
379    */
isNaN()380   public boolean isNaN() {
381       return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z);
382   }
383 
384   /**
385    * Returns true if any coordinate of this vector is infinite and none are NaN;
386    * false otherwise
387    * @return  true if any coordinate of this vector is infinite and none are NaN;
388    * false otherwise
389    */
isInfinite()390   public boolean isInfinite() {
391       return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y) || Double.isInfinite(z));
392   }
393 
394   /**
395    * Test for the equality of two 3D vectors.
396    * <p>
397    * If all coordinates of two 3D vectors are exactly the same, and none are
398    * <code>Double.NaN</code>, the two 3D vectors are considered to be equal.
399    * </p>
400    * <p>
401    * <code>NaN</code> coordinates are considered to affect globally the vector
402    * and be equals to each other - i.e, if either (or all) coordinates of the
403    * 3D vector are equal to <code>Double.NaN</code>, the 3D vector is equal to
404    * {@link #NaN}.
405    * </p>
406    *
407    * @param other Object to test for equality to this
408    * @return true if two 3D vector objects are equal, false if
409    *         object is null, not an instance of Vector3D, or
410    *         not equal to this Vector3D instance
411    *
412    */
413   @Override
equals(Object other)414   public boolean equals(Object other) {
415 
416     if (this == other) {
417       return true;
418     }
419 
420     if (other instanceof Vector3D) {
421       final Vector3D rhs = (Vector3D)other;
422       if (rhs.isNaN()) {
423           return this.isNaN();
424       }
425 
426       return (x == rhs.x) && (y == rhs.y) && (z == rhs.z);
427     }
428     return false;
429   }
430 
431   /**
432    * Get a hashCode for the 3D vector.
433    * <p>
434    * All NaN values have the same hash code.</p>
435    *
436    * @return a hash code value for this object
437    */
438   @Override
hashCode()439   public int hashCode() {
440       if (isNaN()) {
441           return 8;
442       }
443       return 31 * (23 * MathUtils.hash(x) +  19 * MathUtils.hash(y) +  MathUtils.hash(z));
444   }
445 
446   /** Compute the dot-product of two vectors.
447    * @param v1 first vector
448    * @param v2 second vector
449    * @return the dot product v1.v2
450    */
dotProduct(Vector3D v1, Vector3D v2)451   public static double dotProduct(Vector3D v1, Vector3D v2) {
452     return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
453   }
454 
455   /** Compute the cross-product of two vectors.
456    * @param v1 first vector
457    * @param v2 second vector
458    * @return the cross product v1 ^ v2 as a new Vector
459    */
crossProduct(Vector3D v1, Vector3D v2)460   public static Vector3D crossProduct(Vector3D v1, Vector3D v2) {
461     return new Vector3D(v1.y * v2.z - v1.z * v2.y,
462                         v1.z * v2.x - v1.x * v2.z,
463                         v1.x * v2.y - v1.y * v2.x);
464   }
465 
466   /** Compute the distance between two vectors according to the L<sub>1</sub> norm.
467    * <p>Calling this method is equivalent to calling:
468    * <code>v1.subtract(v2).getNorm1()</code> except that no intermediate
469    * vector is built</p>
470    * @param v1 first vector
471    * @param v2 second vector
472    * @return the distance between v1 and v2 according to the L<sub>1</sub> norm
473    */
distance1(Vector3D v1, Vector3D v2)474   public static double distance1(Vector3D v1, Vector3D v2) {
475     final double dx = FastMath.abs(v2.x - v1.x);
476     final double dy = FastMath.abs(v2.y - v1.y);
477     final double dz = FastMath.abs(v2.z - v1.z);
478     return dx + dy + dz;
479   }
480 
481   /** Compute the distance between two vectors according to the L<sub>2</sub> norm.
482    * <p>Calling this method is equivalent to calling:
483    * <code>v1.subtract(v2).getNorm()</code> except that no intermediate
484    * vector is built</p>
485    * @param v1 first vector
486    * @param v2 second vector
487    * @return the distance between v1 and v2 according to the L<sub>2</sub> norm
488    */
distance(Vector3D v1, Vector3D v2)489   public static double distance(Vector3D v1, Vector3D v2) {
490     final double dx = v2.x - v1.x;
491     final double dy = v2.y - v1.y;
492     final double dz = v2.z - v1.z;
493     return FastMath.sqrt(dx * dx + dy * dy + dz * dz);
494   }
495 
496   /** Compute the distance between two vectors according to the L<sub>&infin;</sub> norm.
497    * <p>Calling this method is equivalent to calling:
498    * <code>v1.subtract(v2).getNormInf()</code> except that no intermediate
499    * vector is built</p>
500    * @param v1 first vector
501    * @param v2 second vector
502    * @return the distance between v1 and v2 according to the L<sub>&infin;</sub> norm
503    */
distanceInf(Vector3D v1, Vector3D v2)504   public static double distanceInf(Vector3D v1, Vector3D v2) {
505     final double dx = FastMath.abs(v2.x - v1.x);
506     final double dy = FastMath.abs(v2.y - v1.y);
507     final double dz = FastMath.abs(v2.z - v1.z);
508     return FastMath.max(FastMath.max(dx, dy), dz);
509   }
510 
511   /** Compute the square of the distance between two vectors.
512    * <p>Calling this method is equivalent to calling:
513    * <code>v1.subtract(v2).getNormSq()</code> except that no intermediate
514    * vector is built</p>
515    * @param v1 first vector
516    * @param v2 second vector
517    * @return the square of the distance between v1 and v2
518    */
distanceSq(Vector3D v1, Vector3D v2)519   public static double distanceSq(Vector3D v1, Vector3D v2) {
520     final double dx = v2.x - v1.x;
521     final double dy = v2.y - v1.y;
522     final double dz = v2.z - v1.z;
523     return dx * dx + dy * dy + dz * dz;
524   }
525 
526   /** Get a string representation of this vector.
527    * @return a string representation of this vector
528    */
529   @Override
toString()530   public String toString() {
531       return DEFAULT_FORMAT.format(this);
532   }
533 
534 }
535