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

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

package rec.meche;

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

public class BeltRecognizer extends Recognizer
{
  public static WMBelt isBelt( Squiggle sq )
  {
    GPolySegment ps = sq.getPolygon();

    if( ps == null )
      return( null );

    int num = ps.getNumberOfPoints();

    // Make sure the belt is semi-curvy.

    if( num < 4 )
      return( null );

    // Make sure a loop is formed.

    GPoint prev = ps.getPointAt( 0 );

    if( Geometry.distance( prev, ps.getPointAt( num - 1 ) ) > 10.0 )
      return( null );

    GPoint curr = null;

    // Save an extra space for the last connector (num - 1 to 0).

    GShape[] shapes = new GShape[num];

    for( int i = 1; i < num; ++i ) {
      curr = ps.getPointAt( i );
      shapes[i - 1] = new GSegment( prev, curr );
      prev = curr;
    }

    for( int i = 0; i < num - 2; ++i ) {

      GSegment s1 = (GSegment)shapes[i];

      // Avoid lines next to one another because they shouldn't be able
      //   to intersect with one another (unless there is aliasing).
      //
      // Therefore, incrementing by 2 eliminates aliasing problems and
      //   reduces computation time.

      for( int j = i + 2; j < num - 1; ++j ) {

	GSegment s2 = (GSegment)shapes[j];

	GPoint inter = s1.getSafeIntersection( s2 );

	if( inter == null )
	  continue;

	// Check if there is a loop in this squiggle.

	if( s1.getBounds().contains( inter.getPoint() )
	    && s2.getBounds().contains( inter.getPoint() ) )
	  return( null );
      }
    }

    // Looks like we have a belt.

    shapes[num - 1] = new GSegment( ps.getPointAt( num - 1 ),
				    ps.getPointAt( 0 ) );

    return( new WMBelt( sq.getLocation().getCenter(),
			Math.PI / 2.0,
			sq.size(),
			new CloneableArray( shapes ) ) );
  }

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

  public BeltRecognizer()
  {
  }

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

  public boolean recognize( Surface s, Widget w )
  {
    Squiggle sq = (Squiggle)w;

    WMBelt part = isBelt( sq );

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

    s.removeWidget( w );

    s.addWidget( part );

    return( true );
  }

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

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

    v.addElement( "rec.sys.Squiggle" );

    return( v.iterator() );
  }

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

    v.addElement( "rec.meche.WMBelt" );

    return( v.iterator() );
  }
}

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