// File:	BRepLib.cxx
// Created:	Wed Dec 15 17:53:58 1993
// Author:	Remi LEQUETTE
//		<rle@zerox>

// History: pmn 26/09/97 Ajout des parametres d'approx dans BuildCurve3d
//  Modified by skv - Thu Jun  3 12:39:19 2004 OCC5898

#include <BRepLib.ixx>
#include <BRepAdaptor_Surface.hxx>
#include <BRepAdaptor_HSurface.hxx>
#include <BRepAdaptor_HCurve2d.hxx>
#include <BRep_Tool.hxx>
#include <BRep_Builder.hxx>
#include <Geom_Surface.hxx>
#include <Geom_RectangularTrimmedSurface.hxx>
#include <Geom_Plane.hxx>
#include <Geom_Curve.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <Geom2d_Curve.hxx>
#include <Geom2d_TrimmedCurve.hxx>
#include <Geom2d_BSplineCurve.hxx>
#include <GeomLib.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopTools_MapOfShape.hxx>
#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
#include <TopTools_ListIteratorOfListOfShape.hxx>
#include <TopExp.hxx>
#include <gp.hxx>
#include <gp_Ax2.hxx>
#include <gp_Pln.hxx>
#include <Standard_Real.hxx>
#include <Precision.hxx>
#include <BRep_GCurve.hxx>
#include <BRep_TEdge.hxx>
#include <BRep_TFace.hxx>
#include <AppParCurves_MultiCurve.hxx>
#include <AppParCurves_MultiBSpCurve.hxx>
#include <BRep_ListOfCurveRepresentation.hxx>
#include <BRep_CurveRepresentation.hxx>
#include <BRep_ListIteratorOfListOfCurveRepresentation.hxx>
#include <BRep_TVertex.hxx>
#include <AdvApprox_ApproxAFunction.hxx>
#include <Approx_SameParameter.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <TColgp_Array1OfPnt2d.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <TColStd_MapOfTransient.hxx>
#include <GeomAdaptor_Curve.hxx>
#include <GeomAdaptor_HCurve.hxx>
#include <GeomAdaptor_Surface.hxx>
#include <GeomAdaptor_HSurface.hxx>
#include <Geom2dAdaptor_Curve.hxx>
#include <Geom2dAdaptor_HCurve.hxx>
#include <Geom2dAdaptor.hxx>
#include <Geom2dConvert.hxx>
#include <GCPnts_QuasiUniformDeflection.hxx>
#include <BSplCLib.hxx>
#include <ElSLib.hxx>
#include <Adaptor3d_CurveOnSurface.hxx>
#include <Extrema_LocateExtPC.hxx>
#include <ProjLib_ProjectedCurve.hxx>
#include <BRepClass3d_SolidClassifier.hxx>
#include <Bnd_Box.hxx>
#include <BRepBndLib.hxx>
#include <Approx_CurvilinearParameter.hxx>
#include <Geom_BSplineSurface.hxx>

//
//  comme on ne pas patcher en cdl GEOMLITE temporairement les GeomLib_ migrent
//  dans BRepLib ...
//

static Standard_Real thePrecision = Precision::Confusion();     
static Handle(Geom_Plane) thePlane;

//=======================================================================
//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;
#ifndef DEB
  Standard_Real current_first =0., current_last =0. ;
#else
  Standard_Real current_first,current_last ;
