// Created by: Peter KURNEV
// 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.


#include <Bnd_Box.hxx>
#include <Bnd_OBB.hxx>
#include <BRep_Tool.hxx>
#include <BRepBndLib.hxx>
#include <BRepClass3d_SolidClassifier.hxx>
#include <Extrema_LocateExtPC.hxx>
#include <Geom2d_Curve.hxx>
#include <Geom2d_TrimmedCurve.hxx>
#include <Geom2dHatch_Hatcher.hxx>
#include <Geom2dHatch_Intersector.hxx>
#include <Geom_BoundedCurve.hxx>
#include <Geom_Curve.hxx>
#include <GeomAdaptor_Curve.hxx>
#include <GeomAPI_ProjectPointOnCurve.hxx>
#include <GeomAPI_ProjectPointOnSurf.hxx>
#include <gp_Pnt.hxx>
#include <gp_Pnt2d.hxx>
#include <IntTools_Context.hxx>
#include <IntTools_FClass2d.hxx>
#include <IntTools_SurfaceRangeLocalizeData.hxx>
#include <IntTools_Tools.hxx>
#include <Precision.hxx>
#include <Standard_Type.hxx>
#include <TopAbs_State.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>

IMPLEMENT_STANDARD_RTTIEXT(IntTools_Context,Standard_Transient)

// 
//=======================================================================
//function : 
//purpose  : 
//=======================================================================
IntTools_Context::IntTools_Context()
:
  myAllocator(NCollection_BaseAllocator::CommonBaseAllocator()),
  myFClass2dMap(100, myAllocator),
  myProjPSMap(100, myAllocator),
  myProjPCMap(100, myAllocator),
  mySClassMap(100, myAllocator),
  myProjPTMap(100, myAllocator),
  myHatcherMap(100, myAllocator),
  myProjSDataMap(100, myAllocator),
  myBndBoxDataMap(100, myAllocator),
  mySurfAdaptorMap(100, myAllocator),
  myOBBMap(100, myAllocator),
  myCreateFlag(0),
  myPOnSTolerance(1.e-12)
{
}
//=======================================================================
//function : 
//purpose  : 
//=======================================================================
IntTools_Context::IntTools_Context
  (const Handle(NCollection_BaseAllocator)& theAllocator)
:
  myAllocator(theAllocator),
  myFClass2dMap(100, myAllocator),
  myProjPSMap(100, myAllocator),
  myProjPCMap(100, myAllocator),
  mySClassMap(100, myAllocator),
  myProjPTMap(100, myAllocator),
  myHatcherMap(100, myAllocator),
  myProjSDataMap(100, myAllocator),
  myBndBoxDataMap(100, myAllocator),
  mySurfAdaptorMap(100, myAllocator),
  myOBBMap(100, myAllocator),
  myCreateFlag(1),
  myPOnSTolerance(1.e-12)
{
}
//=======================================================================
//function : ~
//purpose  : 
//=======================================================================
IntTools_Context::~IntTools_Context()
{
  for (NCollection_DataMap<TopoDS_Shape, IntTools_FClass2d*, TopTools_ShapeMapHasher>::Iterator anIt (myFClass2dMap);
       anIt.More(); anIt.Next())
  {
    IntTools_FClass2d* pFClass2d = anIt.Value();;
    (*pFClass2d).~IntTools_FClass2d();
    myAllocator->Free (pFClass2d);
  }
  myFClass2dMap.Clear();

  clearCachedPOnSProjectors();
  for (NCollection_DataMap<TopoDS_Shape, GeomAPI_ProjectPointOnCurve*, TopTools_ShapeMapHasher>::Iterator anIt (myProjPCMap);
       anIt.More(); anIt.Next())
  {
    GeomAPI_ProjectPointOnCurve* pProjPC = anIt.Value();
    (*pProjPC).~GeomAPI_ProjectPointOnCurve();
    myAllocator->Free (pProjPC);
  }
  myProjPCMap.Clear();

  for (NCollection_DataMap<TopoDS_Shape, BRepClass3d_SolidClassifier*, TopTools_ShapeMapHasher>::Iterator anIt (mySClassMap);
       anIt.More(); anIt.Next())
  {
    BRepClass3d_SolidClassifier* pSC = anIt.Value();
    (*pSC).~BRepClass3d_SolidClassifier();
    myAllocator->Free (pSC);
  }
  mySClassMap.Clear();

  for (NCollection_DataMap<Handle(Geom_Curve), GeomAPI_ProjectPointOnCurve*>::Iterator anIt (myProjPTMap);
       anIt.More(); anIt.Next())
  {
    GeomAPI_ProjectPointOnCurve* pProjPT = anIt.Value();
    (*pProjPT).~GeomAPI_ProjectPointOnCurve();
    myAllocator->Free (pProjPT);
  }
  myProjPTMap.Clear();

  for (NCollection_DataMap<TopoDS_Shape, Geom2dHatch_Hatcher*, TopTools_ShapeMapHasher>::Iterator anIt (myHatcherMap);
       anIt.More(); anIt.Next())
  {
    Geom2dHatch_Hatcher* pHatcher = anIt.Value();
    (*pHatcher).~Geom2dHatch_Hatcher();
    myAllocator->Free (pHatcher);
  }
  myHatcherMap.Clear();

  for (NCollection_DataMap<TopoDS_Shape, IntTools_SurfaceRangeLocalizeData*, TopTools_ShapeMapHasher>::Iterator anIt (myProjSDataMap);
       anIt.More(); anIt.Next())
  {
    IntTools_SurfaceRangeLocalizeData* pSData = anIt.Value();
    (*pSData).~IntTools_SurfaceRangeLocalizeData();
    myAllocator->Free (pSData);
  }
  myProjSDataMap.Clear();

  for (NCollection_DataMap<TopoDS_Shape, Bnd_Box*, TopTools_ShapeMapHasher>::Iterator anIt (myBndBoxDataMap);
       anIt.More(); anIt.Next())
  {
    Bnd_Box* pBox = anIt.Value();
    (*pBox).~Bnd_Box();
    myAllocator->Free (pBox);
  }
  myBndBoxDataMap.Clear();

  for (NCollection_DataMap<TopoDS_Shape, BRepAdaptor_Surface*, TopTools_ShapeMapHasher>::Iterator anIt (mySurfAdaptorMap);
       anIt.More(); anIt.Next())
  {
    BRepAdaptor_Surface* pSurfAdaptor = anIt.Value();
    (*pSurfAdaptor).~BRepAdaptor_Surface();
    myAllocator->Free (pSurfAdaptor);
  }
  mySurfAdaptorMap.Clear();

  for (NCollection_DataMap<TopoDS_Shape, Bnd_OBB*, TopTools_ShapeMapHasher>::Iterator anIt (myOBBMap);
       anIt.More(); anIt.Next())
  {
    Bnd_OBB* pOBB = anIt.Value();
    (*pOBB).~Bnd_OBB();
    myAllocator->Free (pOBB);
  }
  myOBBMap.Clear();
}

