// Created on: 1993-08-25
// Created by: Bruno DUMORTIER
// 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.

//  Modified by skv - Wed Aug 11 15:45:58 2004 OCC6272

#include <Standard_NoSuchObject.hxx>
#include <Standard_NotImplemented.hxx>
#include <ProjLib_ProjectedCurve.hxx>
#include <ProjLib_HCompProjectedCurve.hxx>
#include <ProjLib_ComputeApproxOnPolarSurface.hxx>
#include <ProjLib_ComputeApprox.hxx>
#include <ProjLib_Projector.hxx>
#include <Adaptor3d_Curve.hxx>
#include <Adaptor3d_Surface.hxx>
#include <Approx_CurveOnSurface.hxx>
#include <ProjLib_Plane.hxx>
#include <ProjLib_Cylinder.hxx>
#include <ProjLib_Cone.hxx>
#include <ProjLib_Sphere.hxx>
#include <ProjLib_Torus.hxx>
#include <Precision.hxx>
#include <Geom2d_BezierCurve.hxx>
#include <gp_Vec2d.hxx>
#include <StdFail_NotDone.hxx>
#include <Geom2dConvert_CompCurveToBSplineCurve.hxx>
#include <Geom2dConvert.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <GeomAbs_IsoType.hxx>
#include <Geom2d_Line.hxx>
#include <Geom2d_TrimmedCurve.hxx>
#include <GeomLib.hxx>
#include <Extrema_ExtPC.hxx>
#include <NCollection_DataMap.hxx>
#include <ElSLib.hxx>
#include <ElCLib.hxx>

IMPLEMENT_STANDARD_RTTIEXT(ProjLib_ProjectedCurve, Adaptor2d_Curve2d)

//=======================================================================
//function : ComputeTolU
//purpose  : 
//=======================================================================

static Standard_Real ComputeTolU(const Handle(Adaptor3d_Surface)& theSurf,
                                 const Standard_Real theTolerance)
{
  Standard_Real aTolU = theSurf->UResolution(theTolerance);
  if (theSurf->IsUPeriodic())
  {
    aTolU = Min(aTolU, 0.01*theSurf->UPeriod());
  }

  return aTolU;
}

//=======================================================================
//function : ComputeTolV
//purpose  : 
//=======================================================================

static Standard_Real ComputeTolV(const Handle(Adaptor3d_Surface)& theSurf,
                                 const Standard_Real theTolerance)
{
  Standard_Real aTolV = theSurf->VResolution(theTolerance);
  if (theSurf->IsVPeriodic())
  {
    aTolV = Min(aTolV, 0.01*theSurf->VPeriod());
  }

  return aTolV;
}

//=======================================================================
//function : IsoIsDeg
//purpose  : 
//=======================================================================

static Standard_Boolean IsoIsDeg  (const Adaptor3d_Surface& S,
				   const Standard_Real      Param,
				   const GeomAbs_IsoType    IT,
				   const Standard_Real      TolMin,
				   const Standard_Real      TolMax) 
{
    Standard_Real U1=0.,U2=0.,V1=0.,V2=0.,T;
    Standard_Boolean Along = Standard_True;
    U1 = S.FirstUParameter();
    U2 = S.LastUParameter();
    V1 = S.FirstVParameter();
    V2 = S.LastVParameter();
    gp_Vec D1U,D1V;
    gp_Pnt P;
    Standard_Real Step,D1NormMax;
    if (IT == GeomAbs_IsoV) 
    {
      Step = (U2 - U1)/10;
      D1NormMax=0.;
      for (T=U1;T<=U2;T=T+Step) 
      {
        S.D1(T,Param,P,D1U,D1V);
        D1NormMax=Max(D1NormMax,D1U.Magnitude());
      }

      if (D1NormMax >TolMax || D1NormMax < TolMin ) 
           Along = Standard_False;
    }
    else 
    {
      Step = (V2 - V1)/10;
      D1NormMax=0.;
      for (T=V1;T<=V2;T=T+Step) 
      {
	S.D1(Param,T,P,D1U,D1V);
        D1NormMax=Max(D1NormMax,D1V.Magnitude());
      }

      if (D1NormMax >TolMax || D1NormMax < TolMin ) 
           Along = Standard_False;


    }
    return Along;
}

//=======================================================================
//function : TrimC3d
//purpose  : 
//=======================================================================

static void TrimC3d(Handle(Adaptor3d_Curve)& myCurve,
                    Standard_Boolean* IsTrimmed,
                    const Standard_Real dt,
                    const gp_Pnt& Pole,
                    Standard_Integer* SingularCase,
                    const Standard_Integer NumberOfSingularCase,
                    const Standard_Real TolConf)
{
  Standard_Real f = myCurve->FirstParameter();
  Standard_Real l = myCurve->LastParameter();

  gp_Pnt P = myCurve->Value(f);

  if(P.Distance(Pole) <= TolConf) {
    IsTrimmed[0] = Standard_True;
    f = f+dt;
    myCurve = myCurve->Trim(f, l, Precision::Confusion());
    SingularCase[0] = NumberOfSingularCase;
  }
  
  P = myCurve->Value(l);
  if(P.Distance(Pole) <= TolConf) {
    IsTrimmed[1] = Standard_True;
    l = l-dt;
    myCurve = myCurve->Trim(f, l, Precision::Confusion());
    SingularCase[1] = NumberOfSingularCase;
  }
}

//=======================================================================
//function : ExtendC2d
//purpose  : 
//=======================================================================

