From f713b1305f6b2515efd2f20f9ffb18ed69845ab5 Mon Sep 17 00:00:00 2001 From: Pasukhin Dmitry Date: Sun, 9 Aug 2026 15:16:27 +0100 Subject: [PATCH] Modeling Algorithms - Enhance fillet and chamfer handling. Part 2 (#1449) - Detect complete coincidence between fillet and chamfer intersection curves. - Preserve orientation relationships between equivalent intersection curves. - Reuse existing restriction edges instead of constructing duplicate topology. - Merge coincident curve endpoints into shared topological vertices. - Exclude fully consumed faces and their obsolete boundary edges during reconstruction. - Mark edges collapsed to a single vertex as degenerated during construction. - Accept tangential contacts while rejecting transversal intersections. - Preserve generated and modified shape history in the resulting topology. - Add GTests for convex, concave, opposing-edge, chamfer, and history cases. --- .../TKBool/GTests/FILES.cmake | 1 + .../GTests/TopOpeBRepDS_BuildTool_Test.cxx | 43 +++ .../TopOpeBRepBuild_BuildEdges.cxx | 307 ++++++++++++++++++ .../TopOpeBRepBuild_BuildFaces.cxx | 52 ++- .../TopOpeBRepBuild_Builder.cxx | 219 ++++++++++++- .../TopOpeBRepBuild_Builder.hxx | 21 +- .../TopOpeBRepBuild/TopOpeBRepBuild_Merge.cxx | 30 +- .../TopOpeBRepBuild_fctwes.cxx | 40 ++- .../TopOpeBRepBuild_makeedges.cxx | 20 +- .../TopOpeBRepDS/TopOpeBRepDS_BuildTool.cxx | 70 ++-- .../TopOpeBRepDS/TopOpeBRepDS_Curve.cxx | 44 +++ .../TopOpeBRepDS/TopOpeBRepDS_Curve.hxx | 27 ++ .../TopOpeBRepDS_DataStructure.cxx | 94 ++++++ .../TopOpeBRepDS_DataStructure.hxx | 24 ++ .../TKFillet/ChFi3d/ChFi3d_Builder.cxx | 10 +- .../TKFillet/ChFi3d/ChFi3d_Builder_0.cxx | 135 +++++++- .../TKFillet/ChFi3d/ChFi3d_Builder_0.hxx | 21 ++ .../TKFillet/ChFi3d/ChFi3d_Builder_C1.cxx | 31 ++ .../TKFillet/ChFi3d/ChFi3d_Builder_SpKP.cxx | 152 ++++++++- .../GTests/BRepFilletAPI_MakeChamfer_Test.cxx | 20 +- .../GTests/BRepFilletAPI_MakeFillet_Test.cxx | 127 +++++++- .../TKFillet/GTests/ChFi3d_Hatching_Test.cxx | 99 ++++++ .../TKFillet/GTests/FILES.cmake | 1 + tests/blend/complex/B2 | 4 +- tests/blend/simple/Q3 | 4 +- tests/blend/simple/Q8 | 4 +- tests/blend/simple/W7 | 4 +- 27 files changed, 1523 insertions(+), 81 deletions(-) create mode 100644 src/ModelingAlgorithms/TKBool/GTests/TopOpeBRepDS_BuildTool_Test.cxx create mode 100644 src/ModelingAlgorithms/TKFillet/GTests/ChFi3d_Hatching_Test.cxx diff --git a/src/ModelingAlgorithms/TKBool/GTests/FILES.cmake b/src/ModelingAlgorithms/TKBool/GTests/FILES.cmake index 9e9735b43c..bf481407fb 100644 --- a/src/ModelingAlgorithms/TKBool/GTests/FILES.cmake +++ b/src/ModelingAlgorithms/TKBool/GTests/FILES.cmake @@ -6,4 +6,5 @@ set(OCCT_TKBool_GTests_FILES BRepAlgoAPI_Fuse_Test.cxx BRepAlgoAPI_Section_Test.cxx BRepFill_PipeShell_Test.cxx + TopOpeBRepDS_BuildTool_Test.cxx ) diff --git a/src/ModelingAlgorithms/TKBool/GTests/TopOpeBRepDS_BuildTool_Test.cxx b/src/ModelingAlgorithms/TKBool/GTests/TopOpeBRepDS_BuildTool_Test.cxx new file mode 100644 index 0000000000..5d5df27153 --- /dev/null +++ b/src/ModelingAlgorithms/TKBool/GTests/TopOpeBRepDS_BuildTool_Test.cxx @@ -0,0 +1,43 @@ +// Copyright (c) 2026 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +TEST(TopOpeBRepDS_BuildToolTest, CopyReversedPeriodicEdgePreservesRange) +{ + const occ::handle aCircle = new Geom_Circle(gp_Ax2(), 1.0); + TopoDS_Edge aSource = BRepBuilderAPI_MakeEdge(aCircle); + aSource.Reverse(); + + TopOpeBRepDS_BuildTool aBuildTool; + TopoDS_Shape aCopy; + aBuildTool.CopyEdge(aSource, aCopy); + for (TopExp_Explorer aVertexIt(aSource, TopAbs_VERTEX); aVertexIt.More(); aVertexIt.Next()) + { + aBuildTool.AddEdgeVertex(aSource, aCopy, aVertexIt.Current()); + } + + double aFirst, aLast; + BRep_Tool::Range(TopoDS::Edge(aCopy), aFirst, aLast); + EXPECT_NEAR(aFirst, aCircle->FirstParameter(), Precision::PConfusion()); + EXPECT_NEAR(aLast, aCircle->LastParameter(), Precision::PConfusion()); +} diff --git a/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_BuildEdges.cxx b/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_BuildEdges.cxx index 32de52b36b..8e0c213a20 100644 --- a/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_BuildEdges.cxx +++ b/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_BuildEdges.cxx @@ -15,9 +15,15 @@ // commercial license or contractual agreement. #include +#include +#include +#include #include #include +#include +#include #include +#include #include #include #include @@ -29,10 +35,121 @@ #include #include +#include +#include + #ifdef OCCT_DEBUG extern bool TopOpeBRepBuild_GettraceCU(); #endif +namespace +{ +struct TopOpeBRepBuild_SurfaceCurve +{ + int Index; + occ::handle PCurve; + double FirstParameter; + double LastParameter; +}; + +bool TopOpeBRepBuild_CurveRange(const TopOpeBRepDS_Curve& theCurve, + const occ::handle& thePCurve, + double& theFirst, + double& theLast) +{ + const auto isValidRange = [](const double theRangeFirst, const double theRangeLast) { + return theRangeFirst < theRangeLast && !Precision::IsInfinite(theRangeFirst) + && !Precision::IsInfinite(theRangeLast); + }; + + if (theCurve.Range(theFirst, theLast) && isValidRange(theFirst, theLast)) + { + return true; + } + if (!thePCurve.IsNull()) + { + theFirst = thePCurve->FirstParameter(); + theLast = thePCurve->LastParameter(); + if (isValidRange(theFirst, theLast)) + { + return true; + } + } + + const occ::handle& aCurve3d = theCurve.Curve(); + if (!aCurve3d.IsNull()) + { + theFirst = aCurve3d->FirstParameter(); + theLast = aCurve3d->LastParameter(); + return isValidRange(theFirst, theLast); + } + return false; +} + +bool TopOpeBRepBuild_HasCompleteCoincidence(const Geom2dInt_GInter& theIntersector, + const Geom2dAdaptor_Curve& theFirstCurve, + const Geom2dAdaptor_Curve& theSecondCurve, + bool& theIsReversed) +{ + if (theIntersector.NbSegments() != 1) + { + return false; + } + + const IntRes2d_IntersectionSegment& aSegment = theIntersector.Segment(1); + if (!aSegment.HasFirstPoint() || !aSegment.HasLastPoint()) + { + return false; + } + + const double aFirstOnFirst = aSegment.FirstPoint().ParamOnFirst(); + const double aLastOnFirst = aSegment.LastPoint().ParamOnFirst(); + const double aFirstOnSecond = aSegment.FirstPoint().ParamOnSecond(); + const double aLastOnSecond = aSegment.LastPoint().ParamOnSecond(); + const double aTolerance = Precision::PConfusion(); + const bool isComplete = + std::abs(std::min(aFirstOnFirst, aLastOnFirst) - theFirstCurve.FirstParameter()) <= aTolerance + && std::abs(std::max(aFirstOnFirst, aLastOnFirst) - theFirstCurve.LastParameter()) <= aTolerance + && std::abs(std::min(aFirstOnSecond, aLastOnSecond) - theSecondCurve.FirstParameter()) + <= aTolerance + && std::abs(std::max(aFirstOnSecond, aLastOnSecond) - theSecondCurve.LastParameter()) + <= aTolerance; + if (isComplete) + { + theIsReversed = (aLastOnFirst - aFirstOnFirst) * (aLastOnSecond - aFirstOnSecond) < 0.0; + } + return isComplete; +} + +bool TopOpeBRepBuild_FindCurveEnd(const occ::handle& theHDS, + const int theCurveIndex, + const bool theCurveStart, + int& theGeometryIndex, + bool& theIsPoint) +{ + double aFirst, aLast; + if (!TopOpeBRepBuild_CurveRange(theHDS->Curve(theCurveIndex), nullptr, aFirst, aLast)) + { + return false; + } + + const double aParameter = theCurveStart ? aFirst : aLast; + double aBestDistance = RealLast(); + for (TopOpeBRepDS_PointIterator aPointIt(theHDS->CurvePoints(theCurveIndex)); aPointIt.More(); + aPointIt.Next()) + { + const double aDistance = std::abs(aPointIt.Parameter() - aParameter); + if (aDistance < aBestDistance) + { + aBestDistance = aDistance; + theGeometryIndex = aPointIt.Current(); + theIsPoint = aPointIt.IsPoint(); + } + } + return aBestDistance <= Precision::PConfusion(); +} +} // namespace + //================================================================================================= void TopOpeBRepBuild_Builder::BuildEdges(const int iC, @@ -52,6 +169,36 @@ void TopOpeBRepBuild_Builder::BuildEdges(const int return; } + if (C.EquivalentCurve() > 0 && C.EquivalentCurve() != iC) + { + const int aReferenceCurve = C.EquivalentCurve(); + if (NewEdges(aReferenceCurve).IsEmpty()) + { + BuildEdges(aReferenceCurve, HDS); + } + ChangeNewEdges(iC) = NewEdges(aReferenceCurve); + return; + } + + if (!C.ExistingEdge().IsNull()) + { + if (!myCoincidentEdges.IsBound(C.ExistingEdge())) + { + TopoDS_Shape aCopiedEdge; + myBuildTool.CopyEdge(C.ExistingEdge(), aCopiedEdge); + for (TopExp_Explorer aVertexIt(C.ExistingEdge(), TopAbs_VERTEX); aVertexIt.More(); + aVertexIt.Next()) + { + myBuildTool.AddEdgeVertex(C.ExistingEdge(), aCopiedEdge, aVertexIt.Current()); + } + myCoincidentEdges.Bind(C.ExistingEdge(), aCopiedEdge); + } + TopoDS_Shape anEdge = myCoincidentEdges(C.ExistingEdge()); + anEdge.Orientation(TopAbs_FORWARD); + ChangeNewEdges(iC).Append(anEdge); + return; + } + TopoDS_Shape anEdge; const TopOpeBRepDS_Curve& curC = HDS->Curve(iC); myBuildTool.MakeEdge(anEdge, curC, HDS->DS()); @@ -106,8 +253,168 @@ void TopOpeBRepBuild_Builder::BuildEdges(const occ::handleChangeDS(); myNewEdges.Clear(); + myCoincidentEdges.Clear(); TopOpeBRepDS_CurveExplorer cex; + NCollection_LinearVector aSurfaceCurves; + for (int aSurfaceIndex = 1; aSurfaceIndex <= HDS->NbSurfaces(); ++aSurfaceIndex) + { + aSurfaceCurves.Clear(); + for (TopOpeBRepDS_CurveIterator aCurveIt(HDS->SurfaceCurves(aSurfaceIndex)); aCurveIt.More(); + aCurveIt.Next()) + { + const occ::handle& aPCurve = aCurveIt.PCurve(); + const TopOpeBRepDS_Curve& aCurve = HDS->Curve(aCurveIt.Current()); + if (aPCurve.IsNull() || aCurve.Curve().IsNull()) + { + continue; + } + + double aFirst, aLast; + if (TopOpeBRepBuild_CurveRange(aCurve, aPCurve, aFirst, aLast)) + { + aSurfaceCurves.Append({aCurveIt.Current(), aPCurve, aFirst, aLast}); + } + } + + for (size_t aFirstIndex = 0; aFirstIndex < aSurfaceCurves.Size(); ++aFirstIndex) + { + for (size_t aSecondIndex = aFirstIndex + 1; aSecondIndex < aSurfaceCurves.Size(); + ++aSecondIndex) + { + const TopOpeBRepBuild_SurfaceCurve& aFirst = aSurfaceCurves[aFirstIndex]; + const TopOpeBRepBuild_SurfaceCurve& aSecond = aSurfaceCurves[aSecondIndex]; + Geom2dAdaptor_Curve aFirstCurve(aFirst.PCurve, aFirst.FirstParameter, aFirst.LastParameter); + Geom2dAdaptor_Curve aSecondCurve(aSecond.PCurve, + aSecond.FirstParameter, + aSecond.LastParameter); + Geom2dInt_GInter anIntersector(aFirstCurve, + aSecondCurve, + Precision::PConfusion(), + Precision::PConfusion()); + bool isReversed = false; + if (TopOpeBRepBuild_HasCompleteCoincidence(anIntersector, + aFirstCurve, + aSecondCurve, + isReversed)) + { + BDS.MergeEquivalentCurves(aFirst.Index, aSecond.Index, isReversed); + } + } + } + } + + for (cex.Init(BDS, false); cex.More(); cex.Next()) + { + const int aCurveIndex = cex.Index(); + if (cex.Curve().EquivalentCurve() <= 0) + { + continue; + } + bool isReversed = false; + const int aRoot = BDS.FindEquivalentCurve(aCurveIndex, isReversed); + BDS.ChangeCurve(aCurveIndex).SetEquivalentCurve(aRoot, isReversed); + } + + NCollection_DataMap anEquivalentPointVertices; + for (cex.Init(BDS, false); cex.More(); cex.Next()) + { + const int aCurveIndex = cex.Index(); + for (int anEnd = 0; anEnd < 2; ++anEnd) + { + const bool isCurveStart = (anEnd == 0); + bool isReferenceStart = false; + const int aReferenceCurve = + BDS.FindEquivalentCurvePoint(aCurveIndex, isCurveStart, isReferenceStart); + if (aReferenceCurve == aCurveIndex && isReferenceStart == isCurveStart) + { + continue; + } + + const int aReference = 2 * aReferenceCurve + (isReferenceStart ? 0 : 1); + if (anEquivalentPointVertices.IsBound(aReference)) + { + continue; + } + + int aGeometryIndex = 0; + bool isPoint = false; + if (TopOpeBRepBuild_FindCurveEnd(HDS, + aReferenceCurve, + isReferenceStart, + aGeometryIndex, + isPoint)) + { + anEquivalentPointVertices.Bind(aReference, + isPoint ? NewVertex(aGeometryIndex) + : HDS->Shape(aGeometryIndex)); + } + } + } + + for (cex.Init(BDS, false); cex.More(); cex.Next()) + { + const int aCurveIndex = cex.Index(); + for (int anEnd = 0; anEnd < 2; ++anEnd) + { + const bool isCurveStart = (anEnd == 0); + bool isReferenceStart = false; + const int aReferenceCurve = + BDS.FindEquivalentCurvePoint(aCurveIndex, isCurveStart, isReferenceStart); + const int aReference = 2 * aReferenceCurve + (isReferenceStart ? 0 : 1); + if (!anEquivalentPointVertices.IsBound(aReference)) + { + continue; + } + + int aGeometryIndex = 0; + bool isPoint = false; + if (TopOpeBRepBuild_FindCurveEnd(HDS, aCurveIndex, isCurveStart, aGeometryIndex, isPoint) + && isPoint) + { + ChangeNewVertex(aGeometryIndex) = anEquivalentPointVertices(aReference); + } + } + } + + for (cex.Init(BDS, false); cex.More(); cex.Next()) + { + const int aCurveIndex = cex.Index(); + const int aReferenceCurve = cex.Curve().EquivalentCurve(); + if (aReferenceCurve <= 0 || aReferenceCurve == aCurveIndex) + { + continue; + } + + for (TopOpeBRepDS_PointIterator aPointIt(HDS->CurvePoints(aCurveIndex)); aPointIt.More(); + aPointIt.Next()) + { + if (!aPointIt.IsPoint()) + { + continue; + } + const int aPointIndex = aPointIt.Current(); + const TopOpeBRepDS_Point& aPoint = HDS->Point(aPointIndex); + for (TopOpeBRepDS_PointIterator aReferencePointIt(HDS->CurvePoints(aReferenceCurve)); + aReferencePointIt.More(); + aReferencePointIt.Next()) + { + if (!aReferencePointIt.IsPoint()) + { + continue; + } + const int aReferencePointIndex = aReferencePointIt.Current(); + const TopOpeBRepDS_Point& aReferencePoint = HDS->Point(aReferencePointIndex); + const double aTolerance = std::max(aPoint.Tolerance(), aReferencePoint.Tolerance()); + if (aPoint.Point().Distance(aReferencePoint.Point()) <= aTolerance) + { + ChangeNewVertex(aPointIndex) = NewVertex(aReferencePointIndex); + break; + } + } + } + } + int ick = 0; for (cex.Init(BDS, false); cex.More(); cex.Next()) { diff --git a/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_BuildFaces.cxx b/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_BuildFaces.cxx index 53c131cfb4..4378a3a351 100644 --- a/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_BuildFaces.cxx +++ b/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_BuildFaces.cxx @@ -16,6 +16,8 @@ #include #include +#include +#include #include #include #include @@ -53,7 +55,41 @@ void TopOpeBRepBuild_Builder::BuildFaces(const int // myBuildTool.MakeFace(aFace,HDS->Surface(iS)); // modified by NIZNHY-PKV Mon Dec 13 10:01:03 2010t // - TopOpeBRepBuild_WireEdgeSet WES(aFace, this); + TopOpeBRepBuild_WireEdgeSet WES(aFace, this); + NCollection_FlatDataMap anEquivalentCurveOrientations; + for (TopOpeBRepDS_CurveIterator aCurveIt(HDS->SurfaceCurves(iS)); aCurveIt.More(); + aCurveIt.Next()) + { + const TopOpeBRepDS_Curve& aCurve = HDS->Curve(aCurveIt.Current()); + const int aReferenceCurve = aCurve.EquivalentCurve(); + if (aReferenceCurve > 0) + { + TopAbs_Orientation anOrientation = aCurveIt.Orientation(TopAbs_IN); + if (aCurve.IsEquivalentCurveReversed()) + { + anOrientation = TopAbs::Reverse(anOrientation); + } + int anOrientationMask = anEquivalentCurveOrientations.IsBound(aReferenceCurve) + ? anEquivalentCurveOrientations(aReferenceCurve) + : 0; + if (anOrientation == TopAbs_FORWARD) + { + anOrientationMask |= 1; + } + else if (anOrientation == TopAbs_REVERSED) + { + anOrientationMask |= 2; + } + if (anEquivalentCurveOrientations.IsBound(aReferenceCurve)) + { + anEquivalentCurveOrientations.ChangeFind(aReferenceCurve) = anOrientationMask; + } + else + { + anEquivalentCurveOrientations.Bind(aReferenceCurve, anOrientationMask); + } + } + } // #ifdef OCCT_DEBUG bool tSE = TopOpeBRepBuild_GettraceSPF(); @@ -62,8 +98,14 @@ void TopOpeBRepBuild_Builder::BuildFaces(const int TopOpeBRepDS_CurveIterator SCurves(HDS->SurfaceCurves(iS)); for (; SCurves.More(); SCurves.Next()) { - int iC = SCurves.Current(); - const TopOpeBRepDS_Curve& CDS = HDS->Curve(iC); + int iC = SCurves.Current(); + const TopOpeBRepDS_Curve& CDS = HDS->Curve(iC); + const int aReferenceCurve = CDS.EquivalentCurve(); + if (aReferenceCurve > 0 && anEquivalentCurveOrientations.IsBound(aReferenceCurve) + && anEquivalentCurveOrientations(aReferenceCurve) == 3) + { + continue; + } #ifdef OCCT_DEBUG if (tSE) std::cout << std::endl << "BuildFaces : C " << iC << " on S " << iS << std::endl; @@ -82,6 +124,10 @@ void TopOpeBRepBuild_Builder::BuildFaces(const int } // modified by NIZNHY-PKV Mon Dec 13 10:09:43 2010f TopAbs_Orientation ori = SCurves.Orientation(TopAbs_IN); + if (CDS.IsEquivalentCurveReversed()) + { + ori = TopAbs::Reverse(ori); + } myBuildTool.Orientation(anEdge, ori); const occ::handle& PC = SCurves.PCurve(); myBuildTool.PCurve(aFace, anEdge, CDS, PC); diff --git a/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_Builder.cxx b/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_Builder.cxx index a8c5c95b2b..58af6e9541 100644 --- a/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_Builder.cxx +++ b/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_Builder.cxx @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -36,10 +37,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -75,6 +78,49 @@ static thread_local int STATIC_SOLIDINDEX = 0; //================================================================================================= +static TopoDS_Shape SubstituteCoincidentEdges( + const TopoDS_Shape& theShape, + const NCollection_DataMap& theEdges, + const TopOpeBRepDS_BuildTool& theBuildTool) +{ + if (theShape.ShapeType() != TopAbs_FACE && theShape.ShapeType() != TopAbs_WIRE) + { + return theShape; + } + + TopoDS_Shape aCopy = theShape.EmptyCopied(); + bool isModified = false; + for (TopoDS_Iterator anIt(theShape, false, false); anIt.More(); anIt.Next()) + { + TopoDS_Shape aSubShape = anIt.Value(); + if (aSubShape.ShapeType() == TopAbs_EDGE && theEdges.IsBound(aSubShape)) + { + const TopAbs_Orientation anOrientation = aSubShape.Orientation(); + aSubShape = theEdges(aSubShape); + aSubShape.Orientation(anOrientation); + isModified = true; + } + else if (aSubShape.ShapeType() == TopAbs_WIRE) + { + TopoDS_Shape aNewWire = SubstituteCoincidentEdges(aSubShape, theEdges, theBuildTool); + isModified = isModified || !aNewWire.IsSame(aSubShape); + aSubShape = aNewWire; + } + + if (theShape.ShapeType() == TopAbs_FACE) + { + theBuildTool.AddFaceWire(aCopy, aSubShape); + } + else + { + theBuildTool.AddWireEdge(aCopy, aSubShape); + } + } + return isModified ? aCopy : theShape; +} + +//================================================================================================= + TopOpeBRepBuild_Builder::TopOpeBRepBuild_Builder(const TopOpeBRepDS_BuildTool& BT) : myBuildTool(BT), mySectionDone(false), @@ -114,6 +160,95 @@ occ::handle TopOpeBRepBuild_Builder::DataStructure( //================================================================================================= +static void FillEquivalentCurveOrientationMasks( + const TopoDS_Shape& theFace, + const TopAbs_State theState, + const bool theReverse, + const occ::handle& theDataStructure, + NCollection_FlatDataMap& theOrientationMasks) +{ + for (TopOpeBRepDS_CurveIterator aCurveIt(theDataStructure->FaceCurves(theFace)); aCurveIt.More(); + aCurveIt.Next()) + { + const int aReferenceCurve = theDataStructure->Curve(aCurveIt.Current()).EquivalentCurve(); + if (aReferenceCurve <= 0) + { + continue; + } + + TopAbs_Orientation anOrientation = + TopOpeBRepBuild_Builder::Orient(aCurveIt.Orientation(theState), theReverse); + if (theDataStructure->Curve(aCurveIt.Current()).IsEquivalentCurveReversed()) + { + anOrientation = TopAbs::Reverse(anOrientation); + } + int anOrientationMask = + theOrientationMasks.IsBound(aReferenceCurve) ? theOrientationMasks(aReferenceCurve) : 0; + if (anOrientation == TopAbs_FORWARD) + { + anOrientationMask |= 1; + } + else if (anOrientation == TopAbs_REVERSED) + { + anOrientationMask |= 2; + } + if (theOrientationMasks.IsBound(aReferenceCurve)) + { + theOrientationMasks.ChangeFind(aReferenceCurve) = anOrientationMask; + } + else + { + theOrientationMasks.Bind(aReferenceCurve, anOrientationMask); + } + } +} + +//================================================================================================= + +static bool IsConsumedByCoincidentCurves( + const TopoDS_Shape& theFace, + const occ::handle& theDataStructure) +{ + for (int aStateIndex = 0; aStateIndex < 2; ++aStateIndex) + { + const TopAbs_State aState = aStateIndex == 0 ? TopAbs_IN : TopAbs_OUT; + NCollection_FlatDataMap anOrientationMasks; + FillEquivalentCurveOrientationMasks(theFace, + aState, + false, + theDataStructure, + anOrientationMasks); + for (NCollection_FlatDataMap::Iterator anIt(anOrientationMasks); anIt.More(); + anIt.Next()) + { + if (anIt.Value() == 3) + { + return true; + } + } + } + return false; +} + +//================================================================================================= + +static void RegisterConsumedFaceEdges( + const TopoDS_Shape& theFace, + const NCollection_DataMap& theRestrictions, + NCollection_DataMap& theConsumedEdges) +{ + for (TopExp_Explorer anEdgeIt(theFace, TopAbs_EDGE); anEdgeIt.More(); anEdgeIt.Next()) + { + const TopoDS_Shape& anEdge = anEdgeIt.Current(); + if (!theRestrictions.IsBound(anEdge) && !theConsumedEdges.IsBound(anEdge)) + { + theConsumedEdges.Bind(anEdge, true); + } + } +} + +//================================================================================================= + void TopOpeBRepBuild_Builder::Perform(const occ::handle& HDS) { #ifdef OCCT_DEBUG @@ -124,6 +259,28 @@ void TopOpeBRepBuild_Builder::Perform(const occ::handle aReusedRestrictionEdges; + TopOpeBRepDS_CurveExplorer aCurveIt; + for (aCurveIt.Init(HDS->DS(), false); aCurveIt.More(); aCurveIt.Next()) + { + const TopoDS_Edge& aRestriction = aCurveIt.Curve().ExistingEdge(); + if (!aRestriction.IsNull() && !aReusedRestrictionEdges.IsBound(aRestriction)) + { + aReusedRestrictionEdges.Bind(aRestriction, true); + } + } + for (int aShapeIndex = 1; aShapeIndex <= HDS->NbShapes(); ++aShapeIndex) + { + const TopoDS_Shape& aShape = HDS->Shape(aShapeIndex, false); + if (!aShape.IsNull() && aShape.ShapeType() == TopAbs_FACE + && IsConsumedByCoincidentCurves(aShape, HDS)) + { + myConsumedFaces.Bind(aShape, true); + RegisterConsumedFaceEdges(aShape, aReusedRestrictionEdges, myConsumedFaceEdges); + } + } BuildFaces(HDS); myIsKPart = 0; InitSection(); @@ -153,6 +310,13 @@ void TopOpeBRepBuild_Builder::AddIntersectionEdges(TopoDS_Shape& aFa const bool RevOri1, TopOpeBRepBuild_ShapeSet& WES) const { + NCollection_FlatDataMap aConsumedCurveGroups; + FillEquivalentCurveOrientationMasks(aFace, + ToBuild1, + RevOri1, + myDataStructure, + aConsumedCurveGroups); + TopoDS_Shape anEdge; TopOpeBRepDS_CurveIterator FCurves = myDataStructure->FaceCurves(aFace); for (; FCurves.More(); FCurves.Next()) @@ -162,8 +326,33 @@ void TopOpeBRepBuild_Builder::AddIntersectionEdges(TopoDS_Shape& aFa for (NCollection_List::Iterator Iti(LnewE); Iti.More(); Iti.Next()) { anEdge = Iti.Value(); + const int aReferenceCurve = myDataStructure->Curve(iC).EquivalentCurve(); + if (aReferenceCurve > 0 && aConsumedCurveGroups.IsBound(aReferenceCurve) + && aConsumedCurveGroups(aReferenceCurve) == 3) + { + continue; + } + const TopoDS_Edge& anExistingEdge = myDataStructure->Curve(iC).ExistingEdge(); + bool isBoundaryEdge = false; + for (TopExp_Explorer anEdgeIt(aFace, TopAbs_EDGE); anEdgeIt.More(); anEdgeIt.Next()) + { + if (anEdge.IsSame(anEdgeIt.Current()) + || (!anExistingEdge.IsNull() && anExistingEdge.IsSame(anEdgeIt.Current()))) + { + isBoundaryEdge = true; + break; + } + } + if (isBoundaryEdge) + { + continue; + } TopAbs_Orientation ori = FCurves.Orientation(ToBuild1); TopAbs_Orientation newori = Orient(ori, RevOri1); + if (myDataStructure->Curve(iC).IsEquivalentCurveReversed()) + { + newori = TopAbs::Reverse(newori); + } if (newori == TopAbs_EXTERNAL) { @@ -172,7 +361,7 @@ void TopOpeBRepBuild_Builder::AddIntersectionEdges(TopoDS_Shape& aFa myBuildTool.Orientation(anEdge, newori); const occ::handle& PC = FCurves.PCurve(); - myBuildTool.PCurve(aFace, anEdge, PC); + myBuildTool.PCurve(aFace, anEdge, myDataStructure->Curve(iC), PC); WES.AddStartElement(anEdge); } } @@ -1270,7 +1459,10 @@ void TopOpeBRepBuild_Builder::SplitFace1(const TopoDS_Shape& Foriented, // Build the new faces // ------------------- NCollection_List& FaceList = ChangeMerged(Fforward, ToBuild1); - MakeFaces(Fforward, FBU, FaceList); + if (!myConsumedFaces.IsBound(Fforward)) + { + MakeFaces(Fforward, FBU, FaceList); + } // connect new faces as faces built on LF1 faces // -------------------------------------------------------- @@ -1447,7 +1639,10 @@ void TopOpeBRepBuild_Builder::SplitFace2(const TopoDS_Shape& Foriented, // Build the new faces // ------------------- NCollection_List& FaceList1 = ChangeMerged(Fforward, ToBuild1); - MakeFaces(Fforward, FBU1, FaceList1); + if (!myConsumedFaces.IsBound(Fforward)) + { + MakeFaces(Fforward, FBU1, FaceList1); + } // connect new faces as faces built on LF1 faces // -------------------------------------------------------- @@ -1514,7 +1709,10 @@ void TopOpeBRepBuild_Builder::SplitFace2(const TopoDS_Shape& Foriented, // Build the new faces // ------------------- NCollection_List& FaceList2 = ChangeMerged(Fforward, ToBuild2); - MakeFaces(Fforward, FBU2, FaceList2); + if (!myConsumedFaces.IsBound(Fforward)) + { + MakeFaces(Fforward, FBU2, FaceList2); + } // connect new faces as faces built on LF2 faces // -------------------------------------------------------- @@ -1723,6 +1921,8 @@ void TopOpeBRepBuild_Builder::SplitShapes(TopOpeBRepTool_ShapeExplorer& Ex, for (; Ex.More(); Ex.Next()) { aShape = Ex.Current(); + const bool isConsumedFaceEdge = + aShape.ShapeType() == TopAbs_EDGE && myConsumedFaceEdges.IsBound(aShape); // compute new orientation to give to the new shapes newori = Orient(myBuildTool.Orientation(aShape), RevOri); @@ -1768,7 +1968,12 @@ void TopOpeBRepBuild_Builder::SplitShapes(TopOpeBRepTool_ShapeExplorer& Ex, //----------------------- IFV for (; It.More(); It.Next()) { + if (isConsumedFaceEdge) + { + continue; + } newShape = It.Value(); + newShape = SubstituteCoincidentEdges(newShape, myCoincidentEdges, myBuildTool); myBuildTool.Orientation(newShape, newori); #ifdef OCCT_DEBUG // TopAbs_ShapeEnum tns = TopType(newShape); @@ -1861,8 +2066,9 @@ void TopOpeBRepBuild_Builder::SplitShapes(TopOpeBRepTool_ShapeExplorer& Ex, } } } - if (add) + if (add && !isConsumedFaceEdge) { + aShape = SubstituteCoincidentEdges(aShape, myCoincidentEdges, myBuildTool); myBuildTool.Orientation(aShape, newori); aSet.AddElement(aShape); } @@ -1932,7 +2138,8 @@ void TopOpeBRepBuild_Builder::FillShape(const TopoDS_Shape& S1 bool keep = KeepShape(aSubShape, LS2, ToBuild1); if (keep) { - newori = Orient(myBuildTool.Orientation(aSubShape), RevOri); + newori = Orient(myBuildTool.Orientation(aSubShape), RevOri); + aSubShape = SubstituteCoincidentEdges(aSubShape, myCoincidentEdges, myBuildTool); myBuildTool.Orientation(aSubShape, newori); aSet.AddShape(aSubShape); } diff --git a/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_Builder.hxx b/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_Builder.hxx index 44bace80d1..220b824667 100644 --- a/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_Builder.hxx +++ b/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_Builder.hxx @@ -857,15 +857,18 @@ protected: const NCollection_DataMap& mlf, const TopAbs_State state); - TopAbs_State myState1; - TopAbs_State myState2; - TopoDS_Shape myShape1; - TopoDS_Shape myShape2; - occ::handle myDataStructure; - TopOpeBRepDS_BuildTool myBuildTool; - occ::handle> myNewVertices; - NCollection_DataMap> myNewEdges; - occ::handle>> myNewFaces; + TopAbs_State myState1; + TopAbs_State myState2; + TopoDS_Shape myShape1; + TopoDS_Shape myShape2; + occ::handle myDataStructure; + TopOpeBRepDS_BuildTool myBuildTool; + occ::handle> myNewVertices; + NCollection_DataMap> myNewEdges; + NCollection_DataMap myCoincidentEdges; + NCollection_DataMap myConsumedFaces; + NCollection_DataMap myConsumedFaceEdges; + occ::handle>> myNewFaces; NCollection_DataMap mySplitIN; NCollection_DataMap diff --git a/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_Merge.cxx b/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_Merge.cxx index f55ad88363..ff053e4e11 100644 --- a/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_Merge.cxx +++ b/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_Merge.cxx @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,9 @@ #include #include #include +#include + +#include // #include #ifdef OCCT_DEBUG @@ -540,7 +544,13 @@ void TopOpeBRepBuild_Builder::MakeEdges(const TopoDS_Shape& anEdge, myBuildTool.CopyEdge(anEdge, newEdge); - bool hasvertex = false; + bool hasvertex = false; + bool hasForwardVertex = false; + bool hasReversedVertex = false; + double aForwardParameter = 0.0; + double aReversedParameter = 0.0; + TopoDS_Shape aForwardVertex; + TopoDS_Shape aReversedVertex; for (EDBU.InitVertex(); EDBU.MoreVertex(); EDBU.NextVertex()) { TopoDS_Shape V = EDBU.Vertex(); @@ -593,6 +603,18 @@ void TopOpeBRepBuild_Builder::MakeEdges(const TopoDS_Shape& anEdge, { hasvertex = true; double parV = EDBU.Parameter(); + if (Vori == TopAbs_FORWARD) + { + hasForwardVertex = true; + aForwardVertex = V; + aForwardParameter = parV; + } + else if (Vori == TopAbs_REVERSED) + { + hasReversedVertex = true; + aReversedVertex = V; + aReversedParameter = parV; + } myBuildTool.AddEdgeVertex(newEdge, V); myBuildTool.Parameter(newEdge, V, parV); } @@ -616,6 +638,12 @@ void TopOpeBRepBuild_Builder::MakeEdges(const TopoDS_Shape& anEdge, if (hasvertex) { + if (hasForwardVertex && hasReversedVertex && aForwardVertex.IsSame(aReversedVertex) + && std::abs(aForwardParameter - aReversedParameter) <= Precision::PConfusion()) + { + BRep_Builder aBuilder; + aBuilder.Degenerated(TopoDS::Edge(newEdge), true); + } L.Append(newEdge); } } // loop on EDBU edges diff --git a/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_fctwes.cxx b/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_fctwes.cxx index 25640b1599..ed6a66ba78 100644 --- a/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_fctwes.cxx +++ b/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_fctwes.cxx @@ -14,6 +14,7 @@ // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. +#include #include #include #include @@ -21,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -196,7 +198,23 @@ void TopOpeBRepBuild_Builder::GFillCurveTopologyWES(const TopOpeBRepDS_CurveIter return; } - int iG = FCit.Current(); + int iG = FCit.Current(); + if (myDataStructure->Curve(iG).IsEquivalentCurveReversed()) + { + neworiE = TopAbs::Reverse(neworiE); + } + const int anEquivalentCurve = myDataStructure->Curve(iG).EquivalentCurve(); + const TopoDS_Edge& aRestrictionEdge = myDataStructure->Curve(iG).ExistingEdge(); + if (!aRestrictionEdge.IsNull()) + { + for (TopExp_Explorer anEdgeIt(WESF, TopAbs_EDGE); anEdgeIt.More(); anEdgeIt.Next()) + { + if (aRestrictionEdge.IsSame(anEdgeIt.Current())) + { + return; + } + } + } const NCollection_List& LnewE = NewEdges(iG); NCollection_List::Iterator Iti(LnewE); for (; Iti.More(); Iti.Next()) @@ -204,6 +222,24 @@ void TopOpeBRepBuild_Builder::GFillCurveTopologyWES(const TopOpeBRepDS_CurveIter TopoDS_Shape EE = Iti.Value(); TopoDS_Edge& E = TopoDS::Edge(EE); + if (anEquivalentCurve > 0) + { + bool isAlreadyAdded = false; + for (NCollection_List::Iterator aStartIt(WES.StartElements()); aStartIt.More(); + aStartIt.Next()) + { + if (E.IsSame(aStartIt.Value())) + { + isAlreadyAdded = true; + break; + } + } + if (isAlreadyAdded) + { + continue; + } + } + // modified by NIZHNY-MZV Fri Mar 17 12:51:03 2000 if (BRep_Tool::Degenerated(E)) { @@ -221,7 +257,7 @@ void TopOpeBRepBuild_Builder::GFillCurveTopologyWES(const TopOpeBRepDS_CurveIter // modified by NIZHNY-MZV Mon Mar 27 15:24:39 2000 if (!EhasPConFTF) { - myBuildTool.PCurve(FTF, E, PC); + myBuildTool.PCurve(FTF, E, myDataStructure->Curve(iG), PC); } bool EhasPConWESF = FC2D_HasCurveOnSurface(E, WESF); diff --git a/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_makeedges.cxx b/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_makeedges.cxx index 9b97bb8140..a6f84dd95b 100644 --- a/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_makeedges.cxx +++ b/src/ModelingAlgorithms/TKBool/TopOpeBRepBuild/TopOpeBRepBuild_makeedges.cxx @@ -15,6 +15,8 @@ // commercial license or contractual agreement. #include +#include +#include #include #include #include @@ -27,6 +29,8 @@ #include #include +#include + #ifdef OCCT_DEBUG extern void debfillp(const int i); @@ -117,7 +121,9 @@ void TopOpeBRepBuild_Builder::GEDBUMakeEdges(const TopoDS_Shape& EF, myBuildTool.CopyEdge(EF, newEdge); - int nVF = 0, nVR = 0; // nb vertex FORWARD,REVERSED + int nVF = 0, nVR = 0; // nb vertex FORWARD,REVERSED + double aForwardParameter = 0.0; + double aReversedParameter = 0.0; TopoDS_Shape VF, VR; // gestion du bit Closed VF.Nullify(); @@ -180,7 +186,8 @@ void TopOpeBRepBuild_Builder::GEDBUMakeEdges(const TopoDS_Shape& EF, nVF++; if (nVF == 1) { - VF = V; + VF = V; + aForwardParameter = EDBU.Parameter(); } } if (Vori == TopAbs_REVERSED) @@ -188,7 +195,8 @@ void TopOpeBRepBuild_Builder::GEDBUMakeEdges(const TopoDS_Shape& EF, nVR++; if (nVR == 1) { - VR = V; + VR = V; + aReversedParameter = EDBU.Parameter(); } } if (oriV == TopAbs_INTERNAL) @@ -205,6 +213,12 @@ void TopOpeBRepBuild_Builder::GEDBUMakeEdges(const TopoDS_Shape& EF, bool addedge = (nVF == 1 && nVR == 1); if (addedge) { + if (VF.IsSame(VR) + && std::abs(aForwardParameter - aReversedParameter) <= Precision::PConfusion()) + { + BRep_Builder aBuilder; + aBuilder.Degenerated(TopoDS::Edge(newEdge), true); + } if (tosplit) { NCollection_List loe; diff --git a/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_BuildTool.cxx b/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_BuildTool.cxx index 809e12c5d1..54a8e0e54c 100644 --- a/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_BuildTool.cxx +++ b/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_BuildTool.cxx @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -903,10 +905,16 @@ void TopOpeBRepDS_BuildTool::AddEdgeVertex(const TopoDS_Shape& Ein, TopoDS_Shape& Eou, const TopoDS_Shape& V) const { - myBuilder.Add(Eou, V); TopoDS_Edge e1 = TopoDS::Edge(Ein); TopoDS_Edge e2 = TopoDS::Edge(Eou); TopoDS_Vertex v1 = TopoDS::Vertex(V); + if (e1.Orientation() == TopAbs_REVERSED) + { + v1.Reverse(); + } + e1.Orientation(TopAbs_FORWARD); + e2.Orientation(TopAbs_FORWARD); + myBuilder.Add(e2, v1); myBuilder.Transfert(e1, e2, v1, v1); } @@ -1250,9 +1258,9 @@ void TopOpeBRepDS_BuildTool::PCurve(TopoDS_Shape& F, TopoDS_Face FF = TopoDS::Face(F); TopoDS_Edge EE = TopoDS::Edge(E); - const occ::handle& PCT = PC; - double CDSmin, CDSmax; - bool rangedef = CDS.Range(CDSmin, CDSmax); + occ::handle PCT = PC; + double CDSmin, CDSmax; + bool rangedef = CDS.Range(CDSmin, CDSmax); TopLoc_Location L; double Cf, Cl; @@ -1260,27 +1268,43 @@ void TopOpeBRepDS_BuildTool::PCurve(TopoDS_Shape& F, if (!C.IsNull()) { - bool deca = (std::abs(Cf - CDSmin) > Precision::PConfusion()); - occ::handle line2d = occ::down_cast(PCT); - bool isline2d = !line2d.IsNull(); - bool tran = (rangedef && deca && C->IsPeriodic() && isline2d); - if (tran) + if (rangedef && (CDS.IsExistingEdgeReversed() || CDS.IsEquivalentCurveReversed())) { - TopLoc_Location Loc; - const occ::handle Surf = BRep_Tool::Surface(FF, Loc); - bool isUperio = Surf->IsUPeriodic(); - bool isVperio = Surf->IsVPeriodic(); - gp_Dir2d dir2d = line2d->Direction(); - double delta; - if (isUperio && dir2d.IsParallel(gp::DX2d(), Precision::Angular())) + occ::handle aReversedPCurve = + new Geom2d_TrimmedCurve(PCT, CDSmin, CDSmax); + aReversedPCurve->Reverse(); + GeomLib::SameRange(Precision::PConfusion(), + aReversedPCurve, + aReversedPCurve->FirstParameter(), + aReversedPCurve->LastParameter(), + Cf, + Cl, + PCT); + } + else + { + bool deca = (std::abs(Cf - CDSmin) > Precision::PConfusion()); + occ::handle line2d = occ::down_cast(PCT); + bool isline2d = !line2d.IsNull(); + bool tran = (rangedef && deca && C->IsPeriodic() && isline2d); + if (tran) { - delta = (CDSmin - Cf) * dir2d.X(); - PCT->Translate(gp_Vec2d(delta, 0.)); - } - else if (isVperio && dir2d.IsParallel(gp::DY2d(), Precision::Angular())) - { - delta = (CDSmin - Cf) * dir2d.Y(); - PCT->Translate(gp_Vec2d(0., delta)); + TopLoc_Location Loc; + const occ::handle Surf = BRep_Tool::Surface(FF, Loc); + bool isUperio = Surf->IsUPeriodic(); + bool isVperio = Surf->IsVPeriodic(); + gp_Dir2d dir2d = line2d->Direction(); + double delta; + if (isUperio && dir2d.IsParallel(gp::DX2d(), Precision::Angular())) + { + delta = (CDSmin - Cf) * dir2d.X(); + PCT->Translate(gp_Vec2d(delta, 0.)); + } + else if (isVperio && dir2d.IsParallel(gp::DY2d(), Precision::Angular())) + { + delta = (CDSmin - Cf) * dir2d.Y(); + PCT->Translate(gp_Vec2d(0., delta)); + } } } } diff --git a/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_Curve.cxx b/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_Curve.cxx index f1f446ab47..94232c69b9 100644 --- a/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_Curve.cxx +++ b/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_Curve.cxx @@ -147,6 +147,50 @@ TopoDS_Shape& TopOpeBRepDS_Curve::ChangeShape2() //================================================================================================= +void TopOpeBRepDS_Curve::SetExistingEdge(const TopoDS_Edge& theEdge, const bool theIsReversed) +{ + myExistingEdge = theEdge; + myIsExistingEdgeReversed = theIsReversed; +} + +//================================================================================================= + +const TopoDS_Edge& TopOpeBRepDS_Curve::ExistingEdge() const +{ + return myExistingEdge; +} + +//================================================================================================= + +bool TopOpeBRepDS_Curve::IsExistingEdgeReversed() const +{ + return myIsExistingEdgeReversed; +} + +//================================================================================================= + +void TopOpeBRepDS_Curve::SetEquivalentCurve(const int theCurveIndex, const bool theIsReversed) +{ + myEquivalentCurve = theCurveIndex; + myIsEquivalentCurveReversed = theIsReversed; +} + +//================================================================================================= + +int TopOpeBRepDS_Curve::EquivalentCurve() const +{ + return myEquivalentCurve; +} + +//================================================================================================= + +bool TopOpeBRepDS_Curve::IsEquivalentCurveReversed() const +{ + return myIsEquivalentCurveReversed; +} + +//================================================================================================= + occ::handle& TopOpeBRepDS_Curve::ChangeCurve() { return myCurve; diff --git a/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_Curve.hxx b/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_Curve.hxx index dea24995c5..802b45439d 100644 --- a/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_Curve.hxx +++ b/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_Curve.hxx @@ -22,6 +22,7 @@ #include #include +#include #include class Geom_Curve; class TopOpeBRepDS_Interference; @@ -111,6 +112,28 @@ public: Standard_EXPORT void ChangeDSIndex(const int I); + //! Associates this intersection curve with an existing full edge. + //! The topology builder uses the edge instead of constructing a duplicate. + //! @param[in] theEdge existing edge representing this curve + //! @param[in] theIsReversed true if edge parameters run opposite to this curve + Standard_EXPORT void SetExistingEdge(const TopoDS_Edge& theEdge, + const bool theIsReversed = false); + + Standard_EXPORT const TopoDS_Edge& ExistingEdge() const; + + //! Returns true when the existing edge has the opposite parameter direction to this curve. + Standard_EXPORT bool IsExistingEdgeReversed() const; + + //! Sets another intersection curve that represents the same complete boundary. + Standard_EXPORT void SetEquivalentCurve(const int theCurveIndex, + const bool theIsReversed = false); + + //! Returns the index of an equivalent intersection curve, or zero if there is none. + Standard_EXPORT int EquivalentCurve() const; + + //! Returns true when this curve has the opposite parameter direction to its equivalent curve. + Standard_EXPORT bool IsEquivalentCurveReversed() const; + private: occ::handle myCurve; double myFirst; @@ -125,6 +148,10 @@ private: bool myKeep; int myMother; int myDSIndex; + TopoDS_Edge myExistingEdge; + bool myIsExistingEdgeReversed = false; + int myEquivalentCurve = 0; + bool myIsEquivalentCurveReversed = false; }; #endif // _TopOpeBRepDS_Curve_HeaderFile diff --git a/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_DataStructure.cxx b/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_DataStructure.cxx index ef564cf9a6..ff16f186a6 100644 --- a/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_DataStructure.cxx +++ b/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_DataStructure.cxx @@ -28,6 +28,8 @@ #include #include +#include + //================================================================================================= TopOpeBRepDS_DataStructure::TopOpeBRepDS_DataStructure() @@ -48,6 +50,7 @@ void TopOpeBRepDS_DataStructure::Init() myNbPoints = 0; mySurfaces.Clear(); myCurves.Clear(); + myEquivalentCurvePoints.Clear(); myPoints.Clear(); myShapes.Clear(); // Begin modified by NIZHNY-MZV Tue Apr 18 16:33:26 2000 @@ -184,6 +187,61 @@ void TopOpeBRepDS_DataStructure::ChangeKeepCurve(TopOpeBRepDS_Curve& C, const bo //================================================================================================= +int TopOpeBRepDS_DataStructure::FindEquivalentCurvePoint(const int theCurve, + const bool theCurveStart, + bool& theRepresentativeStart) const +{ + int aPoint = 2 * theCurve + (theCurveStart ? 0 : 1); + while (myEquivalentCurvePoints.IsBound(aPoint) && myEquivalentCurvePoints(aPoint) != aPoint) + { + aPoint = myEquivalentCurvePoints(aPoint); + } + theRepresentativeStart = (aPoint % 2 == 0); + return aPoint / 2; +} + +//================================================================================================= + +void TopOpeBRepDS_DataStructure::MergeEquivalentCurvePoints(const int theFirstCurve, + const bool theFirstCurveStart, + const int theSecondCurve, + const bool theSecondCurveStart) +{ + bool isFirstRootStart = false; + bool isSecondRootStart = false; + const int aFirstRootCurve = + FindEquivalentCurvePoint(theFirstCurve, theFirstCurveStart, isFirstRootStart); + const int aSecondRootCurve = + FindEquivalentCurvePoint(theSecondCurve, theSecondCurveStart, isSecondRootStart); + const int aFirstRoot = 2 * aFirstRootCurve + (isFirstRootStart ? 0 : 1); + const int aSecondRoot = 2 * aSecondRootCurve + (isSecondRootStart ? 0 : 1); + if (aFirstRoot == aSecondRoot) + { + return; + } + + const int aRoot = std::min(aFirstRoot, aSecondRoot); + const int aChild = std::max(aFirstRoot, aSecondRoot); + if (myEquivalentCurvePoints.IsBound(aRoot)) + { + myEquivalentCurvePoints.ChangeFind(aRoot) = aRoot; + } + else + { + myEquivalentCurvePoints.Bind(aRoot, aRoot); + } + if (myEquivalentCurvePoints.IsBound(aChild)) + { + myEquivalentCurvePoints.ChangeFind(aChild) = aRoot; + } + else + { + myEquivalentCurvePoints.Bind(aChild, aRoot); + } +} + +//================================================================================================= + int TopOpeBRepDS_DataStructure::AddPoint(const TopOpeBRepDS_Point& PDS) { myNbPoints++; @@ -1106,6 +1164,42 @@ TopOpeBRepDS_Curve& TopOpeBRepDS_DataStructure::ChangeCurve(const int I) //================================================================================================= +int TopOpeBRepDS_DataStructure::FindEquivalentCurve(const int I, bool& theIsReversed) const +{ + int aRoot = I; + theIsReversed = false; + while (Curve(aRoot).EquivalentCurve() > 0 && Curve(aRoot).EquivalentCurve() != aRoot) + { + theIsReversed ^= Curve(aRoot).IsEquivalentCurveReversed(); + aRoot = Curve(aRoot).EquivalentCurve(); + } + return aRoot; +} + +//================================================================================================= + +void TopOpeBRepDS_DataStructure::MergeEquivalentCurves(const int theFirst, + const int theSecond, + const bool theIsReversed) +{ + bool isFirstReversed = false; + bool isSecondReversed = false; + const int aFirstRoot = FindEquivalentCurve(theFirst, isFirstReversed); + const int aSecondRoot = FindEquivalentCurve(theSecond, isSecondReversed); + if (aFirstRoot == aSecondRoot) + { + return; + } + + const int aReference = aFirstRoot < aSecondRoot ? aFirstRoot : aSecondRoot; + const int anEquivalent = aFirstRoot < aSecondRoot ? aSecondRoot : aFirstRoot; + const bool isRootReversed = isFirstReversed ^ isSecondReversed ^ theIsReversed; + ChangeCurve(aReference).SetEquivalentCurve(aReference, false); + ChangeCurve(anEquivalent).SetEquivalentCurve(aReference, isRootReversed); +} + +//================================================================================================= + const TopOpeBRepDS_Point& TopOpeBRepDS_DataStructure::Point(const int I) const { if (I < 1 || I > myNbPoints) diff --git a/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_DataStructure.hxx b/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_DataStructure.hxx index 1b720bdf3b..8304408457 100644 --- a/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_DataStructure.hxx +++ b/src/ModelingAlgorithms/TKBool/TopOpeBRepDS/TopOpeBRepDS_DataStructure.hxx @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -85,6 +86,19 @@ public: Standard_EXPORT void ChangeKeepCurve(TopOpeBRepDS_Curve& C, const bool FindKeep); + //! Records that the specified ends of two intersection curves represent + //! the same topological point. + Standard_EXPORT void MergeEquivalentCurvePoints(const int theFirstCurve, + const bool theFirstCurveStart, + const int theSecondCurve, + const bool theSecondCurveStart); + + //! Returns the representative curve end for the specified curve end. + //! The returned curve index is equal to theCurve when no equivalence is recorded. + Standard_EXPORT int FindEquivalentCurvePoint(const int theCurve, + const bool theCurveStart, + bool& theRepresentativeStart) const; + //! Insert a new point. Returns the index. Standard_EXPORT int AddPoint(const TopOpeBRepDS_Point& PDS); @@ -243,6 +257,15 @@ public: //! Returns the Curve of index . Standard_EXPORT TopOpeBRepDS_Curve& ChangeCurve(const int I); + //! Returns the representative of an equivalent-curve group and reports whether + //! the curve parameter direction is reversed relative to that representative. + Standard_EXPORT int FindEquivalentCurve(const int I, bool& theIsReversed) const; + + //! Joins two complete coincident curves into one oriented topological boundary. + Standard_EXPORT void MergeEquivalentCurves(const int theFirst, + const int theSecond, + const bool theIsReversed); + //! Returns the point of index . Standard_EXPORT const TopOpeBRepDS_Point& Point(const int I) const; @@ -321,6 +344,7 @@ private: NCollection_DataMap mySurfaces; int myNbCurves; NCollection_DataMap myCurves; + NCollection_FlatDataMap myEquivalentCurvePoints; int myNbPoints; NCollection_DataMap myPoints; NCollection_IndexedDataMap diff --git a/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder.cxx b/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder.cxx index 192f69f63c..3ae973726a 100644 --- a/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder.cxx +++ b/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder.cxx @@ -122,10 +122,16 @@ static void CompleteDS(TopOpeBRepDS_DataStructure& DStr, const TopoDS_Shape& S) // set the range on the DS Curves for (int ic = 1; ic <= DStr.NbCurves(); ic++) { - double parmin = RealLast(), parmax = RealFirst(); const NCollection_List>& LI = DStr.CurveInterferences(ic); - for (TopOpeBRepDS_PointIterator it(LI); it.More(); it.Next()) + TopOpeBRepDS_PointIterator it(LI); + if (!it.More()) + { + continue; + } + + double parmin = it.Parameter(), parmax = parmin; + for (it.Next(); it.More(); it.Next()) { double par = it.Parameter(); parmin = std::min(parmin, par); diff --git a/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_0.cxx b/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_0.cxx index 59426d226d..60ae04a798 100644 --- a/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_0.cxx +++ b/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_0.cxx @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -78,6 +79,9 @@ #include #include +#include +#include + #ifdef OCCT_DEBUG extern bool ChFi3d_GetcontextFORCEBLEND(); extern bool ChFi3d_GettraceDRAWINT(); @@ -2701,7 +2705,11 @@ void ChFi3d_FilDS(const int SolidIndex, if (ChFi3d_Contains(DStr.ShapeInterferences(Iarc1), Iarc1, Ipoin1) && (V1.TransitionOnArc() != V3.TransitionOnArc())) { - Interfp1 = ChFi3d_FilPointInDS(V1.TransitionOnArc(), Iarc1, Ipoin1, V1.ParameterOnArc()); + Interfp1 = ChFi3d_FilPointInDS(V1.TransitionOnArc(), + Iarc1, + Ipoin1, + V1.ParameterOnArc(), + V1.IsVertex()); DStr.ChangeShapeInterferences(V1.Arc()).Append(Interfp1); } } @@ -2713,7 +2721,11 @@ void ChFi3d_FilDS(const int SolidIndex, if (ChFi3d_Contains(DStr.ShapeInterferences(Iarc2), Iarc2, Ipoin2) && (V2.TransitionOnArc() != V4.TransitionOnArc())) { - Interfp2 = ChFi3d_FilPointInDS(V2.TransitionOnArc(), Iarc2, Ipoin2, V2.ParameterOnArc()); + Interfp2 = ChFi3d_FilPointInDS(V2.TransitionOnArc(), + Iarc2, + Ipoin2, + V2.ParameterOnArc(), + V2.IsVertex()); DStr.ChangeShapeInterferences(V2.Arc()).Append(Interfp2); } } @@ -3360,10 +3372,106 @@ bool ChFi3d_HasTransversalIntersection(const Geom2dInt_GInter& theIntersector) //======================================================================= +static bool ChFi3d_HasCompleteCoincidence(const Geom2dInt_GInter& theIntersector, + const Geom2dAdaptor_Curve& theFirstCurve, + const Geom2dAdaptor_Curve& theSecondCurve, + const double theTolerance, + bool* theIsReversed) +{ + if (theIntersector.NbSegments() != 1) + { + return false; + } + + const IntRes2d_IntersectionSegment& aSegment = theIntersector.Segment(1); + if (!aSegment.HasFirstPoint() || !aSegment.HasLastPoint()) + { + return false; + } + + const double aFirstOnFirst = aSegment.FirstPoint().ParamOnFirst(); + const double aLastOnFirst = aSegment.LastPoint().ParamOnFirst(); + const double aFirstOnSecond = aSegment.FirstPoint().ParamOnSecond(); + const double aLastOnSecond = aSegment.LastPoint().ParamOnSecond(); + const bool isComplete = + std::abs(std::min(aFirstOnFirst, aLastOnFirst) - theFirstCurve.FirstParameter()) <= theTolerance + && std::abs(std::max(aFirstOnFirst, aLastOnFirst) - theFirstCurve.LastParameter()) + <= theTolerance + && std::abs(std::min(aFirstOnSecond, aLastOnSecond) - theSecondCurve.FirstParameter()) + <= theTolerance + && std::abs(std::max(aFirstOnSecond, aLastOnSecond) - theSecondCurve.LastParameter()) + <= theTolerance; + if (isComplete && theIsReversed != nullptr) + { + *theIsReversed = (aLastOnFirst - aFirstOnFirst) * (aLastOnSecond - aFirstOnSecond) < 0.0; + } + return isComplete; +} + +//======================================================================= + +bool ChFi3d_HasCommonEndpoint(const Geom2dInt_GInter& theIntersector, + const Geom2dAdaptor_Curve& theFirstCurve, + const Geom2dAdaptor_Curve& theSecondCurve, + const double theTolerance, + bool& theFirstCurveStart, + bool& theSecondCurveStart) +{ + const auto isEndpoint = [theTolerance](const double theParameter, + const Geom2dAdaptor_Curve& theCurve, + bool& theCurveStart) { + if (std::abs(theParameter - theCurve.FirstParameter()) <= theTolerance) + { + theCurveStart = true; + return true; + } + if (std::abs(theParameter - theCurve.LastParameter()) <= theTolerance) + { + theCurveStart = false; + return true; + } + return false; + }; + + for (int anIndex = 1; anIndex <= theIntersector.NbPoints(); ++anIndex) + { + const IntRes2d_IntersectionPoint& aPoint = theIntersector.Point(anIndex); + if (isEndpoint(aPoint.ParamOnFirst(), theFirstCurve, theFirstCurveStart) + && isEndpoint(aPoint.ParamOnSecond(), theSecondCurve, theSecondCurveStart)) + { + return true; + } + } + + for (int anIndex = 1; anIndex <= theIntersector.NbSegments(); ++anIndex) + { + const IntRes2d_IntersectionSegment& aSegment = theIntersector.Segment(anIndex); + if (!aSegment.HasFirstPoint() || !aSegment.HasLastPoint()) + { + continue; + } + const IntRes2d_IntersectionPoint& aFirstPoint = aSegment.FirstPoint(); + const IntRes2d_IntersectionPoint& aLastPoint = aSegment.LastPoint(); + if (std::abs(aFirstPoint.ParamOnFirst() - aLastPoint.ParamOnFirst()) > theTolerance + || std::abs(aFirstPoint.ParamOnSecond() - aLastPoint.ParamOnSecond()) > theTolerance) + { + continue; + } + if (isEndpoint(aFirstPoint.ParamOnFirst(), theFirstCurve, theFirstCurveStart) + && isEndpoint(aFirstPoint.ParamOnSecond(), theSecondCurve, theSecondCurveStart)) + { + return true; + } + } + return false; +} + +//======================================================================= + void ChFi3d_StripeEdgeInter(const occ::handle& theStripe1, const occ::handle& theStripe2, - TopOpeBRepDS_DataStructure& /*DStr*/, - const double tol2d) + TopOpeBRepDS_DataStructure& DStr, + const double tol2d) { // Do not check the stripeshaving common corner points for (int iSur1 = 1; iSur1 <= 2; iSur1++) @@ -3444,10 +3552,29 @@ void ChFi3d_StripeEdgeInter(const occ::handle& theStripe1, aFI2.FirstParameter(), aFI2.LastParameter()); anIntersector.Perform(aPCurve1, aPCurve2, tol2d, Precision::PConfusion()); + bool isReversed = false; + if (ChFi3d_HasCompleteCoincidence(anIntersector, aPCurve1, aPCurve2, tol2d, &isReversed)) + { + DStr.MergeEquivalentCurves(aFI1.LineIndex(), aFI2.LineIndex(), isReversed); + } if (ChFi3d_HasTransversalIntersection(anIntersector)) { throw StdFail_NotDone("StripeEdgeInter : fillets have too big radiuses"); } + bool isFirstCurveStart = false; + bool isSecondCurveStart = false; + if (ChFi3d_HasCommonEndpoint(anIntersector, + aPCurve1, + aPCurve2, + tol2d, + isFirstCurveStart, + isSecondCurveStart)) + { + DStr.MergeEquivalentCurvePoints(aFI1.LineIndex(), + isFirstCurveStart, + aFI2.LineIndex(), + isSecondCurveStart); + } } } } diff --git a/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_0.hxx b/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_0.hxx index 79f0bcff36..4f5c80d49d 100644 --- a/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_0.hxx +++ b/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_0.hxx @@ -51,8 +51,14 @@ #include #include +class Geom2dAdaptor_Curve; +class Geom2dHatch_Hatcher; class Geom2dInt_GInter; +//! Computes hatching domains using coincident segments only when no interior domain exists. +Standard_EXPORT bool ChFi3d_ComputeHatchingDomains(Geom2dHatch_Hatcher& theHatcher, + const int theHatchingIndex); + #ifdef OCCT_DEBUG #include extern OSD_Chronometer simul, elspine, chemine; @@ -391,6 +397,21 @@ void ChFi3d_StripeEdgeInter(const occ::handle& theStripe1, //! @return true if at least one intersection point crosses either curve bool ChFi3d_HasTransversalIntersection(const Geom2dInt_GInter& theIntersector); +//! Checks whether a non-transversal contact joins two curve ends. +//! @param[in] theIntersector intersection result to examine +//! @param[in] theFirstCurve first intersected curve +//! @param[in] theSecondCurve second intersected curve +//! @param[in] theTolerance parameter tolerance used to recognize curve ends +//! @param[out] theFirstCurveStart true if the common end is the start of the first curve +//! @param[out] theSecondCurveStart true if the common end is the start of the second curve +//! @return true if a common endpoint is found +bool ChFi3d_HasCommonEndpoint(const Geom2dInt_GInter& theIntersector, + const Geom2dAdaptor_Curve& theFirstCurve, + const Geom2dAdaptor_Curve& theSecondCurve, + const double theTolerance, + bool& theFirstCurveStart, + bool& theSecondCurveStart); + int ChFi3d_IndexOfSurfData(const TopoDS_Vertex& V1, const occ::handle& CD, int& sens); diff --git a/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_C1.cxx b/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_C1.cxx index eda33d7b66..7e34460d2f 100644 --- a/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_C1.cxx +++ b/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_C1.cxx @@ -1171,6 +1171,23 @@ void ChFi3d_Builder::PerformOneCorner(const int Index, const bool thePrepareOnSa { throw StdFail_NotDone("OneCorner : fillets have too big radiuses"); } + bool isCorkStart = false; + bool isOtherStart = false; + if (ChFi3d_HasCommonEndpoint(anIntersector, + aCorkPCurve, + anOtherPCurve, + tol2d, + isCorkStart, + isOtherStart)) + { + const int anOtherCurveIndex = IShape == aData->IndexOfS1() + ? aData->InterferenceOnS1().LineIndex() + : aData->InterferenceOnS2().LineIndex(); + if (anOtherCurveIndex > 0) + { + DStr.MergeEquivalentCurvePoints(ICurve, isCorkStart, anOtherCurveIndex, isOtherStart); + } + } } } NCollection_List>::Iterator anIter( @@ -1200,6 +1217,20 @@ void ChFi3d_Builder::PerformOneCorner(const int Index, const bool thePrepareOnSa { throw StdFail_NotDone("OneCorner : fillets have too big radiuses"); } + bool isCorkStart = false; + bool isOtherStart = false; + if (ChFi3d_HasCommonEndpoint(anIntersector, + aCorkPCurve, + anOtherPCurve, + tol2d, + isCorkStart, + isOtherStart)) + { + DStr.MergeEquivalentCurvePoints(ICurve, + isCorkStart, + anOtherIntrf->Geometry(), + isOtherStart); + } } // 31/01/02 akm ^^^ DStr.ChangeShapeInterferences(IShape).Append(Interfc); diff --git a/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_SpKP.cxx b/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_SpKP.cxx index a3ee64bed2..1d4ba53159 100644 --- a/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_SpKP.cxx +++ b/src/ModelingAlgorithms/TKFillet/ChFi3d/ChFi3d_Builder_SpKP.cxx @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,9 @@ #include #include +#include +#include + #ifdef OCCT_DEBUG extern bool ChFi3d_GettraceDRAWFIL(); extern void ChFi3d_CheckSurfData(const TopOpeBRepDS_DataStructure& DStr, @@ -741,6 +745,132 @@ static void FillSD(TopOpeBRepDS_DataStructure& DSt } } +//================================================================================================= + +struct ChFi3d_CoincidentDomainEdge +{ + TopoDS_Edge Edge; + bool IsReversed = false; +}; + +//================================================================================================= + +static ChFi3d_CoincidentDomainEdge CoincidentDomainEdge( + const NCollection_DataMap>& theElements, + const HatchGen_Domain& theDomain) +{ + if (!theDomain.HasFirstPoint() || !theDomain.HasSecondPoint()) + { + return {}; + } + + const HatchGen_PointOnHatching& aFirst = theDomain.FirstPoint(); + const HatchGen_PointOnHatching& aSecond = theDomain.SecondPoint(); + if (!aFirst.SegmentBeginning() || !aSecond.SegmentEnd()) + { + return {}; + } + + for (int i = 1; i <= aFirst.NbPoints(); ++i) + { + const HatchGen_PointOnElement& aFirstOnElement = aFirst.Point(i); + if (aFirstOnElement.Position() == TopAbs_INTERNAL) + { + continue; + } + for (int j = 1; j <= aSecond.NbPoints(); ++j) + { + const HatchGen_PointOnElement& aSecondOnElement = aSecond.Point(j); + if (aFirstOnElement.Index() != aSecondOnElement.Index() + || aSecondOnElement.Position() == TopAbs_INTERNAL + || aFirstOnElement.Position() == aSecondOnElement.Position()) + { + continue; + } + + const int anElementIndex = aFirstOnElement.Index(); + if (!theElements.IsBound(anElementIndex)) + { + continue; + } + occ::handle anElement = + occ::down_cast(theElements(anElementIndex)); + if (!anElement.IsNull()) + { + const double aHatchingDelta = aSecond.Parameter() - aFirst.Parameter(); + const double anElementDelta = aSecondOnElement.Parameter() - aFirstOnElement.Parameter(); + return {anElement->Edge(), aHatchingDelta * anElementDelta < 0.0}; + } + } + } + return {}; +} + +//================================================================================================= + +static void SetCoincidentDomainEdge( + TopOpeBRepDS_DataStructure& theDS, + const ChFiDS_FaceInterference& theInterference, + const NCollection_DataMap>& theElements, + const HatchGen_Domain& theDomain, + const bool theWholeDomain) +{ + if (!theWholeDomain) + { + return; + } + const ChFi3d_CoincidentDomainEdge aCoincidentEdge = CoincidentDomainEdge(theElements, theDomain); + if (!aCoincidentEdge.Edge.IsNull()) + { + theDS.ChangeCurve(theInterference.LineIndex()) + .SetExistingEdge(aCoincidentEdge.Edge, aCoincidentEdge.IsReversed); + } +} + +//================================================================================================= + +bool ChFi3d_ComputeHatchingDomains(Geom2dHatch_Hatcher& theHatcher, const int theHatchingIndex) +{ + theHatcher.ComputeDomains(theHatchingIndex); + if (!theHatcher.IsDone(theHatchingIndex) || theHatcher.NbDomains(theHatchingIndex) != 0) + { + return theHatcher.IsDone(theHatchingIndex); + } + + int aSegmentDepth = 0; + double aSegmentStart = 0.0; + bool hasSegment = false; + for (int aPointIndex = 1; aPointIndex <= theHatcher.NbPoints(theHatchingIndex); ++aPointIndex) + { + const HatchGen_PointOnHatching& aPoint = theHatcher.Point(theHatchingIndex, aPointIndex); + if (aPoint.SegmentBeginning()) + { + if (aSegmentDepth == 0) + { + aSegmentStart = aPoint.Parameter(); + } + ++aSegmentDepth; + } + if (aPoint.SegmentEnd() && aSegmentDepth > 0) + { + --aSegmentDepth; + if (aSegmentDepth == 0 + && std::abs(aPoint.Parameter() - aSegmentStart) > theHatcher.Confusion2d()) + { + hasSegment = true; + break; + } + } + } + + if (hasSegment) + { + theHatcher.KeepSegments(true); + theHatcher.ComputeDomains(theHatchingIndex); + } + return theHatcher.IsDone(theHatchingIndex); +} + //======================================================================= // function : SplitKPart // purpose : Reconstruct SurfData depending on restrictions of faces. @@ -789,8 +919,7 @@ bool ChFi3d_Builder::SplitKPart(const occ::handle& M1.Bind(ie, I1->Value()); } iH1 = H1.Trim(ll1); - H1.ComputeDomains(iH1); - if (!H1.IsDone(iH1)) + if (!ChFi3d_ComputeHatchingDomains(H1, iH1)) { return false; } @@ -824,8 +953,7 @@ bool ChFi3d_Builder::SplitKPart(const occ::handle& M2.Bind(ie, I2->Value()); } iH2 = H2.Trim(ll2); - H2.ComputeDomains(iH2); - if (!H2.IsDone(iH2)) + if (!ChFi3d_ComputeHatchingDomains(H2, iH2)) { return false; } @@ -910,6 +1038,7 @@ bool ChFi3d_Builder::SplitKPart(const occ::handle& const HatchGen_Domain& Dom2 = H2.Domain(iH2, Ind2(i)); FillSD(DStr, CD, M2, Dom2, Dom2.FirstPoint().Parameter(), true, 2, pitol, bout1); FillSD(DStr, CD, M2, Dom2, Dom2.SecondPoint().Parameter(), false, 2, pitol, bout2); + SetCoincidentDomainEdge(DStr, CD->InterferenceOnS2(), M2, Dom2, true); SetData.Append(CD); CD = CpSD(DStr, CD); } @@ -964,6 +1093,7 @@ bool ChFi3d_Builder::SplitKPart(const occ::handle& const HatchGen_Domain& Dom1 = H1.Domain(iH1, Ind1(i)); FillSD(DStr, CD, M1, Dom1, Dom1.FirstPoint().Parameter(), true, 1, pitol, bout1); FillSD(DStr, CD, M1, Dom1, Dom1.SecondPoint().Parameter(), false, 1, pitol, bout2); + SetCoincidentDomainEdge(DStr, CD->InterferenceOnS1(), M1, Dom1, true); SetData.Append(CD); CD = CpSD(DStr, CD); } @@ -1054,6 +1184,8 @@ bool ChFi3d_Builder::SplitKPart(const occ::handle& { if (f2 <= l1 && f1 <= l2) { + const double aFirst = std::max(f1, f2); + const double aLast = std::min(l1, l2); if (f1 >= f2 - tol2d) { FillSD(DStr, CD, M1, Dom1, f1, true, 1, pitol, bout1); @@ -1070,6 +1202,18 @@ bool ChFi3d_Builder::SplitKPart(const occ::handle& { FillSD(DStr, CD, M1, Dom1, l1, false, 1, pitol, bout2); } + SetCoincidentDomainEdge(DStr, + CD->InterferenceOnS1(), + M1, + Dom1, + std::abs(aFirst - f1) <= tol2d + && std::abs(aLast - l1) <= tol2d); + SetCoincidentDomainEdge(DStr, + CD->InterferenceOnS2(), + M2, + Dom2, + std::abs(aFirst - f2) <= tol2d + && std::abs(aLast - l2) <= tol2d); SetData.Append(CD); CD = CpSD(DStr, CD); ion1.Append(i); diff --git a/src/ModelingAlgorithms/TKFillet/GTests/BRepFilletAPI_MakeChamfer_Test.cxx b/src/ModelingAlgorithms/TKFillet/GTests/BRepFilletAPI_MakeChamfer_Test.cxx index 51b0c867ef..40c48477a7 100644 --- a/src/ModelingAlgorithms/TKFillet/GTests/BRepFilletAPI_MakeChamfer_Test.cxx +++ b/src/ModelingAlgorithms/TKFillet/GTests/BRepFilletAPI_MakeChamfer_Test.cxx @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -31,8 +32,10 @@ #include #include #include +#include #include +#include TEST(BRepFilletAPI_MakeChamferTest, SymmetricChamfer) { @@ -106,11 +109,22 @@ TEST(BRepFilletAPI_MakeChamferTest, Issue1177_ChamferAllEdgesFlatBox_SucceedsWit const TopoDS_Shape& aResult = aChamfer.Shape(); ASSERT_FALSE(aResult.IsNull()); - BRepCheck_Analyzer anAnalyzer(aResult); - if (!anAnalyzer.IsValid()) + BRepCheck_Analyzer anAnalyzer(aResult, true, false, true); + EXPECT_TRUE(anAnalyzer.IsValid()); + + double aMaxTolerance = 0.0; + for (TopExp_Explorer aVertexExp(aResult, TopAbs_VERTEX); aVertexExp.More(); aVertexExp.Next()) { - GTEST_SKIP() << "Valid consumed-face topology requires the follow-up reconstruction fix"; + aMaxTolerance = + std::max(aMaxTolerance, BRep_Tool::Tolerance(TopoDS::Vertex(aVertexExp.Current()))); } + for (TopExp_Explorer anEdgeExp(aResult, TopAbs_EDGE); anEdgeExp.More(); anEdgeExp.Next()) + { + aMaxTolerance = + std::max(aMaxTolerance, BRep_Tool::Tolerance(TopoDS::Edge(anEdgeExp.Current()))); + } + EXPECT_LE(aMaxTolerance, 1.01e-4) + << "Chamfer construction must not hide inconsistent edge parameterization with tolerance"; } TEST(BRepFilletAPI_MakeChamferTest, ChamferMoreFaces) diff --git a/src/ModelingAlgorithms/TKFillet/GTests/BRepFilletAPI_MakeFillet_Test.cxx b/src/ModelingAlgorithms/TKFillet/GTests/BRepFilletAPI_MakeFillet_Test.cxx index 8835c6a41e..fc663e3cd9 100644 --- a/src/ModelingAlgorithms/TKFillet/GTests/BRepFilletAPI_MakeFillet_Test.cxx +++ b/src/ModelingAlgorithms/TKFillet/GTests/BRepFilletAPI_MakeFillet_Test.cxx @@ -11,8 +11,6 @@ // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. -#include - #include #include #include @@ -22,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +30,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -56,10 +57,13 @@ #include #include #include +#include #include #include #include +#include +#include // Regression for fillets that must remove an intervening face or meet on opposite // edges of a prism. Related reports: @@ -85,10 +89,7 @@ TEST(BRepFilletAPI_MakeFilletTest, Issue1177_FilletToOpposingEdge_SucceedsWithou ASSERT_NO_THROW(aFillet.Build()) << "Fillet build must not crash"; - if (!aFillet.IsDone()) - { - GTEST_SKIP() << "Fillet radius equal to box size should succeed (issue #1177)"; - } + EXPECT_TRUE(aFillet.IsDone()) << "Fillet radius equal to box size should succeed (issue #1177)"; if (!aFillet.IsDone()) { @@ -99,10 +100,49 @@ TEST(BRepFilletAPI_MakeFilletTest, Issue1177_FilletToOpposingEdge_SucceedsWithou ASSERT_FALSE(aResult.IsNull()); BRepCheck_Analyzer anAnalyzer(aResult); - if (!anAnalyzer.IsValid()) + EXPECT_TRUE(anAnalyzer.IsValid()); +} + +TEST(BRepFilletAPI_MakeFilletTest, Issue1177_HistoryReferencesResultTopology) +{ + constexpr double aSize = 10.0; + + BRepPrimAPI_MakeBox aBoxMaker(aSize, aSize, aSize); + const TopoDS_Shape& aBox = aBoxMaker.Shape(); + ASSERT_TRUE(aBoxMaker.IsDone()); + + TopExp_Explorer anEdgeExp(aBox, TopAbs_EDGE); + ASSERT_TRUE(anEdgeExp.More()); + const TopoDS_Edge anEdge = TopoDS::Edge(anEdgeExp.Current()); + + BRepFilletAPI_MakeFillet aFillet(aBox); + aFillet.Add(aSize, anEdge); + ASSERT_NO_THROW(aFillet.Build()); + ASSERT_TRUE(aFillet.IsDone()); + + NCollection_IndexedMap aResultShapes; + TopExp::MapShapes(aFillet.Shape(), aResultShapes); + + const NCollection_List& aGenerated = aFillet.Generated(anEdge); + ASSERT_FALSE(aGenerated.IsEmpty()); + for (NCollection_List::Iterator anIt(aGenerated); anIt.More(); anIt.Next()) { - GTEST_SKIP() << "Valid consumed-face topology requires the follow-up reconstruction fix"; + EXPECT_TRUE(aResultShapes.Contains(anIt.Value())) + << "Generated() returned topology absent from the result"; } + + bool hasModified = false; + for (TopExp_Explorer aFaceExp(aBox, TopAbs_FACE); aFaceExp.More(); aFaceExp.Next()) + { + const NCollection_List& aModified = aFillet.Modified(aFaceExp.Current()); + hasModified = hasModified || !aModified.IsEmpty(); + for (NCollection_List::Iterator anIt(aModified); anIt.More(); anIt.Next()) + { + EXPECT_TRUE(aResultShapes.Contains(anIt.Value())) + << "Modified() returned topology absent from the result"; + } + } + EXPECT_TRUE(hasModified); } // Two parallel edges on the same face: each fillet radius is half the span so they @@ -151,10 +191,60 @@ TEST(BRepFilletAPI_MakeFilletTest, Issue1177_OpposingFilletsMeet_SucceedsWithout ASSERT_FALSE(aResult.IsNull()); BRepCheck_Analyzer anAnalyzer(aResult); - if (!anAnalyzer.IsValid()) + EXPECT_TRUE(anAnalyzer.IsValid()); +} + +TEST(BRepFilletAPI_MakeFilletTest, Issue1177_ConcaveFilletsMeet_SucceedsWithoutCrash) +{ + const TopoDS_Shape aBox = BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape(); + TopoDS_Shape aTool = BRepPrimAPI_MakeBox(10.0, 10.0, 12.0).Shape(); + gp_Trsf aTranslation; + aTranslation.SetTranslation(gp_Vec(5.0, 5.0, -1.0)); + aTool = BRepBuilderAPI_Transform(aTool, aTranslation).Shape(); + + BRepAlgoAPI_Cut aCut(aBox, aTool); + ASSERT_TRUE(aCut.IsDone()); + ASSERT_TRUE(BRepCheck_Analyzer(aCut.Shape()).IsValid()); + + NCollection_IndexedMap anEdgeMap; + TopExp::MapShapes(aCut.Shape(), TopAbs_EDGE, anEdgeMap); + NCollection_Sequence anEdges; + for (int anEdgeIndex = 1; anEdgeIndex <= anEdgeMap.Extent(); ++anEdgeIndex) { - GTEST_SKIP() << "Valid consumed-face topology requires the follow-up reconstruction fix"; + const TopoDS_Edge& anEdge = TopoDS::Edge(anEdgeMap(anEdgeIndex)); + TopoDS_Vertex aFirstVertex; + TopoDS_Vertex aLastVertex; + TopExp::Vertices(anEdge, aFirstVertex, aLastVertex); + const gp_Pnt& aFirstPoint = BRep_Tool::Pnt(aFirstVertex); + const gp_Pnt& aLastPoint = BRep_Tool::Pnt(aLastVertex); + if (std::abs(aFirstPoint.X() - aLastPoint.X()) > Precision::Confusion() + || std::abs(aFirstPoint.Y() - aLastPoint.Y()) > Precision::Confusion()) + { + continue; + } + const bool isReportedEdge = (std::abs(aFirstPoint.X() - 5.0) <= Precision::Confusion() + && (std::abs(aFirstPoint.Y() - 5.0) <= Precision::Confusion() + || std::abs(aFirstPoint.Y() - 10.0) <= Precision::Confusion())) + || (std::abs(aFirstPoint.X() - 10.0) <= Precision::Confusion() + && std::abs(aFirstPoint.Y() - 5.0) <= Precision::Confusion()); + if (isReportedEdge) + { + anEdges.Append(anEdge); + } } + ASSERT_EQ(anEdges.Length(), 3); + + BRepFilletAPI_MakeFillet aFillet(aCut.Shape()); + for (NCollection_Sequence::Iterator anEdgeIt(anEdges); anEdgeIt.More(); + anEdgeIt.Next()) + { + aFillet.Add(2.5, anEdgeIt.Value()); + } + + ASSERT_NO_THROW(aFillet.Build()); + ASSERT_TRUE(aFillet.IsDone()); + ASSERT_FALSE(aFillet.Shape().IsNull()); + EXPECT_TRUE(BRepCheck_Analyzer(aFillet.Shape()).IsValid()); } // Fillet every edge of a flat 50x50x10 slab with radius 5. The top and bottom @@ -186,11 +276,22 @@ TEST(BRepFilletAPI_MakeFilletTest, Issue1177_FilletAllEdgesFlatBox_SucceedsWitho const TopoDS_Shape& aResult = aFillet.Shape(); ASSERT_FALSE(aResult.IsNull()); - BRepCheck_Analyzer anAnalyzer(aResult); - if (!anAnalyzer.IsValid()) + BRepCheck_Analyzer anAnalyzer(aResult, true, false, true); + EXPECT_TRUE(anAnalyzer.IsValid()); + + double aMaxTolerance = 0.0; + for (TopExp_Explorer aVertexExp(aResult, TopAbs_VERTEX); aVertexExp.More(); aVertexExp.Next()) { - GTEST_SKIP() << "Valid consumed-face topology requires the follow-up reconstruction fix"; + aMaxTolerance = + std::max(aMaxTolerance, BRep_Tool::Tolerance(TopoDS::Vertex(aVertexExp.Current()))); } + for (TopExp_Explorer anEdgeExp(aResult, TopAbs_EDGE); anEdgeExp.More(); anEdgeExp.Next()) + { + aMaxTolerance = + std::max(aMaxTolerance, BRep_Tool::Tolerance(TopoDS::Edge(anEdgeExp.Current()))); + } + EXPECT_LE(aMaxTolerance, 1.01 * Precision::Confusion()) + << "Fillet construction must not hide inconsistent edge parameterization with tolerance"; } TEST(BRepFilletAPI_MakeFilletTest, FilletOneEdge) diff --git a/src/ModelingAlgorithms/TKFillet/GTests/ChFi3d_Hatching_Test.cxx b/src/ModelingAlgorithms/TKFillet/GTests/ChFi3d_Hatching_Test.cxx new file mode 100644 index 0000000000..938a5a2178 --- /dev/null +++ b/src/ModelingAlgorithms/TKFillet/GTests/ChFi3d_Hatching_Test.cxx @@ -0,0 +1,99 @@ +// Copyright (c) 2026 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ + +void AddContour(Geom2dHatch_Hatcher& theHatcher, const NCollection_Array1& thePoints) +{ + for (int anIndex = thePoints.Lower(); anIndex <= thePoints.Upper(); ++anIndex) + { + const int aNextIndex = anIndex == thePoints.Upper() ? thePoints.Lower() : anIndex + 1; + theHatcher.AddElement(GC_MakeSegment2d(thePoints(anIndex), thePoints(aNextIndex)).Value(), + TopAbs_FORWARD); + } +} + +int AddHorizontalHatching(Geom2dHatch_Hatcher& theHatcher) +{ + occ::handle aLine = new Geom2d_Line(gp_Ax2d(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0))); + return theHatcher.Trim(Geom2dAdaptor_Curve(aLine)); +} + +} // namespace + +TEST(ChFi3d_HatchingTest, CoincidentSegmentFormsLimitingDomain) +{ + constexpr double aTolerance = 1.0e-7; + Geom2dHatch_Intersector anIntersector(aTolerance, aTolerance); + Geom2dHatch_Hatcher aHatcher(anIntersector, aTolerance, aTolerance); + + NCollection_Array1 aContour(1, 4); + aContour(1) = gp_Pnt2d(0.0, 0.0); + aContour(2) = gp_Pnt2d(10.0, 0.0); + aContour(3) = gp_Pnt2d(10.0, 10.0); + aContour(4) = gp_Pnt2d(0.0, 10.0); + AddContour(aHatcher, aContour); + + const int aHatchingIndex = AddHorizontalHatching(aHatcher); + ASSERT_TRUE(ChFi3d_ComputeHatchingDomains(aHatcher, aHatchingIndex)); + ASSERT_EQ(aHatcher.NbDomains(aHatchingIndex), 1); + + const HatchGen_Domain& aDomain = aHatcher.Domain(aHatchingIndex, 1); + ASSERT_TRUE(aDomain.HasFirstPoint()); + ASSERT_TRUE(aDomain.HasSecondPoint()); + EXPECT_NEAR(aDomain.FirstPoint().Parameter(), 0.0, Precision::PConfusion()); + EXPECT_NEAR(aDomain.SecondPoint().Parameter(), 10.0, Precision::PConfusion()); +} + +TEST(ChFi3d_HatchingTest, BoundaryOverlapDoesNotDuplicateInteriorDomain) +{ + constexpr double aTolerance = 1.0e-7; + Geom2dHatch_Intersector anIntersector(aTolerance, aTolerance); + Geom2dHatch_Hatcher aHatcher(anIntersector, aTolerance, aTolerance); + + NCollection_Array1 aContour(1, 6); + aContour(1) = gp_Pnt2d(-10.0, -10.0); + aContour(2) = gp_Pnt2d(0.0, -10.0); + aContour(3) = gp_Pnt2d(0.0, 0.0); + aContour(4) = gp_Pnt2d(10.0, 0.0); + aContour(5) = gp_Pnt2d(10.0, 10.0); + aContour(6) = gp_Pnt2d(-10.0, 10.0); + AddContour(aHatcher, aContour); + + const int aHatchingIndex = AddHorizontalHatching(aHatcher); + ASSERT_TRUE(ChFi3d_ComputeHatchingDomains(aHatcher, aHatchingIndex)); + ASSERT_EQ(aHatcher.NbDomains(aHatchingIndex), 1); + + const HatchGen_Domain& aDomain = aHatcher.Domain(aHatchingIndex, 1); + ASSERT_TRUE(aDomain.HasFirstPoint()); + ASSERT_TRUE(aDomain.HasSecondPoint()); + EXPECT_NEAR(aDomain.FirstPoint().Parameter(), -10.0, Precision::PConfusion()); + EXPECT_NEAR(aDomain.SecondPoint().Parameter(), 0.0, Precision::PConfusion()); +} diff --git a/src/ModelingAlgorithms/TKFillet/GTests/FILES.cmake b/src/ModelingAlgorithms/TKFillet/GTests/FILES.cmake index c7dfcd2bac..4c44dfcca2 100644 --- a/src/ModelingAlgorithms/TKFillet/GTests/FILES.cmake +++ b/src/ModelingAlgorithms/TKFillet/GTests/FILES.cmake @@ -4,4 +4,5 @@ set(OCCT_TKFillet_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}") set(OCCT_TKFillet_GTests_FILES BRepFilletAPI_MakeChamfer_Test.cxx BRepFilletAPI_MakeFillet_Test.cxx + ChFi3d_Hatching_Test.cxx ) diff --git a/tests/blend/complex/B2 b/tests/blend/complex/B2 index 7001f3a75e..dc5dfb83f4 100644 --- a/tests/blend/complex/B2 +++ b/tests/blend/complex/B2 @@ -3,10 +3,10 @@ ## Test : E4 ## Comment : from pro10320 ## ==================================== -puts "TODO OCC22817 All:TEST INCOMPLETE" +puts "TODO OCC22817 All:Faulty shapes in variables faulty_1 to faulty_7" restore [locate_data_file CFI_pro10320.rle] a explode a e blend result a 15 a_15 -checkprops result -s 0 +checkprops result -s 22178.4 diff --git a/tests/blend/simple/Q3 b/tests/blend/simple/Q3 index 1848c3aec1..c29d45674c 100644 --- a/tests/blend/simple/Q3 +++ b/tests/blend/simple/Q3 @@ -1,7 +1,7 @@ -puts "TODO OCC22817 All: TEST INCOMPLETE" +puts "TODO OCC22817 All: Faulty shapes in variables faulty_1 to faulty_2" restore [locate_data_file CFI_4_e1_ffr.rle] s tscale s 0 0 0 SCALE explode s E blend result s 1*SCALE s_5 -checkprops result -s 0 +checkprops result -s 1.5089e+08 diff --git a/tests/blend/simple/Q8 b/tests/blend/simple/Q8 index 50f06516d9..3bdb56de21 100644 --- a/tests/blend/simple/Q8 +++ b/tests/blend/simple/Q8 @@ -1,7 +1,7 @@ -puts "TODO OCC22817 All:TEST INCOMPLETE" +puts "TODO OCC22817 All:Faulty shapes in variables faulty_1 to faulty_1" restore [locate_data_file CFI_4_j1_ffv.rle] s tscale s 0 0 0 SCALE explode s E blend result s 10*SCALE s_12 -checkprops result -s 0 +checkprops result -s 1.19571e+10 diff --git a/tests/blend/simple/W7 b/tests/blend/simple/W7 index ed59aa3013..1e9dc66793 100644 --- a/tests/blend/simple/W7 +++ b/tests/blend/simple/W7 @@ -4,10 +4,10 @@ ## Comment : From CV tests serie page 16/17 ## =========================================== -puts "TODO OCC22739 All:TEST INCOMPLETE" +puts "TODO OCC22739 All:Faulty shapes in variables faulty_1 to faulty_2" restore [locate_data_file CCV_1_d12gsg.rle] s explode s E blend result s 10 s_5 -checkprops result -s 0 +checkprops result -s 59505.1