diff --git a/src/App/ComplexGeoData.h b/src/App/ComplexGeoData.h index 2f6804432d..40b7d3fe27 100644 --- a/src/App/ComplexGeoData.h +++ b/src/App/ComplexGeoData.h @@ -53,6 +53,12 @@ namespace Data { //struct MappedChildElements; +/// Option for App::GeoFeature::searchElementCache() +enum class SearchOptions { + /// Whether to compare shape geometry + CheckGeometry = 1, + SingleResult = 2, +}; /** Segments * Sub-element type of the ComplexGeoData type diff --git a/src/App/GeoFeature.cpp b/src/App/GeoFeature.cpp index 4cb34bedd6..1d9e6d763c 100644 --- a/src/App/GeoFeature.cpp +++ b/src/App/GeoFeature.cpp @@ -30,6 +30,7 @@ #include "GeoFeature.h" #include "GeoFeatureGroupExtension.h" #include "ElementNamingUtils.h" +#include "Link.h" using namespace App; @@ -141,6 +142,10 @@ DocumentObject *GeoFeature::resolveElement(DocumentObject *obj, const char *subn ElementNameType type, const DocumentObject *filter, const char **_element, GeoFeature **geoFeature) { +#ifdef FC_USE_TNP_FIX + elementName.first.clear(); + elementName.second.clear(); +#endif if(!obj || !obj->isAttachedToDocument()) return nullptr; if(!subname) @@ -148,13 +153,16 @@ DocumentObject *GeoFeature::resolveElement(DocumentObject *obj, const char *subn const char *element = Data::findElementName(subname); if(_element) *_element = element; #ifdef FC_USE_TNP_FIX - elementName.first.clear(); - elementName.second.clear(); auto sobj = obj->getSubObject(std::string(subname, element).c_str()); if(!sobj) return nullptr; auto linked = sobj->getLinkedObject(true); auto geo = Base::freecad_dynamic_cast(linked); + if(!geo && linked) { + auto ext = linked->getExtensionByType(true); + if(ext) + geo = Base::freecad_dynamic_cast(ext->getTrueLinkedObject(true)); + } #else auto sobj = obj->getSubObject(subname); if(!sobj) @@ -202,43 +210,73 @@ void GeoFeature::setMaterialAppearance(const App::Material& material) } #ifdef FC_USE_TNP_FIX -bool GeoFeature::hasMissingElement(const char *subname) { +bool GeoFeature::hasMissingElement(const char* subname) +{ return Data::hasMissingElement(subname); - if(!subname) + if (!subname) { return false; - auto dot = strrchr(subname,'.'); - if(!dot) - return subname[0]=='?'; - return dot[1]=='?'; + } + auto dot = strrchr(subname, '.'); + if (!dot) { + return subname[0] == '?'; + } + return dot[1] == '?'; } -void GeoFeature::updateElementReference() { +void GeoFeature::updateElementReference() +{ auto prop = getPropertyOfGeometry(); - if(!prop) return; + if (!prop) { + return; + } auto geo = prop->getComplexData(); - if(!geo) return; + if (!geo) { + return; + } bool reset = false; - PropertyLinkBase::updateElementReferences(this,reset); + PropertyLinkBase::updateElementReferences(this, reset); } -void GeoFeature::onChanged(const Property *prop) { - if(prop==getPropertyOfGeometry()) { - if(getDocument() && !getDocument()->testStatus(Document::Restoring) - && !getDocument()->isPerformingTransaction()) - { +void GeoFeature::onChanged(const Property* prop) +{ + if (prop == getPropertyOfGeometry()) { + if (getDocument() && !getDocument()->testStatus(Document::Restoring) + && !getDocument()->isPerformingTransaction()) { updateElementReference(); } } DocumentObject::onChanged(prop); } +const std::vector& GeoFeature::searchElementCache(const std::string& element, + Data::SearchOptions options, + double tol, + double atol) const +{ + static std::vector none; + (void)element; + (void)options; + (void)tol; + (void)atol; + return none; +} -std::vector -GeoFeature::getHigherElements(const char *element, bool silent) const +const std::vector& GeoFeature::getElementTypes(bool /*all*/) const +{ + static std::vector nil; + auto prop = getPropertyOfGeometry(); + if (!prop) { + return nil; + } + return prop->getComplexData()->getElementTypes(); +} + +std::vector GeoFeature::getHigherElements(const char* element, bool silent) const { auto prop = getPropertyOfGeometry(); - if (!prop) + if (!prop) { return {}; + } return prop->getComplexData()->getHigherElements(element, silent); } #endif diff --git a/src/App/GeoFeature.h b/src/App/GeoFeature.h index 3dcdffd6eb..025d5f969e 100644 --- a/src/App/GeoFeature.h +++ b/src/App/GeoFeature.h @@ -28,6 +28,7 @@ #include "PropertyGeo.h" #include "MappedElement.h" #include "Material.h" +#include "ComplexGeoData.h" namespace App { @@ -141,12 +142,34 @@ public: */ virtual void setMaterialAppearance(const App::Material& material); #ifdef FC_USE_TNP_FIX + /** Search sub element using internal cached geometry + * + * @param element: element name + * @param options: search options + * @param tol: coordinate tolerance + * @param atol: angle tolerance + * + * @return Returns a list of found element reference to the new geometry. + * The returned value will be invalidated when the geometry is changed. + * + * Before changing the property of geometry, GeoFeature will internally + * make a snapshot of all referenced element geometry. After change, user + * code may call this function to search for the new element name that + * reference to the same geometry of the old element. + */ + virtual const std::vector& searchElementCache(const std::string &element, + Data::SearchOptions options = Data::SearchOptions::CheckGeometry, + double tol = 1e-7, + double atol = 1e-10) const; + static bool hasMissingElement(const char *subname); /// Return the object that owns the shape that contains the give element name virtual DocumentObject *getElementOwner(const Data::MappedName & /*name*/) const {return nullptr;} + virtual const std::vector& getElementTypes(bool all=true) const; + /// Return the higher level element names of the given element virtual std::vector getHigherElements(const char *name, bool silent=false) const; diff --git a/src/App/Link.cpp b/src/App/Link.cpp index 84ecc88b54..4a9933378f 100644 --- a/src/App/Link.cpp +++ b/src/App/Link.cpp @@ -1420,8 +1420,22 @@ void LinkBaseExtension::checkGeoElementMap(const App::DocumentObject *obj, !PyObject_TypeCheck(*pyObj, &Data::ComplexGeoDataPy::Type)) return; - // auto geoData = static_cast(*pyObj)->getComplexGeoDataPtr(); - // geoData->reTagElementMap(obj->getID(),obj->getDocument()->Hasher,postfix); +// auto geoData = static_cast(*pyObj)->getComplexGeoDataPtr(); +// geoData->reTagElementMap(obj->getID(),obj->getDocument()->Hasher,postfix); + + auto geoData = static_cast(*pyObj)->getComplexGeoDataPtr(); + std::string _postfix; + if (linked && obj && linked->getDocument() != obj->getDocument()) { + _postfix = Data::POSTFIX_EXTERNAL_TAG; + if (postfix) { + if (!boost::starts_with(postfix, Data::ComplexGeoData::elementMapPrefix())) + _postfix += Data::ComplexGeoData::elementMapPrefix(); + _postfix += postfix; + } + postfix = _postfix.c_str(); + } + geoData->reTagElementMap(obj->getID(),obj->getDocument()->getStringHasher(),postfix); + } void LinkBaseExtension::onExtendedUnsetupObject() { diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index c0c02d3e45..71b16cd45a 100644 --- a/src/App/PropertyLinks.cpp +++ b/src/App/PropertyLinks.cpp @@ -218,6 +218,19 @@ static std::string propertyName(const Property *prop) { return prop->getFullName(); } +const std::unordered_set& +PropertyLinkBase::getElementReferences(DocumentObject* feature) +{ + static std::unordered_set none; + + auto it = _ElementRefMap.find(feature); + if (it == _ElementRefMap.end()) { + return none; + } + + return it->second; +} + void PropertyLinkBase::updateElementReferences(DocumentObject *feature, bool reverse) { #ifdef FC_USE_TNP_FIX if (!feature || !feature->getNameInDocument()) { @@ -394,15 +407,15 @@ bool PropertyLinkBase::_updateElementReference(DocumentObject *feature, } } - bool missing = Data::hasMissingElement(elementName.second.c_str()); + bool missing = GeoFeature::hasMissingElement(elementName.second.c_str()); if (feature == geo && (missing || reverse)) { // If the referenced element is missing, or we are generating element // map for the first time, or we are re-generating the element map due // to version change, i.e. 'reverse', try search by geometry first const char* oldElement = Data::findElementName(shadow.second.c_str()); if (!Data::hasMissingElement(oldElement)) { - // const auto& names = geo->searchElementCache(oldElement); - std::vector names; // searchElementCache isn't implemented. + const auto& names = geo->searchElementCache(oldElement); +// std::vector names; // searchElementCache isn't implemented. if (names.size()) { missing = false; std::string newsub(subname, strlen(subname) - strlen(element)); @@ -502,11 +515,18 @@ bool PropertyLinkBase::_updateElementReference(DocumentObject *feature, } std::pair -PropertyLinkBase::tryReplaceLink(const PropertyContainer *owner, DocumentObject *obj, - const DocumentObject *parent, DocumentObject *oldObj, DocumentObject *newObj, const char *subname) +PropertyLinkBase::tryReplaceLink(const PropertyContainer* owner, + DocumentObject* obj, + const DocumentObject* parent, + DocumentObject* oldObj, + DocumentObject* newObj, + const char* subname) { std::pair res; res.first = 0; + if (!obj) { + return res; + } if (oldObj == obj) { if (owner == parent) { @@ -536,6 +556,9 @@ PropertyLinkBase::tryReplaceLink(const PropertyContainer *owner, DocumentObject for (auto pos = sub.find('.'); pos != std::string::npos; pos = sub.find('.', pos)) { ++pos; char c = sub[pos]; + if (c == '.') { + continue; + } sub[pos] = 0; auto sobj = obj->getSubObject(sub.c_str()); sub[pos] = c; @@ -577,6 +600,8 @@ PropertyLinkBase::tryReplaceLinkSubs(const PropertyContainer *owner, { std::pair > res; res.first = 0; + if (!obj) + return res; auto r = tryReplaceLink(owner,obj,parent,oldObj,newObj); if(r.first) { @@ -1545,8 +1570,12 @@ std::string PropertyLinkBase::tryImportSubName(const App::DocumentObject *obj, c #define ATTR_SHADOW "shadow" #define ATTR_MAPPED "mapped" +#ifdef FC_USE_TNP_FIX +#define IGNORE_SHADOW false +#else // We do not have topo naming yet, ignore shadow sub for now #define IGNORE_SHADOW true +#endif void PropertyLinkSub::Save (Base::Writer &writer) const { diff --git a/src/App/PropertyLinks.h b/src/App/PropertyLinks.h index 124865f66c..c08262fc4a 100644 --- a/src/App/PropertyLinks.h +++ b/src/App/PropertyLinks.h @@ -28,6 +28,9 @@ #include #include #include +#include +#include + #include "Property.h" namespace Base { @@ -38,6 +41,8 @@ namespace App { class DocumentObject; class Document; +class GeoFeature; +class SubObjectT; class DocInfo; using DocInfoPtr = std::shared_ptr; @@ -262,7 +267,7 @@ public: /// Helper function to return a map of linked object and its subname references void getLinkedElements(std::map > &elements, - bool newStyle=true, bool all=true) const + bool newStyle=true, bool all=false) const { std::vector ret; std::vector subs; @@ -275,7 +280,7 @@ public: /// Helper function to return a map of linked object and its subname references std::map > - linkedElements(bool newStyle=true, bool all=true) const + linkedElements(bool newStyle=true, bool all=false) const { std::map > ret; getLinkedElements(ret,newStyle,all); @@ -346,6 +351,8 @@ public: /// Update all element references in all link properties of \a feature static void updateElementReferences(DocumentObject *feature, bool reverse=false); + /// Obtain link properties that contain element references to a given object + static const std::unordered_set& getElementReferences(DocumentObject *); /** Helper function for update individual element reference * diff --git a/src/Mod/PartDesign/App/FeatureDressUp.cpp b/src/Mod/PartDesign/App/FeatureDressUp.cpp index 3784506039..e91e8eb249 100644 --- a/src/Mod/PartDesign/App/FeatureDressUp.cpp +++ b/src/Mod/PartDesign/App/FeatureDressUp.cpp @@ -259,84 +259,6 @@ std::vector DressUp::getFaces(const TopoShape& shape) return ret; } -std::vector DressUp::getContinuousEdges(const TopoShape &shape) { - std::vector ret; - std::unordered_set shapeSet; - - auto addEdge = [&](const TopoDS_Shape &subshape, const std::string &ref) { - if (!shapeSet.insert(subshape).second) - return; - - auto faces = shape.findAncestorsShapes(subshape, TopAbs_FACE); - if(faces.size() != 2) { - FC_WARN(getFullName() << ": skip edge " - << ref << " with less two attaching faces"); - return; - } - const TopoDS_Shape& face1 = faces.front(); - const TopoDS_Shape& face2 = faces.back(); - GeomAbs_Shape cont = BRep_Tool::Continuity(TopoDS::Edge(subshape), - TopoDS::Face(face1), - TopoDS::Face(face2)); - if (cont != GeomAbs_C0) { - FC_WARN(getFullName() << ": skip edge " - << ref << " that is not C0 continuous"); - return; - } - ret.push_back(subshape); - }; - - for(const auto &v : Base.getShadowSubs()) { - TopoDS_Shape subshape; - const auto &ref = v.first.size()?v.first:v.second; - subshape = shape.getSubShape(ref.c_str(), true); - if(subshape.IsNull()) - FC_THROWM(Base::CADKernelError, "Invalid edge link: " << v.second); - - if (subshape.ShapeType() == TopAbs_EDGE) - addEdge(subshape, ref); - else if(subshape.ShapeType() == TopAbs_FACE || subshape.ShapeType() == TopAbs_WIRE) { - for(TopExp_Explorer exp(subshape,TopAbs_EDGE);exp.More();exp.Next()) - addEdge(exp.Current(), std::string()); - } else - FC_WARN(getFullName() << ": skip invalid shape '" - << ref << "' with type " << TopoShape::shapeName(subshape.ShapeType())); - } - return ret; -} - -std::vector DressUp::getFaces(const TopoShape &shape) { - std::vector ret; - const auto &vals = Base.getSubValues(); - const auto &subs = Base.getShadowSubs(); - size_t i=0; - for(auto &val : vals) { - if(!boost::starts_with(val,"Face")) - continue; - auto &sub = subs[i++]; - auto &ref = sub.first.size()?sub.first:val; - TopoShape subshape; - try { - subshape = shape.getSubTopoShape(ref.c_str()); - }catch(...) - { - } - - if(subshape.isNull()) { - FC_ERR(getFullName() << ": invalid face reference '" << ref << "'"); - throw Part::NullShapeException("Invalid Invalid face link"); - } - - if(subshape.shapeType() != TopAbs_FACE) { - FC_WARN(getFullName() << ": skip invalid shape '" - << ref << "' with type " << subshape.shapeName()); - continue; - } - ret.push_back(subshape); - } - return ret; -} - void DressUp::onChanged(const App::Property* prop) { // the BaseFeature property should track the Base and vice-versa as long as diff --git a/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py b/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py index f09e534327..8ea9260656 100644 --- a/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py +++ b/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py @@ -1362,4 +1362,4 @@ class TestTopologicalNamingProblem(unittest.TestCase): def tearDown(self): """ Close our test document """ - # App.closeDocument("PartDesignTestTNP") + App.closeDocument("PartDesignTestTNP")