package yi.iu.processor.ort;

import yi.iu.processor.*;
import yi.iu.ui.DisplayFrame;
import yi.iu.processor.canny.*;
import java.awt.*;
import java.util.Vector;

public class Fex extends yi.iu.processor.ImageProcessor
{
    int MaxJumpPixels=2;    //used by in StringToSegments.  The difference in the number of pixels leads to a-symetry

    //canny edge detector arguments
    double sigma= 1;        // Standard deviation of the gaussian kernel.
    double tlow= 0.5;       // Fraction of the high threshold in hysteresis.
    double thigh= 50;      // High hysteresis threshold control. The actual
    double MINLENGTH= 5;

    public int getArguNum() { return 5;}

    public void setArgument(int i, String st)
    {
        if (st.length()==0) return;
        if (i==0) sigma= Double.valueOf(st).doubleValue();
        else if (i==1) tlow= Double.valueOf(st).doubleValue();
        else if (i==2) thigh= Double.valueOf(st).doubleValue();
        else if (i==3) MINLENGTH= Double.valueOf(st).doubleValue();
        else if (i==4) MaxJumpPixels= Integer.valueOf(st).intValue();
    }

    public String getArgument(int i)
    {
        if (i==0) return (new Double(sigma)).toString();
        else if (i==1) return (new Double(tlow)).toString();
        else if (i==2) return (new Double(thigh)).toString();
        else if (i==3) return (new Double(MINLENGTH)).toString();
        else if (i==4) return (new Integer(MaxJumpPixels)).toString();
        return null;
    }

    public String getArguName(int i)
    {
        if (i==0) return "canny_sigma";
        else if (i==1) return "canny_low";
        else if (i==2) return "canny_high";
        else if (i==3) return "min_length";
        else if (i==4) return "max_jump";
        return null;
    }

    public Fex() {super();}

    public Fex(double s, double l, double h) {
        sigma=s; tlow=l; thigh=h;
    }

    public Fex(double s, double l, double h, double minl, int jump) {
        sigma=s; tlow=l; thigh=h; MINLENGTH=minl; MaxJumpPixels=jump;
    }

    public int[] getProcessed(int source[], Dimension d)
    {
        // This method is derived from class yi.iu.processor.ImageProcessor
        // to do: code goes here
        return null;
    }

    public int[] getProcessedPseudoColor(int source[], Dimension d)
    {

        Vector[] segs= fex(source, d);
        Vector ID_ListORTLine= segs[0];
        Vector ID_ListORTCircularArc= segs[1];

        char[][] label= DrawUtil.ort2image( d, ID_ListORTLine,  ID_ListORTCircularArc);
        int[] label_1= yi.util.ImageUtil.twoD2oneD(label);
        int[] img= yi.util.ImageUtil.makeNewImageFromLabel(label_1);

        //This is to display the linked-line after canny and the line detected
        //int[] rst= (new CannyGray(sigma,tlow,thigh)).getProcessed(source,d);
        //yi.util.ImageUtil.makeImageFromGray(rst);
        //for (int i=0;i<img.length;i++)
        //    if (img[i]!=0xff000000) rst[i]=img[i];
        //(new DisplayFrame("rst", rst, d.width, d.height)).show();

        (new DisplayFrame("rst", img, d.width, d.height)).show();
        /*
        for (int i=0;i<img.length;i++)
            if (img[i]==0xff000000) img[i]=source[i];
        (new DisplayFrame("rst", img, d.width, d.height)).show();
        */
        return img;
    }

/*
    public Vector getLines(int source[], Dimension d)
    {
        Vector[] segs= fex(source, d);
        Vector lines= segs[0];
        int XCenter= d.width/2, YCenter= d.height/2;
        for (int i=0;i<lines.size();i++) {
            ORTLine line= (ORTLine) lines.elementAt(i);
            line.Start.Col += XCenter;
            line.Start.Row += YCenter;
            line.End.Col   += XCenter;
            line.End.Row   += YCenter;
            line.End.Col   += XCenter;
            line.End.Row   += YCenter;

        }
        return lines;
    }
*/

