diff --git a/src/BOPAlgo/BOPAlgo_Builder_2.cxx b/src/BOPAlgo/BOPAlgo_Builder_2.cxx index b17734d7ad..a19d2e0329 100644 --- a/src/BOPAlgo/BOPAlgo_Builder_2.cxx +++ b/src/BOPAlgo/BOPAlgo_Builder_2.cxx @@ -45,6 +45,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -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()) { // // diff --git a/src/BOPTools/BOPTools_AlgoTools2D.cxx b/src/BOPTools/BOPTools_AlgoTools2D.cxx index 9af14eae11..90bd350ddf 100644 --- a/src/BOPTools/BOPTools_AlgoTools2D.cxx +++ b/src/BOPTools/BOPTools_AlgoTools2D.cxx @@ -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 diff --git a/src/BOPTools/BOPTools_AlgoTools2D.hxx b/src/BOPTools/BOPTools_AlgoTools2D.hxx index eb3f602dec..7af73538f5 100644 --- a/src/BOPTools/BOPTools_AlgoTools2D.hxx +++ b/src/BOPTools/BOPTools_AlgoTools2D.hxx @@ -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: diff --git a/src/GeomLib/GeomLib.cxx b/src/GeomLib/GeomLib.cxx index 972dfb81d2..c19096bbfc 100644 --- a/src/GeomLib/GeomLib.cxx +++ b/src/GeomLib/GeomLib.cxx @@ -135,6 +135,12 @@ #include #include #include +// +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; +} diff --git a/src/GeomLib/GeomLib.hxx b/src/GeomLib/GeomLib.hxx index 2eb9a89968..b51028d2fc 100644 --- a/src/GeomLib/GeomLib.hxx +++ b/src/GeomLib/GeomLib.hxx @@ -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 index 0000000000..7e312db431 --- /dev/null +++ b/tests/bugs/modalg_6/bug27035 @@ -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