package org.waltermilner;

/**
 * WMVector represents a 3rd order vector.
 * As of 13 October 2008 - all floating point quantites are doubles not floats
 * @author Walter Milner
 */
public class WMVector {

    private double x,  y,  z;
    private static final double RAD_TO_DEG = 180.0/Math.PI;
    /** the sines and cosines of an angle to rotate */
    static final double C=Math.cos(0.01); // 0.5 degree
    static final double S=Math.sin(0.01);

   




    public double getX() {
        return x;
    }

    public double getY() {
        return y;
    }

    public double getZ() {
        return z;
    }

    public void show(String string) {
        System.out.println(string + ": x=" + x + " y=" + y + " z=" + z);
    }

    public WMVector(double x, double y, double z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public static double[] normal(double x4, double y4, double z4, double x1, double y1, double z1, double x2, double y2, double z2) {
        WMVector one = new WMVector(x4, y4, z4);
        WMVector two = new WMVector(x1, y1, z1);
        WMVector three = new WMVector(x2, y2, z2);
        WMVector diff = one.sub(two);
        WMVector n = diff.cross(three.sub(two));
        n = n.normalise();
        double[] f = {n.x, n.y, n.z};
        return f;

    }

    public void setX(double x)
    {
        this.x=x;
    }
    public void setY(double y)
    {
        this.y=y;
    }
    public void setZ(double z)
    {
        this.z=z;
    }

    /**
     * Find the angle between this vector and another.
     * 
     * @param other The other vector
     * @return The angle between them, in radians, in the range 0 to ?
     */
    public double angleWith(WMVector other) {
        //if ( Math.abs(x-other.x)<1e-3 && Math.abs(y-other.y)<1e-3 && Math.abs(z-other.z)<1e-3)
        // return 0;
        //angle = atan2(norm(cross(a,b)),dot(a,b));
        double angle = Math.atan2(this.cross(other).getMag(), this.dot(other));
        return angle;

        /* was..
        WMVector thisNorm = normalise();
        WMVector otherNorm = other.normalise();
        double cos = thisNorm.dot(otherNorm);
        return Math.acos(cos); */
    }
    
/**
     * Find the angle between this vector and another.
     * 
     * @param other The other vector
     * @return The angle between them, in degrees, from 0 to 180
     */
    public double degAngleWith(WMVector other) {
        WMVector thisNorm = normalise();
        WMVector otherNorm = other.normalise();
        double cos = thisNorm.dot(otherNorm);
        return RAD_TO_DEG*Math.acos(cos);
    }
    
    /**
     * The dot product.
     * Return the dot product between this vector and another
     * @param other The other vector
     * @return The dot product
     */
    public double dot(WMVector other) {
        return (x * other.x + y * other.y + z * other.z);
    }



    public WMVector times(double scalar) {
        WMVector temp = new WMVector(x * scalar, y * scalar, z * scalar);
        return temp;
    }

    public WMVector cross(WMVector b) {
        WMVector temp = new WMVector(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x);
        return temp;
    }

    public WMVector add(WMVector other) {
        WMVector temp = new WMVector(x + other.x, y + other.y, z + other.z);
        return temp;
    }

    public WMVector sub(WMVector other) {
        WMVector temp = new WMVector(x - other.x, y - other.y, z - other.z);
        return temp;
    }

    public double getMag() {
        double mag = x * x + y * y + z * z;
        mag = Math.sqrt(mag);
        return mag;
    }

    public WMVector setMag(double m) {
        double mag = getMag();
        WMVector temp = new WMVector(x, y, z);
        temp = temp.normalise();
        temp = temp.times(mag);
        return temp;
    }

    public boolean isZero()
    {
        if (x==0.0 && y==0.0 && z==0.0)
            return true;
        else return false;

    }
    public WMVector normalise() {
        double mag = x * x + y * y + z * z;
        mag = Math.sqrt(mag);
        WMVector temp = new WMVector(x / mag, y / mag, z / mag);
        return temp;
    }
    /** rotate this vector around another
     *
     * @param angle Angle in radians to rotate
     * @param axis A WMVector of the axis to rotate around
     * @return
     */
    public WMVector rotate(double angle, WMVector axis)
    {
        /** See Hearn and Baker p.419
         *
         */
        Quaternion p = new Quaternion(0,this.x, this.y, this.z);
        Quaternion rot = new Quaternion(angle, axis);
        Quaternion  c = rot.times(p.times(rot.inverse()));
        return c.getVector();
    }
}

