0027035: General fuse algorithm loses face
authornbv <nbv@opencascade.com>
Mon, 15 Feb 2016 10:51:12 +0000 (13:51 +0300)
committerabv <abv@opencascade.com>
Sat, 20 Feb 2016 10:04:32 +0000 (13:04 +0300)
The main reason of the bug is incorrect check, if the edge is seam-edge or not.
In the fix it is determined with new methods in GeomLib class.

The bug is fixed.

Creation of test case for this fix

Small correction in the code

src/BOPAlgo/BOPAlgo_Builder_2.cxx
src/BOPTools/BOPTools_AlgoTools2D.cxx
src/BOPTools/BOPTools_AlgoTools2D.hxx
src/GeomLib/GeomLib.cxx
src/GeomLib/GeomLib.hxx
tests/bugs/modalg_6/bug27035 [new file with mode: 0644]

index b17734d..a19d2e0 100644 (file)
@@ -45,6 +45,9 @@
 #include <BOPTools_MapOfSet.hxx>
 #include <BRep_Builder.hxx>
 #include <BRep_Tool.hxx>
+#include <GeomAdaptor_Surface.hxx>
+#include <GeomLib.hxx>
+#include <Precision.hxx>
 #include <IntTools_Context.hxx>
 #include <TopExp_Explorer.hxx>
 #include <TopoDS_Compound.hxx>
@@ -284,6 +287,9 @@ void BOPAlgo_Builder::BuildSplitFaces()
     }
     //
     const TopoDS_Face& aF=(*(TopoDS_Face*)(&aSI.Shape()));