static void ExtendC2d (Handle(Geom2d_BSplineCurve)& aRes,
                       const Standard_Real /*t*/,
                       const Standard_Real /*dt*/,
                       const Standard_Real u1,
                       const Standard_Real u2,
                       const Standard_Real v1,
                       const Standard_Real v2,
                       const Standard_Integer FirstOrLast,
                       const Standard_Integer NumberOfSingularCase)
{
  Standard_Real theParam = (FirstOrLast == 0)? aRes->FirstParameter()
    : aRes->LastParameter();

  gp_Pnt2d                              aPBnd;
  gp_Vec2d                              aVBnd;
  gp_Dir2d                              aDBnd;
  Handle(Geom2d_TrimmedCurve)           aSegment;
  Geom2dConvert_CompCurveToBSplineCurve aCompCurve(aRes, Convert_RationalC1);
  Standard_Real                         aTol = Precision::Confusion();

  aRes->D1(theParam, aPBnd, aVBnd);
  aDBnd.SetXY(aVBnd.XY());
  gp_Lin2d aLin(aPBnd, aDBnd); //line in direction of derivative

  gp_Pnt2d thePole;
  gp_Dir2d theBoundDir;
  switch (NumberOfSingularCase)
  {
  case 1:
    {
      thePole.SetCoord(u1, v1);
      theBoundDir.SetCoord(0., 1.);
      break;
    }
  case 2:
    {
      thePole.SetCoord(u2, v1);
      theBoundDir.SetCoord(0., 1.);
      break;
    }
  case 3:
    {
      thePole.SetCoord(u1, v1);
      theBoundDir.SetCoord(1., 0.);
      break;
    }
  case 4:
    {
      thePole.SetCoord(u1, v2);
      theBoundDir.SetCoord(1., 0.);
      break;
    }
  }
  gp_Lin2d BoundLin(thePole, theBoundDir); //one of the bounds of rectangle
  Standard_Real ParOnLin = 0.;
  if (theBoundDir.IsParallel(aDBnd, 100.*Precision::Angular()))
  {
    ParOnLin = ElCLib::Parameter(aLin, thePole);
  }
  else
  {
    Standard_Real U1x = BoundLin.Direction().X();
    Standard_Real U1y = BoundLin.Direction().Y();
    Standard_Real U2x = aLin.Direction().X();
    Standard_Real U2y = aLin.Direction().Y();
    Standard_Real Uo21x = aLin.Location().X() - BoundLin.Location().X();
    Standard_Real Uo21y = aLin.Location().Y() - BoundLin.Location().Y();

    Standard_Real D = U1y*U2x - U1x*U2y;

    ParOnLin = (Uo21y * U1x - Uo21x * U1y) / D; //parameter of intersection point
  }
  
  Handle(Geom2d_Line) aSegLine = new Geom2d_Line(aLin);
  aSegment = (FirstOrLast == 0)?
    new Geom2d_TrimmedCurve(aSegLine, ParOnLin, 0.) :
    new Geom2d_TrimmedCurve(aSegLine, 0., ParOnLin);
  
  Standard_Boolean anAfter = FirstOrLast != 0;
  aCompCurve.Add(aSegment, aTol, anAfter);
  aRes = aCompCurve.BSplineCurve();
}

//=======================================================================
//function : Project
//purpose  : 
//=======================================================================

static void Project(ProjLib_Projector& P, Handle(Adaptor3d_Curve)& C)
{
  GeomAbs_CurveType CType = C->GetType();
  switch (CType) {
    case GeomAbs_Line:
      P.Project(C->Line());
      break;
    case GeomAbs_Circle:
      P.Project(C->Circle());
      break;
    case GeomAbs_Ellipse:
      P.Project(C->Ellipse());
      break;
    case GeomAbs_Hyperbola:
      P.Project(C->Hyperbola());
      break;
    case GeomAbs_Parabola:
      P.Project(C->Parabola());
      break;
    case GeomAbs_BSplineCurve:
    case GeomAbs_BezierCurve:
    case GeomAbs_OffsetCurve:
    case GeomAbs_OtherCurve:    // try the approximation
      break;
    default:
      throw Standard_NoSuchObject(" ");
  }
}

//=======================================================================
//function : ProjLib_ProjectedCurve
//purpose  : 
//=======================================================================

ProjLib_ProjectedCurve::ProjLib_ProjectedCurve() :
  myTolerance(Precision::Confusion()),
  myDegMin(-1), myDegMax(-1),
  myMaxSegments(-1),
  myMaxDist(-1.),
  myBndPnt(AppParCurves_TangencyPoint)
{
}


//=======================================================================
//function : ProjLib_ProjectedCurve
//purpose  : 
//=======================================================================

ProjLib_ProjectedCurve::ProjLib_ProjectedCurve
(const Handle(Adaptor3d_Surface)& S) :
  myTolerance(Precision::Confusion()),
  myDegMin(-1), myDegMax(-1),
  myMaxSegments(-1),
  myMaxDist(-1.),
  myBndPnt(AppParCurves_TangencyPoint)
{
  Load(S);
}


//=======================================================================
//function : ProjLib_ProjectedCurve
//purpose  : 
//=======================================================================

ProjLib_ProjectedCurve::ProjLib_ProjectedCurve
(const Handle(Adaptor3d_Surface)& S,
 const Handle(Adaptor3d_Curve)& C) :
  myTolerance(Precision::Confusion()),
  myDegMin(-1), myDegMax(-1),
  myMaxSegments(-1),
  myMaxDist(-1.),
  myBndPnt(AppParCurves_TangencyPoint)
{
  Load(S);
  Perform(C);
}


