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

#include <IntTools_EdgeFace.hxx>

#include <BRep_Tool.hxx>
#include <BRepAdaptor_Surface.hxx>
#include <Geom_Curve.hxx>
#include <Geom_Surface.hxx>
#include <GeomAdaptor_Curve.hxx>
#include <GeomAdaptor_Surface.hxx>
#include <gp_Ax1.hxx>
#include <gp_Circ.hxx>
#include <gp_Cone.hxx>
#include <gp_Cylinder.hxx>
#include <gp_Lin.hxx>
#include <gp_Pln.hxx>
#include <gp_Pnt.hxx>
#include <gp_Torus.hxx>
#include <IntCurveSurface_HInter.hxx>
#include <IntCurveSurface_IntersectionPoint.hxx>
#include <IntTools.hxx>
#include <IntTools_BeanFaceIntersector.hxx>
#include <IntTools_CommonPrt.hxx>
#include <IntTools_Context.hxx>
#include <IntTools_FClass2d.hxx>
#include <IntTools_Range.hxx>
#include <IntTools_Tools.hxx>
#include <Precision.hxx>

#include <algorithm>
static
  Standard_Boolean IsCoplanar (const BRepAdaptor_Curve&  ,
                               const BRepAdaptor_Surface& );
static
  Standard_Boolean IsRadius (const BRepAdaptor_Curve& aCurve ,
                             const BRepAdaptor_Surface& aSurface,
                             const Standard_Real aCriteria);

//=======================================================================
//function : IntTools_EdgeFace::IntTools_EdgeFace
//purpose  : 
//=======================================================================
  IntTools_EdgeFace::IntTools_EdgeFace()
{
  myFuzzyValue = Precision::Confusion();
  myIsDone=Standard_False;
  myErrorStatus=1;
  myQuickCoincidenceCheck=Standard_False;
  myMinDistance = RealLast();
}
//=======================================================================
//function :  IsCoincident
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_EdgeFace::IsCoincident() 
{
  Standard_Integer i, iCnt;
  Standard_Real dT, aT, aD, aT1, aT2, aU, aV;

  gp_Pnt aP;
  TopAbs_State aState;
  gp_Pnt2d aP2d;
  //
  GeomAPI_ProjectPointOnSurf& aProjector=myContext->ProjPS(myFace);

  Standard_Integer aNbSeg=23;
  if (myC.GetType() == GeomAbs_Line &&
      myS.GetType() == GeomAbs_Plane)
    aNbSeg = 2; // Check only three points for Line/Plane intersection

  const Standard_Real aTresh = 0.5;
  const Standard_Integer aTreshIdxF = RealToInt((aNbSeg+1)*0.25),
                         aTreshIdxL = RealToInt((aNbSeg+1)*0.75);
  const Handle(Geom_Surface) aSurf = BRep_Tool::Surface(myFace);

  aT1=myRange.First();
  aT2=myRange.Last();
  Standard_Real aBndShift = 0.01 * (aT2 - aT1);
  //Shifting first and last curve points in order to avoid projection
  //on surface boundary and rejection projection point with minimal distance
  aT1 += aBndShift;
  aT2 -= aBndShift;
  dT=(aT2-aT1)/aNbSeg;
  //
  Standard_Boolean isClassified = Standard_False;
  iCnt=0;
  for(i=0; i <= aNbSeg; ++i) {
    aT = aT1+i*dT;
    aP=myC.Value(aT);
    //
    aProjector.Perform(aP);
    if (!aProjector.IsDone()) {
      continue;
    }
    //
    
    aD=aProjector.LowerDistance();
    if (aD > myCriteria) {
      if (aD > 100. * myCriteria)
        return Standard_False;
      else
        continue;
    }
    //

    ++iCnt; 

    //We classify only three points: in the begin, in the 
    //end and in the middle of the edge.
    //However, exact middle point (when i == (aNbSeg + 1)/2)
    //can be unprojectable. Therefore, it will not be able to
    //be classified. Therefore, points with indexes in 
    //[aTreshIdxF, aTreshIdxL] range are made available 
    //for classification.
    //isClassified == TRUE if MIDDLE point has been chosen and
    //classified correctly.

    if(((0 < i) && (i < aTreshIdxF)) || ((aTreshIdxL < i ) && (i < aNbSeg)))
      continue;

    if(isClassified && (i != aNbSeg))
      continue;

    aProjector.LowerDistanceParameters(aU, aV);
    aP2d.SetX(aU);
    aP2d.SetY(aV);

    IntTools_FClass2d& aClass2d=myContext->FClass2d(myFace);
    aState = aClass2d.Perform(aP2d);
    
    if(aState == TopAbs_OUT)
      return Standard_False;

    if(i != 0)
      isClassified = Standard_True;
  }
  //
  const Standard_Real aCoeff=(Standard_Real)iCnt/((Standard_Real)aNbSeg+1);
  return (aCoeff > aTresh);
}
//=======================================================================
//function : CheckData
//purpose  : 
//=======================================================================
void IntTools_EdgeFace::CheckData()
{
  if (BRep_Tool::Degenerated(myEdge)) {
    myErrorStatus=2;
  }
  if (!BRep_Tool::IsGeometric(myEdge)) { 
     myErrorStatus=3;
  }
}

