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

//pmn 26/09/97 Add parameters of approximation in BuildCurve3d
//  Modified by skv - Thu Jun  3 12:39:19 2004 OCC5898

#include <BRepLib.hxx>

#include <AdvApprox_ApproxAFunction.hxx>
#include <AppParCurves_MultiBSpCurve.hxx>
#include <Approx_CurvilinearParameter.hxx>
#include <Approx_SameParameter.hxx>
#include <BRep_Builder.hxx>
#include <BRep_GCurve.hxx>
#include <BRepCheck.hxx>
#include <BRep_TEdge.hxx>
#include <BRep_Tool.hxx>
#include <BRep_TVertex.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <BRepBndLib.hxx>
#include <BRepClass3d_SolidClassifier.hxx>
#include <BRepLib_MakeFace.hxx>
#include <BSplCLib.hxx>
#include <ElSLib.hxx>
#include <Extrema_LocateExtPC.hxx>
#include <GCPnts_QuasiUniformDeflection.hxx>
#include <Geom2d_Curve.hxx>
#include <Geom2dAdaptor.hxx>
#include <Geom2dAdaptor_Curve.hxx>
#include <Geom2dConvert.hxx>
#include <Geom_BSplineSurface.hxx>
#include <Geom_Curve.hxx>
#include <Geom_Plane.hxx>
#include <Geom_RectangularTrimmedSurface.hxx>
#include <Geom_Surface.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <GeomAdaptor_Curve.hxx>
#include <GeomAdaptor_Surface.hxx>
#include <GeomLib.hxx>
#include <GeomLProp_SLProps.hxx>
#include <gp.hxx>
#include <gp_Ax2.hxx>
#include <gp_Pln.hxx>
#include <Poly_PolygonOnTriangulation.hxx>
#include <Poly_Triangulation.hxx>
#include <Precision.hxx>
#include <ProjLib_ProjectedCurve.hxx>
#include <Standard_ErrorHandler.hxx>
#include <Standard_Real.hxx>
#include <TColgp_Array1OfPnt2d.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <TColStd_MapOfTransient.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Solid.hxx>
#include <TopoDS_Vertex.hxx>
#include <TColgp_Array1OfXY.hxx>
#include <BRepTools_ReShape.hxx>
#include <TopTools_DataMapOfShapeReal.hxx>
#include <TopoDS_LockedShape.hxx>
#include <Standard_HashUtils.hxx>

#include <algorithm>

// TODO - not thread-safe static variables
static Standard_Real thePrecision = Precision::Confusion();     
static Handle(Geom_Plane) thePlane;

static void InternalUpdateTolerances(const TopoDS_Shape& theOldShape,
  const Standard_Boolean IsVerifyTolerance, const Standard_Boolean IsMutableInput, BRepTools_ReShape& theReshaper);

//=======================================================================
// function: BRepLib_ComparePoints
// purpose:  implementation of IsLess() function for two points
//=======================================================================
struct BRepLib_ComparePoints {
  bool operator()(const gp_Pnt& theP1, const gp_Pnt& theP2)
  {
    for (Standard_Integer i = 1; i <= 3; ++i) {
      if (theP1.Coord(i) < theP2.Coord(i)) {
        return Standard_True;
      }
      else if (theP1.Coord(i) > theP2.Coord(i)) {
        return Standard_False;
      }
    }
    return Standard_False;
  }
};


//=======================================================================
//function : Precision
//purpose  : 
//=======================================================================

void BRepLib::Precision(const Standard_Real P)
{
  thePrecision = P;
}

//=======================================================================
//function : Precision
//purpose  : 
//=======================================================================

Standard_Real  BRepLib::Precision()
{
  return thePrecision;
}

//=======================================================================
//function : Plane
//purpose  : 
//=======================================================================

void  BRepLib::Plane(const Handle(Geom_Plane)& P)
{
  thePlane = P;
}


//=======================================================================
//function : Plane
//purpose  : 
//=======================================================================

const Handle(Geom_Plane)&  BRepLib::Plane()
{
  if (thePlane.IsNull()) thePlane = new Geom_Plane(gp::XOY());
  return thePlane;
}
//=======================================================================
//function : CheckSameRange
//purpose  : 
//=======================================================================

Standard_Boolean  BRepLib::CheckSameRange(const TopoDS_Edge& AnEdge,
  const Standard_Real Tolerance) 
{
  Standard_Boolean  IsSameRange = Standard_True,
    first_time_in = Standard_True ;

  BRep_ListIteratorOfListOfCurveRepresentation an_Iterator
    ((*((Handle(BRep_TEdge)*)&AnEdge.TShape()))->ChangeCurves());

  Standard_Real first, last;
  Standard_Real current_first =0., current_last =0. ;
  Handle(BRep_GCurve) geometric_representation_ptr ;

  while (IsSameRange && an_Iterator.More()) {
    geometric_representation_ptr =
      Handle(BRep_GCurve)::DownCast(an_Iterator.Value());
    if (!geometric_representation_ptr.IsNull()) {

      first = geometric_representation_ptr->First();
      last =  geometric_representation_ptr->Last();
      if (first_time_in ) {
        current_first = first ;
        current_last = last   ;
        first_time_in = Standard_False ;
      }
      else {
        IsSameRange = (Abs(current_first - first) <= Tolerance) 
          && (Abs(current_last -last) <= Tolerance ) ;
      }
    }
    an_Iterator.Next() ;
  }
  return IsSameRange ;
}

//=======================================================================
//function : SameRange
//purpose  : 
//=======================================================================

void BRepLib::SameRange(const TopoDS_Edge& AnEdge,
  const Standard_Real Tolerance) 
{
  BRep_ListIteratorOfListOfCurveRepresentation an_Iterator
    ((*((Handle(BRep_TEdge)*)&AnEdge.TShape()))->ChangeCurves());

  Handle(Geom2d_Curve) Curve2dPtr, Curve2dPtr2, NewCurve2dPtr, NewCurve2dPtr2;
  TopLoc_Location LocalLoc ;

  Standard_Boolean first_time_in = Standard_True,
    has_curve,
    has_closed_curve ;
  Handle(BRep_GCurve) geometric_representation_ptr ;
  Standard_Real first,
    current_first,
    last,
    current_last ;

  const Handle(Geom_Curve) C = BRep_Tool::Curve(AnEdge,
    LocalLoc,
    current_first,
    current_last);
  if (!C.IsNull()) {
    first_time_in = Standard_False ;
  }

  while (an_Iterator.More()) {
    geometric_representation_ptr =
      Handle(BRep_GCurve)::DownCast(an_Iterator.Value());
    if (! geometric_representation_ptr.IsNull()) {
      has_closed_curve =
        has_curve = Standard_False ;
      first = geometric_representation_ptr->First();
      last =  geometric_representation_ptr->Last();
      if (geometric_representation_ptr->IsCurveOnSurface()) {
        Curve2dPtr = geometric_representation_ptr->PCurve() ; 
        has_curve = Standard_True ;
      }
      if (geometric_representation_ptr->IsCurveOnClosedSurface()) {
        Curve2dPtr2 = geometric_representation_ptr->PCurve2() ;
        has_closed_curve = Standard_True ;
      }
      if (has_curve || has_closed_curve) {
        if (first_time_in) {
          current_first = first ;
          current_last = last ;
          first_time_in = Standard_False ;
        }

        if (Abs(first - current_first) > Precision::Confusion() ||
          Abs(last - current_last) > Precision::Confusion() )
        {
          if (has_curve)
          {
            GeomLib::SameRange(Tolerance,
              Curve2dPtr,
              geometric_representation_ptr->First(),
              geometric_representation_ptr->Last(),
              current_first,
              current_last,
              NewCurve2dPtr);
            geometric_representation_ptr->PCurve(NewCurve2dPtr) ;
          }
          if (has_closed_curve)
          {
            GeomLib::SameRange(Tolerance,
              Curve2dPtr2,
              geometric_representation_ptr->First(),
              geometric_representation_ptr->Last(),
              current_first,
              current_last,
              NewCurve2dPtr2);
            geometric_representation_ptr->PCurve2(NewCurve2dPtr2) ;
          }
        }
      }
    }
    an_Iterator.Next() ;
  }
  BRep_Builder B;
  B.Range(TopoDS::Edge(AnEdge),
    current_first,
    current_last) ;

  B.SameRange(AnEdge,
    Standard_True) ;
}

//=======================================================================
//function : EvaluateMaxSegment
//purpose  : return MaxSegment to pass in approximation, if MaxSegment==0 provided
//=======================================================================

static Standard_Integer evaluateMaxSegment(const Standard_Integer aMaxSegment,
  const Adaptor3d_CurveOnSurface& aCurveOnSurface)
{
  if (aMaxSegment != 0) return aMaxSegment;

  const Handle(Adaptor3d_Surface)& aSurf   = aCurveOnSurface.GetSurface();
  const Handle(Adaptor2d_Curve2d)& aCurv2d = aCurveOnSurface.GetCurve();

  Standard_Real aNbSKnots = 0, aNbC2dKnots = 0;

  if (aSurf->GetType() == GeomAbs_BSplineSurface) {
    Handle(Geom_BSplineSurface) aBSpline = aSurf->BSpline();
    aNbSKnots = Max(aBSpline->NbUKnots(), aBSpline->NbVKnots());
  }
  if (aCurv2d->GetType() == GeomAbs_BSplineCurve) {
    aNbC2dKnots = aCurv2d->NbKnots();
  }
  Standard_Integer aReturn = (Standard_Integer) (  30 + Max(aNbSKnots, aNbC2dKnots) ) ;
  return aReturn;
}

//=======================================================================
//function : BuildCurve3d
//purpose  : 
//=======================================================================

Standard_Boolean  BRepLib::BuildCurve3d(const TopoDS_Edge& AnEdge,
  const Standard_Real Tolerance,
  const GeomAbs_Shape Continuity,
  const Standard_Integer MaxDegree,
  const Standard_Integer MaxSegment)
{
  Standard_Integer //ErrorCode,
    //                   ReturnCode = 0,
    ii,
    //                   num_knots,
    jj;

  TopLoc_Location LocalLoc,L[2],LC;
  Standard_Real f,l,fc,lc, first[2], last[2],
    tolerance,
    max_deviation,
    average_deviation ;
  Handle(Geom2d_Curve) Curve2dPtr, Curve2dArray[2]  ;
  Handle(Geom_Surface) SurfacePtr, SurfaceArray[2]  ;

  Standard_Integer not_done ;
  // if the edge has a 3d curve returns true


  const Handle(Geom_Curve) C = BRep_Tool::Curve(AnEdge,LocalLoc,f,l);
  if (!C.IsNull()) 
    return Standard_True;
  //
  // this should not exists but UpdateEdge makes funny things 
  // if the edge is not same range 
  //
  if (! CheckSameRange(AnEdge,
    Precision::Confusion())) {
      SameRange(AnEdge,
        Tolerance) ;
  }



  // search a curve on a plane
  Handle(Geom_Surface) S;
  Handle(Geom2d_Curve) PC;
  Standard_Integer i = 0;
  Handle(Geom_Plane) P;
  not_done = 1 ;

  while (not_done) {
    i++;
    BRep_Tool::CurveOnSurface(AnEdge,PC,S,LocalLoc,f,l,i);
    Handle(Geom_RectangularTrimmedSurface) RT = 
      Handle(Geom_RectangularTrimmedSurface)::DownCast(S);
    if ( RT.IsNull()) {
      P = Handle(Geom_Plane)::DownCast(S);
    }
    else {
      P = Handle(Geom_Plane)::DownCast(RT->BasisSurface());
    }
    not_done = ! (S.IsNull() || !P.IsNull()) ;
  }
  if (! P.IsNull()) {
    // compute the 3d curve
    gp_Ax2 axes = P->Position().Ax2();
    Handle(Geom_Curve) C3d = GeomLib::To3d(axes,PC);
    if (C3d.IsNull())
      return Standard_False;
    // update the edge
    Standard_Real First, Last;

    BRep_Builder B;
    B.UpdateEdge(AnEdge,C3d,LocalLoc,0.0e0);
    BRep_Tool::Range(AnEdge, S, LC, First, Last);
    B.Range(AnEdge, First, Last); //Do not forget 3D range.(PRO6412)
  }
  else {
    //
    // compute the 3d curve using existing surface
    //
    fc = f ;
    lc = l ;
    if (!BRep_Tool::Degenerated(AnEdge)) {
      jj = 0 ;
      for (ii = 0 ; ii < 3 ; ii++ ) {
        BRep_Tool::CurveOnSurface(TopoDS::Edge(AnEdge),
          Curve2dPtr,
          SurfacePtr,
          LocalLoc,
          fc,
          lc,
          ii) ;

        if (!Curve2dPtr.IsNull() && jj < 2){
          Curve2dArray[jj] = Curve2dPtr ;
          SurfaceArray[jj] = SurfacePtr ;
          L[jj] = LocalLoc ;
          first[jj] = fc ;
          last[jj] = lc ;
          jj += 1 ;
        }
      }
      f = first[0] ;
      l = last[0] ;
      Curve2dPtr = Curve2dArray[0] ;
      SurfacePtr = SurfaceArray[0] ;

      Geom2dAdaptor_Curve     AnAdaptor3dCurve2d (Curve2dPtr, f, l) ;
      GeomAdaptor_Surface     AnAdaptor3dSurface (SurfacePtr) ;
      Handle(Geom2dAdaptor_Curve) AnAdaptor3dCurve2dPtr =
        new Geom2dAdaptor_Curve(AnAdaptor3dCurve2d) ;
      Handle(GeomAdaptor_Surface) AnAdaptor3dSurfacePtr =
        new GeomAdaptor_Surface (AnAdaptor3dSurface) ;
      Adaptor3d_CurveOnSurface  CurveOnSurface( AnAdaptor3dCurve2dPtr,
        AnAdaptor3dSurfacePtr) ;

      Handle(Geom_Curve) NewCurvePtr ;

      GeomLib::BuildCurve3d(Tolerance,
        CurveOnSurface,
        f,
        l,
        NewCurvePtr,
        max_deviation,
        average_deviation,
        Continuity,
        MaxDegree,
        evaluateMaxSegment(MaxSegment,CurveOnSurface)) ;
      BRep_Builder B;	
      tolerance = BRep_Tool::Tolerance(AnEdge) ;
      //Patch
      //max_deviation = Max(tolerance, max_deviation) ;
      max_deviation = Max( tolerance, Tolerance );
      if (NewCurvePtr.IsNull())
        return Standard_False;
      B.UpdateEdge(TopoDS::Edge(AnEdge),
        NewCurvePtr,
        L[0],
        max_deviation) ;
      if (jj == 1 ) {
        //
        // if there is only one curve on surface attached to the edge
        // than it can be qualified sameparameter
        //
        B.SameParameter(TopoDS::Edge(AnEdge),
          Standard_True) ;
      }
    }
    else {
      return Standard_False ;
    }

  }         
  return Standard_True;
}
//=======================================================================
//function : BuildCurves3d
//purpose  : 
//=======================================================================

