// Created on: 2000-11-23
// Created by: Michael KLOKOV
// Copyright (c) 2000-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 <IntTools_FaceFace.hxx>

#include <BRepTools.hxx>
#include <BRep_Tool.hxx>
#include <ElCLib.hxx>
#include <ElSLib.hxx>
#include <Geom2dAdaptor_Curve.hxx>
#include <Geom2dInt_GInter.hxx>
#include <Geom2d_BSplineCurve.hxx>
#include <Geom2d_Line.hxx>
#include <Geom2d_TrimmedCurve.hxx>
#include <GeomAPI_ProjectPointOnSurf.hxx>
#include <GeomAdaptor_Surface.hxx>
#include <GeomInt_IntSS.hxx>
#include <GeomInt_WLApprox.hxx>
#include <GeomLib_Check2dBSplineCurve.hxx>
#include <GeomLib_CheckBSplineCurve.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Ellipse.hxx>
#include <Geom_Hyperbola.hxx>
#include <Geom_Line.hxx>
#include <Geom_OffsetSurface.hxx>
#include <Geom_Parabola.hxx>
#include <Geom_RectangularTrimmedSurface.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <IntAna_QuadQuadGeo.hxx>
#include <IntPatch_GLine.hxx>
#include <IntPatch_RLine.hxx>
#include <IntRes2d_Domain.hxx>
#include <IntSurf_Quadric.hxx>
#include <IntTools_Context.hxx>
#include <IntTools_Tools.hxx>
#include <IntTools_TopolTool.hxx>
#include <IntTools_WLineTool.hxx>
#include <ProjLib_Plane.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>
#include <gp_Elips.hxx>
#include <ApproxInt_KnotTools.hxx>

static void Parameters(const Handle(GeomAdaptor_Surface)&,
                       const Handle(GeomAdaptor_Surface)&,
                       const gp_Pnt&,
                       Standard_Real&,
                       Standard_Real&,
                       Standard_Real&,
                       Standard_Real&);

static void CorrectSurfaceBoundaries(const TopoDS_Face&  theFace,
                                     const Standard_Real theTolerance,
                                     Standard_Real&      theumin,
                                     Standard_Real&      theumax,
                                     Standard_Real&      thevmin,
                                     Standard_Real&      thevmax);

static Standard_Boolean ParameterOutOfBoundary(const Standard_Real       theParameter,
                                               const Handle(Geom_Curve)& theCurve,
                                               const TopoDS_Face&        theFace1,
                                               const TopoDS_Face&        theFace2,
                                               const Standard_Real       theOtherParameter,
                                               const Standard_Boolean    bIncreasePar,
                                               const Standard_Real       theTol,
                                               Standard_Real&            theNewParameter,
                                               const Handle(IntTools_Context)&);

static Standard_Boolean IsCurveValid(const Handle(Geom2d_Curve)& thePCurve);

static Standard_Boolean ApproxWithPCurves(const gp_Cylinder& theCyl, const gp_Sphere& theSph);

static void PerformPlanes(const Handle(GeomAdaptor_Surface)& theS1,
                          const Handle(GeomAdaptor_Surface)& theS2,
                          const Standard_Real                TolF1,
                          const Standard_Real                TolF2,
                          const Standard_Real                TolAng,
                          const Standard_Real                TolTang,
                          const Standard_Boolean             theApprox1,
                          const Standard_Boolean             theApprox2,
                          IntTools_SequenceOfCurves&         theSeqOfCurve,
                          Standard_Boolean&                  theTangentFaces);

static Standard_Boolean ClassifyLin2d(const Handle(GeomAdaptor_Surface)& theS,
                                      const gp_Lin2d&                    theLin2d,
                                      const Standard_Real                theTol,
                                      Standard_Real&                     theP1,
                                      Standard_Real&                     theP2);
//
static void ApproxParameters(const Handle(GeomAdaptor_Surface)& aHS1,
                             const Handle(GeomAdaptor_Surface)& aHS2,
                             Standard_Integer&                  iDegMin,
                             Standard_Integer&                  iNbIter,
                             Standard_Integer&                  iDegMax);

static void Tolerances(const Handle(GeomAdaptor_Surface)& aHS1,
                       const Handle(GeomAdaptor_Surface)& aHS2,
                       Standard_Real&                     aTolTang);

static Standard_Boolean SortTypes(const GeomAbs_SurfaceType aType1,
                                  const GeomAbs_SurfaceType aType2);
static Standard_Integer IndexType(const GeomAbs_SurfaceType aType);

//
static Standard_Boolean CheckPCurve(const Handle(Geom2d_Curve)&     aPC,
                                    const TopoDS_Face&              aFace,
                                    const Handle(IntTools_Context)& theCtx);

static Standard_Real MaxDistance(const Handle(Geom_Curve)&   theC,
                                 const Standard_Real         aT,
                                 GeomAPI_ProjectPointOnSurf& theProjPS);

static Standard_Real FindMaxDistance(const Handle(Geom_Curve)&   theC,
                                     const Standard_Real         theFirst,
                                     const Standard_Real         theLast,
                                     GeomAPI_ProjectPointOnSurf& theProjPS,
                                     const Standard_Real         theEps);

static Standard_Real FindMaxDistance(const Handle(Geom_Curve)&       theCurve,
                                     const Standard_Real             theFirst,
                                     const Standard_Real             theLast,
                                     const TopoDS_Face&              theFace,
                                     const Handle(IntTools_Context)& theContext);

static void CorrectPlaneBoundaries(Standard_Real& aUmin,
                                   Standard_Real& aUmax,
                                   Standard_Real& aVmin,
                                   Standard_Real& aVmax);

//=================================================================================================

IntTools_FaceFace::IntTools_FaceFace()
{
  myIsDone       = Standard_False;
  myTangentFaces = Standard_False;
  //
  myHS1        = new GeomAdaptor_Surface();
  myHS2        = new GeomAdaptor_Surface();
  myTolF1      = 0.;
  myTolF2      = 0.;
  myTol        = 0.;
  myFuzzyValue = Precision::Confusion();
  SetParameters(Standard_True, Standard_True, Standard_True, 1.e-07);
}

//=================================================================================================

void IntTools_FaceFace::SetContext(const Handle(IntTools_Context)& aContext)
{
  myContext = aContext;
}

//=================================================================================================

const Handle(IntTools_Context)& IntTools_FaceFace::Context() const
{
  return myContext;
}

//=================================================================================================

const TopoDS_Face& IntTools_FaceFace::Face1() const
{
  return myFace1;
}

//=================================================================================================

const TopoDS_Face& IntTools_FaceFace::Face2() const
{
  return myFace2;
}

//=================================================================================================

Standard_Boolean IntTools_FaceFace::TangentFaces() const
{
  return myTangentFaces;
}

//=================================================================================================

const IntTools_SequenceOfPntOn2Faces& IntTools_FaceFace::Points() const
{
  return myPnts;
}

//=================================================================================================

Standard_Boolean IntTools_FaceFace::IsDone() const
{
  return myIsDone;
}

//=======================================================================
// function : Lines
// purpose  : return lines of intersection
//=======================================================================
const IntTools_SequenceOfCurves& IntTools_FaceFace::Lines() const
{
  StdFail_NotDone_Raise_if(!myIsDone, "IntTools_FaceFace::Lines() => myIntersector NOT DONE");
  return mySeqOfCurve;
}

// =======================================================================
// function: SetParameters
//
// =======================================================================
void IntTools_FaceFace::SetParameters(const Standard_Boolean ToApproxC3d,
                                      const Standard_Boolean ToApproxC2dOnS1,
                                      const Standard_Boolean ToApproxC2dOnS2,
                                      const Standard_Real    ApproximationTolerance)
{
  myApprox    = ToApproxC3d;
  myApprox1   = ToApproxC2dOnS1;
  myApprox2   = ToApproxC2dOnS2;
  myTolApprox = ApproximationTolerance;
}

//=================================================================================================

void IntTools_FaceFace::SetFuzzyValue(const Standard_Real theFuzz)
{
  myFuzzyValue = Max(theFuzz, Precision::Confusion());
}

//=================================================================================================

Standard_Real IntTools_FaceFace::FuzzyValue() const
{
  return myFuzzyValue;
}

//=================================================================================================

void IntTools_FaceFace::SetList(IntSurf_ListOfPntOn2S& aListOfPnts)
{
  myListOfPnts = aListOfPnts;
}

static Standard_Boolean isTreatAnalityc(const BRepAdaptor_Surface& theBAS1,
                                        const BRepAdaptor_Surface& theBAS2,
                                        const Standard_Real        theTol)
{
  const Standard_Real Tolang = 1.e-8;
  Standard_Real       aHigh  = 0.0;

  const GeomAbs_SurfaceType aType1 = theBAS1.GetType();
  const GeomAbs_SurfaceType aType2 = theBAS2.GetType();

  gp_Pln      aS1;
  gp_Cylinder aS2;
  if (aType1 == GeomAbs_Plane)
  {
    aS1 = theBAS1.Plane();
  }
  else if (aType2 == GeomAbs_Plane)
  {
    aS1 = theBAS2.Plane();
  }
  else
  {
    return Standard_True;
  }

  if (aType1 == GeomAbs_Cylinder)
  {
    aS2                      = theBAS1.Cylinder();
    const Standard_Real VMin = theBAS1.FirstVParameter();
    const Standard_Real VMax = theBAS1.LastVParameter();

    if (Precision::IsNegativeInfinite(VMin) || Precision::IsPositiveInfinite(VMax))
      return Standard_True;
    else
      aHigh = VMax - VMin;
  }
  else if (aType2 == GeomAbs_Cylinder)
  {
    aS2 = theBAS2.Cylinder();

    const Standard_Real VMin = theBAS2.FirstVParameter();
    const Standard_Real VMax = theBAS2.LastVParameter();

    if (Precision::IsNegativeInfinite(VMin) || Precision::IsPositiveInfinite(VMax))
      return Standard_True;
    else
      aHigh = VMax - VMin;
  }
  else
  {
    return Standard_True;
  }

  IntAna_QuadQuadGeo inter;
  inter.Perform(aS1, aS2, Tolang, theTol, aHigh);
  if (inter.TypeInter() == IntAna_Ellipse)
  {
    const gp_Elips      anEl    = inter.Ellipse(1);
    const Standard_Real aMajorR = anEl.MajorRadius();
    const Standard_Real aMinorR = anEl.MinorRadius();

    return (aMajorR < 100000.0 * aMinorR);
  }
  else
  {
    return inter.IsDone();
  }
}