#endif
  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;

  Handle(Adaptor3d_HSurface) aSurf   = aCurveOnSurface.GetSurface();
  Handle(Adaptor2d_HCurve2d) 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);

    // update the edge
    Standard_Real First, Last;

    BRep_Builder B;
    Standard_Boolean is_closed ;
     is_closed = AnEdge.Closed() ;
    
    B.UpdateEdge(AnEdge,C3d,LocalLoc,0.0e0);
    BRep_Tool::Range(AnEdge, S, LC, First, Last);
    B.Range(AnEdge, First, Last); //Ne pas oublier le range du 3d.(PRO6412)
    TopoDS_Edge E = AnEdge ;
    E.Closed(is_closed) ;

  }
  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_HCurve) AnAdaptor3dCurve2dPtr =
	new Geom2dAdaptor_HCurve(AnAdaptor3dCurve2d) ;
      Handle(GeomAdaptor_HSurface) AnAdaptor3dSurfacePtr =
	new GeomAdaptor_HSurface (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 );

      Standard_Boolean is_closed ;
      is_closed = AnEdge.Closed() ;
      B.UpdateEdge(TopoDS::Edge(AnEdge),
		   NewCurvePtr,
		   L[0],
		   max_deviation) ;
      TopoDS_Edge  E = AnEdge ;
      E.Closed(is_closed) ;
      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_HCurve) AnAdaptor3dCurve2dPtr =
	new Geom2dAdaptor_HCurve(AnAdaptor3dCurve2d) ;
      Handle(GeomAdaptor_HSurface) AnAdaptor3dSurfacePtr =
	new GeomAdaptor_HSurface (AnAdaptor3dSurface) ;
      curve_on_surface_reference.Load( AnAdaptor3dCurve2dPtr) ;
      curve_on_surface_reference.Load( 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_HCurve) an_adaptor_curve2d_ptr =
	    new Geom2dAdaptor_HCurve(an_adaptor_curve2d) ;
	  Handle(GeomAdaptor_HSurface) an_adaptor_surface_ptr =
	    new GeomAdaptor_HSurface (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 : SetEdgeTol
//purpose  : 
//=======================================================================

static void SetEdgeTol(const TopoDS_Edge& E,
		       const TopoDS_Face& F)
{
  TopLoc_Location L;
  const Handle(Geom_Surface)& S = BRep_Tool::Surface(F,L);
  TopLoc_Location l = L.Predivided(E.Location());

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

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

  static Handle(GeomAdaptor_HCurve) HC;
  static Handle(GeomAdaptor_HSurface) HS;
  if (HC.IsNull()) {
    HC = new GeomAdaptor_HCurve();
    HS = new GeomAdaptor_HSurface();
  }
  
  TopLoc_Location LC;
  Standard_Real First, Last;
  GeomAdaptor_Curve& GAC = HC->ChangeCurve();
  GAC.Load(BRep_Tool::Curve(E,LC,First,Last));
  LC = L.Predivided(LC);

  if (!LC.IsIdentity()) {
    GP = Handle(Geom_Plane)::DownCast(
	 GP->Transformed(LC.Transformation()));
  }
  GeomAdaptor_Surface& GAS = HS->ChangeSurface();
  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 temp = Pc3d.SquareDistance(Pcons);
    if(temp > d2) d2 = temp;
  }
  d2 = 1.5*sqrt(d2);
  TE->UpdateTolerance(d2);
}

//=======================================================================
//function : SameParameter
//purpose  : 
//=======================================================================
void  BRepLib::SameParameter(const TopoDS_Shape& S,
			     const Standard_Real Tolerance,
			     const Standard_Boolean forced) 
{
  TopExp_Explorer ex(S,TopAbs_EDGE);
  TopTools_MapOfShape  Done;
  BRep_Builder brB;

  while (ex.More()) {
    if (Done.Add(ex.Current())) {
      if (forced) {
	brB.SameRange(TopoDS::Edge(ex.Current()), Standard_False);
	brB.SameParameter(TopoDS::Edge(ex.Current()), Standard_False);
      }
      BRepLib::SameParameter(TopoDS::Edge(ex.Current()),Tolerance);
    }
    ex.Next();
  }

  Done.Clear();
  BRepAdaptor_Surface BS;
  for(ex.Init(S,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());
      SetEdgeTol(E,curface);
    }
  }
  BRepLib::UpdateTolerances(S);
}

//=======================================================================
//function : SameParameter
//WARNING  : Nouvelle spec DUB LBO 9/9/97.
//  On recode dans l arete la meilleure tolerance trouvee, 
//  pour les vertex extremites il faudra trouver autre chose.
//=======================================================================
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 dist = Projector.Value();
      if(dist > tolbail) tolbail = dist;
      ok++;
    }
  }
  return (ok > 2);
}

static Standard_Real ComputeTol(const Handle(Adaptor3d_HCurve)& c3d,
				const Handle(Adaptor2d_HCurve2d)& c2d,
				const Handle(Adaptor3d_HSurface)& surf,
				const Standard_Integer        nbp)