//=======================================================================
//function : BndBox
//purpose  : 
//=======================================================================
Bnd_Box& IntTools_Context::BndBox(const TopoDS_Shape& aS)
{
  Bnd_Box* pBox = NULL;
  if (!myBndBoxDataMap.Find (aS, pBox))
  {
    //
    pBox=(Bnd_Box*)myAllocator->Allocate(sizeof(Bnd_Box));
    new (pBox) Bnd_Box();
    //
    Bnd_Box &aBox=*pBox;
    BRepBndLib::Add(aS, aBox);
    //
    myBndBoxDataMap.Bind (aS, pBox);
  }
  return *pBox;
}

//=======================================================================
//function : IsInfiniteFace
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_Context::IsInfiniteFace
  (const TopoDS_Face& aFace)
{
  const Bnd_Box& aBox = BndBox(aFace);
  return aBox.IsOpenXmax() ||
         aBox.IsOpenXmin() ||
         aBox.IsOpenYmax() ||
         aBox.IsOpenYmin() ||
         aBox.IsOpenZmax() ||
         aBox.IsOpenZmin();
}
//=======================================================================
//function : FClass2d
//purpose  : 
//=======================================================================
IntTools_FClass2d& IntTools_Context::FClass2d(const TopoDS_Face& aF)
{
  IntTools_FClass2d* pFClass2d = NULL;
  if (!myFClass2dMap.Find (aF, pFClass2d))
  {
    Standard_Real aTolF;
    TopoDS_Face aFF;
    //
    aFF=aF;
    aFF.Orientation(TopAbs_FORWARD);
    aTolF=BRep_Tool::Tolerance(aFF);
    //
    pFClass2d=(IntTools_FClass2d*)myAllocator->Allocate(sizeof(IntTools_FClass2d));
    new (pFClass2d) IntTools_FClass2d(aFF, aTolF);
    //
    myFClass2dMap.Bind(aFF, pFClass2d);
  }
  return *pFClass2d;
}