//=======================================================================
// function : Perform
// purpose  : intersect surfaces of the faces
//=======================================================================
void IntTools_FaceFace::Perform(const TopoDS_Face&     aF1,
                                const TopoDS_Face&     aF2,
                                const Standard_Boolean theToRunParallel)
{
  if (myContext.IsNull())
  {
    myContext = new IntTools_Context;
  }

  mySeqOfCurve.Clear();
  myIsDone  = Standard_False;
  myNbrestr = 0; //?

  myFace1 = aF1;
  myFace2 = aF2;

  const BRepAdaptor_Surface& aBAS1  = myContext->SurfaceAdaptor(myFace1);
  const BRepAdaptor_Surface& aBAS2  = myContext->SurfaceAdaptor(myFace2);
  GeomAbs_SurfaceType        aType1 = aBAS1.GetType();
  GeomAbs_SurfaceType        aType2 = aBAS2.GetType();

  const Standard_Boolean bReverse = SortTypes(aType1, aType2);
  if (bReverse)
  {
    myFace1 = aF2;
    myFace2 = aF1;
    aType1  = aBAS2.GetType();
    aType2  = aBAS1.GetType();

    if (myListOfPnts.Extent())
    {
      Standard_Real                       aU1, aV1, aU2, aV2;
      IntSurf_ListIteratorOfListOfPntOn2S aItP2S;
      //
      aItP2S.Initialize(myListOfPnts);
      for (; aItP2S.More(); aItP2S.Next())
      {
        IntSurf_PntOn2S& aP2S = aItP2S.ChangeValue();
        aP2S.Parameters(aU1, aV1, aU2, aV2);
        aP2S.SetValue(aU2, aV2, aU1, aV1);
      }
    }
    //
    Standard_Boolean anAproxTmp = myApprox1;
    myApprox1                   = myApprox2;
    myApprox2                   = anAproxTmp;
  }

  const Handle(Geom_Surface) S1 = BRep_Tool::Surface(myFace1);
  const Handle(Geom_Surface) S2 = BRep_Tool::Surface(myFace2);

  Standard_Real aFuzz = myFuzzyValue / 2.;
  myTolF1             = BRep_Tool::Tolerance(myFace1) + aFuzz;
  myTolF2             = BRep_Tool::Tolerance(myFace2) + aFuzz;
  myTol               = myTolF1 + myTolF2;

  Standard_Real TolArc  = myTol;
  Standard_Real TolTang = TolArc;

  const Standard_Boolean isFace1Quad =
    (aType1 == GeomAbs_Cylinder || aType1 == GeomAbs_Cone || aType1 == GeomAbs_Torus);

  const Standard_Boolean isFace2Quad =
    (aType2 == GeomAbs_Cylinder || aType2 == GeomAbs_Cone || aType2 == GeomAbs_Torus);

  if (aType1 == GeomAbs_Plane && aType2 == GeomAbs_Plane)
  {
    Standard_Real umin, umax, vmin, vmax;
    //
    myContext->UVBounds(myFace1, umin, umax, vmin, vmax);
    myHS1->Load(S1, umin, umax, vmin, vmax);
    //
    myContext->UVBounds(myFace2, umin, umax, vmin, vmax);
    myHS2->Load(S2, umin, umax, vmin, vmax);
    //
    Standard_Real TolAng = 1.e-8;
    //
    PerformPlanes(myHS1,
                  myHS2,
                  myTolF1,
                  myTolF2,
                  TolAng,
                  TolTang,
                  myApprox1,
                  myApprox2,
                  mySeqOfCurve,
                  myTangentFaces);
    //
    myIsDone = Standard_True;
    //
    if (!myTangentFaces)
    {
      const Standard_Integer NbLinPP = mySeqOfCurve.Length();
      if (NbLinPP && bReverse)
      {
        Handle(Geom2d_Curve)   aC2D1, aC2D2;
        const Standard_Integer aNbLin = mySeqOfCurve.Length();
        for (Standard_Integer i = 1; i <= aNbLin; ++i)
        {
          IntTools_Curve& aIC = mySeqOfCurve(i);
          aC2D1               = aIC.FirstCurve2d();
          aC2D2               = aIC.SecondCurve2d();
          aIC.SetFirstCurve2d(aC2D2);
          aIC.SetSecondCurve2d(aC2D1);
        }
      }
    }
    return;
  } // if(aType1==GeomAbs_Plane && aType2==GeomAbs_Plane){

  if ((aType1 == GeomAbs_Plane) && isFace2Quad)
  {
    Standard_Real umin, umax, vmin, vmax;
    // F1
    myContext->UVBounds(myFace1, umin, umax, vmin, vmax);
    CorrectPlaneBoundaries(umin, umax, vmin, vmax);
    myHS1->Load(S1, umin, umax, vmin, vmax);
    // F2
    myContext->UVBounds(myFace2, umin, umax, vmin, vmax);
    CorrectSurfaceBoundaries(myFace2, myTol * 2., umin, umax, vmin, vmax);
    myHS2->Load(S2, umin, umax, vmin, vmax);
  }
  else if ((aType2 == GeomAbs_Plane) && isFace1Quad)
  {
    Standard_Real umin, umax, vmin, vmax;
    // F1
    myContext->UVBounds(myFace1, umin, umax, vmin, vmax);
    CorrectSurfaceBoundaries(myFace1, myTol * 2., umin, umax, vmin, vmax);
    myHS1->Load(S1, umin, umax, vmin, vmax);
    // F2
    myContext->UVBounds(myFace2, umin, umax, vmin, vmax);
    CorrectPlaneBoundaries(umin, umax, vmin, vmax);
    myHS2->Load(S2, umin, umax, vmin, vmax);
  }
  else
  {
    Standard_Real umin, umax, vmin, vmax;
    myContext->UVBounds(myFace1, umin, umax, vmin, vmax);
    CorrectSurfaceBoundaries(myFace1, myTol * 2., umin, umax, vmin, vmax);
    myHS1->Load(S1, umin, umax, vmin, vmax);
    myContext->UVBounds(myFace2, umin, umax, vmin, vmax);
    CorrectSurfaceBoundaries(myFace2, myTol * 2., umin, umax, vmin, vmax);
    myHS2->Load(S2, umin, umax, vmin, vmax);
  }

  const Handle(IntTools_TopolTool) dom1 = new IntTools_TopolTool(myHS1);
  const Handle(IntTools_TopolTool) dom2 = new IntTools_TopolTool(myHS2);

  myLConstruct.Load(dom1, dom2, myHS1, myHS2);

  Tolerances(myHS1, myHS2, TolTang);

  {
    const Standard_Real UVMaxStep =
      IntPatch_Intersection::DefineUVMaxStep(myHS1, dom1, myHS2, dom2);
    Standard_Real Deflection = 0.1;
    if (aType1 == GeomAbs_BSplineSurface && aType2 == GeomAbs_BSplineSurface)
    {
      Deflection /= 10.;
    }
    myIntersector.SetTolerances(TolArc, TolTang, UVMaxStep, Deflection);
  }

  if ((aType1 != GeomAbs_BSplineSurface) && (aType1 != GeomAbs_BezierSurface)
      && (aType1 != GeomAbs_OtherSurface) && (aType2 != GeomAbs_BSplineSurface)
      && (aType2 != GeomAbs_BezierSurface) && (aType2 != GeomAbs_OtherSurface))
  {
    if ((aType1 == GeomAbs_Torus) || (aType2 == GeomAbs_Torus))
    {
      myListOfPnts.Clear();
    }
  }

#ifdef INTTOOLS_FACEFACE_DEBUG
  if (!myListOfPnts.IsEmpty())
  {
    char aBuff[10000];

    Sprintf(aBuff, "bopcurves <face1 face2> -2d");
    IntSurf_ListIteratorOfListOfPntOn2S IterLOP1(myListOfPnts);
    for (; IterLOP1.More(); IterLOP1.Next())
    {
      const IntSurf_PntOn2S& aPt = IterLOP1.Value();
      Standard_Real          u1, v1, u2, v2;
      aPt.Parameters(u1, v1, u2, v2);

      Sprintf(aBuff, "%s -p %+10.20f %+10.20f %+10.20f %+10.20f", aBuff, u1, v1, u2, v2);
    }

    std::cout << aBuff << std::endl;
  }
#endif

  const Standard_Boolean isGeomInt = isTreatAnalityc(aBAS1, aBAS2, myTol);
  if (aF1.IsSame(aF2))
    myIntersector.Perform(myHS1, dom1, TolArc, TolTang);
  else
    myIntersector.Perform(myHS1, dom1, myHS2, dom2, TolArc, TolTang, myListOfPnts, isGeomInt);

  myIsDone = myIntersector.IsDone();

  if (myIsDone)
  {
    myTangentFaces = myIntersector.TangentFaces();
    if (myTangentFaces)
    {
      return;
    }
    //
    const Standard_Integer aNbLinIntersector = myIntersector.NbLines();
    for (Standard_Integer i = 1; i <= aNbLinIntersector; ++i)
    {
      MakeCurve(i, dom1, dom2, TolArc);
    }
    //
    ComputeTolReached3d(theToRunParallel);
    //
    if (bReverse)
    {
      Handle(Geom2d_Curve) aC2D1, aC2D2;
      //
      const Standard_Integer aNbLinSeqOfCurve = mySeqOfCurve.Length();
      for (Standard_Integer i = 1; i <= aNbLinSeqOfCurve; ++i)
      {
        IntTools_Curve& aIC = mySeqOfCurve(i);
        aC2D1               = aIC.FirstCurve2d();
        aC2D2               = aIC.SecondCurve2d();
        aIC.SetFirstCurve2d(aC2D2);
        aIC.SetSecondCurve2d(aC2D1);
      }
    }

    // Points
    Standard_Boolean     bValid2D1, bValid2D2;
    Standard_Real        U1, V1, U2, V2;
    IntTools_PntOnFace   aPntOnF1, aPntOnF2;
    IntTools_PntOn2Faces aPntOn2Faces;
    //
    const Standard_Integer aNbPnts = myIntersector.NbPnts();
    for (Standard_Integer i = 1; i <= aNbPnts; ++i)
    {
      const IntSurf_PntOn2S& aISPnt = myIntersector.Point(i).PntOn2S();
      const gp_Pnt&          aPnt   = aISPnt.Value();
      aISPnt.Parameters(U1, V1, U2, V2);
      //
      // check the validity of the intersection point for the faces
      bValid2D1 = myContext->IsPointInOnFace(myFace1, gp_Pnt2d(U1, V1));
      if (!bValid2D1)
      {
        continue;
      }
      //
      bValid2D2 = myContext->IsPointInOnFace(myFace2, gp_Pnt2d(U2, V2));
      if (!bValid2D2)
      {
        continue;
      }
      //
      // add the intersection point
      aPntOnF1.Init(myFace1, aPnt, U1, V1);
      aPntOnF2.Init(myFace2, aPnt, U2, V2);
      //
      if (!bReverse)
      {
        aPntOn2Faces.SetP1(aPntOnF1);
        aPntOn2Faces.SetP2(aPntOnF2);
      }
      else
      {
        aPntOn2Faces.SetP2(aPntOnF1);
        aPntOn2Faces.SetP1(aPntOnF2);
      }

      myPnts.Append(aPntOn2Faces);
    }
  }
}

//=================================================================================================

void IntTools_FaceFace::ComputeTolReached3d(const Standard_Boolean theToRunParallel)
{
  Standard_Integer i, j, aNbLin = mySeqOfCurve.Length();
  if (!aNbLin)
  {
    return;
  }
  //
  // Minimal tangential tolerance for the curve
  Standard_Real aTolFMax = Max(myTolF1, myTolF2);
  //
  const Handle(Geom_Surface)& aS1 = myHS1->Surface();
  const Handle(Geom_Surface)& aS2 = myHS2->Surface();
  //
  for (i = 1; i <= aNbLin; ++i)
  {
    IntTools_Curve&           aIC  = mySeqOfCurve(i);
    const Handle(Geom_Curve)& aC3D = aIC.Curve();
    if (aC3D.IsNull())
    {
      continue;
    }
    //
    Standard_Real aTolC  = aIC.Tolerance();
    Standard_Real aFirst = aC3D->FirstParameter();
    Standard_Real aLast  = aC3D->LastParameter();
    //
    // Compute the tolerance for the curve
    const Handle(Geom2d_Curve)& aC2D1 = aIC.FirstCurve2d();
    const Handle(Geom2d_Curve)& aC2D2 = aIC.SecondCurve2d();
    //
    for (j = 0; j < 2; ++j)
    {
      const Handle(Geom2d_Curve)& aC2D = !j ? aC2D1 : aC2D2;
      if (!aC2D.IsNull())
      {
        // Look for the maximal deviation between 3D and 2D curves
        Standard_Real               aD, aT;
        const Handle(Geom_Surface)& aS = !j ? aS1 : aS2;
        if (IntTools_Tools::ComputeTolerance(aC3D,
                                             aC2D,
                                             aS,
                                             aFirst,
                                             aLast,
                                             aD,
                                             aT,
                                             Precision::PConfusion(),
                                             theToRunParallel))
        {
          if (aD > aTolC)
          {
            aTolC = aD;
          }
        }
      }
      else
      {
        // Look for the maximal deviation between 3D curve and surface
        const TopoDS_Face& aF = !j ? myFace1 : myFace2;
        Standard_Real      aD = FindMaxDistance(aC3D, aFirst, aLast, aF, myContext);
        if (aD > aTolC)
        {
          aTolC = aD;
        }
      }
    }
    // Set the valid tolerance for the curve
    aIC.SetTolerance(aTolC);
    //
    // Set the tangential tolerance for the curve.
    // Note, that, currently, computation of the tangential tolerance is
    // implemented for the Plane/Plane case only.
    // Thus, set the tangential tolerance equal to maximal tolerance of faces.
    if (aIC.TangentialTolerance() < aTolFMax)
    {
      aIC.SetTangentialTolerance(aTolFMax);
    }
  }
}

//=================================================================================================

