// File:	BOP_WireSplitter.cxx
// Created:	Mon Apr  9 11:00:15 2001
// Author:	Peter KURNEV
//		<pkv@irinox>


#include <BOP_WireSplitter.ixx>

#include <gp_Pnt2d.hxx>
#include <gp_Vec2d.hxx>

#include <Geom_Curve.hxx>
#include <Geom2d_Curve.hxx>

#include <TopoDS.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopAbs_Orientation.hxx>

#include <BRep_Tool.hxx>

#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>

#include <TColgp_SequenceOfPnt2d.hxx>

#include <TopTools_SequenceOfShape.hxx>
#include <TopTools_ListOfShape.hxx>
#include <TopTools_ListIteratorOfListOfShape.hxx>
#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
#include <TopTools_IndexedMapOfShape.hxx>

#include <BOPTColStd_ListOfListOfShape.hxx>
#include <BOPTColStd_ListIteratorOfListOfListOfShape.hxx>

#include <BOPTools_Tools2D.hxx>

#include <BOP_EdgeInfo.hxx>
#include <BOP_ListOfEdgeInfo.hxx>
#include <BOP_ListIteratorOfListOfEdgeInfo.hxx>
#include <BOP_IndexedDataMapOfVertexListEdgeInfo.hxx>


#include <BRepAdaptor_Surface.hxx>
#include <GeomAdaptor_Surface.hxx>
#include <BRepAdaptor_Curve2d.hxx>
#include <TColStd_SequenceOfReal.hxx>
#include <Precision.hxx>

static
  void Path (const GeomAdaptor_Surface& aGAS,
	     const TopoDS_Face& myFace,
	     const TopoDS_Vertex& aVa,
	     const TopoDS_Edge& aEOuta,
	     BOP_EdgeInfo& anEdgeInfo,
	     TopTools_SequenceOfShape& aLS,
	     TopTools_SequenceOfShape& aVertVa,
	     TColgp_SequenceOfPnt2d& aCoordVa,
	     BOPTColStd_ListOfListOfShape& myShapes,
	     BOP_IndexedDataMapOfVertexListEdgeInfo& mySmartMap);


static
  Standard_Real Angle (const gp_Dir2d& aDir2D);


static
  void GetNextVertex(const TopoDS_Vertex& aV,
		     const TopoDS_Edge& aE,
		     TopoDS_Vertex& aV1);
static
  Standard_Real ClockWiseAngle(const Standard_Real aAngleIn,
			       const Standard_Real aAngleOut);

static
  Standard_Real AngleIn(const TopoDS_Edge& aEIn,
			const BOP_ListOfEdgeInfo& aLEInfo);

static
  Standard_Real Angle2D (const TopoDS_Vertex& aV,
			 const TopoDS_Edge& anEdge,
			 const TopoDS_Face& myFace,
			 const GeomAdaptor_Surface& aGAS,
			 const Standard_Boolean aFlag);
static
  gp_Pnt2d Coord2d (const TopoDS_Vertex& aV1,
		    const TopoDS_Edge& aE1,
		    const TopoDS_Face& aF);
static
  gp_Pnt2d Coord2dVf (const TopoDS_Edge& aE,
		      const TopoDS_Face& aF);
static
  Standard_Real Tolerance2D (const TopoDS_Vertex& aV,
			    const GeomAdaptor_Surface& aGAS);	

static
Standard_Real UTolerance2D (const TopoDS_Vertex& aV,
			   const GeomAdaptor_Surface& aGAS);
static
Standard_Real VTolerance2D (const TopoDS_Vertex& aV,
			   const GeomAdaptor_Surface& aGAS);

static
Standard_Boolean RecomputeAngles(const BOP_ListOfEdgeInfo& aLEInfo, 
				 const TopoDS_Face&        theFace, 
				 const gp_Pnt2d&           thePb, 
				 const TopoDS_Vertex&      theVb,
				 const GeomAdaptor_Surface& theGAS,
				 const TopoDS_Edge&        theEOuta, 
				 const Standard_Boolean&   bHasClosed,
				 const Standard_Real&      theTol2D,
				 TColStd_SequenceOfReal&   theRecomputedAngles);
//
static
  Standard_Integer NbWaysOut(const BOP_ListOfEdgeInfo& );
//

//=======================================================================
// function: BOP_WireSplitter::BOP_WireSplitter
// purpose: 
//=======================================================================
  BOP_WireSplitter::BOP_WireSplitter()
:
  myIsDone(Standard_False),
  myNothingToDo(Standard_False)
{
}
//=======================================================================
// function: SetFace
// purpose: 
//=======================================================================
  void BOP_WireSplitter::SetFace(const TopoDS_Face& aFace)
{
  myFace=aFace;
}
//=======================================================================
// function: Face
// purpose: 
//=======================================================================
  const TopoDS_Face& BOP_WireSplitter::Face()const
{
  return myFace;
}

