package yi.iu.processor.ort;

public final class GeometricUtil
{
    final static boolean VERBOSE=false;

    public static boolean getInterceptPoint(ORTLine Line1, ORTLine Line2, ORTPoint intercept)
    {
        if ( Math.abs(Line1.Theta - Line2.Theta) < 0.001 ||
            Math.abs(Line1.Theta - Line2.Theta) > (Math.PI - 0.001) ) {
            return false;
        }

        /*
        Check to make sure the position of the Junction point is not ambiguous
        due to variances.
        */

        double dtheta = Math.abs(Line1.Theta - Line2.Theta);
        if (dtheta > PIBY2)		/* Acute angle is what we want */
            dtheta = Math.PI - dtheta;

        if (dtheta <= (Line1.ThetaVar + Line2.ThetaVar) &&
                    (Line1.ThetaVar + Line2.ThetaVar) != 0.0) {
            return false;
        }

        /*
        First calculate the row intercepts of the lines.

        */

        double Line1RowIntercept= GeometricUtil.LineSegRowIntercept(Line1.Start.Col,
                                Line1.Start.Row,
                                Line1.End.Col,
                                Line1.End.Row);

        double Line2RowIntercept=GeometricUtil.LineSegRowIntercept(Line2.Start.Col,
                                Line2.Start.Row,
                                Line2.End.Col,
                                Line2.End.Row);

        /*
        Now if one of the lines is vertical, then its real easy to find the
        Junction point.

        */

        if (GeometricUtil.MACCompareWithPIBY2(Line1.Theta)) {	/* 1st line is vertical */
                intercept.Col = Line1.Start.Col;		/* Find the Junction point */
            intercept.Row = Math.tan(Line2.Theta)*Line1.Start.Col + Line2RowIntercept;
        }

        if (GeometricUtil.MACCompareWithPIBY2(Line2.Theta)) {	/* 2nd line is vertical */
                intercept.Col = Line2.Start.Col;		/* Find the Junction point */
            intercept.Row = Math.tan(Line1.Theta)*Line2.Start.Col + Line1RowIntercept;
        }

        /*
        Otherwise we have to really work it all out...

        */

        if (!GeometricUtil.MACCompareWithPIBY2(Line1.Theta) &&
                !GeometricUtil.MACCompareWithPIBY2(Line2.Theta)) {
                intercept.Col = (Line2RowIntercept - Line1RowIntercept)/
                                    (Math.tan(Line1.Theta) - Math.tan(Line2.Theta));
                intercept.Row = Math.tan(Line1.Theta)*(intercept.Col) + Line1RowIntercept;
        }
        return true;

    }



    public static double LineLength(double StartCol,
                    double StartRow,
                    double EndCol,
                    double EndRow)
    {

        double dx,dy;

        double Length;

        if (VERBOSE) {
                System.err.println(" Start of function LineLength \n");
        }

        dx = EndCol - StartCol;
        dy = EndRow - StartRow;

        Length = hypot( dx , dy );

        return(Length);

    }

    static double HUGE= 999999999.0;

    //This function finds the Intercept point of a line with the x axis, given
    //any two points that lie along the line.
    public static double LineSegColIntercept(double StartCol,double StartRow,double EndCol,double EndRow)
    {
        double ColIntercept;

        if (VERBOSE) {
                System.err.println(" Start of function LineSegColIntercept \n");
        }

        if (EndCol == StartCol) { 	/* Line is vertical */
            ColIntercept = StartCol;
            return ColIntercept;
        }

        if ( EndRow != StartRow) {	/* Line is not horizontal */
            ColIntercept = (StartCol*EndRow - EndCol*StartRow)/
                                (EndRow - StartRow);
            return ColIntercept;
        }

        ColIntercept = HUGE;     /* Line is horizontal */
        return ColIntercept;

    }

    public static double LineSegColIntercept(double x,double y, double Theta)
    {
        double XIntercept;

        if (Theta > 0.001 && Theta < (Math.PI-0.001)) {/* Line is not horizontal */
            XIntercept = x-y/Math.tan(Theta);
                return XIntercept;
        }

        XIntercept = HUGE;	/* Line is vertical */
        return XIntercept;

    }


    //This function finds the intercept point of a line with the y axis, given
    //any two points that lie along the line.
    public static double LineSegRowIntercept(double StartCol,
                        double StartRow,
                        double EndCol,
                        double EndRow)

    {
        double RowIntercept;

        if (VERBOSE) {
            System.err.println(" Start of function LineSegRowIntercept \n");
        }

        if (EndCol != StartCol) {	/* Line is not vertical */
            RowIntercept = (EndCol*StartRow - StartCol*EndRow)/
                                    (EndCol - StartCol);
            return RowIntercept;
        }

        RowIntercept = HUGE;	/* Line is vertical */
        return RowIntercept;

    }

