Boolean cut face-face wrong result

Hi,

I am trying to cut a face in another face (or shell). But the result is wrong, see the attached screenshot. I am using Open CASCADE 7.7.0.

One thing I noticed is that the face I use as a tool has small edges, and I am able to get correct results using `fixsmall tool tool 0.1`. But the 0.1 tolerance is very high, and using 0.01 or lower does not help. Don't I run the risk of altering the shape of my tool using `fixsmall` with a tolerance of `0.1`? My code must work with other shapes as well, without altering their geometry.

Is there another way to make the cut operation work? Is this a bug in Open CASCADE?

Thanks.

Attachments: 
Mikhail Sazonov's picture

The tool shape has too high tolerance of two vertices. Please see the attached picture, there spheres of vertices are shown.

Draw[17]> toler t
Tolerance MAX=1.1400113000024099 AVG=0.019912959388688423 MIN=9.9999999999999995e-08
FACE    : MAX=9.9999999999999995e-08 AVG=9.9999999999999995e-08 MIN=9.9999999999999995e-08
EDGE    : MAX=9.9999999999999995e-08 AVG=9.9999999999999995e-08 MIN=9.9999999999999995e-08
VERTEX  : MAX=1.1400113000024099 AVG=0.030000394736905588 MIN=9.9999999999999995e-08

This can be fixed with resetting tolerance with the command btolx:

Draw[36]> btolx t
Draw[37]> toler t
Tolerance MAX=1.0000000000000002e-07 AVG=9.9999999999999903e-08 MIN=9.9999999999999995e-08
FACE    : MAX=9.9999999999999995e-08 AVG=9.9999999999999995e-08 MIN=9.9999999999999995e-08
EDGE    : MAX=9.9999999999999995e-08 AVG=9.9999999999999995e-08 MIN=9.9999999999999995e-08
VERTEX  : MAX=1.0000000000000002e-07 AVG=1.0000000000000002e-07 MIN=1.0000000000000002e-07

After this BO is performed correct.

Attachments: 
jerome caucat's picture

Awesome, thank you.

Is there a C++ equivalent to `btolx`?

Do I need to reimplement `btolx`, or can I simply use `ShapeFix_ShapeTolerance` with a fixed value?

Dmitrii Pasukhin's picture

Hello.

OCCT is openSource you be able to find our a source code. But it look like that the code can be moved to own C++ class. We will look on it.

Path: occt\src\BOPTest\BOPTest_TolerCommands.cxx

//=======================================================================
//function : ProcessEdge
//purpose  : 
//=======================================================================
void ProcessEdge(const TopoDS_Edge& aE, const Standard_Real aTolTreshold)
{
  Standard_Integer i, aNb=23;
  Standard_Real aD2, aTolMax2, aT1, aT2, aT, dT;
  gp_Pnt aPC3D, aP3D;
  gp_Pnt2d aPC2D;

  //TopTools_ListIteratorOfListOfShape anIt;// Wng in Gcc 3.0
  BRep_ListIteratorOfListOfCurveRepresentation itcr;
  //
  Handle(Geom_Curve) aC3D=BRep_Tool::Curve(aE, aT1, aT2);
  if (aC3D.IsNull()) {
    return;
  }
  //
  dT=(aT2-aT1)/aNb;
  //
  Handle(BRep_TEdge)& TE = *((Handle(BRep_TEdge)*)&aE.TShape());
  const TopLoc_Location& Eloc = aE.Location();
  //
  aTolMax2=-1.e6;
  const BRep_ListOfCurveRepresentation& aLCR=TE->Curves();
  //
  itcr.Initialize(aLCR);
  for (; itcr.More(); itcr.Next()) {
    const Handle(BRep_CurveRepresentation)& cr = itcr.Value();
    const TopLoc_Location& loc = cr->Location();
    TopLoc_Location L = (Eloc * loc);//.Predivided(aV.Location());
    //
    // 3D-Curve
    if (cr->IsCurve3D()) {
      continue;
    }
    //
    // 2D-Curve
    else if (cr->IsCurveOnSurface()) {
      const Handle(Geom2d_Curve)& aC2D = cr->PCurve();
      if (aC2D.IsNull()) {
        continue;
      }
      // Surface
      const Handle(Geom_Surface)& aS=cr->Surface();
      //
      // 2D-point treatment
      for (i=0; i<=aNb; ++i) {
        aT=aT1+i*dT;
        if (i==aNb) {
          aT=aT2;
        }
        aPC3D=aC3D->Value(aT);
        aPC2D=aC2D->Value(aT);
        aS->D0(aPC2D.X(), aPC2D.Y(), aP3D);
        aP3D.Transform(L.Transformation());
        aD2=aPC3D.SquareDistance(aP3D);
        if (aD2 > aTolMax2) {
          aTolMax2=aD2;
        }
      }
    } //if (cr->IsCurveOnSurface())
  }//for (; itcr.More(); itcr.Next())

  //#########################################################
  //
  if (aTolMax2<0.){
    return;
  }
  //
  //
  aTolMax2=sqrt(aTolMax2); 
  
  //printf(" aTolMax=%15.10lf, aTolWas=%15.10lf\n", aTolMax2, aTolE);

  Standard_Real aTolSet;
  aTolSet=(aTolMax2>aTolTreshold) ? aTolMax2 : aTolTreshold;

  TE->Tolerance(aTolSet);
}