//=======================================================================
// function: IsNothingToDo
// purpose: 
//=======================================================================
  Standard_Boolean BOP_WireSplitter::IsNothingToDo()const
{
  return myNothingToDo;
}

//=======================================================================
// function: IsDone
// purpose: 
//=======================================================================
  Standard_Boolean BOP_WireSplitter::IsDone()const
{
  return myIsDone;
}

//=======================================================================
// function: Shapes
// purpose: 
//=======================================================================
  const BOPTColStd_ListOfListOfShape& BOP_WireSplitter::Shapes()const
{
  return myShapes;
}
//=======================================================================
// function: DoWithFace
// purpose: 
//=======================================================================
  void BOP_WireSplitter::DoWithFace()
{
  myEdges.Clear();

  TopExp_Explorer anExpEdges (myFace, TopAbs_EDGE);
  for (; anExpEdges.More(); anExpEdges.Next()) {
    const TopoDS_Edge& anEdge = TopoDS::Edge(anExpEdges.Current());
    //
    if (anEdge.Orientation()==TopAbs_INTERNAL){
      continue;
    }
    //
    myEdges.Append(anEdge);
  }
  Do();
}
//=======================================================================
// function: DoWithListOfEdges
// purpose: 
//=======================================================================
  void BOP_WireSplitter::DoWithListOfEdges(const TopTools_ListOfShape& aLE)
{
  myEdges.Clear();
 
  TopTools_ListIteratorOfListOfShape anItList;

  anItList.Initialize(aLE);
  for (; anItList.More(); anItList.Next()) {
    const TopoDS_Edge& anEdge = TopoDS::Edge(anItList.Value());
    //
    if (anEdge.Orientation()==TopAbs_INTERNAL){
      continue;
    }
    //
    myEdges.Append(anEdge);
  }
  Do();
}
//=======================================================================
// function: Do
// purpose: 
//=======================================================================
  void BOP_WireSplitter::Do()
{
  myIsDone=Standard_False;
  myNothingToDo=Standard_True;

  Standard_Integer index, i, aNb, aCntIn, aCntOut;
  Standard_Boolean anIsIn;
  Standard_Real anAngle;
  
  BOP_ListOfEdgeInfo emptyInfo;
  TopTools_ListIteratorOfListOfShape anItList;
  //
  // 1.Filling mySmartMap
  mySmartMap.Clear();

  anItList.Initialize(myEdges);
  for (; anItList.More(); anItList.Next()) {
    const TopoDS_Edge& anEdge = TopoDS::Edge(anItList.Value());
    //
    if (!BOPTools_Tools2D::HasCurveOnSurface (anEdge, myFace)) {
      continue;
    }
    //
    TopExp_Explorer anExpVerts (anEdge, TopAbs_VERTEX);
    for (; anExpVerts.More(); anExpVerts.Next()) {
      const TopoDS_Shape& aVertex= anExpVerts.Current();

      index = mySmartMap.FindIndex(aVertex);
      if (!index) {
	index=mySmartMap.Add(aVertex, emptyInfo);
      }
      
      BOP_ListOfEdgeInfo& aListOfEInfo=mySmartMap(index);

      BOP_EdgeInfo aEInfo;
      aEInfo.SetEdge(anEdge);
      
      TopAbs_Orientation anOr=aVertex.Orientation();

      if (anOr==TopAbs_FORWARD) {
	aEInfo.SetInFlag(Standard_False);
      }

      else if (anOr==TopAbs_REVERSED) {
	aEInfo.SetInFlag(Standard_True);
      }

      aListOfEInfo.Append(aEInfo);
    }
  }
  //
  aNb=mySmartMap.Extent();
  //
  // 2. myNothingToDo 
  myNothingToDo=Standard_True;
  
  for (i=1; i<=aNb; i++) {
    aCntIn=0;
    aCntOut=0;
    const BOP_ListOfEdgeInfo& aLEInfo= mySmartMap(i);
    BOP_ListIteratorOfListOfEdgeInfo anIt(aLEInfo);
    for (; anIt.More(); anIt.Next()) {
      const BOP_EdgeInfo& anEdgeInfo=anIt.Value();
      anIsIn=anEdgeInfo.IsIn();
      if (anIsIn) {
	aCntIn++;
      }
      else {
	aCntOut++;
      }
    }
    if (aCntIn!=1 || aCntOut!=1) {
      myNothingToDo=Standard_False;
      break;
    }
  }
  //
  // Each vertex has one edge In and one - Out. Good. But it is not enought
  // to consider that nothing to do with this. We must check edges on TShape
  // coinsidence. If there are such edges there is something to do with.
  // 
  if (myNothingToDo) {
    Standard_Integer aNbE, aNbMapEE;
    TopTools_IndexedDataMapOfShapeListOfShape aMapEE;
    aNbE=myEdges.Extent();
    
    anItList.Initialize(myEdges);
    for (; anItList.More(); anItList.Next()) {
      const TopoDS_Shape& aE = anItList.Value();
      
      if (!aMapEE.Contains(aE)) {
	TopTools_ListOfShape aLEx;
	aLEx.Append(aE);
	aMapEE.Add(aE, aLEx);
      }
      else {
	TopTools_ListOfShape& aLEx=aMapEE.ChangeFromKey(aE);
	aLEx.Append(aE);
      }
    }
    
    Standard_Boolean bFlag;
    bFlag=Standard_True;
    aNbMapEE=aMapEE.Extent();
    for (i=1; i<=aNbMapEE; i++) {
      const TopTools_ListOfShape& aLEx=aMapEE(i);
      aNbE=aLEx.Extent();
      if (aNbE==1) {
	// usual case
	continue;
      }
      else if (aNbE==2){
	const TopoDS_Shape& aE1=aLEx.First();
	const TopoDS_Shape& aE2=aLEx.Last();
	if (aE1.IsSame(aE2)) {
	  bFlag=Standard_False;
	  break;
	}
      }
      else {
	bFlag=Standard_False;
	break;
      }
    }
    myNothingToDo=myNothingToDo && bFlag;
  }
  // 
  //
  if (myNothingToDo) {
    myIsDone=Standard_True;
    return;
  }
  //
  // 3. Angles in mySmartMap
  BRepAdaptor_Surface aBAS(myFace);
  const GeomAdaptor_Surface& aGAS=aBAS.Surface();
  for (i=1; i<=aNb; i++) {
    const TopoDS_Vertex& aV=TopoDS::Vertex (mySmartMap.FindKey(i));
    const BOP_ListOfEdgeInfo& aLEInfo= mySmartMap(i);

    BOP_ListIteratorOfListOfEdgeInfo anIt(aLEInfo);
    for (; anIt.More(); anIt.Next()) {
      BOP_EdgeInfo& anEdgeInfo=anIt.Value();
      const TopoDS_Edge& aE=anEdgeInfo.Edge();
      //
      TopoDS_Vertex aVV=aV;
      //
      anIsIn=anEdgeInfo.IsIn();
      if (anIsIn) {
	//
	aVV.Orientation(TopAbs_REVERSED);
	anAngle=Angle2D (aVV, aE, myFace, aGAS, Standard_True);
      }
      // 
      else { // OUT
	//
	aVV.Orientation(TopAbs_FORWARD);
	anAngle=Angle2D (aVV, aE, myFace, aGAS, Standard_False);
      }
      anEdgeInfo.SetAngle(anAngle);
      
    }
  }
  //
  // 4. Do
  //
  Standard_Boolean anIsOut, anIsNotPassed;
  
  TopTools_SequenceOfShape aLS, aVertVa;
  TColgp_SequenceOfPnt2d aCoordVa;
  
  BOP_ListIteratorOfListOfEdgeInfo anIt;

  for (i=1; i<=aNb; i++) {
    const TopoDS_Vertex aVa=TopoDS::Vertex (mySmartMap.FindKey(i));
    const BOP_ListOfEdgeInfo& aLEInfo=mySmartMap(i);
    
    anIt.Initialize(aLEInfo);
    for (; anIt.More(); anIt.Next()) {
      BOP_EdgeInfo& anEdgeInfo=anIt.Value();
      const TopoDS_Edge& aEOuta=anEdgeInfo.Edge();
      
      anIsOut=!anEdgeInfo.IsIn();
      anIsNotPassed=!anEdgeInfo.Passed();
      
      if (anIsOut && anIsNotPassed) {
	//
	aLS.Clear();
	aVertVa.Clear();
	aCoordVa.Clear();
	//
	Path(aGAS, myFace, aVa, aEOuta, anEdgeInfo, aLS, 
	     aVertVa, aCoordVa, myShapes, mySmartMap);
      }
    }
  }
  //
  {
    Standard_Integer aNbV, aNbE;
    TopoDS_Vertex aV1, aV2;
    BOPTColStd_ListOfListOfShape aShapes;
    BOPTColStd_ListIteratorOfListOfListOfShape anItW(myShapes);
    
    for (; anItW.More(); anItW.Next()) {
      TopTools_IndexedMapOfShape aMV, aME;
      const TopTools_ListOfShape& aLE=anItW.Value();
      TopTools_ListIteratorOfListOfShape anItE(aLE);
      for (; anItE.More(); anItE.Next()) {
	const TopoDS_Edge& aE=TopoDS::Edge(anItE.Value());
	aME.Add(aE);
	TopExp::Vertices(aE, aV1, aV2);
	aMV.Add(aV1);
	aMV.Add(aV2);
      }
      aNbV=aMV.Extent();
      aNbE=aME.Extent();
      if (aNbV<=aNbE) {
	aShapes.Append(aLE);
      }
    }
    //
    myShapes.Clear();
    anItW.Initialize(aShapes);
    for (; anItW.More(); anItW.Next()) {
      const TopTools_ListOfShape& aLE=anItW.Value();
      myShapes.Append(aLE);
    }
  }
  //
  myIsDone=Standard_True;
}
//=======================================================================
// function: Path
// purpose: 
//=======================================================================
  void Path (const GeomAdaptor_Surface& aGAS,
	     const TopoDS_Face& myFace,
	     const TopoDS_Vertex& aVa,
	     const TopoDS_Edge& aEOuta,
	     BOP_EdgeInfo& anEdgeInfo,
	     TopTools_SequenceOfShape& aLS,
	     TopTools_SequenceOfShape& aVertVa,
	     TColgp_SequenceOfPnt2d& aCoordVa,
	     BOPTColStd_ListOfListOfShape& myShapes,
	     BOP_IndexedDataMapOfVertexListEdgeInfo& mySmartMap)
			       
