// Created on: 1993-03-17
// Created by: Laurent BUCHARD
// Copyright (c) 1993-1999 Matra Datavision
// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#include <IntSurf_PntOn2S.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <math_FunctionSetRoot.hxx>
#include <StdFail_NotDone.hxx>
#include <GeomAbs_SurfaceType.hxx>
#include <Precision.hxx>

//=======================================================================
//function : IsSingular
//purpose  :    Returns TRUE if vectors theDU || theDV or if at least one
//            of them has null-magnitude.
//              theSqLinTol is square of linear tolerance.
//              theAngTol is angular tolerance.
//=======================================================================
static Standard_Boolean IsSingular( const gp_Vec& theDU,
                                    const gp_Vec& theDV,
                                    const Standard_Real theSqLinTol,
                                    const Standard_Real theAngTol)
{
  gp_Vec aDU(theDU), aDV(theDV);

  const Standard_Real aSqMagnDU = aDU.SquareMagnitude(),
                      aSqMagnDV = aDV.SquareMagnitude();

  if(aSqMagnDU < theSqLinTol)
    return Standard_True;

  aDU.Divide(sqrt(aSqMagnDU));

  if(aSqMagnDV < theSqLinTol)
    return Standard_True;

  aDV.Divide(sqrt(aSqMagnDV));

  //Here aDU and aDV vectors have magnitude 1.0.

  if(aDU.Crossed(aDV).SquareMagnitude() < theAngTol*theAngTol)
    return Standard_True;

  return Standard_False;
}

