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

////////////////////////////////////////////////////////////////////////////////
//
// FILE:	SolenoidRecognizer.java
//
// AUTHOR:	Manoj D. Muzumdar
//		archon@ai.mit.edu
//
// DATE:	Feb 99
//
// COPYRIGHT:	Massachusetts Institute of Technology
//		All rights reserved.
//
// PURPOSE:	Mechanical engineering drawing recognition
//
// HISTORY:     Feb 99  archon  created
//
////////////////////////////////////////////////////////////////////////////////

package rec.meche;

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

public class SolenoidRecognizer extends Recognizer
{
  /**
   * @return  If the rectangles form a solenoid, returns a solenoid.
   */
  public static WMSolenoid isSolenoid( WRectangle r1, WRectangle r2 )
  {
    // Make sure the rectangles are not squares.

    if( r1 instanceof WSquare || r2 instanceof WSquare )
      return( null );

    GSegment majAxis1 = r1.getMajorAxis();
    GSegment minAxis2 = r2.getMinorAxis();

    // Check the orientation.

    if( !Estimator.arePerpendicular( majAxis1, minAxis2 ) )
      return( null );

    GSegment minAxis1 = r1.getMinorAxis();
    GSegment majAxis2 = r2.getMajorAxis();

    // r1 is the solenoid body and r2 is the solenoid.

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

      // Swap rectangles for the second pass.
      //
      // The WMSolenoid constructor call's average of the sizes of r1 and r2
      //   is always the same (therefore r1 and r2 are not swapped in the
      //   second pass.

      if( i == 1 ) {

	GSegment tmp = majAxis1;
	majAxis1 = majAxis2;
	majAxis2 = tmp;

	tmp = minAxis1;
	minAxis1 = minAxis2;
	minAxis2 = tmp;
      }

      if( minAxis1.getLength() > minAxis2.getLength() ) {

	double len = majAxis1.getLength();

	double dist1 = Geometry.distance( majAxis1.getHeadPoint(),
					  majAxis2.getHeadPoint() );
	double dist2 = Geometry.distance( majAxis1.getTailPoint(),
					  majAxis2.getHeadPoint() );

	boolean insideHead1 = dist1 < len && dist2 < len;

	dist1 = Geometry.distance( majAxis1.getHeadPoint(),
				   majAxis2.getTailPoint() );
	dist2 = Geometry.distance( majAxis1.getTailPoint(),
				   majAxis2.getTailPoint() );

	boolean insideTail1 = dist1 < len && dist2 < len;

	// Make sure the solenoid is within the rectangle.

	len = minAxis1.getLength();

	dist1 = Geometry.distance( minAxis1.getHeadPoint(),
				   majAxis2.getHeadPoint() );
	dist2 = Geometry.distance( minAxis1.getTailPoint(),
				   majAxis2.getHeadPoint() );

	boolean insideHead2 = dist1 < len && dist2 < len;

	dist1 = Geometry.distance( minAxis1.getHeadPoint(),
				   majAxis2.getTailPoint() );
	dist2 = Geometry.distance( minAxis1.getTailPoint(),
				   majAxis2.getTailPoint() );

	boolean insideTail2 = dist1 < len && dist2 < len;

	if( insideHead1 && insideHead2 && !insideTail1 ) {

	  /*
	  GPoint cen1 = majAxis1.getMidpoint();
	  GPoint cen2 = majAxis2.getMidpoint();
	  GPoint cen = new GPoint( (cen1.x + cen2.x) / 2.0,
				   (cen1.y + cen2.y) / 2.0 );
	  */

	  return( new WMSolenoid( majAxis1.getMidpoint(),
				  Geometry.angle( majAxis2.getHeadPoint(),
						  majAxis2.getTailPoint() )
				    + Math.PI / 2.0,
				  (r1.size() + r2.size()) / 2.0,
				  null ) );
	}

	if( !insideHead1 && insideTail1 && insideTail2 ) {

	  /*
	  GPoint cen1 = majAxis1.getMidpoint();
	  GPoint cen2 = majAxis2.getMidpoint();
	  GPoint cen = new GPoint( (cen1.x + cen2.x) / 2.0,
				   (cen1.y + cen2.y) / 2.0 );
	  */

	  return( new WMSolenoid( majAxis1.getMidpoint(),
				  Geometry.angle( majAxis2.getTailPoint(),
						  majAxis2.getHeadPoint() )
				    + Math.PI / 2.0,
				  (r1.size() + r2.size()) / 2.0,
				  null ) );
	}
      }
    }

    return( null );
  }

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

  public SolenoidRecognizer()
  {
  }

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

  public boolean recognize( Surface s, Widget w )
  {
    WRectangle r = (WRectangle)w;

    GeometryCalculator gc = new GeometryCalculator( s );

    Iterator e = gc.getVisibleWidgets().iterator();

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

    while( e.hasNext() ) {

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

      if( !( vw instanceof WRectangle ) )
	continue;

      if( vw.equals( r ) )
	continue;

      WRectangle wr = (WRectangle)vw;

      WMSolenoid ws = null;

      if( (ws = isSolenoid( r, wr )) != null ) {

	s.removeWidget( r );
	s.removeWidget( wr );
	s.addWidget( ws );

	return( true );
      }
    }

    return( false );
  }

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

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

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

    return( v.iterator() );
  }

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

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

    return( v.iterator() );
  }
}

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