From 99ad972319deb0f9d8e45d03c8a78bdaf1b20289 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Mon, 15 Jan 2024 19:07:18 -0600 Subject: [PATCH] Part/Toponaming: Merge makECopy from Toponaming --- src/Mod/Part/App/TopoShape.cpp | 48 +++++++++++++++++++++++++ src/Mod/Part/App/TopoShape.h | 32 +++++++++++++++++ src/Mod/Part/App/TopoShapeExpansion.cpp | 24 +++++++++++++ 3 files changed, 104 insertions(+) diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index 674ced35f2..b043e2800b 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -3146,6 +3146,54 @@ void TopoShape::sewShape(double tolerance) this->_Shape = sew.SewedShape(); } +bool TopoShape::fix() +{ + if (this->_Shape.IsNull()) + return false; + + // First, we do fix regardless if the current shape is valid or not, + // because not all problems that are handled by ShapeFix_Shape can be + // recognized by BRepCheck_Analyzer. + // + // Second, for some reason, a failed fix (i.e. a fix that produces invalid shape) + // will affect the input shape. (See // https://github.com/realthunder/FreeCAD/issues/585, + // BTW, the file attached in the issue also shows that ShapeFix_Shape may + // actually make a valid input shape invalid). So, it actually change the + // underlying shape data. Therefore, we try with a copy first. + auto copy = makECopy(); + ShapeFix_Shape fix(copy._Shape); + fix.Perform(); + + if (fix.Shape().IsSame(copy._Shape)) + return false; + + BRepCheck_Analyzer aChecker(fix.Shape()); + if (!aChecker.IsValid()) + return false; + + // If the above fix produces a valid shape, then we fix the original shape, + // because BRepBuilderAPI_Copy has some undesired side effect (e.g. flatten + // underlying shape, and thus break internal shape sharing). + ShapeFix_Shape fixThis(this->_Shape); + fixThis.Perform(); + + aChecker.Init(fixThis.Shape()); + if (aChecker.IsValid()) { + // Must call makESHAPE() (which calls mapSubElement()) to remap element + // names because ShapeFix_Shape may delete (e.g. small edges) or modify + // the input shape. + // + // See https://github.com/realthunder/FreeCAD/issues/595. Sketch001 + // has small edges. Simply recompute the sketch to trigger call of fix() + // through makEWires(), and it will remove those edges. Without + // remapping, there will be invalid index jumpping in reference in + // Sketch002.ExternalEdge5. + makESHAPE(fixThis.Shape(), MapperHistory(fixThis), {*this}); + } else + makESHAPE(fix.Shape(), MapperHistory(fix), {copy}); + return true; +} + bool TopoShape::fix(double precision, double mintol, double maxtol) { if (this->_Shape.IsNull()) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index eaba85f014..5bf650c343 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -384,6 +384,7 @@ public: TopoDS_Shape replaceShape(const std::vector>& s) const; TopoDS_Shape removeShape(const std::vector& s) const; void sewShape(double tolerance = 1.0e-06); + bool fix(); bool fix(double, double, double); bool removeInternalWires(double); TopoDS_Shape removeSplitter() const; @@ -755,6 +756,37 @@ public: return TopoShape(0, Hasher).makeElementWires(*this, op, tol, policy, output); } + + + /** Make a deep copy of the shape + * + * @param source: input shape + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param copyGeom: whether to copy internal geometry of the shape + * @param copyMesh: whether to copy internal meshes of the shape + * + * @return The original content of this TopoShape is discarded and replaced + * with a deep copy of the input shape. The function returns the + * TopoShape itself as a self reference so that multiple operations + * can be carried out for the same shape in the same line of code. + */ + TopoShape &makECopy(const TopoShape &source, const char *op=nullptr, bool copyGeom=true, bool copyMesh=false); + + /** Make a deep copy of the shape + * + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param copyGeom: whether to copy internal geometry of the shape + * @param copyMesh: whether to copy internal meshes of the shape + * + * @return Return a deep copy of the shape. The shape itself is not + * modified + */ + TopoShape makECopy(const char *op=nullptr, bool copyGeom=true, bool copyMesh=false) const { + return TopoShape(Tag,Hasher).makECopy(*this,op,copyGeom,copyMesh); + } + friend class TopoShapeCache; private: diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 24dd9e1792..4d08dba521 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -860,4 +861,27 @@ TopoShape& TopoShape::makeElementOrderedWires(const std::vector& shap return makeElementCompound(wires, nullptr, SingleShapeCompoundCreationPolicy::RETURN_SHAPE); } + +TopoShape &TopoShape::makECopy(const TopoShape &shape, const char *op, bool copyGeom, bool copyMesh) +{ + if(shape.isNull()) + return *this; + + TopoShape tmp(shape); +#if OCC_VERSION_HEX >= 0x070000 + tmp.setShape(BRepBuilderAPI_Copy(shape.getShape(),copyGeom,copyMesh).Shape(), false); +#else + tmp.setShape(BRepBuilderAPI_Copy(shape.getShape()).Shape(), false); +#endif + if(op || (shape.Tag && shape.Tag!=Tag)) { + setShape(tmp._Shape); + initCache(); + if (!Hasher) + Hasher = tmp.Hasher; + copyElementMap(tmp, op); + }else + *this = tmp; + return *this; +} + } // namespace Part