//=======================================================================
//function : ProjLib_ProjectedCurve
//purpose  : 
//=======================================================================

ProjLib_ProjectedCurve::ProjLib_ProjectedCurve
(const Handle(Adaptor3d_Surface)& S,
 const Handle(Adaptor3d_Curve)&   C,
 const Standard_Real             Tol) :
  myTolerance(Max(Tol, Precision::Confusion())),
  myDegMin(-1), myDegMax(-1),
  myMaxSegments(-1),
  myMaxDist(-1.),
  myBndPnt(AppParCurves_TangencyPoint)
{
  Load(S);
  Perform(C);
}

//=======================================================================
//function : ShallowCopy
//purpose  : 
//=======================================================================

Handle(Adaptor2d_Curve2d) ProjLib_ProjectedCurve::ShallowCopy() const
{
  Handle(ProjLib_ProjectedCurve) aCopy = new ProjLib_ProjectedCurve();

  aCopy->myTolerance   = myTolerance;
  if (!mySurface.IsNull())
  {
    aCopy->mySurface = mySurface->ShallowCopy();
  }
  if (!myCurve.IsNull())
  {
    aCopy->myCurve = myCurve->ShallowCopy();
  }
  aCopy->myResult      = myResult;
  aCopy->myDegMin      = myDegMin;
  aCopy->myDegMax      = myDegMax;
  aCopy->myMaxSegments = myMaxSegments;
  aCopy->myMaxDist     = myMaxDist;
  aCopy->myBndPnt      = myBndPnt;

  return aCopy;
}

//=======================================================================
//function : Load
//purpose  : 
//=======================================================================

void ProjLib_ProjectedCurve::Load(const Handle(Adaptor3d_Surface)& S)
{
  mySurface = S ;
}

//=======================================================================
//function : Load
//purpose  : 
//=======================================================================

void ProjLib_ProjectedCurve::Load(const Standard_Real theTol)
{
  myTolerance = theTol;
}

//=======================================================================
//function : Perform
//purpose  : 
//=======================================================================

