From e5ef0b5833450403a29a65e4f8a0a8c9c53e64f9 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Tue, 27 Feb 2024 15:33:21 -0500 Subject: [PATCH] Toposhape/Part: element methods in ComplexGeoData and TopoShape --- src/App/ComplexGeoData.cpp | 76 +++++++++++++++++++++++++ src/App/ComplexGeoData.h | 22 +++++++ src/Mod/Part/App/TopoShape.cpp | 1 + src/Mod/Part/App/TopoShapeExpansion.cpp | 21 +++++++ 4 files changed, 120 insertions(+) diff --git a/src/App/ComplexGeoData.cpp b/src/App/ComplexGeoData.cpp index 717e173cb7..89941ea3f3 100644 --- a/src/App/ComplexGeoData.cpp +++ b/src/App/ComplexGeoData.cpp @@ -637,6 +637,82 @@ unsigned int ComplexGeoData::getMemSize() const return 0; } +void ComplexGeoData::traceElement(const MappedName &name, TraceCallback cb) const +{ + long tag = this->Tag, encodedTag = 0; + int len = 0; + + auto pos = findTagInElementName(name,&encodedTag,&len,nullptr,nullptr,true); + if(cb(name, len, encodedTag, tag) || pos < 0) + return; + + if (name.startsWith(externalTagPostfix(), len)) + return; + + std::set tagSet; + + std::vector names; + if (tag) + tagSet.insert(std::abs(tag)); + if (encodedTag) + tagSet.insert(std::abs(encodedTag)); + names.push_back(name); + + tag = encodedTag; + MappedName tmp; + bool first = true; + + // TODO: element tracing without object is inheriently unsafe, because of + // possible external linking object which means the element may be encoded + // using external string table. Looking up the wrong table may accidentally + // cause circular mapping, and is actually quite easy to reproduce. See + // + // https://github.com/realthunder/FreeCAD_assembly3/issues/968 + // + // A random depth limit is set here to not waste time. 'tagSet' above is + // also used for early detection of 'recursive' mapping. + + for (int i=0; i<50; ++i) { + if(!len || len>pos) + return; + if(first) { + first = false; + size_t offset = 0; + if(name.startsWith(elementMapPrefix())) + offset = elementMapPrefix().size(); + tmp = MappedName(name, offset, len); + }else + tmp = MappedName(tmp, 0, len); + tmp = dehashElementName(tmp); + names.push_back(tmp); + encodedTag = 0; + pos = findTagInElementName(tmp,&encodedTag,&len,nullptr,nullptr,true); + if (pos >= 0 && tmp.startsWith(externalTagPostfix(), len)) + break; + + if (encodedTag && tag != std::abs(encodedTag) + && !tagSet.insert(std::abs(encodedTag)).second) { + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + FC_WARN("circular element mapping"); + if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_TRACE)) { + auto doc = App::GetApplication().getActiveDocument(); + if (doc) { + auto obj = doc->getObjectByID(this->Tag); + if (obj) + FC_LOG("\t" << obj->getFullName() << obj->getFullName() << "." << getIndexedName(name)); + } + for (auto &name : names) + FC_ERR("\t" << name); + } + } + break; + } + + if(cb(tmp, len, encodedTag, tag) || pos < 0) + return; + tag = encodedTag; + } +} void ComplexGeoData::setMappedChildElements(const std::vector & children) { diff --git a/src/App/ComplexGeoData.h b/src/App/ComplexGeoData.h index 0ce246a5e6..7d9699178b 100644 --- a/src/App/ComplexGeoData.h +++ b/src/App/ComplexGeoData.h @@ -301,6 +301,28 @@ public: static bool isElementName(const char *subName) { return (subName != nullptr) && (*subName != 0) && findElementName(subName)==subName; } + /** Element trace callback + * + * The callback has the following call signature + * (const std::string &name, size_t offset, long encodedTag, long tag) -> bool + * + * @param name: the current element name. + * @param offset: the offset skipping the encoded element name for the next iteration. + * @param encodedTag: the tag encoded inside the current element, which is usually the tag + * of the previous step in the shape history. + * @param tag: the tag of the current shape element. + * + * @sa traceElement() + */ + typedef std::function TraceCallback; + + /** Iterate through the history of the give element name with a given callback + * + * @param name: the input element name + * @param cb: trace callback with call signature. + * @sa TraceCallback + */ + void traceElement(const MappedName& name, TraceCallback cb) const; /** Flush an internal buffering for element mapping */ virtual void flushElementMap() const; diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index b303f2ad50..883d7b3a77 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -4084,6 +4084,7 @@ TopoShape &TopoShape::makeRefine(const TopoShape &shape, const char *op, RefineF return *this; } +// TODO: Does the toponaming branch version of this method need to be here? bool TopoShape::findPlane(gp_Pln &pln, double tol) const { if(_Shape.IsNull()) return false; diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index f2b50f95d5..ff46e6596b 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -5003,4 +5003,25 @@ bool TopoShape::isSame(const Data::ComplexGeoData &_other) const && _Shape.IsEqual(other._Shape); } +void TopoShape::cacheRelatedElements(const Data::MappedName &name, + bool sameType, + const QVector & names) const +{ + INIT_SHAPE_CACHE(); + _Cache->insertRelation(ShapeRelationKey(name,sameType), names); +} + +bool TopoShape::getRelatedElementsCached(const Data::MappedName &name, + bool sameType, + QVector &names) const +{ + if(!_Cache) + return false; + auto it = _Cache->relations.find(ShapeRelationKey(name,sameType)); + if(it == _Cache->relations.end()) + return false; + names = it->second; + return true; +} + } // namespace Part