//=======================================================================
//function : SingularProcessing
//purpose  :    Computes 2D-representation (in UV-coordinates) of 
//            theTg3D vector on the surface in case when
//            theDU.Crossed(theDV).Magnitude() == 0.0. Stores result in
//            theTg2D variable.
//              theDU and theDV are vectors of 1st derivative
//            (with respect to U and V variables correspondingly).
//              If theIsTo3DTgCompute == TRUE then theTg3D has not been 
//            defined yet (it should be computed).
//              theLinTol is SQUARE of the tolerance.
//
//Algorithm:
//              Condition
//                  Tg=theDU*theTg2D.X()+theDV*theTg2D.Y()  
//            has to be satisfied strictly.
//              More over, vector Tg has to be NORMALIZED
//            (if theIsTo3DTgCompute == TRUE then new computed vector will
//            always have magnitude 1.0).
//=======================================================================
static Standard_Boolean SingularProcessing( const gp_Vec& theDU,
                                            const gp_Vec& theDV,
                                            const Standard_Boolean theIsTo3DTgCompute,
                                            const Standard_Real theLinTol,
                                            const Standard_Real theAngTol,
                                            gp_Vec& theTg3D,
                                            gp_Vec2d& theTg2D)
{
  //Attention: @ \sin theAngTol \approx theAngTol @ (for cross-product)

  //Really, vector theTg3D has to be normalized (if theIsTo3DTgCompute == FALSE).
  const Standard_Real aSQTan = theTg3D.SquareMagnitude();

  const Standard_Real aSqMagnDU = theDU.SquareMagnitude(),
                      aSqMagnDV = theDV.SquareMagnitude();

  //There are some reasons of singularity

  //1.
  if((aSqMagnDU < theLinTol) && (aSqMagnDV < theLinTol))
  {
    //For future, this case can be processed as same as in case of 
    //osculating surfaces (expanding in Taylor series). Here,
    //we return only.

    return Standard_False;
  }

  //2.
  if(aSqMagnDU < theLinTol)
  {
    //In this case, theTg3D vector will be parallel with theDV.
    //Its true direction shall be precised later (the algorithm is
    //based on array of Walking-points).
    
    if(theIsTo3DTgCompute)
    {
      //theTg3D will be normalized. Its magnitude is
      const Standard_Real aTgMagn = 1.0;

      const Standard_Real aNorm = sqrt(aSqMagnDV);
      theTg3D = theDV.Divided(aNorm);
      theTg2D.SetCoord(0.0, aTgMagn/aNorm);
    }
    else
    {
      //theTg3D is already defined.
      //Here we check only, if this tangent is parallel to theDV.

      if(theDV.Crossed(theTg3D).SquareMagnitude() < 
                    theAngTol*theAngTol*aSqMagnDV*aSQTan)
      {
        //theTg3D is parallel to theDV
        
        //Use sign "+" if theTg3D and theDV are codirectional
        //and sign "-" if opposite
        const Standard_Real aDP = theTg3D.Dot(theDV);
        theTg2D.SetCoord(0.0, Sign(sqrt(aSQTan/aSqMagnDV), aDP));
      }
      else
      {
        //theTg3D is not parallel to theDV
        //It is abnormal

        return Standard_False;
      }
    }

    return Standard_True;
  }

  //3.
  if(aSqMagnDV < theLinTol)
  {
    //In this case, theTg3D vector will be parallel with theDU.
    //Its true direction shall be precised later (the algorithm is
    //based on array of Walking-points).
    
    if(theIsTo3DTgCompute)
    {
      //theTg3D will be normalized. Its magnitude is
      const Standard_Real aTgMagn = 1.0;

      const Standard_Real aNorm = sqrt(aSqMagnDU);
      theTg3D = theDU.Divided(aNorm);
      theTg2D.SetCoord(aTgMagn/aNorm, 0.0);
    }
    else
    {
      //theTg3D is already defined.
      //Here we check only, if this tangent is parallel to theDU.

      if(theDU.Crossed(theTg3D).SquareMagnitude() < 
                    theAngTol*theAngTol*aSqMagnDU*aSQTan)
      {
        //theTg3D is parallel to theDU

        //Use sign "+" if theTg3D and theDU are codirectional
        //and sign "-" if opposite
        const Standard_Real aDP = theTg3D.Dot(theDU);
        theTg2D.SetCoord(Sign(sqrt(aSQTan/aSqMagnDU), aDP), 0.0);
      }
      else
      {
        //theTg3D is not parallel to theDU
        //It is abnormal

        return Standard_False;
      }
    }

    return Standard_True;
  }

  //4. If aSqMagnDU > 0.0 && aSqMagnDV > 0.0 but theDV || theDU.

  const Standard_Real aLenU = sqrt(aSqMagnDU),
                      aLenV = sqrt(aSqMagnDV);

  //aLenSum > 0.0 definitely
  const Standard_Real aLenSum = aLenU + aLenV;

  if(theDV.Dot(theDU) > 0.0)
  {
    //Vectors theDV and theDU are codirectional.

    if(theIsTo3DTgCompute)
    {
      theTg2D.SetCoord(1.0/aLenSum, 1.0/aLenSum);
      theTg3D = theDU*theTg2D.X() + theDV*theTg2D.Y();
    }
    else
    {
      //theTg3D is already defined.
      //Here we check only, if this tangent is parallel to theDU
      //(and theDV together).

      if(theDU.Crossed(theTg3D).SquareMagnitude() < 
                    theAngTol*theAngTol*aSqMagnDU*aSQTan)
      {
        //theTg3D is parallel to theDU

        const Standard_Real aDP = theTg3D.Dot(theDU);
        const Standard_Real aLenTg = Sign(sqrt(aSQTan), aDP);
        theTg2D.SetCoord(aLenTg/aLenSum, aLenTg/aLenSum);
      }
      else
      {
        //theTg3D is not parallel to theDU
        //It is abnormal

        return Standard_False;
      }
    }
  }
  else
  {
    //Vectors theDV and theDU are opposite.

    if(theIsTo3DTgCompute)
    {
      //Here we chose theDU as direction of theTg3D.
      //True direction shall be precised later (the algorithm is
      //based on array of Walking-points).

      theTg2D.SetCoord(1.0/aLenSum, -1.0/aLenSum);
      theTg3D = theDU*theTg2D.X() + theDV*theTg2D.Y();
    }
    else
    {
      //theTg3D is already defined.
      //Here we check only, if this tangent is parallel to theDU
      //(and theDV together).

      if(theDU.Crossed(theTg3D).SquareMagnitude() < 
                    theAngTol*theAngTol*aSqMagnDU*aSQTan)
      {
        //theTg3D is parallel to theDU

        const Standard_Real aDP = theTg3D.Dot(theDU);
        const Standard_Real aLenTg = Sign(sqrt(aSQTan), aDP);
        theTg2D.SetCoord(aLenTg/aLenSum, -aLenTg/aLenSum);
      }
      else
      {
        //theTg3D is not parallel to theDU
        //It is abnormal

        return Standard_False;
      }
    }
  }

  return Standard_True;
}