{

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


  Adaptor3d_CurveOnSurface  cons(c2d,surf);
  Standard_Real d2 = 0.;
  Standard_Integer nn = nbp;
  Standard_Real unsurnn = 1./nn;
  Standard_Real first = c3d->FirstParameter();
  Standard_Real last  = c3d->LastParameter();
  Standard_Integer i = 0;
  for(i = 0; i <= nn; i++){
    Standard_Real t = unsurnn*i;
    Standard_Real u = first*(1.-t) + last*t;
    gp_Pnt Pc3d = c3d->Value(u);
    gp_Pnt Pcons = cons.Value(u);
    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;


    if(temp > d2) d2 = temp;
  }


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

  for( 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( i = 1; i<= nbp+10; i++)
      if( dist(i) > 0 && dist(i) < 1.0 )
	if( dist(i) > D2 ) D2 = dist(i);
  }

  //d2 = 1.5*sqrt(d2);
  d2 = (!ana) ? 1.5*sqrt(d2) : 1.5*sqrt(D2);
  if(d2<1.e-7) d2 = 1.e-7;

  return d2;
}



void BRepLib::SameParameter(const TopoDS_Edge&  AnEdge,
			    const Standard_Real Tolerance)
{
  if (BRep_Tool::SameParameter(AnEdge)) return;

  const Standard_Integer NCONTROL = 22;

  static Handle(GeomAdaptor_HCurve) HC;
  static Handle(Geom2dAdaptor_HCurve) HC2d;
  static Handle(GeomAdaptor_HSurface) HS;
  if(HC.IsNull()){
    HC = new GeomAdaptor_HCurve();
    HC2d = new Geom2dAdaptor_HCurve();
    HS = new GeomAdaptor_HSurface();
  }
  GeomAdaptor_Curve& GAC = HC->ChangeCurve();
  Geom2dAdaptor_Curve& GAC2d = HC2d->ChangeCurve2d();
  GeomAdaptor_Surface& GAS = HS->ChangeSurface();
#ifndef DEB
  Standard_Real f3d =0.,l3d =0.;
#else
  Standard_Real f3d,l3d;
#endif
  TopLoc_Location L3d;
  Handle(Geom_Curve) C3d;

  const Handle(BRep_TEdge)& TE = *((Handle(BRep_TEdge)*) &AnEdge.TShape());
  BRep_ListOfCurveRepresentation& CList = TE->ChangeCurves();
  BRep_ListIteratorOfListOfCurveRepresentation It(CList);
  
  Standard_Boolean NotDone = Standard_True;

  while (NotDone && It.More()) {
    Handle(BRep_GCurve) GCurve = Handle(BRep_GCurve)::DownCast(It.Value());
    if (!GCurve.IsNull() && GCurve->IsCurve3D()) {
      C3d = GCurve->Curve3D() ;
      f3d = GCurve->First();
      l3d = GCurve->Last();
      L3d = GCurve->Location() ;
      NotDone = Standard_False;
    } 
    It.Next() ;
  }

  if(C3d.IsNull()) return;

  // 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))
    {
      const Handle(Geom_Curve)& gtC = (*((Handle(Geom_TrimmedCurve)*)&C3d))->BasisCurve();
      m_TrimmedPeriodical = gtC->IsPeriodic();
    }
  // modified by NIZHNY-OCC486  Tue Aug 27 17:15:17 2002 .

  BRep_Builder B;
  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_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(AnEdge);
//  Modified by skv - Thu Jun  3 12:39:20 2004 OCC5898 End
  Standard_Boolean SameRange = BRep_Tool::SameRange(AnEdge);
  Standard_Boolean YaPCu = Standard_False;
  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 UResol = GAS.UResolution(Tolerance);
      Standard_Real VResol = GAS.VResolution(Tolerance);
      Standard_Real Tol2d  = Min(UResol, VResol);
      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(Tol2d,
			     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(GAC2d.GetType() == GeomAbs_BSplineCurve && 
	   GAC2d.Continuity() == GeomAbs_C0) {
	  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, Tol2d);
	  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,Tolerance,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),Tol2d);

	      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,Tolerance)) {
	      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,Tolerance), 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,Tolerance),
						    cont, maxdeg, 10);
	      if(AppCurPar.IsDone() || AppCurPar.HasResult()) {
		bs2d = AppCurPar.Curve2d1();
		GAC2d.Load(bs2d,f3d,l3d);
		curPC = bs2d;

		if(Abs(bs2d->FirstParameter() - fC0) > Tol2d ||
		   Abs(bs2d->LastParameter() - lC0) > Tol2d    ) {
		  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 : Tolerance;
	  Approx_SameParameter SameP(HC,HC2d,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 IsSameP = 0;
	
	}
	else IsSameP = 0;

//  Modified by skv - Thu Jun  3 12:39:19 2004 OCC5898 Begin
	if (!IsSameP) {
	  if (anEdgeTol > error) {
	    maxdist = Max(maxdist, anEdgeTol);
	    IsSameP = Standard_True;
	  }
	}
//  Modified by skv - Thu Jun  3 12:39:20 2004 OCC5898 End
      }
    }
    It.Next() ;
  }
  B.Range(AnEdge,f3d,l3d);
  B.SameRange(AnEdge,Standard_True);
  if ( IsSameP) {
    // On diminue eventuellement la tolerance de l arete, puisque
    // l on a traite toutes ses representations ( Sauf celles associees
    // a des plans et non stockees dans l'arete !) 
    // Il n'en va pas de meme des Vertex que l on ne peut que grossir 
    // ou laisser tels quels.
    if (YaPCu) {
      // On evite de mettre des tol trop petites.
      maxdist = Max(maxdist,Precision::Confusion());
      TopoDS_Vertex V1,V2;
      TopExp::Vertices(AnEdge,V1,V2);
      if (!V1.IsNull())
	B.UpdateVertex(V1,maxdist);
      if (!V2.IsNull())
	B.UpdateVertex(V2,maxdist);
      TE->Modified(Standard_True);
      TE->Tolerance(maxdist);
    }
    B.SameParameter(AnEdge,Standard_True);
  }
}