    public Vector[] fex(int source[], Dimension d)
    {
        Vector chained= (new ChainPixel(sigma,tlow,thigh)).link_and_save(source, d, ChainPixel.ALLLISTS);
        int NoOfCols=d.width, NoOfRows=d.height;
        /*
        //=======================================================================
        //===================== START OF DECLARATION ============================
        //=======================================================================
        */
        int i;
        int LineCounter;
        int CurveCounter;

        /*
        //Number of lines in corresponding lists

        */
        //int StringListSize;	  /* Number of points in string */
        //int SegmentListSize;	  /* Number of segments found along string */
        int StringID;
        /*
        //ID numbers of appropriate lists

        */
        Vector StringListID;
        Vector SegmentListID;
        Vector LineListID;
        Vector CircularArcListID;

        /*
        //Pointer to list

        */
        ORTPoint          StringList;
        ORTSegment        SegmentList;
        ORTLine    	     Line;
        ORTCircularArc    CircularArc;

        /*
        //Buffers for outputting result
        */

        int semicircle;
        int direction=0;

        long Buffer;
        Object Segment;

        ORTSegment SegmentBuffer;
        ORTPoint   PointBuffer;

        double Length;
        double LengthParlVar;
        double LengthPerpVar;
        double ThetaVar;
        double Height;
        double Width;

        double StartCol,EndCol;
        double StartRow,EndRow;

        double side;
        double x1,x2,x3,x4;
        double y1,y2,y3,y4;
        double MidToVL;
        double MidToJct;

        /*
        //=======================================================================
        //============================ MAIN LOOP ================================
        //=======================================================================
        */

        /*
        //Get one string from string file and store it in a linked list for processing
        */

        LineListID 	    = new Vector();
        CircularArcListID = new Vector();

        LineCounter  = 0;
        CurveCounter = 0;
        StringID     = 0;


        for (int chain_i=0; chain_i<chained.size(); chain_i++)
        {
            StringListID= CreateStringList(chained, chain_i, NoOfCols, NoOfRows);
          StringID+=1;
            /*
            //Find segments along the string using Strider algorithm and store them in
            //a list for linking
            */
            if ((SegmentListID=StringToSegments(MaxJumpPixels,StringListID))!=null) {
                if ((SegmentListID= LinkSegments(StringListID,SegmentListID)) != null) {


                    //for (i = 1; i <= SegmentListID.size() ; i++) {
                    for (i = 0; i < SegmentListID.size() ; i++) {
                    Segment 	= SegmentListID.elementAt(i);
                    SegmentBuffer = (ORTSegment)Segment;

                        /*
                        //Now classify the bloody things and output to appropriate files
                        */

                      Height = LineLength(SegmentBuffer.MidPoint.Col,
                              SegmentBuffer.MidPoint.Row,
                              SegmentBuffer.VLPoint.Col,
                              SegmentBuffer.VLPoint.Row);

                      Length = (double)(SegmentBuffer.StringEnd -
                              SegmentBuffer.StringStart + 1);
                        /*
                        //Due to ambiguity at end points
                        */
                      LengthParlVar = Math.sqrt(2.0);
                      LengthPerpVar = Math.sqrt(2.0);
                      ThetaVar = (2.0*LengthPerpVar)/Length;

                        /*
                        //This is the condition for a segment to be classified as a straight-line
                        */
                      if ( Height <= 2.0*LengthPerpVar || Height >= HUGE ||
                          SegmentBuffer.JctPoint.Col >= HUGE ||
                          SegmentBuffer.JctPoint.Row >= HUGE) {
                            /*
                            //Add these segments to the line list, removing ambiguous end points.
                            */
                            Line = new ORTLine();

                            //? it is to remove the ambigous points, but also make the length being 0 possibly
                            //? yi
                            StringList = (ORTPoint)StringListID.elementAt( SegmentBuffer.StringStart+1 );
                            Line.Start.Col 	= StringList.Col;
                            Line.Start.Row 	= StringList.Row;

                            //? yi
                            StringList = (ORTPoint)StringListID.elementAt(SegmentBuffer.StringEnd-1 );
                            Line.End.Col 	= StringList.Col;
                            Line.End.Row 	= StringList.Row;

                            Line.Length 		= LineLength(Line.Start.Col,Line.Start.Row,
                                                            Line.End.Col,Line.End.Row);
                            if (Line.Length<MINLENGTH) continue;
                          LineCounter++;

                            Line.ID 		= LineCounter;
                            Line.StringID 	= StringID;

//                            Line.MidPoint.Col 	= SegmentBuffer.MidPoint.Col;
//                            Line.MidPoint.Row 	= SegmentBuffer.MidPoint.Row;


                            Line.LengthParlVar  = 2.0*LengthParlVar;
                            Line.LengthPerpVar  = LengthPerpVar;
                            Line.Theta 		= LineSegTheta(Line.Start.Col,
                                          Line.Start.Row,
                                          Line.End.Col,
                                          Line.End.Row);
//                      // the assignment conflicts with the definition in ORTLine
//                      // remove it to make the results consistent
//                      // (Yi 10/21/2002)
//                      Line.ThetaVar 	= ThetaVar;
                      Line.ThetaVar = Line.LineSegThetaVar(Line.Length);
                      LineListID.addElement(Line);

                  }else {
                      if (Length<MINLENGTH) continue;
                      // as a curve
                          CurveCounter++;
                          CircularArc = new ORTCircularArc();
                          CircularArc.ID 		 = CurveCounter;
                          CircularArc.StringID 	 = StringID;
                          CircularArc.Start.Col 	 = SegmentBuffer.Start.Col;
                          CircularArc.Start.Row 	 = SegmentBuffer.Start.Row;
                          CircularArc.End.Col 	 = SegmentBuffer.End.Col;
                          CircularArc.End.Row 	 = SegmentBuffer.End.Row;
                          CircularArc.MidPoint.Col   = SegmentBuffer.MidPoint.Col;
                          CircularArc.MidPoint.Row   = SegmentBuffer.MidPoint.Row;
                          CircularArc.VLPoint.Col 	 = SegmentBuffer.VLPoint.Col;
                          CircularArc.VLPoint.Row 	 = SegmentBuffer.VLPoint.Row;
                          CircularArc.Origin.Col 	 = SegmentBuffer.JctPoint.Col;
                          CircularArc.Origin.Row 	 = SegmentBuffer.JctPoint.Row;
                          CircularArc.Radius 	 = (1.0/3.0) * (
                              LineLength(CircularArc.Origin.Col,
                                  CircularArc.Origin.Row,
                                  CircularArc.Start.Col,
                                  CircularArc.Start.Row) +
                              LineLength(CircularArc.Origin.Col,
                                  CircularArc.Origin.Row,
                                  CircularArc.MidPoint.Col,
                                  CircularArc.MidPoint.Row) +
                              LineLength(CircularArc.Origin.Col,
                                  CircularArc.Origin.Row,
                                  CircularArc.End.Col,
                                  CircularArc.End.Row) );
                          CircularArc.Length 	 = Length;
                          CircularArc.LengthParlVar  = 2.0*LengthParlVar;
                          CircularArc.LengthPerpVar  = LengthPerpVar;
                          CircularArc.Theta		 = LineSegTheta(CircularArc.Origin.Col,
                                  CircularArc.Origin.Row,
                                  CircularArc.MidPoint.Col,
                                  CircularArc.MidPoint.Row);
                          CircularArc.Height 	 = Height;
                          CircularArc.Width 		 = LineLength(SegmentBuffer.Start.Col,
                                          SegmentBuffer.Start.Row,
                                          SegmentBuffer.End.Col,
                                          SegmentBuffer.End.Row);
                            /*
                            //First figure out whether curve is bigger or smaller than a semicircle
                            */

                            MidToVL  = LineLength( CircularArc.MidPoint.Col,
                                                    CircularArc.MidPoint.Row,
                                                    CircularArc.VLPoint.Col,
                                                    CircularArc.VLPoint.Row);

                            MidToJct = LineLength( CircularArc.MidPoint.Col,
                                                    CircularArc.MidPoint.Row,
                                                    CircularArc.Origin.Col,
                                                    CircularArc.Origin.Row);
                            semicircle = 0;
                            if (MidToVL >= MidToJct)  semicircle = 1;
                            /*
                            //Now figure out whether to draw clockwise or anticlockwise
                            */
                            x1 = CircularArc.Start.Col;
                            y1 = CircularArc.Start.Row;
                            x2 = CircularArc.MidPoint.Col;
                            y2 = CircularArc.MidPoint.Row;
                            x3 = CircularArc.End.Col;
                            y3 = CircularArc.End.Row;
                            x4 = CircularArc.Origin.Col;
                            y4 = CircularArc.Origin.Row;

                            side = - (x4-x3)*(y1-y3) + (y4-y3)*(x1-x3) ;

                            if (side <= 0.0 && semicircle != 1)
                              direction = -1;

                            if (side <= 0.0 && semicircle == 1)
                              direction =  1;

                            if (side  > 0.0 && semicircle != 1)
                              direction = 1;

                            if (side  > 0.0 && semicircle == 1)
                              direction = -1;

                          CircularArc.Direction	 = direction;
                          CircularArcListID.addElement(CircularArc);
                      }
                  }
                }
            }
            else {
                //System.err.println("StringToSegments return null");
            }
        }

        StringListID=SegmentListID=null;
        System.gc();

        //Sort by theta and reassign ID numbers
        //SortList(LineListID, SortByThetaAscending);

        Vector[] rst= {LineListID,CircularArcListID};
        return rst;
    }