Standard_Boolean  BRepLib::BuildCurves3d(const TopoDS_Shape& S) 

{
  return BRepLib::BuildCurves3d(S,
    1.0e-5) ;
}

//=======================================================================
//function : BuildCurves3d
//purpose  : 
//=======================================================================

Standard_Boolean  BRepLib::BuildCurves3d(const TopoDS_Shape& S,
  const Standard_Real Tolerance,
  const GeomAbs_Shape Continuity,
  const Standard_Integer MaxDegree,
  const Standard_Integer MaxSegment)
{
  Standard_Boolean boolean_value,
    ok = Standard_True;
  TopTools_MapOfShape a_counter ;
  TopExp_Explorer ex(S,TopAbs_EDGE);

  while (ex.More()) {
    if (a_counter.Add(ex.Current())) {
      boolean_value = 
        BuildCurve3d(TopoDS::Edge(ex.Current()),
        Tolerance, Continuity,
        MaxDegree, MaxSegment);
      ok = ok && boolean_value ;
    }
    ex.Next();
  }
  return ok;
}
//=======================================================================
//function : UpdateEdgeTolerance
//purpose  : 
//=======================================================================

Standard_Boolean  BRepLib::UpdateEdgeTol(const TopoDS_Edge& AnEdge,
  const Standard_Real MinToleranceRequested,
  const Standard_Real MaxToleranceToCheck)
{

  Standard_Integer curve_on_surface_index,
    curve_index,
    not_done,
    has_closed_curve,
    has_curve,
    jj,
    ii,
    geom_reference_curve_flag = 0,
    max_sampling_points = 90,
    min_sampling_points = 30 ;

  Standard_Real factor = 100.0e0,
    //     sampling_array[2],
    safe_factor = 1.4e0,
    current_last,
    current_first,
    max_distance,
    coded_edge_tolerance,
    edge_tolerance = 0.0e0 ;
  Handle(TColStd_HArray1OfReal) parameters_ptr ;
  Handle(BRep_GCurve) geometric_representation_ptr ;

  if (BRep_Tool::Degenerated(AnEdge)) return Standard_False ;
  coded_edge_tolerance = BRep_Tool::Tolerance(AnEdge) ;
  if (coded_edge_tolerance > MaxToleranceToCheck) return Standard_False ;

  const Handle(BRep_TEdge)& TE = *((Handle(BRep_TEdge)*)&AnEdge.TShape());
  BRep_ListOfCurveRepresentation& list_curve_rep = TE->ChangeCurves() ;
  BRep_ListIteratorOfListOfCurveRepresentation an_iterator(list_curve_rep),
    second_iterator(list_curve_rep) ;
  Handle(Geom2d_Curve) curve2d_ptr, new_curve2d_ptr;
  Handle(Geom_Surface) surface_ptr ;
  TopLoc_Location local_location ;
  GCPnts_QuasiUniformDeflection  a_sampler ;
  GeomAdaptor_Curve  geom_reference_curve ;
  Adaptor3d_CurveOnSurface  curve_on_surface_reference ; 
  Handle(Geom_Curve) C = BRep_Tool::Curve(AnEdge,
    local_location,
    current_first,
    current_last);
  curve_on_surface_index = -1 ;
  if (!C.IsNull()) {
    if (! local_location.IsIdentity()) {
      C = Handle(Geom_Curve)::
        DownCast(C-> Transformed(local_location.Transformation()) ) ;
    }
    geom_reference_curve.Load(C) ;
    geom_reference_curve_flag = 1 ;
    a_sampler.Initialize(geom_reference_curve,
      MinToleranceRequested * factor,
      current_first,
      current_last) ;
  }
  else {
    not_done = 1 ;
    curve_on_surface_index = 0 ;  

    while (not_done && an_iterator.More()) {
      geometric_representation_ptr =
        Handle(BRep_GCurve)::DownCast(second_iterator.Value());
      if (!geometric_representation_ptr.IsNull() 
        && geometric_representation_ptr->IsCurveOnSurface()) {
          curve2d_ptr = geometric_representation_ptr->PCurve() ;
          local_location = geometric_representation_ptr->Location() ;
          current_first = geometric_representation_ptr->First();
          //first = geometric_representation_ptr->First();
          current_last =  geometric_representation_ptr->Last();
          // must be inverted 
          //
          if (! local_location.IsIdentity() ) {
            surface_ptr = Handle(Geom_Surface)::
              DownCast( geometric_representation_ptr->Surface()->
              Transformed(local_location.Transformation()) ) ;
          }
          else {
            surface_ptr = 
              geometric_representation_ptr->Surface() ;
          }
          not_done = 0 ;
      }
      curve_on_surface_index += 1 ;
    }
    Geom2dAdaptor_Curve     AnAdaptor3dCurve2d (curve2d_ptr) ;
    GeomAdaptor_Surface     AnAdaptor3dSurface (surface_ptr) ;
    Handle(Geom2dAdaptor_Curve) AnAdaptor3dCurve2dPtr =
      new Geom2dAdaptor_Curve(AnAdaptor3dCurve2d) ;
    Handle(GeomAdaptor_Surface) AnAdaptor3dSurfacePtr =
      new GeomAdaptor_Surface (AnAdaptor3dSurface) ;
    curve_on_surface_reference.Load (AnAdaptor3dCurve2dPtr, AnAdaptor3dSurfacePtr);
    a_sampler.Initialize(curve_on_surface_reference,
      MinToleranceRequested * factor,
      current_first,
      current_last) ;
  }
  TColStd_Array1OfReal   sampling_parameters(1,a_sampler.NbPoints()) ;
  for (ii = 1 ; ii <= a_sampler.NbPoints() ; ii++) {
    sampling_parameters(ii) = a_sampler.Parameter(ii) ;
  }
  if (a_sampler.NbPoints() < min_sampling_points) {
    GeomLib::DensifyArray1OfReal(min_sampling_points,
      sampling_parameters,
      parameters_ptr) ;
  }
  else if (a_sampler.NbPoints() > max_sampling_points) {
    GeomLib::RemovePointsFromArray(max_sampling_points,
      sampling_parameters,
      parameters_ptr) ; 
  }
  else {
    jj = 1 ;
    parameters_ptr =
      new TColStd_HArray1OfReal(1,sampling_parameters.Length()) ;
    for (ii = sampling_parameters.Lower() ; ii <= sampling_parameters.Upper() ; ii++) {
      parameters_ptr->ChangeArray1()(jj) =
        sampling_parameters(ii) ;
      jj +=1 ;
    }
  }

  curve_index = 0 ;

  while (second_iterator.More()) {
    geometric_representation_ptr =
      Handle(BRep_GCurve)::DownCast(second_iterator.Value());
    if (! geometric_representation_ptr.IsNull() && 
      curve_index != curve_on_surface_index) {
        has_closed_curve =
          has_curve = Standard_False ;
        //	first = geometric_representation_ptr->First();
        //	last =  geometric_representation_ptr->Last();
        local_location = geometric_representation_ptr->Location() ;
        if (geometric_representation_ptr->IsCurveOnSurface()) {
          curve2d_ptr = geometric_representation_ptr->PCurve() ; 
          has_curve = Standard_True ;
        }
        if (geometric_representation_ptr->IsCurveOnClosedSurface()) {
          curve2d_ptr = geometric_representation_ptr->PCurve2() ;
          has_closed_curve = Standard_True ;
        }

        if (has_curve ||
          has_closed_curve) {
            if (! local_location.IsIdentity() ) {
              surface_ptr = Handle(Geom_Surface)::
                DownCast( geometric_representation_ptr->Surface()->
                Transformed(local_location.Transformation()) ) ;
            }
            else {
              surface_ptr = 
                geometric_representation_ptr->Surface() ;
            }
            Geom2dAdaptor_Curve     an_adaptor_curve2d (curve2d_ptr) ;
            GeomAdaptor_Surface     an_adaptor_surface(surface_ptr) ;
            Handle(Geom2dAdaptor_Curve) an_adaptor_curve2d_ptr =
              new Geom2dAdaptor_Curve(an_adaptor_curve2d) ;
            Handle(GeomAdaptor_Surface) an_adaptor_surface_ptr =
              new GeomAdaptor_Surface (an_adaptor_surface) ;
            Adaptor3d_CurveOnSurface a_curve_on_surface(an_adaptor_curve2d_ptr,
              an_adaptor_surface_ptr) ;

            if (BRep_Tool::SameParameter(AnEdge)) {

              GeomLib::EvalMaxParametricDistance(a_curve_on_surface,
                geom_reference_curve,
                MinToleranceRequested,
                parameters_ptr->Array1(),
                max_distance) ;
            }
            else if (geom_reference_curve_flag) {
              GeomLib::EvalMaxDistanceAlongParameter(a_curve_on_surface,
                geom_reference_curve,
                MinToleranceRequested,
                parameters_ptr->Array1(),
                max_distance) ;
            }
            else {

              GeomLib::EvalMaxDistanceAlongParameter(a_curve_on_surface,
                curve_on_surface_reference,
                MinToleranceRequested,
                parameters_ptr->Array1(),
                max_distance) ;
            }
            max_distance *= safe_factor ;
            edge_tolerance = Max(max_distance, edge_tolerance) ;
        }


    }
    curve_index += 1 ;
    second_iterator.Next() ; 
  }

  TE->Tolerance(edge_tolerance);
  return Standard_True ;

}
//=======================================================================
//function : UpdateEdgeTolerance
//purpose  : 
//=======================================================================
Standard_Boolean BRepLib::UpdateEdgeTolerance(const TopoDS_Shape& S,
  const Standard_Real MinToleranceRequested,
  const Standard_Real MaxToleranceToCheck) 
{
  TopExp_Explorer ex(S,TopAbs_EDGE);
  TopTools_MapOfShape  a_counter ;

  Standard_Boolean     return_status = Standard_False,
    local_flag ;

  while (ex.More()) {
    if (a_counter.Add(ex.Current())) {
      local_flag =
        BRepLib::UpdateEdgeTol(TopoDS::Edge(ex.Current()),
        MinToleranceRequested,
        MaxToleranceToCheck) ;
      if (local_flag && ! return_status) {
        return_status = Standard_True ;
      }
    }
    ex.Next();
  }
  return return_status ;
}

//=======================================================================
//function : GetEdgeTol
//purpose  : 
//=======================================================================
static void GetEdgeTol(const TopoDS_Edge& theEdge,
  const TopoDS_Face& theFace, Standard_Real& theEdTol)
{
  TopLoc_Location L;
  const Handle(Geom_Surface)& S = BRep_Tool::Surface(theFace,L);
  TopLoc_Location l = L.Predivided(theEdge.Location());

  const Handle(BRep_TEdge)& TE = *((Handle(BRep_TEdge)*)&theEdge.TShape());
  BRep_ListIteratorOfListOfCurveRepresentation itcr(TE->Curves());

  while (itcr.More()) {
    const Handle(BRep_CurveRepresentation)& cr = itcr.Value();
    if(cr->IsCurveOnSurface(S,l)) return;
    itcr.Next();
  }

  Handle(Geom_Plane) GP;
  Handle(Geom_RectangularTrimmedSurface) GRTS;
  GRTS = Handle(Geom_RectangularTrimmedSurface)::DownCast(S);
  if(!GRTS.IsNull())
    GP = Handle(Geom_Plane)::DownCast(GRTS->BasisSurface());
  else
    GP = Handle(Geom_Plane)::DownCast(S);

  Handle(GeomAdaptor_Curve) HC = new GeomAdaptor_Curve();
  Handle(GeomAdaptor_Surface) HS = new GeomAdaptor_Surface();

  TopLoc_Location LC;
  Standard_Real First, Last;
  HC->Load(BRep_Tool::Curve(theEdge,LC,First,Last));
  LC = L.Predivided(LC);

  if (!LC.IsIdentity()) {
    GP = Handle(Geom_Plane)::DownCast(
      GP->Transformed(LC.Transformation()));
  }
  GeomAdaptor_Surface& GAS = *HS;
  GAS.Load(GP);

  ProjLib_ProjectedCurve Proj(HS,HC);
  Handle(Geom2d_Curve) pc = Geom2dAdaptor::MakeCurve(Proj);

  gp_Pln pln = GAS.Plane();
  Standard_Real d2 = 0.;
  Standard_Integer nn = 23;
  Standard_Real unsurnn = 1./nn;
  for(Standard_Integer i = 0; i <= nn; i++){
    Standard_Real t = unsurnn*i;
    Standard_Real u = First*(1.-t) + Last*t;
    gp_Pnt Pc3d = HC->Value(u);
    gp_Pnt2d p2d = pc->Value(u);
    gp_Pnt Pcons = ElSLib::Value(p2d.X(),p2d.Y(),pln);
    Standard_Real eps = Max(Pc3d.XYZ().SquareModulus(), Pcons.XYZ().SquareModulus());
    eps = Epsilon(eps);
    Standard_Real temp = Pc3d.SquareDistance(Pcons);
    if(temp <= eps)
    {
      temp = 0.;
    }
    if(temp > d2) d2 = temp;
  }
  d2 = 1.05*sqrt(d2);
  theEdTol = d2;
}

//=======================================================================
//function : UpdTolMap
//purpose  : Update map ShToTol (shape to tolerance)
//=======================================================================
static void UpdTolMap(const TopoDS_Shape& theSh, Standard_Real theNewTol, 
  TopTools_DataMapOfShapeReal& theShToTol)
{
  TopAbs_ShapeEnum aSt = theSh.ShapeType();
  Standard_Real aShTol;
  if (aSt == TopAbs_VERTEX)
    aShTol = BRep_Tool::Tolerance(TopoDS::Vertex(theSh));
  else if (aSt == TopAbs_EDGE)
    aShTol = BRep_Tool::Tolerance(TopoDS::Edge(theSh));
  else
    return;
  //
  if (theNewTol > aShTol)
  {
    const Standard_Real* anOldtol = theShToTol.Seek(theSh);
    if (!anOldtol)
      theShToTol.Bind(theSh, theNewTol);
    else
      theShToTol(theSh) = Max(*anOldtol, theNewTol);
  }
}