//=======================================================================
//function : UpdateTolerances
//purpose  : 
//=======================================================================
void  BRepLib::UpdateTolerances(const TopoDS_Shape& aShape,
				const Standard_Boolean verifyTolerance) 
{

// On harmonise les tolerance
// avec la regle Tolerance(VERTEX)>=Tolerance(EDGE)>=Tolerance(FACE)
  BRep_Builder B;
  Standard_Real tol=0;
  if (verifyTolerance) {
    // On force la tolerance a sa valeur minimale
    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(aShape, 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)*)&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;
	  // On ne traite pas les tolerance > 1.
	  if (tol>1.) tol=0.99;
	}
	const Handle(BRep_TFace)& Tf = *((Handle(BRep_TFace)*)&curf.TShape());
	Tf->Tolerance(tol);
      }
    }
  }
  
  //On traite les edges
  TopTools_IndexedDataMapOfShapeListOfShape parents;
  TopExp::MapShapesAndAncestors(aShape, 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()) {
      tol=Max(tol, BRep_Tool::Tolerance(TopoDS::Face(lConx.Value())));
    }
    // Update ne peut que augmenter la tolerance, donc si l'edge a
    // une tolerance + grande que ses faces on y touche pas
    B.UpdateEdge(TopoDS::Edge(parents.FindKey(iCur)), tol);
  }

  //On traite les Vertices
  parents.Clear();
  TopExp::MapShapesAndAncestors(aShape, TopAbs_VERTEX, TopAbs_EDGE, parents);
  TColStd_MapOfTransient Initialized;
  TopTools_MapOfShape Done;
  Standard_Integer nbV = parents.Extent();
  for (iCur=1; iCur<=nbV; iCur++) {
    tol=0;
    Done.Clear();
    const TopoDS_Vertex& V = TopoDS::Vertex(parents.FindKey(iCur));
    Bnd_Box box;
    box.Add(BRep_Tool::Pnt(V));
    gp_Pnt p3d;
    for (lConx.Initialize(parents(iCur)); lConx.More(); lConx.Next()) {
      const TopoDS_Edge& E = TopoDS::Edge(lConx.Value());
      if(!Done.Add(E)) continue;
      tol=Max(tol, BRep_Tool::Tolerance(E));
      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()) {
	// Pour chaque CurveRepresentation, on verifie le parametre fourni
	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 degenere
	    p3d = C->Value(par);
	    p3d.Transform(L.Transformation());
	    box.Add(p3d);
	  }
	}
	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());
	  box.Add(p3d);
	  if (!PC2.IsNull()) {
	    p2d = PC2->Value(par);
	    p3d = Su->Value(p2d.X(),p2d.Y());
	    p3d.Transform(L.Transformation());
	    box.Add(p3d);
	  }
	}
	itcr.Next();
      }
    }
    Standard_Real aXmin, aYmin, aZmin, aXmax, aYmax, aZmax;
    box.Get(aXmin, aYmin, aZmin, aXmax, aYmax, aZmax);
    aXmax -= aXmin; aYmax -= aYmin; aZmax -= aZmin;
    tol = Max(tol,sqrt(aXmax*aXmax+aYmax*aYmax+aZmax*aZmax));
    tol += 2.*Epsilon(tol);
    if (verifyTolerance) {
      // On force la tolerance a sa valeur minimale
      // Attention au partage du vertex par d'autre shapes
      const Handle(BRep_TVertex)& TV = *((Handle(BRep_TVertex)*)&V.TShape());
      if (Initialized.Add(TV)) 
	TV->Tolerance(tol);
      else 
	B.UpdateVertex(V, tol);
    }
    else {
      // Update ne peut que augmenter la tolerance, donc si le vertex a
      // une tolerance + grande que ses edges on y touche pas
      B.UpdateVertex(V, tol);
    }
  }
}