//=======================================================================
//function : NonSingularProcessing
//purpose  :    Computes 2D-representation (in UV-coordinates) of 
//            theTg3D vector on the surface in case when
//            theDU.Crossed(theDV).Magnitude() > 0.0. Stores result in
//            theTg2D variable.
//              theDU and theDV are vectors of 1st derivative
//            (with respect to U and V variables correspondingly).
//              theLinTol is SQUARE of the tolerance.
//
//Algorithm:
//              Condition
//                  Tg=theDU*theTg2D.X()+theDV*theTg2D.Y()  
//            has to be satisfied strictly.
//              More over, vector Tg has always to be NORMALIZED.
//=======================================================================
static Standard_Boolean NonSingularProcessing(const gp_Vec& theDU,
                                              const gp_Vec& theDV,
                                              const gp_Vec& theTg3D,
                                              const Standard_Real theLinTol,
                                              const Standard_Real theAngTol,
                                              gp_Vec2d& theTg2D)
{
  const gp_Vec aNormal = theDU.Crossed(theDV);
  const Standard_Real aSQMagn = aNormal.SquareMagnitude();

  if(IsSingular(theDU, theDV, theLinTol, theAngTol))
  {
    gp_Vec aTg(theTg3D);
    return 
      SingularProcessing(theDU, theDV, Standard_False,
                          theLinTol, theAngTol, aTg, theTg2D);
  }

  //If @\vec{T}=\vec{A}*U+\vec{B}*V@ then 

  //  \left\{\begin{matrix}
  //  \vec{A} \times \vec{T} = (\vec{A} \times \vec{B})*V 
  //  \vec{B} \times \vec{T} = (\vec{B} \times \vec{A})*U 
  //  \end{matrix}\right.

  //From here, values of U and V can be found very easily
  //(if @\left \| \vec{A} \times \vec{B} \right \| > 0.0 @,
  //else it is singular case). 

  const gp_Vec aTgU(theTg3D.Crossed(theDU)), aTgV(theTg3D.Crossed(theDV));
  const Standard_Real aDeltaU = aTgV.SquareMagnitude()/aSQMagn;
  const Standard_Real aDeltaV = aTgU.SquareMagnitude()/aSQMagn;

  theTg2D.SetCoord(Sign(sqrt(aDeltaU), aTgV.Dot(aNormal)), -Sign(sqrt(aDeltaV), aTgU.Dot(aNormal)));

  return Standard_True;
}

//--------------------------------------------------------------------------------
ApproxInt_ImpPrmSvSurfaces::ApproxInt_ImpPrmSvSurfaces( const TheISurface& ISurf
						       ,const ThePSurface& PSurf):
       MyIsTangent(Standard_False),
       MyHasBeenComputed(Standard_False),
       MyIsTangentbis(Standard_False),
       MyHasBeenComputedbis(Standard_False),
       MyImplicitFirst(Standard_True),
       MyZerImpFunc(PSurf,ISurf)
{ 
  SetUseSolver(Standard_True);
}
//--------------------------------------------------------------------------------
ApproxInt_ImpPrmSvSurfaces::ApproxInt_ImpPrmSvSurfaces( const ThePSurface& PSurf
						       ,const TheISurface& ISurf):
       MyIsTangent(Standard_False),
       MyHasBeenComputed(Standard_False),
       MyIsTangentbis(Standard_False),
       MyHasBeenComputedbis(Standard_False),
       MyImplicitFirst(Standard_False),
       MyZerImpFunc(PSurf,ISurf)
{ 
  SetUseSolver(Standard_True);
}
//--------------------------------------------------------------------------------
void ApproxInt_ImpPrmSvSurfaces::Pnt(const Standard_Real u1,
				     const Standard_Real v1,
				     const Standard_Real u2,
				     const Standard_Real v2,
				     gp_Pnt& P) { 
  gp_Pnt aP;
  gp_Vec aT;
  gp_Vec2d aTS1,aTS2;
  Standard_Real tu1=u1;
  Standard_Real tu2=u2;
  Standard_Real tv1=v1;
  Standard_Real tv2=v2;
  this->Compute(tu1,tv1,tu2,tv2,aP,aT,aTS1,aTS2);
  P=MyPnt;
}
//--------------------------------------------------------------------------------
Standard_Boolean ApproxInt_ImpPrmSvSurfaces::Tangency(const Standard_Real u1,
						      const Standard_Real v1,
						      const Standard_Real u2,
						      const Standard_Real v2,
						      gp_Vec& T) { 
  gp_Pnt aP;
  gp_Vec aT;
  gp_Vec2d aTS1,aTS2;
  Standard_Real tu1=u1;
  Standard_Real tu2=u2;
  Standard_Real tv1=v1;
  Standard_Real tv2=v2;
  Standard_Boolean t=this->Compute(tu1,tv1,tu2,tv2,aP,aT,aTS1,aTS2);
  T=MyTg;
  return(t);
}
//--------------------------------------------------------------------------------
Standard_Boolean ApproxInt_ImpPrmSvSurfaces::TangencyOnSurf1(const Standard_Real u1,
							     const Standard_Real v1,
							     const Standard_Real u2,
							     const Standard_Real v2,
							     gp_Vec2d& T) { 
  gp_Pnt aP;
  gp_Vec aT;
  gp_Vec2d aTS1,aTS2;
  Standard_Real tu1=u1;
  Standard_Real tu2=u2;
  Standard_Real tv1=v1;
  Standard_Real tv2=v2;
  Standard_Boolean t=this->Compute(tu1,tv1,tu2,tv2,aP,aT,aTS1,aTS2);
  T=MyTguv1;
  return(t);
}
//--------------------------------------------------------------------------------
Standard_Boolean ApproxInt_ImpPrmSvSurfaces::TangencyOnSurf2(const Standard_Real u1,
							     const Standard_Real v1,
							     const Standard_Real u2,
							     const Standard_Real v2,
							     gp_Vec2d& T) { 
  gp_Pnt aP;
  gp_Vec aT;
  gp_Vec2d aTS1,aTS2;
  Standard_Real tu1=u1;
  Standard_Real tu2=u2;
  Standard_Real tv1=v1;
  Standard_Real tv2=v2;
  Standard_Boolean t=this->Compute(tu1,tv1,tu2,tv2,aP,aT,aTS1,aTS2);
  T=MyTguv2;
  return(t);
}