{
  Standard_Integer i,j, aNb, aNbj;
  Standard_Real aD, aTol=1.e-7, anAngleIn, anAngleOut, anAngle, aMinAngle; 
  Standard_Real aTol2D, aTolVb, aTolVPrev;
  Standard_Boolean anIsSameV2d, anIsSameV, anIsFound, anIsOut, anIsNotPassed;
  BOP_ListIteratorOfListOfEdgeInfo anIt;
  
  TopoDS_Vertex aVb;
  TopoDS_Edge aEOutb;
  //
  // append block
  //
  // Do not escape through edge from which you enter 
  aNb=aLS.Length();
  if (aNb==1) {
    const TopoDS_Shape& anEPrev=aLS(aNb);

    if (anEPrev.IsSame(aEOuta)) {
      return;
    }
  }
  //
  //
  anEdgeInfo.SetPassed(Standard_True);
  aLS.Append(aEOuta);
  aVertVa.Append(aVa);
  
  TopoDS_Vertex pVa=aVa;
  pVa.Orientation(TopAbs_FORWARD);
  gp_Pnt2d aPa=Coord2d(pVa, aEOuta, myFace);
  aCoordVa.Append(aPa);
  
  GetNextVertex (pVa, aEOuta, aVb);

  gp_Pnt2d aPb=Coord2d(aVb, aEOuta, myFace);

  const BOP_ListOfEdgeInfo& aLEInfoVb=mySmartMap.FindFromKey(aVb);

  TopoDS_Vertex aV1, aV2;
  TopExp::Vertices(aEOuta, aV1, aV2);
  Standard_Boolean bHasClosedEdge = aV1.IsNull() || aV2.IsNull() || aV1.IsSame(aV2);
  Standard_Boolean bHasDegenerated = BRep_Tool::Degenerated(aEOuta);
  Standard_Boolean bHasSeam = BRep_Tool::IsClosed(aEOuta, myFace);
  anIt.Initialize(aLEInfoVb);
  
  for (; anIt.More(); anIt.Next()) {
    BOP_EdgeInfo& anEI=anIt.Value();
    const TopoDS_Edge& aE=anEI.Edge();
    bHasDegenerated = bHasDegenerated || BRep_Tool::Degenerated(aE);
    bHasSeam = bHasSeam || BRep_Tool::IsClosed(aE, myFace);
    aV1.Nullify();
    aV2.Nullify();
    TopExp::Vertices(aE, aV1, aV2);
    bHasClosedEdge = bHasClosedEdge || aV1.IsNull() || aV2.IsNull() || aV1.IsSame(aV2);
  }

  aNb=aLS.Length();
  if (aNb>0) {
    //
    TopTools_ListOfShape aBuf;
    for (i=aNb; i>0; i--) {
      const TopoDS_Shape& aVPrev=aVertVa(i);
      const gp_Pnt2d& aPaPrev=aCoordVa(i);
      const TopoDS_Shape& aEPrev=aLS(i);

      aBuf.Append(aEPrev);

      anIsSameV=aVPrev.IsSame(aVb);
      anIsSameV2d = Standard_False;

      if (anIsSameV) {
	anIsSameV2d = Standard_True;

	if(bHasDegenerated || bHasSeam || bHasClosedEdge) {
	  aTolVb   =BRep_Tool::Tolerance(TopoDS::Vertex(aVb));
	  aTolVPrev=BRep_Tool::Tolerance(TopoDS::Vertex(aVPrev));
	  aTol=aTolVb+aTolVPrev;
	  //
	  aTol=2.*Tolerance2D(aVb, aGAS);
	  aD=aPaPrev.Distance(aPb);
	  anIsSameV2d = (aD < aTol);

	  if(anIsSameV2d) {
	    Standard_Real udist = fabs(aPaPrev.X() - aPb.X());
	    Standard_Real vdist = fabs(aPaPrev.Y() - aPb.Y());
	    Standard_Real aTolU = 2. * UTolerance2D(aVb, aGAS);
	    Standard_Real aTolV = 2. * VTolerance2D(aVb, aGAS);

	    if((udist > aTolU) ||
	       (vdist > aTolV)) {
	      anIsSameV2d = Standard_False;
	    }
	  }
	}
      }

      //
      if (anIsSameV && anIsSameV2d) {
	myShapes.Append(aBuf);
	//
	TopTools_SequenceOfShape aLSt, aVertVat;
	TColgp_SequenceOfPnt2d aCoordVat;
	//
	aNbj=i-1;
	if (aNbj<1) {
	  //
	  aLS.Clear();
	  aVertVa.Clear();
	  aCoordVa.Clear();
	  //
	  return;
	}

	aVb=TopoDS::Vertex(aVertVa(i));

	for (j=1; j<=aNbj; j++) {
	  aLSt.Append(aLS(j));
	  aVertVat.Append(aVertVa(j));
	  aCoordVat.Append(aCoordVa(j));
	}
	//
	aLS.Clear();
	aVertVa.Clear();
	aCoordVa.Clear();

	aLS=aLSt;
	aVertVa=aVertVat;
	aCoordVa=aCoordVat;
	//
	break;
      }
    }
  }
  //
  aTol2D=2.*Tolerance2D(aVb, aGAS);
  //
  // anAngleIn in Vb from edge aEOuta
  const BOP_ListOfEdgeInfo& aLEInfo=mySmartMap.FindFromKey(aVb);
  //
  anAngleIn=AngleIn(aEOuta, aLEInfo);
  //
  // aEOutb
  BOP_EdgeInfo *pEdgeInfo=NULL;

  aMinAngle=100.;
  anIsFound=Standard_False;

  TColStd_SequenceOfReal aRecomputedAngles;

  Standard_Boolean bRecomputeAngle = 
    RecomputeAngles(aLEInfo, myFace, aPb, aVb, aGAS, aEOuta, 
		    (bHasDegenerated || bHasSeam || bHasClosedEdge),
		    aTol2D, aRecomputedAngles);

  Standard_Integer aCurIndexE = 0;

  anIt.Initialize(aLEInfo);
  for (; anIt.More(); anIt.Next()) {
    BOP_EdgeInfo& anEI=anIt.Value();
    const TopoDS_Edge& aE=anEI.Edge();
    anIsOut=!anEI.IsIn();
    anIsNotPassed=!anEI.Passed();
    
    if (anIsOut && anIsNotPassed) {
      aCurIndexE++;
      //
      // Is there one way to go out of the vertex 
      // we have to use it only.
      Standard_Integer iCnt;
      iCnt=NbWaysOut (aLEInfo);
      //
      if (!iCnt) {
	// no way to go . (Error)
	return ;
      }
      //
      if (iCnt==1) {
	// the one and only way to go out .
	pEdgeInfo=&anEI;
	anIsFound=Standard_True;
	break;
      }
      //
      // Look for minimal angle and make the choice.
      gp_Pnt2d aP2Dx;
      //
      aP2Dx=Coord2dVf(aE, myFace);
      //
      aD=aP2Dx.Distance(aPb);
      if (aD > aTol2D){
	continue;
      }
      //
      //
      anAngleOut=anEI.Angle();
      //
      if(bRecomputeAngle) {
	if(aCurIndexE <= aRecomputedAngles.Length()) {
	  anAngleOut = aRecomputedAngles.Value(aCurIndexE);
	}
      }
      //
      anAngle=ClockWiseAngle(anAngleIn, anAngleOut);
      if (anAngle < aMinAngle) {
	aMinAngle=anAngle;
	pEdgeInfo=&anEI;
	anIsFound=Standard_True;
      }
    }
  } // for (; anIt.More(); anIt.Next()) 
  //
  if (!anIsFound) {
    // no way to go . (Error)
    return;
  }
  
  aEOutb=pEdgeInfo->Edge();
  Path (aGAS, myFace, aVb, aEOutb, *pEdgeInfo, aLS, 
	aVertVa, aCoordVa, myShapes, mySmartMap);
}
//=======================================================================
// function:  Coord2dVf
// purpose:
//=======================================================================
 gp_Pnt2d Coord2dVf (const TopoDS_Edge& aE,
		     const TopoDS_Face& aF)
{
  Standard_Real aCoord=99.;
  gp_Pnt2d aP2D1(aCoord, aCoord);

  TopExp_Explorer anExp(aE, TopAbs_VERTEX);
  for (; anExp.More(); anExp.Next()) {
    const TopoDS_Vertex& aVx=TopoDS::Vertex(anExp.Current());
    if (aVx.Orientation()==TopAbs_FORWARD) {
       aP2D1=Coord2d(aVx, aE, aF);
       return aP2D1;
    }
  }
  return aP2D1;
}
//=======================================================================
// function:  Tolerance2D
// purpose:
//=======================================================================
 Standard_Real Tolerance2D (const TopoDS_Vertex& aV,
			    const GeomAdaptor_Surface& aGAS)		     
{
  Standard_Real aTol2D, anUr, aVr, aTolV3D;
  GeomAbs_SurfaceType aType;
  //
  aType=aGAS.GetType();
  aTolV3D=BRep_Tool::Tolerance(aV);

  anUr=aGAS.UResolution(aTolV3D);
  aVr =aGAS.VResolution(aTolV3D);
  aTol2D=(aVr>anUr) ? aVr : anUr;
  //
  if (aType==GeomAbs_BSplineSurface||
      aType==GeomAbs_Sphere) {
    if (aTol2D < aTolV3D) {
      aTol2D=aTolV3D;
    }
  }
  //modified by NIZNHY-PKV Wed Jul  5 16:44:59 2006f
  if (aType==GeomAbs_BSplineSurface) {
    aTol2D=1.1*aTol2D;
  }
  //modified by NIZNHY-PKV Wed Jul  5 16:45:02 2006t
  //
  return aTol2D;
}

