Vector3f.java Source code

Java tutorial

Introduction

Here is the source code for Vector3f.java

Source

/* $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;
    }
}