//=======================================================================
//function : Compute
//purpose  :    Computes point on curve, 3D and 2D-tangents of a curve and
//            parameters on the surfaces.
//=======================================================================
Standard_Boolean ApproxInt_ImpPrmSvSurfaces::Compute( Standard_Real& u1,
                                                      Standard_Real& v1,
                                                      Standard_Real& u2,
                                                      Standard_Real& v2,
                                                      gp_Pnt& P,
                                                      gp_Vec& Tg,
                                                      gp_Vec2d& Tguv1,
                                                      gp_Vec2d& Tguv2)
{ 
  const IntSurf_Quadric&  aQSurf = MyZerImpFunc.ISurface();
  const ThePSurface&      aPSurf = MyZerImpFunc.PSurface();
  gp_Vec2d& aQuadTg = MyImplicitFirst ? Tguv1 : Tguv2;
  gp_Vec2d& aPrmTg = MyImplicitFirst ? Tguv2 : Tguv1;

  //for square
  const Standard_Real aNullValue =  Precision::Approximation()*
                                    Precision::Approximation(),
                      anAngTol = Precision::Angular();

  Standard_Real tu1=u1;
  Standard_Real tu2=u2;
  Standard_Real tv1=v1;
  Standard_Real tv2=v2;
  
  if(MyHasBeenComputed) { 
    if(  (MyParOnS1.X()==u1)&&(MyParOnS1.Y()==v1)
       &&(MyParOnS2.X()==u2)&&(MyParOnS2.Y()==v2)) {
      return(MyIsTangent);
    }
    else if(MyHasBeenComputedbis == Standard_False) { 
      MyTgbis         = MyTg;
      MyTguv1bis      = MyTguv1;
      MyTguv2bis      = MyTguv2;
      MyPntbis        = MyPnt;
      MyParOnS1bis    = MyParOnS1;
      MyParOnS2bis    = MyParOnS2;
      MyIsTangentbis  = MyIsTangent;
      MyHasBeenComputedbis = MyHasBeenComputed; 
    }
  }

  if(MyHasBeenComputedbis) { 
    if(  (MyParOnS1bis.X()==u1)&&(MyParOnS1bis.Y()==v1)
       &&(MyParOnS2bis.X()==u2)&&(MyParOnS2bis.Y()==v2)) {

      gp_Vec            TV(MyTg);
      gp_Vec2d          TV1(MyTguv1);
      gp_Vec2d          TV2(MyTguv2);
      gp_Pnt            TP(MyPnt);
      gp_Pnt2d          TP1(MyParOnS1);
      gp_Pnt2d          TP2(MyParOnS2);
      Standard_Boolean  TB=MyIsTangent;

      MyTg        = MyTgbis;
      MyTguv1     = MyTguv1bis;
      MyTguv2     = MyTguv2bis;
      MyPnt       = MyPntbis;
      MyParOnS1   = MyParOnS1bis;
      MyParOnS2   = MyParOnS2bis;
      MyIsTangent = MyIsTangentbis;

      MyTgbis         = TV;
      MyTguv1bis      = TV1;
      MyTguv2bis      = TV2;
      MyPntbis        = TP;
      MyParOnS1bis    = TP1;
      MyParOnS2bis    = TP2;
      MyIsTangentbis  = TB;

      return(MyIsTangent);
    }
  }

  math_Vector X(1,2);
  math_Vector BornInf(1,2), BornSup(1,2), Tolerance(1,2);
  //--- ThePSurfaceTool::GetResolution(aPSurf,Tolerance(1),Tolerance(2));
  Tolerance(1) = 1.0e-8; Tolerance(2) = 1.0e-8;
  Standard_Real binfu,bsupu,binfv,bsupv;
  binfu = ThePSurfaceTool::FirstUParameter(aPSurf);
  binfv = ThePSurfaceTool::FirstVParameter(aPSurf);
  bsupu = ThePSurfaceTool::LastUParameter(aPSurf);
  bsupv = ThePSurfaceTool::LastVParameter(aPSurf);
  BornInf(1) = binfu; BornSup(1) = bsupu; 
  BornInf(2) = binfv; BornSup(2) = bsupv;
  Standard_Real TranslationU = 0., TranslationV = 0.;
  
  if (!FillInitialVectorOfSolution(u1, v1, u2, v2,
                                   binfu, bsupu, binfv, bsupv,
                                   X,
                                   TranslationU, TranslationV))
  {
    MyIsTangent=MyIsTangentbis=Standard_False;
    MyHasBeenComputed = MyHasBeenComputedbis = Standard_False;
    return(Standard_False);
  }

  Standard_Boolean aRsnldIsDone = Standard_False;
  Standard_Real PourTesterU = X(1);
  Standard_Real PourTesterV = X(2);
  if (GetUseSolver())
  {
    math_FunctionSetRoot  Rsnld(MyZerImpFunc);
    Rsnld.SetTolerance(Tolerance);
    Rsnld.Perform(MyZerImpFunc, X, BornInf, BornSup);
    aRsnldIsDone = Rsnld.IsDone();
    if (aRsnldIsDone)
      Rsnld.Root(X);
  }
  if(aRsnldIsDone || !GetUseSolver()) 
  {
    MyHasBeenComputed = Standard_True;
    
    Standard_Real DistAvantApresU = Abs(PourTesterU-X(1));
    Standard_Real DistAvantApresV = Abs(PourTesterV-X(2));
    
    MyPnt = P = ThePSurfaceTool::Value(aPSurf, X(1), X(2));
    
    if( (DistAvantApresV <= 0.001 ) &&
        (DistAvantApresU <= 0.001 ))
    {
      gp_Vec aD1uPrm,aD1vPrm;
      gp_Vec aD1uQuad,aD1vQuad;

      if(MyImplicitFirst)
      { 
        u2 = X(1)-TranslationU;
        v2 = X(2)-TranslationV;

        if(aQSurf.TypeQuadric() != GeomAbs_Plane)
        { 
          while(u1-tu1>M_PI)  u1-=M_PI+M_PI;
          while(tu1-u1>M_PI)  u1+=M_PI+M_PI;
        }

        MyParOnS1.SetCoord(tu1,tv1);
        MyParOnS2.SetCoord(tu2,tv2);

        gp_Pnt aP2;

        ThePSurfaceTool::D1(aPSurf, X(1), X(2), P, aD1uPrm, aD1vPrm);
        aQSurf.D1(u1,v1, aP2, aD1uQuad, aD1vQuad);

        //Middle-point of P-P2 segment
        P.BaryCenter(1.0, aP2, 1.0);
      }
      else
      {
        u1 = X(1)-TranslationU;
        v1 = X(2)-TranslationV;
        //aQSurf.Parameters(P, u2, v2);
        if(aQSurf.TypeQuadric() != GeomAbs_Plane)
        {
          while(u2-tu2>M_PI)  u2-=M_PI+M_PI;
          while(tu2-u2>M_PI)  u2+=M_PI+M_PI;
        }

        MyParOnS1.SetCoord(tu1,tv1);
        MyParOnS2.SetCoord(tu2,tu2);

        gp_Pnt aP2;
        ThePSurfaceTool::D1(aPSurf, X(1), X(2), P, aD1uPrm, aD1vPrm);

        aQSurf.D1(u2, v2, aP2, aD1uQuad, aD1vQuad);

        //Middle-point of P-P2 segment
        P.BaryCenter(1.0, aP2, 1.0);
      }

      MyPnt = P;

      //Normals to the surfaces
      gp_Vec  aNormalPrm(aD1uPrm.Crossed(aD1vPrm)), 
              aNormalImp(aQSurf.Normale(MyPnt));

      const Standard_Real aSQMagnPrm = aNormalPrm.SquareMagnitude(),
                          aSQMagnImp = aNormalImp.SquareMagnitude();

      Standard_Boolean  isPrmSingular = Standard_False,
                        isImpSingular = Standard_False;

      if(IsSingular(aD1uPrm, aD1vPrm, aNullValue, anAngTol))
      {
        isPrmSingular = Standard_True;
        if(!SingularProcessing(aD1uPrm, aD1vPrm, Standard_True,
                                aNullValue, anAngTol, Tg, aPrmTg))
        {
          MyIsTangent = Standard_False;
          MyHasBeenComputed = MyHasBeenComputedbis = Standard_False;
          return Standard_False;
        }

        MyTg = Tg;
      }
      else
      {
        aNormalPrm.Divide(sqrt(aSQMagnPrm));
      }

      //Analogically for implicit surface
      if(aSQMagnImp < aNullValue)
      {
        isImpSingular = Standard_True;

        if(!SingularProcessing(aD1uQuad, aD1vQuad, !isPrmSingular,
                                  aNullValue, anAngTol, Tg, aQuadTg))
        {
          MyIsTangent = Standard_False;
          MyHasBeenComputed = MyHasBeenComputedbis = Standard_False;
          return Standard_False;
        }

        MyTg = Tg;
      }
      else
      {
        aNormalImp.Divide(sqrt(aSQMagnImp));
      }

      if(isImpSingular && isPrmSingular)
      {
        //All is OK. All abnormal cases were processed above.

        MyTguv1 = Tguv1;
        MyTguv2 = Tguv2;

        MyIsTangent=Standard_True;
        return MyIsTangent;
      }
      else if(!(isImpSingular || isPrmSingular))
      {
        //Processing pure non-singular case
        //(3D- and 2D-tangents are still not defined)

        //Ask to pay attention to the fact that here
        //aNormalImp and aNormalPrm are normalized.
        //Therefore, @ \left \| \vec{Tg} \right \| = 0.0 @
        //if and only if (aNormalImp || aNormalPrm).
        Tg = aNormalImp.Crossed(aNormalPrm);
      }

      const Standard_Real aSQMagnTg = Tg.SquareMagnitude();

      if(aSQMagnTg < aNullValue)
      {
        MyIsTangent = Standard_False;
        MyHasBeenComputed = MyHasBeenComputedbis = Standard_False;
        return Standard_False;
      }

      //Normalize Tg vector
      Tg.Divide(sqrt(aSQMagnTg));
      MyTg = Tg;

      if(!isPrmSingular)
      {
        //If isPrmSingular==TRUE then aPrmTg has already been computed.

        if(!NonSingularProcessing(aD1uPrm, aD1vPrm, Tg, aNullValue, anAngTol, aPrmTg))
        {
          MyIsTangent = Standard_False;
          MyHasBeenComputed = MyHasBeenComputedbis = Standard_False;
          return Standard_False;
        }
      }

      if(!isImpSingular)
      {
        //If isImpSingular==TRUE then aQuadTg has already been computed.

        if(!NonSingularProcessing(aD1uQuad, aD1vQuad, Tg, aNullValue, anAngTol, aQuadTg))
        {
          MyIsTangent = Standard_False;
          MyHasBeenComputed = MyHasBeenComputedbis = Standard_False;
          return Standard_False;
        }
      }
        
      MyTguv1 = Tguv1;
      MyTguv2 = Tguv2;

      MyIsTangent=Standard_True;

#ifdef OCCT_DEBUG
      //cout << "+++++++++++++++++  ApproxInt_ImpPrmSvSurfaces::Compute(...)  ++++++++++" << endl;
      //printf( "P2d_1(%+10.20f, %+10.20f); P2d_2(%+10.20f, %+10.20f)\n"
      //        "P(%+10.20f, %+10.20f, %+10.20f);\n"
      //        "Tg = {%+10.20f, %+10.20f, %+10.20f};\n"
      //        "Tguv1 = {%+10.20f, %+10.20f};\n"
      //        "Tguv2 = {%+10.20f, %+10.20f}\n",
      //        u1, v1, u2, v2,
      //        P.X(), P.Y(), P.Z(),
      //        Tg.X(), Tg.Y(), Tg.Z(),
      //        Tguv1.X(), Tguv1.Y(), Tguv2.X(), Tguv2.Y());
      //cout << "-----------------------------------------------------------------------" << endl;
#endif

      return Standard_True; 
    }
    else { 
      //-- cout<<" ApproxInt_ImpImpSvSurfaces.gxx : Distance apres recadrage Trop Grande "<<endl;
    
      MyIsTangent=Standard_False;
      MyHasBeenComputed = MyHasBeenComputedbis = Standard_False;
      return Standard_False;
    }
  }
  else { 
    MyIsTangent = Standard_False;
    MyHasBeenComputed = MyHasBeenComputedbis = Standard_False;
    return Standard_False;
  }
}