//=======================================================================
//function : UpdShTol
//purpose  : Update vertices/edges/faces according to ShToTol map (create copies of necessary)
//=======================================================================
static void UpdShTol(const TopTools_DataMapOfShapeReal& theShToTol,
  const Standard_Boolean IsMutableInput, BRepTools_ReShape& theReshaper,
  Standard_Boolean theVForceUpdate)
{
  BRep_Builder aB;
  TopTools_DataMapIteratorOfDataMapOfShapeReal SHToTolit(theShToTol);
  for (;SHToTolit.More();SHToTolit.Next())
  {
    const TopoDS_Shape& aSh = SHToTolit.Key();
    Standard_Real aTol = SHToTolit.Value();
    //
    TopoDS_Shape aNsh;
    const TopoDS_Shape& aVsh = theReshaper.Value(aSh);
    Standard_Boolean UseOldSh = IsMutableInput || theReshaper.IsNewShape(aSh) || !aVsh.IsSame(aSh);
    if (UseOldSh)
      aNsh = aVsh;
    else
    {
      aNsh = aSh.EmptyCopied();
      //add subshapes from the original shape
      TopoDS_Iterator sit(aSh);
      for (;sit.More();sit.Next())
        aB.Add(aNsh, sit.Value());
      //
      aNsh.Free(aSh.Free());
      aNsh.Checked(aSh.Checked());
      aNsh.Orientable(aSh.Orientable());
      aNsh.Closed(aSh.Closed());
      aNsh.Infinite(aSh.Infinite());
      aNsh.Convex(aSh.Convex());
      //
    }
    //
    switch (aSh.ShapeType())
    {
    case TopAbs_FACE: 
      {
        aB.UpdateFace(TopoDS::Face(aNsh), aTol); 
        break;
      }
    case TopAbs_EDGE: 
      {
        aB.UpdateEdge(TopoDS::Edge(aNsh), aTol);   
        break;
      }
    case TopAbs_VERTEX: 
      {
        const Handle(BRep_TVertex)& aTV = *((Handle(BRep_TVertex)*)&aNsh.TShape());
        if (theVForceUpdate)
          aTV->Tolerance(aTol);
        else
          aTV->UpdateTolerance(aTol);
        aTV->Modified(Standard_True);
        break;
      }
    default: 
      break;
    }
    //
    if (!UseOldSh)
      theReshaper.Replace(aSh, aNsh);
  }
}

//=======================================================================
//function : InternalSameParameter
//purpose  : 
//=======================================================================
static void InternalSameParameter(const TopoDS_Shape& theSh, BRepTools_ReShape& theReshaper,
  const Standard_Real theTol, const Standard_Boolean IsForced, const Standard_Boolean IsMutableInput ) 
{
  TopExp_Explorer ex(theSh,TopAbs_EDGE);
  TopTools_MapOfShape  Done;
  BRep_Builder aB;
  TopTools_DataMapOfShapeReal aShToTol;
 
  while (ex.More()) 
  {
    const TopoDS_Edge& aCE = TopoDS::Edge(ex.Current());
    if (Done.Add(aCE))
    {
      TopoDS_Edge aNE = TopoDS::Edge(theReshaper.Value(aCE));
      Standard_Boolean UseOldEdge = IsMutableInput || theReshaper.IsNewShape(aCE) || !aNE.IsSame(aCE);
      if (IsForced && (BRep_Tool::SameRange(aCE) || BRep_Tool::SameParameter(aCE)))
      {
        if (!UseOldEdge)
        {
          aNE = TopoDS::Edge(aCE.EmptyCopied());
          TopoDS_Iterator sit(aCE);
          for (;sit.More();sit.Next())
            aB.Add(aNE, sit.Value());
          theReshaper.Replace(aCE, aNE);
          UseOldEdge = Standard_True;
        }
        aB.SameRange(aNE, Standard_False);
        aB.SameParameter(aNE, Standard_False);
      }
      Standard_Real aNewTol = -1;
      TopoDS_Edge aResEdge = BRepLib::SameParameter(aNE, theTol, aNewTol, UseOldEdge);
      if (!UseOldEdge && !aResEdge.IsNull())
        //NE have been empty-copied
        theReshaper.Replace(aNE, aResEdge);
      if (aNewTol > 0)
      {
        TopoDS_Vertex aV1, aV2;
        TopExp::Vertices(aCE,aV1,aV2);
        if (!aV1.IsNull())
          UpdTolMap(aV1, aNewTol, aShToTol);
        if (!aV2.IsNull()) 
          UpdTolMap(aV2, aNewTol, aShToTol);
      }
    }
    ex.Next();
  }
 
  Done.Clear();
  BRepAdaptor_Surface BS;
  for(ex.Init(theSh,TopAbs_FACE); ex.More(); ex.Next()){
    const TopoDS_Face& curface = TopoDS::Face(ex.Current());
    if(!Done.Add(curface)) continue;
    BS.Initialize(curface);
    if(BS.GetType() != GeomAbs_Plane) continue;
    TopExp_Explorer ex2;
    for(ex2.Init(curface,TopAbs_EDGE); ex2.More(); ex2.Next()){
      const TopoDS_Edge& E = TopoDS::Edge(ex2.Current());
      if (BRep_Tool::Degenerated(E))
        continue;

      TopoDS_Shape aNe = theReshaper.Value(E);
      Standard_Real aNewEtol = -1;
      GetEdgeTol(TopoDS::Edge(aNe), curface, aNewEtol);
      if (aNewEtol >= 0) //not equal to -1
        UpdTolMap(E, aNewEtol, aShToTol);
    }
  }
 
  //
  UpdShTol(aShToTol, IsMutableInput, theReshaper, Standard_False );

  InternalUpdateTolerances(theSh, Standard_False, IsMutableInput, theReshaper );
}

//================================================================
//function : SameParameter
//WARNING  : New spec DUB LBO 9/9/97.
//  Recode in the edge the best tolerance found, 
//  for vertex extremities it is required to find something else
//================================================================
void  BRepLib::SameParameter(const TopoDS_Shape& S,
  const Standard_Real Tolerance,
  const Standard_Boolean forced)
{
  BRepTools_ReShape reshaper;
  InternalSameParameter( S, reshaper, Tolerance, forced, Standard_True);
}

//=======================================================================
//function : SameParameter
//purpose  : 
//=======================================================================
void BRepLib::SameParameter(const TopoDS_Shape& S, BRepTools_ReShape& theReshaper,
  const Standard_Real Tolerance, const Standard_Boolean forced ) 
{
  InternalSameParameter( S, theReshaper, Tolerance, forced, Standard_False);
}

//=======================================================================
//function : EvalTol
//purpose  : 
//=======================================================================
static Standard_Boolean EvalTol(const Handle(Geom2d_Curve)& pc,
  const Handle(Geom_Surface)& s,
  const GeomAdaptor_Curve&    gac,
  const Standard_Real         tol,
  Standard_Real&              tolbail)
{
  Standard_Integer ok = 0;
  Standard_Real f = gac.FirstParameter();
  Standard_Real l = gac.LastParameter();
  Extrema_LocateExtPC Projector;
  Projector.Initialize(gac,f,l,tol);
  Standard_Real u,v;
  gp_Pnt p;
  tolbail = tol;
  for(Standard_Integer i = 1; i <= 5; i++){
    Standard_Real t = i/6.;
    t = (1.-t) * f + t * l;
    pc->Value(t).Coord(u,v);
    p = s->Value(u,v);
    Projector.Perform(p,t);
    if (Projector.IsDone()) {
      Standard_Real dist2 = Projector.SquareDistance();
      if(dist2 > tolbail * tolbail) tolbail = sqrt (dist2);
      ok++;
    }
  }
  return (ok > 2);
}

//=======================================================================
//function : ComputeTol
//purpose  : 
//=======================================================================
static Standard_Real ComputeTol(const Handle(Adaptor3d_Curve)& c3d,
  const Handle(Adaptor2d_Curve2d)& c2d,
  const Handle(Adaptor3d_Surface)& surf,
  const Standard_Integer        nbp)

{

  TColStd_Array1OfReal dist(1,nbp+10);
  dist.Init(-1.);

  //Adaptor3d_CurveOnSurface  cons(c2d,surf);
  Standard_Real uf = surf->FirstUParameter(), ul = surf->LastUParameter(),
                vf = surf->FirstVParameter(), vl = surf->LastVParameter();
  Standard_Real du = 0.01 * (ul - uf), dv = 0.01 * (vl - vf);
  Standard_Boolean isUPeriodic = surf->IsUPeriodic(), isVPeriodic = surf->IsVPeriodic();
  Standard_Real DSdu = 1./surf->UResolution(1.), DSdv = 1./surf->VResolution(1.);
  Standard_Real d2 = 0.;
  Standard_Real first = c3d->FirstParameter();
  Standard_Real last  = c3d->LastParameter();
  Standard_Real dapp = -1.;
  for (Standard_Integer i = 0; i <= nbp; ++i)
  {
    const Standard_Real t = IntToReal(i)/IntToReal(nbp);
    const Standard_Real u = first*(1.-t) + last*t;
    gp_Pnt Pc3d = c3d->Value(u);
    gp_Pnt2d Puv = c2d->Value(u);
    if(!isUPeriodic)
    {
      if(Puv.X() < uf - du)
      {
        dapp = Max(dapp, DSdu * (uf - Puv.X()));
        continue;
      }
      else if(Puv.X() > ul + du)
      {
        dapp = Max(dapp, DSdu * (Puv.X() - ul));
        continue;
      }
    }
    if(!isVPeriodic)
    {
      if(Puv.Y() < vf - dv)
      {
        dapp = Max(dapp, DSdv * (vf - Puv.Y()));
        continue;
      }
      else if(Puv.Y() > vl + dv)
      {
        dapp = Max(dapp, DSdv * (Puv.Y() - vl));
        continue;
      }
    }
    gp_Pnt Pcons = surf->Value(Puv.X(), Puv.Y());
    if (Precision::IsInfinite(Pcons.X()) ||
        Precision::IsInfinite(Pcons.Y()) ||
        Precision::IsInfinite(Pcons.Z()))
    {
      d2 = Precision::Infinite();
      break;
    }
    Standard_Real temp = Pc3d.SquareDistance(Pcons);

    dist(i+1) = temp;

    d2 = Max (d2, temp);
  }

  if(Precision::IsInfinite(d2))
  {
    return d2;
  }

  d2 = Sqrt(d2);
  if(dapp > d2)
  {
    return dapp;
  }

  Standard_Boolean ana = Standard_False;
  Standard_Real D2 = 0;
  Standard_Integer N1 = 0;
  Standard_Integer N2 = 0;
  Standard_Integer N3 = 0;

  for (Standard_Integer i = 1; i<= nbp+10; ++i)
  {
    if (dist(i) > 0)
    {
      if (dist(i) < 1.0)
      {
        ++N1;
      }
      else
      {
        ++N2;
      }
    }
  }

  if (N1 > N2 && N2 != 0)
  {
    N3 = 100*N2/(N1+N2);
  }
  if (N3 < 10 && N3 != 0)
  {
    ana = Standard_True;
    for (Standard_Integer i = 1; i <= nbp+10; ++i)
    {
      if (dist(i) > 0 && dist(i) < 1.0)
      {
        D2 = Max (D2, dist(i));
      }
    }
  }

  //d2 = 1.5*sqrt(d2);
  d2 = (!ana) ? 1.5 * d2 : 1.5*sqrt(D2);
  d2 = Max (d2, 1.e-7);
  return d2;
}

//=======================================================================
//function : GetCurve3d
//purpose  : 
//=======================================================================
static void GetCurve3d(const TopoDS_Edge& theEdge, Handle(Geom_Curve)& theC3d, Standard_Real& theF3d, 
  Standard_Real& theL3d, TopLoc_Location& theLoc3d, BRep_ListOfCurveRepresentation& theCList)
{
  const Handle(BRep_TEdge)& aTE = *((Handle(BRep_TEdge)*) &theEdge.TShape());
  theCList = aTE->ChangeCurves(); // current function (i.e. GetCurve3d()) will not change any of this curves
  BRep_ListIteratorOfListOfCurveRepresentation anIt(theCList);
  Standard_Boolean NotDone = Standard_True;
  while (NotDone && anIt.More()) {
    Handle(BRep_GCurve) GCurve = Handle(BRep_GCurve)::DownCast(anIt.Value());
    if (!GCurve.IsNull() && GCurve->IsCurve3D()) {
      theC3d = GCurve->Curve3D() ;
      theF3d = GCurve->First();
      theL3d = GCurve->Last();
      theLoc3d = GCurve->Location() ;
      NotDone = Standard_False;
    } 
    anIt.Next() ;
  }
}

//=======================================================================
//function : UpdateVTol
//purpose  : 
//=======================================================================
void UpdateVTol(const TopoDS_Vertex& theV1, const TopoDS_Vertex& theV2, Standard_Real theTol)
{
  BRep_Builder aB;
  if (!theV1.IsNull())
    aB.UpdateVertex(theV1,theTol);
  if (!theV2.IsNull())
    aB.UpdateVertex(theV2,theTol);
}

//=======================================================================
//function : SameParameter
//purpose  : 
//=======================================================================
void BRepLib::SameParameter(const TopoDS_Edge& theEdge,
  const Standard_Real theTolerance)
{
  Standard_Real aNewTol = -1;
  SameParameter(theEdge, theTolerance, aNewTol, Standard_True);
  if (aNewTol > 0)
  {
    TopoDS_Vertex aV1, aV2;
    TopExp::Vertices(theEdge,aV1,aV2);
    UpdateVTol(aV1, aV2, aNewTol);
  }
}