    //change the coordinate -system, to center at (0,0)
    Vector  CreateStringList( Vector chained, int index, int NoOfCols, int NoOfRows)
    {
        int[][] a_chain= (int[][])chained.elementAt(index);
        ORTPoint StringList=null;
        Vector StringListID= new Vector();
        int Row,Col;
        for (int i=0;i<a_chain.length;i++) {
            Row= a_chain[i][0]; Col= a_chain[i][1];
            StringList= new ORTPoint();
          StringList.Col = (double) Row - ((double) (NoOfCols))/2.0;
          StringList.Row = (double) Col - ((double) (NoOfRows))/2.0;
          StringListID.addElement(StringList);
      }
      return StringListID;
    }

/***************************************************************************
StringToSegments
***************************************************************************/
    static int MinSegmentLength= 3;

    Vector StringToSegments(int MaxJumpPixels, Vector StringListID)
    {
        Vector SegmentListID;
        ORTPoint StringList;
        ORTSegment SegmentList;
        int StringListSize= StringListID.size();
        double[] tmp;

        int i;		/* Dummy variables */
        int StartPosition;
        int EndPosition;
        int FoundSegment;
        int AsymmetryFlag;
        int Step;

        double[] StringRow= new double[StringListSize];
        double[] StringCol= new double[StringListSize];

        double BodyCol;
        double BodyRow;
        double StartCol;
        double StartRow;
        double EndCol;
        double EndRow;

        double LeftArmLength;
        double RightArmLength;
        double Length;

        double LeftArmTheta;
        double RightArmTheta;
        double LeftArmGrad;
        double RightArmGrad;
        double LeftArmCol;
        double LeftArmRow;
        double RightArmCol;
        double RightArmRow;
        double JctPtCol=0;
        double JctPtRow=0;
        double InitJctCol;
        double InitJctRow;
        double Radius;
        double CRight;
        double CLeft;

        double VLColIntercept;
        double VLRowIntercept;
        double VLPtCol;
        double VLPtRow;
        double VLTheta;

        double OldTotalArmLength;
        double CurrentTotalArmLength;
        double SegmentLength;

        double Disparity;

        if (VERBOSE) {
                System.err.println(" Start of function StringToSegments \n");
        }
        /*
        //String is too small to process
        */

        if (StringListSize < 3)
          return null;

        /*
        //Create linked list to put segments in
        */
        SegmentListID = new Vector();

        /*
        //Store string data in array
        */

        //for (i=1;i<=StringListSize;i++) {
        for (i=0;i<StringListSize;i++) {
            StringList = (ORTPoint) StringListID.elementAt(i);
            StringRow[i] = StringList.Row;
            StringCol[i] = StringList.Col;
        }

        /*
        //Can start anywhere along the Segment as long as the position is more than
        //1 pixel from end points. This point will be treated as an end point. Since
        //we set the min Segmentlength to 3 we start at the 2nd pixel along.
        */

        //StartPosition = 1;
        //EndPosition = 3;
        StartPosition = 0;
        EndPosition = 2;
        AsymmetryFlag = 0;
        FoundSegment = 0;
        LeftArmTheta = 0.0;
        RightArmTheta = 0.0;
        CurrentTotalArmLength = 0.0;
        OldTotalArmLength = 0.0;

        /*
        //Loop until the whole Segment is processed or we have only 2 pixel bits left
        */

        while ( EndPosition < StringListSize) {

            Step = 0;
            while (FoundSegment == 0 ) {

                /*
                //Now use the end points of the line segments forming the arms, and the position
                //of the body to find the the virtual line parameters
                */
                VLColIntercept= LineSegColIntercept( StringCol[StartPosition],
                                    StringRow[StartPosition],
                                    StringCol[EndPosition],
                                    StringRow[EndPosition]);

                VLRowIntercept= LineSegRowIntercept( StringCol[StartPosition],
                                    StringRow[StartPosition],
                                    StringCol[EndPosition],
                                    StringRow[EndPosition]);

                VLTheta = LineSegTheta(StringCol[StartPosition],
                              StringRow[StartPosition],
                              StringCol[EndPosition],
                              StringRow[EndPosition]);

                BodyCol = (StringCol[StartPosition + (Step/2)] +
                                    StringCol[EndPosition - (Step/2)])/2.0;
                BodyRow = (StringRow[StartPosition + (Step/2)] +
                        StringRow[EndPosition - (Step/2)])/2.0;

                tmp=PtLinePerpIntercept(BodyCol,
                                    BodyRow,
                                    VLColIntercept,
                                    VLRowIntercept,
                                    VLTheta);
                VLPtCol=tmp[0];VLPtRow=tmp[1];
                /*
                //Find if we are roughly symmetric by comparing arm lengths (could compare diff.
                //in Heights of arms below intercept point)
                */

                LeftArmLength  = LineLength(VLPtCol,VLPtRow,
                          StringCol[StartPosition],
                          StringRow[StartPosition]);

                RightArmLength = LineLength(VLPtCol,VLPtRow,
                          StringCol[EndPosition],
                          StringRow[EndPosition]);

                CurrentTotalArmLength = LeftArmLength + RightArmLength;

                Length =  LineLength(StringCol[StartPosition],
                        StringRow[StartPosition],
                        StringCol[EndPosition],
                        StringRow[EndPosition]);

                Disparity = 1.0/Math.sqrt(1.0 + (Length-1.0)*(Length-1.0));

                if ( Math.abs(RightArmLength - LeftArmLength) >= Disparity ||
                    CurrentTotalArmLength < OldTotalArmLength) {
                        AsymmetryFlag++;
                }
                else {
                    AsymmetryFlag = 0;
                }

                OldTotalArmLength = CurrentTotalArmLength;
                /*
                //If still near-symmetric move body by one position, and extend the arms
                */

                if (AsymmetryFlag > MaxJumpPixels || EndPosition == StringListSize-1) {
                    EndPosition = EndPosition - AsymmetryFlag;
                    FoundSegment = 1;
                }
                else {
                    EndPosition++;
                    Step++;
                }
            } /* While segment not found */

            /*
            //If we reached an end point then stop, store the result and continue
            */
            SegmentLength = EndPosition - StartPosition + 1;
            if (SegmentLength > 2) {
                if (AsymmetryFlag == 0) {
                    BodyCol = ( StringCol[StartPosition + (int)(SegmentLength/2)] +
                        StringCol[EndPosition   - (int)(SegmentLength/2)] )/2.0;
                    BodyRow = ( StringRow[StartPosition + (int)(SegmentLength/2)] +
                        StringRow[EndPosition   - (int)(SegmentLength/2)] )/2.0;

                  StartCol = StringCol[StartPosition];
                  StartRow = StringRow[StartPosition];
                  EndCol   = StringCol[EndPosition];
                  EndRow   = StringRow[EndPosition];
                    /*
                    //Compute the virtual line parameters
                    */

                    VLColIntercept= LineSegColIntercept( StartCol,
                                            StartRow,
                                            EndCol,
                                            EndRow);

                    VLRowIntercept= LineSegRowIntercept( StartCol,
                                            StartRow,
                                            EndCol,
                                            EndRow);

                    VLTheta = LineSegTheta(StartCol,
                            StartRow,
                            EndCol,
                            EndRow);

                    tmp=PtLinePerpIntercept(BodyCol,
                                        BodyRow,
                                        VLColIntercept,
                                        VLRowIntercept,
                                        VLTheta);
                    VLPtCol=tmp[0];VLPtRow=tmp[1];


                    /*
                    //Now find the rough position of the center of curvature
                    */

                    LeftArmTheta  = LineSegTheta(StartCol,
                                              StartRow,
                                                BodyCol,
                                                BodyRow);

                    RightArmTheta = LineSegTheta(EndCol,
                                              EndRow,
                                                BodyCol,
                                                BodyRow);

                    LeftArmCol  = (StartCol + BodyCol)/2.0;
                    LeftArmRow  = (StartRow + BodyRow)/2.0;
                    RightArmCol = (EndCol + BodyCol)/2.0;
                    RightArmRow = (EndRow + BodyRow)/2.0;

                  RightArmTheta = RightArmTheta + PIBY2;
                  if (RightArmTheta > Math.PI)
                      RightArmTheta = RightArmTheta - Math.PI;

                  LeftArmTheta  = LeftArmTheta + PIBY2;
                  if (LeftArmTheta > Math.PI)
                      LeftArmTheta = LeftArmTheta - Math.PI;

                    RightArmGrad = Math.tan(RightArmTheta);
                    LeftArmGrad  = Math.tan(LeftArmTheta);

                    CRight = RightArmRow - RightArmGrad*RightArmCol;
                    CLeft  = LeftArmRow  - LeftArmGrad*LeftArmCol;

                    if (Math.abs(LeftArmTheta - RightArmTheta) < 0.001) {
                        JctPtCol = HUGE;
                        JctPtRow = HUGE;
                    }
                    else {

                        if (MACCompareWithPIBY2(LeftArmTheta)) {
                            InitJctCol = JctPtCol = LeftArmCol;
                            InitJctRow = JctPtRow = RightArmGrad*LeftArmCol + CRight;
                            Radius   = LineLength(JctPtCol,JctPtRow,
                            StringCol[(int)((EndPosition - StartPosition)/2)],
                            StringRow[(int)((EndPosition - StartPosition)/2)]);

                            double[] args={JctPtCol,JctPtRow,Radius};
                            int rst=CircleLeastSq(StringCol,StringRow,
                                  StartPosition,EndPosition,
                              args);
                      JctPtCol=args[0];JctPtRow=args[1];Radius=args[2];
                            if (  rst!= 0) {
                                JctPtCol = InitJctCol;
                                JctPtRow = InitJctRow;
                          }
                      }

                        if (MACCompareWithPIBY2(RightArmTheta)) {
                            InitJctCol = JctPtCol = RightArmCol;
                            InitJctRow = JctPtRow = LeftArmGrad*RightArmCol + CLeft;

                            Radius   = LineLength(JctPtCol,JctPtRow,
                            StringCol[(int)((EndPosition - StartPosition)/2)],
                            StringRow[(int)((EndPosition - StartPosition)/2)]);

                            double[] args={JctPtCol,JctPtRow,Radius};
                            int rst=CircleLeastSq(StringCol,StringRow,
                                  StartPosition,EndPosition,
                              args);
                      JctPtCol=args[0];JctPtRow=args[1];Radius=args[2];
                            if (  rst!= 0) {

                                JctPtCol = InitJctCol;
                                JctPtRow = InitJctRow;
                          }

                        }


                        if (!MACCompareWithPIBY2(RightArmTheta) &&
                            !MACCompareWithPIBY2(LeftArmTheta)) {
                            InitJctCol = JctPtCol = (CRight - CLeft)/(LeftArmGrad - RightArmGrad);
                            InitJctRow = JctPtRow = RightArmGrad*JctPtCol + CRight;
                            Radius   = LineLength(JctPtCol,JctPtRow,
                            StringCol[(int)((EndPosition - StartPosition)/2)],
                            StringRow[(int)((EndPosition - StartPosition)/2)]);

                            double[] args={JctPtCol,JctPtRow,Radius};
                            int rst=CircleLeastSq(StringCol,StringRow,
                                  StartPosition,EndPosition,
                              args);
                      JctPtCol=args[0];JctPtRow=args[1];Radius=args[2];
                            if (  rst!= 0) {
                                JctPtCol = InitJctCol;
                                JctPtRow = InitJctRow;
                          }
                        }
                    }
                    /*
                    //Allocate memory for the segment
                    */

                  SegmentList = new ORTSegment();

                  SegmentList.StringStart  = StartPosition;
                  SegmentList.StringEnd    = EndPosition;
                  SegmentList.Start.Col    = StartCol;
                  SegmentList.Start.Row    = StartRow;
                  SegmentList.End.Col      = EndCol;
                  SegmentList.End.Row	= EndRow;
                  SegmentList.MidPoint.Col = BodyCol;
                  SegmentList.MidPoint.Row = BodyRow;
                  SegmentList.JctPoint.Col = JctPtCol;
                  SegmentList.JctPoint.Row = JctPtRow;
                  SegmentList.VLPoint.Col  = VLPtCol;
                  SegmentList.VLPoint.Row  = VLPtRow;
                  SegmentList.VLTheta      = VLTheta;
                    /*
                    //Add element to list
                    */

                    SegmentListID.addElement(SegmentList);
                }
                else {

                    BodyCol = ( StringCol[StartPosition + (int)(SegmentLength/2)] +
                        StringCol[EndPosition   - (int)(SegmentLength/2)] )/2.0;
                    BodyRow = ( StringRow[StartPosition + (int)(SegmentLength/2)] +
                        StringRow[EndPosition   - (int)(SegmentLength/2)] )/2.0;

                  StartCol = StringCol[StartPosition];
                  StartRow = StringRow[StartPosition];
                  EndCol   = StringCol[EndPosition];
                  EndRow   = StringRow[EndPosition];
                    /*
                    //Compute the virtual line parameters
                    */

                    VLColIntercept= LineSegColIntercept( StartCol,
                                            StartRow,
                                            EndCol,
                                            EndRow);

                    VLRowIntercept= LineSegRowIntercept( StartCol,
                                            StartRow,
                                            EndCol,
                                            EndRow);

                    VLTheta = LineSegTheta(StartCol,
                            StartRow,
                            EndCol,
                            EndRow);

                    tmp= PtLinePerpIntercept(BodyCol,
                                        BodyRow,
                                        VLColIntercept,
                                        VLRowIntercept,
                                        VLTheta);
                    VLPtCol=tmp[0];VLPtRow=tmp[1];

                    /*
                    //Now find the rough position of the center of curvature
                    */

                    LeftArmTheta  = LineSegTheta(StartCol,
                                              StartRow,
                                                BodyCol,
                                                BodyRow);

                    RightArmTheta = LineSegTheta(EndCol,
                                              EndRow,
                                                BodyCol,
                                                BodyRow);

                    LeftArmCol  = (StartCol + BodyCol)/2.0;
                    RightArmCol = (EndCol + BodyCol)/2.0;
                    LeftArmRow  = (StartRow + BodyRow)/2.0;
                    RightArmRow = (EndRow + BodyRow)/2.0;

                RightArmTheta = RightArmTheta + PIBY2;
                if (RightArmTheta > Math.PI)
                    RightArmTheta = RightArmTheta - Math.PI;

                LeftArmTheta = LeftArmTheta + PIBY2;
                if (LeftArmTheta > Math.PI)
                    LeftArmTheta = LeftArmTheta - Math.PI;

                    RightArmGrad = Math.tan(RightArmTheta);
                    LeftArmGrad  = Math.tan(LeftArmTheta);

                    CRight = RightArmRow - RightArmGrad*RightArmCol;
                    CLeft  = LeftArmRow  - LeftArmGrad*LeftArmCol;

                    if (Math.abs(LeftArmTheta - RightArmTheta) < 0.001) {
                        JctPtCol = HUGE;
                        JctPtRow = HUGE;
                    }
                    else {

                        if (MACCompareWithPIBY2(LeftArmTheta)) {
                            InitJctCol = JctPtCol = LeftArmCol;
                            InitJctRow = JctPtRow = RightArmGrad*LeftArmCol + CRight;
                            Radius   = LineLength(JctPtCol,JctPtRow,
                            StringCol[(int)((EndPosition - StartPosition)/2)],
                            StringRow[(int)((EndPosition - StartPosition)/2)]);

                            double[] args={JctPtCol,JctPtRow,Radius};
                            int rst=CircleLeastSq(StringCol,StringRow,
                                  StartPosition,EndPosition,
                              args);
                      JctPtCol=args[0];JctPtRow=args[1];Radius=args[2];
                            if (  rst!= 0) {

                                JctPtCol = InitJctCol;
                                JctPtRow = InitJctRow;
                          }
                        }

                        if (MACCompareWithPIBY2(RightArmTheta)) {
                            InitJctCol = JctPtCol = RightArmCol;
                            InitJctRow = JctPtRow = LeftArmGrad*RightArmCol + CLeft;
                            Radius   = LineLength(JctPtCol,JctPtRow,
                            StringCol[(int)((EndPosition - StartPosition)/2)],
                            StringRow[(int)((EndPosition - StartPosition)/2)]);

                            double[] args={JctPtCol,JctPtRow,Radius};
                            int rst=CircleLeastSq(StringCol,StringRow,
                                  StartPosition,EndPosition,
                              args);
                      JctPtCol=args[0];JctPtRow=args[1];Radius=args[2];
                            if (  rst!= 0) {
                                JctPtCol = InitJctCol;
                                JctPtRow = InitJctRow;
                          }

                        }

                        if (!MACCompareWithPIBY2(RightArmTheta) &&
                            !MACCompareWithPIBY2(LeftArmTheta)) {
                            InitJctCol = JctPtCol = (CRight - CLeft)/(LeftArmGrad - RightArmGrad);
                            InitJctRow = JctPtRow = RightArmGrad*JctPtCol + CRight;
                            Radius   = LineLength(JctPtCol,JctPtRow,
                            StringCol[(int)((EndPosition - StartPosition)/2)],
                            StringRow[(int)((EndPosition - StartPosition)/2)]);

                            double[] args={JctPtCol,JctPtRow,Radius};
                            int rst=CircleLeastSq(StringCol,StringRow,
                                  StartPosition,EndPosition,
                              args);
                      JctPtCol=args[0];JctPtRow=args[1];Radius=args[2];
                            if (  rst!= 0) {
                                JctPtCol = InitJctCol;
                                JctPtRow = InitJctRow;
                          }
                        }
                    }
                    /*
                    //Allocate memory for the segment
                    */

                  SegmentList = new ORTSegment();

                  SegmentList.StringStart  = StartPosition;
                  SegmentList.StringEnd    = EndPosition;
                  SegmentList.Start.Col    = StartCol;
                  SegmentList.Start.Row    = StartRow;
                  SegmentList.End.Col      = EndCol;
                  SegmentList.End.Row	= EndRow;
                  SegmentList.MidPoint.Col = BodyCol;
                  SegmentList.MidPoint.Row = BodyRow;
                  SegmentList.JctPoint.Col = JctPtCol;
                  SegmentList.JctPoint.Row = JctPtRow;
                  SegmentList.VLPoint.Col  = VLPtCol;
                  SegmentList.VLPoint.Row  = VLPtRow;
                  SegmentList.VLTheta      = VLTheta;
                    /*
                    //Add element to list
                    */
                    SegmentListID.addElement(SegmentList );

                } /* depending on asymmetry make correction and add segment to the list */
            }
            StartPosition = EndPosition + 1;
            EndPosition = StartPosition + 2;
          OldTotalArmLength = 0.0;
          CurrentTotalArmLength = 0.0;
            AsymmetryFlag = 0;
            FoundSegment = 0;

        } /* while the string is not completely processed */

        if (SegmentListID.size() > 0) {
          return SegmentListID;
        }
        else {
            System.err.println("no segment find");
          return null;
        }
    }


    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.
    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;

    }

    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;

    }

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

    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);

    }

    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);

    }

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

    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;

    }

    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 MaxDisparity= 1.4142137;

    Vector LinkSegments(Vector StringListID, Vector SegmentListID)
    {
        ORTPoint StringList;
        ORTSegment SegmentList;
        int StringListSize= StringListID.size();
        int SegmentListSize= SegmentListID.size();

        int i;		/* Dummy variables */
        int JoinSegments;

        int OldSegmentID;
        int NewSegmentID;
        int OldNoOfSegments;
        int NewNoOfSegments;
        int OldSegmentCurvature;
        int NewSegmentCurvature;

        ORTSegment OldSegment=null;
        ORTSegment NewSegment=null;

        double OldSegmentTheta;
        double NewSegmentTheta;
        double OldGrad;
        double NewGrad;

        double JunctionPtCol;
        double JunctionPtRow;
        double InitJctCol;
        double InitJctRow;
        double Radius;

        double OldMidLength;
        double NewMidLength;
        double OldEndLength;
        double NewEndLength;
        double OldStartLength;
        double NewStartLength;

        double[] StringRow= new double[StringListID.size()]; //[MAXPOINT]
        double[] StringCol= new double[StringListID.size()]; //[MAXPOINT];

        double VLColIntercept;
        double VLRowIntercept;

        double OldHeight;
        double NewHeight;
        double OldEndHeight;

        int Length;
        double NewStartHeight;
        double Height;
        double Row,Col;
        double VLTheta;
        double MidPtCol;
        double MidPtRow;

        int LinkedSegmentLength;
        Vector LinkedSegmentListID;
        Vector BufferListID;
        ORTSegment LinkedSegment;

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

        /*
        //String is too small to process
        */

        if (StringListSize < 3)
          return null;
        /*
        //Only one segment so no point linking it..
        */
        if ((SegmentListID.size()) == 1)
          return SegmentListID;

        LinkedSegmentListID = new Vector();
        /*
        Store string data in array
        */

        //for (i=1;i<=StringListSize;i++) {
        for (i=0;i<StringListSize;i++) {
            StringList = (ORTPoint) (StringListID.elementAt(i));
            StringRow[i] = StringList.Row;
            StringCol[i] = StringList.Col;
        }

        /*
        Initialise variables
        */
          OldNoOfSegments = (SegmentListID.size());
          NewNoOfSegments = 2;
          //OldSegmentID = 1;
          //NewSegmentID = 2;
          OldSegmentID = 0;
          NewSegmentID = 1;
        /*
        Loop until we can't link any more segments together
        */
        while (NewNoOfSegments < OldNoOfSegments &&
                NewNoOfSegments > 1 && OldNoOfSegments > 1) {
            /*
            Loop until we've joined all segments we could in this pass
            */
            //while (NewSegmentID <= (SegmentListID.size()) &&
            while (NewSegmentID < (SegmentListID.size()) &&
                    (SegmentListID.size()) > 1) {

                /*
                Find the junction point (if any) between lines passing through the VL point
                and the midpoint of the segment, which should be perpendicular to the segment.
                This will
                  a) give us an idea of the relative curvature of the two segments
                  b) allow us to check if the two segments are co-curvilinear.
                */
              OldSegment = (ORTSegment)
                    SegmentListID.elementAt(OldSegmentID);

              NewSegment = (ORTSegment)
                    SegmentListID.elementAt(NewSegmentID);

                /*
                First find the orientation of the lines joining the midpoint to the VL point.
                by rotating the VL by PIBY2.
                */

            OldSegmentTheta = (OldSegment.VLTheta) + PIBY2;
            if (OldSegmentTheta > Math.PI)
                OldSegmentTheta = OldSegmentTheta - Math.PI;

            NewSegmentTheta = (NewSegment.VLTheta) + PIBY2;
            if (NewSegmentTheta > Math.PI)
                NewSegmentTheta = NewSegmentTheta - Math.PI;
                /*
                Now we got the orientations of the perps to the segments we can find the
                junction point. We check first though to see if the segments are not collinear.
                */

                JoinSegments = 1;

                /*
                Find the junction point
                */

              OldGrad = Math.tan(OldSegmentTheta);
              NewGrad = Math.tan(NewSegmentTheta);

                if (Math.abs(OldSegmentTheta - NewSegmentTheta) < 0.001) {
                JunctionPtCol = HUGE;
                JunctionPtRow = HUGE;

              }
              else {

                  InitJctCol = JunctionPtCol = ( (NewSegment.MidPoint.Row) -
                          (OldSegment.MidPoint.Row) -
                          (NewGrad * (NewSegment.MidPoint.Col)) +
                          (OldGrad * (OldSegment.MidPoint.Col)) ) /
                          (OldGrad - NewGrad);

                  InitJctRow = JunctionPtRow = (OldSegment.MidPoint.Row) +
                  (OldGrad * (JunctionPtCol - (OldSegment.MidPoint.Col)));

                    Radius   = LineLength(JunctionPtCol,JunctionPtRow,
                              StringCol[OldSegment.StringEnd],
                              StringRow[OldSegment.StringEnd]);

                    double[] args={JunctionPtCol,JunctionPtRow,Radius};
                    int rst=CircleLeastSq(StringCol,StringRow,
                      OldSegment.StringStart,
                      NewSegment.StringEnd,
                      args);
              JunctionPtCol=args[0];JunctionPtRow=args[1];Radius=args[2];
                    if (  rst!= 0) {
    /*
                if ( CircleLeastSq(StringCol,StringRow,
                      OldSegment.StringStart,
                      NewSegment.StringEnd,
                      &JunctionPtCol,&JunctionPtRow,&Radius) != 0) {
    */
                        JunctionPtCol = InitJctCol;
                        JunctionPtRow = InitJctRow;
                  }

              }
                /*
                Find the heights associated with the segments
                */
              OldHeight = LineLength(OldSegment.VLPoint.Col,
                      OldSegment.VLPoint.Row,
                      OldSegment.MidPoint.Col,
                      OldSegment.MidPoint.Row);

              NewHeight = LineLength(NewSegment.VLPoint.Col,
                      NewSegment.VLPoint.Row,
                      NewSegment.MidPoint.Col,
                      NewSegment.MidPoint.Row);

                /*
                Now find the curvatures of the segments relative to the junction point
                */

              OldMidLength =  LineLength(JunctionPtCol,
                          JunctionPtRow,
                          OldSegment.MidPoint.Col,
                          OldSegment.MidPoint.Row);

              NewMidLength =  LineLength(JunctionPtCol,
                          JunctionPtRow,
                          NewSegment.MidPoint.Col,
                          NewSegment.MidPoint.Row);

              OldEndLength =  LineLength(JunctionPtCol,
                          JunctionPtRow,
                          OldSegment.End.Col,
                          OldSegment.End.Row);

              NewEndLength =  LineLength(JunctionPtCol,
                          JunctionPtRow,
                          NewSegment.End.Col,
                          NewSegment.End.Row);

              OldStartLength =  LineLength(JunctionPtCol,
                              JunctionPtRow,
                              OldSegment.Start.Col,
                              OldSegment.Start.Row);

              NewStartLength =  LineLength(JunctionPtCol,
                              JunctionPtRow,
                              NewSegment.Start.Col,
                              NewSegment.Start.Row);
              if ( OldMidLength >
                  LineLength(JunctionPtCol,
                  JunctionPtRow,
                  OldSegment.VLPoint.Col,
                  OldSegment.VLPoint.Row) ||
                        OldHeight <= MaxDisparity) {
                  OldSegmentCurvature = 0;
              }
              else {
                  OldSegmentCurvature = 1;
              }

              if ( NewMidLength >
                  LineLength(JunctionPtCol,
                  JunctionPtRow,
                  NewSegment.VLPoint.Col,
                  NewSegment.VLPoint.Row) ||
                        NewHeight <= MaxDisparity) {
                  NewSegmentCurvature = 0;
              }
              else {
                  NewSegmentCurvature = 1;
              }

                /*
                Check if segments are co-curvilinear and if so join them. The criterion is:

                  1. They have same curvature which is not infinity (HUGE)
                  2. The end point of the old segment is 3 pixels or less away from
                  the start point of the new segment
                  3. The difference between any pair of the distances between
                  the points along the suspected curve and the center of curvature
                  of the combined segments is less than MaxDisparity
                */

              if (( NewSegmentCurvature == OldSegmentCurvature &&
                    Math.abs(OldSegment.StringEnd - NewSegment.StringStart) <= 3 &&
                  Math.abs(OldMidLength - NewMidLength)   <= MaxDisparity &&
                  Math.abs(OldMidLength - NewStartLength) <= MaxDisparity &&
                  Math.abs(OldMidLength - NewEndLength)   <= MaxDisparity &&
                  Math.abs(OldMidLength - OldEndLength)   <= MaxDisparity &&
                  Math.abs(OldMidLength - OldStartLength) <= MaxDisparity &&
                  Math.abs(NewMidLength - NewStartLength) <= MaxDisparity &&
                  Math.abs(NewMidLength - NewEndLength)   <= MaxDisparity &&
                  Math.abs(NewMidLength - OldEndLength)   <= MaxDisparity &&
                  Math.abs(NewMidLength - OldStartLength) <= MaxDisparity) )
                JoinSegments = 0;
                /*
                Check if the segments are collinear, and if so join them. The criterion is :

                  1. The end point of the old segment is 2 pixels or less away from
                  the start point of the new segment
                  2. They have the same curvature
                  3. The joined segment is linear ie the heights of perps. dropped
                  onto the line joining the endpts of the joined segment is less
                  than 2.0 (otherwise they'll be classified as curves)
                */

                VLColIntercept= LineSegColIntercept( OldSegment.Start.Col,
                                        OldSegment.Start.Row,
                                        NewSegment.End.Col,
                                        NewSegment.End.Row);

                VLRowIntercept=LineSegRowIntercept( OldSegment.Start.Col,
                                        OldSegment.Start.Row,
                                        NewSegment.End.Col,
                                        NewSegment.End.Row);

                VLTheta = LineSegTheta(OldSegment.Start.Col,
                        OldSegment.Start.Row,
                        NewSegment.End.Col,
                        NewSegment.End.Row);


                double[] tmp= PtLinePerpIntercept(OldSegment.End.Col,
                                    OldSegment.End.Row,
                                    VLColIntercept,
                                    VLRowIntercept,
                                    VLTheta);
                Col=tmp[0];Row=tmp[1];

              OldEndHeight = LineLength(Col,Row,
                          OldSegment.End.Col,
                          OldSegment.End.Row);

                tmp= PtLinePerpIntercept(NewSegment.Start.Col,
                      NewSegment.Start.Row,
                                    VLColIntercept,
                                    VLRowIntercept,
                                    VLTheta);
                Col=tmp[0];Row=tmp[1];

              NewStartHeight = LineLength(Col,Row,
                          NewSegment.Start.Col,
                          NewSegment.Start.Row);

              Length = NewSegment.StringEnd - OldSegment.StringStart + 1;

                MidPtCol =
              ( StringCol[OldSegment.StringStart + (int)(Length/2)] +
                    StringCol[NewSegment.StringEnd   - (int)(Length/2)])/2.0;

                MidPtRow =
              ( StringRow[OldSegment.StringStart + (int)(Length/2)] +
                    StringRow[NewSegment.StringEnd   - (int)(Length/2)])/2.0;

                tmp= PtLinePerpIntercept(MidPtCol,
                      MidPtRow,
                                    VLColIntercept,
                                    VLRowIntercept,
                                    VLTheta);
                Col=tmp[0];Row=tmp[1];

              Height = LineLength(Col,Row,
                      MidPtCol,
                      MidPtRow);

              if ( Math.abs(OldSegment.StringEnd - NewSegment.StringStart) <= 3 &&
                NewSegmentCurvature == OldSegmentCurvature &&
                Height         < 2.0 &&
                OldEndHeight   < 2.0 &&
                NewStartHeight < 2.0)
                JoinSegments = 0;

                /*
                If everything is OK then delete the segments from the List and replace them
                with one segment
                */

              if (JoinSegments == 0) {

                  LinkedSegment = new ORTSegment();

                LinkedSegment.StringStart = OldSegment.StringStart;
                LinkedSegment.StringEnd   = NewSegment.StringEnd;
                LinkedSegment.Start.Col   = OldSegment.Start.Col;
                LinkedSegment.Start.Row   = OldSegment.Start.Row;
                LinkedSegment.End.Col     = NewSegment.End.Col;
                LinkedSegment.End.Row     = NewSegment.End.Row;

                  LinkedSegmentLength = LinkedSegment.StringEnd -
                        LinkedSegment.StringStart + 1;

                    LinkedSegment.MidPoint.Col =
                  ( StringCol[LinkedSegment.StringStart + (int)(LinkedSegmentLength/2)] +
                        StringCol[LinkedSegment.StringEnd   - (int)(LinkedSegmentLength/2)])/2.0;

                    LinkedSegment.MidPoint.Row =
                  ( StringRow[LinkedSegment.StringStart + (int)(LinkedSegmentLength/2)] +
                  StringRow[LinkedSegment.StringEnd   - (int)(LinkedSegmentLength/2)])/2.0;

                    /*
                    Compute the virtual line parameters
                    */
                    VLColIntercept= LineSegColIntercept( LinkedSegment.Start.Col,
                                            LinkedSegment.Start.Row,
                                            LinkedSegment.End.Col,
                                            LinkedSegment.End.Row);

                    VLRowIntercept=LineSegRowIntercept( LinkedSegment.Start.Col,
                                            LinkedSegment.Start.Row,
                                            LinkedSegment.End.Col,
                                            LinkedSegment.End.Row);

                    LinkedSegment.VLTheta = LineSegTheta(LinkedSegment.Start.Col,
                                  LinkedSegment.Start.Row,
                                  LinkedSegment.End.Col,
                                  LinkedSegment.End.Row);

                    tmp= PtLinePerpIntercept(LinkedSegment.MidPoint.Col,
                                        LinkedSegment.MidPoint.Row,
                                        VLColIntercept,
                                        VLRowIntercept,
                                        LinkedSegment.VLTheta);
                    LinkedSegment.VLPoint.Col=tmp[0];LinkedSegment.VLPoint.Row=tmp[1];

                  Height = LineLength(LinkedSegment.VLPoint.Col,
                          LinkedSegment.VLPoint.Row,
                          LinkedSegment.MidPoint.Col,
                          LinkedSegment.MidPoint.Row);

                    /*
                    This is to get over problems with determining the center of
                    complete circle, since the estimate of the center turn out to
                    be infinity in this case
                    */

                  InitJctCol = JunctionPtCol = (LinkedSegment.VLPoint.Col +
                                  LinkedSegment.MidPoint.Col)/2.0;
                  InitJctRow = JunctionPtRow = (LinkedSegment.VLPoint.Row +
                                  LinkedSegment.MidPoint.Row)/2.0;

                    Radius   = LineLength(JunctionPtCol,JunctionPtRow,
                        LinkedSegment.MidPoint.Col,
                        LinkedSegment.MidPoint.Row);

                    double[] args={JunctionPtCol,JunctionPtRow,Radius};
                    int rst=CircleLeastSq(StringCol,StringRow,
                      LinkedSegment.StringStart,
                      LinkedSegment.StringEnd,
                      args);
              JunctionPtCol=args[0];JunctionPtRow=args[1];Radius=args[2];
                    if (  rst!= 0) {
    /*
                if ( CircleLeastSq(StringCol,StringRow,
                      LinkedSegment.StringStart,
                      LinkedSegment.StringEnd,
                      &JunctionPtCol,&JunctionPtRow,&Radius) != 0) {
    */
                        JunctionPtCol = HUGE;
                        JunctionPtRow = HUGE;
                  }

                LinkedSegment.JctPoint.Col = JunctionPtCol;
                LinkedSegment.JctPoint.Row = JunctionPtRow;
                    /*
                    This is a botch to get around the fact that the Liste library adds elements to
                    the end of a list and not where you want them. What I do is essentially
                    recreate the list and replaced the two linked segments by one segment.
                    */
                  if (!LinkedSegmentListID.isEmpty())
                    LinkedSegmentListID=null;

                    LinkedSegmentListID = new Vector();

                  for (i = 0 ; i < OldSegmentID ; i++) {
                  //for (i = 1 ; i < OldSegmentID ; i++) {
                      NewSegment = (ORTSegment)
                        SegmentListID.elementAt(i);
                      LinkedSegmentListID.addElement(NewSegment) ;
                  }

                    LinkedSegmentListID.addElement(LinkedSegment );

                  for (i = (NewSegmentID + 1) ; i < (SegmentListID.size()) ; i++) {
                  //for (i = (NewSegmentID + 1) ; i <= (SegmentListID.size()) ; i++) {
                      NewSegment = (ORTSegment)
                        SegmentListID.elementAt(i);
                      LinkedSegmentListID.addElement( NewSegment );
                  }

                  BufferListID = (Vector)LinkedSegmentListID.clone();
                  SegmentListSize = BufferListID.size();
                  NewNoOfSegments = SegmentListSize;
                  SegmentListID = BufferListID;
                  SegmentList   = LinkedSegment;

                  /*
                  OldSegmentID = 1;
                  NewSegmentID = 2;
                  */
                    OldSegmentID = 0;
                  NewSegmentID = 1;
              }

              else {

                      OldSegmentID = NewSegmentID;
                      NewSegmentID = NewSegmentID + 1;
              }

            } /* while there are still segments pairs to join */

          OldNoOfSegments = NewNoOfSegments;
          /*
          OldSegmentID = 1;
          NewSegmentID = 2;
          */
          OldSegmentID = 0;
          NewSegmentID = 1;


        } /* while the list is still shrinking */
      return SegmentListID;
    }

    int CircleLeastSq(double[]   StringCol,double[] StringRow,
          int StringStart, int StringEnd,
          double args[])

    /*
    Variable Definitions:
    ---------------------
    StringCol,StringRow  : String arrays
    StringStart,StringEnd: Start/end points of string
    a,b,r	         : input  : initial values of center pt and radius
                        : output : final values of center pt and radius

    */

    {
      double  a=args[0],b=args[1],r=args[2];

      int e,det_flag;
      double xx,yy;
      double[][] xmatrix= new double[StringEnd+1][3];
      double[] zmatrix= new double [StringEnd+1];
      double[] parameters= new double[3];
      double init_x,init_y;

      init_x = (a);
      init_y = (b);

        for (e = StringStart; e <= StringEnd; e++) {

      xx = StringCol[e]-init_x;
      yy = StringRow[e]-init_y;

      xmatrix[e][0] =   xx;
      xmatrix[e][1] =   yy;
      xmatrix[e][2] =  1.0;
      zmatrix[e]    =  0.5*(xx*xx+yy*yy);
        }

        det_flag = 0;
        for (e = 0; e < 3 ; e++) {
      parameters[e] = 0.0;
        }

        det_flag= PseudoInverse3n(StringStart,StringEnd,
                        xmatrix,zmatrix,parameters);

        if ( det_flag == 0 ) {
        a = parameters[0];
        b = parameters[1];
        r = parameters[2];
        r = 2.0*(r) + (a)*(a)+ (b)*(b);
        if (r <= 0.0 ) {
              args[0]=a;args[1]=b;args[2]=r;

                        return(-1);
        } else {
          r = Math.sqrt((r));
          a = a + init_x;
          b = b + init_y;
        }
        } else {
            args[0]=a;args[1]=b;args[2]=r;
                    return(-1);
        }
        args[0]=a;args[1]=b;args[2]=r;
        return(0);
    }

    /* ===================================================== */
    /* 	End of LEAST SQ ERROR subroutine			 */
    /* ===================================================== */

    /* ===================================================== */
    /* 	Beginning of PSEUDO INVERSE subroutine		 */
    /* ===================================================== */


    int PseudoInverse3n(int StringStart,int StringEnd,
            double[][] a1,double[] b1,double[] c)

    {
        int det_flag=0;

        int ii,jj,n;
        double[][] aa= new double [4][4],bb= new double[4][4];
        double[] cc= new double[3];
        double det;

        for (ii = 0; ii < 3 ; ii++) {
        cc[ii] = 0.0;
        for (jj = 0; jj < 3 ; jj++) {
      aa[ii][jj] = 0.0;
      bb[ii][jj] = 0.0;
        }
        }

        for (ii = 0; ii < 3 ; ii++) {
        for (jj = 0; jj < 3 ; jj++) {
            for ( n = StringStart; n <= StringEnd ; n++) {
          aa[ii][jj] = aa[ii][jj]+a1[n][ii]*a1[n][jj];
        } /* end for n */
    } /* end for jj */
    } /* end for ii */

        det = aa[0][0]*aa[1][1]*aa[2][2]-
            aa[0][0]*aa[1][2]*aa[2][1]+
            aa[0][1]*aa[1][2]*aa[2][0]-
            aa[0][1]*aa[1][0]*aa[2][2]+
            aa[0][2]*aa[1][0]*aa[2][1]-
            aa[0][2]*aa[1][1]*aa[2][0];

        if (det == 0.0 ) {
        det_flag = 1;

        } else {

        bb[0][0] = (aa[1][1]*aa[2][2]-aa[2][1]*aa[1][2])/det;
        bb[0][1] = (aa[2][1]*aa[0][2]-aa[0][1]*aa[2][2])/det;
        bb[0][2] = (aa[0][1]*aa[1][2]-aa[1][1]*aa[0][2])/det;
        bb[1][0] = (aa[2][0]*aa[1][2]-aa[1][0]*aa[2][2])/det;
        bb[1][1] = (aa[0][0]*aa[2][2]-aa[2][0]*aa[0][2])/det;
        bb[1][2] = (aa[1][0]*aa[0][2]-aa[0][0]*aa[1][2])/det;
        bb[2][0] = (aa[1][0]*aa[2][1]-aa[2][0]*aa[1][1])/det;
        bb[2][1] = (aa[2][0]*aa[0][1]-aa[0][0]*aa[2][1])/det;
        bb[2][2] = (aa[0][0]*aa[1][1]-aa[1][0]*aa[0][1])/det;

        for (ii = 0; ii < 3; ii++) {
            for (n = StringStart; n <= StringEnd; n++) {
                  cc[ii] = cc[ii]+a1[n][ii]*b1[n];
            }
        }

        for (ii = 0; ii < 3; ii++) {
            for (jj = 0; jj < 3; jj++) {
        c[ii]=c[ii]+bb[jj][ii]*cc[jj];
            }
        }

    } /* end of else */
    return det_flag;

    }

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

}