Mega Code Archive

 
Categories / Java / 2D Graphics GUI
 

Implements an Vector in 3D space

/* $Id: Vector3f.java,v 1.3 2005/08/20 10:15:55 joda Exp $  * Created on 22.07.2004  */ //package net.sourceforge.ftgl.util; /**  * Implements an Vector in 3D space.  *   * @author Ralf Petring  * @author funsheep  */ public class Vector3f implements Cloneable {   /** The tolerance we accept when prooving length against a value (e.g. 1.0f). */   public final static float TOLERANCE = 1E-6f;   /** x component*/   public float x;   /** y component*/   public float y;   /** z component*/   public float z;   //public float dx, dy;    // tkoch: only for test purposes regarding inclusion of .3DS loader   /**    * Constructs a new vector.    *     * @param x the first coordinate    * @param y the second coordinate    * @param z the third coordinate    */   public Vector3f(final float x, final float y, final float z)   {     this.x = x;     this.y = y;     this.z = z;   }   /**    * Constructs a new vector.    * @param vector new vector's coordinates    */   public Vector3f(final float[] vector)   {     this(vector[0], vector[1], vector[2]);   }   /**    * Constructs a new vector whith (0,0,0) as coordinates.    */   public Vector3f()   {     this(0, 0, 0);   }   /**    * Copyconstructor.    *     * @param v the vector to be copied    */   public Vector3f(final Vector3f v)   {     this(v.x, v.y, v.z);   }   /**    * {@inheritDoc}    */   public boolean equals(Object o)   {     if (o instanceof Vector3f)       return this.equals((Vector3f)o);     return false;   }   /**    * Compares this vector with the given vector.    *     * @param v the vector to be used for compare    * @return <code>true</code> if this vector has the same components as the argument vector    * <code>v</code>, false otherwise    */   public boolean equals(Vector3f v)   {     if (v == null)       return false;     return (this.x == v.x && this.y == v.y && this.z == v.z);   }   /**    * Compares this vector with the given vector.    * <p>Note: tolerance is applied per component, thus all vectors    * which equal this vector will form a box.</p>    * @param v the vector to be used for compare    * @param tolerance accepted tolerance    * @return <code>true</code> if this the components of this vector are equal to the argument vector    * <code>v</code> with a tolerance of argument <code>tolerance</code>, false otherwise    */   public boolean equals(Vector3f v, float tolerance)   {     if (v == null)       return false;     return (Math.abs(this.x-v.x)<tolerance &&         Math.abs(this.y-v.y)<tolerance &&         Math.abs(this.z-v.z)<tolerance);   }   /**    * {@inheritDoc}    */   public int hashCode()   {     int i1 = Float.floatToIntBits(this.x);     int i2 = Float.floatToIntBits(this.y);     int i3 = Float.floatToIntBits(this.z);     return i1 ^ ((i2 << 8) | (i2 >>> 24)) ^ ((i3 << 16) | (i3 >>> 16));   }   /**    * Calculates the (squared) distance between two Vectors.    *     * @param v1    * @param v2    * @return the distance (squared)    */   public static float distanceSquared(Vector3f v1, Vector3f v2)   {     float x = v1.x - v2.x;     float y = v1.y - v2.y;     float z = v1.z - v2.z;     return x*x + y*y + z*z;   }   /**    * Calculates the distance between two Vectors.    *     * @param v1    * @param v2    * @return the distance    */   public static float distance(Vector3f v1, Vector3f v2)   {     return (float)Math.sqrt(distanceSquared(v1, v2));   }   /**    * Adds the given float values to the coordinates of this vector.    *     * @param dx the value to be added to the x coordinate    * @param dy the value to be added to the y coordinate    * @param dz the value to be added to the z coordinate    * @return this    */   public Vector3f add(final float dx, final float dy, final float dz)   {     this.x += dx;     this.y += dy;     this.z += dz;     return this;   }   /**    * Adds a given vector to this vector.    *     * @param v the vector to be added    * @return this    */   public Vector3f add(final Vector3f v)   {     return this.add(v.x, v.y, v.z);   }   /**    * Adds to this vector the given one with the given scale.    * @param v A vector to add.    * @param d The length of the vector, to be added.    * @return this    */   public Vector3f addScaled(final Vector3f v, final float d)   {     return this.add(v.x * d, v.y * d, v.z * d);   }   /**    * Substracts a given vector from this vector.    *     * @param v the vector to be substracted    * @return this    */   public Vector3f sub(final Vector3f v)   {     return this.sub(v.x, v.y, v.z);   }   /**    * Substracts the given float values from the coordinates of this vector.    *     * @param dx the value to be substracted from the x coordinate    * @param dy the value to be substracted from the y coordinate    * @param dz the value to be substracted from the z coordinate    * @return this    */   public Vector3f sub(final float dx, final float dy, final float dz)   {     this.x -= dx;     this.y -= dy;     this.z -= dz;     return this;   }   /**    * Multiplies the coordinates of this vector with the given float values.    *     * @param dx the value to be multiplied the x coordinate    * @param dy the value to be multiplied the y coordinate    * @param dz the value to be multiplied the z coordinate    * @return this    */   public Vector3f scale(final float dx, final float dy, final float dz)   {     this.x *= dx;     this.y *= dy;     this.z *= dz;     return this;   }   /**    * Multiplies all coordinates of this vector with the given float value.    *     * @param d the value to be multiplied with all coordinates    * @return this    */   public Vector3f scale(final float d)   {     return this.scale(d, d, d);   }   /**    * Calculates the dotprodukt of this vector and the given vector.    *     * @param v the vector to be used for calculation    * @return the dotproduct    */   public float dot(final Vector3f v)   {     return this.x * v.x + this.y * v.y + this.z * v.z;   }   /**    * Normalizes this vector.    *     * @return this    */   public Vector3f normalize()   {     float l = this.length();     if (l != 0)     {       this.x /= l;       this.y /= l;       this.z /= l;     }     return this;   }   //Daniel K. wanted this name   /**    * Improved version of {@link #normalize()} which also works for VERY short vectors,    * or very large ones. This implementation is slighty slower than {@link #normalize()}.    * @return this    */   public Vector3f normalize2()   {     if (this.isZero())       throw new IllegalStateException("this is the zero vector");     if (this.isInvalid())       throw new IllegalStateException("invalid vector");     while (x>-0.5f && x<0.5f && y>-0.5f && y<0.5f && z>-0.5f && z<0.5f)     {       x *= 2;       y *= 2;       z *= 2;     }     while (x<=-1.0f || x>=1.0f || y<=-1.0f || y>=1.0f || z<=-1.0f || z>=1.0f)     {       x /= 2;       y /= 2;       z /= 2;     }     float l = this.length();     this.x /= l;     this.y /= l;     this.z /= l;     return this;   }   /**    * Returns <code>true</code> if vector is {@link Float#NEGATIVE_INFINITY},    * {@link Float#POSITIVE_INFINITY} or {@link Float#NaN}    * @return  <code>true</code> if vector is {@link Float#NEGATIVE_INFINITY},    * {@link Float#POSITIVE_INFINITY} or {@link Float#NaN}.    */   public boolean isInvalid()   {     return Float.isInfinite(x) || Float.isInfinite(y) || Float.isInfinite(z)       || Float.isNaN(x) || Float.isNaN(y) || Float.isNaN(z);   }   /**    * Returns <code>true</code> iff x exactly zero (0,0,0).    * @return <code>true</code> iff x exactly zero (0,0,0).    */   public boolean isZero()   {     return x==0f && y==0f && z==0f;   }   /**    * Returns <code>true</code> if vector is nearly zero (0,0,0).    * @param tolerance accepted tolerance    * @return <code>true</code> if vector is nearly zero (0,0,0).    */   public boolean isZero(float tolerance)   {     return Math.abs(this.x)<tolerance &&       Math.abs(this.y)<tolerance &&       Math.abs(this.z)<tolerance;   }   /**    * Calculates the angle between this vector and the given vector.    *     * @param v the vector to be used for calculation    * @return the angle between this and v    */   public float angle(final Vector3f v)   {     float dls = dot(v) / (this.length() * v.length());     if (dls < -1.0f)       dls = -1.0f;     else if (dls > 1.0f)       dls = 1.0f;     return (float)Math.acos(dls);   }   /**    * Sets all coordinates of this vector to zero.    *     * @return this    */   public Vector3f setZero()   {     this.x = 0;     this.y = 0;     this.z = 0;     return this;   }   /**    * Copies all values from the given vector to this vector.    *     * @param v the vector to be copied    * @return this    */   public Vector3f set(final Vector3f v)   {     this.x = v.x;     this.y = v.y;     this.z = v.z;     return this;   }   public Vector3f set(float x, float y, float z)   {     this.x = x;     this.y = y;     this.z = z;     return this;   }   /**    * Sets the vector's components to the first 3 values    * in argument <code>array</code>.    * @param array array which contains the new values    * @return this    */   public Vector3f set(final float[] array)   {     this.x = array[0];     this.y = array[1];     this.z = array[2];     return this;   }   /**    * Negates all coordinates of this vector.    *     * @return this    */   public Vector3f negate()   {     this.x = -this.x;     this.y = -this.y;     this.z = -this.z;     return this;   }   /**    * Returns the squared length of this vector.    *     * @return the squared length of this vector    */   public float lengthSquared()   {     return (this.x * this.x + this.y * this.y + this.z * this.z);   }   /**    * Returns the length of this vector.    *     * @return the length of this vector    */   public float length()   {     return (float)Math.sqrt(this.lengthSquared());   }   /**    * Calculates the vector cross product of this vector and the given vector.    *     * @param v the second vector    * @return this    */   public Vector3f cross(final Vector3f v)   {     final float a = this.y * v.z - this.z * v.y;     final float b = this.z * v.x - this.x * v.z;     final float c = this.x * v.y - this.y * v.x;     this.x = a;     this.y = b;     this.z = c;     return this;   }   /**    * Calculates the vector cross product of the two given vectors.    * <br><code>a x b = c</code> c is returned.    * @param a The first vector.    * @param b The second vector.    * @return A new vector, holding the cross product of a x b.    */   public static Vector3f cross(final Vector3f a, final Vector3f b)   {     return new Vector3f(         a.y * b.z - a.z * b.y,         a.z * b.x - a.x * b.z,         a.x * b.y - a.y * b.x);   }   /**    * {@inheritDoc}    */   public Object clone()   {     try     {       return super.clone();     }     catch (CloneNotSupportedException e)     {       throw new RuntimeException("the roof is on fire", e);     }   }   /**    * {@inheritDoc}    */   public String toString()   {     return "[" + this.x + "," + this.y + "," + this.z + "]";   }   /**    * Copies all components of this vector into the first 3 array components.    * @param dest array to be filled - may be <code>null</code>    * @return argument <code>dest</code> or a new array filled with this    * instances components.    */   public float[] toArray(float[] dest)   {     if (dest==null)       dest = new float[3];     dest[0] = this.x;     dest[1] = this.y;     dest[2] = this.z;     return dest;   }   /**    * Returns <code>true</code> if this vector is normalized. If it's length is <code>1</code>.    * @return <code>true</code> if this vector is normalized.    */   public boolean isNormalized()   {     return this.lengthSquared()==1;   }   /**    * Returns <code>true</code> if this vector is normalized.    * If it's length is <code>1</code> within a tolerance of argument <code>tolerance</code>.    * @param tolerance amount of error to be ignores - must be positive!    * <p>Note: the normal is computed by using {@link #lengthSquared()}, thus    * the following equation approximated.    * For a Vector3f <code>v</code>:    * Correct:<pre>return {@link Math#sqrt(double) sqrt}({@link #lengthSquared()    * v.lengthSquared()})>1f-tolerance;</pre>    *     * Steps to a more numerical stable and faster test:    * <pre>    * v.lengthSquared()>(1f-tolerance)^2    *  &lt;=&gt; v.lengthSquared()>1f-2f*tolerance+tolerance^2;</pre>    * Optimized and approximated result:    * <pre>return v.lengthSquared()>1f-2f*tolerance;</pre>    * This test is infact less precise than the original test,    * but seems to be reasonable, as a tolerance is assumed to be pretty small!    * @return <code>true</code> if this vector is normalized.    */   public boolean isNormalized(float tolerance)   {     assert tolerance>=0:"tolerance must be positive or 0";     float tmp1 = this.lengthSquared() - 1f;     float tmp2 = 2f * tolerance + tolerance * tolerance;     return tmp1 < tmp2 && -tmp1 < tmp2;   }   /**    * Returns <code>true</code> if this vector is orthogonal to argument <code>other</code>.    * @param other other vector    * @return <code>true</code> if this vector is orthogonal to argument <code>other</code>.    */   public boolean isOrthogonal(Vector3f other)   {     return this.dot(other)==0;   }   /**    * Returns <code>true</code> if this vector is orthogonal to argument <code>other</code>.    * @param other other vector    * @param tolerance amount of error to be ignores - must be positive!    * @return <code>true</code> if this vector is orthogonal to argument <code>other</code>.    */   public boolean isOrthogonal(Vector3f other, float tolerance)   {     assert tolerance>=0:"tolerance must be positive or 0";     return this.dot(other)>-tolerance;   } }