//=======================================================================
//function : OrientClosedSolid
//purpose  : 
//=======================================================================
Standard_Boolean BRepLib::OrientClosedSolid(TopoDS_Solid& solid) 
{
// On met la matiere a l'interieur du 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;
}

//=======================================================================
//function : tgtfaces
//purpose  : controle de l angle a la frontiere entre 2 carreaux.
//           Les deux carreaux doivent partager leur edge frontiere.
//=======================================================================

static Standard_Boolean tgtfaces(const TopoDS_Edge& Ed,
				 const TopoDS_Face& F1,
				 const TopoDS_Face& F2,
				 const Standard_Real ta,
				 const Standard_Boolean couture)
{
  Standard_Real u;
  TopoDS_Edge E = Ed;
  BRepAdaptor_Surface aBAS1(F1,Standard_False);
  BRepAdaptor_Surface aBAS2(F2,Standard_False);
  Handle(BRepAdaptor_HSurface) HS1 = new BRepAdaptor_HSurface(aBAS1);
  Handle(BRepAdaptor_HSurface) HS2;
  if(couture) HS2 = HS1;
  else HS2 = new BRepAdaptor_HSurface(aBAS2);

  E.Orientation(TopAbs_FORWARD);
  Handle(BRepAdaptor_HCurve2d) HC2d1 = new BRepAdaptor_HCurve2d();
  HC2d1->ChangeCurve2d().Initialize(E,F1);
  if(couture) E.Orientation(TopAbs_REVERSED);
  Handle(BRepAdaptor_HCurve2d) HC2d2 = new BRepAdaptor_HCurve2d();
  HC2d2->ChangeCurve2d().Initialize(E,F2);
  Adaptor3d_CurveOnSurface C1(HC2d1,HS1);
  Adaptor3d_CurveOnSurface C2(HC2d2,HS2);

  Standard_Boolean rev1 = (F1.Orientation() == TopAbs_REVERSED);
  Standard_Boolean rev2 = (F2.Orientation() == TopAbs_REVERSED);
  Standard_Real f,l,eps, angmax = -PI;
#ifndef DEB
  Standard_Real ang =0.;
#else
  Standard_Real ang;
#endif
  BRep_Tool::Range(E,f,l);
  Extrema_LocateExtPC ext;
  Standard_Boolean IsInitialized = Standard_False;
  
  eps = (l - f)/100.;
  f += eps; // pour eviter de faire des calculs sur les 
  l -= eps; // pointes des carreaux pointus.
  gp_Pnt2d p;
  gp_Pnt pp1,pp2;//,PP;
  gp_Vec du,dv;
  gp_Vec d1,d2;
  Standard_Real uu, vv, norm;

  Standard_Integer i;
  Standard_Boolean Nok;
  for(i = 0; (i<= 20) && (angmax<=ta) ; i++){
    // On suppose d'abord que c'est sameParameter
    Nok = Standard_True;
    u = f + (l-f)*i/20;
    HC2d1->D0(u,p);
    HS1->D1(p.X(),p.Y(),pp1,du,dv);
    d1 = (du.Crossed(dv));
    norm = d1.Magnitude(); 
    if (norm > 1.e-12) d1 /= norm;
    else Nok=Standard_False;
    if(rev1) d1.Reverse();

    HC2d2->D0(u,p);
    HS2->D1(p.X(), p.Y(), pp2, du, dv);
    d2 = (du.Crossed(dv));
    norm = d2.Magnitude();
    if (norm> 1.e-12) d2 /= norm;
    else Nok=Standard_False;
    if(rev2) d2.Reverse();
    if (Nok) ang = d1.Angle(d2);

    if (Nok &&(ang > ta)) { // On raffine par projection
      if (! IsInitialized ) {
	ext.Initialize(C2,f,l,Precision::PConfusion());
	IsInitialized = Standard_True;
      }      
      ext.Perform(pp1,u);
      if(ext.IsDone() && ext.IsMin()){
	Extrema_POnCurv poc = ext.Point();
	Standard_Real v = poc.Parameter();

	HC2d2->D0(v,p);
	p.Coord(uu,vv);
	HS2->D1(p.X(), p.Y(), pp2, du, dv);
	d2 = (du.Crossed(dv));
	norm = d2.Magnitude();
	if (norm> 1.e-12) d2 /= norm;
	else Nok = Standard_False;
	if(rev2) d2.Reverse();
	if (Nok) ang = d1.Angle(d2);
      }
    }
    if(ang >= angmax) angmax = ang;
  }     
 
  return (angmax<=ta);

}