void ProjLib_ProjectedCurve::Perform(const Handle(Adaptor3d_Curve)& C)
{
  myTolerance = Max(myTolerance, Precision::Confusion());
  myCurve = C;
  Standard_Real FirstPar = C->FirstParameter();
  Standard_Real LastPar  = C->LastParameter();
  GeomAbs_SurfaceType SType = mySurface->GetType();
  GeomAbs_CurveType   CType = myCurve->GetType();
  Standard_Boolean isAnalyticalSurf = Standard_True;
  Standard_Boolean IsTrimmed[2] = { Standard_False, Standard_False };
  Standard_Integer SingularCase[2];
  const Standard_Real eps = 0.01;
  Standard_Real TolConf = Precision::Confusion();
  Standard_Real dt = (LastPar - FirstPar) * eps;
  Standard_Real U1 = 0.0, U2 = 0.0, V1 = 0.0, V2 = 0.0;
  U1 = mySurface->FirstUParameter();
  U2 = mySurface->LastUParameter();
  V1 = mySurface->FirstVParameter();
  V2 = mySurface->LastVParameter();

  switch (SType)
  {
    case GeomAbs_Plane:
      {
        ProjLib_Plane P(mySurface->Plane());
        Project(P,myCurve);
        myResult = P;
      }
      break;

    case GeomAbs_Cylinder:
      {
        ProjLib_Cylinder P(mySurface->Cylinder());
        Project(P,myCurve);
        myResult = P;
      }
      break;

    case GeomAbs_Cone:
      {
        ProjLib_Cone P(mySurface->Cone());
        Project(P,myCurve);
        myResult = P;
      }
      break;

    case GeomAbs_Sphere:
      {
        ProjLib_Sphere P(mySurface->Sphere());
        Project(P,myCurve);
        if ( P.IsDone())
        {
          // on met dans la pseudo-periode ( car Sphere n'est pas
          // periodique en V !)
          P.SetInBounds(myCurve->FirstParameter());
        }
        else
        {
          const Standard_Real Vmax = M_PI / 2.;
          const Standard_Real Vmin = -Vmax;
          const Standard_Real minang = 1.e-5 * M_PI;
          gp_Sphere aSph = mySurface->Sphere();
          Standard_Real anR = aSph.Radius();
          Standard_Real f = myCurve->FirstParameter();
          Standard_Real l = myCurve->LastParameter();

          gp_Pnt Pf = myCurve->Value(f);
          gp_Pnt Pl = myCurve->Value(l);
          gp_Pnt aLoc = aSph.Position().Location();
          Standard_Real maxdist = Max(Pf.Distance(aLoc), Pl.Distance(aLoc));
          TolConf = Max(anR * minang, Abs(anR - maxdist));

          //Surface has pole at V = Vmin and Vmax
          gp_Pnt Pole = mySurface->Value(U1, Vmin);
          TrimC3d(myCurve, IsTrimmed, dt, Pole, SingularCase, 3, TolConf);
          Pole = mySurface->Value(U1, Vmax);
          TrimC3d(myCurve, IsTrimmed, dt, Pole, SingularCase, 4, TolConf);
        }
        myResult = P;
      }
      break;

    case GeomAbs_Torus:
      {
        ProjLib_Torus P(mySurface->Torus());
        Project(P,myCurve);
        myResult = P;
      }
      break;

    case GeomAbs_BezierSurface:
    case GeomAbs_BSplineSurface:
      {
        isAnalyticalSurf = Standard_False;
        Standard_Real f, l;
        f = myCurve->FirstParameter();
        l = myCurve->LastParameter();
        dt = (l - f) * eps;

        const Adaptor3d_Surface& S = *mySurface;
        U1 = S.FirstUParameter();
        U2 = S.LastUParameter();
        V1 = S.FirstVParameter();
        V2 = S.LastVParameter();

        if(IsoIsDeg(S, U1, GeomAbs_IsoU, 0., myTolerance))
        {
          //Surface has pole at U = Umin
          gp_Pnt Pole = mySurface->Value(U1, V1);
          TrimC3d(myCurve, IsTrimmed, dt, Pole, SingularCase, 1, TolConf);
        }

        if(IsoIsDeg(S, U2, GeomAbs_IsoU, 0., myTolerance))
        {
          //Surface has pole at U = Umax
          gp_Pnt Pole = mySurface->Value(U2, V1);
          TrimC3d(myCurve, IsTrimmed, dt, Pole, SingularCase, 2, TolConf);
        }

        if(IsoIsDeg(S, V1, GeomAbs_IsoV, 0., myTolerance))
        {
          //Surface has pole at V = Vmin
          gp_Pnt Pole = mySurface->Value(U1, V1);
          TrimC3d(myCurve, IsTrimmed, dt, Pole, SingularCase, 3, TolConf);
        }

        if(IsoIsDeg(S, V2, GeomAbs_IsoV, 0., myTolerance))
        {
          //Surface has pole at V = Vmax
          gp_Pnt Pole = mySurface->Value(U1, V2);
          TrimC3d(myCurve, IsTrimmed, dt, Pole, SingularCase, 4, TolConf);
        }

        ProjLib_ComputeApproxOnPolarSurface polar;
        polar.SetTolerance(myTolerance);
        polar.SetDegree(myDegMin, myDegMax);
        polar.SetMaxSegments(myMaxSegments);
        polar.SetBndPnt(myBndPnt);
        polar.SetMaxDist(myMaxDist);
        polar.Perform(myCurve, mySurface); 

        Handle(Geom2d_BSplineCurve) aRes = polar.BSpline();

        if (!aRes.IsNull())
        {
          myTolerance = polar.Tolerance();
          if( (IsTrimmed[0] || IsTrimmed[1]))
          {
            if(IsTrimmed[0])
            {
              //Add segment before start of curve
              f = myCurve->FirstParameter();
              ExtendC2d(aRes, f, -dt, U1, U2, V1, V2, 0, SingularCase[0]);
            }
            if(IsTrimmed[1])
            {
              //Add segment after end of curve
              l = myCurve->LastParameter();
              ExtendC2d(aRes, l,  dt, U1, U2, V1, V2, 1, SingularCase[1]);
            }
            Handle(Geom2d_Curve) NewCurve2d;
            GeomLib::SameRange(Precision::PConfusion(), aRes,
              aRes->FirstParameter(), aRes->LastParameter(),
              FirstPar, LastPar, NewCurve2d);
            aRes = Handle(Geom2d_BSplineCurve)::DownCast(NewCurve2d);
          }
          myResult.SetBSpline(aRes);
          myResult.Done();
          myResult.SetType(GeomAbs_BSplineCurve);
        }
      }
      break;

    default:
      {
        isAnalyticalSurf = Standard_False;
        Standard_Real Vsingular[2] = {0.0 , 0.0}; //for surfaces of revolution
        Standard_Real f = 0.0, l = 0.0;
        dt = 0.0;

        if(mySurface->GetType() == GeomAbs_SurfaceOfRevolution)
        {
          //Check possible singularity

          gp_Pnt P = mySurface->AxeOfRevolution().Location();
          gp_Dir N = mySurface->AxeOfRevolution().Direction();

          gp_Lin L(P, N);

          f = myCurve->FirstParameter();
          l = myCurve->LastParameter();
          dt = (l - f) * eps;

          P = myCurve->Value(f);
          if(L.Distance(P) < Precision::Confusion())
          {
            IsTrimmed[0] = Standard_True;
            f = f + dt;
            myCurve = myCurve->Trim(f, l, Precision::Confusion());
            // Searching the parameter on the basis curve for surface of revolution
            Extrema_ExtPC anExtr(P, *mySurface->BasisCurve(), myTolerance);
            if (anExtr.IsDone())
            {
              Standard_Real aMinDist = RealLast();
              for(Standard_Integer anIdx = 1; anIdx <= anExtr.NbExt(); anIdx++)
              {
                if (anExtr.IsMin(anIdx) &&
                    anExtr.SquareDistance(anIdx) < aMinDist)
                {
                  aMinDist = anExtr.SquareDistance(anIdx);
                  Vsingular[0] = anExtr.Point(anIdx).Parameter();
                }
              }
            }
            else
              Vsingular[0] = ElCLib::Parameter(L, P);
            //SingularCase[0] = 3;
          }

          P = myCurve->Value(l);
          if(L.Distance(P) < Precision::Confusion())
          {
            IsTrimmed[1] = Standard_True;
            l = l - dt;
            myCurve = myCurve->Trim(f, l, Precision::Confusion());
            // Searching the parameter on the basis curve for surface of revolution
            Extrema_ExtPC anExtr(P, *mySurface->BasisCurve(), myTolerance);
            if (anExtr.IsDone())
            {
              Standard_Real aMinDist = RealLast();
              for(Standard_Integer anIdx = 1; anIdx <= anExtr.NbExt(); anIdx++)
              {
                if (anExtr.IsMin(anIdx) &&
                    anExtr.SquareDistance(anIdx) < aMinDist)
                {
                  aMinDist = anExtr.SquareDistance(anIdx);
                  Vsingular[1] = anExtr.Point(anIdx).Parameter();
                }
              }
            }
            else
              Vsingular[1] = ElCLib::Parameter(L, P);
            //SingularCase[1] = 4;
          }
        }

        Standard_Real aTolU = Max(ComputeTolU(mySurface, myTolerance), Precision::Confusion());
        Standard_Real aTolV = Max(ComputeTolV(mySurface, myTolerance), Precision::Confusion());
        Standard_Real aTol2d = Sqrt(aTolU*aTolU + aTolV*aTolV);

        Standard_Real aMaxDist = 100. * myTolerance;
        if(myMaxDist > 0.)
        {
          aMaxDist = myMaxDist;
        }
        Handle(ProjLib_HCompProjectedCurve) HProjector = new ProjLib_HCompProjectedCurve (mySurface,myCurve, aTolU, aTolV, aMaxDist);

        // Normalement, dans le cadre de ProjLib, le resultat 
        // doit etre une et une seule courbe !!!
        // De plus, cette courbe ne doit pas etre Single point
        Standard_Integer NbCurves = HProjector->NbCurves();
        Standard_Real Udeb = 0.0,Ufin = 0.0;
        if (NbCurves > 0)
        {
          HProjector->Bounds(1, Udeb, Ufin);
        }
        else 
        {
          return;
        }
        // Approximons cette courbe algorithmique.
        Standard_Boolean Only3d = Standard_False;
        Standard_Boolean Only2d = Standard_True;
        GeomAbs_Shape Continuity = GeomAbs_C1;
        if(myBndPnt == AppParCurves_PassPoint)
        {
          Continuity = GeomAbs_C0;
        }
        Standard_Integer MaxDegree = 14;
        if(myDegMax > 0)
        {
          MaxDegree = myDegMax;
        }
        Standard_Integer MaxSeg    = 16;
        if(myMaxSegments > 0)
        {
          MaxSeg = myMaxSegments;
        }

        Approx_CurveOnSurface appr(HProjector, mySurface, Udeb, Ufin, myTolerance);
        appr.Perform(MaxSeg, MaxDegree, Continuity, Only3d, Only2d);

        Handle(Geom2d_BSplineCurve) aRes = appr.Curve2d();

        if (!aRes.IsNull())
        {
          aTolU = appr.MaxError2dU();
          aTolV = appr.MaxError2dV();
          Standard_Real aNewTol2d = Sqrt(aTolU*aTolU + aTolV*aTolV);
          myTolerance *= (aNewTol2d / aTol2d);
          if(IsTrimmed[0] || IsTrimmed[1])
          {
            // Treatment only for surface of revolution
            Standard_Real u1, u2, v1, v2;
            u1 = mySurface->FirstUParameter();
            u2 = mySurface->LastUParameter();
            v1 = mySurface->FirstVParameter();
            v2 = mySurface->LastVParameter();

            if(IsTrimmed[0])
            {
              //Add segment before start of curve
              ExtendC2d(aRes, f, -dt, u1, u2, Vsingular[0], v2, 0, 3);
            }
            if(IsTrimmed[1])
            {
              //Add segment after end of curve
              ExtendC2d(aRes, l,  dt, u1, u2, v1, Vsingular[1], 1, 4);
            }
            Handle(Geom2d_Curve) NewCurve2d;
            GeomLib::SameRange(Precision::PConfusion(), aRes,
              aRes->FirstParameter(), aRes->LastParameter(),
              FirstPar, LastPar, NewCurve2d);
            aRes = Handle(Geom2d_BSplineCurve)::DownCast(NewCurve2d);
            if(Continuity == GeomAbs_C0)
            {
              // try to smoother the Curve GeomAbs_C1.
              Standard_Integer aDeg = aRes->Degree();
              Standard_Boolean OK = Standard_True;
              Standard_Real aSmoothTol = Max(Precision::Confusion(), aNewTol2d);
              for (Standard_Integer ij = 2; ij < aRes->NbKnots(); ij++) {
                OK = OK && aRes->RemoveKnot(ij, aDeg-1, aSmoothTol);  
              }
            }
          }

          myResult.SetBSpline(aRes);
          myResult.Done();
          myResult.SetType(GeomAbs_BSplineCurve);
        }
      }
  }

  if ( !myResult.IsDone() && isAnalyticalSurf)
  {
    // Use advanced analytical projector if base analytical projection failed.
    ProjLib_ComputeApprox Comp;
    Comp.SetTolerance(myTolerance);
    Comp.SetDegree(myDegMin, myDegMax);
    Comp.SetMaxSegments(myMaxSegments);
    Comp.SetBndPnt(myBndPnt);
    Comp.Perform(myCurve, mySurface);
    if (Comp.Bezier().IsNull() && Comp.BSpline().IsNull())
      return; // advanced projector has been failed too
    myResult.Done();
    Handle(Geom2d_BSplineCurve) aRes;
    if (Comp.BSpline().IsNull())
    {
      aRes = Geom2dConvert::CurveToBSplineCurve(Comp.Bezier());
    }
    else
    {
      aRes = Comp.BSpline();
    }
    if ((IsTrimmed[0] || IsTrimmed[1]))
    {
      if (IsTrimmed[0])
      {
        //Add segment before start of curve
        Standard_Real f = myCurve->FirstParameter();
        ExtendC2d(aRes, f, -dt, U1, U2, V1, V2, 0, SingularCase[0]);
      }
      if (IsTrimmed[1])
      {
        //Add segment after end of curve
        Standard_Real l = myCurve->LastParameter();
        ExtendC2d(aRes, l, dt, U1, U2, V1, V2, 1, SingularCase[1]);
      }
      Handle(Geom2d_Curve) NewCurve2d;
      GeomLib::SameRange(Precision::PConfusion(), aRes,
        aRes->FirstParameter(), aRes->LastParameter(),
        FirstPar, LastPar, NewCurve2d);
      aRes = Handle(Geom2d_BSplineCurve)::DownCast(NewCurve2d);
      myResult.SetBSpline(aRes);
      myResult.SetType(GeomAbs_BSplineCurve);
    }
    else
    {
      // set the type
      if (SType == GeomAbs_Plane && CType == GeomAbs_BezierCurve)
      {
        myResult.SetType(GeomAbs_BezierCurve);
        myResult.SetBezier(Comp.Bezier());
      }
      else
      {
        myResult.SetType(GeomAbs_BSplineCurve);
        myResult.SetBSpline(Comp.BSpline());
      }
    }
    // set the periodicity flag
    if (SType == GeomAbs_Plane        &&
      CType == GeomAbs_BSplineCurve &&
      myCurve->IsPeriodic())
    {
      myResult.SetPeriodic();
    }
    myTolerance = Comp.Tolerance();
  }

  Standard_Boolean isPeriodic[] = {mySurface->IsUPeriodic(),
                                   mySurface->IsVPeriodic()};
  if (myResult.IsDone() &&
     (isPeriodic[0] || isPeriodic[1]))
  {
    // Check result curve to be in params space.

    // U and V parameters space correspondingly.
    const Standard_Real aSurfFirstPar[2] = {mySurface->FirstUParameter(),
                                            mySurface->FirstVParameter()};
    Standard_Real aSurfPeriod[2] = {0.0, 0.0};
    if (isPeriodic[0])
      aSurfPeriod[0] = mySurface->UPeriod();
    if (isPeriodic[1])
      aSurfPeriod[1] = mySurface->VPeriod();

    for(Standard_Integer anIdx = 1; anIdx <= 2; anIdx++)
    {
      if (!isPeriodic[anIdx - 1])
        continue;

      if (myResult.GetType() == GeomAbs_BSplineCurve)
      {
        NCollection_DataMap<Standard_Integer, Standard_Integer> aMap; 
        Handle(Geom2d_BSplineCurve) aRes = myResult.BSpline();
        const Standard_Integer aDeg = aRes->Degree();

        for(Standard_Integer aKnotIdx = aRes->FirstUKnotIndex();
                             aKnotIdx < aRes->LastUKnotIndex();
                             aKnotIdx++)
        {
          const Standard_Real aFirstParam = aRes->Knot(aKnotIdx);
          const Standard_Real aLastParam  = aRes->Knot(aKnotIdx + 1);

          for(Standard_Integer anIntIdx = 0; anIntIdx <= aDeg; anIntIdx++)
          {
            const Standard_Real aCurrParam = aFirstParam + (aLastParam - aFirstParam) * anIntIdx / (aDeg + 1.0);
            gp_Pnt2d aPnt2d;
            aRes->D0(aCurrParam, aPnt2d);

            Standard_Integer aMapKey = Standard_Integer ((aPnt2d.Coord(anIdx) - aSurfFirstPar[anIdx - 1]) / aSurfPeriod[anIdx - 1]);

            if (aPnt2d.Coord(anIdx) - aSurfFirstPar[anIdx - 1] < 0.0)
              aMapKey--;

            if (aMap.IsBound(aMapKey))
              aMap.ChangeFind(aMapKey)++;
            else
              aMap.Bind(aMapKey, 1);
          }
        }

        Standard_Integer aMaxPoints = 0, aMaxIdx = 0;
        NCollection_DataMap<Standard_Integer, Standard_Integer>::Iterator aMapIter(aMap);
        for( ; aMapIter.More(); aMapIter.Next())
        {
          if (aMapIter.Value() > aMaxPoints)
          {
            aMaxPoints = aMapIter.Value();
            aMaxIdx = aMapIter.Key();
          }
        }
        if (aMaxIdx != 0)
        {
          gp_Pnt2d aFirstPnt = aRes->Value(aRes->FirstParameter());
          gp_Pnt2d aSecondPnt = aFirstPnt;
          aSecondPnt.SetCoord(anIdx, aFirstPnt.Coord(anIdx) - aSurfPeriod[anIdx - 1] * aMaxIdx);
          aRes->Translate(gp_Vec2d(aFirstPnt, aSecondPnt));
        }
      }

      if (myResult.GetType() == GeomAbs_Line)
      {
        Standard_Real aT1 = myCurve->FirstParameter();
        Standard_Real aT2 = myCurve->LastParameter();

        if (anIdx == 1)
        {
          // U param space.
          myResult.UFrame(aT1, aT2, aSurfFirstPar[anIdx - 1], aSurfPeriod[anIdx - 1]);
        }
        else
        {
          // V param space.
          myResult.VFrame(aT1, aT2, aSurfFirstPar[anIdx - 1], aSurfPeriod[anIdx - 1]);
        }
      }
    }
  }
}