//=======================================================================
//function : IsProjectable
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_EdgeFace::IsProjectable
  (const Standard_Real aT) const
{
  Standard_Boolean bFlag; 
  gp_Pnt aPC;
  //
  myC.D0(aT, aPC);
  bFlag=myContext->IsValidPointForFace(aPC, myFace, myCriteria);
  //
  return bFlag;
}
//=======================================================================
//function : DistanceFunction
//purpose  : 
//=======================================================================
Standard_Real IntTools_EdgeFace::DistanceFunction
  (const Standard_Real t)
{
  Standard_Real aD;

  //
  gp_Pnt P;
  myC.D0(t, P);
  //
  Standard_Boolean bIsEqDistance;

  bIsEqDistance= IntTools_EdgeFace::IsEqDistance(P, myS, 1.e-7, aD); 
  if (bIsEqDistance) {
    aD=aD-myCriteria;
    return aD; 
  }
  
  //
  Standard_Boolean bFlag = Standard_False;

  GeomAPI_ProjectPointOnSurf& aLocProj = myContext->ProjPS(myFace);
  aLocProj.Perform(P);
  bFlag = aLocProj.IsDone();
  
  if(bFlag) {
    aD = aLocProj.LowerDistance();
  }
  //
  
  if (!bFlag) {
    myErrorStatus = 4;
    return 99.;
  }
  
  // 
  //   aD=aProjector.LowerDistance();
  // 
  aD=aD-myCriteria;
  return aD; 
}
//
//=======================================================================
//function : IsEqDistance
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_EdgeFace::IsEqDistance
  (const gp_Pnt& aP,
   const BRepAdaptor_Surface& aBAS,
   const Standard_Real aTol,
   Standard_Real& aD)
{
  Standard_Boolean bRetFlag=Standard_True;

  GeomAbs_SurfaceType aSurfType=aBAS.GetType();

  if (aSurfType==GeomAbs_Cylinder) {
    gp_Cylinder aCyl=aBAS.Cylinder();
    const gp_Ax1& anAx1  =aCyl.Axis();
    gp_Lin aLinAxis(anAx1);
    Standard_Real aDC, aRadius=aCyl.Radius();
    aDC=aLinAxis.Distance(aP);
    if (aDC < aTol) {
      aD=aRadius;
      return bRetFlag; 
    }
  }

  if (aSurfType==GeomAbs_Cone) {
    gp_Cone aCone=aBAS.Cone();
    const gp_Ax1& anAx1  =aCone.Axis();
    gp_Lin aLinAxis(anAx1);
    Standard_Real aDC, aRadius, aDS, aSemiAngle;
    aDC=aLinAxis.Distance(aP);
    if (aDC < aTol) {
      gp_Pnt anApex=aCone.Apex();
      aSemiAngle=aCone.SemiAngle();
      aDS=aP.Distance(anApex);
      
      aRadius=aDS*tan(aSemiAngle);
      aD=aRadius;
      return bRetFlag; 
    }
  }

  if (aSurfType==GeomAbs_Torus) {
    Standard_Real aMajorRadius, aMinorRadius, aDC;

    gp_Torus aTorus=aBAS.Torus();
    gp_Pnt aPLoc=aTorus.Location();
    aMajorRadius=aTorus.MajorRadius();
    
    aDC=fabs(aPLoc.Distance(aP)-aMajorRadius);
    if (aDC < aTol) {
      aMinorRadius=aTorus.MinorRadius();
      aD=aMinorRadius;
      return bRetFlag; 
    }
  }
  return !bRetFlag; 
}
//
//=======================================================================
//function : MakeType
//purpose  : 
//=======================================================================
Standard_Integer IntTools_EdgeFace::MakeType
  (IntTools_CommonPrt&  aCommonPrt)
{
  Standard_Real  af1, al1;
  Standard_Real  df1, tm;
  Standard_Boolean bAllNullFlag;
  //
  bAllNullFlag=aCommonPrt.AllNullFlag();
  if (bAllNullFlag) {
    aCommonPrt.SetType(TopAbs_EDGE);
    return 0;
  }
  //
  aCommonPrt.Range1(af1, al1);

  {
    gp_Pnt aPF, aPL;
    myC.D0(af1, aPF);
    myC.D0(al1, aPL);
    df1=aPF.Distance(aPL);
    Standard_Boolean isWholeRange = Standard_False;
    
    if((Abs(af1 - myRange.First()) < myC.Resolution(myCriteria)) &&
       (Abs(al1 - myRange.Last()) < myC.Resolution(myCriteria)))
      isWholeRange = Standard_True;
    
    
    if ((df1 > myCriteria * 2.) && isWholeRange) {
      aCommonPrt.SetType(TopAbs_EDGE);
    }
    else {
      if(isWholeRange) {
        tm = (af1 + al1) * 0.5;
        
        if(aPF.Distance(myC.Value(tm)) > myCriteria * 2.) {
          aCommonPrt.SetType(TopAbs_EDGE);
          return 0;
        }
      }
      
      if(!CheckTouch(aCommonPrt, tm)) {
        tm = (af1 + al1) * 0.5;
      }
      aCommonPrt.SetType(TopAbs_VERTEX);
      aCommonPrt.SetVertexParameter1(tm);
      aCommonPrt.SetRange1 (af1, al1);
    }
  }
 return 0;
}