    public static double LineSegTheta(double StartCol,
                        double StartRow,
                        double EndCol,
                        double EndRow)
    {

        double dx,dy; 			/* Buffers */

        double Orientation;

        if (VERBOSE) {
                System.err.println(" Start of function LineSegTheta \n");
        }

        dx = EndCol - StartCol;
        dy = EndRow - StartRow;

        if (dx == 0.0) return(PIBY2); 	/* Line is vertical */
        if (dy == 0.0) return(0.0);     	/* Line is Horizontal */

        Orientation =  Math.atan2( dy, dx );

        /*
        Make it so line orientation is always between 0 and pi.

        */

        if (Orientation < 0.0 )
            Orientation = Orientation + Math.PI;

        if (Orientation > Math.PI)
            Orientation = Orientation - Math.PI;

        return(Orientation);

    }



    //This function finds the intercept point of a line with the y axis, given
    //any point on the line (x,y) and the angel of that line (Theta)
    public static double LineSegRowIntercept(double x,double y, double Theta)

    {
        double YIntercept;

        if (!MACCompareWithPIBY2(Theta)) {	/* Line is not vertical */
            YIntercept = y-Math.tan(Theta)*x;
                return YIntercept;
        }

        YIntercept = HUGE;	/* Line is vertical */
        return YIntercept;

    }


    public static double[]  PtLinePerpIntercept(
        ORTPoint p, ORTLine l)
    {
        double lColIntercept=GeometricUtil.LineSegColIntercept(
                 l.Start.Col, l.Start.Row, l.End.Col, l.End.Row);

        double lRowIntercept=GeometricUtil.LineSegRowIntercept(
                 l.Start.Col, l.Start.Row, l.End.Col, l.End.Row);

        double lTheta= ORTLine.LineSegTheta(
                 l.Start.Col, l.Start.Row, l.End.Col, l.End.Row);
        return PtLinePerpIntercept (
            p.Col, p.Row, lColIntercept, lRowIntercept, lTheta);


        }

    public static double[]  PtLinePerpIntercept(double PtCol,
                            double PtRow,
                            double ColIntercept,
                            double RowIntercept,
                            double Theta)
    {
        double InterceptPtCol=0;
        double InterceptPtRow=0;


        double Grad;
        double GradSquared;
        double OnePlusGradSquared;

        if (VERBOSE) {
                System.err.println(" Start of function PtLinePerpIntercept \n");
        }


                if (MACCompareWithPIBY2(Theta)) { /* Line is vertical */
                        InterceptPtRow = PtRow;
                        InterceptPtCol = ColIntercept;
                }

                if (Theta < 0.001 || Theta > (Math.PI-0.001)) { /*Line is horizontal */
                        InterceptPtRow = RowIntercept;
                        InterceptPtCol = PtCol;
                }

        /*
        Otherwise we have to work things out...

        */
            if (!MACCompareWithPIBY2(Theta) && Theta >= 0.001 && Theta <= (Math.PI-0.001)) {

            Grad = Math.tan(Theta);
            GradSquared = Grad*Grad;
            OnePlusGradSquared = 1.0 + GradSquared;

                InterceptPtRow = (RowIntercept  /(OnePlusGradSquared)) +
                        (Grad*PtCol/(OnePlusGradSquared)) +
                        (GradSquared*PtRow/(OnePlusGradSquared));

                InterceptPtCol = (InterceptPtRow - RowIntercept)/Grad;
            }

                double[] rst= {InterceptPtCol,InterceptPtRow};
                return rst;

    }

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Name : PtLinePerpDistance
Type : double
Written on   : 20-Nov-90     By : A. Etemadi
Modified on  :               By :
Directory    : ~atae/ORT/C/src/LineRoutines

==============================================================================

Input parameters    :

Point Data:

 PtCol			-- Column position of point
 PtRow			-- Column position of point

Line data:

 ColIntercept		-- Intercepts of the line
 RowIntercept		--
 Theta			-- Orientation of the line

Output parameters    :

Output result       :

 Distance		-- Perpendicular distance between the point and the line

Called functions :

 PtLinePerpIntercept
 LineLength

Calling procedure:

 double PtCol;
 double PtRow;

 double ColIntercept;
 double RowIntercept;
 double Theta;

 double Distance;

 distance = PtLinePerpDistance(PtCol,
                                   PtRow,
                                   ColIntercept,
                                   RowIntercept,
                                   Theta);

Functionality:

This function finds the perpendicular distance from a point to a line.

----------------------------------------------------------------------------*/

    public static double PtLinePerpDistance(double PtCol,
                             double PtRow,
                            double ColIntercept,
                            double RowIntercept,
                            double Theta)


    {

    double InterceptPtCol;
    double InterceptPtRow;

    double Distance;

    if (VERBOSE)
            System.err.print(" Start of function PtLinePerpDistance \n");


    /*
    Find the intercept point of perpendicular dropped onto the line

    */

    double[] tmp=PtLinePerpIntercept(PtCol,
                        PtRow,
                        ColIntercept,
                        RowIntercept,
                        Theta);
                        InterceptPtCol=tmp[0];
                        InterceptPtRow=tmp[1];


        Distance = LineLength(InterceptPtCol,InterceptPtRow,
                                PtCol,PtRow);
        return(Distance);



    }