void IntTools_FaceFace::MakeCurve(const Standard_Integer             Index,
                                  const Handle(Adaptor3d_TopolTool)& dom1,
                                  const Handle(Adaptor3d_TopolTool)& dom2,
                                  const Standard_Real                theToler)
{
  Standard_Boolean      bDone, rejectSurface, reApprox, bAvoidLineConstructor;
  Standard_Boolean      ok, bPCurvesOk;
  Standard_Integer      i, j, aNbParts;
  Standard_Real         fprm, lprm;
  Standard_Real         Tolpc;
  Handle(IntPatch_Line) L;
  IntPatch_IType        typl;
  Handle(Geom_Curve)    newc;
  //
  const Standard_Real TOLCHECK    = 1.e-7;
  const Standard_Real TOLANGCHECK = 1.e-6;
  //
  rejectSurface = Standard_False;
  reApprox      = Standard_False;
  //
  bPCurvesOk = Standard_True;

reapprox:;

  Tolpc                 = myTolApprox;
  bAvoidLineConstructor = Standard_False;
  L                     = myIntersector.Line(Index);
  typl                  = L->ArcType();
  //
  if (typl == IntPatch_Walking)
  {
    Handle(IntPatch_WLine) aWLine(Handle(IntPatch_WLine)::DownCast(L));
    if (aWLine.IsNull())
    {
      return;
    }
    L = aWLine;

    Standard_Integer       nbp = aWLine->NbPnts();
    const IntSurf_PntOn2S& p1  = aWLine->Point(1);
    const IntSurf_PntOn2S& p2  = aWLine->Point(nbp);

    const gp_Pnt& P1 = p1.Value();
    const gp_Pnt& P2 = p2.Value();

    if (P1.SquareDistance(P2) < 1.e-14)
    {
      bAvoidLineConstructor = Standard_False;
    }
  }

  typl = L->ArcType();

  if (typl == IntPatch_Restriction)
    bAvoidLineConstructor = Standard_True;

  //
  // Line Constructor
  if (!bAvoidLineConstructor)
  {
    myLConstruct.Perform(L);
    //
    bDone = myLConstruct.IsDone();
    if (!bDone)
    {
      return;
    }

    if (typl != IntPatch_Restriction)
    {
      aNbParts = myLConstruct.NbParts();
      if (aNbParts <= 0)
      {
        return;
      }
    }
  }
  // Do the Curve

  switch (typl)
  {
    // ########################################
    //  Line, Parabola, Hyperbola
    // ########################################
    case IntPatch_Lin:
    case IntPatch_Parabola:
    case IntPatch_Hyperbola: {
      if (typl == IntPatch_Lin)
      {
        newc = new Geom_Line(Handle(IntPatch_GLine)::DownCast(L)->Line());
      }

      else if (typl == IntPatch_Parabola)
      {
        newc = new Geom_Parabola(Handle(IntPatch_GLine)::DownCast(L)->Parabola());
      }

      else if (typl == IntPatch_Hyperbola)
      {
        newc = new Geom_Hyperbola(Handle(IntPatch_GLine)::DownCast(L)->Hyperbola());
      }
      //
      aNbParts = myLConstruct.NbParts();
      for (i = 1; i <= aNbParts; i++)
      {
        Standard_Boolean bFNIt, bLPIt;
        //
        myLConstruct.Part(i, fprm, lprm);
        //
        bFNIt = Precision::IsNegativeInfinite(fprm);
        bLPIt = Precision::IsPositiveInfinite(lprm);
        //
        if (!bFNIt && !bLPIt)
        {
          //
          IntTools_Curve aCurve;
          //
          Handle(Geom_TrimmedCurve) aCT3D = new Geom_TrimmedCurve(newc, fprm, lprm);
          aCurve.SetCurve(aCT3D);
          if (typl == IntPatch_Parabola)
          {
            Standard_Real aTolC = IntTools_Tools::CurveTolerance(aCT3D, myTol);
            aCurve.SetTolerance(aTolC);
          }
          //
          if (myApprox1)
          {
            Handle(Geom2d_Curve) C2d;
            GeomInt_IntSS::BuildPCurves(fprm, lprm, Tolpc, myHS1->Surface(), newc, C2d);

            if (C2d.IsNull())
              continue;

            aCurve.SetFirstCurve2d(new Geom2d_TrimmedCurve(C2d, fprm, lprm));
          }
          //
          if (myApprox2)
          {
            Handle(Geom2d_Curve) C2d;
            GeomInt_IntSS::BuildPCurves(fprm, lprm, Tolpc, myHS2->Surface(), newc, C2d);

            if (C2d.IsNull())
              continue;

            aCurve.SetSecondCurve2d(new Geom2d_TrimmedCurve(C2d, fprm, lprm));
          }
          //
          mySeqOfCurve.Append(aCurve);
        } // if (!bFNIt && !bLPIt) {
        else
        {
          //  on regarde si on garde
          //
          Standard_Real aTestPrm, dT = 100.;
          //
          aTestPrm = 0.;
          if (bFNIt && !bLPIt)
          {
            aTestPrm = lprm - dT;
          }
          else if (!bFNIt && bLPIt)
          {
            aTestPrm = fprm + dT;
          }
          else
          {
            // i.e, if (bFNIt && bLPIt)
            aTestPrm = IntTools_Tools::IntermediatePoint(-dT, dT);
          }
          //
          gp_Pnt ptref(newc->Value(aTestPrm));
          //
          GeomAbs_SurfaceType typS1 = myHS1->GetType();
          GeomAbs_SurfaceType typS2 = myHS2->GetType();
          if (typS1 == GeomAbs_SurfaceOfExtrusion || typS1 == GeomAbs_OffsetSurface
              || typS1 == GeomAbs_SurfaceOfRevolution || typS2 == GeomAbs_SurfaceOfExtrusion
              || typS2 == GeomAbs_OffsetSurface || typS2 == GeomAbs_SurfaceOfRevolution)
          {
            Handle(Geom2d_BSplineCurve) H1;
            mySeqOfCurve.Append(IntTools_Curve(newc, H1, H1));
            continue;
          }

          Standard_Real u1, v1, u2, v2, Tol;

          Tol = Precision::Confusion();
          Parameters(myHS1, myHS2, ptref, u1, v1, u2, v2);
          ok = (dom1->Classify(gp_Pnt2d(u1, v1), Tol) != TopAbs_OUT);
          if (ok)
          {
            ok = (dom2->Classify(gp_Pnt2d(u2, v2), Tol) != TopAbs_OUT);
          }
          if (ok)
          {
            Handle(Geom2d_BSplineCurve) H1;
            mySeqOfCurve.Append(IntTools_Curve(newc, H1, H1));
          }
        }
      } // for (i=1; i<=aNbParts; i++) {
    } // case IntPatch_Lin:  case IntPatch_Parabola:  case IntPatch_Hyperbola:
    break;

    // ########################################
    //  Circle and Ellipse
    // ########################################
    case IntPatch_Circle:
    case IntPatch_Ellipse: {

      if (typl == IntPatch_Circle)
      {
        newc = new Geom_Circle(Handle(IntPatch_GLine)::DownCast(L)->Circle());
      }
      else
      { // IntPatch_Ellipse
        newc = new Geom_Ellipse(Handle(IntPatch_GLine)::DownCast(L)->Ellipse());
      }
      //
      aNbParts = myLConstruct.NbParts();
      //
      Standard_Real          aPeriod, aNul;
      TColStd_SequenceOfReal aSeqFprm, aSeqLprm;

      aNul    = 0.;
      aPeriod = M_PI + M_PI;

      for (i = 1; i <= aNbParts; i++)
      {
        myLConstruct.Part(i, fprm, lprm);

        if (fprm < aNul && lprm > aNul)
        {
          // interval that goes through 0. is divided on two intervals;
          while (fprm < aNul || fprm > aPeriod)
            fprm = fprm + aPeriod;
          while (lprm < aNul || lprm > aPeriod)
            lprm = lprm + aPeriod;
          //
          if ((aPeriod - fprm) > Tolpc)
          {
            aSeqFprm.Append(fprm);
            aSeqLprm.Append(aPeriod);
          }
          else
          {
            gp_Pnt P1 = newc->Value(fprm);
            gp_Pnt P2 = newc->Value(aPeriod);

            if (P1.Distance(P2) > myTol)
            {
              Standard_Real anewpar = fprm;

              if (ParameterOutOfBoundary(fprm,
                                         newc,
                                         myFace1,
                                         myFace2,
                                         lprm,
                                         Standard_False,
                                         myTol,
                                         anewpar,
                                         myContext))
              {
                fprm = anewpar;
              }
              aSeqFprm.Append(fprm);
              aSeqLprm.Append(aPeriod);
            }
          }

          //
          if ((lprm - aNul) > Tolpc)
          {
            aSeqFprm.Append(aNul);
            aSeqLprm.Append(lprm);
          }
          else
          {
            gp_Pnt P1 = newc->Value(aNul);
            gp_Pnt P2 = newc->Value(lprm);

            if (P1.Distance(P2) > myTol)
            {
              Standard_Real anewpar = lprm;

              if (ParameterOutOfBoundary(lprm,
                                         newc,
                                         myFace1,
                                         myFace2,
                                         fprm,
                                         Standard_True,
                                         myTol,
                                         anewpar,
                                         myContext))
              {
                lprm = anewpar;
              }
              aSeqFprm.Append(aNul);
              aSeqLprm.Append(lprm);
            }
          }
        }
        else
        {
          // usual interval
          aSeqFprm.Append(fprm);
          aSeqLprm.Append(lprm);
        }
      }
      //
      aNbParts = aSeqFprm.Length();
      for (i = 1; i <= aNbParts; i++)
      {
        fprm = aSeqFprm(i);
        lprm = aSeqLprm(i);
        //
        constexpr Standard_Real aRealEpsilon = RealEpsilon();
        if (Abs(fprm) > aRealEpsilon || Abs(lprm - 2. * M_PI) > aRealEpsilon)
        {
          //==============================================
          ////
          IntTools_Curve            aCurve;
          Handle(Geom_TrimmedCurve) aTC3D = new Geom_TrimmedCurve(newc, fprm, lprm);
          aCurve.SetCurve(aTC3D);
          fprm = aTC3D->FirstParameter();
          lprm = aTC3D->LastParameter();
          ////
          if (typl == IntPatch_Circle || typl == IntPatch_Ellipse)
          { ////
            if (myApprox1)
            {
              Handle(Geom2d_Curve) C2d;
              GeomInt_IntSS::BuildPCurves(fprm,
                                          lprm,
                                          myHS1->FirstUParameter(),
                                          myHS1->LastUParameter(),
                                          myHS1->FirstVParameter(),
                                          myHS1->LastVParameter(),
                                          Tolpc,
                                          myHS1->Surface(),
                                          newc,
                                          C2d);
              aCurve.SetFirstCurve2d(C2d);
            }

            if (myApprox2)
            {
              Handle(Geom2d_Curve) C2d;
              GeomInt_IntSS::BuildPCurves(fprm,
                                          lprm,
                                          myHS2->FirstUParameter(),
                                          myHS2->LastUParameter(),
                                          myHS2->FirstVParameter(),
                                          myHS2->LastVParameter(),
                                          Tolpc,
                                          myHS2->Surface(),
                                          newc,
                                          C2d);
              aCurve.SetSecondCurve2d(C2d);
            }
          }
          //
          mySeqOfCurve.Append(aCurve);
          //==============================================
        } // if (Abs(fprm) > RealEpsilon() || Abs(lprm-2.*M_PI) > RealEpsilon())

        else
        {
          //  on regarde si on garde
          //
          if (aNbParts == 1)
          {
            //           if (Abs(fprm) < RealEpsilon() &&  Abs(lprm-2.*M_PI) < RealEpsilon()) {
            if (Abs(fprm) <= aRealEpsilon && Abs(lprm - 2. * M_PI) <= aRealEpsilon)
            {
              IntTools_Curve            aCurve;
              Handle(Geom_TrimmedCurve) aTC3D = new Geom_TrimmedCurve(newc, fprm, lprm);
              aCurve.SetCurve(aTC3D);
              fprm = aTC3D->FirstParameter();
              lprm = aTC3D->LastParameter();

              if (myApprox1)
              {
                Handle(Geom2d_Curve) C2d;
                GeomInt_IntSS::BuildPCurves(fprm, lprm, Tolpc, myHS1->Surface(), newc, C2d);
                aCurve.SetFirstCurve2d(C2d);
              }

              if (myApprox2)
              {
                Handle(Geom2d_Curve) C2d;
                GeomInt_IntSS::BuildPCurves(fprm, lprm, Tolpc, myHS2->Surface(), newc, C2d);
                aCurve.SetSecondCurve2d(C2d);
              }
              //
              mySeqOfCurve.Append(aCurve);
              break;
            }
          }
          //
          Standard_Real aTwoPIdiv17, u1, v1, u2, v2, Tol;

          aTwoPIdiv17 = 2. * M_PI / 17.;

          for (j = 0; j <= 17; j++)
          {
            gp_Pnt ptref(newc->Value(j * aTwoPIdiv17));
            Tol = Precision::Confusion();

            Parameters(myHS1, myHS2, ptref, u1, v1, u2, v2);
            ok = (dom1->Classify(gp_Pnt2d(u1, v1), Tol) != TopAbs_OUT);
            if (ok)
            {
              ok = (dom2->Classify(gp_Pnt2d(u2, v2), Tol) != TopAbs_OUT);
            }
            if (ok)
            {
              IntTools_Curve aCurve;
              aCurve.SetCurve(newc);
              //==============================================
              if (typl == IntPatch_Circle || typl == IntPatch_Ellipse)
              {

                if (myApprox1)
                {
                  Handle(Geom2d_Curve) C2d;
                  GeomInt_IntSS::BuildPCurves(fprm,
                                              lprm,
                                              myHS1->FirstUParameter(),
                                              myHS1->LastUParameter(),
                                              myHS1->FirstVParameter(),
                                              myHS1->LastVParameter(),
                                              Tolpc,
                                              myHS1->Surface(),
                                              newc,
                                              C2d);
                  aCurve.SetFirstCurve2d(C2d);
                }

                if (myApprox2)
                {
                  Handle(Geom2d_Curve) C2d;
                  GeomInt_IntSS::BuildPCurves(fprm,
                                              lprm,
                                              myHS2->FirstUParameter(),
                                              myHS2->LastUParameter(),
                                              myHS2->FirstVParameter(),
                                              myHS2->LastVParameter(),
                                              Tolpc,
                                              myHS2->Surface(),
                                              newc,
                                              C2d);
                  aCurve.SetSecondCurve2d(C2d);
                }
              } //  end of if (typl == IntPatch_Circle || typl == IntPatch_Ellipse)
              //==============================================
              //
              mySeqOfCurve.Append(aCurve);
              break;

            } //  end of if (ok) {
          } //  end of for (Standard_Integer j=0; j<=17; j++)
        } //  end of else { on regarde si on garde
      } // for (i=1; i<=myLConstruct.NbParts(); i++)
    } // IntPatch_Circle: IntPatch_Ellipse:
    break;

    case IntPatch_Analytic:
      // This case was processed earlier (in IntPatch_Intersection)
      break;

    case IntPatch_Walking: {
      Handle(IntPatch_WLine) WL = Handle(IntPatch_WLine)::DownCast(L);

#ifdef INTTOOLS_FACEFACE_DEBUG
      WL->Dump(0);
#endif

      //
      Standard_Integer ifprm, ilprm;
      //
      if (!myApprox)
      {
        aNbParts = 1;
        if (!bAvoidLineConstructor)
        {
          aNbParts = myLConstruct.NbParts();
        }
        for (i = 1; i <= aNbParts; ++i)
        {
          Handle(Geom2d_BSplineCurve) H1, H2;
          Handle(Geom_Curve)          aBSp;
          //
          if (bAvoidLineConstructor)
          {
            ifprm = 1;
            ilprm = WL->NbPnts();
          }
          else
          {
            myLConstruct.Part(i, fprm, lprm);
            ifprm = (Standard_Integer)fprm;
            ilprm = (Standard_Integer)lprm;
          }
          //
          if (myApprox1)
          {
            H1 = GeomInt_IntSS::MakeBSpline2d(WL, ifprm, ilprm, Standard_True);
          }
          //
          if (myApprox2)
          {
            H2 = GeomInt_IntSS::MakeBSpline2d(WL, ifprm, ilprm, Standard_False);
          }
          //
          aBSp = GeomInt_IntSS::MakeBSpline(WL, ifprm, ilprm);
          IntTools_Curve aIC(aBSp, H1, H2);
          mySeqOfCurve.Append(aIC);
        } // for (i=1; i<=aNbParts; ++i) {
      } // if (!myApprox) {
      //
      else
      { // X
        Standard_Boolean           bIsDecomposited;
        Standard_Integer           nbiter, aNbSeqOfL;
        Standard_Real              tol2d, aTolApproxImp;
        IntPatch_SequenceOfLine    aSeqOfL;
        GeomInt_WLApprox           theapp3d;
        Approx_ParametrizationType aParType = Approx_ChordLength;
        //
        Standard_Boolean anApprox1 = myApprox1;
        Standard_Boolean anApprox2 = myApprox2;
        //
        aTolApproxImp = 1.e-5;
        tol2d         = myTolApprox;

        GeomAbs_SurfaceType typs1, typs2;
        typs1                     = myHS1->GetType();
        typs2                     = myHS2->GetType();
        Standard_Boolean anWithPC = Standard_True;

        if (typs1 == GeomAbs_Cylinder && typs2 == GeomAbs_Sphere)
        {
          anWithPC = ApproxWithPCurves(myHS1->Cylinder(), myHS2->Sphere());
        }
        else if (typs1 == GeomAbs_Sphere && typs2 == GeomAbs_Cylinder)
        {
          anWithPC = ApproxWithPCurves(myHS2->Cylinder(), myHS1->Sphere());
        }
        //
        if (!anWithPC)
        {
          myTolApprox = aTolApproxImp; // 1.e-5;
          anApprox1   = Standard_False;
          anApprox2   = Standard_False;
          //
          tol2d = myTolApprox;
        }

        bIsDecomposited = IntTools_WLineTool::DecompositionOfWLine(WL,
                                                                   myHS1,
                                                                   myHS2,
                                                                   myFace1,
                                                                   myFace2,
                                                                   myLConstruct,
                                                                   bAvoidLineConstructor,
                                                                   myTol,
                                                                   aSeqOfL,
                                                                   myContext);
        //
        aNbSeqOfL = aSeqOfL.Length();
        //
        Standard_Real aTolC = 0.;
        if (bIsDecomposited)
        {
          nbiter = aNbSeqOfL;
          aTolC  = Precision::Confusion();
        }
        else
        {
          nbiter   = 1;
          aNbParts = 1;
          if (!bAvoidLineConstructor)
          {
            aNbParts = myLConstruct.NbParts();
            nbiter   = aNbParts;
          }
        }
        //
        for (i = 1; i <= nbiter; ++i)
        {
          if (bIsDecomposited)
          {
            WL    = Handle(IntPatch_WLine)::DownCast(aSeqOfL.Value(i));
            ifprm = 1;
            ilprm = WL->NbPnts();
          }
          else
          {
            if (bAvoidLineConstructor)
            {
              ifprm = 1;
              ilprm = WL->NbPnts();
            }
            else
            {
              myLConstruct.Part(i, fprm, lprm);
              ifprm = (Standard_Integer)fprm;
              ilprm = (Standard_Integer)lprm;
            }
          }

          Standard_Boolean anApprox = myApprox;
          if (typs1 == GeomAbs_Plane)
          {
            anApprox  = Standard_False;
            anApprox1 = Standard_True;
          }
          else if (typs2 == GeomAbs_Plane)
          {
            anApprox  = Standard_False;
            anApprox2 = Standard_True;
          }

          aParType =
            ApproxInt_KnotTools::DefineParType(WL, ifprm, ilprm, anApprox, anApprox1, anApprox2);
          if (myHS1 == myHS2)
          {
            theapp3d.SetParameters(myTolApprox, tol2d, 4, 8, 0, 30, Standard_False, aParType);
            rejectSurface = Standard_True;
          }
          else
          {
            if (reApprox && !rejectSurface)
              theapp3d.SetParameters(myTolApprox, tol2d, 4, 8, 0, 30, Standard_False, aParType);
            else
            {
              Standard_Integer iDegMax, iDegMin, iNbIter;
              //
              ApproxParameters(myHS1, myHS2, iDegMin, iDegMax, iNbIter);
              theapp3d.SetParameters(myTolApprox,
                                     tol2d,
                                     iDegMin,
                                     iDegMax,
                                     iNbIter,
                                     30,
                                     Standard_True,
                                     aParType);
            }
          }

          //-- lbr :
          //-- Si une des surfaces est un plan , on approxime en 2d
          //-- sur cette surface et on remonte les points 2d en 3d.
          if (typs1 == GeomAbs_Plane)
          {
            theapp3d
              .Perform(myHS1, myHS2, WL, Standard_False, Standard_True, myApprox2, ifprm, ilprm);
          }
          else if (typs2 == GeomAbs_Plane)
          {
            theapp3d
              .Perform(myHS1, myHS2, WL, Standard_False, myApprox1, Standard_True, ifprm, ilprm);
          }
          else
          {
            //
            if (myHS1 != myHS2)
            {
              if ((typs1 == GeomAbs_BezierSurface || typs1 == GeomAbs_BSplineSurface)
                  && (typs2 == GeomAbs_BezierSurface || typs2 == GeomAbs_BSplineSurface))
              {

                theapp3d.SetParameters(myTolApprox, tol2d, 4, 8, 0, 30, Standard_True, aParType);

                Standard_Boolean bUseSurfaces;
                bUseSurfaces =
                  IntTools_WLineTool::NotUseSurfacesForApprox(myFace1, myFace2, WL, ifprm, ilprm);
                if (bUseSurfaces)
                {
                  // ######
                  rejectSurface = Standard_True;
                  // ######
                  theapp3d.SetParameters(myTolApprox, tol2d, 4, 8, 0, 30, Standard_False, aParType);
                }
              }
            }
            //
            theapp3d.Perform(myHS1, myHS2, WL, Standard_True, anApprox1, anApprox2, ifprm, ilprm);
          }
          //
          if (!theapp3d.IsDone())
          {
            Handle(Geom2d_BSplineCurve) H1;
            Handle(Geom2d_BSplineCurve) H2;
            //
            Handle(Geom_Curve) aBSp = GeomInt_IntSS::MakeBSpline(WL, ifprm, ilprm);
            //
            if (myApprox1)
            {
              H1 = GeomInt_IntSS::MakeBSpline2d(WL, ifprm, ilprm, Standard_True);
            }
            //
            if (myApprox2)
            {
              H2 = GeomInt_IntSS::MakeBSpline2d(WL, ifprm, ilprm, Standard_False);
            }
            //
            IntTools_Curve aIC(aBSp, H1, H2);
            mySeqOfCurve.Append(aIC);
          }
          else
          {
            if (typs1 == GeomAbs_Plane || typs2 == GeomAbs_Plane)
            {
              //
              if (typs1 == GeomAbs_Torus || typs2 == GeomAbs_Torus)
              {
                if (aTolC < 1.e-6)
                {
                  aTolC = 1.e-6;
                }
              }
            }
            //
            Standard_Integer aNbMultiCurves, nbpoles;
            aNbMultiCurves = theapp3d.NbMultiCurves();
            for (j = 1; j <= aNbMultiCurves; j++)
            {
              if (typs1 == GeomAbs_Plane)
              {
                const AppParCurves_MultiBSpCurve& mbspc = theapp3d.Value(j);
                nbpoles                                 = mbspc.NbPoles();

                TColgp_Array1OfPnt2d tpoles2d(1, nbpoles);
                TColgp_Array1OfPnt   tpoles(1, nbpoles);

                mbspc.Curve(1, tpoles2d);
                const gp_Pln& Pln = myHS1->Plane();
                //
                Standard_Integer ik;
                for (ik = 1; ik <= nbpoles; ik++)
                {
                  tpoles.SetValue(
                    ik,
                    ElSLib::Value(tpoles2d.Value(ik).X(), tpoles2d.Value(ik).Y(), Pln));
                }
                //
                Handle(Geom_BSplineCurve) BS = new Geom_BSplineCurve(tpoles,
                                                                     mbspc.Knots(),
                                                                     mbspc.Multiplicities(),
                                                                     mbspc.Degree());
                GeomLib_CheckBSplineCurve Check(BS, TOLCHECK, TOLANGCHECK);
                Check.FixTangent(Standard_True, Standard_True);
                //
                IntTools_Curve aCurve;
                aCurve.SetCurve(BS);

                if (myApprox1)
                {
                  Handle(Geom2d_BSplineCurve) BS1 = new Geom2d_BSplineCurve(tpoles2d,
                                                                            mbspc.Knots(),
                                                                            mbspc.Multiplicities(),
                                                                            mbspc.Degree());
                  GeomLib_Check2dBSplineCurve Check1(BS1, TOLCHECK, TOLANGCHECK);
                  Check1.FixTangent(Standard_True, Standard_True);
                  //
                  // ############################################
                  if (!rejectSurface && !reApprox)
                  {
                    Standard_Boolean isValid = IsCurveValid(BS1);
                    if (!isValid)
                    {
                      reApprox = Standard_True;
                      goto reapprox;
                    }
                  }
                  // ############################################
                  aCurve.SetFirstCurve2d(BS1);
                }

                if (myApprox2)
                {
                  mbspc.Curve(2, tpoles2d);

                  Handle(Geom2d_BSplineCurve) BS2 = new Geom2d_BSplineCurve(tpoles2d,
                                                                            mbspc.Knots(),
                                                                            mbspc.Multiplicities(),
                                                                            mbspc.Degree());
                  GeomLib_Check2dBSplineCurve newCheck(BS2, TOLCHECK, TOLANGCHECK);
                  newCheck.FixTangent(Standard_True, Standard_True);

                  // ###########################################
                  if (!rejectSurface && !reApprox)
                  {
                    Standard_Boolean isValid = IsCurveValid(BS2);
                    if (!isValid)
                    {
                      reApprox = Standard_True;
                      goto reapprox;
                    }
                  }
                  // ###########################################
                  //
                  aCurve.SetSecondCurve2d(BS2);
                }
                //
                aCurve.SetTolerance(aTolC);
                //
                mySeqOfCurve.Append(aCurve);

              } // if(typs1 == GeomAbs_Plane) {

              else if (typs2 == GeomAbs_Plane)
              {
                const AppParCurves_MultiBSpCurve& mbspc = theapp3d.Value(j);
                nbpoles                                 = mbspc.NbPoles();

                TColgp_Array1OfPnt2d tpoles2d(1, nbpoles);
                TColgp_Array1OfPnt   tpoles(1, nbpoles);
                mbspc.Curve((myApprox1 == Standard_True) ? 2 : 1, tpoles2d);
                const gp_Pln& Pln = myHS2->Plane();
                //
                Standard_Integer ik;
                for (ik = 1; ik <= nbpoles; ik++)
                {
                  tpoles.SetValue(
                    ik,
                    ElSLib::Value(tpoles2d.Value(ik).X(), tpoles2d.Value(ik).Y(), Pln));
                }
                //
                Handle(Geom_BSplineCurve) BS = new Geom_BSplineCurve(tpoles,
                                                                     mbspc.Knots(),
                                                                     mbspc.Multiplicities(),
                                                                     mbspc.Degree());
                GeomLib_CheckBSplineCurve Check(BS, TOLCHECK, TOLANGCHECK);
                Check.FixTangent(Standard_True, Standard_True);
                //
                IntTools_Curve aCurve;
                aCurve.SetCurve(BS);
                aCurve.SetTolerance(aTolC);

                if (myApprox2)
                {
                  Handle(Geom2d_BSplineCurve) BS1 = new Geom2d_BSplineCurve(tpoles2d,
                                                                            mbspc.Knots(),
                                                                            mbspc.Multiplicities(),
                                                                            mbspc.Degree());
                  GeomLib_Check2dBSplineCurve Check1(BS1, TOLCHECK, TOLANGCHECK);
                  Check1.FixTangent(Standard_True, Standard_True);
                  //
                  // ###########################################
                  if (!rejectSurface && !reApprox)
                  {
                    Standard_Boolean isValid = IsCurveValid(BS1);
                    if (!isValid)
                    {
                      reApprox = Standard_True;
                      goto reapprox;
                    }
                  }
                  // ###########################################
                  bPCurvesOk = CheckPCurve(BS1, myFace2, myContext);
                  aCurve.SetSecondCurve2d(BS1);
                }

                if (myApprox1)
                {
                  mbspc.Curve(1, tpoles2d);
                  Handle(Geom2d_BSplineCurve) BS2 = new Geom2d_BSplineCurve(tpoles2d,
                                                                            mbspc.Knots(),
                                                                            mbspc.Multiplicities(),
                                                                            mbspc.Degree());
                  GeomLib_Check2dBSplineCurve Check2(BS2, TOLCHECK, TOLANGCHECK);
                  Check2.FixTangent(Standard_True, Standard_True);
                  //
                  // ###########################################
                  if (!rejectSurface && !reApprox)
                  {
                    Standard_Boolean isValid = IsCurveValid(BS2);
                    if (!isValid)
                    {
                      reApprox = Standard_True;
                      goto reapprox;
                    }
                  }
                  // ###########################################
                  bPCurvesOk = bPCurvesOk && CheckPCurve(BS2, myFace1, myContext);
                  aCurve.SetFirstCurve2d(BS2);
                }
                //
                // if points of the pcurves are out of the faces bounds
                // create 3d and 2d curves without approximation
                if (!bPCurvesOk)
                {
                  Handle(Geom2d_BSplineCurve) H1, H2;
                  bPCurvesOk = Standard_True;
                  //
                  Handle(Geom_Curve) aBSp = GeomInt_IntSS::MakeBSpline(WL, ifprm, ilprm);

                  if (myApprox1)
                  {
                    H1         = GeomInt_IntSS::MakeBSpline2d(WL, ifprm, ilprm, Standard_True);
                    bPCurvesOk = CheckPCurve(H1, myFace1, myContext);
                  }

                  if (myApprox2)
                  {
                    H2         = GeomInt_IntSS::MakeBSpline2d(WL, ifprm, ilprm, Standard_False);
                    bPCurvesOk = bPCurvesOk && CheckPCurve(H2, myFace2, myContext);
                  }
                  //
                  // if pcurves created without approximation are out of the
                  // faces bounds, use approximated 3d and 2d curves
                  if (bPCurvesOk)
                  {
                    IntTools_Curve aIC(aBSp, H1, H2, aTolC);
                    mySeqOfCurve.Append(aIC);
                  }
                  else
                  {
                    mySeqOfCurve.Append(aCurve);
                  }
                }
                else
                {
                  mySeqOfCurve.Append(aCurve);
                }

              } // else if(typs2 == GeomAbs_Plane)
              //
              else
              { // typs2 != GeomAbs_Plane && typs1 != GeomAbs_Plane
                Standard_Boolean          bIsValid1, bIsValid2;
                Handle(Geom_BSplineCurve) BS;
                IntTools_Curve            aCurve;
                //
                bIsValid1 = Standard_True;
                bIsValid2 = Standard_True;
                //
                const AppParCurves_MultiBSpCurve& mbspc = theapp3d.Value(j);
                nbpoles                                 = mbspc.NbPoles();
                TColgp_Array1OfPnt tpoles(1, nbpoles);
                mbspc.Curve(1, tpoles);
                BS = new Geom_BSplineCurve(tpoles,
                                           mbspc.Knots(),
                                           mbspc.Multiplicities(),
                                           mbspc.Degree());
                GeomLib_CheckBSplineCurve Check(BS, TOLCHECK, TOLANGCHECK);
                Check.FixTangent(Standard_True, Standard_True);
                //
                aCurve.SetCurve(BS);
                aCurve.SetTolerance(aTolC);
                //
                if (myApprox1)
                {
                  if (anApprox1)
                  {
                    Handle(Geom2d_BSplineCurve) BS1;
                    TColgp_Array1OfPnt2d        tpoles2d(1, nbpoles);
                    mbspc.Curve(2, tpoles2d);
                    //
                    BS1 = new Geom2d_BSplineCurve(tpoles2d,
                                                  mbspc.Knots(),
                                                  mbspc.Multiplicities(),
                                                  mbspc.Degree());
                    GeomLib_Check2dBSplineCurve newCheck(BS1, TOLCHECK, TOLANGCHECK);
                    newCheck.FixTangent(Standard_True, Standard_True);
                    //
                    if (!reApprox)
                    {
                      bIsValid1 = CheckPCurve(BS1, myFace1, myContext);
                    }
                    //
                    aCurve.SetFirstCurve2d(BS1);
                  }
                  else
                  {
                    Handle(Geom2d_BSplineCurve) BS1;
                    fprm = BS->FirstParameter();
                    lprm = BS->LastParameter();

                    Handle(Geom2d_Curve) C2d;
                    Standard_Real        aTol = myTolApprox;
                    GeomInt_IntSS::BuildPCurves(fprm, lprm, aTol, myHS1->Surface(), BS, C2d);
                    BS1 = Handle(Geom2d_BSplineCurve)::DownCast(C2d);
                    aCurve.SetFirstCurve2d(BS1);
                  }
                } // if(myApprox1) {
                  //
                if (myApprox2)
                {
                  if (anApprox2)
                  {
                    Handle(Geom2d_BSplineCurve) BS2;
                    TColgp_Array1OfPnt2d        tpoles2d(1, nbpoles);
                    mbspc.Curve((myApprox1 == Standard_True) ? 3 : 2, tpoles2d);
                    BS2 = new Geom2d_BSplineCurve(tpoles2d,
                                                  mbspc.Knots(),
                                                  mbspc.Multiplicities(),
                                                  mbspc.Degree());
                    GeomLib_Check2dBSplineCurve newCheck(BS2, TOLCHECK, TOLANGCHECK);
                    newCheck.FixTangent(Standard_True, Standard_True);
                    //
                    if (!reApprox)
                    {
                      bIsValid2 = CheckPCurve(BS2, myFace2, myContext);
                    }
                    aCurve.SetSecondCurve2d(BS2);
                  }
                  else
                  {
                    Handle(Geom2d_BSplineCurve) BS2;
                    fprm = BS->FirstParameter();
                    lprm = BS->LastParameter();

                    Handle(Geom2d_Curve) C2d;
                    Standard_Real        aTol = myTolApprox;
                    GeomInt_IntSS::BuildPCurves(fprm, lprm, aTol, myHS2->Surface(), BS, C2d);
                    BS2 = Handle(Geom2d_BSplineCurve)::DownCast(C2d);
                    aCurve.SetSecondCurve2d(BS2);
                  }
                } // if(myApprox2) {
                if (!bIsValid1 || !bIsValid2)
                {
                  myTolApprox = aTolApproxImp; // 1.e-5;
                  tol2d       = myTolApprox;
                  reApprox    = Standard_True;
                  goto reapprox;
                }
                //
                mySeqOfCurve.Append(aCurve);
              }
            }
          }
        }
      } // else { // X
    } // case IntPatch_Walking:{
    break;

    case IntPatch_Restriction: {
      Handle(IntPatch_RLine) RL = Handle(IntPatch_RLine)::DownCast(L);

#ifdef INTTOOLS_FACEFACE_DEBUG
      RL->Dump(0);
#endif

      Handle(Geom_Curve)   aC3d;
      Handle(Geom2d_Curve) aC2d1, aC2d2;
      Standard_Real        aTolReached;
      GeomInt_IntSS::TreatRLine(RL, myHS1, myHS2, aC3d, aC2d1, aC2d2, aTolReached);

      if (aC3d.IsNull())
        break;

      Bnd_Box2d aBox1, aBox2;

      const Standard_Real aU1f = myHS1->FirstUParameter(), aV1f = myHS1->FirstVParameter(),
                          aU1l = myHS1->LastUParameter(), aV1l = myHS1->LastVParameter();
      const Standard_Real aU2f = myHS2->FirstUParameter(), aV2f = myHS2->FirstVParameter(),
                          aU2l = myHS2->LastUParameter(), aV2l = myHS2->LastVParameter();

      aBox1.Add(gp_Pnt2d(aU1f, aV1f));
      aBox1.Add(gp_Pnt2d(aU1l, aV1l));
      aBox2.Add(gp_Pnt2d(aU2f, aV2f));
      aBox2.Add(gp_Pnt2d(aU2l, aV2l));

      GeomInt_VectorOfReal anArrayOfParameters;

      // We consider here that the intersection line is same-parameter-line
      anArrayOfParameters.Append(aC3d->FirstParameter());
      anArrayOfParameters.Append(aC3d->LastParameter());

      GeomInt_IntSS::TrimILineOnSurfBoundaries(aC2d1, aC2d2, aBox1, aBox2, anArrayOfParameters);

      // Intersect with true boundaries. After that, enlarge bounding-boxes in order to
      // correct definition, if point on curve is inscribed in the box.
      aBox1.Enlarge(theToler);
      aBox2.Enlarge(theToler);

      const Standard_Integer aNbIntersSolutionsm1 = anArrayOfParameters.Length() - 1;

      // Trim RLine found.
      for (Standard_Integer anInd = 0; anInd < aNbIntersSolutionsm1; anInd++)
      {
        Standard_Real &aParF = anArrayOfParameters(anInd), &aParL = anArrayOfParameters(anInd + 1);

        if ((aParL - aParF) <= Precision::PConfusion())
        {
          // In order to more precise extending to the boundaries of source curves.
          if (anInd < aNbIntersSolutionsm1 - 1)
            aParL = aParF;

          continue;
        }

        const Standard_Real aPar = 0.5 * (aParF + aParL);
        gp_Pnt2d            aPt;

        Handle(Geom2d_Curve) aCurv2d1, aCurv2d2;
        if (!aC2d1.IsNull())
        {
          aC2d1->D0(aPar, aPt);

          if (aBox1.IsOut(aPt))
            continue;

          if (myApprox1)
            aCurv2d1 = new Geom2d_TrimmedCurve(aC2d1, aParF, aParL);
        }

        if (!aC2d2.IsNull())
        {
          aC2d2->D0(aPar, aPt);

          if (aBox2.IsOut(aPt))
            continue;

          if (myApprox2)
            aCurv2d2 = new Geom2d_TrimmedCurve(aC2d2, aParF, aParL);
        }

        Handle(Geom_Curve) aCurv3d = new Geom_TrimmedCurve(aC3d, aParF, aParL);

        IntTools_Curve aIC(aCurv3d, aCurv2d1, aCurv2d2);
        mySeqOfCurve.Append(aIC);
      }
    }
    break;
    default:
      break;
  }
}