//=======================================================================
//function : CheckTouch 
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_EdgeFace::CheckTouch
  (const IntTools_CommonPrt& aCP,
   Standard_Real&            aTx) 
{
  Standard_Real aTF, aTL, Tol, U1f, U1l, V1f, V1l, af, al,aDist2, aMinDist2;
  Standard_Boolean theflag=Standard_False;
  Standard_Integer aNbExt, iLower;

  aCP.Range1(aTF, aTL);

  //
  Standard_Real aCR;
  aCR=myC.Resolution(myCriteria);
  if((Abs(aTF - myRange.First()) < aCR) &&
     (Abs(aTL - myRange.Last())  < aCR)) {
    return theflag; // EDGE 
  }
  //

  Tol = Precision::PConfusion();

  const Handle(Geom_Curve)&  Curve   =BRep_Tool::Curve  (myC.Edge(), af, al);
  const Handle(Geom_Surface)& Surface=BRep_Tool::Surface(myS.Face());
  //   Surface->Bounds(U1f,U1l,V1f,V1l);
  U1f = myS.FirstUParameter();
  U1l = myS.LastUParameter();
  V1f = myS.FirstVParameter();
  V1l = myS.LastVParameter();
  
  GeomAdaptor_Curve   TheCurve   (Curve,aTF, aTL);
  GeomAdaptor_Surface TheSurface (Surface, U1f, U1l, V1f, V1l); 
     
  Extrema_ExtCS anExtrema (TheCurve, TheSurface, Tol, Tol);

  aDist2 = 1.e100;

  if(anExtrema.IsDone()) {
    aMinDist2 = aDist2;

    if(!anExtrema.IsParallel()) {
      aNbExt=anExtrema.NbExt();
      
      if(aNbExt > 0) {
 iLower=1;
 for (Standard_Integer i=1; i<=aNbExt; i++) {
   aDist2=anExtrema.SquareDistance(i);
   if (aDist2 < aMinDist2) {
     aMinDist2=aDist2;
     iLower=i;
   }
 }
 aDist2=anExtrema.SquareDistance(iLower);
 Extrema_POnCurv aPOnC;
 Extrema_POnSurf aPOnS;
 anExtrema.Points(iLower, aPOnC, aPOnS);
 aTx=aPOnC.Parameter();
      }
      else {
 // modified by NIZHNY-MKK  Thu Jul 21 11:35:32 2005.BEGIN
 IntCurveSurface_HInter anExactIntersector;
  
 Handle(GeomAdaptor_Curve) aCurve     = new GeomAdaptor_Curve(TheCurve);
 Handle(GeomAdaptor_Surface) aSurface = new GeomAdaptor_Surface(TheSurface);
 
 anExactIntersector.Perform(aCurve, aSurface);

 if(anExactIntersector.IsDone()) {
   for(Standard_Integer i = 1; i <= anExactIntersector.NbPoints(); i++) {
     const IntCurveSurface_IntersectionPoint& aPoint = anExactIntersector.Point(i);
      
     if((aPoint.W() >= aTF) && (aPoint.W() <= aTL)) {
       aDist2=0.;
       aTx = aPoint.W();
     }
   }
 }
 // modified by NIZHNY-MKK  Thu Jul 21 11:35:40 2005.END
      }
    }
    else {
      return theflag;
    }
  }

  Standard_Real aBoundaryDist;

  aBoundaryDist = DistanceFunction(aTF) + myCriteria;
  if(aBoundaryDist * aBoundaryDist < aDist2) {
    aDist2 = aBoundaryDist * aBoundaryDist;
    aTx = aTF;
  }
  
  aBoundaryDist = DistanceFunction(aTL) + myCriteria;
  if(aBoundaryDist * aBoundaryDist < aDist2) {
    aDist2 = aBoundaryDist * aBoundaryDist;
    aTx = aTL;
  }

  Standard_Real aParameter = (aTF + aTL) * 0.5;
  aBoundaryDist = DistanceFunction(aParameter) + myCriteria;
  if(aBoundaryDist * aBoundaryDist < aDist2) {
    aDist2 = aBoundaryDist * aBoundaryDist;
    aTx = aParameter;
  }

  if(aDist2 > myCriteria * myCriteria) {
    return theflag;
  }
  
  if (fabs (aTx-aTF) < Precision::PConfusion()) {
    return !theflag;
  }

  if (fabs (aTx-aTL) < Precision::PConfusion()) {
    return !theflag;
  }

  if (aTx>aTF && aTx<aTL) {
    return !theflag;
  }

  return theflag;
}