+    Standard_Boolean isUClosed = Standard_False,
+                     isVClosed = Standard_False,
+                     isChecked = Standard_False;
     //
     bHasFaceInfo=myDS->HasFaceInfo(i);
     if(!bHasFaceInfo) {
@@ -321,8 +327,6 @@ void BOPAlgo_Builder::BuildSplitFaces()
     for (; aExp.More(); aExp.Next()) {
       const TopoDS_Edge& aE=(*(TopoDS_Edge*)(&aExp.Current()));
       anOriE=aE.Orientation();
-      bIsDegenerated=BRep_Tool::Degenerated(aE);
-      bIsClosed=BRep_Tool::IsClosed(aE, aF);
       //
       if (!myImages.IsBound(aE)) {
         if (anOriE==TopAbs_INTERNAL) {
@@ -335,48 +339,71 @@ void BOPAlgo_Builder::BuildSplitFaces()
         else {
           aLE.Append(aE);
         }
+
+        continue;
       }
-      else {//else 1
-        const BOPCol_ListOfShape& aLIE=myImages.Find(aE);
-        aIt.Initialize(aLIE);
-        for (; aIt.More(); aIt.Next()) {
-          aSp=(*(TopoDS_Edge*)(&aIt.Value()));
-          if (bIsDegenerated) {
-            aSp.Orientation(anOriE);
-            aLE.Append(aSp);
-            continue;
-          }
+
+      if(!isChecked)
+      {
+        const Handle(Geom_Surface) aSurf = BRep_Tool::Surface(aF);
+        GeomLib::IsClosed(aSurf, BRep_Tool::Tolerance(aE),
+          isUClosed, isVClosed);
+
+        isChecked = Standard_True;
+      }
+
+      bIsClosed = Standard_False;
+
+      if((isUClosed || isVClosed) && BRep_Tool::IsClosed(aE, aF)) 
+      {
+
+        Standard_Boolean isUIso = Standard_False, isVIso = Standard_False;
+        BOPTools_AlgoTools2D::IsEdgeIsoline(aE, aF, isUIso, isVIso);
+
+        bIsClosed = ((isUClosed && isUIso) || (isVClosed && isVIso));
+      }
+
+      bIsDegenerated=BRep_Tool::Degenerated(aE);
+
+      const BOPCol_ListOfShape& aLIE=myImages.Find(aE);
+      aIt.Initialize(aLIE);
+      for (; aIt.More(); aIt.Next()) {
+        aSp=(*(TopoDS_Edge*)(&aIt.Value()));
+        if (bIsDegenerated) {
+          aSp.Orientation(anOriE);
+          aLE.Append(aSp);
+          continue;
+        }
+        //
+        if (anOriE==TopAbs_INTERNAL) {
+          aSp.Orientation(TopAbs_FORWARD);
+          aLE.Append(aSp);
+          aSp.Orientation(TopAbs_REVERSED);
+          aLE.Append(aSp);
+          continue;
+        }
           //
-          if (anOriE==TopAbs_INTERNAL) {
+        if (bIsClosed) {
+          if (aMFence.Add(aSp)) {
+            if (!BRep_Tool::IsClosed(aSp, aF)){
+              BOPTools_AlgoTools3D::DoSplitSEAMOnFace(aSp, aF);
+            }
+            //
             aSp.Orientation(TopAbs_FORWARD);
             aLE.Append(aSp);
             aSp.Orientation(TopAbs_REVERSED);
             aLE.Append(aSp);
-            continue;
-          }
-          //
-          if (bIsClosed) {
-            if (aMFence.Add(aSp)) {
-              if (!BRep_Tool::IsClosed(aSp, aF)){
-                BOPTools_AlgoTools3D::DoSplitSEAMOnFace(aSp, aF);
-                }
-              //
-              aSp.Orientation(TopAbs_FORWARD);
-              aLE.Append(aSp);
-              aSp.Orientation(TopAbs_REVERSED);
-              aLE.Append(aSp);
-            }// if (aMFence.Add(aSp))
-            continue;
-          }// if (bIsClosed){
-          //
-          aSp.Orientation(anOriE);
-          bToReverse=BOPTools_AlgoTools::IsSplitToReverse(aSp, aE, myContext);
-          if (bToReverse) {
-            aSp.Reverse();
-          }
-          aLE.Append(aSp);
-        }// for (; aIt.More(); aIt.Next()) {
-      }// else 1
+          }// if (aMFence.Add(aSp))
+          continue;
+        }// if (bIsClosed){
+        //
+        aSp.Orientation(anOriE);
+        bToReverse=BOPTools_AlgoTools::IsSplitToReverse(aSp, aE, myContext);
+        if (bToReverse) {
+          aSp.Reverse();
+        }
+        aLE.Append(aSp);
+      }// for (; aIt.More(); aIt.Next()) {
     }// for (; aExp.More(); aExp.Next()) {
     // 
     //
index 9af14ea..90bd350 100644 (file)
@@ -891,3 +891,39 @@ Standard_Real MaxToleranceEdge (const TopoDS_Face& aF)
   }
   return aTolMax;
 }
+
+//=======================================================================
+//function : IsEdgeIsoline
+//purpose  : 
+//=======================================================================
+void BOPTools_AlgoTools2D::IsEdgeIsoline( const TopoDS_Edge& theE,
+                                          const TopoDS_Face& theF,
+                                          Standard_Boolean& isTheUIso,
+                                          Standard_Boolean& isTheVIso)
+{
+  isTheUIso = isTheVIso = Standard_False;
+
+  gp_Vec2d aT;
+  gp_Pnt2d aP;
+  Standard_Real aFirst = 0.0, aLast = 0.0;
+  const Handle(Geom2d_Curve) aPC = BRep_Tool::CurveOnSurface(theE, theF, aFirst, aLast);
+
+  aPC->D1(0.5*(aFirst+aLast), aP, aT);
+
+  const Standard_Real aSqMagn = aT.SquareMagnitude();
+  if(aSqMagn <= gp::Resolution())
+    return;
+
+  //Normalyze aT
+  aT /= sqrt(aSqMagn);
+
+  //sin(da) ~ da, when da->0.
+  const Standard_Real aTol = Precision::Angular();
+  const gp_Vec2d aRefVDir(0.0, 1.0), aRefUDir(1.0, 0.0);
+
+  const Standard_Real aDPv = aT.CrossMagnitude(aRefVDir),
+                      aDPu = aT.CrossMagnitude(aRefUDir);
+
+  isTheUIso = (aDPv <= aTol);
+  isTheVIso = (aDPu <= aTol);
+}
\ No newline at end of file
index eb3f602..7af7353 100644 (file)
@@ -152,7 +152,20 @@ public:
   //! Returns 0 in case of success
   Standard_EXPORT static Standard_Integer AttachExistingPCurve (const TopoDS_Edge& aEold, const TopoDS_Edge& aEnew, const TopoDS_Face& aF, const Handle(IntTools_Context)& aCtx);
 