//=======================================================================
// function:  Coord2d
// purpose:
//=======================================================================
 gp_Pnt2d Coord2d (const TopoDS_Vertex& aV1,
		   const TopoDS_Edge& aE1,
		   const TopoDS_Face& aF)
{
  Standard_Real t, aFirst, aLast, aToler;
  Handle(Geom2d_Curve) aC2D;

  t=BRep_Tool::Parameter (aV1, aE1, aF);
  BOPTools_Tools2D::CurveOnSurface 
    (aE1, aF, aC2D, aFirst, aLast, aToler, Standard_True);
  
  gp_Pnt2d aP2D1;
  aC2D->D0 (t, aP2D1);

  return aP2D1;
}
//=======================================================================
// function:  AngleIn
// purpose:
//=======================================================================
 Standard_Real AngleIn(const TopoDS_Edge& aEIn,
		       const BOP_ListOfEdgeInfo& aLEInfo)
{
  Standard_Real anAngleIn;
  Standard_Boolean anIsIn;
  BOP_ListIteratorOfListOfEdgeInfo anIt;

  anIt.Initialize(aLEInfo);
  for (; anIt.More(); anIt.Next()) {
    BOP_EdgeInfo& anEdgeInfo=anIt.Value();
    const TopoDS_Edge& aE=anEdgeInfo.Edge();
    anIsIn=anEdgeInfo.IsIn();
    //
    if (anIsIn && aE==aEIn) {
      anAngleIn=anEdgeInfo.Angle();
      return anAngleIn;
    }
  }
  anAngleIn=0.;
  return anAngleIn;
}
//=======================================================================
// function:  ClockWiseAngle
// purpose:
//=======================================================================
 Standard_Real ClockWiseAngle(const Standard_Real aAngleIn,
			      const Standard_Real aAngleOut)
{
  Standard_Real aTwoPi=Standard_PI+Standard_PI;
  Standard_Real dA, A1, A2, AIn, AOut ;

  AIn=aAngleIn;
  AOut=aAngleOut;
  if (AIn >= aTwoPi) {
    AIn=AIn-aTwoPi;
  }
  
  if (AOut >= aTwoPi) {
    AOut=AOut-aTwoPi;
  }

  A1=AIn+Standard_PI;
  
  if (A1 >= aTwoPi) {
    A1=A1-aTwoPi;
  }
  
  A2=AOut;
  
  dA=A1-A2;
  if (dA <= 0.) {
    dA=aTwoPi+dA;
  }
  //xx
  //else if (dA <= 1.e-15) {
  else if (dA <= 1.e-14) {
    dA=aTwoPi;
  }
  return dA;
}
//=======================================================================
// function: GetNextVertex
// purpose: 
//=======================================================================
 void GetNextVertex(const TopoDS_Vertex& aV,
		    const TopoDS_Edge& aE,
		    TopoDS_Vertex& aV1)
{
  TopExp_Explorer anExp(aE, TopAbs_VERTEX);
  for (; anExp.More(); anExp.Next()) {
    const TopoDS_Vertex& aVx=TopoDS::Vertex(anExp.Current());
    if (!aVx.IsEqual(aV)) {
      aV1=aVx;
      return ;
    }
  }
  aV1=aV;
}
//=======================================================================
// function: Angle2D
// purpose: 
//=======================================================================
  Standard_Real Angle2D (const TopoDS_Vertex& aV,
			 const TopoDS_Edge& anEdge,
			 const TopoDS_Face& myFace,
			 const GeomAdaptor_Surface& aGAS,
			 const Standard_Boolean aFlag)
{
  Standard_Real aFirst, aLast, aToler, dt, aTV, aTV1, anAngle;
  
  Handle(Geom2d_Curve) aC2D;
  
  BOPTools_Tools2D::CurveOnSurface (anEdge, myFace, aC2D, 
				    aFirst, aLast, aToler, Standard_True);

  aTV=BRep_Tool::Parameter (aV, anEdge, myFace);
  if (Precision::IsInfinite(aTV))
    return 0.;

  //dt=1.e-7;
  dt=Tolerance2D(aV, aGAS);
  
  if(dt > (aLast - aFirst) * 0.25) {
    // to save direction of the curve as much as it possible
    // in the case of big tolerances
    dt = (aLast - aFirst) * 0.25; 
  }
  //
  if (fabs (aTV-aFirst) < fabs(aTV - aLast)) {
    aTV1=aTV + dt;
  }
  else {
    aTV1=aTV - dt;
  }
  
  gp_Pnt2d aPV, aPV1;
  aC2D->D0 (aTV, aPV);
  aC2D->D0 (aTV1, aPV1);
  
  gp_Vec2d aV2D;
  //
  if (aFlag) {//IN
    gp_Vec2d aV2DIn(aPV1, aPV);
    //
    aV2D=aV2DIn;
  }

  else {
    gp_Vec2d aV2DOut(aPV, aPV1);
    aV2D=aV2DOut;
  }

  gp_Dir2d aDir2D(aV2D);
  anAngle=Angle(aDir2D);

  return anAngle;
}
//=======================================================================
// function: Angle
// purpose: 
//=======================================================================
Standard_Real Angle (const gp_Dir2d& aDir2D)
{
  gp_Dir2d      aRefDir(1., 0.);
  Standard_Real anAngle = aRefDir.Angle(aDir2D);

  if (anAngle < 0.)
    anAngle += Standard_PI + Standard_PI;

  return anAngle;
}
//
//=======================================================================
// function: NbWaysOut
// purpose: 
//=======================================================================
Standard_Integer NbWaysOut(const BOP_ListOfEdgeInfo& aLEInfo)
{
  Standard_Boolean bIsOut, bIsNotPassed;
  Standard_Integer iCnt=0;
  BOP_ListIteratorOfListOfEdgeInfo anIt;
  //
  anIt.Initialize(aLEInfo);
  for (; anIt.More(); anIt.Next()) {
    BOP_EdgeInfo& anEI=anIt.Value();
    //
    //const TopoDS_Edge& aE=anEI.Edge();
    bIsOut=!anEI.IsIn();
    bIsNotPassed=!anEI.Passed();
    if (bIsOut && bIsNotPassed) {
      iCnt++;
    }
  }
  return iCnt;
}