//=======================================================================
//function : SetDegree
//purpose  : 
//=======================================================================
void ProjLib_ProjectedCurve::SetDegree(const Standard_Integer theDegMin, 
                                       const Standard_Integer theDegMax)
{
  myDegMin = theDegMin;
  myDegMax = theDegMax;
}
//=======================================================================
//function : SetMaxSegments
//purpose  : 
//=======================================================================
void ProjLib_ProjectedCurve::SetMaxSegments(const Standard_Integer theMaxSegments)
{
  myMaxSegments = theMaxSegments;
}

//=======================================================================
//function : SetBndPnt
//purpose  : 
//=======================================================================
void ProjLib_ProjectedCurve::SetBndPnt(const AppParCurves_Constraint theBndPnt)
{
  myBndPnt = theBndPnt;
}

//=======================================================================
//function : SetMaxDist
//purpose  : 
//=======================================================================
void ProjLib_ProjectedCurve::SetMaxDist(const Standard_Real theMaxDist)
{
  myMaxDist = theMaxDist;
}

//=======================================================================
//function : GetSurface
//purpose  : 
//=======================================================================

const Handle(Adaptor3d_Surface)& ProjLib_ProjectedCurve::GetSurface() const
{
  return mySurface;
}


//=======================================================================
//function : GetCurve
//purpose  : 
//=======================================================================