//=================================================================================================

void Parameters(const Handle(GeomAdaptor_Surface)& HS1,
                const Handle(GeomAdaptor_Surface)& HS2,
                const gp_Pnt&                      Ptref,
                Standard_Real&                     U1,
                Standard_Real&                     V1,
                Standard_Real&                     U2,
                Standard_Real&                     V2)
{

  IntSurf_Quadric     quad1, quad2;
  GeomAbs_SurfaceType typs = HS1->GetType();

  switch (typs)
  {
    case GeomAbs_Plane:
      quad1.SetValue(HS1->Plane());
      break;
    case GeomAbs_Cylinder:
      quad1.SetValue(HS1->Cylinder());
      break;
    case GeomAbs_Cone:
      quad1.SetValue(HS1->Cone());
      break;
    case GeomAbs_Sphere:
      quad1.SetValue(HS1->Sphere());
      break;
    case GeomAbs_Torus:
      quad1.SetValue(HS1->Torus());
      break;
    default:
      throw Standard_ConstructionError("GeomInt_IntSS::MakeCurve");
  }

  typs = HS2->GetType();
  switch (typs)
  {
    case GeomAbs_Plane:
      quad2.SetValue(HS2->Plane());
      break;
    case GeomAbs_Cylinder:
      quad2.SetValue(HS2->Cylinder());
      break;
    case GeomAbs_Cone:
      quad2.SetValue(HS2->Cone());
      break;
    case GeomAbs_Sphere:
      quad2.SetValue(HS2->Sphere());
      break;
    case GeomAbs_Torus:
      quad2.SetValue(HS2->Torus());
      break;
    default:
      throw Standard_ConstructionError("GeomInt_IntSS::MakeCurve");
  }

  quad1.Parameters(Ptref, U1, V1);
  quad2.Parameters(Ptref, U2, V2);
}