//=======================================================================
//function : SameParameter
//purpose  : 
//=======================================================================
TopoDS_Edge BRepLib::SameParameter(const TopoDS_Edge& theEdge,
  const Standard_Real theTolerance, Standard_Real& theNewTol, Standard_Boolean IsUseOldEdge)
{
  if (BRep_Tool::SameParameter(theEdge)) 
    return TopoDS_Edge();
  Standard_Real f3d =0.,l3d =0.;
  TopLoc_Location L3d;
  Handle(Geom_Curve) C3d;
  BRep_ListOfCurveRepresentation CList;
  GetCurve3d(theEdge, C3d, f3d, l3d, L3d, CList);
  if(C3d.IsNull()) 
    return TopoDS_Edge();

  BRep_Builder B;
  TopoDS_Edge aNE;
  Handle(BRep_TEdge) aNTE;
  if (IsUseOldEdge)
  {
    aNE = theEdge;
    aNTE = *((Handle(BRep_TEdge)*) &theEdge.TShape());
  }
  else
  {
    aNE = TopoDS::Edge(theEdge.EmptyCopied()); //will be modified a little bit later, so copy anyway  
    GetCurve3d(aNE, C3d, f3d, l3d, L3d, CList); //C3d pointer and CList will be differ after copying
    aNTE = *((Handle(BRep_TEdge)*) &aNE.TShape());
    TopoDS_Iterator sit(theEdge);
    for (;sit.More();sit.Next()) //add vertices from old edge to the new ones
      B.Add(aNE, sit.Value());
  }

  BRep_ListIteratorOfListOfCurveRepresentation It(CList);

  const Standard_Integer NCONTROL = 22;

  Handle(GeomAdaptor_Curve) HC = new GeomAdaptor_Curve();
  Handle(Geom2dAdaptor_Curve) HC2d = new Geom2dAdaptor_Curve();
  Handle(GeomAdaptor_Surface) HS = new GeomAdaptor_Surface();
  GeomAdaptor_Curve& GAC = *HC;
  Geom2dAdaptor_Curve& GAC2d = *HC2d;
  GeomAdaptor_Surface& GAS = *HS;

  // modified by NIZHNY-OCC486  Tue Aug 27 17:15:13 2002 :
  Standard_Boolean m_TrimmedPeriodical = Standard_False;
  Handle(Standard_Type) TheType = C3d->DynamicType();
  if( TheType == STANDARD_TYPE(Geom_TrimmedCurve))
  {
    Handle(Geom_Curve) gtC (Handle(Geom_TrimmedCurve)::DownCast (C3d)->BasisCurve());
    m_TrimmedPeriodical = gtC->IsPeriodic();
  }
  // modified by NIZHNY-OCC486  Tue Aug 27 17:15:17 2002 .


  if(!C3d->IsPeriodic()) {
    Standard_Real Udeb = C3d->FirstParameter();
    Standard_Real Ufin = C3d->LastParameter();
    // modified by NIZHNY-OCC486  Tue Aug 27 17:17:14 2002 :
    //if (Udeb > f3d) f3d = Udeb;
    //if (l3d > Ufin) l3d = Ufin;
    if(!m_TrimmedPeriodical)
    {
      if (Udeb > f3d) f3d = Udeb;
      if (l3d > Ufin) l3d = Ufin;
    }
    // modified by NIZHNY-OCC486  Tue Aug 27 17:17:55 2002 .
  }
  if(!L3d.IsIdentity()){
    C3d = Handle(Geom_Curve)::DownCast(C3d->Transformed(L3d.Transformation()));
  }
  GAC.Load(C3d,f3d,l3d);

  Standard_Real Prec_C3d = BRepCheck::PrecCurve(GAC);

  Standard_Boolean IsSameP = 1;
  Standard_Real maxdist = 0.;

  //  Modified by skv - Thu Jun  3 12:39:19 2004 OCC5898 Begin
  Standard_Real anEdgeTol = BRep_Tool::Tolerance(aNE);
  //  Modified by skv - Thu Jun  3 12:39:20 2004 OCC5898 End
  Standard_Boolean SameRange = BRep_Tool::SameRange(aNE);
  Standard_Boolean YaPCu = Standard_False;
  const Standard_Real BigError = 1.e10;
  It.Initialize(CList);

  while (It.More()) {

    Standard_Boolean isANA = Standard_False;
    Standard_Boolean isBSP = Standard_False;
    Handle(BRep_GCurve) GCurve = Handle(BRep_GCurve)::DownCast(It.Value());
    Handle(Geom2d_Curve) PC[2];
    Handle(Geom_Surface) S;
    if (!GCurve.IsNull() && GCurve->IsCurveOnSurface()) {
      YaPCu = Standard_True;
      PC[0] = GCurve->PCurve();
      TopLoc_Location PCLoc = GCurve->Location();
      S = GCurve->Surface();
      if (!PCLoc.IsIdentity() ) {
        S = Handle(Geom_Surface)::DownCast(S->Transformed(PCLoc.Transformation()));
      }

      GAS.Load(S);
      if (GCurve->IsCurveOnClosedSurface()) {
        PC[1] = GCurve->PCurve2();
      }

      // Eval tol2d to compute SameRange
      Standard_Real TolSameRange = Max(GAC.Resolution(theTolerance), Precision::PConfusion());
      for(Standard_Integer i = 0; i < 2; i++){
        Handle(Geom2d_Curve) curPC = PC[i];
        Standard_Boolean updatepc = 0;
        if(curPC.IsNull()) break;
        if(!SameRange){
          GeomLib::SameRange(TolSameRange,
            PC[i],GCurve->First(),GCurve->Last(),
            f3d,l3d,curPC);

          updatepc = (curPC != PC[i]);

        }
        Standard_Boolean goodpc = 1;
        GAC2d.Load(curPC,f3d,l3d);

        Standard_Real error = ComputeTol(HC, HC2d, HS, NCONTROL);

        if(error > BigError)
        {
          maxdist = error;
          break;
        }

        if(GAC2d.GetType() == GeomAbs_BSplineCurve && 
          GAC2d.Continuity() == GeomAbs_C0) {
            Standard_Real UResol = GAS.UResolution(theTolerance);
            Standard_Real VResol = GAS.VResolution(theTolerance);
            Standard_Real TolConf2d = Min(UResol, VResol);
            TolConf2d = Max(TolConf2d, Precision::PConfusion());
            Handle(Geom2d_BSplineCurve) bs2d = GAC2d.BSpline();
            Handle(Geom2d_BSplineCurve) bs2dsov = bs2d;
            Standard_Real fC0 = bs2d->FirstParameter(), lC0 = bs2d->LastParameter();
            Standard_Boolean repar = Standard_True;
            gp_Pnt2d OriginPoint;
            bs2d->D0(fC0, OriginPoint);
            Geom2dConvert::C0BSplineToC1BSplineCurve(bs2d, TolConf2d);
            isBSP = Standard_True; 

            if(bs2d->IsPeriodic()) { // -------- IFV, Jan 2000
              gp_Pnt2d NewOriginPoint;
              bs2d->D0(bs2d->FirstParameter(), NewOriginPoint);
              if(Abs(OriginPoint.X() - NewOriginPoint.X()) > Precision::PConfusion() ||
                Abs(OriginPoint.Y() - NewOriginPoint.Y()) > Precision::PConfusion()    ) {

                  TColStd_Array1OfReal Knotbs2d (1, bs2d->NbKnots());
                  bs2d->Knots(Knotbs2d);

                  for(Standard_Integer Index = 1; Index <= bs2d->NbKnots(); Index++) {
                    bs2d->D0(Knotbs2d(Index), NewOriginPoint);
                    if(Abs(OriginPoint.X() - NewOriginPoint.X()) > Precision::PConfusion() ||
                      Abs(OriginPoint.Y() - NewOriginPoint.Y()) > Precision::PConfusion()    ) continue;

                    bs2d->SetOrigin(Index);
                    break;
                  }
              }
            }

            if(bs2d->Continuity() == GeomAbs_C0) {
              Standard_Real tolbail;
              if(EvalTol(curPC,S,GAC,theTolerance,tolbail)){
                bs2d = bs2dsov;
                Standard_Real UResbail = GAS.UResolution(tolbail);
                Standard_Real VResbail = GAS.VResolution(tolbail);
                Standard_Real Tol2dbail  = Min(UResbail,VResbail);
                bs2d->D0(bs2d->FirstParameter(), OriginPoint); 

                Standard_Integer nbp = bs2d->NbPoles();
                TColgp_Array1OfPnt2d poles(1,nbp);
                bs2d->Poles(poles);
                gp_Pnt2d p = poles(1), p1;
                Standard_Real d = Precision::Infinite();
                for(Standard_Integer ip = 2; ip <= nbp; ip++) {
                  p1 = poles(ip);
                  d = Min(d,p.SquareDistance(p1));
                  p = p1;
                }
                d = sqrt(d)*.1;

                Tol2dbail = Max(Min(Tol2dbail,d), TolConf2d);

                Geom2dConvert::C0BSplineToC1BSplineCurve(bs2d,Tol2dbail);

                if(bs2d->IsPeriodic()) { // -------- IFV, Jan 2000
                  gp_Pnt2d NewOriginPoint;
                  bs2d->D0(bs2d->FirstParameter(), NewOriginPoint);
                  if(Abs(OriginPoint.X() - NewOriginPoint.X()) > Precision::PConfusion() ||
                    Abs(OriginPoint.Y() - NewOriginPoint.Y()) > Precision::PConfusion()    ) {

                      TColStd_Array1OfReal Knotbs2d (1, bs2d->NbKnots());
                      bs2d->Knots(Knotbs2d);

                      for(Standard_Integer Index = 1; Index <= bs2d->NbKnots(); Index++) {
                        bs2d->D0(Knotbs2d(Index), NewOriginPoint);
                        if(Abs(OriginPoint.X() - NewOriginPoint.X()) > Precision::PConfusion() ||
                          Abs(OriginPoint.Y() - NewOriginPoint.Y()) > Precision::PConfusion()    ) continue;

                        bs2d->SetOrigin(Index);
                        break;
                      }
                  }
                }


                if(bs2d->Continuity() == GeomAbs_C0) {
                  goodpc = 1;
                  bs2d = bs2dsov;
                  repar = Standard_False;
                }
              }
              else goodpc = 0;
            }

            if(goodpc){
              if(repar) {
                Standard_Integer NbKnots = bs2d->NbKnots();
                TColStd_Array1OfReal Knots(1,NbKnots);
                bs2d->Knots(Knots);
                //	    BSplCLib::Reparametrize(f3d,l3d,Knots);
                BSplCLib::Reparametrize(fC0,lC0,Knots);
                bs2d->SetKnots(Knots);
                GAC2d.Load(bs2d,f3d,l3d);
                curPC = bs2d;
                Standard_Boolean updatepcsov = updatepc;
                updatepc = Standard_True;

                Standard_Real error1 = ComputeTol(HC, HC2d, HS, NCONTROL);
                if(error1 > error) {
                  bs2d = bs2dsov;
                  GAC2d.Load(bs2d,f3d,l3d);
                  curPC = bs2d;
                  updatepc = updatepcsov;
                  isANA = Standard_True;
                }
                else {
                  error = error1;
                }
              }

              //check, if new BSpline "good" or not --------- IFV, Jan of 2000
              GeomAbs_Shape cont = bs2d->Continuity();
              Standard_Boolean IsBad = Standard_False;

              if(cont > GeomAbs_C0 && error > Max(1.e-3,theTolerance)) {
                Standard_Integer NbKnots = bs2d->NbKnots();
                TColStd_Array1OfReal Knots(1,NbKnots);
                bs2d->Knots(Knots);
                Standard_Real critratio = 10.; 
                Standard_Real dtprev = Knots(2) - Knots(1), dtratio = 1.;
                Standard_Real dtmin = dtprev;
                Standard_Real dtcur;
                for(Standard_Integer j = 2; j < NbKnots; j++) {
                  dtcur = Knots(j+1) - Knots(j);
                  dtmin = Min(dtmin, dtcur);

                  if(IsBad) continue;

                  if(dtcur > dtprev) dtratio = dtcur/dtprev;
                  else dtratio = dtprev/dtcur;
                  if(dtratio > critratio) {IsBad = Standard_True;}
                  dtprev = dtcur;

                }
                if(IsBad) {
                  // To avoid failures in Approx_CurvilinearParameter 
                  bs2d->Resolution(Max(1.e-3,theTolerance), dtcur);
                  if(dtmin < dtcur) IsBad = Standard_False;
                }
              }


              if(IsBad ) { //if BSpline "bad", try to reparametrize it
                // by its curve length

                //	      GeomAbs_Shape cont = bs2d->Continuity();
                if(cont > GeomAbs_C2) cont = GeomAbs_C2;
                Standard_Integer maxdeg = bs2d->Degree();
                if(maxdeg == 1) maxdeg = 14;
                Approx_CurvilinearParameter AppCurPar(HC2d, HS, Max(1.e-3,theTolerance),
                  cont, maxdeg, 10);
                if(AppCurPar.IsDone() || AppCurPar.HasResult()) {
                  bs2d = AppCurPar.Curve2d1();
                  GAC2d.Load(bs2d,f3d,l3d);
                  curPC = bs2d;

                  if(Abs(bs2d->FirstParameter() - fC0) > TolSameRange ||
                    Abs(bs2d->LastParameter() - lC0) > TolSameRange) {
                      Standard_Integer NbKnots = bs2d->NbKnots();
                      TColStd_Array1OfReal Knots(1,NbKnots);
                      bs2d->Knots(Knots);
                      //		  BSplCLib::Reparametrize(f3d,l3d,Knots);
                      BSplCLib::Reparametrize(fC0,lC0,Knots);
                      bs2d->SetKnots(Knots);
                      GAC2d.Load(bs2d,f3d,l3d);
                      curPC = bs2d;

                  }
                }
              }


            }
        }


        if(goodpc){
          //	  Approx_SameParameter SameP(HC,HC2d,HS,Tolerance);
          Standard_Real aTol = (isANA && isBSP) ? 1.e-7 : theTolerance;
          const Handle(Adaptor3d_Curve)& aHCurv = HC; // to avoid ambiguity
          const Handle(Adaptor2d_Curve2d)& aHCurv2d = HC2d; // to avoid ambiguity
          Approx_SameParameter SameP(aHCurv,aHCurv2d,HS,aTol);

          if (SameP.IsSameParameter()) {
            maxdist = Max(maxdist,SameP.TolReached());
            if(updatepc){
              if (i == 0) GCurve->PCurve(curPC);
              else GCurve->PCurve2(curPC);
            }
          }
          else if (SameP.IsDone()) {
            Standard_Real tolreached = SameP.TolReached();
            if(tolreached <= error) {
              curPC = SameP.Curve2d();
              updatepc = Standard_True;
              maxdist = Max(maxdist,tolreached);
            }
            else {
              maxdist = Max(maxdist, error);
            }
            if(updatepc){
              if (i == 0) GCurve->PCurve(curPC);
              else GCurve->PCurve2(curPC);
            }
          }
          else
          {
            //Approx_SameParameter has failed.
            //Consequently, the situation might be,
            //when 3D and 2D-curve do not have same-range.
            GeomLib::SameRange( TolSameRange, PC[i],
                                GCurve->First(), GCurve->Last(),
                                f3d,l3d,curPC);

            if (i == 0) GCurve->PCurve(curPC);
            else GCurve->PCurve2(curPC);

            IsSameP = 0;
          }

        }
        else IsSameP = 0;

        //  Modified by skv - Thu Jun  3 12:39:19 2004 OCC5898 Begin
        if (!IsSameP) {
          Standard_Real Prec_Surf = BRepCheck::PrecSurface(HS);
          Standard_Real CurTol = anEdgeTol + Max(Prec_C3d, Prec_Surf);
          if (CurTol >= error) {
            maxdist = Max(maxdist, anEdgeTol);
            IsSameP = Standard_True;
          }
        }
        //  Modified by skv - Thu Jun  3 12:39:20 2004 OCC5898 End
      }
    }
    It.Next() ;
  }
  B.Range(aNE,f3d,l3d);
  B.SameRange(aNE,Standard_True);
  if ( IsSameP) {
    // Reduce eventually the tolerance of the edge, as
    // all its representations are processed (except for some associated
    // to planes and not stored in the edge !) 
    // The same cannot be done with vertices that cannot be enlarged 
    // or left as is.
    if (YaPCu) {
      // Avoid setting too small tolerances.
      maxdist = Max(maxdist,Precision::Confusion());
      theNewTol = maxdist;
      aNTE->Modified(Standard_True);
      aNTE->Tolerance(maxdist);
    }
    B.SameParameter(aNE,Standard_True);
  }
  
  return aNE;
}

