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

////////////////////////////////////////////////////////////////////////////////
//
// FILE:	GAngle.java
//
// AUTHOR:	Manoj D. Muzumdar
//		archon@ai.mit.edu
//
// DATE:	06-XX-98
//
// COPYRIGHT:	Massachusetts Institute of Technology
//		All rights reserved.
//
// PURPOSE:	Geometric modeling
//
// HISTORY:     11-15-98 archon
//              Made mutable
//
////////////////////////////////////////////////////////////////////////////////

package rec.geo;

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

public class GAngle extends GShape
{
  public GAngle( GPoint gp1, GPoint mp, GPoint gp2 )
  {
    initialize( gp1, mp, gp2 );
  }

  public GAngle( GSegment gs1, GSegment gs2 )
  {
    if( gs1.getHeadPoint().equals( gs2.getHeadPoint() ) ) {
      initialize( gs1.getTailPoint(),
		  gs1.getHeadPoint(),
		  gs2.getTailPoint() );
      return;
    }

    if( gs1.getHeadPoint().equals( gs2.getTailPoint() ) ) {
      initialize( gs1.getTailPoint(),
		  gs1.getTailPoint(),
		  gs2.getHeadPoint() );
      return;
    }

    if( gs1.getTailPoint().equals( gs2.getHeadPoint() ) ) {
      initialize( gs1.getHeadPoint(),
		  gs1.getTailPoint(),
		  gs2.getTailPoint() );
      return;
    }

    if( gs1.getTailPoint().equals( gs2.getTailPoint() ) ) {
      initialize( gs1.getHeadPoint(),
		  gs1.getTailPoint(),
		  gs2.getHeadPoint() );
      return;
    }

    Assert.error( "segments do not connect" );
  }

  public GAngle( GAngle ga )
  {
    this( ga.m_Point1, ga.m_MiddlePoint, ga.m_Point2 );
  }

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

  private void initialize( GPoint gp1, GPoint mp, GPoint gp2 )
  {
    m_Point1 = new GPoint( gp1 );
    m_MiddlePoint = new GPoint( mp );
    m_Point2 = new GPoint( gp2 );
  }

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

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

  public void reflect()
  {
    m_Point1.reflect();
    m_MiddlePoint.reflect();
    m_Point2.reflect();
  }

  public void translate( double dx, double dy )
  {
    m_Point1.translate( dx, dy );
    m_MiddlePoint.translate( dx, dy );
    m_Point2.translate( dx, dy );
  }

  public void rotate( GPoint gp, double radians )
  {
    m_Point1.rotate( gp, radians );
    m_MiddlePoint.rotate( gp, radians );
    m_Point2.rotate( gp, radians );
  }

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

  public double size()
  {
    // An angle's size is the average of its two lines.
    return( (Geometry.distance( m_Point1, m_MiddlePoint )
             + Geometry.distance( m_MiddlePoint, m_Point2 )) / 2.0 );
  }

  public void resize( double scaleFactor )
  {
    // Scale around the angles's middle point.
    double x1 = scaleFactor * (m_Point1.x - m_MiddlePoint.x) + m_MiddlePoint.x;
    double y1 = scaleFactor * (m_Point1.y - m_MiddlePoint.y) + m_MiddlePoint.y;

    double x2 = scaleFactor * (m_Point2.x - m_MiddlePoint.x) + m_MiddlePoint.x;
    double y2 = scaleFactor * (m_Point2.y - m_MiddlePoint.y) + m_MiddlePoint.y;

    m_Point1 = new GPoint( x1, y1 );
    m_Point2 = new GPoint( x2, y2 );
  }

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

  public Rectangle getBounds()
  {
    int tlx = Math.min( m_Point1.getIntX(),
      Math.min( m_MiddlePoint.getIntX(),
		m_Point2.getIntX() ) );
    int tly = Math.min( m_Point1.getIntY(),
      Math.min( m_MiddlePoint.getIntY(),
		m_Point2.getIntY() ) );

    int brx = Math.max( m_Point1.getIntX(),
      Math.max( m_MiddlePoint.getIntX(),
		m_Point2.getIntX() ) );
    int bry = Math.max( m_Point1.getIntY(),
      Math.max( m_MiddlePoint.getIntY(),
		m_Point2.getIntY() ) );

    return( new Rectangle( tlx - 1, tly - 1,
			   brx - tlx + 2, bry - tly + 2 ) );
  }

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

  public void display( Graphics g )
  {
    g.drawLine( m_Point1.getIntX(), m_Point1.getIntY(),
		m_MiddlePoint.getIntX(), m_MiddlePoint.getIntY() );
    g.drawLine( m_MiddlePoint.getIntX(), m_MiddlePoint.getIntY(),
		m_Point2.getIntX(), m_Point2.getIntY() );
  }

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

  public boolean contains( GPoint gp )
  {
    Assert.error( "Not implemented" );
    return false;
  }
  

  public boolean includes( GPoint gp )
  {
    return(
      (new GSegment( m_Point1, m_MiddlePoint )).includes( gp )
      ||
      (new GSegment( m_MiddlePoint, m_Point2 )).includes( gp ) );
  }

  public boolean includes( GPoint gp, double offset )
  {
    Assert.check( offset >= 0.0, "offset cannot be negative" );

    return( (new GSegment( m_Point1, m_MiddlePoint ))
	    .includes( gp, offset )
	    || (new GSegment( m_MiddlePoint, m_Point2 ))
	    .includes( gp, offset ) );
  }

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

  /**
   * @return  The angle in radians in the range
   *               [0.0, 2.0 * Math.PI);
   *          It sweeps from point one to point two.
   */
  public double getAngle()
  {
    return( Geometry.angleBetween( m_Point1, m_MiddlePoint, m_Point2 ) );
  }

  public GSegment getSegmentContaining( GPoint gp )
  {
    GSegment gs = new GSegment( m_Point1, m_MiddlePoint );
    if( gs.includes( gp ) )
      return( gs );

    gs = new GSegment( m_MiddlePoint, m_Point2 );
    if( gs.includes( gp ) )
      return( gs );

    return( null );
  }

  public GSegment getSegmentContaining( GPoint gp, double offset )
  {
    Assert.check( offset >= 0.0, "offset cannot be negative" );

    GSegment gs = new GSegment( m_Point1, m_MiddlePoint );
    if( gs.includes( gp, offset ) )
      return( gs );

    gs = new GSegment( m_MiddlePoint, m_Point2 );
    if( gs.includes( gp, offset ) )
      return( gs );

    return( null );
  }

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

  private GPoint m_Point1;
  private GPoint m_MiddlePoint;
  private GPoint m_Point2;
}

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