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

////////////////////////////////////////////////////////////////////////////////
//
// FILE:	GCircle.java
//
// AUTHOR:	Manoj D. Muzumdar
//		archon@ai.mit.edu
//
// DATE:	Jun 98
//
// COPYRIGHT:	Massachusetts Institute of Technology
//		All rights reserved.
//
// PURPOSE:	Geometric modeling
//
// HISTORY:     Jun 98  archon  created
//
//              Nov 98  archon
//              Made mutable.
//
////////////////////////////////////////////////////////////////////////////////

package rec.geo;

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

public class GCircle extends GOval
{
  public GCircle( GPoint center, double radius )
  {
    super( center, 0.0, radius, radius );
  }

  public GCircle( GCircle gc )
  {
    super( gc );
  }

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

  /**
   * Return true if the point is _inside_ the circle.
   */
  public boolean contains( GPoint p )
  {
    return( contains( p, 0.0 ) );
  }

  /**
   * Return true if the point is within offset of being _inside_ the circle.
   */
  public boolean contains( GPoint p, double offset )
  {
    return ( Geometry.distance( getCenter(), p )
	     <= ( getRadius() + offset ) );
  }

  public boolean contains( double x, double y, double offset )
  {
    Assert.check( offset >= 0, "need non-negative offset in contains()" );
			
    return ( Geometry.distance( x, y, m_Center.x, m_Center.y )
	     <= ( getRadius() + offset ) );
  }

  public boolean intersects( GSegment s )
  {
    // move the circle to the origin
    double offsetX = -1 * m_Center.x;
    double offsetY = -1 * m_Center.y;

    double x1 = s.getHeadPoint().x + offsetX;
    double x2 = s.getTailPoint().x + offsetX; 
    double y1 = s.getHeadPoint().y + offsetY;
    double y2 = s.getTailPoint().y + offsetY;

    Console.writeln( "EndPoints of segment are: [ " + x1 + ", " + y1 
		     + " ], [ " + x2 + ", " + y2 + " ]");
    Console.writeln( "Radius of circle is: " + getRadius() );

    double radius = getRadius();

    // now handle the case of a vertical line
    if ( s.isSlopeInfinite() ) {
      if ( x1 >= radius ) {
	return false;
      }
      if ( x1 <= radius * -1 ) {
	return false;
      }

      double y_lim = Math.sqrt( (radius * radius) - (x1 * x1) );
      double bigY = y2;
      double littleY = y1;
      // correct our guess, if necesary
      if ( y1 > y2 ) {
	bigY = y1;
	littleY = y2;
      }
      if ( (bigY > y_lim) || (littleY < y_lim) ) {
	return false;
      }
      // otherwise return true
      return true;
    }
    // else slope is not infinite
    else {
      Console.writeln( "Not a vertical segment" );
      double m = s.getSlope();
      double b = y1 - m*x1;
      double x_int1;
      double x_int2;
      double y_int1;
      double y_int2;

      // check to see if there are any real solutions on the whole line
      double discr = (radius*radius) + (radius*radius*m*m) - (b*b);
      if ( discr < 0 ) {
	//	Console.writeln( "Line defined by segment does not intersect circle" );
	return false;
      }
      else {
	x_int1 = ( -2*m*b + Math.sqrt( 4 * discr ) ) / ( 2 + 2*m*m );
	y_int1 = m*x_int1 + b;
	x_int2 = ( -2*m*b - Math.sqrt( 4 * discr ) ) / ( 2 + 2*m*m );
	y_int2 = m*x_int2 + b;

	// move the points back to thier original positions
	x_int1 -= offsetX;
	x_int2 -= offsetX;
	y_int1 -= offsetY;
	y_int2 -= offsetY;
	
	GPoint int1 = new GPoint( x_int1, y_int1 );
	GPoint int2 = new GPoint( x_int2, y_int2 );
	if ( s.onSegment( int1 ) || s.onSegment( int2 ) ) {
	  return true;
	}
      }
      return false;
    }
  }

  public boolean overlaps( GCircle c )
  {
    double x_dif = getCenter().x - c.getCenter().x;
    double y_dif = getCenter().y - c.getCenter().y;
    double dist = Math.sqrt( x_dif*x_dif + y_dif*y_dif );
    if( dist < ( getRadius() + c.getRadius() ) ) {
      return true;
    }
    return false;
  }

  public boolean overlaps( GPolygon p )
  {
      GSegment[] sides = p.getSides();
      for( int i = 0; i < p.getNumberOfSides(); i++ ) {
	if ( intersects( sides[i] ) ) {
	  return true;
	}
      }
      return false;

  }


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

  public GShape copy()
  {
    return( new GCircle( this ) );
  }

  /**
   * Inherits reflect(), translate(), and rotate() from GOval.
   **/

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

  public Rectangle getBounds()
  {
    GPoint p = getCenter();
    double r = getRadius();

    return( new Rectangle( p.getIntX() - (int)Math.floor( r ) - 2,
			   p.getIntY() - (int)Math.floor( r ) - 2,
			   (int)Math.floor( 2.0 * r ) + 2,
			   (int)Math.floor( 2.0 * r ) + 2 ) );
  }

  /**
   * Inherits size() and resize() from GOval.
   **/

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

  /**
   * display() overridden to allow for more efficient drawing.
   */
  public void display( Graphics g )
  {
    int radius = (int)Math.rint( getRadius() );
    GPoint center = getCenter();

    int x = center.getIntX() - radius;
    int y = center.getIntY() - radius;
    int wh = 2 * radius;

    g.drawOval( x, y, wh, wh );
  }

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

  public double getRadius()
  {
    return( getMajorAxis().getLength() / 2.0 );
  }

  public double getDiameter()
  {
    return( getMajorAxis().getLength() );
  }

  public double getCircumference()
  {
    return( Math.PI * getDiameter() );
  }


}

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