// (c) MIT 2003.  All rights reserved.

////////////////////////////////////////////////////////////////////////////////
//
// FILE:	Geometry.java
//
// AUTHOR:	Manoj D. Muzumdar
//		archon@ai.mit.edu
//
//              Adapted from code written by Luke Weisman
//              luke@ai.mit.edu
//
// DATE:	06-XX-98
//
// COPYRIGHT:	Massachusetts Institute of Technology
//		All rights reserved.
//
// PURPOSE:	Geometric modeling utility library
//
// HISTORY:     11-15-98 archon
//              Incorporated into rec.geo package
//
////////////////////////////////////////////////////////////////////////////////

package rec.geo;

import java.awt.*;
import rec.util.*;

/**
 * Geometry is a library of useful, geometric routines.
 *
 * The graphics coordinate system is equal to (x, -y)
 *      where (x, y) would be the coordinates in a
 *      standard Cartesian coordinate system.
 *
 * The cos and sin routines should be used by other
 *      classes to ensure that the proper adjustments
 *      are made.
 */
public final class Geometry
{
  public static GPoint midpoint( GPoint p1, GPoint p2 )
  {
    return( new GPoint( (p1.x + p2.x) / 2.0, (p1.y + p2.y) / 2.0 ) );
  }

  /**
   * @return  The mathematical angle in radians for a vector
   *               starting at point p1 and going through p2;
   *          Return range is [0.0, 2.0 * Math.PI).
   */
  public static double angle( GPoint p1, GPoint p2 )
  {
    // The y terms have been rotated to account for the
    //      flipped coordinate system.

    return( Radian.reduce( Math.atan2( p1.y - p2.y,
				       p2.x - p1.x ) ) );
  }

  /**
   * @return  The mathematical angle in radians between two
   *               connected vectors going out from the midpoint;
   *          The angle is calculated by going from p1 to p2.
   *          Return range is [0.0, 2.0 * Math.PI).
   */
  public static double angleBetween( GPoint p1, GPoint mp, GPoint p2 )
  {
    double ang1 = angle( mp, p1 );
    double ang2 = angle( mp, p2 );

    if( ang1 <= ang2 )
      return( ang2 - ang1 );

    return( 2.0 * Math.PI + ang2 - ang1 );
  }

  public static double distance( GPoint p1, GPoint p2 )
  {
    return( Math.sqrt( Math.pow( p2.x - p1.x, 2.0 )
		       + Math.pow( p2.y - p1.y, 2.0 ) ) );
  }

  public static double distance( double x1, double y1, double x2, double y2 )
  {
    return( Math.sqrt( Math.pow( x1 - x2, 2.0 )
		       + Math.pow( y1 - y2, 2.0 ) ) );    
  }

  /**
   * @param angle  The angle in radians.
   */
  public static GPoint toRectangular( double magnitude, double angle )
  {
    return( new GPoint( magnitude * Geometry.cos( angle ),
			magnitude * Geometry.sin( angle ) ) );
  }

  /**
   * @param angle  The angle in radians.
   */
  public static GPoint locatePoint( GPoint p, double magnitude, double angle )
  {
    GPoint rect = toRectangular( magnitude, angle );

    return( new GPoint( p.x + rect.x, p.y + rect.y ) );
  }

  //////////////////////////////////////////////////////////////////////////////

  /**
   * @param angle  The angle in radians.
   */
  public static double cos( double angle )
  {
    return( Math.cos( angle ) );
  }

  /**
   * @param angle  The angle in radians.
   */
  public static double sin( double angle )
  {
    return( -1.0 * Math.sin( angle ) );
  }




  public static double delta( double angle1, double angle2 )
  {
    if ( angle1 > angle2 )
      return angle1 - angle2;
    else
      return angle2 - angle1;
  }


  /**
   * @ return angle between p1-p2, and p2-p3 in _degrees_
   *   this is between 0 and 180.
   */
  public static int delta_angle( GPoint p1, GPoint p2, GPoint p3 )
  {
    int a1 = (int)( 360.0 * angle( p1, p2 ) / ( 2 * Math.PI ) );
    int a2 = (int)( 360.0 * angle( p2, p3 ) / ( 2 * Math.PI ) );
    
    Assert.check( a1 <= 360, "bad angle1" );
    Assert.check( a2 <= 360, "bad angle2" );
    Assert.check( a1 >= 0, "bad angle1 (neg)" );
    Assert.check( a1 >= 0, "bad angle2 (neg)" );

    a1 = Math.abs( a1 + 180 - a2 );

    while( a1 >= 360 )
      a1 -= 360;

    if ( a1 > 180 )
      return 360 - a1;
    else
      return a1;
  }


  public static double theta( GPoint l, GPoint l2 )
  {
    double x = l2.x - l.x;
    double y = l2.y - l.y;

    
    //System.err.println( "we got: p = " + Math.atan( 4.0 / -1.0 ) );
    //System.err.println( "we got: n = " + Math.atan( -4.0 / 1.0 ) );
    //    System.err.println( "we got: 1000 = " + Math.atan( 1000.0 ) );

    if ( x == 0 )
      return ( y > 0 ) ? Math.PI / 2 : Math.PI / -2;
    else
      return ( Math.atan( (double)y / (double)x )
	       + ( ( x > 0 ) ? 0.0 : Math.PI ) );
  }

  /**
   * Check if line interesects the given box.
   **/
  public static boolean intersects( GSegment line, Rectangle b ) 
  {
    GSegment l1= new GSegment( b.x, b.y,
			       b.x, b.y + b.height );
    GSegment l2= new GSegment( b.x, b.y + b.height,
			       b.x + b.width, b.y + b.height );
    GSegment l3= new GSegment( b.x + b.width, b.y + b.height,
			       b.x + b.width, b.y );
    GSegment l4= new GSegment( b.x + b.width, b.y,
			       b.x, b.y );
    
    return ( line.getIntersectionOnSegment( l1 ) != null ||
	     line.getIntersectionOnSegment( l2 ) != null ||
	     line.getIntersectionOnSegment( l3 ) != null ||
	     line.getIntersectionOnSegment( l4 ) != null );
	
      

  }
  
    


  
}

////////////////////////////////////////////////////////////////////////////////