//=======================================================================
//function : InternalUpdateTolerances
//purpose  : 
//=======================================================================
static void InternalUpdateTolerances(const TopoDS_Shape& theOldShape,
  const Standard_Boolean IsVerifyTolerance, const Standard_Boolean IsMutableInput, BRepTools_ReShape& theReshaper)
{
  TopTools_DataMapOfShapeReal aShToTol;
  // Harmonize tolerances
  // with rule Tolerance(VERTEX)>=Tolerance(EDGE)>=Tolerance(FACE)
  Standard_Real tol=0;
  if (IsVerifyTolerance) {
    // Set tolerance to its minimum value
    Handle(Geom_Surface) S;
    TopLoc_Location l;
    TopExp_Explorer ex;
    Bnd_Box aB;
    Standard_Real aXmin, aYmin, aZmin, aXmax, aYmax, aZmax, dMax;
    for (ex.Init(theOldShape, TopAbs_FACE); ex.More(); ex.Next()) {
      const TopoDS_Face& curf=TopoDS::Face(ex.Current());
      S = BRep_Tool::Surface(curf, l);
      if (!S.IsNull()) {
        aB.SetVoid();
        BRepBndLib::Add(curf,aB);
        if (S->DynamicType() == STANDARD_TYPE(Geom_RectangularTrimmedSurface)) {
          S = Handle(Geom_RectangularTrimmedSurface)::DownCast (S)->BasisSurface();
        }
        GeomAdaptor_Surface AS(S);
        switch (AS.GetType()) {
        case GeomAbs_Plane: 
        case GeomAbs_Cylinder: 
        case GeomAbs_Cone: 
          {
            tol=Precision::Confusion();
            break;
          }
        case GeomAbs_Sphere: 
        case GeomAbs_Torus: 
          {
            tol=Precision::Confusion()*2;
            break;
          }
        default:
          tol=Precision::Confusion()*4;
        }
        if (!aB.IsWhole()) {
          aB.Get(aXmin, aYmin, aZmin, aXmax, aYmax, aZmax);
          dMax=1.;
          if (!aB.IsOpenXmin() && !aB.IsOpenXmax()) dMax=aXmax-aXmin;
          if (!aB.IsOpenYmin() && !aB.IsOpenYmax()) aYmin=aYmax-aYmin;
          if (!aB.IsOpenZmin() && !aB.IsOpenZmax()) aZmin=aZmax-aZmin;
          if (aYmin>dMax) dMax=aYmin;
          if (aZmin>dMax) dMax=aZmin;
          tol=tol*dMax;
          // Do not process tolerances > 1.
          if (tol>1.) tol=0.99;
        }
        aShToTol.Bind(curf, tol);
      }
    }
  }

  //Process edges
  TopTools_IndexedDataMapOfShapeListOfShape parents;
  TopExp::MapShapesAndAncestors(theOldShape, TopAbs_EDGE, TopAbs_FACE, parents);
  TopTools_ListIteratorOfListOfShape lConx;
  Standard_Integer iCur;
  for (iCur=1; iCur<=parents.Extent(); iCur++) {
    tol=0;
    for (lConx.Initialize(parents(iCur)); lConx.More(); lConx.Next()) 
    {
      const TopoDS_Face& FF = TopoDS::Face(lConx.Value());
      Standard_Real Ftol;
      if (IsVerifyTolerance && aShToTol.IsBound(FF)) //first condition for speed-up
        Ftol = aShToTol(FF);
      else
        Ftol = BRep_Tool::Tolerance(FF); //tolerance have not been updated
      tol=Max(tol, Ftol);
    }
    // Update can only increase tolerance, so if the edge has a greater
    //  tolerance than its faces it is not concerned
    const TopoDS_Edge& EK = TopoDS::Edge(parents.FindKey(iCur));
    if (tol > BRep_Tool::Tolerance(EK))
      aShToTol.Bind(EK, tol);
  }

  //Vertices are processed
  const Standard_Real BigTol = 1.e10;
  parents.Clear();

  TopExp::MapShapesAndUniqueAncestors(theOldShape, TopAbs_VERTEX, TopAbs_EDGE, parents);
  TColStd_MapOfTransient Initialized;
  Standard_Integer nbV = parents.Extent();
  for (iCur=1; iCur<=nbV; iCur++) {
    tol=0;
    const TopoDS_Vertex& V = TopoDS::Vertex(parents.FindKey(iCur));
    gp_Pnt aPV = BRep_Tool::Pnt(V);
    Standard_Real aMaxDist = 0.;
    gp_Pnt p3d;
    for (lConx.Initialize(parents(iCur)); lConx.More(); lConx.Next()) {
      const TopoDS_Edge& E = TopoDS::Edge(lConx.Value());
      const Standard_Real* aNtol = aShToTol.Seek(E);
      tol=Max(tol, aNtol ? *aNtol : BRep_Tool::Tolerance(E));
      if(tol > BigTol) continue;
      if(!BRep_Tool::SameRange(E)) continue;
      Standard_Real par = BRep_Tool::Parameter(V,E);
      Handle(BRep_TEdge)& TE = *((Handle(BRep_TEdge)*)&E.TShape());
      BRep_ListIteratorOfListOfCurveRepresentation itcr(TE->Curves());
      const TopLoc_Location& Eloc = E.Location();
      while (itcr.More()) {
        // For each CurveRepresentation, check the provided parameter
        const Handle(BRep_CurveRepresentation)& cr = itcr.Value();
        const TopLoc_Location& loc = cr->Location();
        TopLoc_Location L = (Eloc * loc);
        if (cr->IsCurve3D()) {
          const Handle(Geom_Curve)& C = cr->Curve3D();
          if (!C.IsNull()) { // edge non degenerated
            p3d = C->Value(par);
            p3d.Transform(L.Transformation());
            Standard_Real aDist = p3d.SquareDistance(aPV);
            if (aDist > aMaxDist)
              aMaxDist = aDist;
           }
        }
        else if (cr->IsCurveOnSurface()) {
          const Handle(Geom_Surface)& Su = cr->Surface();
          const Handle(Geom2d_Curve)& PC = cr->PCurve();
          Handle(Geom2d_Curve) PC2;
          if (cr->IsCurveOnClosedSurface()) {
            PC2 = cr->PCurve2();
          }
          gp_Pnt2d p2d = PC->Value(par);
          p3d = Su->Value(p2d.X(),p2d.Y());
          p3d.Transform(L.Transformation());
          Standard_Real aDist = p3d.SquareDistance(aPV);
          if (aDist > aMaxDist)
            aMaxDist = aDist;
          if (!PC2.IsNull()) {
            p2d = PC2->Value(par);
            p3d = Su->Value(p2d.X(),p2d.Y());
            p3d.Transform(L.Transformation());
            aDist = p3d.SquareDistance(aPV);
            if (aDist > aMaxDist)
              aMaxDist = aDist;
          }
        }
        itcr.Next();
      }
    }
    tol = Max(tol, sqrt(aMaxDist));
    tol += 2.*Epsilon(tol);
    //
    Standard_Real aVTol = BRep_Tool::Tolerance(V);
    Standard_Boolean anUpdTol = tol > aVTol;
    const Handle(BRep_TVertex)& aTV = *((Handle(BRep_TVertex)*)&V.TShape());
    Standard_Boolean toAdd = Standard_False;
    if (IsVerifyTolerance) 
    {
      // ASet minimum value of the tolerance 
      // Attention to sharing of the vertex by other shapes      
      toAdd = Initialized.Add(aTV) && aVTol != tol; //if Vtol == tol => no need to update toler
    }
    //'Initialized' map is not used anywhere outside this block
    if (anUpdTol || toAdd)
      aShToTol.Bind(V, tol);
  }

  UpdShTol(aShToTol, IsMutableInput, theReshaper, Standard_True);
}

//=======================================================================
//function : UpdateTolerances
//purpose  : 
//=======================================================================
void BRepLib::UpdateTolerances (const TopoDS_Shape& S, const Standard_Boolean verifyFaceTolerance)
{
  BRepTools_ReShape aReshaper;
  InternalUpdateTolerances(S, verifyFaceTolerance, Standard_True, aReshaper);
}

//=======================================================================
//function : UpdateTolerances
//purpose  : 
//=======================================================================
void BRepLib::UpdateTolerances (const TopoDS_Shape& S, BRepTools_ReShape& theReshaper, const Standard_Boolean verifyFaceTolerance )
{
  InternalUpdateTolerances(S, verifyFaceTolerance, Standard_False, theReshaper);
}

//=======================================================================
//function : UpdateInnerTolerances
//purpose  : 
//=======================================================================
void  BRepLib::UpdateInnerTolerances(const TopoDS_Shape& aShape)
{
  TopTools_IndexedDataMapOfShapeListOfShape EFmap;
  TopExp::MapShapesAndAncestors(aShape, TopAbs_EDGE, TopAbs_FACE, EFmap);
  BRep_Builder BB;
  for (Standard_Integer i = 1; i <= EFmap.Extent(); i++)
  {
    TopoDS_Edge anEdge = TopoDS::Edge(EFmap.FindKey(i));

    if (!BRep_Tool::IsGeometric(anEdge))
    {
      continue;
    }

    TopoDS_Vertex V1, V2;
    TopExp::Vertices(anEdge, V1, V2);
    Standard_Real fpar, lpar;
    BRep_Tool::Range(anEdge, fpar, lpar);
    Standard_Real TolEdge = BRep_Tool::Tolerance(anEdge);
    gp_Pnt Pnt1, Pnt2;
    Handle(BRepAdaptor_Curve) anHCurve = new BRepAdaptor_Curve();
    anHCurve->Initialize(anEdge);
    if (!V1.IsNull())
      Pnt1 = BRep_Tool::Pnt(V1);
    if (!V2.IsNull())
      Pnt2 = BRep_Tool::Pnt(V2);
    
    if (!BRep_Tool::Degenerated(anEdge) &&
        EFmap(i).Extent() > 0)
    {
      NCollection_Sequence<Handle(Adaptor3d_Curve)> theRep;
      theRep.Append(anHCurve);
      TopTools_ListIteratorOfListOfShape itl(EFmap(i));
      for (; itl.More(); itl.Next())
      {
        const TopoDS_Face& aFace = TopoDS::Face(itl.Value());
        Handle(BRepAdaptor_Curve) anHCurvOnSurf = new BRepAdaptor_Curve();
        anHCurvOnSurf->Initialize(anEdge, aFace);
        theRep.Append(anHCurvOnSurf);
      }
      
      const Standard_Integer NbSamples = (BRep_Tool::SameParameter(anEdge))? 23 : 2;
      Standard_Real delta = (lpar - fpar)/(NbSamples-1);
      Standard_Real MaxDist = 0.;
      for (Standard_Integer j = 2; j <= theRep.Length(); j++)
      {
        for (Standard_Integer k = 0; k <= NbSamples; k++)
        {
          Standard_Real ParamOnCenter = (k == NbSamples)? lpar :
            fpar + k*delta;
          gp_Pnt Center = theRep(1)->Value(ParamOnCenter);
          Standard_Real ParamOnCurve = (BRep_Tool::SameParameter(anEdge))? ParamOnCenter
            : ((k == 0)? theRep(j)->FirstParameter() : theRep(j)->LastParameter());
          gp_Pnt aPoint = theRep(j)->Value(ParamOnCurve);
          Standard_Real aDist = Center.Distance(aPoint);
          //aDist *= 1.1;
          aDist += 2.*Epsilon(aDist);
          if (aDist > MaxDist)
            MaxDist = aDist;

          //Update tolerances of vertices
          if (k == 0 && !V1.IsNull())
          {
            Standard_Real aDist1 = Pnt1.Distance(aPoint);
            aDist1 += 2.*Epsilon(aDist1);
            BB.UpdateVertex(V1, aDist1);
          }
          if (k == NbSamples && !V2.IsNull())
          {
            Standard_Real aDist2 = Pnt2.Distance(aPoint);
            aDist2 += 2.*Epsilon(aDist2);
            BB.UpdateVertex(V2, aDist2);
          }
        }
      }
      BB.UpdateEdge(anEdge, MaxDist);
    }
    TolEdge = BRep_Tool::Tolerance(anEdge);
    if (!V1.IsNull())
    {
      gp_Pnt End1 = anHCurve->Value(fpar);
      Standard_Real dist1 = Pnt1.Distance(End1);
      dist1 += 2.*Epsilon(dist1);
      BB.UpdateVertex(V1, Max(dist1, TolEdge));
    }
    if (!V2.IsNull())
    {
      gp_Pnt End2 = anHCurve->Value(lpar);
      Standard_Real dist2 = Pnt2.Distance(End2);
      dist2 += 2.*Epsilon(dist2);
      BB.UpdateVertex(V2, Max(dist2, TolEdge));
    }
  }
}