//=======================================================================
//function : ProjPS
//purpose  : 
//=======================================================================
GeomAPI_ProjectPointOnSurf& IntTools_Context::ProjPS(const TopoDS_Face& aF)
{
  GeomAPI_ProjectPointOnSurf* pProjPS = NULL;
  if (!myProjPSMap.Find (aF, pProjPS))
  {
    Standard_Real Umin, Usup, Vmin, Vsup;
    UVBounds(aF, Umin, Usup, Vmin, Vsup);
    const Handle(Geom_Surface)& aS=BRep_Tool::Surface(aF);
    //
    pProjPS=(GeomAPI_ProjectPointOnSurf*)myAllocator->Allocate(sizeof(GeomAPI_ProjectPointOnSurf));
    new (pProjPS) GeomAPI_ProjectPointOnSurf();
    pProjPS->Init(aS ,Umin, Usup, Vmin, Vsup, myPOnSTolerance);
    pProjPS->SetExtremaFlag(Extrema_ExtFlag_MIN); ///
    //
    myProjPSMap.Bind(aF, pProjPS);
  }
  return *pProjPS;
}
//=======================================================================
//function : ProjPC
//purpose  : 
//=======================================================================
GeomAPI_ProjectPointOnCurve& IntTools_Context::ProjPC(const TopoDS_Edge& aE)
{
  GeomAPI_ProjectPointOnCurve* pProjPC = NULL;
  if (!myProjPCMap.Find (aE, pProjPC))
  {
    Standard_Real f, l;
    //
    Handle(Geom_Curve)aC3D=BRep_Tool::Curve (aE, f, l);
    //
    pProjPC=(GeomAPI_ProjectPointOnCurve*)myAllocator->Allocate(sizeof(GeomAPI_ProjectPointOnCurve));
    new (pProjPC) GeomAPI_ProjectPointOnCurve();
    pProjPC->Init(aC3D, f, l);
    //
    myProjPCMap.Bind(aE, pProjPC);
  }
  return *pProjPC;
}

//=======================================================================
//function : ProjPT
//purpose  : 
//=======================================================================
GeomAPI_ProjectPointOnCurve& IntTools_Context::ProjPT
  (const Handle(Geom_Curve)& aC3D)

{
  GeomAPI_ProjectPointOnCurve* pProjPT = NULL;
  if (!myProjPTMap.Find (aC3D, pProjPT))
  {
    Standard_Real f, l;
    f=aC3D->FirstParameter();
    l=aC3D->LastParameter();
    //
    pProjPT=(GeomAPI_ProjectPointOnCurve*)myAllocator->Allocate(sizeof(GeomAPI_ProjectPointOnCurve));
    new (pProjPT) GeomAPI_ProjectPointOnCurve();
    pProjPT->Init(aC3D, f, l);
    //
    myProjPTMap.Bind (aC3D, pProjPT);
  }
  return *pProjPT;
}

//=======================================================================
//function : SolidClassifier
//purpose  : 
//=======================================================================
BRepClass3d_SolidClassifier& IntTools_Context::SolidClassifier
  (const TopoDS_Solid& aSolid)
{
  BRepClass3d_SolidClassifier* pSC = NULL;
  if (!mySClassMap.Find (aSolid, pSC))
  {
    pSC=(BRepClass3d_SolidClassifier*)myAllocator->Allocate(sizeof(BRepClass3d_SolidClassifier));
    new (pSC) BRepClass3d_SolidClassifier(aSolid);
    //
    mySClassMap.Bind (aSolid, pSC);
  }
  return *pSC;
}

//=======================================================================
//function : SurfaceAdaptor
//purpose  : 
//=======================================================================
BRepAdaptor_Surface& IntTools_Context::SurfaceAdaptor
  (const TopoDS_Face& theFace)
{
  BRepAdaptor_Surface* pBAS = NULL;
  if (!mySurfAdaptorMap.Find (theFace, pBAS))
  {
    //
    pBAS=(BRepAdaptor_Surface*)myAllocator->Allocate(sizeof(BRepAdaptor_Surface));
    new (pBAS) BRepAdaptor_Surface(theFace, Standard_True);
    //
    mySurfAdaptorMap.Bind (theFace, pBAS);
  }
  return *pBAS;
}