//=======================================================================
// function : EncodeRegularity
// purpose  : code les regularites sur tous les edges du shape,frontiere
//            de deux faces qui n en ont pas.
//=======================================================================

void BRepLib::EncodeRegularity(const TopoDS_Shape& S,
			       const Standard_Real TolAng)
{
  BRep_Builder B;
  TopTools_IndexedDataMapOfShapeListOfShape M;
  TopExp::MapShapesAndAncestors(S,TopAbs_EDGE,TopAbs_FACE,M);
  TopTools_ListIteratorOfListOfShape It;
  TopExp_Explorer Ex;
  TopoDS_Face F1,F2;
  Standard_Boolean found, couture;
  for(Standard_Integer i = 1; i <= M.Extent(); i++){
    TopoDS_Edge E = TopoDS::Edge(M.FindKey(i));
    found = Standard_False; couture = Standard_False;
    F1.Nullify();
    for(It.Initialize(M.FindFromIndex(i));It.More() && !found;It.Next()){
      if(F1.IsNull()) { F1 = TopoDS::Face(It.Value()); }
      else {
	if(!F1.IsSame(TopoDS::Face(It.Value()))){
	  found = Standard_True;
	  F2 = TopoDS::Face(It.Value());
	}
      }
    }
    if (!found && !F1.IsNull()){//est ce un edge de couture?
      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;
	  couture = Standard_True;
	  F2 = F1;
	}
      }
    }
    if(found){
      if(BRep_Tool::Continuity(E,F1,F2)<=GeomAbs_C0){
	if(tgtfaces(E, F1, F2, TolAng, couture)){
	  B.Continuity(E,F1,F2,GeomAbs_G1);
	}
      }
    }
  }
}

//=======================================================================
// function : EncodeRegularity
// purpose  : code la regularite entre 2 face sur une 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){
    if( tgtfaces(E, F1, F2, TolAng, F1.IsEqual(F2))) {
     B.Continuity(E,F1,F2,GeomAbs_G1);
   }
 }
}

//=======================================================================
//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)*)&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();
  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)*)&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(LTri); LF.Append(LOther); LF.Append(LTor ); LF.Append(LSphere);
  LF.Append(LCon); LF.Append(LCyl  ); LF.Append(LPlan);
   
}