const Handle(Adaptor3d_Curve)& ProjLib_ProjectedCurve::GetCurve() const
{
  return myCurve;
}


//=======================================================================
//function : GetTolerance
//purpose  : 
//=======================================================================

Standard_Real ProjLib_ProjectedCurve::GetTolerance() const 
{
  return myTolerance;
}


//=======================================================================
//function : FirstParameter
//purpose  : 
//=======================================================================

Standard_Real ProjLib_ProjectedCurve::FirstParameter() const 
{
  return myCurve->FirstParameter();
}


//=======================================================================
//function : LastParameter
//purpose  : 
//=======================================================================

Standard_Real ProjLib_ProjectedCurve::LastParameter() const 
{
  return myCurve->LastParameter();
}


//=======================================================================
//function : Continuity
//purpose  : 
//=======================================================================

GeomAbs_Shape ProjLib_ProjectedCurve::Continuity() const
{
  throw Standard_NotImplemented ("ProjLib_ProjectedCurve::Continuity() - method is not implemented");
}


//=======================================================================
//function : NbIntervals
//purpose  : 
//=======================================================================

Standard_Integer ProjLib_ProjectedCurve::NbIntervals(const GeomAbs_Shape ) const 
{
  throw Standard_NotImplemented ("ProjLib_ProjectedCurve::NbIntervals() - method is not implemented");
}