//=======================================================================
//function : Hatcher
//purpose  : 
//=======================================================================
Geom2dHatch_Hatcher& IntTools_Context::Hatcher(const TopoDS_Face& aF)
{
  Geom2dHatch_Hatcher* pHatcher = NULL;
  if (!myHatcherMap.Find (aF, pHatcher))
  {
    Standard_Real aTolArcIntr, aTolTangfIntr, aTolHatch2D, aTolHatch3D;
    Standard_Real aU1, aU2, aEpsT;
    TopAbs_Orientation aOrE;
    Handle(Geom_Surface) aS;
    Handle(Geom2d_Curve) aC2D;
    Handle(Geom2d_TrimmedCurve) aCT2D;
    TopoDS_Face aFF;
    TopExp_Explorer aExp;
    //
    aTolHatch2D=1.e-8;
    aTolHatch3D=1.e-8;
    aTolArcIntr=1.e-10;
    aTolTangfIntr=1.e-10;
    aEpsT=Precision::PConfusion();
    //
    Geom2dHatch_Intersector aIntr(aTolArcIntr, aTolTangfIntr);
    pHatcher=(Geom2dHatch_Hatcher*)
      myAllocator->Allocate(sizeof(Geom2dHatch_Hatcher));
    new (pHatcher) Geom2dHatch_Hatcher(aIntr,
                                       aTolHatch2D, aTolHatch3D,
                                       Standard_True, Standard_False);
    //
    aFF=aF;
    aFF.Orientation(TopAbs_FORWARD);
    aS=BRep_Tool::Surface(aFF);

    aExp.Init (aFF, TopAbs_EDGE);
    for (; aExp.More() ; aExp.Next()) {
      const TopoDS_Edge& aE=*((TopoDS_Edge*)&aExp.Current());
      aOrE=aE.Orientation();
      //
      aC2D=BRep_Tool::CurveOnSurface (aE, aFF, aU1, aU2);
      if (aC2D.IsNull() ) {
        continue;
      }
      if (fabs(aU1-aU2) < aEpsT) {
        continue;
      }
      //
      aCT2D=new Geom2d_TrimmedCurve(aC2D, aU1, aU2);
      Geom2dAdaptor_Curve aGAC (aCT2D);
      pHatcher->AddElement(aGAC, aOrE);
    }// for (; aExp.More() ; aExp.Next()) {
    //
    myHatcherMap.Bind (aFF, pHatcher);
  }
  return *pHatcher;
}

//=======================================================================
//function : OBB
//purpose  : 
//=======================================================================
Bnd_OBB& IntTools_Context::OBB(const TopoDS_Shape& aS,
                               const Standard_Real theGap)
{
  Bnd_OBB* pBox = NULL;
  if (!myOBBMap.Find (aS, pBox))
  {
    pBox = (Bnd_OBB*)myAllocator->Allocate(sizeof(Bnd_OBB));
    new (pBox) Bnd_OBB();
    //
    Bnd_OBB &aBox = *pBox;
    BRepBndLib::AddOBB(aS, aBox);
    aBox.Enlarge(theGap);
    //
    myOBBMap.Bind(aS, pBox);
  }
  return *pBox;
}

//=======================================================================
//function : SurfaceData
//purpose  : 
//=======================================================================
IntTools_SurfaceRangeLocalizeData& IntTools_Context::SurfaceData
  (const TopoDS_Face& aF) 
{
  IntTools_SurfaceRangeLocalizeData* pSData = NULL;
  if (!myProjSDataMap.Find (aF, pSData))
  {
    pSData=(IntTools_SurfaceRangeLocalizeData*)
      myAllocator->Allocate(sizeof(IntTools_SurfaceRangeLocalizeData));
    new (pSData) IntTools_SurfaceRangeLocalizeData
      (3, 
       3, 
       10. * Precision::PConfusion(), 
       10. * Precision::PConfusion());
    //
    myProjSDataMap.Bind (aF, pSData);
  }
  return *pSData;
}