//=======================================================================
//function : Perform
//purpose  : 
//=======================================================================
void IntTools_EdgeFace::Perform() 
{
  Standard_Integer i, aNb;
  IntTools_CommonPrt aCommonPrt;
  //
  aCommonPrt.SetEdge1(myEdge);
  //
  myErrorStatus=0;
  CheckData();
  if (myErrorStatus) {
    return;
  }
  //
  if (myContext.IsNull()) {
    myContext=new IntTools_Context;
  }
  //
  myIsDone = Standard_False;
  myC.Initialize(myEdge);
  GeomAbs_CurveType aCurveType;
  aCurveType=myC.GetType();
  //
  // Prepare myCriteria
  Standard_Real aFuzz = myFuzzyValue / 2.;
  Standard_Real aTolF = BRep_Tool::Tolerance(myFace) + aFuzz;
  Standard_Real aTolE = BRep_Tool::Tolerance(myEdge) + aFuzz;
  if (aCurveType == GeomAbs_BSplineCurve ||
      aCurveType==GeomAbs_BezierCurve) {
    //--- 5112
    Standard_Real diff1 = (aTolE/aTolF);
    Standard_Real diff2 = (aTolF/aTolE);
    if( diff1 > 100 || diff2 > 100 ) {
      myCriteria = Max(aTolE,aTolF);
    }
    else //--- 5112
      myCriteria = 1.5*aTolE + aTolF;
  }
  else {
    myCriteria = aTolE + aTolF;
  }
  
  myS = myContext->SurfaceAdaptor(myFace);
  
  if (myQuickCoincidenceCheck) {
    if (IsCoincident()) {
      aCommonPrt.SetType(TopAbs_EDGE);
      aCommonPrt.SetRange1(myRange.First(), myRange.Last());
      mySeqOfCommonPrts.Append(aCommonPrt);
      myIsDone=Standard_True;
      return;
    }
  }
  //
  IntTools_BeanFaceIntersector anIntersector(myC, myS, aTolE, aTolF);
  anIntersector.SetBeanParameters(myRange.First(), myRange.Last());
  //
  anIntersector.SetContext(myContext);
  //
  anIntersector.Perform();

  if (anIntersector.MinimalSquareDistance() < RealLast())
    myMinDistance = Sqrt (anIntersector.MinimalSquareDistance());

  if(!anIntersector.IsDone()) {
    return;
  }
  
  for(Standard_Integer r = 1; r <= anIntersector.Result().Length(); r++) {
    const IntTools_Range& aRange = anIntersector.Result().Value(r);
    
    if(IsProjectable(IntTools_Tools::IntermediatePoint(aRange.First(), aRange.Last()))) {
      aCommonPrt.SetRange1(aRange.First(), aRange.Last());
      mySeqOfCommonPrts.Append(aCommonPrt);
    }
  }

  aNb = mySeqOfCommonPrts.Length();

  for (i=1; i<=aNb; i++) {
    IntTools_CommonPrt& aCP=mySeqOfCommonPrts.ChangeValue(i);
    //
    Standard_Real aTx1, aTx2;
    gp_Pnt aPx1, aPx2;
    //
    aCP.Range1(aTx1, aTx2);
    myC.D0(aTx1, aPx1);
    myC.D0(aTx2, aPx2);
    aCP.SetBoundingPoints(aPx1, aPx2);
    //
    MakeType (aCP); 
  }
  {
    // Line\Cylinder's Common Parts treatment
    GeomAbs_CurveType   aCType;
    GeomAbs_SurfaceType aSType;
    TopAbs_ShapeEnum aType;
    Standard_Boolean bIsTouch;
    Standard_Real aTx;
    
    aCType=myC.GetType();
    aSType=myS.GetType();
    
    if (aCType==GeomAbs_Line && aSType==GeomAbs_Cylinder) {
      for (i=1; i<=aNb; i++) {
        IntTools_CommonPrt& aCP=mySeqOfCommonPrts(i);
        aType=aCP.Type();
        if (aType==TopAbs_EDGE) {
          bIsTouch=CheckTouch (aCP, aTx);
          if (bIsTouch) {
            aCP.SetType(TopAbs_VERTEX);
            aCP.SetVertexParameter1(aTx);
            //aCP.SetRange1 (aTx, aTx);
          }
        }
        else if (aType==TopAbs_VERTEX) {
          bIsTouch=CheckTouchVertex (aCP, aTx);
          if (bIsTouch) {
            aCP.SetVertexParameter1(aTx);
            //aCP.SetRange1 (aTx, aTx);
          }
        }
      }
    }
    
    // Circle\Plane's Common Parts treatment
    
    if (aCType==GeomAbs_Circle && aSType==GeomAbs_Plane) {
      Standard_Boolean bIsCoplanar, bIsRadius;
      bIsCoplanar=IsCoplanar(myC, myS);
      bIsRadius=IsRadius(myC, myS, myCriteria);
      if (!bIsCoplanar && !bIsRadius) {
        for (i=1; i<=aNb; i++) {
          IntTools_CommonPrt& aCP=mySeqOfCommonPrts(i);
          aType=aCP.Type();
          if (aType==TopAbs_EDGE) {
            bIsTouch=CheckTouch (aCP, aTx);
            if (bIsTouch) {
              aCP.SetType(TopAbs_VERTEX);
              aCP.SetVertexParameter1(aTx);
              //aCP.SetRange1 (aTx, aTx);
            }
          }
          else if (aType==TopAbs_VERTEX) {
            bIsTouch=CheckTouchVertex (aCP, aTx);
            if (bIsTouch) {
              aCP.SetVertexParameter1(aTx);
              //aCP.SetRange1 (aTx, aTx);
            }
          }
        }
      }
    }
  }
  myIsDone=Standard_True;
}