//=================================================================================================

Handle(Geom_Curve) MakeBSpline(const Handle(IntPatch_WLine)& WL,
                               const Standard_Integer        ideb,
                               const Standard_Integer        ifin)
{
  Standard_Integer        i, nbpnt = ifin - ideb + 1;
  TColgp_Array1OfPnt      poles(1, nbpnt);
  TColStd_Array1OfReal    knots(1, nbpnt);
  TColStd_Array1OfInteger mults(1, nbpnt);
  Standard_Integer        ipidebm1;
  for (i = 1, ipidebm1 = i + ideb - 1; i <= nbpnt; ipidebm1++, i++)
  {
    poles(i) = WL->Point(ipidebm1).Value();
    mults(i) = 1;
    knots(i) = i - 1;
  }
  mults(1) = mults(nbpnt) = 2;
  return new Geom_BSplineCurve(poles, knots, mults, 1);
}

//=================================================================================================

void IntTools_FaceFace::PrepareLines3D(const Standard_Boolean bToSplit)
{
  Standard_Integer          i, aNbCurves;
  GeomAbs_SurfaceType       aType1, aType2;
  IntTools_SequenceOfCurves aNewCvs;
  //
  // 1. Treatment closed  curves
  aNbCurves = mySeqOfCurve.Length();
  for (i = 1; i <= aNbCurves; ++i)
  {
    const IntTools_Curve& aIC = mySeqOfCurve(i);
    //
    if (bToSplit)
    {
      Standard_Integer          j, aNbC;
      IntTools_SequenceOfCurves aSeqCvs;
      //
      aNbC = IntTools_Tools::SplitCurve(aIC, aSeqCvs);
      if (aNbC)
      {
        for (j = 1; j <= aNbC; ++j)
        {
          const IntTools_Curve& aICNew = aSeqCvs(j);
          aNewCvs.Append(aICNew);
        }
      }
      else
      {
        aNewCvs.Append(aIC);
      }
    }
    else
    {
      aNewCvs.Append(aIC);
    }
  }
  //
  // 2. Plane\Cone intersection when we had 4 curves
  aType1    = myHS1->GetType();
  aType2    = myHS2->GetType();
  aNbCurves = aNewCvs.Length();
  //
  if ((aType1 == GeomAbs_Plane && aType2 == GeomAbs_Cone)
      || (aType2 == GeomAbs_Plane && aType1 == GeomAbs_Cone))
  {
    if (aNbCurves == 4)
    {
      GeomAbs_CurveType aCType1;
      //
      aCType1 = aNewCvs(1).Type();
      if (aCType1 == GeomAbs_Line)
      {
        IntTools_SequenceOfCurves aSeqIn, aSeqOut;
        //
        for (i = 1; i <= aNbCurves; ++i)
        {
          const IntTools_Curve& aIC = aNewCvs(i);
          aSeqIn.Append(aIC);
        }
        //
        IntTools_Tools::RejectLines(aSeqIn, aSeqOut);
        //
        aNewCvs.Clear();
        aNbCurves = aSeqOut.Length();
        for (i = 1; i <= aNbCurves; ++i)
        {
          const IntTools_Curve& aIC = aSeqOut(i);
          aNewCvs.Append(aIC);
        }
      }
    }
  } // if ((aType1==GeomAbs_Plane && aType2==GeomAbs_Cone)...
  //
  // 3. Fill  mySeqOfCurve
  mySeqOfCurve.Clear();
  aNbCurves = aNewCvs.Length();
  for (i = 1; i <= aNbCurves; ++i)
  {
    const IntTools_Curve& aIC = aNewCvs(i);
    mySeqOfCurve.Append(aIC);
  }
}