-
+  //! Checks if CurveOnSurface of theE on theF matches with isoline of theF surface.
+  //! Sets corresponding values for isTheUIso and isTheVIso variables.
+  //! ATTENTION!!!
+  //!     This method is based on comparation between direction of
+  //!   surface (which theF is based on) iso-lines and the direction
+  //!   of the edge p-curve (on theF) in middle-point of the p-curve.
+  //!     This method should be used carefully
+  //!   (e.g. BRep_Tool::IsClosed(...) together) in order to
+  //!   avoid false classification some p-curves as isoline (e.g. circle
+  //!   on a plane).
+  Standard_EXPORT static void IsEdgeIsoline(const TopoDS_Edge& theE,
+                                            const TopoDS_Face& theF,
+                                            Standard_Boolean& isTheUIso,
+                                            Standard_Boolean& isTheVIso);
 
 
 protected:
index 972dfb8..c19096b 100644 (file)
 #include <TColStd_Array2OfReal.hxx>
 #include <TColStd_HArray1OfReal.hxx>
 #include <TColStd_HArray2OfReal.hxx>
+//
+static Standard_Boolean CompareWeightPoles(const TColgp_Array1OfPnt& thePoles1, 
+                                           const TColStd_Array1OfReal* const theW1,
+                                           const TColgp_Array1OfPnt& thePoles2,
+                                           const TColStd_Array1OfReal* const theW2,
+                                           const Standard_Real theTol);
 
 //=======================================================================
 //function : ComputeLambda
@@ -2454,5 +2460,314 @@ Standard_Integer GeomLib::NormEstim(const Handle(Geom_Surface)& S,
   }
   return 3;
 }
 