//=======================================================================
//function : Intervals
//purpose  : 
//=======================================================================

//void ProjLib_ProjectedCurve::Intervals(TColStd_Array1OfReal&  T,
void ProjLib_ProjectedCurve::Intervals(TColStd_Array1OfReal&  ,
				       const GeomAbs_Shape ) const 
{
  throw Standard_NotImplemented ("ProjLib_ProjectedCurve::Intervals() - method is not implemented");
}


//=======================================================================
//function : IsClosed
//purpose  : 
//=======================================================================

Standard_Boolean ProjLib_ProjectedCurve::IsClosed() const
{
  throw Standard_NotImplemented ("ProjLib_ProjectedCurve::IsClosed() - method is not implemented");
}


//=======================================================================
//function : IsPeriodic
//purpose  : 
//=======================================================================

Standard_Boolean ProjLib_ProjectedCurve::IsPeriodic() const
{
  return myResult.IsPeriodic();
}


//=======================================================================
//function : Period
//purpose  : 
//=======================================================================

Standard_Real ProjLib_ProjectedCurve::Period() const
{
  throw Standard_NotImplemented ("ProjLib_ProjectedCurve::Period() - method is not implemented");
}


//=======================================================================
//function : Value
//purpose  : 
//=======================================================================

gp_Pnt2d ProjLib_ProjectedCurve::Value(const Standard_Real ) const 
{
  throw Standard_NotImplemented ("ProjLib_ProjectedCurve::Value() - method is not implemented");
}


//=======================================================================
//function : D0
//purpose  : 
//=======================================================================

void ProjLib_ProjectedCurve::D0(const Standard_Real , gp_Pnt2d& ) const
{
  throw Standard_NotImplemented ("ProjLib_ProjectedCurve::D0() - method is not implemented");
}


//=======================================================================
//function : D1
//purpose  : 
//=======================================================================

void ProjLib_ProjectedCurve::D1(const Standard_Real ,
			              gp_Pnt2d&     , 
                                      gp_Vec2d&     ) const 
{
  throw Standard_NotImplemented ("ProjLib_ProjectedCurve::D1() - method is not implemented");
}


//=======================================================================
//function : D2
//purpose  : 
//=======================================================================

void ProjLib_ProjectedCurve::D2(const Standard_Real , 
			              gp_Pnt2d&     , 
                                      gp_Vec2d&     , 
                                      gp_Vec2d&     ) const 
{
  throw Standard_NotImplemented ("ProjLib_ProjectedCurve::D2() - method is not implemented");
}


//=======================================================================
//function : D3
//purpose  : 
//=======================================================================