//=======================================================================
//function : SeekPoint
//purpose  :    Computes point on curve and
//            parameters on the surfaces.
//=======================================================================
Standard_Boolean ApproxInt_ImpPrmSvSurfaces::SeekPoint(const Standard_Real u1,
                                                       const Standard_Real v1,
                                                       const Standard_Real u2,
                                                       const Standard_Real v2,
                                                       IntSurf_PntOn2S& Point) { 
  gp_Pnt aP;
  gp_Vec aT;
  gp_Vec2d aTS1,aTS2;
  const IntSurf_Quadric&  aQSurf = MyZerImpFunc.ISurface();
  const ThePSurface&      aPSurf = MyZerImpFunc.PSurface();

  math_Vector X(1,2);
  math_Vector BornInf(1,2), BornSup(1,2), Tolerance(1,2);
  //--- ThePSurfaceTool::GetResolution(aPSurf,Tolerance(1),Tolerance(2));
  Tolerance(1) = 1.0e-8; Tolerance(2) = 1.0e-8;
  Standard_Real binfu,bsupu,binfv,bsupv;
  binfu = ThePSurfaceTool::FirstUParameter(aPSurf);
  binfv = ThePSurfaceTool::FirstVParameter(aPSurf);
  bsupu = ThePSurfaceTool::LastUParameter(aPSurf);
  bsupv = ThePSurfaceTool::LastVParameter(aPSurf);
  BornInf(1) = binfu; BornSup(1) = bsupu; 
  BornInf(2) = binfv; BornSup(2) = bsupv;
  Standard_Real TranslationU = 0., TranslationV = 0.;
  
  if (!FillInitialVectorOfSolution(u1, v1, u2, v2,
                                   binfu, bsupu, binfv, bsupv,
                                   X,
                                   TranslationU, TranslationV))
    return Standard_False;

  Standard_Real NewU1, NewV1, NewU2, NewV2;
  
  math_FunctionSetRoot  Rsnld(MyZerImpFunc);
  Rsnld.SetTolerance(Tolerance);
  Rsnld.Perform(MyZerImpFunc,X,BornInf,BornSup);
  if(Rsnld.IsDone()) { 
    MyHasBeenComputed = Standard_True;
    Rsnld.Root(X);
  
    MyPnt = ThePSurfaceTool::Value(aPSurf, X(1), X(2));
    
    if(MyImplicitFirst)
    { 
      NewU2 = X(1)-TranslationU;
      NewV2 = X(2)-TranslationV;

      aQSurf.Parameters(MyPnt, NewU1, NewV1);
      //adjust U
      if (aQSurf.TypeQuadric() != GeomAbs_Plane)
      {
        Standard_Real sign = (NewU1 > u1)? -1 : 1;
        while (Abs(u1 - NewU1) > M_PI)
          NewU1 += sign*(M_PI+M_PI);
      }
    }
    else
    {
      NewU1 = X(1)-TranslationU;
      NewV1 = X(2)-TranslationV;

      aQSurf.Parameters(MyPnt, NewU2, NewV2);
      //adjust U
      if (aQSurf.TypeQuadric() != GeomAbs_Plane)
      {
        Standard_Real sign = (NewU2 > u2)? -1 : 1;
        while (Abs(u2 - NewU2) > M_PI)
          NewU2 += sign*(M_PI+M_PI);
      }
    }
  }
  else
    return Standard_False;
  
  Point.SetValue(MyPnt, NewU1, NewV1, NewU2, NewV2);
  return Standard_True;
}
//--------------------------------------------------------------------------------