    public static double PtLinePerpDistance(ORTPoint p, ORTPoint lp1, ORTPoint lp2)
        {
        double lColIntercept=GeometricUtil.LineSegColIntercept(
                 lp1.Col, lp1.Row, lp2.Col, lp2.Row);

        double lRowIntercept=GeometricUtil.LineSegRowIntercept(
                 lp1.Col, lp1.Row, lp2.Col, lp2.Row);

        double lTheta= ORTLine.LineSegTheta(
                 lp1.Col, lp1.Row, lp2.Col, lp2.Row);

        //calculate the distance from the near end point to the virtual line
        double d = GeometricUtil.PtLinePerpDistance(
                p.Col, p.Row, lColIntercept, lRowIntercept, lTheta);

            return d;
        }

    //calculate the distance from p to the line which cross the point lp1 and whose angle is theta
    public static double PtLinePerpDistance(ORTPoint p, ORTPoint lp1, double theta)
        {
        double lColIntercept=GeometricUtil.LineSegColIntercept(
                 lp1.Col, lp1.Row, theta);

        double lRowIntercept=GeometricUtil.LineSegRowIntercept(
                 lp1.Col, lp1.Row, theta);

        //calculate the distance from the near end point to the virtual line
        double d = GeometricUtil.PtLinePerpDistance(
                p.Col, p.Row, lColIntercept, lRowIntercept, theta);

            return d;
        }

    static double SIXPI= Math.PI*6;     //18.84956

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Name : LineSegThetaVar
Type : double
Written on   : 20-Nov-90     By : A. Etemadi
Modified on  :               By :
Directory    : ~atae/ORT/ORT/LPEG/src/LineRoutines

==============================================================================

Input parameters    :

 Length		-- The Length of the line in pixels

Output result       :

 ThetaVar		-- The variance in the angle of the line
 pi/2			-- When length is <= 3


Calling procedure:

 double Length;

 Variance_in_angle_of_my_bloody_line = LineSegThetaVar( Length);

Functionality:

This function finds the variance in the oirntation of a line given its
length. We are using the function derived experimentally by Graham Jones.

        Variance = 6pi/Length_of_Line_squared   (in radians)

The variance is pi/2 degrees when the length is < 3

----------------------------------------------------------------------------*/


    static double LineSegThetaVar(double Length)
    {


        double ThetaVar;

        if (VERBOSE) {
                System.err.println(" Start of function LineSegThetaVar \n");
        }

        if (Length <= 3.0 && VERBOSE) {
                System.err.println(" Warning line has maximum variance \n");
        }

        ThetaVar = ( SIXPI)/(Length*Length);
        return(ThetaVar);

    }

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    Name : LineLengthPerpVar
    Type : double
    Written on   : 20-Nov-90     By : A. Etemadi
    Modified on  :               By :
    Directory    : ~atae/ORT/ORT/LPEG/src/LineRoutines

    ==============================================================================

    Input parameters    :

    Output result       :

    LengthPerpVar	-- The variance in the length of the line

    Calling procedure:

    double Length;

    Variance_in_length_of_my_bloody_line = LineLengthPerpVar();

    Functionality:

    At the moment I am just using an empirical mean value of 4 pixels for the
    variance in the length of the line. Maybe replaced by some other function
    someday.

    ----------------------------------------------------------------------------*/


    static double LineLengthPerpVar()
    {
            return(2.0);
    }

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    Name : LineLengthParlVar
    Type : double
    Written on   : 20-Nov-90     By : A. Etemadi
    Modified on  :               By :
    Directory    : ~atae/ORT/ORT/LPEG/src/LineRoutines

    ==============================================================================

    Input parameters    :

    Output result       :

    LengthParlVar	-- The variance in the length of the line

    Calling procedure:

    double Length;

    Variance_in_length_of_my_bloody_line = LineLengthParlVar();

    Functionality:

    At the moment I am just using an empirical mean value of 4 pixels for the
    variance in the length of the line. Maybe replaced by some other function
    someday.

    ----------------------------------------------------------------------------*/

    static double LineLengthParlVar()

    {
            return(2.0);
    }

    static double hypot(double x, double y)
    {
        return Math.sqrt(x*x+y*y);
    }

    static double PIBY2= Math.PI/2;     //1.570796

    public static boolean MACCompareWithPIBY2(double a) { return (a > (PIBY2-0.001) && a < (PIBY2+0.001) );}

    /**
     * Return the distance of two ORTPoints
     * @param p1
     * @param p2
     * @return
     */
    public static double distance(ORTPoint p1, ORTPoint p2) {
      double dx=p1.Col-p2.Col, dy=p1.Row-p2.Row;
      return Math.sqrt(dx*dx+dy*dy);
    }

}