diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index ca96b028f9..13fe666184 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -606,6 +606,29 @@ public: // double tol=1e-7, double atol=1e-12) const; //@} + void copyElementMap(const TopoShape &other, const char *op=nullptr); + bool canMapElement(const TopoShape &other) const; + void mapSubElement(const TopoShape &other,const char *op=nullptr, bool forceHasher=false); + void mapSubElement(const std::vector &shapes, const char *op); + bool hasPendingElementMap() const; + + + /** Make a compound shape + * + * @param shapes: input shapes + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param force: if true and there is only one input shape, then return + * that shape instead. If false, then always return a + * compound, even if there is no input shape. + * + * @return The original content of this TopoShape is discarded and replaced + * with the new shape. The function returns the TopoShape itself as + * a reference so that multiple operations can be carried out for + * the same shape in the same line of code. + */ + TopoShape &makeElementCompound(const std::vector &shapes, const char *op=nullptr, bool force=true); + friend class TopoShapeCache; private: diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 09b90fe98f..e26a6a43e8 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -22,8 +22,12 @@ * * ***************************************************************************/ -#include + #include "PreCompiled.h" +#ifndef _PreComp_ +#include +#include +#endif #include "TopoShape.h" #include "TopoShapeCache.h" @@ -196,4 +200,212 @@ std::vector TopoShape::findAncestorsShapes(const TopoDS_Shape& sub return shapes; } +#define _HANDLE_NULL_SHAPE(_msg,_throw) do {\ + if(_throw) {\ + FC_THROWM(NullShapeException,_msg);\ + }\ + FC_WARN(_msg);\ +}while(0) + +#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 + && !other.elementMap(false) + && !other.hasPendingElementMap()) + return false; + initCache(); + other.initCache(); + _cache->relations.clear(); + return true; +} + +void TopoShape::mapSubElement(const TopoShape &other, const char *op, bool forceHasher) { +#ifdef FC_NO_ELEMENT_MAP + return; +#endif + + if(!canMapElement(other)) + return; + + if (!getElementMapSize(false) && this->_Shape.IsPartner(other._Shape)) { + if (!this->Hasher) + this->Hasher = other.Hasher; + copyElementMap(other, op); + return; + } + + bool warned = false; + static const std::array types = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE}; + + auto checkHasher = [this](const TopoShape &other) { + if(Hasher) { + if(other.Hasher!=Hasher) { + if(!getElementMapSize(false)) { + if(FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) + FC_WARN("hasher mismatch"); + }else { + // FC_THROWM(Base::RuntimeError, "hasher mismatch"); + FC_ERR("hasher mismatch"); + } + Hasher = other.Hasher; + } + }else + Hasher = other.Hasher; + }; + + for(auto type : types) { + auto &shapeMap = _cache->getAncestry(type); + auto &otherMap = other._cache->getAncestry(type); + if(!shapeMap.count() || !otherMap.count()) + continue; + if(!forceHasher && other.Hasher) { + forceHasher = true; + checkHasher(other); + } + const char *shapetype = shapeName(type).c_str(); + std::ostringstream ss; + + bool forward; + int count; + if(otherMap.count()<=shapeMap.count()) { + forward = true; + count = otherMap.count(); + }else{ + forward = false; + count = shapeMap.count(); + } + for(int k=1;k<=count;++k) { + int i,idx; + if(forward) { + i = k; + idx = shapeMap.find(_Shape,otherMap.find(other._Shape,k)); + if(!idx) continue; + } else { + idx = k; + i = otherMap.find(other._Shape,shapeMap.find(_Shape,k)); + if(!i) continue; + } + Data::IndexedName element = Data::IndexedName::fromConst(shapetype, idx); + for(auto &v : other.getElementMappedNames( + Data::IndexedName::fromConst(shapetype,i),true)) + { + auto &name = v.first; + auto &sids = v.second; + if(sids.size()) { + if (!Hasher) + Hasher = sids[0].getHasher(); + else if (!sids[0].isFromSameHasher(Hasher)) { + if (!warned) { + warned = true; + FC_WARN("hasher mismatch"); + } + sids.clear(); + } + } + ss.str(""); + elementMap()->encodeElementName(shapetype[0],name,ss,&sids,Tag,op,other.Tag); + elementMap()->setElementName(element,name,Tag, &sids); + } + } + } +} + +void TopoShape::mapSubElement(const std::vector &shapes, const char *op) { +#ifdef FC_NO_ELEMENT_MAP + return; +#endif + + if (shapes.empty()) + return; + + if (shapeType(true) == TopAbs_COMPOUND) { + int count = 0; + for (auto & s : shapes) { + if (s.isNull()) + continue; + if (!getSubShape(TopAbs_SHAPE, ++count, true).IsPartner(s._Shape)) { + count = 0; + break; + } + } + if (count) { + std::vector children; + children.reserve(count*3); + TopAbs_ShapeEnum types[] = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE}; + for (unsigned i=0; iaddChildElements(Tag, children); // Replaces the original line below + //setMappedChildElements(children); + return; + } + } + + for(auto &shape : shapes) + mapSubElement(shape,op); +} + +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; + } + + int count = 0; + for(auto &s : shapes) { + if(s.isNull()) { + WARN_NULL_INPUT; + continue; + } + builder.Add(comp,s.getShape()); + ++count; + } + if(!count) + HANDLE_NULL_SHAPE; + setShape(comp); + initCache(); + + mapSubElement(shapes,op); + return *this; +} + } // namespace Part