//=======================================================================
//function : CheckTouch 
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_EdgeFace::CheckTouchVertex 
  (const IntTools_CommonPrt& aCP,
   Standard_Real& aTx) 
{
  Standard_Real aTF, aTL, Tol, U1f,U1l,V1f,V1l;
  Standard_Real aEpsT, af, al,aDist2, aMinDist2, aTm, aDist2New;
  Standard_Boolean theflag=Standard_False;
  Standard_Integer aNbExt, i, iLower ;
  GeomAbs_CurveType aType;
  //
  aCP.Range1(aTF, aTL);
  aType=myC.GetType();
  //
  aEpsT=8.e-5;
  if (aType==GeomAbs_Line) {
    aEpsT=9.e-5;
  }
  //
  aTm=0.5*(aTF+aTL);
  aDist2=DistanceFunction(aTm);
  aDist2 *= aDist2;

  Tol = Precision::PConfusion();

  const Handle(Geom_Curve)&  Curve =BRep_Tool::Curve  (myC.Edge(), af, al);
  const Handle(Geom_Surface)& Surface=BRep_Tool::Surface(myS.Face());

  Surface->Bounds(U1f,U1l,V1f,V1l);
  
  GeomAdaptor_Curve   TheCurve   (Curve,aTF, aTL);
  GeomAdaptor_Surface TheSurface (Surface, U1f, U1l, V1f, V1l); 
     
  Extrema_ExtCS anExtrema (TheCurve, TheSurface, Tol, Tol);
   
  if(!anExtrema.IsDone()) {
    return theflag;
  }
  if (anExtrema.IsParallel()) {
    return theflag;
  }
  
  aNbExt=anExtrema.NbExt() ;
  if (!aNbExt) {
     return theflag;
  }

  iLower=1;
  aMinDist2=1.e100;
  for (i=1; i<=aNbExt; ++i) {
    aDist2=anExtrema.SquareDistance(i);
    if (aDist2 < aMinDist2) {
      aMinDist2=aDist2;
      iLower=i;
    }
  }

  aDist2New=anExtrema.SquareDistance(iLower);
  
  if (aDist2New > aDist2) {
    aTx=aTm;
    return !theflag;
  }
  
  if (aDist2New > myCriteria * myCriteria) {
    return theflag;
  }

  Extrema_POnCurv aPOnC;
  Extrema_POnSurf aPOnS;
  anExtrema.Points(iLower, aPOnC, aPOnS);

 
  aTx=aPOnC.Parameter();
  ///
  if (fabs (aTx-aTF) < aEpsT) {
    return theflag;
  }

  if (fabs (aTx-aTL) < aEpsT) {
    return theflag;
  }

  if (aTx>aTF && aTx<aTL) {
    return !theflag;
  }

  return theflag;
}


