1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-06-20 11:54:07 +03:00

Modeling - Infinite loop when Simplifying Fuse operation, CPU to 100% #557

Minor refactoring of RelocatePCurvesToNewUorigin().
RelocatePCurvesToNewUorigin() can no longer stuck in infinite loop if it found the edge that is not present in theVEmap.
Test bug_gh544 is added to check the fix.
This commit is contained in:
Dmitrii Kulikov 2025-05-22 11:29:54 +01:00 committed by GitHub
parent 2398b87d36
commit 7ed396b0eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 168 additions and 111 deletions

View File

@ -282,26 +282,40 @@ static Standard_Boolean TryMakeLine(const Handle(Geom2d_Curve)& thePCurve,
return Standard_True; return Standard_True;
} }
static void RemoveEdgeFromMap(const TopoDS_Edge& theEdge, // Removes the specified edge from the vertex to edge map.
TopTools_IndexedDataMapOfShapeListOfShape& theVEmap) // @param theEdge The edge to remove.
// @param theVertexToEdges The map of vertices to edges.
// @return True if the edge was removed, false otherwise.
static bool RemoveEdgeFromMap(const TopoDS_Edge& theEdge,
TopTools_IndexedDataMapOfShapeListOfShape& theVertexToEdges)
{ {
TopoDS_Vertex VV[2]; bool anIsRemoved = false;
TopExp::Vertices(theEdge, VV[0], VV[1]); TopoDS_Vertex aFirstVertex;
for (Standard_Integer i = 0; i < 2; i++) TopoDS_Vertex aLastVertex;
TopExp::Vertices(theEdge, aFirstVertex, aLastVertex);
for (const auto& aVertex : {aFirstVertex, aLastVertex})
{ {
if (!theVEmap.Contains(VV[i])) if (!theVertexToEdges.Contains(aVertex))
continue;
TopTools_ListOfShape& Elist = theVEmap.ChangeFromKey(VV[i]);
TopTools_ListIteratorOfListOfShape itl(Elist);
while (itl.More())
{ {
const TopoDS_Shape& anEdge = itl.Value(); continue;
}
TopTools_ListOfShape& aVertexEdges = theVertexToEdges.ChangeFromKey(aVertex);
TopTools_ListIteratorOfListOfShape anEdgesIter(aVertexEdges);
while (anEdgesIter.More())
{
const TopoDS_Shape& anEdge = anEdgesIter.Value();
if (anEdge.IsSame(theEdge)) if (anEdge.IsSame(theEdge))
Elist.Remove(itl); {
anIsRemoved = true;
aVertexEdges.Remove(anEdgesIter);
}
else else
itl.Next(); {
anEdgesIter.Next();
}
} }
} }
return anIsRemoved;
} }
static Standard_Real ComputeMinEdgeSize(const TopTools_SequenceOfShape& theEdges, static Standard_Real ComputeMinEdgeSize(const TopTools_SequenceOfShape& theEdges,
@ -451,6 +465,32 @@ static Standard_Boolean FindCoordBounds(const TopTools_SequenceOfShape&
return Standard_True; return Standard_True;
} }
//==================================================================================================
// Returns the start and end points of the edge in parametric space of the face.
// The orientation of the edge is taken into account, so the start and end points
// will be swapped if the edge has a reversed orientation.
// @param theEdge The edge to get the points from.
// @param theRefFace The reference face to get the parametric points.
// @return A pair of points representing the start and end points of the edge in parametric space.
static std::pair<gp_Pnt2d, gp_Pnt2d> getCurveParams(const TopoDS_Edge& theEdge,
const TopoDS_Face& theRefFace)
{
BRepAdaptor_Curve2d aCurveAdaptor(theEdge, theRefFace);
Standard_Real aFirstParam = aCurveAdaptor.FirstParameter();
Standard_Real aLastParam = aCurveAdaptor.LastParameter();
if (theEdge.Orientation() != TopAbs_FORWARD)
{
std::swap(aFirstParam, aLastParam);
}
const gp_Pnt2d aFirstPoint = aCurveAdaptor.Value(aFirstParam);
const gp_Pnt2d aLastPoint = aCurveAdaptor.Value(aLastParam);
return {aFirstPoint, aLastPoint};
}
//==================================================================================================
static void RelocatePCurvesToNewUorigin( static void RelocatePCurvesToNewUorigin(
const TopTools_SequenceOfShape& theEdges, const TopTools_SequenceOfShape& theEdges,
const TopoDS_Shape& theFirstFace, const TopoDS_Shape& theFirstFace,
@ -462,141 +502,129 @@ static void RelocatePCurvesToNewUorigin(
NCollection_DataMap<TopoDS_Shape, Handle(Geom2d_Curve)>& theEdgeNewPCurve, NCollection_DataMap<TopoDS_Shape, Handle(Geom2d_Curve)>& theEdgeNewPCurve,
TopTools_MapOfShape& theUsedEdges) TopTools_MapOfShape& theUsedEdges)
{ {
TopTools_MapOfShape EdgesOfFirstFace; TopTools_MapOfShape anEdgesOfFirstFace;
TopExp::MapShapes(theFirstFace, EdgesOfFirstFace); TopExp::MapShapes(theFirstFace, anEdgesOfFirstFace);
for (;;) // walk by contours for (;;) // walk by contours
{ {
// try to find the start edge that: // try to find the start edge that:
// 1. belongs to outer edges of first face; // 1. belongs to outer edges of first face;
// 2. has minimum U-coord of its start point // 2. has minimum U-coord of its start point
TopoDS_Edge StartEdge; TopoDS_Edge aStartEdge;
TopAbs_Orientation anOr = TopAbs_FORWARD; TopAbs_Orientation anOrientation = TopAbs_FORWARD;
Standard_Real aCoordMin = RealLast(); Standard_Real aCoordMin = RealLast();
for (Standard_Integer ii = 1; ii <= theEdges.Length(); ii++) for (Standard_Integer anEdgeIndex = 1; anEdgeIndex <= theEdges.Length(); ++anEdgeIndex)
{ {
const TopoDS_Edge& anEdge = TopoDS::Edge(theEdges(ii)); const TopoDS_Edge& anEdge = TopoDS::Edge(theEdges(anEdgeIndex));
if (theUsedEdges.Contains(anEdge)) if (theUsedEdges.Contains(anEdge))
continue;
if (EdgesOfFirstFace.Contains(anEdge))
{ {
if (StartEdge.IsNull()) continue;
}
if (!anEdgesOfFirstFace.Contains(anEdge))
{
continue;
}
if (aStartEdge.IsNull())
{
aStartEdge = anEdge;
const auto&& [aFirstPoint, aLastPoint] = getCurveParams(anEdge, theRefFace);
if (aFirstPoint.Coord(theIndCoord) < aLastPoint.Coord(theIndCoord))
{ {
StartEdge = anEdge; aCoordMin = aFirstPoint.Coord(theIndCoord);
BRepAdaptor_Curve2d StartBAcurve(StartEdge, theRefFace); anOrientation = TopAbs_FORWARD;
Standard_Real aFirstParam, aLastParam;
if (StartEdge.Orientation() == TopAbs_FORWARD)
{
aFirstParam = StartBAcurve.FirstParameter();
aLastParam = StartBAcurve.LastParameter();
}
else
{
aFirstParam = StartBAcurve.LastParameter();
aLastParam = StartBAcurve.FirstParameter();
}
gp_Pnt2d aFirstPoint = StartBAcurve.Value(aFirstParam);
gp_Pnt2d aLastPoint = StartBAcurve.Value(aLastParam);
if (aFirstPoint.Coord(theIndCoord) < aLastPoint.Coord(theIndCoord))
{
aCoordMin = aFirstPoint.Coord(theIndCoord);
anOr = TopAbs_FORWARD;
}
else
{
aCoordMin = aLastPoint.Coord(theIndCoord);
anOr = TopAbs_REVERSED;
}
} }
else else
{ {
BRepAdaptor_Curve2d aBAcurve(anEdge, theRefFace); aCoordMin = aLastPoint.Coord(theIndCoord);
Standard_Real aFirstParam, aLastParam; anOrientation = TopAbs_REVERSED;
if (anEdge.Orientation() == TopAbs_FORWARD)
{
aFirstParam = aBAcurve.FirstParameter();
aLastParam = aBAcurve.LastParameter();
}
else
{
aFirstParam = aBAcurve.LastParameter();
aLastParam = aBAcurve.FirstParameter();
}
gp_Pnt2d aFirstPoint = aBAcurve.Value(aFirstParam);
gp_Pnt2d aLastPoint = aBAcurve.Value(aLastParam);
if (aFirstPoint.Coord(theIndCoord) < aCoordMin)
{
StartEdge = anEdge;
aCoordMin = aFirstPoint.Coord(theIndCoord);
anOr = TopAbs_FORWARD;
}
if (aLastPoint.Coord(theIndCoord) < aCoordMin)
{
StartEdge = anEdge;
aCoordMin = aLastPoint.Coord(theIndCoord);
anOr = TopAbs_REVERSED;
}
} }
} // if (EdgesOfFirstFace.Contains(anEdge)) }
} // for (Standard_Integer ii = 1; ii <= edges.Length(); ii++) else
{
if (StartEdge.IsNull()) // all contours are passed const auto&& [aFirstPoint, aLastPoint] = getCurveParams(anEdge, theRefFace);
break; if (aFirstPoint.Coord(theIndCoord) < aCoordMin)
{
TopoDS_Vertex StartVertex = (anOr == TopAbs_FORWARD) aStartEdge = anEdge;
? TopExp::FirstVertex(StartEdge, Standard_True) aCoordMin = aFirstPoint.Coord(theIndCoord);
: TopExp::LastVertex(StartEdge, Standard_True); anOrientation = TopAbs_FORWARD;
TopoDS_Edge CurEdge = StartEdge; }
Standard_Real fpar, lpar; if (aLastPoint.Coord(theIndCoord) < aCoordMin)
Handle(Geom2d_Curve) CurPCurve = BRep_Tool::CurveOnSurface(CurEdge, theRefFace, fpar, lpar); {
CurPCurve = new Geom2d_TrimmedCurve(CurPCurve, fpar, lpar); aStartEdge = anEdge;
theEdgeNewPCurve.Bind(CurEdge, CurPCurve); aCoordMin = aLastPoint.Coord(theIndCoord);
if (CurEdge.Orientation() == TopAbs_REVERSED) anOrientation = TopAbs_REVERSED;
{ }
Standard_Real tmp = fpar; }
fpar = lpar;
lpar = tmp;
} }
Standard_Real CurParam = (anOr == TopAbs_FORWARD) ? lpar : fpar;
if (aStartEdge.IsNull()) // all contours are passed
{
break;
}
TopoDS_Edge aCurrentEdge = aStartEdge;
Standard_Real anEdgeStartParam, anEdgeEndParam;
Handle(Geom2d_Curve) CurPCurve =
BRep_Tool::CurveOnSurface(aCurrentEdge, theRefFace, anEdgeStartParam, anEdgeEndParam);
CurPCurve = new Geom2d_TrimmedCurve(CurPCurve, anEdgeStartParam, anEdgeEndParam);
theEdgeNewPCurve.Bind(aCurrentEdge, CurPCurve);
if (aCurrentEdge.Orientation() == TopAbs_REVERSED)
{
std::swap(anEdgeStartParam, anEdgeEndParam);
}
Standard_Real CurParam = (anOrientation == TopAbs_FORWARD) ? anEdgeEndParam : anEdgeStartParam;
gp_Pnt2d CurPoint = CurPCurve->Value(CurParam); gp_Pnt2d CurPoint = CurPCurve->Value(CurParam);
for (;;) // collect pcurves of a contour for (;;) // collect pcurves of a contour
{ {
RemoveEdgeFromMap(CurEdge, theVEmap); if (!RemoveEdgeFromMap(aCurrentEdge, theVEmap))
theUsedEdges.Add(CurEdge); {
TopoDS_Vertex CurVertex = (anOr == TopAbs_FORWARD) break; // end of contour in 2d
? TopExp::LastVertex(CurEdge, Standard_True) }
: TopExp::FirstVertex(CurEdge, Standard_True); theUsedEdges.Add(aCurrentEdge);
TopoDS_Vertex CurVertex = (anOrientation == TopAbs_FORWARD)
? TopExp::LastVertex(aCurrentEdge, Standard_True)
: TopExp::FirstVertex(aCurrentEdge, Standard_True);
const TopTools_ListOfShape& Elist = theVEmap.FindFromKey(CurVertex); const TopTools_ListOfShape& Elist = theVEmap.FindFromKey(CurVertex);
if (Elist.IsEmpty()) if (Elist.IsEmpty())
{
break; // end of contour in 3d break; // end of contour in 3d
}
TopTools_ListIteratorOfListOfShape itl(Elist); for (TopTools_ListIteratorOfListOfShape itl(Elist); itl.More(); itl.Next())
for (; itl.More(); itl.Next())
{ {
const TopoDS_Edge& anEdge = TopoDS::Edge(itl.Value()); const TopoDS_Edge& anEdge = TopoDS::Edge(itl.Value());
if (anEdge.IsSame(CurEdge)) if (anEdge.IsSame(aCurrentEdge))
{
continue; continue;
TopoDS_Vertex aFirstVertex = (anOr == TopAbs_FORWARD) }
TopoDS_Vertex aFirstVertex = (anOrientation == TopAbs_FORWARD)
? TopExp::FirstVertex(anEdge, Standard_True) ? TopExp::FirstVertex(anEdge, Standard_True)
: TopExp::LastVertex(anEdge, Standard_True); : TopExp::LastVertex(anEdge, Standard_True);
if (!aFirstVertex.IsSame(CurVertex)) // may be if CurVertex is deg.vertex if (!aFirstVertex.IsSame(CurVertex)) // may be if CurVertex is deg.vertex
{
continue; continue;
}
Handle(Geom2d_Curve) aPCurve = BRep_Tool::CurveOnSurface(anEdge, theRefFace, fpar, lpar); Handle(Geom2d_Curve) aPCurve =
aPCurve = new Geom2d_TrimmedCurve(aPCurve, fpar, lpar); BRep_Tool::CurveOnSurface(anEdge, theRefFace, anEdgeStartParam, anEdgeEndParam);
aPCurve = new Geom2d_TrimmedCurve(aPCurve, anEdgeStartParam, anEdgeEndParam);
if (anEdge.Orientation() == TopAbs_REVERSED) if (anEdge.Orientation() == TopAbs_REVERSED)
{ {
Standard_Real tmp = fpar; std::swap(anEdgeStartParam, anEdgeEndParam);
fpar = lpar;
lpar = tmp;
} }
Standard_Real aParam = (anOr == TopAbs_FORWARD) ? fpar : lpar; Standard_Real aParam =
(anOrientation == TopAbs_FORWARD) ? anEdgeStartParam : anEdgeEndParam;
gp_Pnt2d aPoint = aPCurve->Value(aParam); gp_Pnt2d aPoint = aPCurve->Value(aParam);
Standard_Real anOffset = CurPoint.Coord(theIndCoord) - aPoint.Coord(theIndCoord); Standard_Real anOffset = CurPoint.Coord(theIndCoord) - aPoint.Coord(theIndCoord);
if (!(Abs(anOffset) < theCoordTol || Abs(Abs(anOffset) - thePeriod) < theCoordTol)) if (!(Abs(anOffset) < theCoordTol || Abs(Abs(anOffset) - thePeriod) < theCoordTol))
{
continue; // may be if CurVertex is deg.vertex continue; // may be if CurVertex is deg.vertex
}
if (Abs(anOffset) > thePeriod / 2) if (Abs(anOffset) > thePeriod / 2)
{ {
@ -611,8 +639,8 @@ static void RelocatePCurvesToNewUorigin(
aPCurve = aNewPCurve; aPCurve = aNewPCurve;
} }
theEdgeNewPCurve.Bind(anEdge, aPCurve); theEdgeNewPCurve.Bind(anEdge, aPCurve);
CurEdge = anEdge; aCurrentEdge = anEdge;
TopAbs_Orientation CurOr = TopAbs::Compose(anOr, CurEdge.Orientation()); TopAbs_Orientation CurOr = TopAbs::Compose(anOrientation, aCurrentEdge.Orientation());
CurParam = (CurOr == TopAbs_FORWARD) ? aPCurve->LastParameter() : aPCurve->FirstParameter(); CurParam = (CurOr == TopAbs_FORWARD) ? aPCurve->LastParameter() : aPCurve->FirstParameter();
CurPoint = aPCurve->Value(CurParam); CurPoint = aPCurve->Value(CurParam);
break; break;

View File

@ -0,0 +1,29 @@
# This test is to check the fix for issue #544
# We have to make sure that boolean operation doesn't stuck in infinite loop.
# Load the base solid
restore [locate_data_file bug_gh544.brep] body
# restore solid.brep body
# get all the wires
explode body W
# make it a face
mkplane f body_18
# prism it, z = 12
prism p f 0 0 12
# settings to fuse
bclearobjects
bcleartools
baddobjects p
baddtools body
bfillds
# set simplify to true
bsimplify -f 1
# If this doesn't stuck in infinite loop, behavior is correct.
# If it does, it will be killed by the timeout eventually.
bapibop res_simple 1