Modeling - Fix stack overflow and edge multiplication in shape healing with shared sub-shapes (#1227)

- Add replacement-chain leaf resolution (`ValueLeaf()`), cycle rejection on `Replace()`, and DFS in-flight guards to prevent recursive descent loops.
- Update ShapeFix healing routines to avoid repeated sub-shape re-expansion, add progress cancellation checkpoints, and reduce quadratic wire-fixing cost.
- Add/adjust regression coverage (new GTests; updated existing Draw tests/baselines).
This commit is contained in:
Pasukhin Dmitry
2026-04-28 15:05:42 +01:00
committed by GitHub
parent 3cf18a1452
commit aaa82fc4de
19 changed files with 830 additions and 94 deletions
@@ -20,6 +20,7 @@
#include <BRepTools_History.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_TShape.hxx>
#include <TopTools_ShapeMapHasher.hxx>
#include <NCollection_DataMap.hxx>
#include <NCollection_Map.hxx>
@@ -98,6 +99,14 @@ public:
//! Else, returns the replacing item
Standard_EXPORT virtual TopoDS_Shape Value(const TopoDS_Shape& shape) const;
//! Follows the replacement chain for @p theShape to its leaf without descending into sub-shapes.
//! Iterates Value() until a fixpoint is reached. Unlike Apply(), this does not rebuild
//! the shape from its children, so it is safe to call on edges/wires whose sub-shapes
//! have their own pending replacements (avoids cascading sub-shape re-expansion).
//! @return the final replacement, or the original shape if not recorded,
//! or a Null shape if the chain terminates in a Remove.
Standard_EXPORT TopoDS_Shape ValueLeaf(const TopoDS_Shape& theShape) const;
//! Returns a complete substitution status for a shape
//! 0 : not recorded, <newsh> = original <shape>
//! < 0: to be removed, <newsh> is NULL
@@ -173,6 +182,14 @@ protected:
const TopoDS_Shape& newshape,
const TReplacementKind theKind);
//! Recursive worker for Apply with a DFS in-flight set keyed by TShape handle.
//! Prevents unbounded descent when a replacement is a compound that transitively
//! contains the original shape as a sub-shape (cyclic containment, distinct from
//! cycles in the replacement map itself).
Standard_EXPORT TopoDS_Shape applyImpl(const TopoDS_Shape& theShape,
const TopAbs_ShapeEnum theUntil,
NCollection_Map<occ::handle<TopoDS_TShape>>& theInFlight);
private:
//! Returns 'true' if the kind of a replacement is an ordinary merging.
static bool isOrdinaryMerged(const TReplacementKind theKind)