//=======================================================================
//function : ProcessVertex
//purpose  : 
//=======================================================================
void ProcessVertex(const TopoDS_Vertex& aV,
                   const TopTools_ListOfShape& aLE,
                   const TopTools_ListOfShape& aLF)
{
  Standard_Real aTol, aD2, aTolMax2, aTolE, aParam;
  gp_Pnt aPC3D;
  gp_Pnt2d aPC2D;
  TopAbs_Orientation anOrV;

  TopTools_ListIteratorOfListOfShape anIt;
  TopExp_Explorer aVExp;
  
  BRep_ListIteratorOfListOfCurveRepresentation itcr;
  //
  aTolMax2=-1.e6;
  //
  Handle(BRep_TVertex)& TV = *((Handle(BRep_TVertex)*) &aV.TShape());
  const gp_Pnt& aPV3D = TV->Pnt();
  aTol =BRep_Tool::Tolerance(aV);
  //
  anIt.Initialize(aLE);
  for (; anIt.More(); anIt.Next()) {
    const TopoDS_Edge& aE=TopoDS::Edge(anIt.Value());
    //
    Handle(BRep_TEdge)& TE = *((Handle(BRep_TEdge)*)&aE.TShape());
    const TopLoc_Location& Eloc = aE.Location();
    //
    aVExp.Init(aE, TopAbs_VERTEX);
    for (; aVExp.More(); aVExp.Next()) {
      const TopoDS_Vertex& aVx=TopoDS::Vertex(aVExp.Current());
      //
      if (!aVx.IsSame(aV)) {
        continue;
      }
      //
      anOrV=aVx.Orientation();
      if (!(anOrV==TopAbs_FORWARD || anOrV==TopAbs_REVERSED)) {
        continue;
      }
      //
      const BRep_ListOfCurveRepresentation& aLCR=TE->Curves();
      itcr.Initialize(aLCR);
      for (; itcr.More(); itcr.Next()) {
        const Handle(BRep_CurveRepresentation)& cr = itcr.Value();
        const TopLoc_Location& loc = cr->Location();
        TopLoc_Location L = (Eloc * loc).Predivided(aV.Location());
        //
        // 3D-Curve
        if (cr->IsCurve3D()) {
          const Handle(Geom_Curve)& aC3D = cr->Curve3D();
          //
          if (aC3D.IsNull()) {
            continue;
          }
          // 3D-point treatment
          aParam=BRep_Tool::Parameter(aVx, aE);
          aPC3D= aC3D->Value(aParam);
          aPC3D.Transform(L.Transformation());
          aD2=aPV3D.SquareDistance(aPC3D);
          if (aD2 > aTolMax2) {
            aTolMax2=aD2;
          }
          //
        }//if (cr->IsCurve3D())
        //
        // 2D-Curve
        else if (cr->IsCurveOnSurface()) {
          const Handle(Geom2d_Curve)& aC2D = cr->PCurve();
          if (aC2D.IsNull()) {
            continue;
          }
          // Surface
          const Handle(Geom_Surface)& aS=cr->Surface();
          //
          // 2D-point treatment
          aParam=BRep_Tool::Parameter(aVx, aE, aS, L);
          aPC2D=aC2D->Value(aParam);
          aS->D0(aPC2D.X(), aPC2D.Y(), aPC3D);
          aPC3D.Transform(L.Transformation());
          aD2=aPV3D.SquareDistance(aPC3D);
          if (aD2 > aTolMax2) {
            aTolMax2=aD2;
          }
        } //if (cr->IsCurveOnSurface())
        
      }//for (; itcr.More(); itcr.Next())
    }//for (; aVExp.More(); aVExp.Next()) 
  }//for (; anIt.More(); anIt.Next()) 
  //#########################################################
  //
  // Reducing
  if (aTolMax2<0.){
    return;
  }
  //
  aTolMax2=sqrt(aTolMax2); 
  if (aTolMax2>aTol) {
    return;
  }
  //
  anIt.Initialize(aLE);
  for (; anIt.More(); anIt.Next()) {
    const TopoDS_Edge& aE=TopoDS::Edge(anIt.Value());

    aTolE =BRep_Tool::Tolerance(aE);
    if (aTolMax2 < aTolE) {
      aTolMax2=aTolE;
    }
  }
  //
  anIt.Initialize(aLF);
  for (; anIt.More(); anIt.Next()) {
    const TopoDS_Face& aF=TopoDS::Face(anIt.Value());
    
    aTolE =BRep_Tool::Tolerance(aF);
    if (aTolMax2 < aTolE) {
      aTolMax2=aTolE;
    }
  }
  //
  if (aTolMax2>aTol) {
    return;
  }
  //
  // Update Tolerance
  TV->Tolerance(aTolMax2);
}

