/*
 * Vector3D.java
 *
 * Created on November 21, 2006, 5:13 PM
 *
 */
package edu.oregonstate.cartography.app;

/**
 * Vector3D is a vector in three-dimensional space.
 */
public final class Vector3D {

    /**
     * X component of the vector.
     */
    public double x;
    /**
     * Y component of the vector.
     */
    public double y;
    /**
     * Z component of the vector.
     */
    public double z;

    /**
     * Creates a new instance of Vector3D.
     *
     * @param x X component of the vector.
     * @param y Y component of the vector.
     * @param z Z component of the vector.
     */
    public Vector3D(double x, double y, double z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    /**
     * Creates a new instance of Vector3D. The new vector has a length of 1. Its
     * orientation is specified by azimuth and a zenith.
     *
     * @param azimuth Azimuth of the direction. Counted from north in counter-
     * clockwise direction. Between 0 and 360 degrees.
     * @param zenith The vertical angle of the light direction from the zenith
     * towards the horizon. Between 0 and 90 degrees.
     */
    public Vector3D(double azimuth, double zenith) {
        double a = azimuth / 180. * Math.PI;
        zenith *= Math.PI / 180.;
        double sinz = Math.sin(zenith);
        x = Math.sin(a) * sinz;
        y = Math.cos(a) * sinz;
        z = Math.cos(zenith);
    }
    
    /**
     * Construct a vector of unary length from an azimuth and a zenith angle.
     * @param azimuthRad From y axis in clockwise direction. In radians.
     * @param zenithRad From z axis towards xy plane. In radians.
     */
    public void direction(double azimuthRad, double zenithRad) {
        double sinz = Math.sin(zenithRad);
        x = Math.sin(azimuthRad) * sinz;
        y = Math.cos(azimuthRad) * sinz;
        z = Math.cos(zenithRad);
    }

    /**
     * Normalize the length of the vector. After calling this method, the vector
     * has a length of 1.
     */
    public void normalize() {
        double length = Math.sqrt(x * x + y * y + z * z);
        x /= length;
        y /= length;
        z /= length;
    }

    /**
     * Normalize the length of a vector. After calling this method, the passed
     * vector has a length of 1.
     * @param v vector to normalize
     * @return passed normalized vector v
     */
    public static Vector3D normalize(Vector3D v) {
        v.normalize();
        return v;
    }

    /**
     * Computes the dot product with another vector.
     *
     * @param v The second vector.
     * @return Returns the scalar product: cos(angle between the vectors).
     */
    public double dotProduct(Vector3D v) {
        return v.x * this.x + v.y * this.y + v.z * this.z;
    }

    /**
     * Compute the dot product of two vectors
     *
     * @param v1 The first vector.
     * @param v2 The second vector.
     * @return The dot product (between 0 and 1)
     */
    public static double dotProduct(Vector3D v1, Vector3D v2) {
        return v1.dotProduct(v2);
    }

    /**
     * Computes a new vector perpendicular to this and a second vector.
     *
     * @param v The second vector.
     * @return A new Vector3D that is perpendicular to the two other vectors.
     */
    public Vector3D vectorProduct(Vector3D v) {
        double vx = y * v.z - z * v.y;
        double vy = z * v.x - x * v.z;
        double vz = x * v.y - y * v.x;
        return new Vector3D(vx, vy, vz);
    }

    /**
     * Adds two vectors together
     *
     * @param v the second vector.
     * @return A new Vector3D that is the sum of the input vectors.
     */
    public Vector3D vectorSum(Vector3D v) {
        double vx = x + v.x;
        double vy = y + v.y;
        double vz = z + v.z;
        return new Vector3D(vx, vy, vz);
    }

    /**
     * Component-wise add of another vector.
     * @param v vector to add
     */
    public void add(Vector3D v) {
        x += v.x;
        y += v.y;
        z += v.z;
    }

    /**
     * Makes the xyz components of a vector accessible from outside this class.
     *
     * @param v the vector whose components we're interested in
     * @return A double array of length 3 containing the x, y, and z components
     * of the vector.
     *
     */
    public static double[] getVectorComponents(Vector3D v) {
        return new double[]{v.x, v.y, v.z};
    }

    /**
     * Compute the vector product of two vectors.
     *
     * @param v1 The first vector.
     * @param v2 The second vector.
     * @return A new vector perpendicular to the plane defined by v1 and v2.
     */
    public static Vector3D vectorProduct(Vector3D v1, Vector3D v2) {
        return v1.vectorProduct(v2);
    }
    
    /**
     * Vector product of two three-dimensional vectors.
     * @param v1x x component of first vector
     * @param v1y y component of first vector
     * @param v1z z component of first vector
     * @param v2x x component of second vector
     * @param v2y y component of second vector
     * @param v2z z component of second vector
     * @param n Vector to store the result.
     */
    public static void vectorProduct(double v1x, double v1y, double v1z, 
            double v2x, double v2y, double v2z, Vector3D n) {
        n.x = v1y * v2z - v1z * v2y;
        n.y = v1z * v2x - v1x * v2z;
        n.z = v1x * v2y - v1y * v2x;
    }
}