//=======================================================================
//function : OrientClosedSolid
//purpose  : 
//=======================================================================
Standard_Boolean BRepLib::OrientClosedSolid(TopoDS_Solid& solid) 
{
  // Set material inside the solid
  BRepClass3d_SolidClassifier where(solid);
  where.PerformInfinitePoint(Precision::Confusion());
  if (where.State()==TopAbs_IN) {
    solid.Reverse();
  }
  else if (where.State()==TopAbs_ON || where.State()==TopAbs_UNKNOWN) 
    return Standard_False;

  return Standard_True;
}

// Structure for calculation of properties, necessary for decision about continuity
class SurfaceProperties
{
public:
  SurfaceProperties(const Handle(Geom_Surface)& theSurface,
                    const gp_Trsf&              theSurfaceTrsf,
                    const Handle(Geom2d_Curve)& theCurve2D,
                    const Standard_Boolean      theReversed)
    : mySurfaceProps(theSurface, 2, Precision::Confusion()),
      mySurfaceTrsf(theSurfaceTrsf),
      myCurve2d(theCurve2D),
      myIsReversed(theReversed)
  {}

  // Calculate derivatives on surface related to the point on curve
  void Calculate(const Standard_Real theParamOnCurve)
  {
    gp_Pnt2d aUV;
    myCurve2d->D1(theParamOnCurve, aUV, myCurveTangent);
    mySurfaceProps.SetParameters(aUV.X(), aUV.Y());
  }

  // Returns point just calculated
  gp_Pnt Value() 
  { return mySurfaceProps.Value().Transformed(mySurfaceTrsf); }

  // Calculate a derivative orthogonal to curve's tangent vector
  gp_Vec Derivative()
  {
    gp_Vec aDeriv;
    // direction orthogonal to tangent vector of the curve
    gp_Vec2d anOrtho(-myCurveTangent.Y(), myCurveTangent.X());
    Standard_Real aLen = anOrtho.Magnitude();
    if (aLen < Precision::Confusion())
      return aDeriv;
    anOrtho /= aLen;
    if (myIsReversed)
      anOrtho.Reverse();

    aDeriv.SetLinearForm(anOrtho.X(), mySurfaceProps.D1U(),
                         anOrtho.Y(), mySurfaceProps.D1V());
    return aDeriv.Transformed(mySurfaceTrsf);
  }

  gp_Dir Normal()
  {
    gp_Dir aNormal = mySurfaceProps.Normal();
    return aNormal.Transformed(mySurfaceTrsf);
  }

  // Calculate principal curvatures, which consist of minimal and maximal normal curvatures and
  // the directions on the tangent plane (principal direction) where the extremums are reached
  void Curvature(gp_Dir& thePrincipalDir1, Standard_Real& theCurvature1,
                 gp_Dir& thePrincipalDir2, Standard_Real& theCurvature2)
  {
    mySurfaceProps.CurvatureDirections(thePrincipalDir1, thePrincipalDir2);
    theCurvature1 = mySurfaceProps.MaxCurvature();
    theCurvature2 = mySurfaceProps.MinCurvature();
    if (myIsReversed)
    {
      theCurvature1 = -theCurvature1;
      theCurvature2 = -theCurvature2;
    }
    if (mySurfaceTrsf.IsNegative())
    {
      theCurvature1 = -theCurvature1;
      theCurvature2 = -theCurvature2;
    }

    thePrincipalDir1.Transform(mySurfaceTrsf);
    thePrincipalDir2.Transform(mySurfaceTrsf);
  }

private:
  GeomLProp_SLProps    mySurfaceProps; // properties calculator
  gp_Trsf              mySurfaceTrsf;
  Handle(Geom2d_Curve) myCurve2d;
  Standard_Boolean     myIsReversed; // the face based on the surface is reversed

  // tangent vector to Pcurve in UV
  gp_Vec2d myCurveTangent;
};

//=======================================================================
//function : tgtfaces
//purpose  : check the angle at the border between two squares.
//           Two shares should have a shared front edge.
//=======================================================================
GeomAbs_Shape BRepLib::ContinuityOfFaces(const TopoDS_Edge&  theEdge,
                                         const TopoDS_Face&  theFace1,
                                         const TopoDS_Face&  theFace2,
                                         const Standard_Real theAngleTol)
{
  Standard_Boolean isSeam = theFace1.IsEqual(theFace2);

  TopoDS_Edge anEdgeInFace1, anEdgeInFace2;
  Handle(Geom2d_Curve) aCurve1, aCurve2;
  
  Standard_Real aFirst, aLast;
  
  if (!theFace1.IsSame (theFace2) &&
      BRep_Tool::IsClosed (theEdge, theFace1) &&
      BRep_Tool::IsClosed (theEdge, theFace2))
  {
    //Find the edge in the face 1: this edge will have correct orientation
    TopoDS_Face aFace1 = theFace1;
    aFace1.Orientation (TopAbs_FORWARD);
    TopExp_Explorer anExplo (aFace1, TopAbs_EDGE);
    for (; anExplo.More(); anExplo.Next())
    {
      const TopoDS_Edge& anEdge = TopoDS::Edge (anExplo.Current());
      if (anEdge.IsSame (theEdge))
      {
        anEdgeInFace1 = anEdge;
        break;
      }
    }
    if (anEdgeInFace1.IsNull())
      return GeomAbs_C0;
    
    aCurve1 = BRep_Tool::CurveOnSurface (anEdgeInFace1, aFace1, aFirst, aLast);
    TopoDS_Face aFace2 = theFace2;
    aFace2.Orientation (TopAbs_FORWARD);
    anEdgeInFace2 = anEdgeInFace1;
    anEdgeInFace2.Reverse();
    aCurve2 = BRep_Tool::CurveOnSurface (anEdgeInFace2, aFace2, aFirst, aLast);
  }
  else
  {
    // Obtaining of pcurves of edge on two faces.
    anEdgeInFace1 = anEdgeInFace2 = theEdge;
    aCurve1 = BRep_Tool::CurveOnSurface (anEdgeInFace1, theFace1, aFirst, aLast);
    //For the case of seam edge
    if (theFace1.IsSame(theFace2))
      anEdgeInFace2.Reverse();
    aCurve2 = BRep_Tool::CurveOnSurface (anEdgeInFace2, theFace2, aFirst, aLast);
  }

  if (aCurve1.IsNull() || aCurve2.IsNull())
    return GeomAbs_C0;

  TopLoc_Location aLoc1, aLoc2;
  Handle(Geom_Surface) aSurface1 = BRep_Tool::Surface (theFace1, aLoc1);
  const gp_Trsf& aSurf1Trsf = aLoc1.Transformation();
  Handle(Geom_Surface) aSurface2 = BRep_Tool::Surface (theFace2, aLoc2);
  const gp_Trsf& aSurf2Trsf = aLoc2.Transformation();

  if (aSurface1->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface)))
    aSurface1 = Handle(Geom_RectangularTrimmedSurface)::DownCast(aSurface1)->BasisSurface();
  if (aSurface2->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface)))
    aSurface2 = Handle(Geom_RectangularTrimmedSurface)::DownCast(aSurface2)->BasisSurface();

  // seam edge on elementary surface is always CN
  Standard_Boolean isElementary =
    (aSurface1->IsKind(STANDARD_TYPE(Geom_ElementarySurface)) &&
     aSurface2->IsKind(STANDARD_TYPE(Geom_ElementarySurface)));
  if (isSeam && isElementary)
  {
    return GeomAbs_CN;
  }

  SurfaceProperties aSP1(aSurface1, aSurf1Trsf, aCurve1, theFace1.Orientation() == TopAbs_REVERSED);
  SurfaceProperties aSP2(aSurface2, aSurf2Trsf, aCurve2, theFace2.Orientation() == TopAbs_REVERSED);

  Standard_Real f, l, eps;
  BRep_Tool::Range (theEdge,f,l);
  Extrema_LocateExtPC ext;
  Handle(BRepAdaptor_Curve) aHC2;

  eps = (l - f)/100.;
  f += eps; // to avoid calculations on  
  l -= eps; // points of pointed squares.

  const Standard_Real anAngleTol2 = theAngleTol * theAngleTol;

  gp_Vec aDer1, aDer2;
  Standard_Real aSqLen1, aSqLen2;
  gp_Dir aCrvDir1[2], aCrvDir2[2];
  Standard_Real aCrvLen1[2], aCrvLen2[2];

  GeomAbs_Shape aCont = (isElementary ? GeomAbs_CN : GeomAbs_C2);
  GeomAbs_Shape aCurCont;
  Standard_Real u;
  for (Standard_Integer i = 0; i <= 20 && aCont > GeomAbs_C0; i++)
  {
    // First suppose that this is sameParameter
    u = f + (l-f)*i/20;

    // Check conditions for G1 and C1 continuity:
    // * calculate a derivative in tangent plane of each surface
    //   orthogonal to curve's tangent vector
    // * continuity is C1 if the vectors are equal
    // * continuity is G1 if the vectors are just parallel
    aCurCont = GeomAbs_C0;

    aSP1.Calculate(u);
    aSP2.Calculate(u);

    aDer1 = aSP1.Derivative();
    aSqLen1 = aDer1.SquareMagnitude();
    aDer2 = aSP2.Derivative();
    aSqLen2 = aDer2.SquareMagnitude();
    Standard_Boolean isSmoothSuspect = (aDer1.CrossSquareMagnitude(aDer2) <= anAngleTol2 * aSqLen1 * aSqLen2);
    if (isSmoothSuspect)
    {
      gp_Dir aNormal1 = aSP1.Normal();
      if (theFace1.Orientation() == TopAbs_REVERSED)
        aNormal1.Reverse();
      gp_Dir aNormal2 = aSP2.Normal();
      if (theFace2.Orientation() == TopAbs_REVERSED)
        aNormal2.Reverse();
      
      if (aNormal1 * aNormal2 < 0.)
        return GeomAbs_C0;
    }
    
    if (!isSmoothSuspect)
    {
      // Refine by projection
      if (aHC2.IsNull())
      {
        // adaptor for pcurve on the second surface
        aHC2 = new BRepAdaptor_Curve (anEdgeInFace2, theFace2);
        ext.Initialize(*aHC2, f, l, Precision::PConfusion());
      }
      ext.Perform(aSP1.Value(), u);
      if (ext.IsDone() && ext.IsMin())
      {
        const Extrema_POnCurv& poc = ext.Point();
        aSP2.Calculate(poc.Parameter());
        aDer2 = aSP2.Derivative();
        aSqLen2 = aDer2.SquareMagnitude();
      }
      isSmoothSuspect = (aDer1.CrossSquareMagnitude(aDer2) <= anAngleTol2 * aSqLen1 * aSqLen2);
    }
    if (isSmoothSuspect)
    {
      aCurCont = GeomAbs_G1;
      if (Abs(Sqrt(aSqLen1) - Sqrt(aSqLen2)) < Precision::Confusion() &&
          aDer1.Dot(aDer2) > Precision::SquareConfusion()) // <= check vectors are codirectional
        aCurCont = GeomAbs_C1;
    }
    else
      return GeomAbs_C0;

    if (aCont < GeomAbs_G2)
      continue; // no need further processing, because maximal continuity is less than G2

    // Check conditions for G2 and C2 continuity:
    // * calculate principal curvatures on each surface
    // * continuity is C2 if directions of principal curvatures are equal on different surfaces
    // * continuity is G2 if directions of principal curvatures are just parallel
    //   and values of curvatures are the same
    aSP1.Curvature(aCrvDir1[0], aCrvLen1[0], aCrvDir1[1], aCrvLen1[1]);
    aSP2.Curvature(aCrvDir2[0], aCrvLen2[0], aCrvDir2[1], aCrvLen2[1]);
    for (Standard_Integer aStep = 0; aStep <= 1; ++aStep)
    {
      if (aCrvDir1[0].XYZ().CrossSquareMagnitude(aCrvDir2[aStep].XYZ()) <= Precision::SquareConfusion() &&
          Abs(aCrvLen1[0] - aCrvLen2[aStep]) < Precision::Confusion() &&
          aCrvDir1[1].XYZ().CrossSquareMagnitude(aCrvDir2[1 - aStep].XYZ()) <= Precision::SquareConfusion() &&
          Abs(aCrvLen1[1] - aCrvLen2[1 - aStep]) < Precision::Confusion())
      {
        if (aCurCont == GeomAbs_C1 &&
            aCrvDir1[0].Dot(aCrvDir2[aStep]) > Precision::Confusion() &&
            aCrvDir1[1].Dot(aCrvDir2[1 - aStep]) > Precision::Confusion())
          aCurCont = GeomAbs_C2;
        else
          aCurCont = GeomAbs_G2;
        break;
      }
    }

    if (aCurCont < aCont)
      aCont = aCurCont;
  }

  // according to the list of supported elementary surfaces,
  // if the continuity is C2, than it is totally CN
  if (isElementary && aCont == GeomAbs_C2)
    aCont = GeomAbs_CN;
  return aCont;
}