//=======================================================================
//function : ComputePE
//purpose  : 
//=======================================================================
Standard_Integer IntTools_Context::ComputePE
  (const gp_Pnt& aP1,
   const Standard_Real aTolP1,
   const TopoDS_Edge& aE2,
   Standard_Real& aT,
   Standard_Real& aDist)
{
  if (!BRep_Tool::IsGeometric(aE2)) { 
    return -2;
  }
  Standard_Real aTolE2, aTolSum;
  Standard_Integer aNbProj;
  //
  GeomAPI_ProjectPointOnCurve& aProjector=ProjPC(aE2);
  aProjector.Perform(aP1);

  aNbProj=aProjector.NbPoints();
  if (aNbProj)
  {
    // point falls on the curve
    aDist = aProjector.LowerDistance();
    //
    aTolE2 = BRep_Tool::Tolerance(aE2);
    aTolSum = aTolP1 + aTolE2 + Precision::Confusion();
    //
    aT = aProjector.LowerDistanceParameter();
    if (aDist > aTolSum) {
      return -4;
    }
  }
  else
  {
    // point falls out of the curve, check distance to vertices
    TopoDS_Edge aEFwd = TopoDS::Edge(aE2.Oriented(TopAbs_FORWARD));
    TopoDS_Iterator itV(aEFwd);
    aDist = RealLast();
    for (; itV.More(); itV.Next())
    {
      const TopoDS_Vertex& aV = TopoDS::Vertex(itV.Value());
      if (aV.Orientation() == TopAbs_FORWARD || aV.Orientation() == TopAbs_REVERSED)
      {
        gp_Pnt aPV = BRep_Tool::Pnt(aV);
        aTolSum = aTolP1 + BRep_Tool::Tolerance(aV) + Precision::Confusion();
        Standard_Real aDist1 = aP1.Distance(aPV);
        if (aDist1 < aDist && aDist1 < aTolSum)
        {
          aDist = aDist1;
          aT = BRep_Tool::Parameter(aV, aEFwd);
        }
      }
    }
    if (Precision::IsInfinite(aDist)) {
      return -3;
    }
  }
  return 0;
}
//=======================================================================
//function : ComputeVE
//purpose  : 
//=======================================================================
Standard_Integer IntTools_Context::ComputeVE
  (const TopoDS_Vertex& theV, 
   const TopoDS_Edge&   theE,
   Standard_Real& theT,
   Standard_Real& theTol,
   const Standard_Real theFuzz)
{
  if (BRep_Tool::Degenerated(theE)) {
    return -1;
  }
  if (!BRep_Tool::IsGeometric(theE)) { 
    return -2;
  }
  Standard_Real aDist, aTolV, aTolE, aTolSum;
  Standard_Integer aNbProj;
  gp_Pnt aP;
  //
  aP=BRep_Tool::Pnt(theV);
  //
  GeomAPI_ProjectPointOnCurve& aProjector=ProjPC(theE);
  aProjector.Perform(aP);

  aNbProj=aProjector.NbPoints();
  if (!aNbProj) {
    return -3;
  }
  //
  aDist=aProjector.LowerDistance();
  //
  aTolV=BRep_Tool::Tolerance(theV);
  aTolE=BRep_Tool::Tolerance(theE);
  aTolSum = aTolV + aTolE + Max(theFuzz, Precision::Confusion());
  //
  theTol = aDist + aTolE;
  theT = aProjector.LowerDistanceParameter();
  if (aDist > aTolSum) {
    return -4;
  }
  return 0;
}
//=======================================================================
//function : ComputeVF
//purpose  : 
//=======================================================================
Standard_Integer IntTools_Context::ComputeVF
  (const TopoDS_Vertex& theVertex, 
   const TopoDS_Face&   theFace,
   Standard_Real& theU,
   Standard_Real& theV,
   Standard_Real& theTol,
   const Standard_Real theFuzz)
{
  Standard_Real aTolV, aTolF, aTolSum, aDist;
  gp_Pnt aP;

  aP = BRep_Tool::Pnt(theVertex);
  //
  // 1. Check if the point is projectable on the surface
  GeomAPI_ProjectPointOnSurf& aProjector=ProjPS(theFace);
  aProjector.Perform(aP);
  //
  if (!aProjector.IsDone()) { // the point is not  projectable on the surface
    return -1;
  }
  //
  // 2. Check the distance between the projection point and 
  //    the original point
  aDist = aProjector.LowerDistance();
  //
  aTolV = BRep_Tool::Tolerance(theVertex);
  aTolF = BRep_Tool::Tolerance(theFace);
  //
  aTolSum = aTolV + aTolF + Max(theFuzz, Precision::Confusion());
  theTol = aDist + aTolF;
  aProjector.LowerDistanceParameters(theU, theV);
  //
  if (aDist > aTolSum) {
    // the distance is too large
    return -2;
  }
  //
  gp_Pnt2d aP2d(theU, theV);
  Standard_Boolean pri = IsPointInFace (theFace, aP2d);
  if (!pri) {//  the point lays on the surface but out of the face 
    return -3;
  }
  return 0;
}
//=======================================================================
//function : StatePointFace
//purpose  : 
//=======================================================================
TopAbs_State IntTools_Context::StatePointFace
  (const TopoDS_Face& aF,
   const gp_Pnt2d& aP2d)
{
  TopAbs_State aState;
  IntTools_FClass2d& aClass2d=FClass2d(aF);
  aState=aClass2d.Perform(aP2d);
  return aState;
}
//=======================================================================
//function : IsPointInFace
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_Context::IsPointInFace
  (const TopoDS_Face& aF,
   const gp_Pnt2d& aP2d)
{
  TopAbs_State aState=StatePointFace(aF, aP2d);
  if (aState==TopAbs_OUT || aState==TopAbs_ON) {
    return Standard_False;
  }
  return Standard_True;
}
//=======================================================================
//function : IsPointInFace
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_Context::IsPointInFace
  (const gp_Pnt& aP,
   const TopoDS_Face& aF,
   const Standard_Real aTol) 
{
  Standard_Boolean bIn = Standard_False;
  Standard_Real aDist;
  //
  GeomAPI_ProjectPointOnSurf& aProjector=ProjPS(aF);
  aProjector.Perform(aP);
  //
  Standard_Boolean bDone = aProjector.IsDone();
  if (bDone) {
    aDist = aProjector.LowerDistance();
    if (aDist < aTol) {
      Standard_Real U, V;
      //
      aProjector.LowerDistanceParameters(U, V);
      gp_Pnt2d aP2D(U, V);
      bIn = IsPointInFace(aF, aP2D);
    }
  }
  //
  return bIn;
}
//=======================================================================
//function : IsPointInOnFace
//purpose  : 
//=======================================================================
  Standard_Boolean IntTools_Context::IsPointInOnFace(const TopoDS_Face& aF,
                                                   const gp_Pnt2d& aP2d)
{ 
  TopAbs_State aState=StatePointFace(aF, aP2d);
  if (aState==TopAbs_OUT) {
    return Standard_False;
  }
  return Standard_True;
}
//=======================================================================
//function : IsValidPointForFace
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_Context::IsValidPointForFace
  (const gp_Pnt& aP,
   const TopoDS_Face& aF,
   const Standard_Real aTol) 
{
  Standard_Boolean bFlag;
  Standard_Real Umin, U, V;

  GeomAPI_ProjectPointOnSurf& aProjector=ProjPS(aF);
  aProjector.Perform(aP);
  
  bFlag=aProjector.IsDone();
  if (bFlag) {
    
    Umin=aProjector.LowerDistance();
    //if (Umin > 1.e-3) { // it was 
    if (Umin > aTol) {
      return !bFlag; 
    }
    //
    aProjector.LowerDistanceParameters(U, V);
    gp_Pnt2d aP2D(U, V);
    bFlag=IsPointInOnFace (aF, aP2D);
  }
  return bFlag;
}
//=======================================================================
//function : IsValidPointForFaces
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_Context::IsValidPointForFaces 
  (const gp_Pnt& aP,
   const TopoDS_Face& aF1,
   const TopoDS_Face& aF2,
   const Standard_Real aTol) 
{
  Standard_Boolean bFlag1, bFlag2;

  bFlag1=IsValidPointForFace(aP, aF1, aTol);
  if (!bFlag1) {
    return bFlag1;
  }
  bFlag2=IsValidPointForFace(aP, aF2, aTol);  
  return  bFlag2;
}
//=======================================================================
//function : IsValidBlockForFace
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_Context::IsValidBlockForFace 
  (const Standard_Real aT1,
   const Standard_Real aT2,
   const IntTools_Curve& aC, 
   const TopoDS_Face& aF,
   const Standard_Real aTol) 
{
  Standard_Boolean bFlag;
  Standard_Real aTInterm;
  gp_Pnt aPInterm;

  aTInterm=IntTools_Tools::IntermediatePoint(aT1, aT2);

  const Handle(Geom_Curve)& aC3D=aC.Curve();
  // point 3D
  aC3D->D0(aTInterm, aPInterm);
  //
  bFlag=IsValidPointForFace (aPInterm, aF, aTol);
  return bFlag;
}
//=======================================================================
//function : IsValidBlockForFaces
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_Context::IsValidBlockForFaces(const Standard_Real theT1,
                                                        const Standard_Real theT2,
                                                        const IntTools_Curve& theC, 
                                                        const TopoDS_Face& theF1,
                                                        const TopoDS_Face& theF2,
                                                        const Standard_Real theTol) 
{
  const Standard_Integer aNbElem = 2;
  const Handle(Geom2d_Curve) &aPC1 = theC.FirstCurve2d();
  const Handle(Geom2d_Curve) &aPC2 = theC.SecondCurve2d();
  const Handle(Geom_Curve)   &aC3D = theC.Curve();
  
  const Handle(Geom2d_Curve)* anArrPC[aNbElem] = { &aPC1, &aPC2 };
  const TopoDS_Face* anArrF[aNbElem] = { &theF1, &theF2 };

  const Standard_Real aMidPar = IntTools_Tools::IntermediatePoint(theT1, theT2);
  const gp_Pnt aP(aC3D->Value(aMidPar));

  Standard_Boolean bFlag = Standard_True;  
  gp_Pnt2d aPnt2D;  

  for (Standard_Integer i = 0; (i < 2) && bFlag; ++i)
  {
    const Handle(Geom2d_Curve) &aPC = *anArrPC[i];
    const TopoDS_Face &aF           = *anArrF[i];

    if (!aPC.IsNull())
    {
      aPC->D0(aMidPar, aPnt2D);
      bFlag = IsPointInOnFace(aF, aPnt2D);
    }
    else
    {
      bFlag = IsValidPointForFace(aP, aF, theTol);
    }
  }

  return bFlag;
}
//=======================================================================
//function : IsVertexOnLine
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_Context::IsVertexOnLine 
  (const TopoDS_Vertex& aV,
   const IntTools_Curve& aC, 
   const Standard_Real aTolC,
   Standard_Real& aT)
{
  Standard_Boolean bRet;
  Standard_Real aTolV;
  //
  aTolV=BRep_Tool::Tolerance(aV);
  bRet=IntTools_Context::IsVertexOnLine(aV, aTolV, aC, aTolC , aT);
  //
  return bRet;
}
//=======================================================================
//function : IsVertexOnLine
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_Context::IsVertexOnLine 
  (const TopoDS_Vertex& aV,
   const Standard_Real aTolV,
   const IntTools_Curve& aC, 
   const Standard_Real aTolC,
   Standard_Real& aT)
{
  Standard_Real aFirst, aLast, aDist, aTolSum;
  Standard_Integer aNbProj;
  gp_Pnt aPv; 
  
  aPv=BRep_Tool::Pnt(aV);

  const Handle(Geom_Curve)& aC3D=aC.Curve();
  
  
  aTolSum=aTolV+aTolC;
  //
  GeomAdaptor_Curve aGAC(aC3D);
  GeomAbs_CurveType aType=aGAC.GetType();
  if (aType==GeomAbs_BSplineCurve ||
      aType==GeomAbs_BezierCurve) {
    aTolSum=2.*aTolSum;
    if (aTolSum<1.e-5) {
      aTolSum=1.e-5;
    }
  }
  else {
    aTolSum=2.*aTolSum;//xft
    if(aTolSum < 1.e-6)
      aTolSum = 1.e-6;
  }
  //
  aFirst=aC3D->FirstParameter();
  aLast =aC3D->LastParameter();
  //
  // Checking extremities first
  // It is necessary to chose the closest bound to the point
  Standard_Boolean bFirstValid = Standard_False;
  Standard_Real aFirstDist = Precision::Infinite();
  //
  if (!Precision::IsInfinite(aFirst)) {
    gp_Pnt aPCFirst=aC3D->Value(aFirst);
    aFirstDist = aPv.Distance(aPCFirst);
    if (aFirstDist < aTolSum) {
      bFirstValid = Standard_True;
      aT=aFirst;
      //
      if (aFirstDist > aTolV) {
        Extrema_LocateExtPC anExt(aPv, aGAC, aFirst, 1.e-10);
        
        if(anExt.IsDone()) {
          Extrema_POnCurv aPOncurve = anExt.Point();
          aT = aPOncurve.Parameter();
          
          if((aT > (aLast + aFirst) * 0.5) ||
             (aPv.Distance(aPOncurve.Value()) > aTolSum) ||
             (aPCFirst.Distance(aPOncurve.Value()) < Precision::Confusion()))
            aT = aFirst;
        }
        else
        {
          // Local search may fail. Try to use more precise algo.
          Extrema_ExtPC anExt2(aPv, aGAC, 1.e-10);
          Standard_Real aMinDist = RealLast();
          Standard_Integer aMinIdx = -1;
          if (anExt2.IsDone()) {
            for (Standard_Integer anIdx = 1; anIdx <= anExt2.NbExt(); anIdx++)
            {
              if ( anExt2.IsMin(anIdx) &&
                   anExt2.SquareDistance(anIdx) < aMinDist )
              {
                aMinDist = anExt2.SquareDistance(anIdx);
                aMinIdx = anIdx;
              }
            }
          }
          if (aMinIdx != -1)
          {
            const Extrema_POnCurv& aPOncurve = anExt2.Point(aMinIdx);
            aT = aPOncurve.Parameter();

            if((aT > (aLast + aFirst) * 0.5) ||
              (aPv.Distance(aPOncurve.Value()) > aTolSum) ||
              (aPCFirst.Distance(aPOncurve.Value()) < Precision::Confusion()))
              aT = aFirst;
          }
        }

      }
    }
  }
  //
  if (!Precision::IsInfinite(aLast)) {
    gp_Pnt aPCLast=aC3D->Value(aLast);
    aDist=aPv.Distance(aPCLast);
    if (bFirstValid && (aFirstDist < aDist)) {
      return Standard_True;
    }
    //
    if (aDist < aTolSum) {
      aT=aLast;
      //
      if(aDist > aTolV) {
        Extrema_LocateExtPC anExt(aPv, aGAC, aLast, 1.e-10);
        
        if(anExt.IsDone()) {
          Extrema_POnCurv aPOncurve = anExt.Point();
          aT = aPOncurve.Parameter();
          
          if((aT < (aLast + aFirst) * 0.5) ||
             (aPv.Distance(aPOncurve.Value()) > aTolSum) ||
             (aPCLast.Distance(aPOncurve.Value()) < Precision::Confusion()))
            aT = aLast;
        }
        else
        {
          // Local search may fail. Try to use more precise algo.
          Extrema_ExtPC anExt2(aPv, aGAC, 1.e-10);
          Standard_Real aMinDist = RealLast();
          Standard_Integer aMinIdx = -1;
          if (anExt2.IsDone()) {
            for (Standard_Integer anIdx = 1; anIdx <= anExt2.NbExt(); anIdx++)
            {
              if ( anExt2.IsMin(anIdx) &&
                   anExt2.SquareDistance(anIdx) < aMinDist )
              {
                aMinDist = anExt2.SquareDistance(anIdx);
                aMinIdx = anIdx;
              }
            }
          }
          if (aMinIdx != -1)
          {
            const Extrema_POnCurv& aPOncurve = anExt2.Point(aMinIdx);
            aT = aPOncurve.Parameter();

            if((aT < (aLast + aFirst) * 0.5) ||
              (aPv.Distance(aPOncurve.Value()) > aTolSum) ||
              (aPCLast.Distance(aPOncurve.Value()) < Precision::Confusion()))
              aT = aLast;
          }
        }
      }
      //
      return Standard_True;
    }
  }
  else if (bFirstValid) {
    return Standard_True;
  }
  //
  GeomAPI_ProjectPointOnCurve& aProjector=ProjPT(aC3D);
  aProjector.Perform(aPv);
  
  aNbProj=aProjector.NbPoints();
  if (!aNbProj) {
    Handle(Geom_BoundedCurve) aBC=
      Handle(Geom_BoundedCurve)::DownCast(aC3D);
    if (!aBC.IsNull()) {
      gp_Pnt aPStart=aBC->StartPoint();
      gp_Pnt aPEnd  =aBC->EndPoint();
      
      aDist=aPv.Distance(aPStart);
      if (aDist < aTolSum) {
        aT=aFirst;
        return Standard_True;
      }
      
      aDist=aPv.Distance(aPEnd);
      if (aDist < aTolSum) {
        aT=aLast;
        return Standard_True;
      }
    }
    
    return Standard_False;
  }
  
  aDist=aProjector.LowerDistance();
  
  if (aDist > aTolSum) {
    return Standard_False;
  }

  aT=aProjector.LowerDistanceParameter();

  return Standard_True; 
}
//=======================================================================
//function : ProjectPointOnEdge
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_Context::ProjectPointOnEdge
  (const gp_Pnt& aP,
   const TopoDS_Edge& anEdge,
   Standard_Real& aT)
{
  Standard_Integer aNbPoints;

  GeomAPI_ProjectPointOnCurve& aProjector=ProjPC(anEdge);
  aProjector.Perform(aP);

  aNbPoints=aProjector.NbPoints();
  if (aNbPoints) {
    aT=aProjector.LowerDistanceParameter();
    return Standard_True;
  }
  return Standard_False;
}