void ProjLib_ProjectedCurve::D3(const Standard_Real, 
				      gp_Pnt2d&, 
			              gp_Vec2d&, 
				      gp_Vec2d&, 
			              gp_Vec2d&) const 
{
  throw Standard_NotImplemented ("ProjLib_ProjectedCurve::D3() - method is not implemented");
}


//=======================================================================
//function : DN
//purpose  : 
//=======================================================================

gp_Vec2d ProjLib_ProjectedCurve::DN(const Standard_Real, 
				    const Standard_Integer) const 
{
  throw Standard_NotImplemented ("ProjLib_ProjectedCurve::DN() - method is not implemented");
}


//=======================================================================
//function : Resolution
//purpose  : 
//=======================================================================

Standard_Real ProjLib_ProjectedCurve::Resolution(const Standard_Real) const 
{
  throw Standard_NotImplemented ("ProjLib_ProjectedCurve::Resolution() - method is not implemented");
}
    

//=======================================================================
//function : GetType
//purpose  : 
//=======================================================================

GeomAbs_CurveType ProjLib_ProjectedCurve::GetType() const
{
  return myResult.GetType();
}


//=======================================================================
//function : Line
//purpose  : 
//=======================================================================

gp_Lin2d ProjLib_ProjectedCurve::Line() const
{
  return myResult.Line();
}


//=======================================================================
//function : Circle
//purpose  : 
//=======================================================================

gp_Circ2d ProjLib_ProjectedCurve::Circle() const
{
  return myResult.Circle();
}


//=======================================================================
//function : Ellipse
//purpose  : 
//=======================================================================

gp_Elips2d ProjLib_ProjectedCurve::Ellipse() const
{
  return myResult.Ellipse();
}


//=======================================================================
//function : Hyperbola
//purpose  : 
//=======================================================================

gp_Hypr2d ProjLib_ProjectedCurve::Hyperbola() const
{
  return myResult.Hyperbola();
}


//=======================================================================
//function : Parabola
//purpose  : 
//=======================================================================

gp_Parab2d ProjLib_ProjectedCurve::Parabola() const
{
  return myResult.Parabola();
}



//=======================================================================
//function : Degree
//purpose  : 
//=======================================================================

Standard_Integer ProjLib_ProjectedCurve::Degree() const
{
  Standard_NoSuchObject_Raise_if 
    ( (GetType() != GeomAbs_BSplineCurve) &&
      (GetType() != GeomAbs_BezierCurve),
     "ProjLib_ProjectedCurve:Degree");
  if (GetType() == GeomAbs_BSplineCurve) {
    return myResult.BSpline()->Degree();
  }
  else if (GetType() == GeomAbs_BezierCurve) {
    return myResult.Bezier()->Degree();
  }

  // portage WNT
  return 0;
}

//=======================================================================
//function : IsRational
//purpose  : 
//=======================================================================

Standard_Boolean ProjLib_ProjectedCurve::IsRational() const 
{
  Standard_NoSuchObject_Raise_if 
    ( (GetType() != GeomAbs_BSplineCurve) &&
      (GetType() != GeomAbs_BezierCurve),
     "ProjLib_ProjectedCurve:IsRational");
  if (GetType() == GeomAbs_BSplineCurve) {
    return myResult.BSpline()->IsRational();
  }
  else if (GetType() == GeomAbs_BezierCurve) {
    return myResult.Bezier()->IsRational();
  }
  // portage WNT
  return Standard_False;
}

//=======================================================================
//function : NbPoles
//purpose  : 
//=======================================================================

Standard_Integer ProjLib_ProjectedCurve::NbPoles() const
{
  Standard_NoSuchObject_Raise_if 
    ( (GetType() != GeomAbs_BSplineCurve) &&
      (GetType() != GeomAbs_BezierCurve)   
     ,"ProjLib_ProjectedCurve:NbPoles"  );
  if (GetType() == GeomAbs_BSplineCurve) {
    return myResult.BSpline()->NbPoles();
  }
  else if (GetType() == GeomAbs_BezierCurve) {
    return myResult.Bezier()->NbPoles();
  }

  // portage WNT
  return 0;
}

//=======================================================================
//function : NbKnots
//purpose  : 
//=======================================================================

Standard_Integer ProjLib_ProjectedCurve::NbKnots() const 
{
  Standard_NoSuchObject_Raise_if ( GetType() != GeomAbs_BSplineCurve, 
				  "ProjLib_ProjectedCurve:NbKnots");
  return myResult.BSpline()->NbKnots();
}

//=======================================================================
//function : Bezier
//purpose  : 
//=======================================================================

Handle(Geom2d_BezierCurve) ProjLib_ProjectedCurve::Bezier() const 
{
 return myResult.Bezier() ;
}

//=======================================================================
//function : BSpline
//purpose  : 
//=======================================================================

Handle(Geom2d_BSplineCurve) ProjLib_ProjectedCurve::BSpline() const 
{
 return myResult.BSpline() ;
}
//=======================================================================
//function : Trim
//purpose  : 
//=======================================================================

Handle(Adaptor2d_Curve2d) ProjLib_ProjectedCurve::Trim 
//(const Standard_Real First,
// const Standard_Real Last,
// const Standard_Real Tolerance) const 
(const Standard_Real ,
 const Standard_Real ,
 const Standard_Real ) const 
{
  throw Standard_NotImplemented ("ProjLib_ProjectedCurve::Trim() - method is not implemented");
}