//=================================================================================================

void CorrectSurfaceBoundaries(const TopoDS_Face&  theFace,
                              const Standard_Real theTolerance,
                              Standard_Real&      theumin,
                              Standard_Real&      theumax,
                              Standard_Real&      thevmin,
                              Standard_Real&      thevmax)
{
  Standard_Boolean     enlarge, isuperiodic, isvperiodic;
  Standard_Real        uinf, usup, vinf, vsup, delta;
  GeomAbs_SurfaceType  aType;
  Handle(Geom_Surface) aSurface;
  //
  aSurface = BRep_Tool::Surface(theFace);
  aSurface->Bounds(uinf, usup, vinf, vsup);
  delta   = theTolerance;
  enlarge = Standard_False;
  //
  GeomAdaptor_Surface anAdaptorSurface(aSurface);
  //
  if (aSurface->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface)))
  {
    Handle(Geom_Surface) aBasisSurface =
      (Handle(Geom_RectangularTrimmedSurface)::DownCast(aSurface))->BasisSurface();

    if (aBasisSurface->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface))
        || aBasisSurface->IsKind(STANDARD_TYPE(Geom_OffsetSurface)))
    {
      return;
    }
  }
  //
  if (aSurface->IsKind(STANDARD_TYPE(Geom_OffsetSurface)))
  {
    Handle(Geom_Surface) aBasisSurface =
      (Handle(Geom_OffsetSurface)::DownCast(aSurface))->BasisSurface();

    if (aBasisSurface->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface))
        || aBasisSurface->IsKind(STANDARD_TYPE(Geom_OffsetSurface)))
    {
      return;
    }
  }
  //
  isuperiodic = anAdaptorSurface.IsUPeriodic();
  isvperiodic = anAdaptorSurface.IsVPeriodic();
  //
  aType = anAdaptorSurface.GetType();
  if ((aType == GeomAbs_BezierSurface) || (aType == GeomAbs_BSplineSurface)
      || (aType == GeomAbs_SurfaceOfExtrusion) || (aType == GeomAbs_SurfaceOfRevolution)
      || (aType == GeomAbs_Cylinder))
  {
    enlarge = Standard_True;
  }
  //

  if (!isuperiodic && enlarge)
  {

    if (!Precision::IsInfinite(theumin) && ((theumin - uinf) > delta))
      theumin -= delta;
    else
    {
      theumin = uinf;
    }

    if (!Precision::IsInfinite(theumax) && ((usup - theumax) > delta))
      theumax += delta;
    else
      theumax = usup;
  }

  //
  if (!isvperiodic && enlarge)
  {
    if (!Precision::IsInfinite(thevmin) && ((thevmin - vinf) > delta))
    {
      thevmin -= delta;
    }
    else
    {
      thevmin = vinf;
    }
    if (!Precision::IsInfinite(thevmax) && ((vsup - thevmax) > delta))
    {
      thevmax += delta;
    }
    else
    {
      thevmax = vsup;
    }
  }

  //
  if (isuperiodic || isvperiodic)
  {
    Standard_Boolean correct  = Standard_False;
    Standard_Boolean correctU = Standard_False;
    Standard_Boolean correctV = Standard_False;
    Bnd_Box2d        aBox;
    TopExp_Explorer  anExp;

    for (anExp.Init(theFace, TopAbs_EDGE); anExp.More(); anExp.Next())
    {
      if (BRep_Tool::IsClosed(TopoDS::Edge(anExp.Current()), theFace))
      {
        correct = Standard_True;
        Standard_Real f, l;
        TopoDS_Edge   anEdge = TopoDS::Edge(anExp.Current());

        for (Standard_Integer i = 0; i < 2; i++)
        {
          if (i == 0)
          {
            anEdge.Orientation(TopAbs_FORWARD);
          }
          else
          {
            anEdge.Orientation(TopAbs_REVERSED);
          }
          Handle(Geom2d_Curve) aCurve = BRep_Tool::CurveOnSurface(anEdge, theFace, f, l);

          if (aCurve.IsNull())
          {
            correct = Standard_False;
            break;
          }
          Handle(Geom2d_Line) aLine = Handle(Geom2d_Line)::DownCast(aCurve);

          if (aLine.IsNull())
          {
            correct = Standard_False;
            break;
          }
          gp_Dir2d      anUDir(1., 0.);
          gp_Dir2d      aVDir(0., 1.);
          Standard_Real anAngularTolerance = Precision::Angular();

          correctU =
            correctU || aLine->Position().Direction().IsParallel(aVDir, anAngularTolerance);
          correctV =
            correctV || aLine->Position().Direction().IsParallel(anUDir, anAngularTolerance);

          gp_Pnt2d pp1 = aCurve->Value(f);
          aBox.Add(pp1);
          gp_Pnt2d pp2 = aCurve->Value(l);
          aBox.Add(pp2);
        }
        if (!correct)
          break;
      }
    }

    if (correct)
    {
      Standard_Real umin, vmin, umax, vmax;
      aBox.Get(umin, vmin, umax, vmax);

      if (isuperiodic && correctU)
      {
        if (theumin < umin)
          theumin = umin;
        if (theumax > umax)
        {
          theumax = umax;
        }
      }
      if (isvperiodic && correctV)
      {
        if (thevmin < vmin)
          thevmin = vmin;
        if (thevmax > vmax)
          thevmax = vmax;
      }
    }
  }
}

// ------------------------------------------------------------------------------------------------
// static function: ParameterOutOfBoundary
// purpose:         Computes a new parameter for given curve. The corresponding 2d points
//                  does not lay on any boundary of given faces
// ------------------------------------------------------------------------------------------------
Standard_Boolean ParameterOutOfBoundary(const Standard_Real             theParameter,
                                        const Handle(Geom_Curve)&       theCurve,
                                        const TopoDS_Face&              theFace1,
                                        const TopoDS_Face&              theFace2,
                                        const Standard_Real             theOtherParameter,
                                        const Standard_Boolean          bIncreasePar,
                                        const Standard_Real             theTol,
                                        Standard_Real&                  theNewParameter,
                                        const Handle(IntTools_Context)& aContext)
{
  Standard_Boolean bIsComputed = Standard_False;
  theNewParameter              = theParameter;

  Standard_Real    acurpar    = theParameter;
  TopAbs_State     aState     = TopAbs_ON;
  Standard_Integer iter       = 0;
  Standard_Real    asumtol    = theTol;
  Standard_Real    adelta     = asumtol * 0.1;
  adelta                      = (adelta < Precision::Confusion()) ? Precision::Confusion() : adelta;
  Handle(Geom_Surface) aSurf1 = BRep_Tool::Surface(theFace1);
  Handle(Geom_Surface) aSurf2 = BRep_Tool::Surface(theFace2);

  Standard_Real u1, u2, v1, v2;

  GeomAPI_ProjectPointOnSurf aPrj1;
  aSurf1->Bounds(u1, u2, v1, v2);
  aPrj1.Init(aSurf1, u1, u2, v1, v2);

  GeomAPI_ProjectPointOnSurf aPrj2;
  aSurf2->Bounds(u1, u2, v1, v2);
  aPrj2.Init(aSurf2, u1, u2, v1, v2);

  while (aState == TopAbs_ON)
  {
    if (bIncreasePar)
      acurpar += adelta;
    else
      acurpar -= adelta;
    gp_Pnt aPCurrent = theCurve->Value(acurpar);
    aPrj1.Perform(aPCurrent);
    Standard_Real U = 0., V = 0.;

    if (aPrj1.IsDone())
    {
      aPrj1.LowerDistanceParameters(U, V);
      aState = aContext->StatePointFace(theFace1, gp_Pnt2d(U, V));
    }

    if (aState != TopAbs_ON)
    {
      aPrj2.Perform(aPCurrent);

      if (aPrj2.IsDone())
      {
        aPrj2.LowerDistanceParameters(U, V);
        aState = aContext->StatePointFace(theFace2, gp_Pnt2d(U, V));
      }
    }

    if (iter > 11)
    {
      break;
    }
    iter++;
  }

  if (iter <= 11)
  {
    theNewParameter = acurpar;
    bIsComputed     = Standard_True;

    if (bIncreasePar)
    {
      if (acurpar >= theOtherParameter)
        theNewParameter = theOtherParameter;
    }
    else
    {
      if (acurpar <= theOtherParameter)
        theNewParameter = theOtherParameter;
    }
  }
  return bIsComputed;
}

