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

////////////////////////////////////////////////////////////////////////////////
//
// FILE:	OvalRecognizer.java
//
// AUTHOR:	Manoj D. Muzumdar
//		archon@ai.mit.edu
//
// DATE:	Mar 99
//
// COPYRIGHT:	Massachusetts Institute of Technology
//		All rights reserved.
//
// PURPOSE:	Oval recognition
//
// HISTORY:     Mar 99  archon  created
//
////////////////////////////////////////////////////////////////////////////////

package rec.core.recognizers;

import java.util.*;
import rec.geo.*;
import rec.sys.*;
import rec.core.widgets.*;

public class OvalRecognizer extends Recognizer
{
  public static boolean isOval( WArc a1, WArc a2 )
  {
    return( calculateOval( a1, a2 ) != null );
  }

  public static WOval calculateOval( WArc a1, WArc a2 )
  {
    GArc ga1 = a1.getArc();
    GArc ga2 = a2.getArc();

    double m1 = ga1.getBLength();
    double m2 = ga2.getBLength();

    if( !isSameSize( m1, m2 ) )
      return( null );

    double l1 = ga1.getALength();
    double l2 = ga2.getALength();

    GPoint c1 = ga1.getCenter();
    GPoint c2 = ga2.getCenter();

    double r1 = Geometry.angle( c1, ga1.getMidpoint() );
    double r2 = Geometry.angle( c2, ga2.getMidpoint() );

    if( !isSameAngle( r1, Radian.reduce( r2 + Math.PI ) ) )
      return( null );

    GPoint b1 = ga1.getHead();
    GPoint e1 = ga1.getTail();

    GPoint b2 = ga2.getHead();
    GPoint e2 = ga2.getTail();

    if( Geometry.distance( b1, b2 ) <= 10.0
	&& Geometry.distance( e1, e2 ) <= 10.0 ) {
      return( new WOval(
		new GOval( Geometry.midpoint( c1, c2 ),
			   ga1.getRotation(),
			   (l1 + l2) / 2.0,
			   (m1 + m2) / 2.0 ) ) );
    }

    if( Geometry.distance( b1, e2 ) <= 10.0
	&& Geometry.distance( e1, b2 ) <= 10.0 ) {
      return( new WOval(
		new GOval( Geometry.midpoint( c1, c2 ),
			   ga1.getRotation(),
			   (l1 + l2) / 2.0,
			   (m1 + m2) / 2.0 ) ) );
    }

    return( null );
  }

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

  private static boolean isSameSize( double s1, double s2 )
  {
    double ratio = s1 / s2;

    // Have a 33% tolerance.
    return( ratio >= 3.0 / 4.0 && ratio <= 4.0 / 3.0 );
  }

  private static boolean isSameAngle( double angle1, double angle2 )
  {
    double rad = -1.0;

    if( angle1 <= angle2 )
      rad = angle2 - angle1;
    else
      rad = 2.0 * Math.PI + angle2 - angle1;

    // Get the acute angle.
    if( rad > Math.PI )
      rad = 2.0 * Math.PI - rad;

    // Have a 22.5 degree tolerance.
    return( rad >= Math.PI / -8.0 && rad <= Math.PI / 8.0 );
  }

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

  public OvalRecognizer()
  {
  }

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

  public boolean recognize( Surface s, Widget w )
  {
    WArc a = (WArc)w;

    Iterator iter = s.getWidgetsIterator();

    if( iter == null )
      return( false );

    while( iter.hasNext() ) {

      Widget vw = (Widget)iter.next();

      if( vw instanceof WArc && !vw.equals( a ) ) {

	WOval o = calculateOval( a, (WArc)vw );

	if( o != null ) {

	  s.removeWidget( a );
	  s.removeWidget( vw );

	  s.addWidget( o );

	  return( true );
	}
      }
    }

    return( false );
  }

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

  public Iterator getInputTypes()
  {
    Vector v = new Vector();

    v.addElement( "rec.core.widgets.WArc" );

    return( v.iterator() );
  }

  public Iterator getOutputTypes()
  {
    Vector v = new Vector();

    v.addElement( "rec.core.widgets.WOval" );

    return( v.iterator() );
  }
}

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