//=======================================================================
//function :  IsCoplanar
//purpose  : 
//=======================================================================
Standard_Boolean IsCoplanar (const BRepAdaptor_Curve& aCurve ,
                             const BRepAdaptor_Surface& aSurface)
{
  Standard_Boolean bFlag=Standard_False;

  GeomAbs_CurveType   aCType;
  GeomAbs_SurfaceType aSType;

  aCType=aCurve.GetType();
  aSType=aSurface.GetType();
    
  if (aCType==GeomAbs_Circle && aSType==GeomAbs_Plane) {
    gp_Circ aCirc=aCurve.Circle();
    const gp_Ax1& anAx1=aCirc.Axis();
    const gp_Dir& aDirAx1=anAx1.Direction();
    
    gp_Pln aPln=aSurface.Plane();
    const gp_Ax1& anAx=aPln.Axis();
    const gp_Dir& aDirPln=anAx.Direction();

    bFlag=IntTools_Tools::IsDirsCoinside(aDirAx1, aDirPln);
  }
  return bFlag;
}
//=======================================================================
//function :  IsRadius
//purpose  : 
//=======================================================================
Standard_Boolean IsRadius (const BRepAdaptor_Curve& aCurve,
                           const BRepAdaptor_Surface& aSurface,
                           const Standard_Real aCriteria)
{
  Standard_Boolean bFlag=Standard_False;

  GeomAbs_CurveType   aCType;
  GeomAbs_SurfaceType aSType;

  aCType=aCurve.GetType();
  aSType=aSurface.GetType();
    
  if (aCType==GeomAbs_Circle && aSType==GeomAbs_Plane) {
    gp_Circ aCirc=aCurve.Circle();
    const gp_Pnt aCenter=aCirc.Location();
    Standard_Real aR=aCirc.Radius();
    gp_Pln aPln=aSurface.Plane();
    Standard_Real aD=aPln.Distance(aCenter);
    if (fabs (aD-aR) < aCriteria) {
      return !bFlag;
    }
  }
  return bFlag;
}
//
//=======================================================================
//function :  AdaptiveDiscret
//purpose  : 
//=======================================================================
Standard_Integer AdaptiveDiscret (const Standard_Integer iDiscret,
                                  const BRepAdaptor_Curve& aCurve ,
                                  const BRepAdaptor_Surface& aSurface)
{
  Standard_Integer iDiscretNew;

  iDiscretNew=iDiscret;

  GeomAbs_SurfaceType aSType;

  aSType=aSurface.GetType();
    
  if (aSType==GeomAbs_Cylinder) {
   Standard_Real aELength, aRadius, dLR;

   aELength=IntTools::Length(aCurve.Edge());
   
   gp_Cylinder aCylinder=aSurface.Cylinder();
   aRadius=aCylinder.Radius();
   dLR=2*aRadius;

   iDiscretNew=(Standard_Integer)(aELength/dLR);
   
   if (iDiscretNew<iDiscret) {
     iDiscretNew=iDiscret;
   }
     
  }
  return iDiscretNew;
}