//=================================================================================================

Standard_Boolean IsCurveValid(const Handle(Geom2d_Curve)& thePCurve)
{
  if (thePCurve.IsNull())
    return Standard_False;

  Standard_Real       tolint = 1.e-10;
  Geom2dAdaptor_Curve PCA;
  IntRes2d_Domain     PCD;
  Geom2dInt_GInter    PCI;

  Standard_Real pf = 0., pl = 0.;
  gp_Pnt2d      pntf, pntl;

  if (!thePCurve->IsClosed() && !thePCurve->IsPeriodic())
  {
    pf   = thePCurve->FirstParameter();
    pl   = thePCurve->LastParameter();
    pntf = thePCurve->Value(pf);
    pntl = thePCurve->Value(pl);
    PCA.Load(thePCurve);
    if (!PCA.IsPeriodic())
    {
      if (PCA.FirstParameter() > pf)
        pf = PCA.FirstParameter();
      if (PCA.LastParameter() < pl)
        pl = PCA.LastParameter();
    }
    PCD.SetValues(pntf, pf, tolint, pntl, pl, tolint);
    PCI.Perform(PCA, PCD, tolint, tolint);
    if (PCI.IsDone())
      if (PCI.NbPoints() > 0)
      {
        return Standard_False;
      }
  }

  return Standard_True;
}

//=======================================================================
// static function : ApproxWithPCurves
// purpose  : for bug 20964 only
//=======================================================================
Standard_Boolean ApproxWithPCurves(const gp_Cylinder& theCyl, const gp_Sphere& theSph)
{
  Standard_Boolean bRes = Standard_True;
  Standard_Real    R1 = theCyl.Radius(), R2 = theSph.Radius();
  //
  {
    Standard_Real aD2, aRc2, aEps;
    gp_Pnt        aApexSph;
    //
    aEps = 1.E-7;
    aRc2 = R1 * R1;
    //
    const gp_Ax3& aAx3Sph = theSph.Position();
    const gp_Pnt& aLocSph = aAx3Sph.Location();
    const gp_Dir& aDirSph = aAx3Sph.Direction();
    //
    const gp_Ax1& aAx1Cyl = theCyl.Axis();
    gp_Lin        aLinCyl(aAx1Cyl);
    //
    aApexSph.SetXYZ(aLocSph.XYZ() + R2 * aDirSph.XYZ());
    aD2 = aLinCyl.SquareDistance(aApexSph);
    if (fabs(aD2 - aRc2) < aEps)
    {
      return !bRes;
    }
    //
    aApexSph.SetXYZ(aLocSph.XYZ() - R2 * aDirSph.XYZ());
    aD2 = aLinCyl.SquareDistance(aApexSph);
    if (fabs(aD2 - aRc2) < aEps)
    {
      return !bRes;
    }
  }
  //

  if (R1 < 2. * R2)
  {
    return bRes;
  }
  gp_Lin anCylAx(theCyl.Axis());

  Standard_Real aDist = anCylAx.Distance(theSph.Location());
  Standard_Real aDRel = Abs(aDist - R1) / R2;

  if (aDRel > .2)
    return bRes;

  Standard_Real par = ElCLib::Parameter(anCylAx, theSph.Location());
  gp_Pnt        aP  = ElCLib::Value(par, anCylAx);
  gp_Vec        aV(aP, theSph.Location());

  Standard_Real dd = aV.Dot(theSph.Position().XDirection());

  if (aDist < R1 && dd > 0.)
    return Standard_False;
  if (aDist > R1 && dd < 0.)
    return Standard_False;

  return bRes;
}

//=================================================================================================

void PerformPlanes(const Handle(GeomAdaptor_Surface)& theS1,
                   const Handle(GeomAdaptor_Surface)& theS2,
                   const Standard_Real                TolF1,
                   const Standard_Real                TolF2,
                   const Standard_Real                TolAng,
                   const Standard_Real                TolTang,
                   const Standard_Boolean             theApprox1,
                   const Standard_Boolean             theApprox2,
                   IntTools_SequenceOfCurves&         theSeqOfCurve,
                   Standard_Boolean&                  theTangentFaces)
{

  gp_Pln aPln1 = theS1->Plane();
  gp_Pln aPln2 = theS2->Plane();

  IntAna_QuadQuadGeo aPlnInter(aPln1, aPln2, TolAng, TolTang);

  if (!aPlnInter.IsDone())
  {
    theTangentFaces = Standard_False;
    return;
  }

  IntAna_ResultType aResType = aPlnInter.TypeInter();

  if (aResType == IntAna_Same)
  {
    theTangentFaces = Standard_True;
    return;
  }

  theTangentFaces = Standard_False;

  if (aResType == IntAna_Empty)
  {
    return;
  }

  gp_Lin aLin = aPlnInter.Line(1);

  ProjLib_Plane aProj;

  aProj.Init(aPln1);
  aProj.Project(aLin);
  gp_Lin2d aLin2d1 = aProj.Line();
  //
  aProj.Init(aPln2);
  aProj.Project(aLin);
  gp_Lin2d aLin2d2 = aProj.Line();
  //
  // classify line2d1 relatively first plane
  Standard_Real    P11, P12;
  Standard_Boolean IsCrossed = ClassifyLin2d(theS1, aLin2d1, TolTang, P11, P12);
  if (!IsCrossed)
    return;
  // classify line2d2 relatively second plane
  Standard_Real P21, P22;
  IsCrossed = ClassifyLin2d(theS2, aLin2d2, TolTang, P21, P22);
  if (!IsCrossed)
    return;

  // Analysis of parametric intervals: must have common part

  if (P21 >= P12)
    return;
  if (P22 <= P11)
    return;

  Standard_Real pmin, pmax;
  pmin = Max(P11, P21);
  pmax = Min(P12, P22);

  if (pmax - pmin <= TolTang)
    return;

  Handle(Geom_Line) aGLin = new Geom_Line(aLin);

  IntTools_Curve            aCurve;
  Handle(Geom_TrimmedCurve) aGTLin = new Geom_TrimmedCurve(aGLin, pmin, pmax);

  aCurve.SetCurve(aGTLin);

  if (theApprox1)
  {
    Handle(Geom2d_Line) C2d = new Geom2d_Line(aLin2d1);
    aCurve.SetFirstCurve2d(new Geom2d_TrimmedCurve(C2d, pmin, pmax));
  }
  else
  {
    Handle(Geom2d_Curve) H1;
    aCurve.SetFirstCurve2d(H1);
  }
  if (theApprox2)
  {
    Handle(Geom2d_Line) C2d = new Geom2d_Line(aLin2d2);
    aCurve.SetSecondCurve2d(new Geom2d_TrimmedCurve(C2d, pmin, pmax));
  }
  else
  {
    Handle(Geom2d_Curve) H1;
    aCurve.SetFirstCurve2d(H1);
  }
  //
  // Valid tolerance for the intersection curve between planar faces
  // is the maximal tolerance between tolerances of faces
  Standard_Real aTolC = Max(TolF1, TolF2);
  aCurve.SetTolerance(aTolC);
  //
  // Computation of the tangential tolerance
  Standard_Real anAngle, aDt;
  gp_Dir        aD1, aD2;
  //
  aD1     = aPln1.Position().Direction();
  aD2     = aPln2.Position().Direction();
  anAngle = aD1.Angle(aD2);
  //
  aDt                    = IntTools_Tools::ComputeIntRange(TolF1, TolF2, anAngle);
  Standard_Real aTangTol = sqrt(aDt * aDt + TolF1 * TolF1);
  //
  aCurve.SetTangentialTolerance(aTangTol);
  //
  theSeqOfCurve.Append(aCurve);
}

//=================================================================================================

static inline Standard_Boolean INTER(const Standard_Real d1,
                                     const Standard_Real d2,
                                     const Standard_Real tol)
{
  return (d1 > tol && d2 < -tol) || (d1 < -tol && d2 > tol)
         || ((d1 <= tol && d1 >= -tol) && (d2 > tol || d2 < -tol))
         || ((d2 <= tol && d2 >= -tol) && (d1 > tol || d1 < -tol));
}

static inline Standard_Boolean COINC(const Standard_Real d1,
                                     const Standard_Real d2,
                                     const Standard_Real tol)
{
  return (d1 <= tol && d1 >= -tol) && (d2 <= tol && d2 >= -tol);
}

Standard_Boolean ClassifyLin2d(const Handle(GeomAdaptor_Surface)& theS,
                               const gp_Lin2d&                    theLin2d,
                               const Standard_Real                theTol,
                               Standard_Real&                     theP1,
                               Standard_Real&                     theP2)

{
  Standard_Real    xmin, xmax, ymin, ymax, d1, d2, A, B, C;
  Standard_Real    par[2];
  Standard_Integer nbi = 0;

  xmin = theS->FirstUParameter();
  xmax = theS->LastUParameter();
  ymin = theS->FirstVParameter();
  ymax = theS->LastVParameter();

  theLin2d.Coefficients(A, B, C);

  // xmin, ymin <-> xmin, ymax
  d1 = A * xmin + B * ymin + C;
  d2 = A * xmin + B * ymax + C;

  if (INTER(d1, d2, theTol))
  {
    // Intersection with boundary
    Standard_Real y = -(C + A * xmin) / B;
    par[nbi]        = ElCLib::Parameter(theLin2d, gp_Pnt2d(xmin, y));
    nbi++;
  }
  else if (COINC(d1, d2, theTol))
  {
    // Coincidence with boundary
    par[0] = ElCLib::Parameter(theLin2d, gp_Pnt2d(xmin, ymin));
    par[1] = ElCLib::Parameter(theLin2d, gp_Pnt2d(xmin, ymax));
    nbi    = 2;
  }

  if (nbi == 2)
  {

    if (fabs(par[0] - par[1]) > theTol)
    {
      theP1 = Min(par[0], par[1]);
      theP2 = Max(par[0], par[1]);
      return Standard_True;
    }
    else
      return Standard_False;
  }

  // xmin, ymax <-> xmax, ymax
  d1 = d2;
  d2 = A * xmax + B * ymax + C;

  if (d1 > theTol || d1 < -theTol)
  { // to avoid checking of
    // coincidence with the same point
    if (INTER(d1, d2, theTol))
    {
      Standard_Real x = -(C + B * ymax) / A;
      par[nbi]        = ElCLib::Parameter(theLin2d, gp_Pnt2d(x, ymax));
      nbi++;
    }
    else if (COINC(d1, d2, theTol))
    {
      par[0] = ElCLib::Parameter(theLin2d, gp_Pnt2d(xmin, ymax));
      par[1] = ElCLib::Parameter(theLin2d, gp_Pnt2d(xmax, ymax));
      nbi    = 2;
    }
  }

  if (nbi == 2)
  {

    if (fabs(par[0] - par[1]) > theTol)
    {
      theP1 = Min(par[0], par[1]);
      theP2 = Max(par[0], par[1]);
      return Standard_True;
    }
    else
      return Standard_False;
  }

  // xmax, ymax <-> xmax, ymin
  d1 = d2;
  d2 = A * xmax + B * ymin + C;

  if (d1 > theTol || d1 < -theTol)
  {
    if (INTER(d1, d2, theTol))
    {
      Standard_Real y = -(C + A * xmax) / B;
      par[nbi]        = ElCLib::Parameter(theLin2d, gp_Pnt2d(xmax, y));
      nbi++;
    }
    else if (COINC(d1, d2, theTol))
    {
      par[0] = ElCLib::Parameter(theLin2d, gp_Pnt2d(xmax, ymax));
      par[1] = ElCLib::Parameter(theLin2d, gp_Pnt2d(xmax, ymin));
      nbi    = 2;
    }
  }

  if (nbi == 2)
  {
    if (fabs(par[0] - par[1]) > theTol)
    {
      theP1 = Min(par[0], par[1]);
      theP2 = Max(par[0], par[1]);
      return Standard_True;
    }
    else
      return Standard_False;
  }

  // xmax, ymin <-> xmin, ymin
  d1 = d2;
  d2 = A * xmin + B * ymin + C;

  if (d1 > theTol || d1 < -theTol)
  {
    if (INTER(d1, d2, theTol))
    {
      Standard_Real x = -(C + B * ymin) / A;
      par[nbi]        = ElCLib::Parameter(theLin2d, gp_Pnt2d(x, ymin));
      nbi++;
    }
    else if (COINC(d1, d2, theTol))
    {
      par[0] = ElCLib::Parameter(theLin2d, gp_Pnt2d(xmax, ymin));
      par[1] = ElCLib::Parameter(theLin2d, gp_Pnt2d(xmin, ymin));
      nbi    = 2;
    }
  }

  if (nbi == 2)
  {
    if (fabs(par[0] - par[1]) > theTol)
    {
      theP1 = Min(par[0], par[1]);
      theP2 = Max(par[0], par[1]);
      return Standard_True;
    }
    else
      return Standard_False;
  }

  return Standard_False;
}

