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

////////////////////////////////////////////////////////////////////////////////
//
// FILE:	ScrewRecognizer.java
//
// AUTHOR:	Manoj D. Muzumdar
//		archon@ai.mit.edu
//
// DATE:	Jul 98
//
// COPYRIGHT:	Massachusetts Institute of Technology
//		All rights reserved.
//
// PURPOSE:	Mechanical engineering drawing recognition
//
// HISTORY:     Jul 98  archon  created
//
//              Dec 98  archon
//              integrated new rec.geo changes
//
//              Feb 99  archon
//              changed to relative recognition
//
//              Mar 99  archon
//              integrated Estimator stuff
//
////////////////////////////////////////////////////////////////////////////////

package rec.meche;

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

public class ScrewRecognizer extends Recognizer
{
  /**
   * @return  If the rectangles form a screw, returns a screw.
   */
  public static WMScrew isScrew( WRectangle r1, WRectangle r2 )
  {
    // Make sure the rectangles are not squares.

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

    // Make sure that the rectangles are perpendicular.

    GSegment head = r1.getMajorAxis();
    GSegment tail = r2.getMajorAxis();

    GPoint intersection = head.getIntersection( tail );

    if( intersection == null )
      return( null );

    GPoint p1 = head.getHeadPoint();
    GPoint p2 = tail.getHeadPoint();

    /*
    Console.writeln( "point 1: (" + p1.getIntX() + ", "
		     + p1.getIntY() + ")" );
    Console.writeln( "intersection: (" + intersection.getIntX()
		     + ", " + intersection.getIntY() + ")" );
    Console.writeln( "point 2: (" + p2.getIntX() + ", "
		     + p2.getIntY() + ")" );
    */

    if( intersection.equals( p1 ) )
      p1 = head.getTailPoint();

    if( intersection.equals( p2 ) )
      p2 = tail.getTailPoint();

    double ang = Geometry.angleBetween(
      p1, intersection, p2 );

    //Console.writeln( "angle between rectangles: " + ang );

    if( !( (ang >= Radian.scaleFromDegrees( 70.0 )
	    && ang <= Radian.scaleFromDegrees( 110.0 ))
	   ||
	   (ang >= Radian.scaleFromDegrees( 250.0 )
	    && ang <= Radian.scaleFromDegrees( 290.0 )) ) )
      return( null );

    // If the rectangle form a screw, find out the screw's orientation.

    head = r1.getMinorAxis();

    // Tail is still r2.getMajorAxis().

    GPoint middlePoint = null;

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

      /*
      Console.writeln( "tail midpoint: ( "
		       + tail.getMidpoint().getIntX() + ", "
		       + tail.getMidpoint().getIntY() + " )" );
      Console.writeln( "head midpoint: ( "
		       + head.getMidpoint().getIntX() + ", "
		       + head.getMidpoint().getIntY() + " )" );
      */
      if( (middlePoint
	   = Estimator.connects( head.getHeadPoint(),
				 tail.getHeadPoint() )) != null ) {
	/*
	Console.writeln( "screw angle: " +
	  Geometry.angle( tail.getMidpoint(), head.getMidpoint() ) );
	*/
	return( new WMScrew(
	  tail.getMidpoint(),
	  Geometry.angle( tail.getMidpoint(), head.getMidpoint() ),
	  tail.size(),
	  null ) );
      }

      if( (middlePoint
	   = Estimator.connects( head.getHeadPoint(),
				 tail.getTailPoint() )) != null ) {
	/*
	Console.writeln( "screw angle: " +
	  Geometry.angle( tail.getMidpoint(), head.getMidpoint() ) );
	*/
	return( new WMScrew(
	  tail.getMidpoint(),
	  Geometry.angle( tail.getMidpoint(), head.getMidpoint() ),
	  tail.size(),
	  null ) );
      }

      if( (middlePoint
	   = Estimator.connects( head.getTailPoint(),
				 tail.getHeadPoint() )) != null ) {
	/*
	Console.writeln( "screw angle: " +
	  Geometry.angle( tail.getMidpoint(), head.getMidpoint() ) );
	*/
	return( new WMScrew(
          tail.getMidpoint(),
	  Geometry.angle( tail.getMidpoint(), head.getMidpoint() ),
	  tail.size(),
	  null ) );
      }

      if( (middlePoint
	   = Estimator.connects( head.getTailPoint(),
				 tail.getTailPoint() )) != null ) {
	/*
	Console.writeln( "screw angle: " +
	   Geometry.angle( tail.getMidpoint(), head.getMidpoint() ) );
	*/
	return( new WMScrew(
	  tail.getMidpoint(),
	  Geometry.angle( tail.getMidpoint(), head.getMidpoint() ),
	  tail.size(),
	  null ) );
      }

      head = r2.getMinorAxis();
      tail = r1.getMajorAxis();
    }

    return( null );
  }

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

  public ScrewRecognizer()
  {
  }

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

  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;

      WMScrew ws = null;

      if( (ws = isScrew( 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.WMScrew" );

    return( v.iterator() );
  }
}

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