//=======================================================================
//function : UTolerance2D
//purpose  : 
//=======================================================================
Standard_Real UTolerance2D (const TopoDS_Vertex& aV,
			   const GeomAdaptor_Surface& aGAS)
{
  Standard_Real aTol2D, anUr, aTolV3D;

  aTolV3D = BRep_Tool::Tolerance(aV);

  anUr = aGAS.UResolution(aTolV3D);
  aTol2D = anUr;
  //
  return aTol2D;
}

//=======================================================================
//function : VTolerance2D
//purpose  : 
//=======================================================================
Standard_Real VTolerance2D (const TopoDS_Vertex& aV,
			   const GeomAdaptor_Surface& aGAS)
{
  Standard_Real aTol2D, anVr, aTolV3D;

  aTolV3D = BRep_Tool::Tolerance(aV);

  anVr = aGAS.VResolution(aTolV3D);
  aTol2D = anVr;
  //
  return aTol2D;
}
//=======================================================================
// function: RecomputeAngles
// purpose: 
//=======================================================================
Standard_Boolean RecomputeAngles(const BOP_ListOfEdgeInfo& aLEInfo, 
				 const TopoDS_Face&        theFace, 
				 const gp_Pnt2d&           thePb, 
				 const TopoDS_Vertex&      theVb,
				 const GeomAdaptor_Surface& theGAS,
				 const TopoDS_Edge&        theEOuta, 
				 const Standard_Boolean&   bHasClosed,
				 const Standard_Real&      theTol2D,
				 TColStd_SequenceOfReal&   theRecomputedAngles)
{
  Standard_Boolean bRecomputeAngle = Standard_False;
  BOP_ListIteratorOfListOfEdgeInfo anIt;
  anIt.Initialize(aLEInfo);

  for (; anIt.More(); anIt.Next()) {
    BOP_EdgeInfo& anEI=anIt.Value();
    const TopoDS_Edge& aE=anEI.Edge();
    Standard_Boolean anIsOut=!anEI.IsIn();
    Standard_Boolean anIsNotPassed=!anEI.Passed();
    
    if (anIsOut && anIsNotPassed) {
      theRecomputedAngles.Append(anEI.Angle());
      Standard_Integer acurindex = theRecomputedAngles.Length();

      Standard_Boolean bRecomputeAngleLocal = Standard_False;
      TopExp_Explorer anExp1(aE, TopAbs_VERTEX);

      for(; anExp1.More(); anExp1.Next()) {
	TopExp_Explorer anExp2(theEOuta, TopAbs_VERTEX);
	Standard_Boolean existsInEdge = Standard_False;

	for(; anExp2.More(); anExp2.Next()) {
	  if(anExp1.Current().IsSame(anExp2.Current())) {
	    existsInEdge = Standard_True;
	    break;
	  }
	}
	
	if(!existsInEdge) {
	  bRecomputeAngleLocal = Standard_False;
	  break;
	}
	bRecomputeAngleLocal = Standard_True;
      }
      bRecomputeAngle = bRecomputeAngle || bRecomputeAngleLocal;

      if(!bRecomputeAngle) {
	BOP_ListIteratorOfListOfEdgeInfo anIt2(aLEInfo);
	
	for(; anIt2.More(); anIt2.Next()) {
	  BOP_EdgeInfo& anEI2=anIt2.Value();
	  const TopoDS_Edge& aE2=anEI2.Edge();

	  if(aE2.IsSame(aE))
	    continue;
	  Standard_Boolean anIsOut2=!anEI2.IsIn();
	  Standard_Boolean anIsNotPassed2=!anEI2.Passed();
    
	  if (anIsOut2 && anIsNotPassed2) {
	    anExp1.Init(aE, TopAbs_VERTEX);

	    for(; anExp1.More(); anExp1.Next()) {
	      TopExp_Explorer anExp2(aE2, TopAbs_VERTEX);
	      Standard_Boolean existsInEdge = Standard_False;

	      for(; anExp2.More(); anExp2.Next()) {
		if(anExp1.Current().IsSame(anExp2.Current())) {
		  existsInEdge = Standard_True;
		  break;
		}
	      }
	
	      if(!existsInEdge) {
		bRecomputeAngleLocal = Standard_False;
		break;
	      }
	      bRecomputeAngleLocal = Standard_True;
	    }
	    bRecomputeAngle = bRecomputeAngle || bRecomputeAngleLocal;
	  }
	}
      }

      bRecomputeAngle = bRecomputeAngle || bRecomputeAngleLocal;

      if(bRecomputeAngle) {
	gp_Pnt2d aP2Dx;
	//
	aP2Dx=Coord2dVf(aE, theFace);
	Standard_Real aD = aP2Dx.Distance(thePb);

	TopoDS_Vertex aVf;
	TopExp_Explorer anExp(aE, TopAbs_VERTEX);
      
	for (; anExp.More(); anExp.Next()) {
	  const TopoDS_Vertex& aVx=TopoDS::Vertex(anExp.Current());
	  if (aVx.Orientation()==TopAbs_FORWARD) {
	    aVf = aVx;
	  }
	}
	Standard_Boolean bIgnore = Standard_False;

	if(bHasClosed || aVf.IsNull() || !aVf.IsSame(theVb)) {
	  bIgnore = (aD > theTol2D);
	}

	if(!bIgnore && (theTol2D > PI)) {
	  Standard_Real udist = fabs(aP2Dx.X() - thePb.X());
	  Standard_Real vdist = fabs(aP2Dx.Y() - thePb.Y());
	  Standard_Real aTolU = 2. * UTolerance2D(theVb, theGAS);
	  Standard_Real aTolV = 2. * VTolerance2D(theVb, theGAS);
	  
	  if((udist > aTolU) ||
	     (vdist > aTolV)) {
	    bIgnore = Standard_True;
	  }
	}

	if((aD > Precision::Confusion()) && !bIgnore) {
	  Standard_Real f1, l1;
	  Handle(Geom2d_Curve) ac1 = BRep_Tool::CurveOnSurface(aE, theFace, f1, l1);

	  Standard_Real aTV1 = BRep_Tool::Parameter (aVf, aE, theFace);
	  Standard_Real aTV12 = 0.;
	  Standard_Real dt1 = (l1 - f1) * 0.5;

	  if (fabs (aTV1-f1) < fabs(aTV1 - l1)) {
	    aTV12 = aTV1 + dt1;
	  }
	  else {
	    aTV12 = aTV1 - dt1;
	  }

	  gp_Pnt2d aPointNew = ac1->Value(aTV12);
	  gp_Vec2d aV2DOut(thePb, aPointNew);
     
	  gp_Dir2d aDir2D(aV2DOut);
	  Standard_Real anAngleOut = Angle(aDir2D);
	  theRecomputedAngles.ChangeValue(acurindex) = anAngleOut;
	}
      }
    }
  }
  return bRecomputeAngle;
}