//
//=================================================================================================

void ApproxParameters(const Handle(GeomAdaptor_Surface)& aHS1,
                      const Handle(GeomAdaptor_Surface)& aHS2,
                      Standard_Integer&                  iDegMin,
                      Standard_Integer&                  iDegMax,
                      Standard_Integer&                  iNbIter)

{
  GeomAbs_SurfaceType aTS1, aTS2;

  //
  iNbIter = 0;
  iDegMin = 4;
  iDegMax = 8;
  //
  aTS1 = aHS1->GetType();
  aTS2 = aHS2->GetType();
  //
  // Cylinder/Torus
  if ((aTS1 == GeomAbs_Cylinder && aTS2 == GeomAbs_Torus)
      || (aTS2 == GeomAbs_Cylinder && aTS1 == GeomAbs_Torus))
  {
    Standard_Real aRC, aRT, dR, aPC;
    gp_Cylinder   aCylinder;
    gp_Torus      aTorus;
    //
    aPC = Precision::Confusion();
    //
    aCylinder = (aTS1 == GeomAbs_Cylinder) ? aHS1->Cylinder() : aHS2->Cylinder();
    aTorus    = (aTS1 == GeomAbs_Torus) ? aHS1->Torus() : aHS2->Torus();
    //
    aRC = aCylinder.Radius();
    aRT = aTorus.MinorRadius();
    dR  = aRC - aRT;
    if (dR < 0.)
    {
      dR = -dR;
    }
    //
    if (dR < aPC)
    {
      iDegMax = 6;
    }
  }
  if (aTS1 == GeomAbs_Cylinder && aTS2 == GeomAbs_Cylinder)
  {
    iNbIter = 1;
  }
}

//=================================================================================================

void Tolerances(const Handle(GeomAdaptor_Surface)& aHS1,
                const Handle(GeomAdaptor_Surface)& aHS2,
                Standard_Real&                     aTolTang)
{
  GeomAbs_SurfaceType aTS1, aTS2;
  //
  aTS1 = aHS1->GetType();
  aTS2 = aHS2->GetType();
  //
  // Cylinder/Torus
  if ((aTS1 == GeomAbs_Cylinder && aTS2 == GeomAbs_Torus)
      || (aTS2 == GeomAbs_Cylinder && aTS1 == GeomAbs_Torus))
  {
    Standard_Real aRC, aRT, dR, aPC;
    gp_Cylinder   aCylinder;
    gp_Torus      aTorus;
    //
    aPC = Precision::Confusion();
    //
    aCylinder = (aTS1 == GeomAbs_Cylinder) ? aHS1->Cylinder() : aHS2->Cylinder();
    aTorus    = (aTS1 == GeomAbs_Torus) ? aHS1->Torus() : aHS2->Torus();
    //
    aRC = aCylinder.Radius();
    aRT = aTorus.MinorRadius();
    dR  = aRC - aRT;
    if (dR < 0.)
    {
      dR = -dR;
    }
    //
    if (dR < aPC)
    {
      aTolTang = 0.1 * aTolTang;
    }
  }
}

//=================================================================================================

Standard_Boolean SortTypes(const GeomAbs_SurfaceType aType1, const GeomAbs_SurfaceType aType2)
{
  Standard_Boolean bRet;
  Standard_Integer aI1, aI2;
  //
  bRet = Standard_False;
  //
  aI1 = IndexType(aType1);
  aI2 = IndexType(aType2);
  if (aI1 < aI2)
  {
    bRet = !bRet;
  }
  return bRet;
}

//=================================================================================================

Standard_Integer IndexType(const GeomAbs_SurfaceType aType)
{
  Standard_Integer aIndex;
  //
  aIndex = 11;
  //
  if (aType == GeomAbs_Plane)
  {
    aIndex = 0;
  }
  else if (aType == GeomAbs_Cylinder)
  {
    aIndex = 1;
  }
  else if (aType == GeomAbs_Cone)
  {
    aIndex = 2;
  }
  else if (aType == GeomAbs_Sphere)
  {
    aIndex = 3;
  }
  else if (aType == GeomAbs_Torus)
  {
    aIndex = 4;
  }
  else if (aType == GeomAbs_BezierSurface)
  {
    aIndex = 5;
  }
  else if (aType == GeomAbs_BSplineSurface)
  {
    aIndex = 6;
  }
  else if (aType == GeomAbs_SurfaceOfRevolution)
  {
    aIndex = 7;
  }
  else if (aType == GeomAbs_SurfaceOfExtrusion)
  {
    aIndex = 8;
  }
  else if (aType == GeomAbs_OffsetSurface)
  {
    aIndex = 9;
  }
  else if (aType == GeomAbs_OtherSurface)
  {
    aIndex = 10;
  }
  return aIndex;
}

//=================================================================================================

Standard_Real FindMaxDistance(const Handle(Geom_Curve)&       theCurve,
                              const Standard_Real             theFirst,
                              const Standard_Real             theLast,
                              const TopoDS_Face&              theFace,
                              const Handle(IntTools_Context)& theContext)
{
  Standard_Integer aNbS;
  Standard_Real    aT1, aT2, aDt, aD, aDMax, anEps;
  //
  aNbS  = 11;
  aDt   = (theLast - theFirst) / aNbS;
  aDMax = 0.;
  anEps = 1.e-4 * aDt;
  //
  GeomAPI_ProjectPointOnSurf& aProjPS = theContext->ProjPS(theFace);
  aT2                                 = theFirst;
  for (;;)
  {
    aT1 = aT2;
    aT2 += aDt;
    //
    if (aT2 > theLast)
    {
      break;
    }
    //
    aD = FindMaxDistance(theCurve, aT1, aT2, aProjPS, anEps);
    if (aD > aDMax)
    {
      aDMax = aD;
    }
  }
  //
  return aDMax;
}

//=================================================================================================

Standard_Real FindMaxDistance(const Handle(Geom_Curve)&   theC,
                              const Standard_Real         theFirst,
                              const Standard_Real         theLast,
                              GeomAPI_ProjectPointOnSurf& theProjPS,
                              const Standard_Real         theEps)
{
  Standard_Real aA, aB, aCf, aX, aX1, aX2, aF1, aF2, aF;
  //
  aCf = 0.61803398874989484820458683436564; //(sqrt(5.)-1)/2.;
  aA  = theFirst;
  aB  = theLast;
  //
  aX1 = aB - aCf * (aB - aA);
  aF1 = MaxDistance(theC, aX1, theProjPS);
  aX2 = aA + aCf * (aB - aA);
  aF2 = MaxDistance(theC, aX2, theProjPS);

  while (Abs(aX1 - aX2) > theEps)
  {
    if (aF1 > aF2)
    {
      aB  = aX2;
      aX2 = aX1;
      aF2 = aF1;
      aX1 = aB - aCf * (aB - aA);
      aF1 = MaxDistance(theC, aX1, theProjPS);
    }
    else
    {
      aA  = aX1;
      aX1 = aX2;
      aF1 = aF2;
      aX2 = aA + aCf * (aB - aA);
      aF2 = MaxDistance(theC, aX2, theProjPS);
    }
  }
  //
  aX = 0.5 * (aA + aB);
  aF = MaxDistance(theC, aX, theProjPS);
  //
  if (aF1 > aF)
  {
    aF = aF1;
  }
  //
  if (aF2 > aF)
  {
    aF = aF2;
  }
  //
  return aF;
}

//=================================================================================================

Standard_Real MaxDistance(const Handle(Geom_Curve)&   theC,
                          const Standard_Real         aT,
                          GeomAPI_ProjectPointOnSurf& theProjPS)
{
  Standard_Real aD;
  gp_Pnt        aP;
  //
  theC->D0(aT, aP);
  theProjPS.Perform(aP);
  aD = theProjPS.NbPoints() ? theProjPS.LowerDistance() : 0.;
  //
  return aD;
}

//=======================================================================
// function : CheckPCurve
// purpose  : Checks if points of the pcurve are out of the face bounds.
//=======================================================================
Standard_Boolean CheckPCurve(const Handle(Geom2d_Curve)&     aPC,
                             const TopoDS_Face&              aFace,
                             const Handle(IntTools_Context)& theCtx)
{
  const Standard_Integer NPoints = 23;
  Standard_Integer       i;
  Standard_Real          umin, umax, vmin, vmax;

  theCtx->UVBounds(aFace, umin, umax, vmin, vmax);
  Standard_Real tolU = Max((umax - umin) * 0.01, Precision::Confusion());
  Standard_Real tolV = Max((vmax - vmin) * 0.01, Precision::Confusion());
  Standard_Real fp   = aPC->FirstParameter();
  Standard_Real lp   = aPC->LastParameter();

  // adjust domain for periodic surfaces
  TopLoc_Location      aLoc;
  Handle(Geom_Surface) aSurf = BRep_Tool::Surface(aFace, aLoc);
  if (aSurf->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface)))
  {
    aSurf = (Handle(Geom_RectangularTrimmedSurface)::DownCast(aSurf))->BasisSurface();
  }
  gp_Pnt2d      pnt = aPC->Value((fp + lp) / 2);
  Standard_Real u, v;
  pnt.Coord(u, v);
  //
  if (aSurf->IsUPeriodic())
  {
    Standard_Real    aPer   = aSurf->UPeriod();
    Standard_Integer nshift = (Standard_Integer)((u - umin) / aPer);
    if (u < umin + aPer * nshift)
      nshift--;
    umin += aPer * nshift;
    umax += aPer * nshift;
  }
  if (aSurf->IsVPeriodic())
  {
    Standard_Real    aPer   = aSurf->VPeriod();
    Standard_Integer nshift = (Standard_Integer)((v - vmin) / aPer);
    if (v < vmin + aPer * nshift)
      nshift--;
    vmin += aPer * nshift;
    vmax += aPer * nshift;
  }
  //
  //--------------------------------------------------------
  Standard_Boolean bRet;
  Standard_Integer j, aNbIntervals;
  Standard_Real    aT, dT;
  gp_Pnt2d         aP2D;
  //
  Geom2dAdaptor_Curve aGAC(aPC);
  aNbIntervals = aGAC.NbIntervals(GeomAbs_CN);
  //
  TColStd_Array1OfReal aTI(1, aNbIntervals + 1);
  aGAC.Intervals(aTI, GeomAbs_CN);
  //
  bRet = Standard_False;
  //
  aT = aGAC.FirstParameter();
  for (j = 1; j <= aNbIntervals; ++j)
  {
    dT = (aTI(j + 1) - aTI(j)) / NPoints;
    //
    for (i = 1; i < NPoints; i++)
    {
      aT = aT + dT;
      aGAC.D0(aT, aP2D);
      aP2D.Coord(u, v);
      if (umin - u > tolU || u - umax > tolU || vmin - v > tolV || v - vmax > tolV)
      {
        return bRet;
      }
    }
  }
  return !bRet;
}

//=================================================================================================

void CorrectPlaneBoundaries(Standard_Real& aUmin,
                            Standard_Real& aUmax,
                            Standard_Real& aVmin,
                            Standard_Real& aVmax)
{
  if (!(Precision::IsInfinite(aUmin) || Precision::IsInfinite(aUmax)))
  {
    Standard_Real dU;
    //
    dU    = 0.1 * (aUmax - aUmin);
    aUmin = aUmin - dU;
    aUmax = aUmax + dU;
  }
  if (!(Precision::IsInfinite(aVmin) || Precision::IsInfinite(aVmax)))
  {
    Standard_Real dV;
    //
    dV    = 0.1 * (aVmax - aVmin);
    aVmin = aVmin - dV;
    aVmax = aVmax + dV;
  }
}