//=======================================================================
// function : EncodeRegularity
// purpose  : Code the regularities on all edges of the shape, boundary of 
//            two faces that do not have it.
//            Takes into account that compound may consists of same solid
//            placed with different transformations
//=======================================================================
static void EncodeRegularity(const TopoDS_Shape&        theShape,
                             const Standard_Real        theTolAng,
                             TopTools_MapOfShape&       theMap,
                             const TopTools_MapOfShape& theEdgesToEncode = TopTools_MapOfShape())
{
  TopoDS_Shape aShape = theShape;
  TopLoc_Location aNullLoc;
  aShape.Location(aNullLoc); // nullify location
  if (!theMap.Add(aShape))
    return; // do not need to process shape twice

  if (aShape.ShapeType() == TopAbs_COMPOUND ||
      aShape.ShapeType() == TopAbs_COMPSOLID)
  {
    for (TopoDS_Iterator it(aShape); it.More(); it.Next())
      EncodeRegularity(it.Value(), theTolAng, theMap, theEdgesToEncode);
    return;
  }

  try {
    OCC_CATCH_SIGNALS

    TopTools_IndexedDataMapOfShapeListOfShape M;
    TopExp::MapShapesAndAncestors(aShape, TopAbs_EDGE, TopAbs_FACE, M);
    TopTools_ListIteratorOfListOfShape It;
    TopExp_Explorer Ex;
    TopoDS_Face F1,F2;
    for (Standard_Integer i = 1; i <= M.Extent(); i++){
      TopoDS_Edge E = TopoDS::Edge(M.FindKey(i));
      if (!theEdgesToEncode.IsEmpty())
      {
        // process only the edges from the list to update their regularity
        TopoDS_Shape aPureEdge = E.Located(aNullLoc);
        aPureEdge.Orientation(TopAbs_FORWARD);
        if (!theEdgesToEncode.Contains(aPureEdge))
          continue;
      }

      Standard_Boolean found = Standard_False;
      F1.Nullify();
      for (It.Initialize(M.FindFromIndex(i)); It.More() && !found; It.Next()){
        if (F1.IsNull()) { F1 = TopoDS::Face(It.Value()); }
        else {
          const TopoDS_Face& aTmpF2 = TopoDS::Face(It.Value());
          if (!F1.IsSame(aTmpF2)){
            found = Standard_True;
            F2 = aTmpF2;
          }
        }
      }
      if (!found && !F1.IsNull()){//is it a sewing edge?
        TopAbs_Orientation orE = E.Orientation();
        TopoDS_Edge curE;
        for (Ex.Init(F1, TopAbs_EDGE); Ex.More() && !found; Ex.Next()){
          curE = TopoDS::Edge(Ex.Current());
          if (E.IsSame(curE) && orE != curE.Orientation()) {
            found = Standard_True;
            F2 = F1;
          }
        }
      }
      if (found)
        BRepLib::EncodeRegularity(E, F1, F2, theTolAng);
    }
  }
  catch (Standard_Failure const& anException) {
#ifdef OCCT_DEBUG
    std::cout << "Warning: Exception in BRepLib::EncodeRegularity(): ";
    anException.Print(std::cout);
    std::cout << std::endl;
#endif
    (void)anException;
  }
}

//=======================================================================
// function : EncodeRegularity
// purpose  : code the regularities on all edges of the shape, boundary of 
//            two faces that do not have it.
//=======================================================================

void BRepLib::EncodeRegularity(const TopoDS_Shape& S,
  const Standard_Real TolAng)
{
  TopTools_MapOfShape aMap;
  ::EncodeRegularity(S, TolAng, aMap);
}

//=======================================================================
// function : EncodeRegularity
// purpose  : code the regularities on all edges in the list that do not 
//            have it, and which are boundary of two faces on the shape.
//=======================================================================

void BRepLib::EncodeRegularity(const TopoDS_Shape& S,
  const TopTools_ListOfShape& LE,
  const Standard_Real TolAng)
{
  // Collect edges without location and orientation
  TopTools_MapOfShape aPureEdges;
  TopLoc_Location aNullLoc;
  TopTools_ListIteratorOfListOfShape anEdgeIt(LE);
  for (; anEdgeIt.More(); anEdgeIt.Next())
  {
    TopoDS_Shape anEdge = anEdgeIt.Value();
    anEdge.Location(aNullLoc);
    anEdge.Orientation(TopAbs_FORWARD);
    aPureEdges.Add(anEdge);
  }

  TopTools_MapOfShape aMap;
  ::EncodeRegularity(S, TolAng, aMap, aPureEdges);
}

//=======================================================================
// function : EncodeRegularity
// purpose  : code the regularity between 2 faces connected by edge 
//=======================================================================

void BRepLib::EncodeRegularity(TopoDS_Edge& E,
  const TopoDS_Face& F1,
  const TopoDS_Face& F2,
  const Standard_Real TolAng)
{
  BRep_Builder B;
  if(BRep_Tool::Continuity(E,F1,F2)<=GeomAbs_C0){
    try {
      GeomAbs_Shape aCont = ContinuityOfFaces(E, F1, F2, TolAng);
      B.Continuity(E,F1,F2,aCont);
    }
    catch(Standard_Failure const&)
    {
#ifdef OCCT_DEBUG
      std::cout << "Failure: Exception in BRepLib::EncodeRegularity" << std::endl;
#endif
    }
  }
}

//=======================================================================
// function : EnsureNormalConsistency
// purpose  : Corrects the normals in Poly_Triangulation of faces.
//              Returns TRUE if any correction is done.
//=======================================================================
Standard_Boolean BRepLib::
            EnsureNormalConsistency(const TopoDS_Shape& theShape,
                                    const Standard_Real theAngTol,
                                    const Standard_Boolean theForceComputeNormals)
{
  const Standard_Real aThresDot = cos(theAngTol);

  Standard_Boolean aRetVal = Standard_False, isNormalsFound = Standard_False;

  // compute normals if they are absent
  TopExp_Explorer anExpFace(theShape,TopAbs_FACE);
  for (; anExpFace.More(); anExpFace.Next())
  {
    const TopoDS_Face& aFace = TopoDS::Face(anExpFace.Current());
    const Handle(Geom_Surface) aSurf = BRep_Tool::Surface(aFace);
    if(aSurf.IsNull())
      continue;
    TopLoc_Location aLoc;
    const Handle(Poly_Triangulation)& aPT = BRep_Tool::Triangulation(aFace, aLoc);
    if(aPT.IsNull())
      continue;
    if (!theForceComputeNormals && aPT->HasNormals())
    {
      isNormalsFound = Standard_True;
      continue;
    }

    aPT->AddNormals();
    GeomLProp_SLProps aSLP(aSurf, 2, Precision::Confusion());
    for (Standard_Integer i = 1; i <= aPT->NbNodes(); i++)
    {
      const gp_Pnt2d aP2d = aPT->UVNode (i);
      aSLP.SetParameters(aP2d.X(), aP2d.Y());

      if(!aSLP.IsNormalDefined())
      {
#ifdef OCCT_DEBUG
        std::cout << "BRepLib::EnsureNormalConsistency(): Cannot find normal!" << std::endl;
#endif
        aPT->SetNormal (i, gp_Vec3f (0.0f));
      }
      else
      {
        gp_Dir aNorm = aSLP.Normal();
        if (aFace.Orientation() == TopAbs_REVERSED)
        {
          aNorm.Reverse();
        }
        aPT->SetNormal (i, aNorm);
      }
    }

    aRetVal = Standard_True;
    isNormalsFound = Standard_True;
  }

  if(!isNormalsFound)
  {
    return aRetVal;
  }

  // loop by edges
  TopTools_IndexedDataMapOfShapeListOfShape aMapEF;
  TopExp::MapShapesAndAncestors(theShape,TopAbs_EDGE,TopAbs_FACE,aMapEF);
  for(Standard_Integer anInd = 1; anInd <= aMapEF.Extent(); anInd++)
  {
    const TopoDS_Edge& anEdg = TopoDS::Edge(aMapEF.FindKey(anInd));
    const TopTools_ListOfShape& anEdgList = aMapEF.FindFromIndex(anInd);
    if (anEdgList.Extent() != 2)
      continue;
    TopTools_ListIteratorOfListOfShape anItF(anEdgList);
    const TopoDS_Face aFace1 = TopoDS::Face(anItF.Value());
    anItF.Next();
    const TopoDS_Face aFace2 = TopoDS::Face(anItF.Value());
    TopLoc_Location aLoc1, aLoc2;
    const Handle(Poly_Triangulation)& aPT1 = BRep_Tool::Triangulation(aFace1, aLoc1);
    const Handle(Poly_Triangulation)& aPT2 = BRep_Tool::Triangulation(aFace2, aLoc2);

    if(aPT1.IsNull() || aPT2.IsNull())
      continue;

    if(!aPT1->HasNormals() || !aPT2->HasNormals())
      continue;

    const Handle(Poly_PolygonOnTriangulation)& aPTEF1 = 
                                BRep_Tool::PolygonOnTriangulation(anEdg, aPT1, aLoc1);
    const Handle(Poly_PolygonOnTriangulation)& aPTEF2 = 
                                BRep_Tool::PolygonOnTriangulation(anEdg, aPT2, aLoc2);

    if (aPTEF1->Nodes().Lower() != aPTEF2->Nodes().Lower() || 
        aPTEF1->Nodes().Upper() != aPTEF2->Nodes().Upper()) 
      continue; 

    for(Standard_Integer anEdgNode = aPTEF1->Nodes().Lower();
                         anEdgNode <= aPTEF1->Nodes().Upper(); anEdgNode++)
    {
      //Number of node
      const Standard_Integer aFNodF1 = aPTEF1->Nodes().Value(anEdgNode);
      const Standard_Integer aFNodF2 = aPTEF2->Nodes().Value(anEdgNode);

      gp_Vec3f aNorm1f, aNorm2f;
      aPT1->Normal (aFNodF1, aNorm1f);
      aPT2->Normal (aFNodF2, aNorm2f);
      const gp_XYZ aNorm1 (aNorm1f.x(), aNorm1f.y(), aNorm1f.z());
      const gp_XYZ aNorm2 (aNorm2f.x(), aNorm2f.y(), aNorm2f.z());
      const Standard_Real aDot = aNorm1 * aNorm2;
      if (aDot > aThresDot)
      {
        gp_XYZ aNewNorm = (aNorm1 + aNorm2).Normalized();
        aPT1->SetNormal (aFNodF1, aNewNorm);
        aPT2->SetNormal (aFNodF2, aNewNorm);
        aRetVal = Standard_True;
      }
    }
  }

  return aRetVal;
}

//=======================================================================
//function : UpdateDeflection
//purpose  : 
//=======================================================================
namespace
{
  //! Tool to estimate deflection of the given UV point
  //! with regard to its representation in 3D space.
  struct EvalDeflection
  {
    BRepAdaptor_Surface Surface;

    //! Initializes tool with the given face.
    EvalDeflection (const TopoDS_Face& theFace)
      : Surface (theFace)
    {
    }

    //! Evaluates deflection of the given 2d point from its 3d representation.
    Standard_Real Eval (const gp_Pnt2d& thePoint2d, const gp_Pnt& thePoint3d)
    {
      gp_Pnt aPnt;
      Surface.D0 (thePoint2d.X (), thePoint2d.Y (), aPnt);
      return (thePoint3d.XYZ () - aPnt.XYZ ()).SquareModulus ();
    }
  };

  //! Represents link of triangulation.
  struct Link
  {
    Standard_Integer Node[2];

    //! Constructor
    Link (const Standard_Integer theNode1, const Standard_Integer theNode2)
    {
      Node[0] = theNode1;
      Node[1] = theNode2;
    }

    //! Returns true if this link has the same nodes as the other.
    Standard_Boolean IsEqual (const Link& theOther) const
    {
      return ((Node[0] == theOther.Node[0] && Node[1] == theOther.Node[1]) ||
              (Node[0] == theOther.Node[1] && Node[1] == theOther.Node[0]));
    }

    //! Alias for IsEqual.
    Standard_Boolean operator ==(const Link& theOther) const
    {
      return IsEqual (theOther);
    }
  };
}

namespace std
{
  template <>
  struct hash<Link>
  {
    size_t operator()(const Link& theLink) const noexcept
    {
      int aCombination[2]{ theLink.Node[0], theLink.Node[1] };
      if (aCombination[0] > aCombination[1])
      {
        std::swap(aCombination[0], aCombination[1]);
      }
      return opencascade::hashBytes(aCombination, sizeof(aCombination));
    }
  };
}

void BRepLib::UpdateDeflection (const TopoDS_Shape& theShape)
{
  TopExp_Explorer anExpFace (theShape, TopAbs_FACE);
  for (; anExpFace.More(); anExpFace.Next())
  {
    const TopoDS_Face& aFace = TopoDS::Face (anExpFace.Current());
    const Handle(Geom_Surface) aSurf = BRep_Tool::Surface (aFace);
    if (aSurf.IsNull())
    {
      continue;
    }

    TopLoc_Location aLoc;
    const Handle(Poly_Triangulation)& aPT = BRep_Tool::Triangulation (aFace, aLoc);
    if (aPT.IsNull() || !aPT->HasUVNodes())
    {
      continue;
    }

    // Collect all nodes of degenerative edges and skip elements
    // build upon them due to huge distortions introduced by passage
    // from UV space to 3D.
    NCollection_Map<Standard_Integer> aDegNodes;
    TopExp_Explorer anExpEdge (aFace, TopAbs_EDGE);
    for (; anExpEdge.More(); anExpEdge.Next())
    {
      const TopoDS_Edge& aEdge = TopoDS::Edge (anExpEdge.Current());
      if (BRep_Tool::Degenerated (aEdge))
      {
        const Handle(Poly_PolygonOnTriangulation)& aPolygon = BRep_Tool::PolygonOnTriangulation (aEdge, aPT, aLoc);
        if (aPolygon.IsNull ())
        {
          continue;
        }

        for (Standard_Integer aNodeIt = aPolygon->Nodes().Lower(); aNodeIt <= aPolygon->Nodes().Upper(); ++aNodeIt)
        {
          aDegNodes.Add (aPolygon->Node (aNodeIt));
        }
      }
    }

    EvalDeflection aTool (aFace);
    NCollection_Map<Link> aLinks;
    Standard_Real aSqDeflection = 0.;
    const gp_Trsf& aTrsf = aLoc.Transformation();
    for (Standard_Integer aTriIt = 1; aTriIt <= aPT->NbTriangles(); ++aTriIt)
    {
      const Poly_Triangle& aTriangle = aPT->Triangle (aTriIt);

      int aNode[3];
      aTriangle.Get (aNode[0], aNode[1], aNode[2]);
      if (aDegNodes.Contains (aNode[0]) ||
          aDegNodes.Contains (aNode[1]) ||
          aDegNodes.Contains (aNode[2]))
      {
        continue;
      }

      const gp_Pnt aP3d[3] = {
        aPT->Node (aNode[0]).Transformed (aTrsf),
        aPT->Node (aNode[1]).Transformed (aTrsf),
        aPT->Node (aNode[2]).Transformed (aTrsf)
      };

      const gp_Pnt2d aP2d[3] = {
        aPT->UVNode (aNode[0]),
        aPT->UVNode (aNode[1]),
        aPT->UVNode (aNode[2])
      };

      // Check midpoint of triangle.
      const gp_Pnt   aMid3d_t = (aP3d[0].XYZ() + aP3d[1].XYZ() + aP3d[2].XYZ()) / 3.;
      const gp_Pnt2d aMid2d_t = (aP2d[0].XY () + aP2d[1].XY () + aP2d[2].XY ()) / 3.;

      aSqDeflection = Max (aSqDeflection, aTool.Eval (aMid2d_t, aMid3d_t));

      for (Standard_Integer i = 0; i < 3; ++i)
      {
        const Standard_Integer j = (i + 1) % 3;
        const Link aLink (aNode[i], aNode[j]);
        if (!aLinks.Add (aLink))
        {
          // Do not estimate boundary links due to high distortions at the edge.
          const gp_Pnt&   aP3d1 = aP3d[i];
          const gp_Pnt&   aP3d2 = aP3d[j];

          const gp_Pnt2d& aP2d1 = aP2d[i];
          const gp_Pnt2d& aP2d2 = aP2d[j];

          const gp_Pnt   aMid3d_l = (aP3d1.XYZ() + aP3d2.XYZ()) / 2.;
          const gp_Pnt2d aMid2d_l = (aP2d1.XY () + aP2d2.XY ()) / 2.;

          aSqDeflection = Max (aSqDeflection, aTool.Eval (aMid2d_l, aMid3d_l));
        }
      }
    }

    aPT->Deflection (Sqrt (aSqDeflection));
  }
}