Standard_Boolean
ApproxInt_ImpPrmSvSurfaces::FillInitialVectorOfSolution(const Standard_Real u1,
                                                        const Standard_Real v1,
                                                        const Standard_Real u2,
                                                        const Standard_Real v2,
                                                        const Standard_Real binfu,
                                                        const Standard_Real bsupu,
                                                        const Standard_Real binfv,
                                                        const Standard_Real bsupv,
                                                        math_Vector& X,
                                                        Standard_Real& TranslationU,
                                                        Standard_Real& TranslationV)
{
  const ThePSurface&      aPSurf = MyZerImpFunc.PSurface();

  math_Vector F(1,1);

  TranslationU = 0.0;
  TranslationV = 0.0;

  if(MyImplicitFirst) { 
    if(u2<binfu-0.0000000001) { 
      if(ThePSurfaceTool::IsUPeriodic(aPSurf)) { 
	Standard_Real d = ThePSurfaceTool::UPeriod(aPSurf);
	do {  TranslationU+=d; } while(u2+TranslationU < binfu);
      }
      else 
	return(Standard_False);
    }
    else if(u2>bsupu+0.0000000001) { 
      if(ThePSurfaceTool::IsUPeriodic(aPSurf)) { 
	Standard_Real d = ThePSurfaceTool::UPeriod(aPSurf);
	do { TranslationU-=d; } while(u2+TranslationU > bsupu);
      }
      else
	return(Standard_False);
    }
    if(v2<binfv-0.0000000001) { 
      if(ThePSurfaceTool::IsVPeriodic(aPSurf)) { 
	Standard_Real d = ThePSurfaceTool::VPeriod(aPSurf);
	do { TranslationV+=d; }	while(v2+TranslationV < binfv);
      }
      else
	return(Standard_False);
    }
    else if(v2>bsupv+0.0000000001) { 
      if(ThePSurfaceTool::IsVPeriodic(aPSurf)) { 
	Standard_Real d = ThePSurfaceTool::VPeriod(aPSurf);
	do { TranslationV-=d; } while(v2+TranslationV > bsupv);
      }
      else
	return(Standard_False);
    }
    X(1) = u2+TranslationU; 
    X(2) = v2+TranslationV;
  }
  else { 
    if(u1<binfu-0.0000000001) { 
      if(ThePSurfaceTool::IsUPeriodic(aPSurf)) { 
	Standard_Real d = ThePSurfaceTool::UPeriod(aPSurf);
	do {  TranslationU+=d; 	} while(u1+TranslationU < binfu);
      }
      else
	return(Standard_False);
    }
    else if(u1>bsupu+0.0000000001) { 
      if(ThePSurfaceTool::IsUPeriodic(aPSurf)) { 
	Standard_Real d = ThePSurfaceTool::UPeriod(aPSurf);
	do { TranslationU-=d; } while(u1+TranslationU > bsupu);
      }
      else
	return(Standard_False);
    }
    if(v1<binfv-0.0000000001) { 
      if(ThePSurfaceTool::IsVPeriodic(aPSurf)) { 
	Standard_Real d = ThePSurfaceTool::VPeriod(aPSurf);
	do { TranslationV+=d; }	while(v1+TranslationV < binfv);
      }
      else
	return(Standard_False);
    }
    else if(v1>bsupv+0.0000000001) { 
      if(ThePSurfaceTool::IsVPeriodic(aPSurf)) { 
	Standard_Real d = ThePSurfaceTool::VPeriod(aPSurf);
	do { TranslationV-=d; }	while(v1+TranslationV > bsupv);
      }
      else
	return(Standard_False);
    }
    X(1) = u1+TranslationU;
    X(2) = v1+TranslationV;
  }
  
  //----------------------------------------------------
  //Make a small step from boundaries in order to avoid
  //finding "outboundaried" solution (Rsnld -> NotDone).
  if (GetUseSolver())
  {
    Standard_Real du = Max(Precision::Confusion(), ThePSurfaceTool::UResolution(aPSurf, Precision::Confusion()));
    Standard_Real dv = Max(Precision::Confusion(), ThePSurfaceTool::VResolution(aPSurf, Precision::Confusion()));
    if (X(1) - 0.0000000001 <= binfu) X(1) = X(1) + du;
    if (X(1) + 0.0000000001 >= bsupu) X(1) = X(1) - du;
    if (X(2) - 0.0000000001 <= binfv) X(2) = X(2) + dv;
    if (X(2) + 0.0000000001 >= bsupv) X(2) = X(2) - dv;
  }
  
  return Standard_True;
}