+//=======================================================================
+//function : IsClosed
+//purpose  : 
+//=======================================================================
+void GeomLib::IsClosed (const Handle(Geom_Surface)& S, 
+                        const Standard_Real Tol,
+                        Standard_Boolean& isUClosed, Standard_Boolean& isVClosed) 
+{
+  isUClosed = Standard_False;
+  isVClosed = Standard_False;
+  //
+  GeomAdaptor_Surface aGAS(S);
+  GeomAbs_SurfaceType aSType = aGAS.GetType();
+  //
+  Standard_Real Tol2 = Tol * Tol;
+  switch (aSType)
+  {
+    case GeomAbs_Plane:
+    {
+      return;
+    }
+    case GeomAbs_Cylinder:
+    case GeomAbs_SurfaceOfExtrusion:
+    {
+      Standard_Real u1 = aGAS.FirstUParameter(), u2 = aGAS.LastUParameter();
+      Standard_Real v1 = aGAS.FirstVParameter();
+      if(Precision::IsInfinite(v1))
+        v1 = 0.;
+      gp_Pnt p1 = aGAS.Value(u1, v1);
+      gp_Pnt p2 = aGAS.Value(u2, v1);
+      isUClosed = p1.SquareDistance(p2) <= Tol2;
+      return;
+    }
+    case GeomAbs_Cone:
+    {
+      Standard_Real u1 = aGAS.FirstUParameter(), u2 = aGAS.LastUParameter();
+      Standard_Real v1 = aGAS.FirstVParameter(), v2 = aGAS.LastVParameter();
+      //find v with maximal distance from axis
+      if(!(Precision::IsInfinite(v1) || Precision::IsInfinite(v2)))
+      {
+        gp_Cone aCone = aGAS.Cone();
+        gp_Pnt anApex = aCone.Apex();
+        gp_Pnt P1 = aGAS.Value(u1, v1);
+        gp_Pnt P2 = aGAS.Value(u1, v2);
+        if(P2.SquareDistance(anApex) > P1.SquareDistance(anApex))
+        {
+          v1 = v2;
+        }
+      }
+      else
+      {
+        v1 = 0.;
+      }
+      gp_Pnt p1 = aGAS.Value(u1, v1);
+      gp_Pnt p2 = aGAS.Value(u2, v1);
+      isUClosed = p1.SquareDistance(p2) <= Tol2;
+      return;
+    }
+    case GeomAbs_Sphere:
+    {
+      Standard_Real u1 = aGAS.FirstUParameter(), u2 = aGAS.LastUParameter();
+      Standard_Real v1 = aGAS.FirstVParameter(), v2 = aGAS.LastVParameter();
+      //find v with maximal distance from axis
+      if(v1*v2 <= 0.)
+      {
+        v1 = 0.;
+      }
+      else
+      {
+        if(v1 < 0.)
+        {
+          v1 = v2;
+        }
+      }
+      gp_Pnt p1 = aGAS.Value(u1, v1);
+      gp_Pnt p2 = aGAS.Value(u2, v1);
+      isUClosed = p1.SquareDistance(p2) <= Tol2;
+      return;
+    }
+    case GeomAbs_Torus:
+    {
+      Standard_Real ures = aGAS.UResolution(Tol);
+      Standard_Real vres = aGAS.VResolution(Tol);
+      Standard_Real u1 = aGAS.FirstUParameter(), u2 = aGAS.LastUParameter();
+      Standard_Real v1 = aGAS.FirstVParameter(), v2 = aGAS.LastVParameter();
+      //
+      isUClosed = (u2 - u1) >= aGAS.UPeriod() - ures;
+      isVClosed = (v2 - v1) >= aGAS.VPeriod() - vres;
+      return;
+    }
+    case GeomAbs_BSplineSurface:
+    {
+      Standard_Real u1 = aGAS.FirstUParameter(), u2 = aGAS.LastUParameter();
+      Standard_Real v1 = aGAS.FirstVParameter(), v2 = aGAS.LastVParameter();
+      Handle(Geom_BSplineSurface) aBSpl = aGAS.BSpline();
+      isUClosed = GeomLib::IsBSplUClosed(aBSpl, u1, u2, Tol);
+      isVClosed = GeomLib::IsBSplVClosed(aBSpl, v1, v2, Tol);
+      return;
+    }
+    case GeomAbs_BezierSurface:
+    {
+      Standard_Real u1 = aGAS.FirstUParameter(), u2 = aGAS.LastUParameter();
+      Standard_Real v1 = aGAS.FirstVParameter(), v2 = aGAS.LastVParameter();
+      Handle(Geom_BezierSurface) aBz = aGAS.Bezier();
+      isUClosed = GeomLib::IsBzUClosed(aBz, u1, u2, Tol);
+      isVClosed = GeomLib::IsBzVClosed(aBz, v1, v2, Tol);
+      return;
+    }
+    case GeomAbs_SurfaceOfRevolution:
+    case GeomAbs_OffsetSurface:
+    case GeomAbs_OtherSurface:
+    {
+      Standard_Integer nbp = 23;
+      Standard_Real u1 = aGAS.FirstUParameter(), u2 = aGAS.LastUParameter();
+      Standard_Real v1 = aGAS.FirstVParameter(), v2 = aGAS.LastVParameter();
+      if(Precision::IsInfinite(v1))
+      {
+        v1 = Sign(1., v1);
+      }
+      if(Precision::IsInfinite(v2))
+      {
+        v2 = Sign(1., v2);
+      }
+      //
+      if(aSType == GeomAbs_OffsetSurface ||
+         aSType == GeomAbs_OtherSurface)
+      {
+        if(Precision::IsInfinite(u1))
+        {
+          u1 = Sign(1., u1);
+        }
+        if(Precision::IsInfinite(u2))
+        {
+          u2 = Sign(1., u2);
+        }
+      }
+      isUClosed = Standard_True;
+      Standard_Real dt = (v2 - v1) / (nbp - 1);
+      Standard_Real res = Max(aGAS.UResolution(Tol), Precision::PConfusion());
+      if(dt <= res)
+      {
+        nbp = RealToInt((v2 - v1) /(2.*res)) + 1;
+        nbp = Max(nbp, 2);
+        dt = (v2 - v1) / (nbp - 1);
+      }
+      Standard_Real t;
+      Standard_Integer i;
+      for(i = 0; i < nbp;  ++i)
+      {
+        t = (i == nbp-1 ? v2 : v1 + i * dt);
+        gp_Pnt p1 = aGAS.Value(u1, t);
+        gp_Pnt p2 = aGAS.Value(u2, t);
+        if(p1.SquareDistance(p2) > Tol2)
+        {
+          isUClosed = Standard_False;
+          break;
+        }
+      }
+      // 
+      nbp = 23;
+      isVClosed = Standard_True;
+      dt = (u2 - u1) / (nbp - 1);
+      res = Max(aGAS.VResolution(Tol), Precision::PConfusion());
+      if(dt <= res)
+      {
+        nbp = RealToInt((u2 - u1) /(2.*res)) + 1;
+        nbp = Max(nbp, 2);
+        dt = (u2 - u1) / (nbp - 1);
+      }
+      for(i = 0; i < nbp;  ++i)
+      {
+        t = (i == nbp-1 ? u2 : u1 + i * dt);
+        gp_Pnt p1 = aGAS.Value(t, v1);
+        gp_Pnt p2 = aGAS.Value(t, v2);
+        if(p1.SquareDistance(p2) > Tol2)
+        {
+          isVClosed = Standard_False;
+          break;
+        }
+      }
+      return;
+    }
+    default:
+    {
+      return;
+    }
+  }
+}
+
+//=======================================================================
+//function : IsBSplUClosed
+//purpose  : 
+//=======================================================================
+Standard_Boolean GeomLib::IsBSplUClosed (const Handle(Geom_BSplineSurface)& S, 
+                                         const Standard_Real U1,
+                                         const Standard_Real U2,
+                                         const Standard_Real Tol) 
+{   
+  Handle(Geom_Curve) aCUF = S->UIso( U1 );
+  Handle(Geom_Curve) aCUL = S->UIso( U2 );
+  if(aCUF.IsNull() || aCUL.IsNull())
+    return Standard_False;
+  Standard_Real Tol2 = 2.*Tol;
+  Handle(Geom_BSplineCurve) aBsF = Handle(Geom_BSplineCurve)::DownCast(aCUF);
+  Handle(Geom_BSplineCurve) aBsL = Handle(Geom_BSplineCurve)::DownCast(aCUL);
+  const TColgp_Array1OfPnt& aPF = aBsF->Poles();
+  const TColgp_Array1OfPnt& aPL = aBsL->Poles();
+  const TColStd_Array1OfReal* WF = aBsF->Weights();
+  const TColStd_Array1OfReal* WL = aBsL->Weights();
+  return CompareWeightPoles(aPF, WF, aPL, WL, Tol2);
+}
+
+//=======================================================================
+//function : IsBSplVClosed
+//purpose  : 
+//=======================================================================
+Standard_Boolean GeomLib::IsBSplVClosed (const Handle(Geom_BSplineSurface)& S, 
+                                         const Standard_Real V1,
+                                         const Standard_Real V2,
+                                         const Standard_Real Tol) 
+{
+  Handle(Geom_Curve) aCVF = S->VIso( V1 );
+  Handle(Geom_Curve) aCVL = S->VIso( V2 );
+  if(aCVF.IsNull() || aCVL.IsNull())
+    return Standard_False;
+  Standard_Real Tol2 = 2.*Tol;
+  Handle(Geom_BSplineCurve) aBsF = Handle(Geom_BSplineCurve)::DownCast(aCVF);
+  Handle(Geom_BSplineCurve) aBsL = Handle(Geom_BSplineCurve)::DownCast(aCVL);
+  const TColgp_Array1OfPnt& aPF = aBsF->Poles();
+  const TColgp_Array1OfPnt& aPL = aBsL->Poles();
+  const TColStd_Array1OfReal* WF = aBsF->Weights();
+  const TColStd_Array1OfReal* WL = aBsL->Weights();
+  return CompareWeightPoles(aPF, WF, aPL, WL, Tol2);
+}
+//=======================================================================
+//function : IsBzUClosed
+//purpose  : 
+//=======================================================================
+Standard_Boolean GeomLib::IsBzUClosed (const Handle(Geom_BezierSurface)& S, 
+                                       const Standard_Real U1,
+                                       const Standard_Real U2,
+                                       const Standard_Real Tol) 
+{   
+  Handle(Geom_Curve) aCUF = S->UIso( U1 );
+  Handle(Geom_Curve) aCUL = S->UIso( U2 );
+  if(aCUF.IsNull() || aCUL.IsNull())
+    return Standard_False;
+  Standard_Real Tol2 = 2.*Tol;
+  Handle(Geom_BezierCurve) aBzF = Handle(Geom_BezierCurve)::DownCast(aCUF);
+  Handle(Geom_BezierCurve) aBzL = Handle(Geom_BezierCurve)::DownCast(aCUL);
+  const TColgp_Array1OfPnt& aPF = aBzF->Poles();
+  const TColgp_Array1OfPnt& aPL = aBzL->Poles();
+  //
+  return CompareWeightPoles(aPF, 0, aPL, 0, Tol2);
+}
+
+//=======================================================================
+//function : IsBzVClosed
+//purpose  : 
+//=======================================================================
+Standard_Boolean GeomLib::IsBzVClosed (const Handle(Geom_BezierSurface)& S, 
+                                       const Standard_Real V1,
+                                       const Standard_Real V2,
+                                       const Standard_Real Tol) 
+{
+  Handle(Geom_Curve) aCVF = S->VIso( V1 );
+  Handle(Geom_Curve) aCVL = S->VIso( V2 );
+  if(aCVF.IsNull() || aCVL.IsNull())
+    return Standard_False;
+  Standard_Real Tol2 = 2.*Tol;
+  Handle(Geom_BezierCurve) aBzF = Handle(Geom_BezierCurve)::DownCast(aCVF);
+  Handle(Geom_BezierCurve) aBzL = Handle(Geom_BezierCurve)::DownCast(aCVL);
+  const TColgp_Array1OfPnt& aPF = aBzF->Poles();
+  const TColgp_Array1OfPnt& aPL = aBzL->Poles();
+  //
+  return CompareWeightPoles(aPF, 0, aPL, 0, Tol2);
+}
+
+//=======================================================================
+//function : CompareWeightPoles
+//purpose  : Checks if thePoles1(i)*theW1(i) is equal to thePoles2(i)*theW2(i)
+//            with tolerance theTol.
+//           It is necessary for not rational B-splines and Bezier curves
+//            to set theW1 and theW2 adresses to zero.
+//=======================================================================
+static Standard_Boolean CompareWeightPoles(const TColgp_Array1OfPnt& thePoles1, 
+                                           const TColStd_Array1OfReal* const theW1,
+                                           const TColgp_Array1OfPnt& thePoles2,
+                                           const TColStd_Array1OfReal* const theW2,
+                                           const Standard_Real theTol)
+{
+  if(thePoles1.Length() != thePoles2.Length())
+  {
+    return Standard_False;
+  }
+  //
+  Standard_Integer i = 1;
+  for( i = 1 ; i <= thePoles1.Length(); i++ )
+  {
+    const Standard_Real aW1 = (theW1 == 0) ? 1.0 : theW1->Value(i);
+    const Standard_Real aW2 = (theW2 == 0) ? 1.0 : theW2->Value(i);
+
+    gp_XYZ aPole1 = thePoles1.Value(i).XYZ() * aW1;
+    gp_XYZ aPole2 = thePoles2.Value(i).XYZ() * aW2;
+    if(!aPole1.IsEqual(aPole2, theTol))
+      return Standard_False;
+  }
+  //
+  return Standard_True;
+}
index 2eb9a89..b51028d 100644 (file)
@@ -41,6 +41,7 @@ class Geom_BoundedSurface;
 class gp_Dir;
 class Adaptor3d_Curve;
 class Geom_BSplineSurface;