//=======================================================================
//function : SortFaces
//purpose  : 
//=======================================================================

void  BRepLib::SortFaces (const TopoDS_Shape& Sh,
  TopTools_ListOfShape& LF)
{
  LF.Clear();
  TopTools_ListOfShape LTri,LPlan,LCyl,LCon,LSphere,LTor,LOther;
  TopExp_Explorer exp(Sh,TopAbs_FACE);
  TopLoc_Location l;
  Handle(Geom_Surface) S;

  for (; exp.More(); exp.Next()) {
    const TopoDS_Face&   F = TopoDS::Face(exp.Current());
    S = BRep_Tool::Surface(F, l);
    if (!S.IsNull()) {
      if (S->DynamicType() == STANDARD_TYPE(Geom_RectangularTrimmedSurface)) {
        S = Handle(Geom_RectangularTrimmedSurface)::DownCast (S)->BasisSurface();
      }
      GeomAdaptor_Surface AS(S);
      switch (AS.GetType()) {
      case GeomAbs_Plane: 
        {
          LPlan.Append(F);
          break;
        }
      case GeomAbs_Cylinder: 
        {
          LCyl.Append(F);
          break;
        }
      case GeomAbs_Cone: 
        {
          LCon.Append(F);
          break;
        }
      case GeomAbs_Sphere: 
        {
          LSphere.Append(F);
          break;
        }
      case GeomAbs_Torus: 
        {
          LTor.Append(F);
          break;
        }
      default:
        LOther.Append(F);
      }
    }
    else LTri.Append(F);
  }
  LF.Append(LPlan); LF.Append(LCyl  ); LF.Append(LCon); LF.Append(LSphere);
  LF.Append(LTor ); LF.Append(LOther); LF.Append(LTri); 
}

//=======================================================================
//function : ReverseSortFaces
//purpose  : 
//=======================================================================

void  BRepLib::ReverseSortFaces (const TopoDS_Shape& Sh,
  TopTools_ListOfShape& LF)
{
  LF.Clear();
  // Use the allocator of the result LF for intermediate results
  TopTools_ListOfShape LTri(LF.Allocator()), LPlan(LF.Allocator()),
    LCyl(LF.Allocator()), LCon(LF.Allocator()), LSphere(LF.Allocator()),
    LTor(LF.Allocator()), LOther(LF.Allocator());
  TopExp_Explorer exp(Sh,TopAbs_FACE);
  TopLoc_Location l;

  for (; exp.More(); exp.Next()) {
    const TopoDS_Face&   F = TopoDS::Face(exp.Current());
    const Handle(Geom_Surface)& S = BRep_Tool::Surface(F, l);
    if (!S.IsNull()) {
      GeomAdaptor_Surface AS(S);
      switch (AS.GetType()) {
      case GeomAbs_Plane: 
        {
          LPlan.Append(F);
          break;
        }
      case GeomAbs_Cylinder: 
        {
          LCyl.Append(F);
          break;
        }
      case GeomAbs_Cone: 
        {
          LCon.Append(F);
          break;
        }
      case GeomAbs_Sphere: 
        {
          LSphere.Append(F);
          break;
        }
      case GeomAbs_Torus: 
        {
          LTor.Append(F);
          break;
        }
      default:
        LOther.Append(F);
      }
    }
    else LTri.Append(F);
  }
  LF.Append(LTri); LF.Append(LOther); LF.Append(LTor ); LF.Append(LSphere);
  LF.Append(LCon); LF.Append(LCyl  ); LF.Append(LPlan);

}

//=======================================================================
// function: BoundingVertex
// purpose : 
//=======================================================================
void BRepLib::BoundingVertex(const NCollection_List<TopoDS_Shape>& theLV,
                             gp_Pnt& theNewCenter, Standard_Real& theNewTol)
{
  Standard_Integer aNb;
  //
  aNb=theLV.Extent();
  if (aNb < 2) {
    return;
  }
  //
  else if (aNb==2) {
    Standard_Integer m, n;
    Standard_Real aR[2], dR, aD, aEps;
    TopoDS_Vertex aV[2];
    gp_Pnt aP[2];
    //
    aEps=RealEpsilon();
    for (m=0; m<aNb; ++m) {
      aV[m]=(!m)? 
        *((TopoDS_Vertex*)(&theLV.First())):
        *((TopoDS_Vertex*)(&theLV.Last()));
      aP[m]=BRep_Tool::Pnt(aV[m]);
      aR[m]=BRep_Tool::Tolerance(aV[m]);
    }  
    //
    m=0; // max R
    n=1; // min R
    if (aR[0]<aR[1]) {
      m=1;
      n=0;
    }
    //
    dR=aR[m]-aR[n]; // dR >= 0.
    gp_Vec aVD(aP[m], aP[n]);
    aD=aVD.Magnitude();
    //
    if (aD<=dR || aD<aEps) { 
      theNewCenter = aP[m];
      theNewTol = aR[m];
    }
    else {
      Standard_Real aRr;
      gp_XYZ aXYZr;
      gp_Pnt aPr;
      //
      aRr=0.5*(aR[m]+aR[n]+aD);
      aXYZr=0.5*(aP[m].XYZ()+aP[n].XYZ()-aVD.XYZ()*(dR/aD));
      aPr.SetXYZ(aXYZr);
      //
      theNewCenter = aPr;
      theNewTol = aRr;
      //aBB.MakeVertex (aVnew, aPr, aRr);
    }
    return;
  }// else if (aNb==2) {
  //
  else { // if (aNb>2)
    // compute the point
    //
    // issue 0027540 - sum of doubles may depend on the order
    // of addition, thus sort the coordinates for stable result
    Standard_Integer i;
    NCollection_Array1<gp_Pnt> aPoints(0, aNb-1);
    NCollection_List<TopoDS_Shape>::Iterator aIt(theLV);
    for (i = 0; aIt.More(); aIt.Next(), ++i) {
      const TopoDS_Vertex& aVi = *((TopoDS_Vertex*)(&aIt.Value()));
      gp_Pnt aPi = BRep_Tool::Pnt(aVi);
      aPoints(i) = aPi;
    }
    //
    std::sort(aPoints.begin(), aPoints.end(), BRepLib_ComparePoints());
    //
    gp_XYZ aXYZ(0., 0., 0.);
    for (i = 0; i < aNb; ++i) {
      aXYZ += aPoints(i).XYZ();
    }
    aXYZ.Divide((Standard_Real)aNb);
    //
    gp_Pnt aP(aXYZ);
    //
    // compute the tolerance for the new vertex
    Standard_Real aTi, aDi, aDmax;
    //
    aDmax=-1.;
    aIt.Initialize(theLV);
    for (; aIt.More(); aIt.Next()) {
      TopoDS_Vertex& aVi=*((TopoDS_Vertex*)(&aIt.Value()));
      gp_Pnt aPi=BRep_Tool::Pnt(aVi);
      aTi=BRep_Tool::Tolerance(aVi);
      aDi=aP.SquareDistance(aPi);
      aDi=sqrt(aDi);
      aDi=aDi+aTi;
      if (aDi > aDmax) {
        aDmax=aDi;
      }
    }
    //
    theNewCenter = aP;
    theNewTol = aDmax;
  }
}

//=======================================================================
//function : ExtendFace
//purpose  :
//=======================================================================
void BRepLib::ExtendFace(const TopoDS_Face& theF,
                         const Standard_Real theExtVal,
                         const Standard_Boolean theExtUMin,
                         const Standard_Boolean theExtUMax,
                         const Standard_Boolean theExtVMin,
                         const Standard_Boolean theExtVMax,
                         TopoDS_Face& theFExtended)
{
  // Get face bounds
  BRepAdaptor_Surface aBAS(theF);
  Standard_Real aFUMin = aBAS.FirstUParameter(),
                aFUMax = aBAS.LastUParameter(),
                aFVMin = aBAS.FirstVParameter(),
                aFVMax = aBAS.LastVParameter();
  const Standard_Real aTol = BRep_Tool::Tolerance(theF);

  // Surface to build the face
  Handle(Geom_Surface) aS;

  const GeomAbs_SurfaceType aType = aBAS.GetType();
  // treat analytical surfaces first
  if (aType == GeomAbs_Plane ||
      aType == GeomAbs_Sphere ||
      aType == GeomAbs_Cylinder ||
      aType == GeomAbs_Torus ||
      aType == GeomAbs_Cone)
  {
    // Get basis transformed basis surface
    Handle(Geom_Surface) aSurf = Handle(Geom_Surface)::
      DownCast(aBAS.Surface().Surface()->Transformed(aBAS.Trsf()));

    // Get bounds of the basis surface
    Standard_Real aSUMin, aSUMax, aSVMin, aSVMax;
    aSurf->Bounds(aSUMin, aSUMax, aSVMin, aSVMax);

    Standard_Boolean isUPeriodic = aBAS.IsUPeriodic();
    Standard_Real anUPeriod = isUPeriodic ? aBAS.UPeriod() : 0.0;
    if (isUPeriodic)
    {
      // Adjust face bounds to first period
      Standard_Real aDelta = aFUMax - aFUMin;
      aFUMin = Max(aSUMin, aFUMin + anUPeriod*Ceiling((aSUMin - aFUMin) / anUPeriod));
      aFUMax = aFUMin + aDelta;
    }

    Standard_Boolean isVPeriodic = aBAS.IsVPeriodic();
    Standard_Real aVPeriod = isVPeriodic ? aBAS.VPeriod() : 0.0;
    if (isVPeriodic)
    {
      // Adjust face bounds to first period
      Standard_Real aDelta = aFVMax - aFVMin;
      aFVMin = Max(aSVMin, aFVMin + aVPeriod*Ceiling((aSVMin - aFVMin) / aVPeriod));
      aFVMax = aFVMin + aDelta;
    }

    // Enlarge the face
    Standard_Real anURes = 0., aVRes = 0.;
    if (theExtUMin || theExtUMax)
      anURes = aBAS.UResolution(theExtVal);
    if (theExtVMin || theExtVMax)
      aVRes = aBAS.VResolution(theExtVal);

    if (theExtUMin) aFUMin = Max(aSUMin, aFUMin - anURes);
    if (theExtUMax) aFUMax = Min(isUPeriodic ? aFUMin + anUPeriod : aSUMax, aFUMax + anURes);
    if (theExtVMin) aFVMin = Max(aSVMin, aFVMin - aVRes);
    if (theExtVMax) aFVMax = Min(isVPeriodic ? aFVMin + aVPeriod : aSVMax, aFVMax + aVRes);

    // Check if the periodic surface should become closed.
    // In this case, use the basis surface with basis bounds.
    const Standard_Real anEps = Precision::PConfusion();
    if (isUPeriodic && Abs(aFUMax - aFUMin - anUPeriod) < anEps)
    {
      aFUMin = aSUMin;
      aFUMax = aSUMax;
    }
    if (isVPeriodic && Abs(aFVMax - aFVMin - aVPeriod) < anEps)
    {
      aFVMin = aSVMin;
      aFVMax = aSVMax;
    }

    aS = aSurf;
  }
  else
  {
    // General case

    Handle(Geom_BoundedSurface) aSB =
      Handle(Geom_BoundedSurface)::DownCast(BRep_Tool::Surface(theF));
    if (aSB.IsNull())
    {
      theFExtended = theF;
      return;
    }

    // Get surfaces bounds
    Standard_Real aSUMin, aSUMax, aSVMin, aSVMax;
    aSB->Bounds(aSUMin, aSUMax, aSVMin, aSVMax);

    Standard_Boolean isUClosed = aSB->IsUClosed();
    Standard_Boolean isVClosed = aSB->IsVClosed();

    // Check if the extension in necessary directions is done
    Standard_Boolean isExtUMin = Standard_False,
                     isExtUMax = Standard_False,
                     isExtVMin = Standard_False,
                     isExtVMax = Standard_False;

    // UMin
    if (theExtUMin && !isUClosed && !Precision::IsInfinite(aSUMin)) {
      GeomLib::ExtendSurfByLength(aSB, theExtVal, 1, Standard_True, Standard_False);
      isExtUMin = Standard_True;
    }
    // UMax
    if (theExtUMax && !isUClosed && !Precision::IsInfinite(aSUMax)) {
      GeomLib::ExtendSurfByLength(aSB, theExtVal, 1, Standard_True, Standard_True);
      isExtUMax = Standard_True;
    }
    // VMin
    if (theExtVMin && !isVClosed && !Precision::IsInfinite(aSVMax)) {
      GeomLib::ExtendSurfByLength(aSB, theExtVal, 1, Standard_False, Standard_False);
      isExtVMin = Standard_True;
    }
    // VMax
    if (theExtVMax && !isVClosed && !Precision::IsInfinite(aSVMax)) {
      GeomLib::ExtendSurfByLength(aSB, theExtVal, 1, Standard_False, Standard_True);
      isExtVMax = Standard_True;
    }

    aS = aSB;

    // Get new bounds
    aS->Bounds(aSUMin, aSUMax, aSVMin, aSVMax);
    if (isExtUMin) aFUMin = aSUMin;
    if (isExtUMax) aFUMax = aSUMax;
    if (isExtVMin) aFVMin = aSVMin;
    if (isExtVMax) aFVMax = aSVMax;
  }

  BRepLib_MakeFace aMF(aS, aFUMin, aFUMax, aFVMin, aFVMax, aTol);
  theFExtended = *(TopoDS_Face*)&aMF.Shape();
  if (theF.Orientation() == TopAbs_REVERSED)
    theFExtended.Reverse();
}