 /*
 * Selim Mimaroglu
 * 
 * CS680 
 * Object-Oriented Programming
 * "Marble Applet"
 *
 * File: ~smimarog/cs680/p3/MotionUtils.java
 * 
 * This class is copied from Steve Revilak's pool Applet work
 */

import java.awt.Point;

/**
 * MotionUtils is a class with a number of static methods for findings distances
 * and direction.
 *
 * All distances are given in absolute values
 *
 * all directions are given in radians where 0 <= r <= 2pi.
 *
 * in terms of the screen, direction looks like this
 * <pre>
 *
 *       pi/2
 *        |
 *   pi --x-- 0
 *        |
 *      3pi/2
 * </pre>
 *
 * 
 * 
 */
public class MotionUtils {

  /**
   * Return the distance between the given pair of coordinates.
   */
  public static double distance(double x1, double y1, double x2, double y2) {
    return Math.sqrt((y2 - y1) * (y2 - y1) + (x2 - x1) * (x2 - x1));
  }

  /**
   * Return the distance between the given pair of points.
   */
  public static double distance(Point a, Point b) {
    return distance(a.x, a.y, b.x, b.y);
  }

  /**
   * Return the distance between the given pair of HiRes points.
   */
  public static double distance(HiResPoint a, HiResPoint b) {
    return distance(a.x, a.y, b.x, b.y);
  }

  /**
   * Return the direction from point a to point b.
   *
   * As is taken as the origin of travel, so
   *
   * <pre>
   *    a --> b  gives 0 radians
   * and
   *    b <-- a  gives PI radians
   * </pre>
   */
  public static double direction(Point a, Point b) {
    return direction(a.x, a.y, b.x, b.y);
  }

  /**
   * Return the direction between the given pair of HiResPoints
   */
  public static double direction(HiResPoint a, HiResPoint b) {
    return direction(a.x, a.y, b.x, b.y);
  }

  /**
   * Performs the same function as <tt>direction(Point, Point)</tt>
   * but the arguments are integer coordinates.
   */
  public static double direction(double x1, double y1,
				 double x2, double y2) {

    /* Start of by finding direction as a first quadrant angle */

    /* Gosh ... it's Calculus ! */
    double deltay  = Math.abs(y1 - y2);
    double dist = distance(x1, y1, x2, y2);
    double sinTheta = ((double) deltay) / dist;
    double angle    = Math.asin(sinTheta);

    /* Then translate it into an absolute direction */

    if (x1 <= x2 && y1 >= y2) {
      /* quadrant one */
      return angle;
    }
    else if (x1 > x2 && y1 >= y2) {
      /* quadrant 2. angle is how far we are elevated from PI radians */
      return Math.PI - angle;
    }
    else if (x1 >= x2 && y1 < y2) {
      /* quadrant 3.  Angle is elevation below PI */
      return Math.PI + angle;
    }
    else if (x1 < x2 && y1 < y2) {
      /* quadrant 4.  Angle is elevation below 0 */
      return (2.0 * Math.PI) - angle;
    }

    /* perhaps the same point */
    return 0.0;
  }


  /**
   * Given a magnitude and a direction, return a Point whose x and
   * y members are the deltas that would result from moving a distance of
   * <tt>magnitude</tt> in the given <tt>direction</tt>.
   */
  public static HiResPoint move(double magnitude, double direction) {
    double deltax = magnitude * Math.cos(direction);
    double deltay = magnitude * Math.sin(direction);

    /* Now, we have to translate between the way trigonometry thinks
     * of direction, and they way windowing systems think of direction
     */
    int xcase = 0;
    if (direction > 0.0 && direction <= (Math.PI/2)) {
      deltay = -deltay;
      xcase = 1;
    }
    else if (direction > (Math.PI / 2) && direction < Math.PI) {
      deltay = -deltay;
      xcase = 2;
    }
    else if (direction > Math.PI && direction < (3.0/2.0 * Math.PI)) {
      deltay = -deltay;
      xcase = 3;
    }
    else if (direction >= (3.0/2.0 * Math.PI)) {
      deltay = -deltay;
      xcase = 4;
    }

    HiResPoint p = new HiResPoint(deltax, deltay);
    return p;
  }


  /**
   * Given a direction, return the opposite direction (eg - PI radians
   * the other way).
   */
  public static double oppositeDirection(double direction) {
    if (direction < Math.PI) {
      return direction + Math.PI;
    }
    return direction - Math.PI;
  }


  /**
   * Convert radians to degrees.
   *
   * In JDK1.3, we have Math.toDegrees.  However, in JDK1.1, we don't.
   * It's easy enough to say r/PI * 180, so we will.
   *
   * @param rad an angle in radians
   * @return the angle in degrees
   */
  public static double radiansToDegrees(double rad) {
    return (rad / Math.PI) * 180.0;
  }

  /**
   * Given two magnitudes and two direction, return their resultant.
   */
  public static VectorQuantity resultant(double mag1,
					 double d1,
					 double mag2,
					 double d2) {

    VectorQuantity resultnt = new VectorQuantity();

    /* make our starting coordinates something that cant be negative */
    HiResPoint start = new HiResPoint( mag1 + mag2 + 2,
				       mag1 + mag2 + 2);
    HiResPoint tip = new HiResPoint(start);

    HiResPoint step  = MotionUtils.move(mag1, d1);
    tip.x += step.x;
    tip.y += step.y;

    /* now add the second vector */
    step = MotionUtils.move(mag2, d2);
    tip.x += step.x;
    tip.y += step.y;

    /* Special case, make sure truncation didn't do us in */
    if (tip.x == start.x && tip.y == start.y) {
      return resultnt;
    }

    /* now, find the relative magnitude and direction between tip and start */
    resultnt.magnitude = MotionUtils.distance(start, tip);
    resultnt.direction = MotionUtils.direction(start, tip);
    return resultnt;
  }

}

