//vector.pde by david huebner (2005|05|31)
//modify and use in any way
//markavian (2005|06|01), added clone() method, http://mkv25.net/

public class Vector {

  public static final float TOL = 0.00001f;

  public int N;
  public float x[];

  public Vector(int n) {
    N = n;
    x = new float[N];
    for( int i=0; i<N; i++ )
      x[i] = 0;
  }

  public Vector(float[] xx) {
    N = xx.length;
    x = new float[N]; 
    for( int i=0; i<N; i++ )
      x[i] = xx[i]; 
  }
  
  public Vector(Vector v1) {
    N = v1.x.length;
    x = new float[N]; 
    for( int i=0; i<N; i++ )
      x[i] = v1.x[i]; 
  }

  public Vector(float _x, float _y, float _z) {
    N = 3;
    x = new float[3]; 
    x[0] = _x;
    x[1] = _y;
    x[2] = _z;
  }

  public float magnitude() {
    float sum = 0.0;
    for ( int i=0; i<N; i++ )
      sum = sum + x[i] * x[i];
    return sqrt( sum<TOL?TOL:sum );   
  }

  public float magnitudeSqr() {
    float sum = 0.0;
    for ( int i=0; i<N; i++ )
      sum = sum + x[i] * x[i];
    return sum<TOL?TOL:sum;   
  }
  
  public Vector add(Vector v2) {
    Vector product = new Vector(N);
    if( N == v2.N ) {
      for ( int i=0; i<N; i++ )
        product.x[i] = x[i] + v2.x[i];
      return product;
    }
    else {
      println( "Vectors differ in dimension!" );
      return product;
    }
  }
  
  public void  addMe(Vector v2) {
    if( N == v2.N ) {
      for ( int i=0; i<N; i++ )
        x[i] += v2.x[i];
    }
    else {
      println( "Vectors differ in dimension!" );
    }
  }

  public Vector subtract(Vector v2) {
    Vector product = new Vector(N);
    if( N == v2.N ) {
      for ( int i=0; i<N; i++ )
        product.x[i] = x[i] - v2.x[i];
      return product;
    }
    else {
      println( "Vectors differ in dimension!" );
      return product;
    }
  }

  public void subtractMe(Vector v2) {
    if( N == v2.N ) {
      for ( int i=0; i<N; i++ )
        x[i] -= v2.x[i];
    }
    else {
      println( "Vectors differ in dimension!" );
    }
  }

  public Vector multiply(float n) {
    Vector product = new Vector(N);
    for ( int i=0; i<N; i++ )
      product.x[i] = x[i] * n;
    return product;
  }

  public void multiplyMe(float n) {
    for ( int i=0; i<N; i++ )
      x[i] *= n;
  }
  
  public Vector divide(float n) {
    n = (n < TOL)?TOL:n;
    Vector product = new Vector(N);
    for ( int i=0; i<N; i++ )
      product.x[i] = x[i] / n;
    return product;
  }

  public void divideMe(float n) {
    n = (n < TOL)?TOL:n;
    for ( int i=0; i<N; i++ )
      x[i] /=  n;
  }

  public Vector normalize() {
    return divide(magnitude());
  }
  
  public void normalizeMe() {
    Vector norm = new Vector(divide(magnitude()));
    for ( int i=0; i<N; i++ )
      x[i] = norm.x[i];
  }

  public Vector crossProduct(Vector v2)
  {
    Vector crossProduct = new Vector(3);
    if (N == 3  &&  v2.N == 3) //cross product only defined in R3
    {
      crossProduct.x[0] = x[1] * v2.x[2] - x[2] * v2.x[1];
      crossProduct.x[1] = x[2] * v2.x[0] - x[0] * v2.x[2];
      crossProduct.x[2] = x[0] * v2.x[1] - x[1] * v2.x[0];
      return crossProduct;
    }
    else
      println("Vectors are not both in R3!");
      return crossProduct;
  }
  
  public float dotProduct (Vector v1, Vector v2)
  {
    return v1.x[0] * v2.x[0] + v1.x[1] * v2.x[1] + v1.x[2] * v2.x[2];
  }
  
  public boolean isZero() {
    if ( x[0] == 0  &&  x[1] == 0  &&  x[2] == 0 )
      return true;
    return false;
  }

  public boolean equals(Vector v2) {
    if ( x[0] == v2.x[0]  &&  x[1] == v2.x[1]  &&  x[2] == v2.x[2] )
      return true;
    return false;
  }

  public Vector rotateX(float val) {
    Vector result = this.clone();
    double cosval = cos(val);
    double sinval = sin(val);
    double tmp1 = x[1]*cosval - x[2]*sinval;
    double tmp2 = x[1]*sinval + x[2]*cosval;

    result.x[1] = (float)tmp1;
    result.x[2] = (float)tmp2;

    return result;
  }

  public void rotateMeX(float val) {
    double cosval = cos(val);
    double sinval = sin(val);
    x[1] = (float)(x[1]*cosval - x[2]*sinval);
    x[2] = (float)(x[1]*sinval + x[2]*cosval);
  }

  public Vector rotateY(float val) {
    Vector result = this.clone();
    double cosval = cos(val);
    double sinval = sin(val);
    double tmp1   = x[0]*cosval - x[2]*sinval;
    double tmp2   = x[0]*sinval + x[2]*cosval;

    result.x[0] = (float)tmp1;
    result.x[2] = (float)tmp2;

    return result;
  }

  public void rotateMeY(float val) {
    double cosval = cos(val);
    double sinval = sin(val);
    x[0] = (float)(x[0]*cosval - x[2]*sinval);
    x[2] = (float)(x[0]*sinval + x[2]*cosval);
  }

  public Vector rotateZ(float val) {
    Vector result = this.clone();
    double cosval = cos(val);
    double sinval = sin(val);
    double tmp1   = x[0]*cosval - x[1]*sinval;
    double tmp2   = x[0]*sinval + x[1]*cosval;

    result.x[0] = (float)tmp1;
    result.x[1] = (float)tmp2;

    return result;
  }

  public void rotateMeZ(float val) {
    double cosval = cos(val);
    double sinval = sin(val);
    x[0] = (float)(x[0]*cosval - x[1]*sinval);
    x[1] = (float)(x[0]*sinval + x[1]*cosval);
  }

  public float getX() {
    return x[0];
  }

  public float getY() {
    return x[1];
  }

  public float getZ() {
    return x[2];
  }

  public void setX(float newX) {
    x[0] = newX;
  }

  public void setY(float newY) {
    x[1] = newY;
  }

  public void setZ(float newZ) {
    x[2] = newZ;
  }
  
  public Vector clone() {
    return new Vector(x[0], x[1], x[2]);
  }
} //end of class vector