//=======================================================================
//function : ReduceEdgeTolerance
//purpose  : 
//=======================================================================
void ReduceEdgeTolerance (const TopoDS_Shape& aS, 
                          const Standard_Real aTolTreshold)
{
  Standard_Integer i, aNbE;
  TopTools_IndexedMapOfShape aEMap;
  //
  TopExp::MapShapes(aS, TopAbs_EDGE, aEMap);
  //
  aNbE=aEMap.Extent();
  for (i=1; i<=aNbE; i++) {
    const TopoDS_Edge& aE= TopoDS::Edge(aEMap(i));

    ProcessEdge(aE, aTolTreshold);
  } 
}

//=======================================================================
//function : ReduceFaceTolerance
//purpose  : 
//=======================================================================
void ReduceFaceTolerance (const TopoDS_Shape& aS)
{
  Standard_Integer i, j, aNbF, aNbE;
  Standard_Real aTolE, aTolx, aTolEMin;
  TopTools_IndexedMapOfShape aFMap, aEMap;
  //
  aTolEMin=1.e-7;
  //
  TopExp::MapShapes(aS, TopAbs_FACE, aFMap);
  aNbF=aFMap.Extent();
  for (i=1; i<=aNbF; i++) {
    aTolx=1.e6;
    const TopoDS_Face& aF= TopoDS::Face(aFMap(i));
    Handle(BRep_TFace)& aTF = *((Handle(BRep_TFace)*)&aF.TShape());
    //
    TopExp::MapShapes(aF, TopAbs_EDGE, aEMap);
    aNbE=aEMap.Extent();
    for (j=1; j<=aNbE; ++j) {
      const TopoDS_Edge& aE= TopoDS::Edge(aEMap(j));
      aTolE =BRep_Tool::Tolerance(aE);
      if (aTolE<aTolx) {
        aTolx=aTolE;
      }
    }
    aTolE=(aTolx>aTolEMin) ? aTolx : aTolEMin;
    aTF->Tolerance(aTolE);
  }
}

//=======================================================================
//function : ReduceVertexTolerance
//purpose  : 
//=======================================================================
void ReduceVertexTolerance (const TopoDS_Shape& aS)
{
  Standard_Integer i, aNbV;
  TopTools_IndexedDataMapOfShapeListOfShape aVEMap, aVFMap;
  
  TopExp::MapShapesAndUniqueAncestors(aS, TopAbs_VERTEX, TopAbs_EDGE, aVEMap);
  TopExp::MapShapesAndUniqueAncestors(aS, TopAbs_VERTEX, TopAbs_FACE, aVFMap);

  aNbV=aVEMap.Extent();
  for (i=1; i<=aNbV; i++) {
    const TopoDS_Vertex& aV= TopoDS::Vertex(aVEMap.FindKey(i));
    const TopTools_ListOfShape& aLE=aVEMap(i);
    const TopTools_ListOfShape& aLF=aVFMap.FindFromKey(aV);
    
    ProcessVertex(aV, aLE, aLF);
  }
}

//=======================================================================
//function : btolx
//purpose  : 
//=======================================================================
Standard_Integer btolx(Draw_Interpretor& di, 
                       Standard_Integer n,  
                       const char** a)
{
  if (n<2) {
    di << " use btolx Shape [minTol=1.e-7]\n";
    return 1;
  }

  TopoDS_Shape aS = DBRep::Get(a[1]);
  
  if (aS.IsNull()) {
    di << " Null shape is not allowed\n";
    return 1;
  }
  //
  Standard_Real aTolEMin=1.e-7;
  if (n==3) {
    aTolEMin=Draw::Atof(a[2]);
  }
  //
  // Edge Tolerances
  ReduceEdgeTolerance(aS, aTolEMin);
  //
  // Face Tolerances
  ReduceFaceTolerance(aS);
  //
  // Vertex Tolerances
  ReduceVertexTolerance(aS);
  //
  BRepLib::SameParameter(aS, 1.e-7, Standard_True);
  //
  DBRep::Set (a[1], aS);
  return 0;
}

Best regards, Dmitrii.

jerome caucat's picture

Using `ShapeFix_ShapeTolerance` is most likely going to cause some problems, so I'll indeed reimplement `btolx`.

Thank you for your help.

jerome caucat's picture

For the record, the very high tolerance is caused by the use of `ShapeFix_Face`.