//=======================================================================
//function : SetPOnSProjectionTolerance
//purpose  : 
//=======================================================================
void IntTools_Context::SetPOnSProjectionTolerance(const Standard_Real theValue)
{
  myPOnSTolerance = theValue;
  clearCachedPOnSProjectors();
}

//=======================================================================
//function : clearCachedPOnSProjectors
//purpose  : 
//=======================================================================
void IntTools_Context::clearCachedPOnSProjectors()
{
  for (NCollection_DataMap<TopoDS_Shape, GeomAPI_ProjectPointOnSurf*, TopTools_ShapeMapHasher>::Iterator aIt(myProjPSMap);
       aIt.More(); aIt.Next())
  {
    GeomAPI_ProjectPointOnSurf* pProjPS = aIt.Value();
    (*pProjPS).~GeomAPI_ProjectPointOnSurf();
    myAllocator->Free (pProjPS);
  }
  myProjPSMap.Clear();
}

//=======================================================================
//function : UVBounds
//purpose  : 
//=======================================================================
void IntTools_Context::UVBounds(const TopoDS_Face& theFace,
                                Standard_Real& UMin,
                                Standard_Real& UMax,
                                Standard_Real& VMin,
                                Standard_Real& VMax)
{
  const BRepAdaptor_Surface& aBAS = SurfaceAdaptor(theFace);
  UMin = aBAS.FirstUParameter();
  UMax = aBAS.LastUParameter ();
  VMin = aBAS.FirstVParameter();
  VMax = aBAS.LastVParameter ();
}