+class Geom_BezierSurface;
 class Geom_Surface;
 class gp_Pnt2d;
 class GeomLib_MakeCurvefromApprox;
@@ -187,8 +188,40 @@ public:
   
   Standard_EXPORT static Standard_Integer NormEstim (const Handle(Geom_Surface)& S, const gp_Pnt2d& UV, const Standard_Real Tol, gp_Dir& N);
 
-
-
+  //! This method defines if opposite boundaries of surface
+  //! coincide with given tolerance
+  Standard_EXPORT static void IsClosed(const Handle(Geom_Surface)& S, const Standard_Real Tol,
+                                       Standard_Boolean& isUClosed, Standard_Boolean& isVClosed);
+
+  //! Returns true if the poles of U1 isoline and the poles of
+  //! U2 isoline of surface are identical according to tolerance criterion.
+  //! For rational surfaces Weights(i)*Poles(i) are checked.
+  Standard_EXPORT static Standard_Boolean IsBSplUClosed(const Handle(Geom_BSplineSurface)& S, 
+                                                        const Standard_Real U1,
+                                                        const Standard_Real U2,
+                                                        const Standard_Real Tol);
+
+  //! Returns true if the poles of V1 isoline and the poles of
+  //! V2 isoline of surface are identical according to tolerance criterion.
+  //! For rational surfaces Weights(i)*Poles(i) are checked.
+  Standard_EXPORT static Standard_Boolean IsBSplVClosed(const Handle(Geom_BSplineSurface)& S, 
+                                                        const Standard_Real V1,
+                                                        const Standard_Real V2,
+                                                        const Standard_Real Tol);
+
+  //! Returns true if the poles of U1 isoline and the poles of
+  //! U2 isoline of surface are identical according to tolerance criterion.
+  Standard_EXPORT static Standard_Boolean IsBzUClosed(const Handle(Geom_BezierSurface)& S, 
+                                                        const Standard_Real U1,
+                                                        const Standard_Real U2,
+                                                        const Standard_Real Tol);
+
+  //! Returns true if the poles of V1 isoline and the poles of
+  //! V2 isoline of surface are identical according to tolerance criterion.
+  Standard_EXPORT static Standard_Boolean IsBzVClosed(const Handle(Geom_BezierSurface)& S, 
+                                                        const Standard_Real V1,
+                                                        const Standard_Real V2,
+                                                        const Standard_Real Tol);
 
 protected:
 
diff --git a/tests/bugs/modalg_6/bug27035 b/tests/bugs/modalg_6/bug27035
new file mode 100644 (file)
index 0000000..7e312db
--- /dev/null
@@ -0,0 +1,27 @@
+puts "========"
+puts "OCC27035"
+puts "========"
+puts ""
+###########################################################################
+# General fuse algorithm loses face
+###########################################################################
+
+restore [locate_data_file bug27035_cmpd.brep] a
+
+explode a
+
+bclearobjects
+bcleartools
+
+baddobjects a_1 a_2
+
+bfillds
+bbuild result
+
+checknbshapes result -wire 2 -face 2 -t
+
+smallview 
+donly result
+fit
+
+checkview -screenshot -2d -path ${imagedir}/${test_image}.png
\ No newline at end of file