From d3c5df86eed85aa4f08f2cc8600c7cd6a5cef5dc Mon Sep 17 00:00:00 2001 From: bgbsww Date: Sat, 11 May 2024 22:12:21 -0400 Subject: [PATCH] Toponaming: bring in missing code fragments in Part --- src/App/GeoFeature.h | 21 - src/Mod/Part/App/AttachExtension.cpp | 509 +++++++++++++++++++- src/Mod/Part/App/AttachExtension.h | 125 ++++- src/Mod/Part/App/Attacher.cpp | 5 + src/Mod/Part/App/Attacher.h | 3 + src/Mod/Part/App/PartFeature.cpp | 175 ++++++- src/Mod/Part/App/PartFeature.h | 14 + src/Mod/PartDesign/App/FeatureDressUp.cpp | 78 --- tests/src/Mod/Sketcher/App/SketchObject.cpp | 2 +- 9 files changed, 827 insertions(+), 105 deletions(-) diff --git a/src/App/GeoFeature.h b/src/App/GeoFeature.h index 1efdec8004..025d5f969e 100644 --- a/src/App/GeoFeature.h +++ b/src/App/GeoFeature.h @@ -141,27 +141,6 @@ public: * appearance from an App::Material object. */ virtual void setMaterialAppearance(const App::Material& material); - - /** 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 goemetry. - * 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; - #ifdef FC_USE_TNP_FIX /** Search sub element using internal cached geometry * diff --git a/src/Mod/Part/App/AttachExtension.cpp b/src/Mod/Part/App/AttachExtension.cpp index 530fc748ad..910c8e592b 100644 --- a/src/Mod/Part/App/AttachExtension.cpp +++ b/src/Mod/Part/App/AttachExtension.cpp @@ -23,6 +23,7 @@ #include "PreCompiled.h" #include +#include #include "AttachExtension.h" #include "AttachExtensionPy.h" @@ -33,6 +34,512 @@ using namespace Attacher; EXTENSION_PROPERTY_SOURCE(Part::AttachExtension, App::DocumentObjectExtension) +#ifdef FC_USE_TNP_FIX + +AttachExtension::AttachExtension() +{ + EXTENSION_ADD_PROPERTY_TYPE(AttacherType, + ("Attacher::AttachEngine3D"), + "Attachment", + (App::PropertyType)(App::Prop_None), + "Class name of attach engine object driving the attachment."); + this->AttacherType.setStatus(App::Property::Status::Hidden, true); + + EXTENSION_ADD_PROPERTY_TYPE( + Support, + (nullptr, nullptr), + "Attachment", + (App::PropertyType)(App::Prop_Hidden), + "Support of the 2D geometry (Deprecated! Use AttachmentSupport instead"); + Support.setScope(App::LinkScope::Global); + + EXTENSION_ADD_PROPERTY_TYPE(AttachmentSupport, + (nullptr, nullptr), + "Attachment", + (App::PropertyType)(App::Prop_None), + "Support of the 2D geometry"); + AttachmentSupport.setScope(App::LinkScope::Global); + + EXTENSION_ADD_PROPERTY_TYPE(MapMode, + (mmDeactivated), + "Attachment", + App::Prop_None, + "Mode of attachment to other object"); + MapMode.setEditorName("PartGui::PropertyEnumAttacherItem"); + MapMode.setEnums(AttachEngine::eMapModeStrings); + // a rough test if mode string list in Attacher.cpp is in sync with eMapMode enum. + assert(MapMode.getEnumVector().size() == mmDummy_NumberOfModes); + + EXTENSION_ADD_PROPERTY_TYPE(MapReversed, + (false), + "Attachment", + App::Prop_None, + "Reverse Z direction (flip sketch upside down)"); + + EXTENSION_ADD_PROPERTY_TYPE(MapPathParameter, + (0.0), + "Attachment", + App::Prop_None, + "Sets point of curve to map the sketch to. 0..1 = start..end"); + + EXTENSION_ADD_PROPERTY_TYPE( + AttachmentOffset, + (Base::Placement()), + "Attachment", + App::Prop_None, + "Extra placement to apply in addition to attachment (in local coordinates)"); + + _props.attacherType = &AttacherType; + _props.attachment = &AttachmentSupport; + _props.mapMode = &MapMode; + _props.mapReversed = &MapReversed; + _props.mapPathParameter = &MapPathParameter; + + setAttacher(new AttachEngine3D); // default attacher + _baseProps.attacher.reset(new AttachEngine3D); + + updatePropertyStatus(false); + + initExtensionType(AttachExtension::getExtensionClassTypeId()); +} + +AttachExtension::~AttachExtension() +{} + +template +static inline bool getProp(bool force, + T*& prop, + Base::Type type, + App::PropertyContainer* owner, + const char* name, + const char* doc) +{ + prop = Base::freecad_dynamic_cast(owner->getDynamicPropertyByName(name)); + if (prop || !force) { + return false; + } + prop = static_cast(owner->addDynamicProperty(type.getName(), name, "Attachment", doc)); + if (!prop) { + FC_THROWM(Base::RuntimeError, "Failed to add property " << owner->getFullName() << name); + } + prop->setStatus(App::Property::Status::LockDynamic, true); + prop->setStatus(App::Property::Status::Hidden, true); + return true; +} + +template +static inline bool +getProp(bool force, T*& prop, App::PropertyContainer* owner, const char* name, const char* doc) +{ + return getProp(force, prop, T::getClassTypeId(), owner, name, doc); +} + +void AttachExtension::initBase(bool force) +{ + if (_baseProps.attacherType) { + return; + } + auto obj = getExtendedObject(); + + // Temporary holding the properties so that we only handle onChanged() event + // when all relevant properties are ready. + Properties props; + + if (getProp( + force, + props.attacherType, + obj, + "BaseAttacherType", + "Class name of attach engine object driving the attachment for base geometry.")) { + props.attacherType->setValue(_baseProps.attacher->getTypeId().getName()); + } + else if (!props.attacherType) { + return; + } + + getProp(force, + props.attachment, + App::PropertyLinkSubListHidden::getClassTypeId(), + obj, + "BaseAttachment", + "Link to base geometry."); + + if (getProp(force, + props.mapMode, + obj, + "BaseMapMode", + "Mode of attachment for the base geometry")) { + props.mapMode->setStatus(App::Property::Status::Hidden, false); + } + if (props.mapMode) { + props.mapMode->setEditorName("PartGui::PropertyEnumAttacherItem"); + props.mapMode->setEnums(AttachEngine::eMapModeStrings); + } + + getProp(force, + props.mapReversed, + obj, + "BaseMapReversed", + "Reverse Z direction of the base geometry attachment"); + + getProp(force, + props.mapPathParameter, + obj, + "BaseMapPathParameter", + "Sets point of base curve to map 0..1 = start..end"); + + static_cast(_baseProps) = props; +} + +void AttachExtension::setAttacher(AttachEngine* pAttacher, bool base) +{ + auto& props = base ? _baseProps : _props; + props.attacher.reset(pAttacher); + if (props.attacher) { + if (base) { + initBase(true); + } + const char* typeName = props.attacher->getTypeId().getName(); + if (strcmp(props.attacherType->getValue(), typeName) + != 0) { // make sure we need to change, to break recursive + // onChange->changeAttacherType->onChange... + props.attacherType->setValue(typeName); + } + updateAttacherVals(base); + } + else { + if (props.attacherType + && strlen(props.attacherType->getValue()) + != 0) { // make sure we need to change, to break recursive + // onChange->changeAttacherType->onChange... + props.attacherType->setValue(""); + } + } +} + +bool AttachExtension::changeAttacherType(const char* typeName, bool base) +{ + auto& prop = base ? _baseProps : _props; + + // check if we need to actually change anything + if (prop.attacher) { + if (strcmp(prop.attacher->getTypeId().getName(), typeName) == 0) { + return false; + } + } + else if (strlen(typeName) == 0) { + return false; + } + if (strlen(typeName) == 0) { + setAttacher(nullptr, base); + return true; + } + Base::Type t = Base::Type::fromName(typeName); + if (t.isDerivedFrom(AttachEngine::getClassTypeId())) { + AttachEngine* pNewAttacher = + static_cast(Base::Type::createInstanceByName(typeName)); + this->setAttacher(pNewAttacher, base); + return true; + } + + std::stringstream errMsg; + errMsg << "Object if this type is not derived from AttachEngine: " << typeName; + throw AttachEngineException(errMsg.str()); +} + +bool AttachExtension::positionBySupport() +{ + _active = 0; + if (!_props.attacher) { + throw Base::RuntimeError( + "AttachExtension: can't positionBySupport, because no AttachEngine is set."); + } + Base::Placement plaOriginal = getPlacement().getValue(); + try { + if (_props.attacher->mapMode == mmDeactivated) { + return false; + } + bool subChanged = false; + + getPlacement().setValue(Base::Placement()); + + Base::Placement basePlacement; + if (_baseProps.attacher && _baseProps.attacher->mapMode != mmDeactivated) { + basePlacement = + _baseProps.attacher->calculateAttachedPlacement(Base::Placement(), &subChanged); + if (subChanged) { + _baseProps.attachment->setValues(_baseProps.attachment->getValues(), + _baseProps.attacher->getSubValues()); + } + } + + subChanged = false; + _props.attacher->setOffset(AttachmentOffset.getValue() * basePlacement.inverse()); + auto placement = _props.attacher->calculateAttachedPlacement(plaOriginal, &subChanged); + if (subChanged) { + Base::ObjectStatusLocker guard( + App::Property::User3, + &AttachmentSupport); + AttachmentSupport.setValues(AttachmentSupport.getValues(), + _props.attacher->getSubValues()); + } + getPlacement().setValue(placement); + _active = 1; + return true; + } + catch (ExceptionCancel&) { + // disabled, don't do anything + getPlacement().setValue(plaOriginal); + return false; + } + catch (Base::Exception&) { + getPlacement().setValue(plaOriginal); + throw; + } + catch (Standard_Failure&) { + getPlacement().setValue(plaOriginal); + throw; + } +} + +bool AttachExtension::isAttacherActive() const +{ + if (_active < 0) { + _active = 0; + try { + updateAttacherVals(/*base*/ false); + updateAttacherVals(/*base*/ true); + _props.attacher->calculateAttachedPlacement(getPlacement().getValue()); + _active = 1; + } + catch (Base::Exception&) { + } + } + return _active != 0; +} + +short int AttachExtension::extensionMustExecute() +{ + return DocumentObjectExtension::extensionMustExecute(); +} + + +App::DocumentObjectExecReturn* AttachExtension::extensionExecute() +{ + if (this->isTouched_Mapping()) { + try { + positionBySupport(); + // we let all Base::Exceptions thru, so that App:DocumentObject can take appropriate + // action + /*} catch (Base::Exception &e) { + return new App::DocumentObjectExecReturn(e.what());*/ + // Convert OCC exceptions to Base::Exception + } + catch (Standard_Failure& e) { + throw Base::RuntimeError(e.GetMessageString()); + // return new App::DocumentObjectExecReturn(e.GetMessageString()); + } + } + return App::DocumentObjectExtension::extensionExecute(); +} +void dumpAttacher(std::string name, App::PropertyLinkSubList& a) +{ + std::cerr << name << ": "; + for (auto& sh : a.getShadowSubs()) { + std::cerr << "(" << sh.first << " : " << sh.second << ")"; + } + for (auto& v : a.getValues()) { + std::cerr << v->getNameInDocument(); + } + for (auto& sv : a.getSubValues()) { + std::cerr << "[" << sv << "]"; + } + std::cerr << std::endl; +} + + +void AttachExtension::extensionOnChanged(const App::Property* prop) +{ + if (!getExtendedObject()->isRestoring()) { + if (prop == &Support) { + if (!prop->testStatus(App::Property::User3)) { + Base::ObjectStatusLocker guard( + App::Property::User3, + &Support); + AttachmentSupport.Paste(Support); + } + } + else if (_props.matchProperty(prop)) { + if (prop == &AttachmentSupport) { + Base::ObjectStatusLocker guard( + App::Property::User3, + &Support); + Support.Paste(AttachmentSupport); + dumpAttacher("PasteSupport", AttachmentSupport); + } + _active = -1; + updateAttacherVals(/*base*/ false); + updatePropertyStatus(isAttacherActive()); + } + else if (_baseProps.matchProperty(prop)) { + _active = -1; + updateAttacherVals(/*base*/ true); + updatePropertyStatus(isAttacherActive(), /*base*/ true); + } + } + if (prop == &(this->AttacherType)) { + this->changeAttacherType(this->AttacherType.getValue()); + } + else if (prop == _baseProps.attacherType) { + this->changeAttacherType(_baseProps.attacherType->getValue()); + } + + App::DocumentObjectExtension::extensionOnChanged(prop); +} + +void AttachExtension::extHandleChangedPropertyName(Base::XMLReader& reader, + const char* TypeName, + const char* PropName) +{ + // Was superPlacement + Base::Type type = Base::Type::fromName(TypeName); + if (AttachmentOffset.getClassTypeId() == type && strcmp(PropName, "superPlacement") == 0) { + AttachmentOffset.Restore(reader); + } +} + +void AttachExtension::onExtendedDocumentRestored() +{ + try { + if (Support.getValue()) { + AttachmentSupport.Paste(Support); + } + initBase(false); + if (_baseProps.attachment) { + _baseProps.attachment->setScope(App::LinkScope::Hidden); + } + if (_baseProps.attacherType) { + changeAttacherType(_baseProps.attacherType->getValue(), true); + } + _active = -1; + updatePropertyStatus(isAttacherActive()); + } + catch (Base::Exception&) { + } + catch (Standard_Failure&) { + } +} + +void AttachExtension::updatePropertyStatus(bool bAttached, bool base) +{ + auto& props = base ? this->_baseProps : this->_props; + if (!props.mapMode) { + return; + } + + // Hide properties when not applicable to reduce user confusion + eMapMode mmode = eMapMode(props.mapMode->getValue()); + bool modeIsPointOnCurve = + (mmode == mmNormalToPath || mmode == mmFrenetNB || mmode == mmFrenetTN + || mmode == mmFrenetTB || mmode == mmRevolutionSection || mmode == mmConcentric); + + // MapPathParameter is only used if there is a reference to one edge and not edge + vertex + bool hasOneRef = false; + if (props.attacher && props.attacher->subnames.size() == 1) { + hasOneRef = true; + } + props.mapPathParameter->setStatus(App::Property::Status::Hidden, + !bAttached || !(modeIsPointOnCurve && hasOneRef)); + props.mapReversed->setStatus(App::Property::Status::Hidden, !bAttached); + + if (base) { + props.attachment->setStatus(App::Property::Status::Hidden, !bAttached); + } + else { + this->AttachmentOffset.setStatus(App::Property::Status::Hidden, !bAttached); + if (getExtendedContainer()) { + getPlacement().setReadOnly( + bAttached && mmode != mmTranslate); // for mmTranslate, orientation should remain + // editable even when attached. + } + updatePropertyStatus(bAttached, true); + } +} + +void AttachExtension::updateAttacherVals(bool base) const +{ + auto& props = base ? this->_baseProps : this->_props; + if (!props.attachment) { + return; + } + dumpAttacher("updateAttacherVals", *props.attachment); + attacher(base).setUp(*props.attachment, + eMapMode(props.mapMode->getValue()), + props.mapReversed->getValue(), + props.mapPathParameter->getValue(), + 0.0, + 0.0); +} + +AttachExtension::Properties AttachExtension::getProperties(bool base) const +{ + return base ? _baseProps : _props; +} + +AttachExtension::Properties AttachExtension::getInitedProperties(bool base) +{ + if (base) { + initBase(true); + return _baseProps; + } + return _props; +} + +App::PropertyPlacement& AttachExtension::getPlacement() const +{ + auto pla = Base::freecad_dynamic_cast( + getExtendedObject()->getPropertyByName("Placement")); + if (!pla) { + throw Base::RuntimeError("AttachExtension cannot find placement property"); + } + return *pla; +} + +PyObject* AttachExtension::getExtensionPyObject() +{ + + if (ExtensionPythonObject.is(Py::_None())) { + // ref counter is set to 1 + ExtensionPythonObject = Py::Object(new AttachExtensionPy(this), true); + } + return Py::new_reference_to(ExtensionPythonObject); +} + + +Attacher::AttachEngine& AttachExtension::attacher(bool base) const +{ + auto& props = base ? _baseProps : _props; + if (!props.attacher) { + throw AttachEngineException("AttachableObject: no attacher is set."); + } + return *props.attacher; +} + +// ------------------------------------------------ + +AttachEngineException::AttachEngineException() + : Base::Exception() +{} + +AttachEngineException::AttachEngineException(const char* sMessage) + : Base::Exception(sMessage) +{} + +AttachEngineException::AttachEngineException(const std::string& sMessage) + : Base::Exception(sMessage) +{} + +#else AttachExtension::AttachExtension() { EXTENSION_ADD_PROPERTY_TYPE(AttacherType, ("Attacher::AttachEngine3D"), "Attachment",(App::PropertyType)(App::Prop_None),"Class name of attach engine object driving the attachment."); @@ -320,7 +827,7 @@ AttachEngineException::AttachEngineException(const std::string& sMessage) : Base::Exception(sMessage) { } - +#endif namespace App { /// @cond DOXERR diff --git a/src/Mod/Part/App/AttachExtension.h b/src/Mod/Part/App/AttachExtension.h index 46e6fbc96f..e8f9df2348 100644 --- a/src/Mod/Part/App/AttachExtension.h +++ b/src/Mod/Part/App/AttachExtension.h @@ -40,7 +40,130 @@ namespace Part { +#ifdef FC_USE_TNP_FIX +class PartExport AttachEngineException: public Base::Exception +{ +public: + /// Construction + AttachEngineException(); + explicit AttachEngineException(const char* sMessage); + explicit AttachEngineException(const std::string& sMessage); + /// Destruction + ~AttachEngineException() throw() override + {} +}; + +/** + * @brief The AttachableObject class is the thing to extend an object with + * that should be attachable. It includes the required properties, and + * shortcuts for accessing the attachment math class. + */ +class PartExport AttachExtension: public App::DocumentObjectExtension +{ + EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(Part::AttachableObject); + +public: + AttachExtension(); + ~AttachExtension() override; + + /** + * @brief setAttacher sets the AttachEngine object. The class takes the + * ownership of the pointer, it will be deleted when the class is + * destroyed, or when a new attacher is set. The default attacher is AttachEngine3D. + * @param attacher. AttachableObject takes ownership and will delete it eventually. + */ + virtual void setAttacher(Attacher::AttachEngine* attacher, bool base = false); + + /** + * @brief changeAttacherType + * @param typeName is the typename of new attacher class. Must be derived + * from Attacher::AttachEngine. + * @return true if attacher was changed. false if attacher is already of the + * type requested. Throws if invalid type is supplied. + */ + bool changeAttacherType(const char* typeName, bool base = false); + + Attacher::AttachEngine& attacher(bool base = false) const; + + App::PropertyString AttacherType; + App::PropertyLinkSubList Support; // deprecated, leave here for backward compatibility + App::PropertyLinkSubList AttachmentSupport; + App::PropertyEnumeration MapMode; // see AttachEngine::eMapMode + App::PropertyBool MapReversed; // inverts Z and X internal axes + App::PropertyPlacement AttachmentOffset; + + /** + * @brief MapPathParameter is a parameter value for mmNormalToPath (the + * sketch will be mapped normal to a curve at point specified by parameter + * (from 0.0 to 1.0, from start to end) ) + */ + App::PropertyFloat MapPathParameter; + + /** calculate and update the Placement property based on the Support, and + * mode. Can throw FreeCAD and OCC exceptions. Returns true if attached, + * false if not, throws if attachment failed. + */ + virtual bool positionBySupport(); + + /** Return whether this attacher is active + */ + bool isAttacherActive() const; + + virtual bool isTouched_Mapping() + { + return true; /*support.isTouched isn't true when linked objects are changed... why?..*/ + } + + short int extensionMustExecute() override; + App::DocumentObjectExecReturn* extensionExecute() override; + PyObject* getExtensionPyObject() override; + void onExtendedDocumentRestored() override; + + struct Properties + { + App::PropertyString* attacherType = nullptr; + App::PropertyLinkSubList* attachment = nullptr; + App::PropertyEnumeration* mapMode = nullptr; + App::PropertyBool* mapReversed = nullptr; + App::PropertyFloat* mapPathParameter = nullptr; + bool matchProperty(const App::Property* prop) const + { + return prop == attachment || prop == mapMode || prop == mapReversed + || prop == mapPathParameter; + } + }; + Properties getProperties(bool base) const; + Properties getInitedProperties(bool base); + +protected: + void extensionOnChanged(const App::Property* /*prop*/) override; + virtual void extHandleChangedPropertyName(Base::XMLReader& reader, + const char* TypeName, + const char* PropName); + + App::PropertyPlacement& getPlacement() const; + void initBase(bool force); + +public: + void updateAttacherVals(bool base = false) const; + void updatePropertyStatus(bool attached, bool base = false); + +private: + struct _Properties: Properties + { + mutable std::unique_ptr attacher; + }; + _Properties _props; + _Properties _baseProps; + + mutable int _active = -1; +}; + + +using AttachExtensionPython = App::ExtensionPythonT; + +#else class PartExport AttachEngineException : public Base::Exception { public: @@ -131,7 +254,7 @@ private: using AttachExtensionPython = App::ExtensionPythonT; - +#endif } // namespace Part #endif // PARTATTACHABLEOBJECT_H diff --git a/src/Mod/Part/App/Attacher.cpp b/src/Mod/Part/App/Attacher.cpp index 1eafeca8b2..cce95345fa 100644 --- a/src/Mod/Part/App/Attacher.cpp +++ b/src/Mod/Part/App/Attacher.cpp @@ -257,6 +257,11 @@ void AttachEngine::setUp(const AttachEngine &another) this->attachmentOffset = another.attachmentOffset; } +void AttachEngine::setOffset(const Base::Placement &offset) +{ + this->attachmentOffset = offset; +} + Base::Placement AttachEngine::placementFactory(const gp_Dir &ZAxis, gp_Vec XAxis, gp_Pnt Origin, diff --git a/src/Mod/Part/App/Attacher.h b/src/Mod/Part/App/Attacher.h index da086becbd..2e395901be 100644 --- a/src/Mod/Part/App/Attacher.h +++ b/src/Mod/Part/App/Attacher.h @@ -223,6 +223,9 @@ public: //methods double surfU = 0.0, double surfV = 0.0, const Base::Placement &attachmentOffset = Base::Placement()); virtual void setUp(const AttachEngine &another); + + void setOffset(const Base::Placement &offset); + virtual AttachEngine* copy() const = 0; Base::Placement calculateAttachedPlacement( diff --git a/src/Mod/Part/App/PartFeature.cpp b/src/Mod/Part/App/PartFeature.cpp index 99341d5622..aae5563c10 100644 --- a/src/Mod/Part/App/PartFeature.cpp +++ b/src/Mod/Part/App/PartFeature.cpp @@ -72,6 +72,7 @@ #include "PartFeaturePy.h" #include "PartPyCXX.h" #include "TopoShapePy.h" +#include "Base/Tools.h" using namespace Part; namespace sp = std::placeholders; @@ -872,6 +873,7 @@ static TopoShape _getTopoShape(const App::DocumentObject* obj, std::set nextHiddens = hiddens; const App::DocumentObject* nextLink = lastLink; + // Todo: This might belong. // Toponaming project March 2024: This appears to be a non toponaming feature: // if (!checkLinkVisibility(nextHiddens, true, nextLink, owner, sub.c_str())) { // cacheable = false; @@ -967,6 +969,7 @@ TopoShape Feature::getTopoShape(const App::DocumentObject* obj, const App::DocumentObject* lastLink = 0; std::set hiddens; // Toponaming project March 2024: This appears to be a non toponaming feature: + // Todo is this a cause behind #13886 ? // if (!checkLinkVisibility(hiddens, false, lastLink, obj, subname)) { // return TopoShape(); // } @@ -1068,11 +1071,132 @@ App::DocumentObject *Feature::getShapeOwner(const App::DocumentObject *obj, cons return owner; } +struct Feature::ElementCache +{ + TopoShape shape; + mutable std::vector names; + mutable bool searched; +}; + +void Feature::registerElementCache(const std::string& prefix, PropertyPartShape* prop) +{ + if (prop) { + _elementCachePrefixMap.emplace_back(prefix, prop); + return; + } + for (auto it = _elementCachePrefixMap.begin(); it != _elementCachePrefixMap.end();) { + if (it->first == prefix) { + _elementCachePrefixMap.erase(it); + break; + } + } +} + +void Feature::onBeforeChange(const App::Property* prop) +{ + PropertyPartShape* propShape = nullptr; + const std::string* prefix = nullptr; + if (prop == &Shape) { + propShape = &Shape; + } + else { + for (const auto& v : _elementCachePrefixMap) { + if (prop == v.second) { + prefix = &v.first; + propShape = v.second; + } + } + } + if (propShape) { + if (_elementCachePrefixMap.empty()) { + _elementCache.clear(); + } + else { + for (auto it = _elementCache.begin(); it != _elementCache.end();) { + bool remove; + if (prefix) { + remove = boost::starts_with(it->first, *prefix); + } + else { + remove = true; + for (const auto& v : _elementCache) { + if (boost::starts_with(it->first, v.first)) { + remove = false; + break; + } + } + } + if (remove) { + it = _elementCache.erase(it); + } + else { + ++it; + } + } + } + if (getDocument() && !getDocument()->testStatus(App::Document::Restoring) + && !getDocument()->isPerformingTransaction()) { + std::vector objs; + std::vector subs; + for (auto prop : App::PropertyLinkBase::getElementReferences(this)) { + if (!prop->getContainer()) { + continue; + } + objs.clear(); + subs.clear(); + prop->getLinks(objs, true, &subs, false); + for (auto& sub : subs) { + auto element = Data::findElementName(sub.c_str()); + if (!element || !element[0] || Data::hasMissingElement(element)) { + continue; + } + if (prefix) { + if (!boost::starts_with(element, *prefix)) { + continue; + } + } + else { + bool found = false; + for (const auto& v : _elementCachePrefixMap) { + if (boost::starts_with(element, v.first)) { + found = true; + break; + } + } + if (found) { + continue; + } + } + auto res = + _elementCache.insert(std::make_pair(std::string(element), ElementCache())); + if (res.second) { + res.first->second.searched = false; + res.first->second.shape = propShape->getShape().getSubTopoShape( + element + (prefix ? prefix->size() : 0), + true); + } + } + } + } + } + GeoFeature::onBeforeChange(prop); +} + void Feature::onChanged(const App::Property* prop) { // if the placement has changed apply the change to the point data as well if (prop == &this->Placement) { +#ifdef FC_USE_TNP_FIX + TopoShape shape = this->Shape.getShape(); + shape.setTransform(this->Placement.getValue().toMatrix()); + Base::ObjectStatusLocker guard( + App::Property::NoRecompute, + &this->Shape); + this->Shape.setValue(shape); + +#else this->Shape.setTransform(this->Placement.getValue().toMatrix()); +#endif } // if the point data has changed check and adjust the transformation as well else if (prop == &this->Shape) { @@ -1085,8 +1209,9 @@ void Feature::onChanged(const App::Property* prop) if (!this->Shape.getValue().IsNull()) { try { p.fromMatrix(this->Shape.getShape().getTransform()); - if (p != this->Placement.getValue()) + if (p != this->Placement.getValue()) { this->Placement.setValue(p); + } } catch (const Base::ValueError&) { } @@ -1097,6 +1222,51 @@ void Feature::onChanged(const App::Property* prop) GeoFeature::onChanged(prop); } +#ifdef FC_USE_TNP_FIX + +const std::vector& Feature::searchElementCache(const std::string& element, + Data::SearchOptions options, + double tol, + double atol) const +{ + static std::vector none; + if (element.empty()) { + return none; + } + auto it = _elementCache.find(element); + if (it == _elementCache.end() || it->second.shape.isNull()) { + return none; + } + if (!it->second.searched) { + auto propShape = &Shape; + const std::string* prefix = nullptr; + for (const auto& v : _elementCachePrefixMap) { + if (boost::starts_with(element, v.first)) { + propShape = v.second; + prefix = &v.first; + break; + } + } + it->second.searched = true; + propShape->getShape().findSubShapesWithSharedVertex(it->second.shape, + &it->second.names, + static_cast(options), + tol, + atol); + if (prefix) { + for (auto& name : it->second.names) { + if (auto dot = strrchr(name.c_str(), '.')) { + name.insert(dot + 1 - name.c_str(), *prefix); + } + else { + name.insert(0, *prefix); + } + } + } + } + return it->second.names; +} +#endif TopLoc_Location Feature::getLocation() const { Base::Placement pl = this->Placement.getValue(); @@ -1702,6 +1872,5 @@ std::pair Feature::getElementName(const char* name, } } } - - return App::GeoFeature::getElementName(name, type); + return App::GeoFeature::_getElementName(name, mapped); } diff --git a/src/Mod/Part/App/PartFeature.h b/src/Mod/Part/App/PartFeature.h index e3259089e0..6a1af4d48a 100644 --- a/src/Mod/Part/App/PartFeature.h +++ b/src/Mod/Part/App/PartFeature.h @@ -152,13 +152,23 @@ public: create(const TopoShape& shape, const char* name = nullptr, App::Document* document = nullptr); static bool isElementMappingDisabled(App::PropertyContainer *container); +#ifdef FC_USE_TNP_FIX + const std::vector& searchElementCache(const std::string &element, + Data::SearchOptions options = Data::SearchOptions::CheckGeometry, + double tol = 1e-7, + double atol = 1e-10) const override; +#endif protected: /// recompute only this object App::DocumentObjectExecReturn *recompute() override; /// recalculate the feature App::DocumentObjectExecReturn *execute() override; + void onBeforeChange(const App::Property* prop) override; void onChanged(const App::Property* prop) override; + + void registerElementCache(const std::string &prefix, PropertyPartShape *prop); + /** * Build a history of changes * MakeShape: The operation that created the changes, e.g. BRepAlgoAPI_Common @@ -169,6 +179,10 @@ protected: ShapeHistory buildHistory(BRepBuilderAPI_MakeShape&, TopAbs_ShapeEnum type, const TopoDS_Shape& newS, const TopoDS_Shape& oldS); ShapeHistory joinHistory(const ShapeHistory&, const ShapeHistory&); +private: + struct ElementCache; + std::map _elementCache; + std::vector> _elementCachePrefixMap; }; class FilletBase : public Part::Feature 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/tests/src/Mod/Sketcher/App/SketchObject.cpp b/tests/src/Mod/Sketcher/App/SketchObject.cpp index 8875fbf896..92d396452c 100644 --- a/tests/src/Mod/Sketcher/App/SketchObject.cpp +++ b/tests/src/Mod/Sketcher/App/SketchObject.cpp @@ -281,7 +281,7 @@ TEST_F(SketchObjectTest, testGetElementName) EXPECT_STREQ(forward_normal_name.second.c_str(), "g39;SKT"); EXPECT_STREQ(reverse_normal_name.first.c_str(), ""); EXPECT_STREQ(reverse_normal_name.second.c_str(), "Vertex2"); - EXPECT_STREQ(reverse_export_name.first.c_str(), ""); + EXPECT_STREQ(reverse_export_name.first.c_str(), ";g39v1;SKT.Vertex1"); EXPECT_STREQ(reverse_export_name.second.c_str(), "Vertex1"); #else EXPECT_STREQ(forward_normal_name.first.c_str(), ";g39;SKT.Edge1");