From d4012e1190bf36f095efd32a84ab7a27939075f5 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Wed, 24 Jan 2024 21:49:18 -0500 Subject: [PATCH 1/3] Transfer in makeElementShape --- src/Mod/Part/App/TopoShape.h | 239 +++++++++++++++++++++ src/Mod/Part/App/TopoShapeExpansion.cpp | 268 ++++++++++++++++++++++++ 2 files changed, 507 insertions(+) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 06439746a9..0263b06d3a 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -33,6 +33,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include class gp_Ax1; @@ -764,6 +771,222 @@ public: CN, }; + + /** Generic shape making with mapped element name from shape history + * + * @param mkShape: OCCT shape maker. + * @param sources: list of source shapes. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The original content of this TopoShape is discarded and replaced + * with the new shape built by the shape maker. 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& makeElementShape(BRepBuilderAPI_MakeShape& mkShape, + const std::vector& sources, + const char* op = nullptr); + /** Generic shape making with mapped element name from shape history + * + * @param mkShape: OCCT shape maker. + * @param source: source shape. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The original content of this TopoShape is discarded and replaced + * with the new shape built by the shape maker. 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& makeElementShape(BRepBuilderAPI_MakeShape& mkShape, + const TopoShape& source, + const char* op = nullptr); + /** Generic shape making with mapped element name from shape history + * + * @param mkShape: OCCT shape maker. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return Returns the new shape built by the shape maker with mappend element + * name generated using this shape as the source. The shape itself + * is not modified. + */ + TopoShape makeElementShape(BRepBuilderAPI_MakeShape& mkShape, const char* op = nullptr) const + { + return TopoShape(0, Hasher).makeElementShape(mkShape, *this, op); + } + + /** Specialized shape making for BRepBuilderAPI_Sewing with mapped element name + * + * @param mkShape: OCCT shape maker. + * @param sources: list of source shapes. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The original content of this TopoShape is discarded and replaced + * with the new shape built by the shape maker. 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& makeElementShape(BRepBuilderAPI_Sewing& mkShape, + const std::vector& sources, + const char* op = nullptr); + /** Specialized shape making for BRepBuilderAPI_Sewing with mapped element name + * + * @param mkShape: OCCT shape maker. + * @param source: source shape. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The original content of this TopoShape is discarded and replaced + * with the new shape built by the shape maker. 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& makeElementShape(BRepBuilderAPI_Sewing& mkShape, + const TopoShape& source, + const char* op = nullptr); + /** Specialized shape making for BRepBuilderAPI_Sewing with mapped element name + * + * @param mkShape: OCCT shape maker. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return Returns the new shape built by the shape maker with mappend element + * name generated using this shape as the source. The shape itself + * is not modified. + */ + TopoShape makeElementShape(BRepBuilderAPI_Sewing& mkShape, const char* op = nullptr) const + { + return TopoShape(0, Hasher).makeElementShape(mkShape, *this, op); + } + + /** Specialized shape making for BRepBuilderAPI_ThruSections with mapped element name + * + * @param mkShape: OCCT shape maker. + * @param sources: list of source shapes. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The original content of this TopoShape is discarded and replaced + * with the new shape built by the shape maker. 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& makeElementShape(BRepOffsetAPI_ThruSections& mkShape, + const std::vector& sources, + const char* op = nullptr); + /** Specialized shape making for BRepBuilderAPI_Sewing with mapped element name + * + * @param mkShape: OCCT shape maker. + * @param source: source shape. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The original content of this TopoShape is discarded and replaced + * with the new shape built by the shape maker. 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& makeElementShape(BRepOffsetAPI_ThruSections& mkShape, + const TopoShape& source, + const char* op = nullptr); + /** Specialized shape making for BRepBuilderAPI_Sewing with mapped element name + * + * @param mkShape: OCCT shape maker. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return Returns the new shape built by the shape maker with mappend element + * name generated using this shape as the source. The shape itself + * is not modified. + */ + TopoShape makeElementShape(BRepOffsetAPI_ThruSections& mkShape, const char* op = nullptr) const + { + return TopoShape(0, Hasher).makeElementShape(mkShape, *this, op); + } + + /** Specialized shape making for BRepBuilderAPI_MakePipeShell with mapped element name + * + * @param mkShape: OCCT shape maker. + * @param sources: list of source shapes. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The original content of this TopoShape is discarded and replaced + * with the new shape built by the shape maker. 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& makeElementShape(BRepOffsetAPI_MakePipeShell& mkShape, + const std::vector& sources, + const char* op = nullptr); + + /** Specialized shape making for BRepBuilderAPI_MakeHalfSpace with mapped element name + * + * @param mkShape: OCCT shape maker. + * @param source: source shape. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The original content of this TopoShape is discarded and replaced + * with the new shape built by the shape maker. 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& makeElementShape(BRepPrimAPI_MakeHalfSpace& mkShape, + const TopoShape& source, + const char* op = nullptr); + /** Specialized shape making for BRepBuilderAPI_MakeHalfSpace with mapped element name + * + * @param mkShape: OCCT shape maker. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return Returns the new shape built by the shape maker with mappend element + * name generated using this shape as the source. The shape itself + * is not modified. + */ + TopoShape makeElementShape(BRepPrimAPI_MakeHalfSpace& mkShape, const char* op = nullptr) const + { + return TopoShape(0, Hasher).makeElementShape(mkShape, *this, op); + } + + /** Specialized shape making for BRepBuilderAPI_MakePrism with mapped element name + * + * @param mkShape: OCCT shape maker. + * @param sources: list of source shapes. + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The original content of this TopoShape is discarded and replaced + * with the new shape built by the shape maker. 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& makeElementShape(BRepFeat_MakePrism& mkShape, + const std::vector& sources, + const TopoShape& uptoface, + const char* op); + + /** Helper class to return the generated and modified shape given an input shape + * + * Shape history information is extracted using OCCT APIs + * BRepBuilderAPI_MakeShape::Generated/Modified(). However, there is often + * some glitches in various derived class. So we use this class as an + * abstraction, and create various derived classes to deal with the glitches. + */ + friend class TopoShapeCache; private: @@ -898,6 +1121,22 @@ private: }; + +/** Shape mapper for generic BRepBuilderAPI_MakeShape derived class + * + * Uses BRepBuilderAPI_MakeShape::Modified/Generated() function to extract + * shape history for generating mapped element names + */ +struct PartExport MapperMaker: TopoShape::Mapper +{ + BRepBuilderAPI_MakeShape& maker; + MapperMaker(BRepBuilderAPI_MakeShape& maker) + : maker(maker) + {} + virtual const std::vector& modified(const TopoDS_Shape& s) const override; + virtual const std::vector& generated(const TopoDS_Shape& s) const override; +}; + } // namespace Part diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index f6abf55423..c5a122e95e 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -551,7 +551,146 @@ TopoShape::makeElementCompound(const std::vector& shapes, const char* return *this; } +<<<<<<< HEAD +======= +struct MapperSewing: Part::TopoShape::Mapper +{ + BRepBuilderAPI_Sewing& maker; + MapperSewing(BRepBuilderAPI_Sewing& maker) + : maker(maker) + {} + virtual const std::vector& modified(const TopoDS_Shape& s) const override + { + _res.clear(); + try { + const auto& shape = maker.Modified(s); + if (!shape.IsNull() && !shape.IsSame(s)) { + _res.push_back(shape); + } + else { + const auto& sshape = maker.ModifiedSubShape(s); + if (!sshape.IsNull() && !sshape.IsSame(s)) { + _res.push_back(sshape); + } + } + } + catch (const Standard_Failure& e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } + } + return _res; + } +}; +struct MapperThruSections: MapperMaker +{ + TopoShape firstProfile; + TopoShape lastProfile; + + MapperThruSections(BRepOffsetAPI_ThruSections& tmaker, const std::vector& profiles) + : MapperMaker(tmaker) + { + if (!tmaker.FirstShape().IsNull()) { + firstProfile = profiles.front(); + } + if (!tmaker.LastShape().IsNull()) { + lastProfile = profiles.back(); + } + } + virtual const std::vector& generated(const TopoDS_Shape& s) const override + { + MapperMaker::generated(s); + if (_res.size()) { + return _res; + } + try { + auto& tmaker = static_cast(maker); + auto shape = tmaker.GeneratedFace(s); + if (!shape.IsNull()) { + _res.push_back(shape); + } + if (firstProfile.getShape().IsSame(s) || firstProfile.findShape(s)) { + _res.push_back(tmaker.FirstShape()); + } + else if (lastProfile.getShape().IsSame(s) || lastProfile.findShape(s)) { + _res.push_back(tmaker.LastShape()); + } + } + catch (const Standard_Failure& e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } + } + return _res; + } +}; + + +TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, + const TopoShape& source, + const char* op) +{ + std::vector sources(1, source); + return makeElementShape(mkShape, sources, op); +} + +TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, + const std::vector& shapes, + const char* op) +{ + return makeShapeWithElementMap(mkShape.Shape(), MapperMaker(mkShape), shapes, op); +} + +TopoShape& +TopoShape::makeElementShape(BRepOffsetAPI_ThruSections& mk, const TopoShape& source, const char* op) +{ + if (!op) { + op = Part::OpCodes::ThruSections; + } + return makeElementShape(mk, std::vector(1, source), op); +} + +TopoShape& TopoShape::makeElementShape(BRepOffsetAPI_ThruSections& mk, + const std::vector& sources, + const char* op) +{ + if (!op) { + op = Part::OpCodes::ThruSections; + } + return makeShapeWithElementMap(mk.Shape(), MapperThruSections(mk, sources), sources, op); +} + +TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_Sewing& mk, + const std::vector& shapes, + const char* op) +{ + if (!op) { + op = Part::OpCodes::Sewing; + } + return makeShapeWithElementMap(mk.SewedShape(), MapperSewing(mk), shapes, op); +} + +TopoShape& +TopoShape::makeElementShape(BRepBuilderAPI_Sewing& mkShape, const TopoShape& source, const char* op) +{ + if (!op) { + op = Part::OpCodes::Sewing; + } + return makeElementShape(mkShape, std::vector(1, source), op); +} + +TopoShape& TopoShape::makeElementShape(BRepPrimAPI_MakeHalfSpace& mkShape, + const TopoShape& source, + const char* op) +{ + if (!op) { + op = Part::OpCodes::HalfSpace; + } + return makeShapeWithElementMap(mkShape.Solid(), MapperMaker(mkShape), {source}, op); +} + +>>>>>>> ad521d6a23 (Missing method) TopoShape& TopoShape::makeElementFace(const TopoShape& shape, const char* op, const char* maker, @@ -796,5 +935,134 @@ TopoShape TopoShape::splitWires(std::vector* inner, SplitWireReorient return TopoShape(); } +TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, + const TopoShape& source, + const char* op) +{ + std::vector sources(1, source); + return makeElementShape(mkShape, sources, op); +} + +TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, + const std::vector& shapes, + const char* op) +{ + return makeShapeWithElementMap(mkShape.Shape(), MapperMaker(mkShape), shapes, op); +} + +TopoShape &TopoShape::makeElementShape(BRepOffsetAPI_ThruSections &mk, const TopoShape &source, + const char *op) +{ + if(!op) op = Part::OpCodes::ThruSections; + return makeElementShape(mk,std::vector(1,source),op); +} + +TopoShape &TopoShape::makeElementShape(BRepOffsetAPI_ThruSections &mk, const std::vector &sources, + const char *op) +{ + if(!op) op = Part::OpCodes::ThruSections; + return makeShapeWithElementMap(mk.Shape(),MapperThruSections(mk,sources),sources,op); +} + +TopoShape &TopoShape::makeElementShape(BRepBuilderAPI_Sewing &mk, const std::vector &shapes, + const char *op) +{ + if(!op) op = Part::OpCodes::Sewing; + return makeShapeWithElementMap(mk.SewedShape(),MapperSewing(mk),shapes,op); +} + +TopoShape &TopoShape::makeElementShape(BRepBuilderAPI_Sewing &mkShape, + const TopoShape &source, const char *op) +{ + if(!op) op = Part::OpCodes::Sewing; + return makeElementShape(mkShape,std::vector(1,source),op); +} + +struct MapperSewing: Part::TopoShape::Mapper { + BRepBuilderAPI_Sewing &maker; + MapperSewing(BRepBuilderAPI_Sewing &maker) + :maker(maker) + {} + virtual const std::vector &modified(const TopoDS_Shape &s) const override { + _res.clear(); + try { + const auto &shape = maker.Modified(s); + if(!shape.IsNull() && !shape.IsSame(s)) + _res.push_back(shape); + else { + const auto &sshape = maker.ModifiedSubShape(s); + if(!sshape.IsNull() && !sshape.IsSame(s)) + _res.push_back(sshape); + } + } catch (const Standard_Failure & e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } + return _res; + } +}; + +struct MapperThruSections: MapperMaker { + TopoShape firstProfile; + TopoShape lastProfile; + + MapperThruSections(BRepOffsetAPI_ThruSections &tmaker, + const std::vector &profiles) + :MapperMaker(tmaker) + { + if(!tmaker.FirstShape().IsNull()) + firstProfile = profiles.front(); + if(!tmaker.LastShape().IsNull()) + lastProfile = profiles.back(); + } + virtual const std::vector &generated(const TopoDS_Shape &s) const override { + MapperMaker::generated(s); + if(_res.size()) return _res; + try { + auto &tmaker = static_cast(maker); + auto shape = tmaker.GeneratedFace(s); + if(!shape.IsNull()) + _res.push_back(shape); + if(firstProfile.getShape().IsSame(s) || firstProfile.findShape(s)) + _res.push_back(tmaker.FirstShape()); + else if(lastProfile.getShape().IsSame(s) || lastProfile.findShape(s)) + _res.push_back(tmaker.LastShape()); + } catch (const Standard_Failure & e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } + return _res; + } +}; + +const std::vector & +MapperMaker::modified(const TopoDS_Shape &s) const +{ + _res.clear(); + try { + TopTools_ListIteratorOfListOfShape it; + for (it.Initialize(maker.Modified(s)); it.More(); it.Next()) + _res.push_back(it.Value()); + } catch (const Standard_Failure & e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } + return _res; +} + +const std::vector & +MapperMaker::generated(const TopoDS_Shape &s) const +{ + _res.clear(); + try { + TopTools_ListIteratorOfListOfShape it; + for (it.Initialize(maker.Generated(s)); it.More(); it.Next()) + _res.push_back(it.Value()); + } catch (const Standard_Failure & e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } + return _res; +} } // namespace Part From 42435333ff8be41821f54990b3b071b3425cf913 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Thu, 25 Jan 2024 17:44:46 -0500 Subject: [PATCH 2/3] Clean code and add tests --- src/Mod/Part/App/TopoShape.h | 9 +- src/Mod/Part/App/TopoShapeExpansion.cpp | 135 +- ....sync-conflict-20240128-110016-QVQC5GY.cpp | 1096 +++++++++++++++++ tests/src/Mod/Part/App/CMakeLists.txt | 1 + tests/src/Mod/Part/App/TopoShapeMakeShape.cpp | 129 ++ 5 files changed, 1294 insertions(+), 76 deletions(-) create mode 100644 src/Mod/Part/App/TopoShapeExpansion.sync-conflict-20240128-110016-QVQC5GY.cpp create mode 100644 tests/src/Mod/Part/App/TopoShapeMakeShape.cpp diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 0263b06d3a..2ba595cd13 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -37,11 +37,17 @@ #include #include #include -#include #include #include +// FIXME? +// including instead of the incomplete class declaration +// below results in a broken link on Windows but builds on other platforms. I suspect +// that's why these class declarations appear in the RT branch. +// Something about how that compiler is mangling the names? Maybe we're missing a +// magic windows specific qualifier on the declarations. +class BRepPrimAPI_MakeHalfSpace; class gp_Ax1; class gp_Ax2; class gp_Pln; @@ -56,6 +62,7 @@ namespace Part { class TopoShapeCache; +class TopoShape; /* A special sub-class to indicate null shapes */ diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index c5a122e95e..82145db6b3 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -39,6 +39,7 @@ #include "TopoShapeCache.h" #include "FaceMaker.h" +#include "TopoShapeOpCode.h" FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT @@ -551,8 +552,6 @@ TopoShape::makeElementCompound(const std::vector& shapes, const char* return *this; } -<<<<<<< HEAD -======= struct MapperSewing: Part::TopoShape::Mapper { BRepBuilderAPI_Sewing& maker; @@ -690,7 +689,6 @@ TopoShape& TopoShape::makeElementShape(BRepPrimAPI_MakeHalfSpace& mkShape, return makeShapeWithElementMap(mkShape.Solid(), MapperMaker(mkShape), {source}, op); } ->>>>>>> ad521d6a23 (Missing method) TopoShape& TopoShape::makeElementFace(const TopoShape& shape, const char* op, const char* maker, @@ -950,117 +948,104 @@ TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, return makeShapeWithElementMap(mkShape.Shape(), MapperMaker(mkShape), shapes, op); } -TopoShape &TopoShape::makeElementShape(BRepOffsetAPI_ThruSections &mk, const TopoShape &source, - const char *op) +TopoShape& +TopoShape::makeElementShape(BRepOffsetAPI_ThruSections& mk, const TopoShape& source, const char* op) { - if(!op) op = Part::OpCodes::ThruSections; - return makeElementShape(mk,std::vector(1,source),op); + if (!op) { + op = Part::OpCodes::ThruSections; + } + return makeElementShape(mk, std::vector(1, source), op); } -TopoShape &TopoShape::makeElementShape(BRepOffsetAPI_ThruSections &mk, const std::vector &sources, - const char *op) +TopoShape& TopoShape::makeElementShape(BRepOffsetAPI_ThruSections& mk, + const std::vector& sources, + const char* op) { - if(!op) op = Part::OpCodes::ThruSections; - return makeShapeWithElementMap(mk.Shape(),MapperThruSections(mk,sources),sources,op); + if (!op) { + op = Part::OpCodes::ThruSections; + } + return makeShapeWithElementMap(mk.Shape(), MapperThruSections(mk, sources), sources, op); } -TopoShape &TopoShape::makeElementShape(BRepBuilderAPI_Sewing &mk, const std::vector &shapes, - const char *op) +TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_Sewing& mk, + const std::vector& shapes, + const char* op) { - if(!op) op = Part::OpCodes::Sewing; - return makeShapeWithElementMap(mk.SewedShape(),MapperSewing(mk),shapes,op); + if (!op) { + op = Part::OpCodes::Sewing; + } + return makeShapeWithElementMap(mk.SewedShape(), MapperSewing(mk), shapes, op); } -TopoShape &TopoShape::makeElementShape(BRepBuilderAPI_Sewing &mkShape, - const TopoShape &source, const char *op) +TopoShape& +TopoShape::makeElementShape(BRepBuilderAPI_Sewing& mkShape, const TopoShape& source, const char* op) { - if(!op) op = Part::OpCodes::Sewing; - return makeElementShape(mkShape,std::vector(1,source),op); + if (!op) { + op = Part::OpCodes::Sewing; + } + return makeElementShape(mkShape, std::vector(1, source), op); } -struct MapperSewing: Part::TopoShape::Mapper { - BRepBuilderAPI_Sewing &maker; - MapperSewing(BRepBuilderAPI_Sewing &maker) - :maker(maker) +struct MapperSewing: Part::TopoShape::Mapper +{ + BRepBuilderAPI_Sewing& maker; + MapperSewing(BRepBuilderAPI_Sewing& maker) + : maker(maker) {} - virtual const std::vector &modified(const TopoDS_Shape &s) const override { + virtual const std::vector& modified(const TopoDS_Shape& s) const override + { _res.clear(); try { - const auto &shape = maker.Modified(s); - if(!shape.IsNull() && !shape.IsSame(s)) + const auto& shape = maker.Modified(s); + if (!shape.IsNull() && !shape.IsSame(s)) { _res.push_back(shape); - else { - const auto &sshape = maker.ModifiedSubShape(s); - if(!sshape.IsNull() && !sshape.IsSame(s)) - _res.push_back(sshape); } - } catch (const Standard_Failure & e) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + else { + const auto& sshape = maker.ModifiedSubShape(s); + if (!sshape.IsNull() && !sshape.IsSame(s)) { + _res.push_back(sshape); + } + } + } + catch (const Standard_Failure& e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } } return _res; } }; -struct MapperThruSections: MapperMaker { - TopoShape firstProfile; - TopoShape lastProfile; - - MapperThruSections(BRepOffsetAPI_ThruSections &tmaker, - const std::vector &profiles) - :MapperMaker(tmaker) - { - if(!tmaker.FirstShape().IsNull()) - firstProfile = profiles.front(); - if(!tmaker.LastShape().IsNull()) - lastProfile = profiles.back(); - } - virtual const std::vector &generated(const TopoDS_Shape &s) const override { - MapperMaker::generated(s); - if(_res.size()) return _res; - try { - auto &tmaker = static_cast(maker); - auto shape = tmaker.GeneratedFace(s); - if(!shape.IsNull()) - _res.push_back(shape); - if(firstProfile.getShape().IsSame(s) || firstProfile.findShape(s)) - _res.push_back(tmaker.FirstShape()); - else if(lastProfile.getShape().IsSame(s) || lastProfile.findShape(s)) - _res.push_back(tmaker.LastShape()); - } catch (const Standard_Failure & e) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) - FC_WARN("Exception on shape mapper: " << e.GetMessageString()); - } - return _res; - } -}; - -const std::vector & -MapperMaker::modified(const TopoDS_Shape &s) const +const std::vector& MapperMaker::modified(const TopoDS_Shape& s) const { _res.clear(); try { TopTools_ListIteratorOfListOfShape it; - for (it.Initialize(maker.Modified(s)); it.More(); it.Next()) + for (it.Initialize(maker.Modified(s)); it.More(); it.Next()) { _res.push_back(it.Value()); - } catch (const Standard_Failure & e) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + } + } + catch (const Standard_Failure& e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } } return _res; } -const std::vector & -MapperMaker::generated(const TopoDS_Shape &s) const +const std::vector& MapperMaker::generated(const TopoDS_Shape& s) const { _res.clear(); try { TopTools_ListIteratorOfListOfShape it; - for (it.Initialize(maker.Generated(s)); it.More(); it.Next()) + for (it.Initialize(maker.Generated(s)); it.More(); it.Next()) { _res.push_back(it.Value()); - } catch (const Standard_Failure & e) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + } + } + catch (const Standard_Failure& e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } } return _res; } diff --git a/src/Mod/Part/App/TopoShapeExpansion.sync-conflict-20240128-110016-QVQC5GY.cpp b/src/Mod/Part/App/TopoShapeExpansion.sync-conflict-20240128-110016-QVQC5GY.cpp new file mode 100644 index 0000000000..e96233cfbe --- /dev/null +++ b/src/Mod/Part/App/TopoShapeExpansion.sync-conflict-20240128-110016-QVQC5GY.cpp @@ -0,0 +1,1096 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2022 Zheng, Lei * + * Copyright (c) 2023 FreeCAD Project Association * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +#include +#include +#include +#include + +#include +#include + +#endif + +#include "TopoShape.h" +#include "TopoShapeCache.h" +#include "FaceMaker.h" + +#include "TopoShapeOpCode.h" + +FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT + +namespace Part +{ + +void TopoShape::initCache(int reset) const +{ + if (reset > 0 || !_cache || _cache->isTouched(_Shape)) { + if (_parentCache) { + _parentCache.reset(); + _subLocation.Identity(); + } + _cache = std::make_shared(_Shape); + } +} + +void TopoShape::setShape(const TopoDS_Shape& shape, bool resetElementMap) +{ + if (resetElementMap) { + this->resetElementMap(); + } + else if (_cache && _cache->isTouched(shape)) { + this->flushElementMap(); + } + //_Shape._Shape = shape; // TODO: Replace the next line with this once ShapeProtector is + // available. + _Shape = shape; + if (_cache) { + initCache(); + } +} + + +TopoDS_Shape& TopoShape::move(TopoDS_Shape& tds, const TopLoc_Location& location) +{ +#if OCC_VERSION_HEX < 0x070600 + tds.Move(location); +#else + tds.Move(location, false); +#endif + return tds; +} + +TopoDS_Shape TopoShape::moved(const TopoDS_Shape& tds, const TopLoc_Location& location) +{ +#if OCC_VERSION_HEX < 0x070600 + return tds.Moved(location); +#else + return tds.Moved(location, false); +#endif +} + +TopoDS_Shape& TopoShape::move(TopoDS_Shape& tds, const gp_Trsf& transfer) +{ +#if OCC_VERSION_HEX < 0x070600 + static constexpr double scalePrecision {1e-14}; + if (std::abs(transfer.ScaleFactor()) > scalePrecision) +#else + if (std::abs(transfer.ScaleFactor()) > TopLoc_Location::ScalePrec()) +#endif + { + auto transferCopy(transfer); + transferCopy.SetScaleFactor(1.0); + tds.Move(transferCopy); + } + else { + tds.Move(transfer); + } + return tds; +} + +TopoDS_Shape TopoShape::moved(const TopoDS_Shape& tds, const gp_Trsf& transfer) +{ + TopoDS_Shape sCopy(tds); + return move(sCopy, transfer); +} + +TopoDS_Shape& TopoShape::locate(TopoDS_Shape& tds, const TopLoc_Location& loc) +{ + tds.Location(TopLoc_Location()); + return move(tds, loc); +} + +TopoDS_Shape TopoShape::located(const TopoDS_Shape& tds, const TopLoc_Location& loc) +{ + auto sCopy(tds); + sCopy.Location(TopLoc_Location()); + return moved(sCopy, loc); +} + +TopoDS_Shape& TopoShape::locate(TopoDS_Shape& tds, const gp_Trsf& transfer) +{ + tds.Location(TopLoc_Location()); + return move(tds, transfer); +} + +TopoDS_Shape TopoShape::located(const TopoDS_Shape& tds, const gp_Trsf& transfer) +{ + auto sCopy(tds); + sCopy.Location(TopLoc_Location()); + return moved(sCopy, transfer); +} + + +int TopoShape::findShape(const TopoDS_Shape& subshape) const +{ + initCache(); + return _cache->findShape(_Shape, subshape); +} + + +TopoDS_Shape TopoShape::findShape(const char* name) const +{ + if (!name) { + return {}; + } + + Data::MappedElement res = getElementName(name); + if (!res.index) { + return {}; + } + + auto idx = shapeTypeAndIndex(name); + if (idx.second == 0) { + return {}; + } + initCache(); + return _cache->findShape(_Shape, idx.first, idx.second); +} + +TopoDS_Shape TopoShape::findShape(TopAbs_ShapeEnum type, int idx) const +{ + initCache(); + return _cache->findShape(_Shape, type, idx); +} + +int TopoShape::findAncestor(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const +{ + initCache(); + return _cache->findShape(_Shape, _cache->findAncestor(_Shape, subshape, type)); +} + +TopoDS_Shape TopoShape::findAncestorShape(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const +{ + initCache(); + return _cache->findAncestor(_Shape, subshape, type); +} + +std::vector TopoShape::findAncestors(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const +{ + const auto& shapes = findAncestorsShapes(subshape, type); + std::vector ret; + ret.reserve(shapes.size()); + for (const auto& shape : shapes) { + ret.push_back(findShape(shape)); + } + return ret; +} + +std::vector TopoShape::findAncestorsShapes(const TopoDS_Shape& subshape, + TopAbs_ShapeEnum type) const +{ + initCache(); + std::vector shapes; + _cache->findAncestor(_Shape, subshape, type, &shapes); + return shapes; +} + +// The following lines should be used for now to replace the original macros (in the future we can +// refactor to use std::source_location and eliminate the use of the macros entirely). +// FC_THROWM(NullShapeException, "Null shape"); +// FC_THROWM(NullShapeException, "Null input shape"); +// FC_WARN("Null input shape"); // NOLINT +// +// The original macros: +// #define HANDLE_NULL_SHAPE _HANDLE_NULL_SHAPE("Null shape",true) +// #define HANDLE_NULL_INPUT _HANDLE_NULL_SHAPE("Null input shape",true) +// #define WARN_NULL_INPUT _HANDLE_NULL_SHAPE("Null input shape",false) + +bool TopoShape::hasPendingElementMap() const +{ + return !elementMap(false) && this->_cache + && (this->_parentCache || this->_cache->cachedElementMap); +} + +bool TopoShape::canMapElement(const TopoShape& other) const +{ + if (isNull() || other.isNull() || this == &other || other.Tag == -1 || Tag == -1) { + return false; + } + if ((other.Tag == 0) && !other.elementMap(false) && !other.hasPendingElementMap()) { + return false; + } + initCache(); + other.initCache(); + _cache->relations.clear(); + return true; +} + +namespace +{ +size_t checkSubshapeCount(const TopoShape& topoShape1, + const TopoShape& topoShape2, + TopAbs_ShapeEnum elementType) +{ + auto count = topoShape1.countSubShapes(elementType); + auto other = topoShape2.countSubShapes(elementType); + if (count != other) { + FC_WARN("sub shape mismatch"); // NOLINT + if (count > other) { + count = other; + } + } + return count; +} +} // namespace + +void TopoShape::setupChild(Data::ElementMap::MappedChildElements& child, + TopAbs_ShapeEnum elementType, + const TopoShape& topoShape, + size_t shapeCount, + const char* op) +{ + child.indexedName = Data::IndexedName::fromConst(TopoShape::shapeName(elementType).c_str(), 1); + child.offset = 0; + child.count = static_cast(shapeCount); + child.elementMap = topoShape.elementMap(); + if (this->Tag != topoShape.Tag) { + child.tag = topoShape.Tag; + } + else { + child.tag = 0; + } + if (op) { + child.postfix = op; + } +} + +void TopoShape::copyElementMap(const TopoShape& topoShape, const char* op) +{ + if (topoShape.isNull() || isNull()) { + return; + } + std::vector children; + std::array elementTypes = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE}; + for (const auto elementType : elementTypes) { + auto count = checkSubshapeCount(*this, topoShape, elementType); + if (count == 0) { + continue; + } + children.emplace_back(); + auto& child = children.back(); + setupChild(child, elementType, topoShape, count, op); + } + resetElementMap(); + if (!Hasher) { + Hasher = topoShape.Hasher; + } + setMappedChildElements(children); +} + +namespace +{ +void warnIfLogging() +{ + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("hasher mismatch"); // NOLINT + } +}; + +void hasherMismatchError() +{ + FC_ERR("hasher mismatch"); // NOLINT +} + + +void checkAndMatchHasher(TopoShape& topoShape1, const TopoShape& topoShape2) +{ + if (topoShape1.Hasher) { + if (topoShape2.Hasher != topoShape1.Hasher) { + if (topoShape1.getElementMapSize(false) == 0U) { + warnIfLogging(); + } + else { + hasherMismatchError(); + } + topoShape1.Hasher = topoShape2.Hasher; + } + } + else { + topoShape1.Hasher = topoShape2.Hasher; + } +} +} // namespace + +void TopoShape::mapSubElementTypeForShape(const TopoShape& other, + TopAbs_ShapeEnum type, + const char* op, + int count, + bool forward, + bool& warned) +{ + auto& shapeMap = _cache->getAncestry(type); + auto& otherMap = other._cache->getAncestry(type); + const char* shapeType = shapeName(type).c_str(); + + // 1-indexed for readability (e.g. there is no "Edge0", we started at "Edge1", etc.) + for (int outerCounter = 1; outerCounter <= count; ++outerCounter) { + int innerCounter {0}; + int index {0}; + if (forward) { + innerCounter = outerCounter; + index = shapeMap.find(_Shape, otherMap.find(other._Shape, outerCounter)); + if (index == 0) { + continue; + } + } + else { + index = outerCounter; + innerCounter = otherMap.find(other._Shape, shapeMap.find(_Shape, outerCounter)); + if (innerCounter == 0) { + continue; + } + } + Data::IndexedName element = Data::IndexedName::fromConst(shapeType, index); + for (auto& mappedName : + other.getElementMappedNames(Data::IndexedName::fromConst(shapeType, innerCounter), + true)) { + auto& name = mappedName.first; + auto& sids = mappedName.second; + if (!sids.empty()) { + if (!Hasher) { + Hasher = sids[0].getHasher(); + } + else if (!sids[0].isFromSameHasher(Hasher)) { + if (!warned) { + warned = true; + FC_WARN("hasher mismatch"); // NOLINT + } + sids.clear(); + } + } + std::ostringstream ss; + char elementType {shapeName(type)[0]}; + if ( ! elementMap() ) { + FC_THROWM(NullShapeException, "No element map"); + } + elementMap()->encodeElementName(elementType, name, ss, &sids, Tag, op, other.Tag); + elementMap()->setElementName(element, name, Tag, &sids); + } + } +} + +void TopoShape::mapSubElementForShape(const TopoShape& other, const char* op) +{ + bool warned = false; + static const std::array types = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE}; + + for (auto type : types) { + auto& shapeMap = _cache->getAncestry(type); + auto& otherMap = other._cache->getAncestry(type); + if ((shapeMap.count() == 0) || (otherMap.count() == 0)) { + continue; + } + + bool forward {false}; + int count {0}; + if (otherMap.count() <= shapeMap.count()) { + forward = true; + count = otherMap.count(); + } + else { + forward = false; + count = shapeMap.count(); + } + mapSubElementTypeForShape(other, type, op, count, forward, warned); + } +} + +void TopoShape::mapSubElement(const TopoShape& other, const char* op, bool forceHasher) +{ + if (!canMapElement(other)) { + return; + } + + if ((getElementMapSize(false) == 0U) && this->_Shape.IsPartner(other._Shape)) { + if (!this->Hasher) { + this->Hasher = other.Hasher; + } + copyElementMap(other, op); + return; + } + + if (!forceHasher && other.Hasher) { + checkAndMatchHasher(*this, other); + } + + mapSubElementForShape(other, op); +} + +std::vector +TopoShape::createChildMap(size_t count, const std::vector& shapes, const char* op) +{ + std::vector children; + children.reserve(count * (size_t)3); + std::array types = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE}; + for (const auto topAbsType : types) { + size_t offset = 0; + for (auto& topoShape : shapes) { + if (topoShape.isNull()) { + continue; + } + auto subShapeCount = topoShape.countSubShapes(topAbsType); + if (subShapeCount == 0) { + continue; + } + children.emplace_back(); + auto& child = children.back(); + child.indexedName = + Data::IndexedName::fromConst(TopoShape::shapeName(topAbsType).c_str(), 1); + child.offset = static_cast(offset); + offset += subShapeCount; + child.count = static_cast(subShapeCount); + child.elementMap = topoShape.elementMap(); + child.tag = topoShape.Tag; + if (op) { + child.postfix = op; + } + } + } + return children; +} + +void TopoShape::mapCompoundSubElements(const std::vector& shapes, const char* op) +{ + int count = 0; + for (auto& topoShape : shapes) { + if (topoShape.isNull()) { + continue; + } + ++count; + auto subshape = getSubShape(TopAbs_SHAPE, count, /*silent = */ true); + if (!subshape.IsPartner(topoShape._Shape)) { + return; // Not a partner shape, don't do any mapping at all + } + } + auto children {createChildMap(count, shapes, op)}; + setMappedChildElements(children); +} + +void TopoShape::mapSubElement(const std::vector& shapes, const char* op) +{ + if (shapes.empty()) { + return; + } + + if (shapeType(true) == TopAbs_COMPOUND) { + mapCompoundSubElements(shapes, op); + } + else { + for (auto& shape : shapes) { + mapSubElement(shape, op); + } + } +} + +namespace +{ +void addShapesToBuilder(const std::vector& shapes, + BRep_Builder& builder, + TopoDS_Compound& comp) +{ + int count = 0; + for (auto& topoShape : shapes) { + if (topoShape.isNull()) { + FC_WARN("Null input shape"); // NOLINT + continue; + } + builder.Add(comp, topoShape.getShape()); + ++count; + } + if (count == 0) { + FC_THROWM(NullShapeException, "Null shape"); + } +} +} // namespace + +TopoShape& +TopoShape::makeElementCompound(const std::vector& shapes, const char* op, bool force) +{ + if (!force && shapes.size() == 1) { + *this = shapes[0]; + return *this; + } + + BRep_Builder builder; + TopoDS_Compound comp; + builder.MakeCompound(comp); + + if (shapes.empty()) { + setShape(comp); + return *this; + } + addShapesToBuilder(shapes, builder, comp); + setShape(comp); + initCache(); + + mapSubElement(shapes, op); + return *this; +} + +struct MapperSewing: Part::TopoShape::Mapper +{ + BRepBuilderAPI_Sewing& maker; + MapperSewing(BRepBuilderAPI_Sewing& maker) + : maker(maker) + {} + virtual const std::vector& modified(const TopoDS_Shape& s) const override + { + _res.clear(); + try { + const auto& shape = maker.Modified(s); + if (!shape.IsNull() && !shape.IsSame(s)) { + _res.push_back(shape); + } + else { + const auto& sshape = maker.ModifiedSubShape(s); + if (!sshape.IsNull() && !sshape.IsSame(s)) { + _res.push_back(sshape); + } + } + } + catch (const Standard_Failure& e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } + } + return _res; + } +}; + +struct MapperThruSections: MapperMaker +{ + TopoShape firstProfile; + TopoShape lastProfile; + + MapperThruSections(BRepOffsetAPI_ThruSections& tmaker, const std::vector& profiles) + : MapperMaker(tmaker) + { + if (!tmaker.FirstShape().IsNull()) { + firstProfile = profiles.front(); + } + if (!tmaker.LastShape().IsNull()) { + lastProfile = profiles.back(); + } + } + virtual const std::vector& generated(const TopoDS_Shape& s) const override + { + MapperMaker::generated(s); + if (_res.size()) { + return _res; + } + try { + auto& tmaker = static_cast(maker); + auto shape = tmaker.GeneratedFace(s); + if (!shape.IsNull()) { + _res.push_back(shape); + } + if (firstProfile.getShape().IsSame(s) || firstProfile.findShape(s)) { + _res.push_back(tmaker.FirstShape()); + } + else if (lastProfile.getShape().IsSame(s) || lastProfile.findShape(s)) { + _res.push_back(tmaker.LastShape()); + } + } + catch (const Standard_Failure& e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } + } + return _res; + } +}; + + +TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, + const TopoShape& source, + const char* op) +{ + std::vector sources(1, source); + return makeElementShape(mkShape, sources, op); +} + +TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, + const std::vector& shapes, + const char* op) +{ + return makeShapeWithElementMap(mkShape.Shape(), MapperMaker(mkShape), shapes, op); +} + +TopoShape& +TopoShape::makeElementShape(BRepOffsetAPI_ThruSections& mk, const TopoShape& source, const char* op) +{ + if (!op) { + op = Part::OpCodes::ThruSections; + } + return makeElementShape(mk, std::vector(1, source), op); +} + +TopoShape& TopoShape::makeElementShape(BRepOffsetAPI_ThruSections& mk, + const std::vector& sources, + const char* op) +{ + if (!op) { + op = Part::OpCodes::ThruSections; + } + return makeShapeWithElementMap(mk.Shape(), MapperThruSections(mk, sources), sources, op); +} + +TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_Sewing& mk, + const std::vector& shapes, + const char* op) +{ + if (!op) { + op = Part::OpCodes::Sewing; + } + return makeShapeWithElementMap(mk.SewedShape(), MapperSewing(mk), shapes, op); +} + +TopoShape& +TopoShape::makeElementShape(BRepBuilderAPI_Sewing& mkShape, const TopoShape& source, const char* op) +{ + if (!op) { + op = Part::OpCodes::Sewing; + } + return makeElementShape(mkShape, std::vector(1, source), op); +} + +TopoShape& TopoShape::makeElementShape(BRepPrimAPI_MakeHalfSpace& mkShape, + const TopoShape& source, + const char* op) +{ + if (!op) { + op = Part::OpCodes::HalfSpace; + } + return makeShapeWithElementMap(mkShape.Solid(), MapperMaker(mkShape), {source}, op); +} + +TopoShape& TopoShape::makeElementFace(const TopoShape& shape, + const char* op, + const char* maker, + const gp_Pln* plane) +{ + std::vector shapes; + if (shape.isNull()) { + FC_THROWM(NullShapeException, "Null shape"); + } + if (shape.getShape().ShapeType() == TopAbs_COMPOUND) { + shapes = shape.getSubTopoShapes(); + } + else { + shapes.push_back(shape); + } + return makeElementFace(shapes, op, maker, plane); +} + +TopoShape& TopoShape::makeElementFace(const std::vector& shapes, + const char* op, + const char* maker, + const gp_Pln* plane) +{ + if (!maker || !maker[0]) { + maker = "Part::FaceMakerBullseye"; + } + std::unique_ptr mkFace = FaceMaker::ConstructFromType(maker); + mkFace->MyHasher = Hasher; + mkFace->MyOp = op; + if (plane) { + mkFace->setPlane(*plane); + } + + for (auto& shape : shapes) { + if (shape.getShape().ShapeType() == TopAbs_COMPOUND) { + mkFace->useTopoCompound(shape); + } + else { + mkFace->addTopoShape(shape); + } + } + mkFace->Build(); + + const auto& ret = mkFace->getTopoShape(); + setShape(ret._Shape); + Hasher = ret.Hasher; + resetElementMap(ret.elementMap()); + if (!isValid()) { + ShapeFix_ShapeTolerance aSFT; + aSFT.LimitTolerance(getShape(), + Precision::Confusion(), + Precision::Confusion(), + TopAbs_SHAPE); + + // In some cases, the OCC reports the returned shape having invalid + // tolerance. Not sure about the real cause. + // + // Update: one of the cause is related to OCC bug in + // BRepBuilder_FindPlane, A possible call sequence is, + // + // makEOffset2D() -> TopoShape::findPlane() -> BRepLib_FindSurface + // + // See code comments in findPlane() for the description of the bug and + // work around. + + ShapeFix_Shape fixer(getShape()); + fixer.Perform(); + setShape(fixer.Shape(), false); + + if (!isValid()) { + FC_WARN("makeElementFace: resulting face is invalid"); + } + } + return *this; +} + +/** + * Encode and set an element name in the elementMap. If a hasher is defined, apply it to the name. + * + * @param element The element name(type) that provides 1 one character suffix to the name IF . + * @param names The subnames to build the name from. If empty, return the TopoShape MappedName. + * @param marker The elementMap name or suffix to start the name with. If null, use the + * elementMapPrefix. + * @param op The op text passed to the element name encoder along with the TopoShape Tag + * @param _sids If defined, records the sub ids processed. + * + * @return The encoded, possibly hashed name. + */ +Data::MappedName TopoShape::setElementComboName(const Data::IndexedName& element, + const std::vector& names, + const char* marker, + const char* op, + const Data::ElementIDRefs* _sids) +{ + if (names.empty()) { + return Data::MappedName(); + } + std::string _marker; + if (!marker) { + marker = elementMapPrefix().c_str(); + } + else if (!boost::starts_with(marker, elementMapPrefix())) { + _marker = elementMapPrefix() + marker; + marker = _marker.c_str(); + } + auto it = names.begin(); + Data::MappedName newName = *it; + std::ostringstream ss; + Data::ElementIDRefs sids; + if (_sids) { + sids = *_sids; + } + if (names.size() == 1) { + ss << marker; + } + else { + bool first = true; + ss.str(""); + if (!Hasher) { + ss << marker; + } + ss << '('; + for (++it; it != names.end(); ++it) { + if (first) { + first = false; + } + else { + ss << '|'; + } + ss << *it; + } + ss << ')'; + if (Hasher) { + sids.push_back(Hasher->getID(ss.str().c_str())); + ss.str(""); + ss << marker << sids.back().toString(); + } + } + elementMap()->encodeElementName(element[0], newName, ss, &sids, Tag, op); + return elementMap()->setElementName(element, newName, Tag, &sids); +} + +/** + * Reorient the outer and inner wires of the TopoShape + * + * @param inner If this is not a nullptr, then any inner wires processed will be returned in this + * vector. + * @param reorient One of NoReorient, Reorient ( Outer forward, inner reversed ), + * ReorientForward ( all forward ), or ReorientReversed ( all reversed ) + * @return The outer wire, or an empty TopoShape if this isn't a Face, has no Face subShapes, or the + * outer wire isn't found. + */ +TopoShape TopoShape::splitWires(std::vector* inner, SplitWireReorient reorient) const +{ + // ShapeAnalysis::OuterWire() is un-reliable for some reason. OCC source + // code shows it works by creating face using each wire, and then test using + // BRepTopAdaptor_FClass2d::PerformInfinitePoint() to check if it is an out + // bound wire. And practice shows it sometimes returns the incorrect + // result. Need more investigation. Note that this may be related to + // unreliable solid face orientation + // (https://forum.freecadweb.org/viewtopic.php?p=446006#p445674) + // + // Use BrepTools::OuterWire() instead. OCC source code shows it is + // implemented using simple bound box checking. This should be a + // reliable method, especially so for a planar face. + + TopoDS_Shape tmp; + if (shapeType(true) == TopAbs_FACE) { + tmp = BRepTools::OuterWire(TopoDS::Face(_Shape)); + } + else if (countSubShapes(TopAbs_FACE) == 1) { + tmp = BRepTools::OuterWire(TopoDS::Face(getSubShape(TopAbs_FACE, 1))); + } + if (tmp.IsNull()) { + return TopoShape(); + } + const auto& wires = getSubTopoShapes(TopAbs_WIRE); + auto it = wires.begin(); + + TopAbs_Orientation orientOuter, orientInner; + switch (reorient) { + case ReorientReversed: + orientOuter = orientInner = TopAbs_REVERSED; + break; + case ReorientForward: + orientOuter = orientInner = TopAbs_FORWARD; + break; + default: + orientOuter = TopAbs_FORWARD; + orientInner = TopAbs_REVERSED; + break; + } + + auto doReorient = [](TopoShape& s, TopAbs_Orientation orient) { + // Special case of single edge wire. Make sure the edge is in the + // required orientation. This is necessary because BRepFill_OffsetWire + // has special handling of circular edge offset, which seem to only + // respect the edge orientation and disregard the wire orientation. The + // orientation is used to determine whether to shrink or expand. + if (s.countSubShapes(TopAbs_EDGE) == 1) { + TopoDS_Shape e = s.getSubShape(TopAbs_EDGE, 1); + if (e.Orientation() == orient) { + if (s._Shape.Orientation() == orient) { + return; + } + } + else { + e = e.Oriented(orient); + } + BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(e)); + s.setShape(mkWire.Shape(), false); + } + else if (s._Shape.Orientation() != orient) { + s.setShape(s._Shape.Oriented(orient), false); + } + }; + + for (; it != wires.end(); ++it) { + auto& wire = *it; + if (wire.getShape().IsSame(tmp)) { + if (inner) { + for (++it; it != wires.end(); ++it) { + inner->push_back(*it); + if (reorient) { + doReorient(inner->back(), orientInner); + } + } + } + auto res = wire; + if (reorient) { + doReorient(res, orientOuter); + } + return res; + } + if (inner) { + inner->push_back(wire); + if (reorient) { + doReorient(inner->back(), orientInner); + } + } + } + return TopoShape(); +} + +TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, + const TopoShape& source, + const char* op) +{ + std::vector sources(1, source); + return makeElementShape(mkShape, sources, op); +} + +TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, + const std::vector& shapes, + const char* op) +{ + return makeShapeWithElementMap(mkShape.Shape(), MapperMaker(mkShape), shapes, op); +} + +TopoShape& +TopoShape::makeElementShape(BRepOffsetAPI_ThruSections& mk, const TopoShape& source, const char* op) +{ + if (!op) { + op = Part::OpCodes::ThruSections; + } + return makeElementShape(mk, std::vector(1, source), op); +} + +TopoShape& TopoShape::makeElementShape(BRepOffsetAPI_ThruSections& mk, + const std::vector& sources, + const char* op) +{ + if (!op) { + op = Part::OpCodes::ThruSections; + } + return makeShapeWithElementMap(mk.Shape(), MapperThruSections(mk, sources), sources, op); +} + +TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_Sewing& mk, + const std::vector& shapes, + const char* op) +{ + if (!op) { + op = Part::OpCodes::Sewing; + } + return makeShapeWithElementMap(mk.SewedShape(), MapperSewing(mk), shapes, op); +} + +TopoShape& +TopoShape::makeElementShape(BRepBuilderAPI_Sewing& mkShape, const TopoShape& source, const char* op) +{ + if (!op) { + op = Part::OpCodes::Sewing; + } + return makeElementShape(mkShape, std::vector(1, source), op); +} + +struct MapperSewing: Part::TopoShape::Mapper +{ + BRepBuilderAPI_Sewing& maker; + MapperSewing(BRepBuilderAPI_Sewing& maker) + : maker(maker) + {} + virtual const std::vector& modified(const TopoDS_Shape& s) const override + { + _res.clear(); + try { + const auto& shape = maker.Modified(s); + if (!shape.IsNull() && !shape.IsSame(s)) { + _res.push_back(shape); + } + else { + const auto& sshape = maker.ModifiedSubShape(s); + if (!sshape.IsNull() && !sshape.IsSame(s)) { + _res.push_back(sshape); + } + } + } + catch (const Standard_Failure& e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } + } + return _res; + } +}; + +struct MapperThruSections: MapperMaker +{ + TopoShape firstProfile; + TopoShape lastProfile; + + MapperThruSections(BRepOffsetAPI_ThruSections& tmaker, const std::vector& profiles) + : MapperMaker(tmaker) + { + if (!tmaker.FirstShape().IsNull()) { + firstProfile = profiles.front(); + } + if (!tmaker.LastShape().IsNull()) { + lastProfile = profiles.back(); + } + } + virtual const std::vector& generated(const TopoDS_Shape& s) const override + { + MapperMaker::generated(s); + if (_res.size()) { + return _res; + } + try { + auto& tmaker = static_cast(maker); + auto shape = tmaker.GeneratedFace(s); + if (!shape.IsNull()) { + _res.push_back(shape); + } + if (firstProfile.getShape().IsSame(s) || firstProfile.findShape(s)) { + _res.push_back(tmaker.FirstShape()); + } + else if (lastProfile.getShape().IsSame(s) || lastProfile.findShape(s)) { + _res.push_back(tmaker.LastShape()); + } + } + catch (const Standard_Failure& e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } + } + return _res; + } +}; + +const std::vector& MapperMaker::modified(const TopoDS_Shape& s) const +{ + _res.clear(); + try { + TopTools_ListIteratorOfListOfShape it; + for (it.Initialize(maker.Modified(s)); it.More(); it.Next()) { + _res.push_back(it.Value()); + } + } + catch (const Standard_Failure& e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } + } + return _res; +} + +const std::vector& MapperMaker::generated(const TopoDS_Shape& s) const +{ + _res.clear(); + try { + TopTools_ListIteratorOfListOfShape it; + for (it.Initialize(maker.Generated(s)); it.More(); it.Next()) { + _res.push_back(it.Value()); + } + } + catch (const Standard_Failure& e) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("Exception on shape mapper: " << e.GetMessageString()); + } + } + return _res; +} + +} // namespace Part diff --git a/tests/src/Mod/Part/App/CMakeLists.txt b/tests/src/Mod/Part/App/CMakeLists.txt index 9c642e65e2..8eac8afe17 100644 --- a/tests/src/Mod/Part/App/CMakeLists.txt +++ b/tests/src/Mod/Part/App/CMakeLists.txt @@ -16,4 +16,5 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeCache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeExpansion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeMapper.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeMakeShape.cpp ) diff --git a/tests/src/Mod/Part/App/TopoShapeMakeShape.cpp b/tests/src/Mod/Part/App/TopoShapeMakeShape.cpp new file mode 100644 index 0000000000..11dd498309 --- /dev/null +++ b/tests/src/Mod/Part/App/TopoShapeMakeShape.cpp @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +// Tests for the makeShape methods, extracted from the main set of tests for TopoShape +// due to length and complexity. + +#include "gtest/gtest.h" +#include "src/App/InitApplication.h" +#include "PartTestHelpers.h" +#include + +#include + +using namespace Data; +using namespace Part; +using namespace PartTestHelpers; + +class TopoShapeMakeShapeTests: public ::testing::Test +{ +protected: + static void SetUpTestSuite() + { + tests::initApplication(); + } + + void SetUp() override + { + _docName = App::GetApplication().getUniqueDocumentName("test"); + App::GetApplication().newDocument(_docName.c_str(), "testUser"); + _sids = &_sid; + } + + void TearDown() override + { + App::GetApplication().closeDocument(_docName.c_str()); + } + + Part::TopoShape* Shape() + { + return &_shape; + } + + Part::TopoShape::Mapper* Mapper() + { + return &_mapper; + } + +private: + std::string _docName; + Data::ElementIDRefs _sid; + QVector* _sids = nullptr; + Part::TopoShape _shape; + Part::TopoShape::Mapper _mapper; +}; + +TEST_F(TopoShapeMakeShapeTests, nullShapeThrows) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + std::vector sources {cube1, cube2}; + TopoDS_Vertex nullShape; + + // Act and assert + EXPECT_THROW(Shape()->makeShapeWithElementMap(nullShape, *Mapper(), sources), + Part::NullShapeException); +} + +TEST_F(TopoShapeMakeShapeTests, shapeVertex) +{ + // Arrange + BRepBuilderAPI_MakeVertex vertexMaker = BRepBuilderAPI_MakeVertex(gp_Pnt(10, 10, 10)); + TopoShape topoShape(vertexMaker.Vertex(), 1L); + // Act + TopoShape& result = topoShape.makeElementShape(vertexMaker, topoShape); + auto elements = elementMap(result); + // Assert + EXPECT_EQ(elements.size(), 1); + EXPECT_EQ(elements.count(IndexedName("Vertex", 1)), 1); + EXPECT_EQ(elements[IndexedName("Vertex", 1)], MappedName("Vertex1;MAK;:H:4,V")); + EXPECT_EQ(getArea(result.getShape()), 0); +} + +TEST_F(TopoShapeMakeShapeTests, thruSections) +{ + // Arrange + auto [face1, wire1, edge1, edge2, edge3, edge4] = CreateRectFace(); + TopoDS_Wire wire2 = wire1; + auto transform {gp_Trsf()}; + transform.SetTranslation(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(0.0, 0.5, 1.0)); + wire2.Move(TopLoc_Location(transform)); + TopoShape wire1ts {wire1, 1L}; + TopoShape wire2ts {wire2, 2L}; + BRepOffsetAPI_ThruSections thruMaker; + thruMaker.AddWire(wire1); + thruMaker.AddWire(wire2); + TopoShape topoShape {}; + // Act + TopoShape& result = topoShape.makeElementShape(thruMaker, {wire1ts, wire2ts}); + auto elements = elementMap(result); + // Assert + EXPECT_EQ(elements.size(), 24); + EXPECT_EQ(elements.count(IndexedName("Vertex", 1)), 1); + EXPECT_EQ(elements[IndexedName("Vertex", 1)], MappedName("Vertex1;TRU;:H1:4,V")); + EXPECT_EQ(getVolume(result.getShape()), 4); +} + +TEST_F(TopoShapeMakeShapeTests, sewing) +{ + // Arrange + auto [face1, wire1, edge1, edge2, edge3, edge4] = CreateRectFace(); + auto face2 = face1; + auto transform {gp_Trsf()}; + transform.SetTranslation(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(0.5, 0.5, 0.0)); + face2.Move(TopLoc_Location(transform)); + BRepBuilderAPI_Sewing sewer; + sewer.Add(face1); + sewer.Add(face2); + sewer.Perform(); + std::vector sources {{face1, 1L}, {face2, 2L}}; + TopoShape topoShape {}; + // Act + TopoShape& result = topoShape.makeElementShape(sewer, sources); + auto elements = elementMap(result); + // Assert + EXPECT_EQ(&result, &topoShape); + EXPECT_EQ(elements.size(), 18); // Now a single cube + EXPECT_EQ(elements.count(IndexedName("Vertex", 1)), 1); + EXPECT_EQ(elements[IndexedName("Vertex", 1)], MappedName("Vertex1;SEW;:H1:4,V")); + EXPECT_EQ(getArea(result.getShape()), 12); +} From 408b4dcd85249b5fe74dfa94f245fbf5e09b5a92 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Sun, 28 Jan 2024 11:40:27 -0500 Subject: [PATCH 3/3] Cleanup merge issues and linter concerns --- src/Mod/Part/App/TopoShape.h | 29 +- src/Mod/Part/App/TopoShapeExpansion.cpp | 148 +-- ....sync-conflict-20240128-110016-QVQC5GY.cpp | 1096 ----------------- 3 files changed, 43 insertions(+), 1230 deletions(-) delete mode 100644 src/Mod/Part/App/TopoShapeExpansion.sync-conflict-20240128-110016-QVQC5GY.cpp diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index f779a7a6c6..876b221e0e 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -40,14 +40,6 @@ #include #include -// FIXME? -// including instead of the incomplete class declaration -// below results in a broken link on Windows but builds on other platforms. I suspect -// that's why these class declarations appear in the RT branch. -// Something about how that compiler is mangling the names? Maybe we're missing a -// magic windows specific qualifier on the declarations. - -class BRepPrimAPI_MakeHalfSpace; class gp_Ax1; class gp_Ax2; class gp_Pln; @@ -62,7 +54,6 @@ namespace Part { class TopoShapeCache; -class TopoShape; /* A special sub-class to indicate null shapes */ @@ -731,9 +722,9 @@ public: * a self reference so that multiple operations can be carried out * for the same shape in the same line of code. */ - // TopoShape& makeElementShellFromWires(const std::vector& wires, - // bool silent = true, - // const char* op = nullptr); + TopoShape& makeElementShellFromWires(const std::vector& wires, + bool silent = true, + const char* op = nullptr); /* Make a shell with input wires * * @param wires: input wires @@ -743,10 +734,10 @@ public: * * @return Return the new shape. The TopoShape itself is not modified. */ - // TopoShape& makeElementShellFromWires(bool silent = true, const char* op = nullptr) - // { - // return makeElementShellFromWires(getSubTopoShapes(TopAbs_WIRE), silent, op); - // } + TopoShape& makeElementShellFromWires(bool silent = true, const char* op = nullptr) + { + return makeElementShellFromWires(getSubTopoShapes(TopAbs_WIRE), silent, op); + } TopoShape& makeElementFace(const std::vector& shapes, const char* op = nullptr, @@ -1195,11 +1186,11 @@ private: struct PartExport MapperMaker: TopoShape::Mapper { BRepBuilderAPI_MakeShape& maker; - MapperMaker(BRepBuilderAPI_MakeShape& maker) + explicit MapperMaker(BRepBuilderAPI_MakeShape& maker) : maker(maker) {} - virtual const std::vector& modified(const TopoDS_Shape& s) const override; - virtual const std::vector& generated(const TopoDS_Shape& s) const override; + const std::vector& modified(const TopoDS_Shape& s) const override; + const std::vector& generated(const TopoDS_Shape& s) const override; }; } // namespace Part diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 733d1b495e..b861b58eb4 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -42,7 +42,6 @@ #include "TopoShape.h" #include "TopoShapeCache.h" -#include "TopoShapeOpCode.h" #include "FaceMaker.h" #include "TopoShapeOpCode.h" @@ -263,6 +262,7 @@ size_t checkSubshapeCount(const TopoShape& topoShape1, } return count; } + } // namespace void TopoShape::setupChild(Data::ElementMap::MappedChildElements& child, @@ -316,7 +316,7 @@ void warnIfLogging() if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { FC_WARN("hasher mismatch"); // NOLINT } -}; +} void hasherMismatchError() { @@ -392,11 +392,11 @@ void TopoShape::mapSubElementTypeForShape(const TopoShape& other, sids.clear(); } } - std::ostringstream ss; char elementType {shapeName(type)[0]}; if (!elementMap()) { FC_THROWM(NullShapeException, "No element map"); // NOLINT } + std::ostringstream ss; elementMap()->encodeElementName(elementType, name, ss, &sids, Tag, op, other.Tag); elementMap()->setElementName(element, name, Tag, &sids); } @@ -530,7 +530,7 @@ struct ShapeInfo , shapetype(TopoShape::shapeName(type).c_str()) {} - int count() const + [[nodiscard]] int count() const { return cache.count(); } @@ -1301,10 +1301,10 @@ TopoShape::makeElementCompound(const std::vector& shapes, const char* struct MapperSewing: Part::TopoShape::Mapper { BRepBuilderAPI_Sewing& maker; - MapperSewing(BRepBuilderAPI_Sewing& maker) + explicit MapperSewing(BRepBuilderAPI_Sewing& maker) : maker(maker) {} - virtual const std::vector& modified(const TopoDS_Shape& s) const override + const std::vector& modified(const TopoDS_Shape& s) const override { _res.clear(); try { @@ -1343,14 +1343,14 @@ struct MapperThruSections: MapperMaker lastProfile = profiles.back(); } } - virtual const std::vector& generated(const TopoDS_Shape& s) const override + const std::vector& generated(const TopoDS_Shape& s) const override { MapperMaker::generated(s); - if (_res.size()) { + if ( ! _res.empty()) { return _res; } try { - auto& tmaker = static_cast(maker); + auto& tmaker = dynamic_cast(maker); auto shape = tmaker.GeneratedFace(s); if (!shape.IsNull()) { _res.push_back(shape); @@ -1531,7 +1531,7 @@ Data::MappedName TopoShape::setElementComboName(const Data::IndexedName& element const Data::ElementIDRefs* _sids) { if (names.empty()) { - return Data::MappedName(); + return Data::MappedName {}; } std::string _marker; if (!marker) { @@ -1610,7 +1610,7 @@ TopoShape TopoShape::splitWires(std::vector* inner, SplitWireReorient tmp = BRepTools::OuterWire(TopoDS::Face(getSubShape(TopAbs_FACE, 1))); } if (tmp.IsNull()) { - return TopoShape(); + return TopoShape {}; } const auto& wires = getSubTopoShapes(TopAbs_WIRE); auto it = wires.begin(); @@ -1677,91 +1677,9 @@ TopoShape TopoShape::splitWires(std::vector* inner, SplitWireReorient } } } - return TopoShape(); + return TopoShape {}; } -TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, - const TopoShape& source, - const char* op) -{ - std::vector sources(1, source); - return makeElementShape(mkShape, sources, op); -} - -TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, - const std::vector& shapes, - const char* op) -{ - return makeShapeWithElementMap(mkShape.Shape(), MapperMaker(mkShape), shapes, op); -} - -TopoShape& -TopoShape::makeElementShape(BRepOffsetAPI_ThruSections& mk, const TopoShape& source, const char* op) -{ - if (!op) { - op = Part::OpCodes::ThruSections; - } - return makeElementShape(mk, std::vector(1, source), op); -} - -TopoShape& TopoShape::makeElementShape(BRepOffsetAPI_ThruSections& mk, - const std::vector& sources, - const char* op) -{ - if (!op) { - op = Part::OpCodes::ThruSections; - } - return makeShapeWithElementMap(mk.Shape(), MapperThruSections(mk, sources), sources, op); -} - -TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_Sewing& mk, - const std::vector& shapes, - const char* op) -{ - if (!op) { - op = Part::OpCodes::Sewing; - } - return makeShapeWithElementMap(mk.SewedShape(), MapperSewing(mk), shapes, op); -} - -TopoShape& -TopoShape::makeElementShape(BRepBuilderAPI_Sewing& mkShape, const TopoShape& source, const char* op) -{ - if (!op) { - op = Part::OpCodes::Sewing; - } - return makeElementShape(mkShape, std::vector(1, source), op); -} - -struct MapperSewing: Part::TopoShape::Mapper -{ - BRepBuilderAPI_Sewing& maker; - MapperSewing(BRepBuilderAPI_Sewing& maker) - : maker(maker) - {} - virtual const std::vector& modified(const TopoDS_Shape& s) const override - { - _res.clear(); - try { - const auto& shape = maker.Modified(s); - if (!shape.IsNull() && !shape.IsSame(s)) { - _res.push_back(shape); - } - else { - const auto& sshape = maker.ModifiedSubShape(s); - if (!sshape.IsNull() && !sshape.IsSame(s)) { - _res.push_back(sshape); - } - } - } - catch (const Standard_Failure& e) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - FC_WARN("Exception on shape mapper: " << e.GetMessageString()); - } - } - return _res; - } -}; struct MapperFill: Part::TopoShape::Mapper { BRepFill_Generator& maker; @@ -1911,26 +1829,26 @@ TopoShape& TopoShape::makeElementShell(bool silent, const char* op) return *this; } -// TopoShape& TopoShape::makeElementShellFromWires(const std::vector& wires, -// bool silent, -// const char* op) -// { -// BRepFill_Generator maker; -// for (auto& w : wires) { -// if (w.shapeType(silent) == TopAbs_WIRE) { -// maker.AddWire(TopoDS::Wire(w.getShape())); -// } -// } -// if (wires.empty()) { -// if (silent) { -// _Shape.Nullify(); -// return *this; -// } -// FC_THROWM(NullShapeException, "No input shapes"); -// } -// maker.Perform(); -// this->makeShapeWithElementMap(maker.Shell(), MapperFill(maker), wires, op); -// return *this; -// } +TopoShape& TopoShape::makeElementShellFromWires(const std::vector& wires, + bool silent, + const char* op) +{ + BRepFill_Generator maker; + for (auto& w : wires) { + if (w.shapeType(silent) == TopAbs_WIRE) { + maker.AddWire(TopoDS::Wire(w.getShape())); + } + } + if (wires.empty()) { + if (silent) { + _Shape.Nullify(); + return *this; + } + FC_THROWM(NullShapeException, "No input shapes"); + } + maker.Perform(); + this->makeShapeWithElementMap(maker.Shell(), MapperFill(maker), wires, op); + return *this; +} } // namespace Part diff --git a/src/Mod/Part/App/TopoShapeExpansion.sync-conflict-20240128-110016-QVQC5GY.cpp b/src/Mod/Part/App/TopoShapeExpansion.sync-conflict-20240128-110016-QVQC5GY.cpp deleted file mode 100644 index e96233cfbe..0000000000 --- a/src/Mod/Part/App/TopoShapeExpansion.sync-conflict-20240128-110016-QVQC5GY.cpp +++ /dev/null @@ -1,1096 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1-or-later -/**************************************************************************** - * * - * Copyright (c) 2022 Zheng, Lei * - * Copyright (c) 2023 FreeCAD Project Association * - * * - * This file is part of FreeCAD. * - * * - * FreeCAD is free software: you can redistribute it and/or modify it * - * under the terms of the GNU Lesser General Public License as * - * published by the Free Software Foundation, either version 2.1 of the * - * License, or (at your option) any later version. * - * * - * FreeCAD is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with FreeCAD. If not, see * - * . * - * * - ***************************************************************************/ - - -#include "PreCompiled.h" -#ifndef _PreComp_ -#include -#include -#include -#include - -#include -#include - -#endif - -#include "TopoShape.h" -#include "TopoShapeCache.h" -#include "FaceMaker.h" - -#include "TopoShapeOpCode.h" - -FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT - -namespace Part -{ - -void TopoShape::initCache(int reset) const -{ - if (reset > 0 || !_cache || _cache->isTouched(_Shape)) { - if (_parentCache) { - _parentCache.reset(); - _subLocation.Identity(); - } - _cache = std::make_shared(_Shape); - } -} - -void TopoShape::setShape(const TopoDS_Shape& shape, bool resetElementMap) -{ - if (resetElementMap) { - this->resetElementMap(); - } - else if (_cache && _cache->isTouched(shape)) { - this->flushElementMap(); - } - //_Shape._Shape = shape; // TODO: Replace the next line with this once ShapeProtector is - // available. - _Shape = shape; - if (_cache) { - initCache(); - } -} - - -TopoDS_Shape& TopoShape::move(TopoDS_Shape& tds, const TopLoc_Location& location) -{ -#if OCC_VERSION_HEX < 0x070600 - tds.Move(location); -#else - tds.Move(location, false); -#endif - return tds; -} - -TopoDS_Shape TopoShape::moved(const TopoDS_Shape& tds, const TopLoc_Location& location) -{ -#if OCC_VERSION_HEX < 0x070600 - return tds.Moved(location); -#else - return tds.Moved(location, false); -#endif -} - -TopoDS_Shape& TopoShape::move(TopoDS_Shape& tds, const gp_Trsf& transfer) -{ -#if OCC_VERSION_HEX < 0x070600 - static constexpr double scalePrecision {1e-14}; - if (std::abs(transfer.ScaleFactor()) > scalePrecision) -#else - if (std::abs(transfer.ScaleFactor()) > TopLoc_Location::ScalePrec()) -#endif - { - auto transferCopy(transfer); - transferCopy.SetScaleFactor(1.0); - tds.Move(transferCopy); - } - else { - tds.Move(transfer); - } - return tds; -} - -TopoDS_Shape TopoShape::moved(const TopoDS_Shape& tds, const gp_Trsf& transfer) -{ - TopoDS_Shape sCopy(tds); - return move(sCopy, transfer); -} - -TopoDS_Shape& TopoShape::locate(TopoDS_Shape& tds, const TopLoc_Location& loc) -{ - tds.Location(TopLoc_Location()); - return move(tds, loc); -} - -TopoDS_Shape TopoShape::located(const TopoDS_Shape& tds, const TopLoc_Location& loc) -{ - auto sCopy(tds); - sCopy.Location(TopLoc_Location()); - return moved(sCopy, loc); -} - -TopoDS_Shape& TopoShape::locate(TopoDS_Shape& tds, const gp_Trsf& transfer) -{ - tds.Location(TopLoc_Location()); - return move(tds, transfer); -} - -TopoDS_Shape TopoShape::located(const TopoDS_Shape& tds, const gp_Trsf& transfer) -{ - auto sCopy(tds); - sCopy.Location(TopLoc_Location()); - return moved(sCopy, transfer); -} - - -int TopoShape::findShape(const TopoDS_Shape& subshape) const -{ - initCache(); - return _cache->findShape(_Shape, subshape); -} - - -TopoDS_Shape TopoShape::findShape(const char* name) const -{ - if (!name) { - return {}; - } - - Data::MappedElement res = getElementName(name); - if (!res.index) { - return {}; - } - - auto idx = shapeTypeAndIndex(name); - if (idx.second == 0) { - return {}; - } - initCache(); - return _cache->findShape(_Shape, idx.first, idx.second); -} - -TopoDS_Shape TopoShape::findShape(TopAbs_ShapeEnum type, int idx) const -{ - initCache(); - return _cache->findShape(_Shape, type, idx); -} - -int TopoShape::findAncestor(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const -{ - initCache(); - return _cache->findShape(_Shape, _cache->findAncestor(_Shape, subshape, type)); -} - -TopoDS_Shape TopoShape::findAncestorShape(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const -{ - initCache(); - return _cache->findAncestor(_Shape, subshape, type); -} - -std::vector TopoShape::findAncestors(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const -{ - const auto& shapes = findAncestorsShapes(subshape, type); - std::vector ret; - ret.reserve(shapes.size()); - for (const auto& shape : shapes) { - ret.push_back(findShape(shape)); - } - return ret; -} - -std::vector TopoShape::findAncestorsShapes(const TopoDS_Shape& subshape, - TopAbs_ShapeEnum type) const -{ - initCache(); - std::vector shapes; - _cache->findAncestor(_Shape, subshape, type, &shapes); - return shapes; -} - -// The following lines should be used for now to replace the original macros (in the future we can -// refactor to use std::source_location and eliminate the use of the macros entirely). -// FC_THROWM(NullShapeException, "Null shape"); -// FC_THROWM(NullShapeException, "Null input shape"); -// FC_WARN("Null input shape"); // NOLINT -// -// The original macros: -// #define HANDLE_NULL_SHAPE _HANDLE_NULL_SHAPE("Null shape",true) -// #define HANDLE_NULL_INPUT _HANDLE_NULL_SHAPE("Null input shape",true) -// #define WARN_NULL_INPUT _HANDLE_NULL_SHAPE("Null input shape",false) - -bool TopoShape::hasPendingElementMap() const -{ - return !elementMap(false) && this->_cache - && (this->_parentCache || this->_cache->cachedElementMap); -} - -bool TopoShape::canMapElement(const TopoShape& other) const -{ - if (isNull() || other.isNull() || this == &other || other.Tag == -1 || Tag == -1) { - return false; - } - if ((other.Tag == 0) && !other.elementMap(false) && !other.hasPendingElementMap()) { - return false; - } - initCache(); - other.initCache(); - _cache->relations.clear(); - return true; -} - -namespace -{ -size_t checkSubshapeCount(const TopoShape& topoShape1, - const TopoShape& topoShape2, - TopAbs_ShapeEnum elementType) -{ - auto count = topoShape1.countSubShapes(elementType); - auto other = topoShape2.countSubShapes(elementType); - if (count != other) { - FC_WARN("sub shape mismatch"); // NOLINT - if (count > other) { - count = other; - } - } - return count; -} -} // namespace - -void TopoShape::setupChild(Data::ElementMap::MappedChildElements& child, - TopAbs_ShapeEnum elementType, - const TopoShape& topoShape, - size_t shapeCount, - const char* op) -{ - child.indexedName = Data::IndexedName::fromConst(TopoShape::shapeName(elementType).c_str(), 1); - child.offset = 0; - child.count = static_cast(shapeCount); - child.elementMap = topoShape.elementMap(); - if (this->Tag != topoShape.Tag) { - child.tag = topoShape.Tag; - } - else { - child.tag = 0; - } - if (op) { - child.postfix = op; - } -} - -void TopoShape::copyElementMap(const TopoShape& topoShape, const char* op) -{ - if (topoShape.isNull() || isNull()) { - return; - } - std::vector children; - std::array elementTypes = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE}; - for (const auto elementType : elementTypes) { - auto count = checkSubshapeCount(*this, topoShape, elementType); - if (count == 0) { - continue; - } - children.emplace_back(); - auto& child = children.back(); - setupChild(child, elementType, topoShape, count, op); - } - resetElementMap(); - if (!Hasher) { - Hasher = topoShape.Hasher; - } - setMappedChildElements(children); -} - -namespace -{ -void warnIfLogging() -{ - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - FC_WARN("hasher mismatch"); // NOLINT - } -}; - -void hasherMismatchError() -{ - FC_ERR("hasher mismatch"); // NOLINT -} - - -void checkAndMatchHasher(TopoShape& topoShape1, const TopoShape& topoShape2) -{ - if (topoShape1.Hasher) { - if (topoShape2.Hasher != topoShape1.Hasher) { - if (topoShape1.getElementMapSize(false) == 0U) { - warnIfLogging(); - } - else { - hasherMismatchError(); - } - topoShape1.Hasher = topoShape2.Hasher; - } - } - else { - topoShape1.Hasher = topoShape2.Hasher; - } -} -} // namespace - -void TopoShape::mapSubElementTypeForShape(const TopoShape& other, - TopAbs_ShapeEnum type, - const char* op, - int count, - bool forward, - bool& warned) -{ - auto& shapeMap = _cache->getAncestry(type); - auto& otherMap = other._cache->getAncestry(type); - const char* shapeType = shapeName(type).c_str(); - - // 1-indexed for readability (e.g. there is no "Edge0", we started at "Edge1", etc.) - for (int outerCounter = 1; outerCounter <= count; ++outerCounter) { - int innerCounter {0}; - int index {0}; - if (forward) { - innerCounter = outerCounter; - index = shapeMap.find(_Shape, otherMap.find(other._Shape, outerCounter)); - if (index == 0) { - continue; - } - } - else { - index = outerCounter; - innerCounter = otherMap.find(other._Shape, shapeMap.find(_Shape, outerCounter)); - if (innerCounter == 0) { - continue; - } - } - Data::IndexedName element = Data::IndexedName::fromConst(shapeType, index); - for (auto& mappedName : - other.getElementMappedNames(Data::IndexedName::fromConst(shapeType, innerCounter), - true)) { - auto& name = mappedName.first; - auto& sids = mappedName.second; - if (!sids.empty()) { - if (!Hasher) { - Hasher = sids[0].getHasher(); - } - else if (!sids[0].isFromSameHasher(Hasher)) { - if (!warned) { - warned = true; - FC_WARN("hasher mismatch"); // NOLINT - } - sids.clear(); - } - } - std::ostringstream ss; - char elementType {shapeName(type)[0]}; - if ( ! elementMap() ) { - FC_THROWM(NullShapeException, "No element map"); - } - elementMap()->encodeElementName(elementType, name, ss, &sids, Tag, op, other.Tag); - elementMap()->setElementName(element, name, Tag, &sids); - } - } -} - -void TopoShape::mapSubElementForShape(const TopoShape& other, const char* op) -{ - bool warned = false; - static const std::array types = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE}; - - for (auto type : types) { - auto& shapeMap = _cache->getAncestry(type); - auto& otherMap = other._cache->getAncestry(type); - if ((shapeMap.count() == 0) || (otherMap.count() == 0)) { - continue; - } - - bool forward {false}; - int count {0}; - if (otherMap.count() <= shapeMap.count()) { - forward = true; - count = otherMap.count(); - } - else { - forward = false; - count = shapeMap.count(); - } - mapSubElementTypeForShape(other, type, op, count, forward, warned); - } -} - -void TopoShape::mapSubElement(const TopoShape& other, const char* op, bool forceHasher) -{ - if (!canMapElement(other)) { - return; - } - - if ((getElementMapSize(false) == 0U) && this->_Shape.IsPartner(other._Shape)) { - if (!this->Hasher) { - this->Hasher = other.Hasher; - } - copyElementMap(other, op); - return; - } - - if (!forceHasher && other.Hasher) { - checkAndMatchHasher(*this, other); - } - - mapSubElementForShape(other, op); -} - -std::vector -TopoShape::createChildMap(size_t count, const std::vector& shapes, const char* op) -{ - std::vector children; - children.reserve(count * (size_t)3); - std::array types = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE}; - for (const auto topAbsType : types) { - size_t offset = 0; - for (auto& topoShape : shapes) { - if (topoShape.isNull()) { - continue; - } - auto subShapeCount = topoShape.countSubShapes(topAbsType); - if (subShapeCount == 0) { - continue; - } - children.emplace_back(); - auto& child = children.back(); - child.indexedName = - Data::IndexedName::fromConst(TopoShape::shapeName(topAbsType).c_str(), 1); - child.offset = static_cast(offset); - offset += subShapeCount; - child.count = static_cast(subShapeCount); - child.elementMap = topoShape.elementMap(); - child.tag = topoShape.Tag; - if (op) { - child.postfix = op; - } - } - } - return children; -} - -void TopoShape::mapCompoundSubElements(const std::vector& shapes, const char* op) -{ - int count = 0; - for (auto& topoShape : shapes) { - if (topoShape.isNull()) { - continue; - } - ++count; - auto subshape = getSubShape(TopAbs_SHAPE, count, /*silent = */ true); - if (!subshape.IsPartner(topoShape._Shape)) { - return; // Not a partner shape, don't do any mapping at all - } - } - auto children {createChildMap(count, shapes, op)}; - setMappedChildElements(children); -} - -void TopoShape::mapSubElement(const std::vector& shapes, const char* op) -{ - if (shapes.empty()) { - return; - } - - if (shapeType(true) == TopAbs_COMPOUND) { - mapCompoundSubElements(shapes, op); - } - else { - for (auto& shape : shapes) { - mapSubElement(shape, op); - } - } -} - -namespace -{ -void addShapesToBuilder(const std::vector& shapes, - BRep_Builder& builder, - TopoDS_Compound& comp) -{ - int count = 0; - for (auto& topoShape : shapes) { - if (topoShape.isNull()) { - FC_WARN("Null input shape"); // NOLINT - continue; - } - builder.Add(comp, topoShape.getShape()); - ++count; - } - if (count == 0) { - FC_THROWM(NullShapeException, "Null shape"); - } -} -} // namespace - -TopoShape& -TopoShape::makeElementCompound(const std::vector& shapes, const char* op, bool force) -{ - if (!force && shapes.size() == 1) { - *this = shapes[0]; - return *this; - } - - BRep_Builder builder; - TopoDS_Compound comp; - builder.MakeCompound(comp); - - if (shapes.empty()) { - setShape(comp); - return *this; - } - addShapesToBuilder(shapes, builder, comp); - setShape(comp); - initCache(); - - mapSubElement(shapes, op); - return *this; -} - -struct MapperSewing: Part::TopoShape::Mapper -{ - BRepBuilderAPI_Sewing& maker; - MapperSewing(BRepBuilderAPI_Sewing& maker) - : maker(maker) - {} - virtual const std::vector& modified(const TopoDS_Shape& s) const override - { - _res.clear(); - try { - const auto& shape = maker.Modified(s); - if (!shape.IsNull() && !shape.IsSame(s)) { - _res.push_back(shape); - } - else { - const auto& sshape = maker.ModifiedSubShape(s); - if (!sshape.IsNull() && !sshape.IsSame(s)) { - _res.push_back(sshape); - } - } - } - catch (const Standard_Failure& e) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - FC_WARN("Exception on shape mapper: " << e.GetMessageString()); - } - } - return _res; - } -}; - -struct MapperThruSections: MapperMaker -{ - TopoShape firstProfile; - TopoShape lastProfile; - - MapperThruSections(BRepOffsetAPI_ThruSections& tmaker, const std::vector& profiles) - : MapperMaker(tmaker) - { - if (!tmaker.FirstShape().IsNull()) { - firstProfile = profiles.front(); - } - if (!tmaker.LastShape().IsNull()) { - lastProfile = profiles.back(); - } - } - virtual const std::vector& generated(const TopoDS_Shape& s) const override - { - MapperMaker::generated(s); - if (_res.size()) { - return _res; - } - try { - auto& tmaker = static_cast(maker); - auto shape = tmaker.GeneratedFace(s); - if (!shape.IsNull()) { - _res.push_back(shape); - } - if (firstProfile.getShape().IsSame(s) || firstProfile.findShape(s)) { - _res.push_back(tmaker.FirstShape()); - } - else if (lastProfile.getShape().IsSame(s) || lastProfile.findShape(s)) { - _res.push_back(tmaker.LastShape()); - } - } - catch (const Standard_Failure& e) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - FC_WARN("Exception on shape mapper: " << e.GetMessageString()); - } - } - return _res; - } -}; - - -TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, - const TopoShape& source, - const char* op) -{ - std::vector sources(1, source); - return makeElementShape(mkShape, sources, op); -} - -TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, - const std::vector& shapes, - const char* op) -{ - return makeShapeWithElementMap(mkShape.Shape(), MapperMaker(mkShape), shapes, op); -} - -TopoShape& -TopoShape::makeElementShape(BRepOffsetAPI_ThruSections& mk, const TopoShape& source, const char* op) -{ - if (!op) { - op = Part::OpCodes::ThruSections; - } - return makeElementShape(mk, std::vector(1, source), op); -} - -TopoShape& TopoShape::makeElementShape(BRepOffsetAPI_ThruSections& mk, - const std::vector& sources, - const char* op) -{ - if (!op) { - op = Part::OpCodes::ThruSections; - } - return makeShapeWithElementMap(mk.Shape(), MapperThruSections(mk, sources), sources, op); -} - -TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_Sewing& mk, - const std::vector& shapes, - const char* op) -{ - if (!op) { - op = Part::OpCodes::Sewing; - } - return makeShapeWithElementMap(mk.SewedShape(), MapperSewing(mk), shapes, op); -} - -TopoShape& -TopoShape::makeElementShape(BRepBuilderAPI_Sewing& mkShape, const TopoShape& source, const char* op) -{ - if (!op) { - op = Part::OpCodes::Sewing; - } - return makeElementShape(mkShape, std::vector(1, source), op); -} - -TopoShape& TopoShape::makeElementShape(BRepPrimAPI_MakeHalfSpace& mkShape, - const TopoShape& source, - const char* op) -{ - if (!op) { - op = Part::OpCodes::HalfSpace; - } - return makeShapeWithElementMap(mkShape.Solid(), MapperMaker(mkShape), {source}, op); -} - -TopoShape& TopoShape::makeElementFace(const TopoShape& shape, - const char* op, - const char* maker, - const gp_Pln* plane) -{ - std::vector shapes; - if (shape.isNull()) { - FC_THROWM(NullShapeException, "Null shape"); - } - if (shape.getShape().ShapeType() == TopAbs_COMPOUND) { - shapes = shape.getSubTopoShapes(); - } - else { - shapes.push_back(shape); - } - return makeElementFace(shapes, op, maker, plane); -} - -TopoShape& TopoShape::makeElementFace(const std::vector& shapes, - const char* op, - const char* maker, - const gp_Pln* plane) -{ - if (!maker || !maker[0]) { - maker = "Part::FaceMakerBullseye"; - } - std::unique_ptr mkFace = FaceMaker::ConstructFromType(maker); - mkFace->MyHasher = Hasher; - mkFace->MyOp = op; - if (plane) { - mkFace->setPlane(*plane); - } - - for (auto& shape : shapes) { - if (shape.getShape().ShapeType() == TopAbs_COMPOUND) { - mkFace->useTopoCompound(shape); - } - else { - mkFace->addTopoShape(shape); - } - } - mkFace->Build(); - - const auto& ret = mkFace->getTopoShape(); - setShape(ret._Shape); - Hasher = ret.Hasher; - resetElementMap(ret.elementMap()); - if (!isValid()) { - ShapeFix_ShapeTolerance aSFT; - aSFT.LimitTolerance(getShape(), - Precision::Confusion(), - Precision::Confusion(), - TopAbs_SHAPE); - - // In some cases, the OCC reports the returned shape having invalid - // tolerance. Not sure about the real cause. - // - // Update: one of the cause is related to OCC bug in - // BRepBuilder_FindPlane, A possible call sequence is, - // - // makEOffset2D() -> TopoShape::findPlane() -> BRepLib_FindSurface - // - // See code comments in findPlane() for the description of the bug and - // work around. - - ShapeFix_Shape fixer(getShape()); - fixer.Perform(); - setShape(fixer.Shape(), false); - - if (!isValid()) { - FC_WARN("makeElementFace: resulting face is invalid"); - } - } - return *this; -} - -/** - * Encode and set an element name in the elementMap. If a hasher is defined, apply it to the name. - * - * @param element The element name(type) that provides 1 one character suffix to the name IF . - * @param names The subnames to build the name from. If empty, return the TopoShape MappedName. - * @param marker The elementMap name or suffix to start the name with. If null, use the - * elementMapPrefix. - * @param op The op text passed to the element name encoder along with the TopoShape Tag - * @param _sids If defined, records the sub ids processed. - * - * @return The encoded, possibly hashed name. - */ -Data::MappedName TopoShape::setElementComboName(const Data::IndexedName& element, - const std::vector& names, - const char* marker, - const char* op, - const Data::ElementIDRefs* _sids) -{ - if (names.empty()) { - return Data::MappedName(); - } - std::string _marker; - if (!marker) { - marker = elementMapPrefix().c_str(); - } - else if (!boost::starts_with(marker, elementMapPrefix())) { - _marker = elementMapPrefix() + marker; - marker = _marker.c_str(); - } - auto it = names.begin(); - Data::MappedName newName = *it; - std::ostringstream ss; - Data::ElementIDRefs sids; - if (_sids) { - sids = *_sids; - } - if (names.size() == 1) { - ss << marker; - } - else { - bool first = true; - ss.str(""); - if (!Hasher) { - ss << marker; - } - ss << '('; - for (++it; it != names.end(); ++it) { - if (first) { - first = false; - } - else { - ss << '|'; - } - ss << *it; - } - ss << ')'; - if (Hasher) { - sids.push_back(Hasher->getID(ss.str().c_str())); - ss.str(""); - ss << marker << sids.back().toString(); - } - } - elementMap()->encodeElementName(element[0], newName, ss, &sids, Tag, op); - return elementMap()->setElementName(element, newName, Tag, &sids); -} - -/** - * Reorient the outer and inner wires of the TopoShape - * - * @param inner If this is not a nullptr, then any inner wires processed will be returned in this - * vector. - * @param reorient One of NoReorient, Reorient ( Outer forward, inner reversed ), - * ReorientForward ( all forward ), or ReorientReversed ( all reversed ) - * @return The outer wire, or an empty TopoShape if this isn't a Face, has no Face subShapes, or the - * outer wire isn't found. - */ -TopoShape TopoShape::splitWires(std::vector* inner, SplitWireReorient reorient) const -{ - // ShapeAnalysis::OuterWire() is un-reliable for some reason. OCC source - // code shows it works by creating face using each wire, and then test using - // BRepTopAdaptor_FClass2d::PerformInfinitePoint() to check if it is an out - // bound wire. And practice shows it sometimes returns the incorrect - // result. Need more investigation. Note that this may be related to - // unreliable solid face orientation - // (https://forum.freecadweb.org/viewtopic.php?p=446006#p445674) - // - // Use BrepTools::OuterWire() instead. OCC source code shows it is - // implemented using simple bound box checking. This should be a - // reliable method, especially so for a planar face. - - TopoDS_Shape tmp; - if (shapeType(true) == TopAbs_FACE) { - tmp = BRepTools::OuterWire(TopoDS::Face(_Shape)); - } - else if (countSubShapes(TopAbs_FACE) == 1) { - tmp = BRepTools::OuterWire(TopoDS::Face(getSubShape(TopAbs_FACE, 1))); - } - if (tmp.IsNull()) { - return TopoShape(); - } - const auto& wires = getSubTopoShapes(TopAbs_WIRE); - auto it = wires.begin(); - - TopAbs_Orientation orientOuter, orientInner; - switch (reorient) { - case ReorientReversed: - orientOuter = orientInner = TopAbs_REVERSED; - break; - case ReorientForward: - orientOuter = orientInner = TopAbs_FORWARD; - break; - default: - orientOuter = TopAbs_FORWARD; - orientInner = TopAbs_REVERSED; - break; - } - - auto doReorient = [](TopoShape& s, TopAbs_Orientation orient) { - // Special case of single edge wire. Make sure the edge is in the - // required orientation. This is necessary because BRepFill_OffsetWire - // has special handling of circular edge offset, which seem to only - // respect the edge orientation and disregard the wire orientation. The - // orientation is used to determine whether to shrink or expand. - if (s.countSubShapes(TopAbs_EDGE) == 1) { - TopoDS_Shape e = s.getSubShape(TopAbs_EDGE, 1); - if (e.Orientation() == orient) { - if (s._Shape.Orientation() == orient) { - return; - } - } - else { - e = e.Oriented(orient); - } - BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(e)); - s.setShape(mkWire.Shape(), false); - } - else if (s._Shape.Orientation() != orient) { - s.setShape(s._Shape.Oriented(orient), false); - } - }; - - for (; it != wires.end(); ++it) { - auto& wire = *it; - if (wire.getShape().IsSame(tmp)) { - if (inner) { - for (++it; it != wires.end(); ++it) { - inner->push_back(*it); - if (reorient) { - doReorient(inner->back(), orientInner); - } - } - } - auto res = wire; - if (reorient) { - doReorient(res, orientOuter); - } - return res; - } - if (inner) { - inner->push_back(wire); - if (reorient) { - doReorient(inner->back(), orientInner); - } - } - } - return TopoShape(); -} - -TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, - const TopoShape& source, - const char* op) -{ - std::vector sources(1, source); - return makeElementShape(mkShape, sources, op); -} - -TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape, - const std::vector& shapes, - const char* op) -{ - return makeShapeWithElementMap(mkShape.Shape(), MapperMaker(mkShape), shapes, op); -} - -TopoShape& -TopoShape::makeElementShape(BRepOffsetAPI_ThruSections& mk, const TopoShape& source, const char* op) -{ - if (!op) { - op = Part::OpCodes::ThruSections; - } - return makeElementShape(mk, std::vector(1, source), op); -} - -TopoShape& TopoShape::makeElementShape(BRepOffsetAPI_ThruSections& mk, - const std::vector& sources, - const char* op) -{ - if (!op) { - op = Part::OpCodes::ThruSections; - } - return makeShapeWithElementMap(mk.Shape(), MapperThruSections(mk, sources), sources, op); -} - -TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_Sewing& mk, - const std::vector& shapes, - const char* op) -{ - if (!op) { - op = Part::OpCodes::Sewing; - } - return makeShapeWithElementMap(mk.SewedShape(), MapperSewing(mk), shapes, op); -} - -TopoShape& -TopoShape::makeElementShape(BRepBuilderAPI_Sewing& mkShape, const TopoShape& source, const char* op) -{ - if (!op) { - op = Part::OpCodes::Sewing; - } - return makeElementShape(mkShape, std::vector(1, source), op); -} - -struct MapperSewing: Part::TopoShape::Mapper -{ - BRepBuilderAPI_Sewing& maker; - MapperSewing(BRepBuilderAPI_Sewing& maker) - : maker(maker) - {} - virtual const std::vector& modified(const TopoDS_Shape& s) const override - { - _res.clear(); - try { - const auto& shape = maker.Modified(s); - if (!shape.IsNull() && !shape.IsSame(s)) { - _res.push_back(shape); - } - else { - const auto& sshape = maker.ModifiedSubShape(s); - if (!sshape.IsNull() && !sshape.IsSame(s)) { - _res.push_back(sshape); - } - } - } - catch (const Standard_Failure& e) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - FC_WARN("Exception on shape mapper: " << e.GetMessageString()); - } - } - return _res; - } -}; - -struct MapperThruSections: MapperMaker -{ - TopoShape firstProfile; - TopoShape lastProfile; - - MapperThruSections(BRepOffsetAPI_ThruSections& tmaker, const std::vector& profiles) - : MapperMaker(tmaker) - { - if (!tmaker.FirstShape().IsNull()) { - firstProfile = profiles.front(); - } - if (!tmaker.LastShape().IsNull()) { - lastProfile = profiles.back(); - } - } - virtual const std::vector& generated(const TopoDS_Shape& s) const override - { - MapperMaker::generated(s); - if (_res.size()) { - return _res; - } - try { - auto& tmaker = static_cast(maker); - auto shape = tmaker.GeneratedFace(s); - if (!shape.IsNull()) { - _res.push_back(shape); - } - if (firstProfile.getShape().IsSame(s) || firstProfile.findShape(s)) { - _res.push_back(tmaker.FirstShape()); - } - else if (lastProfile.getShape().IsSame(s) || lastProfile.findShape(s)) { - _res.push_back(tmaker.LastShape()); - } - } - catch (const Standard_Failure& e) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - FC_WARN("Exception on shape mapper: " << e.GetMessageString()); - } - } - return _res; - } -}; - -const std::vector& MapperMaker::modified(const TopoDS_Shape& s) const -{ - _res.clear(); - try { - TopTools_ListIteratorOfListOfShape it; - for (it.Initialize(maker.Modified(s)); it.More(); it.Next()) { - _res.push_back(it.Value()); - } - } - catch (const Standard_Failure& e) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - FC_WARN("Exception on shape mapper: " << e.GetMessageString()); - } - } - return _res; -} - -const std::vector& MapperMaker::generated(const TopoDS_Shape& s) const -{ - _res.clear(); - try { - TopTools_ListIteratorOfListOfShape it; - for (it.Initialize(maker.Generated(s)); it.More(); it.Next()) { - _res.push_back(it.Value()); - } - } - catch (const Standard_Failure& e) { - if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { - FC_WARN("Exception on shape mapper: " << e.GetMessageString()); - } - } - return _res; -} - -} // namespace Part