From db00a9b8e27af7eab16328c1e93b129254459837 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Tue, 30 Sep 2025 20:02:47 +0200 Subject: [PATCH] Merge pull request #24162 from PaddleStroke/asm_linkgroup_asmlink Assembly: Add support to Link groups in sub-assemblies. --- src/Mod/Assembly/App/AssemblyLink.cpp | 125 ++++++++++++++++++++----- src/Mod/Assembly/App/AssemblyUtils.cpp | 12 +++ src/Mod/Assembly/App/AssemblyUtils.h | 1 + 3 files changed, 113 insertions(+), 25 deletions(-) diff --git a/src/Mod/Assembly/App/AssemblyLink.cpp b/src/Mod/Assembly/App/AssemblyLink.cpp index 2371f6be28..9e2dbd57ce 100644 --- a/src/Mod/Assembly/App/AssemblyLink.cpp +++ b/src/Mod/Assembly/App/AssemblyLink.cpp @@ -157,10 +157,29 @@ void AssemblyLink::onChanged(const App::Property* prop) continue; } - auto* prop = - dynamic_cast(obj->getPropertyByName("Placement")); - if (prop) { - prop->setValue(plc * prop->getValue()); + if (obj->isLinkGroup()) { + auto* srcLink = static_cast(obj); + const std::vector srcElements = + srcLink->ElementList.getValues(); + + for (auto elt : srcElements) { + if (!elt) { + continue; + } + + auto* prop = dynamic_cast( + elt->getPropertyByName("Placement")); + if (prop) { + prop->setValue(plc * prop->getValue()); + } + } + } + else { + auto* prop = dynamic_cast( + obj->getPropertyByName("Placement")); + if (prop) { + prop->setValue(plc * prop->getValue()); + } } } @@ -216,6 +235,8 @@ void AssemblyLink::synchronizeComponents() // In which case we need to add an AssemblyLink and not a Link. App::DocumentObject* link = nullptr; bool found = false; + std::set linkGroupsAdded; + for (auto* obj2 : assemblyLinkGroup) { App::DocumentObject* linkedObj; @@ -226,6 +247,30 @@ void AssemblyLink::synchronizeComponents() linkedObj = subAsmLink->getLinkedObject2(false); // not recursive } else if (link2) { + if (obj->isLinkGroup() && link2->isLinkGroup()) { + auto* srcLink = static_cast(obj); + if ((srcLink->getTrueLinkedObject(false) == link2->getTrueLinkedObject(false)) + && link2->ElementCount.getValue() == srcLink->ElementCount.getValue() + && linkGroupsAdded.find(srcLink) == linkGroupsAdded.end()) { + found = true; + link = obj2; + // In case where there are more than 2 link groups with the + // same number of elements. + linkGroupsAdded.insert(srcLink); + + const std::vector srcElements = + srcLink->ElementList.getValues(); + const std::vector newElements = + link2->ElementList.getValues(); + for (int i = 0; i < srcElements.size(); ++i) { + objLinkMap[srcElements[i]] = newElements[i]; + } + break; + } + } + else if (obj->isLinkGroup() && !link2->isLinkGroup()) { + continue; // make sure we migrate sub assemblies that had link to linkgroups + } linkedObj = link2->getLinkedObject(false); // not recursive } else { @@ -253,38 +298,68 @@ void AssemblyLink::synchronizeComponents() addObject(subAsmLink); link = subAsmLink; } + else if (obj->isDerivedFrom() && obj->isLinkGroup()) { + auto* srcLink = static_cast(obj); + + auto* newLink = + static_cast(doc->addObject("App::Link", obj->getNameInDocument())); + newLink->LinkedObject.setValue(srcLink->getTrueLinkedObject(false)); + + newLink->Label.setValue(obj->Label.getValue()); + addObject(newLink); + + newLink->ElementCount.setValue(srcLink->ElementCount.getValue()); + const std::vector srcElements = + srcLink->ElementList.getValues(); + const std::vector newElements = + newLink->ElementList.getValues(); + for (int i = 0; i < srcElements.size(); ++i) { + auto* newObj = newElements[i]; + auto* srcObj = srcElements[i]; + if (newObj && srcObj) { + syncPlacements(srcObj, newObj); + } + objLinkMap[srcObj] = newObj; + } + + link = newLink; + } else { - auto* appLink = new App::Link(); - doc->addObject(appLink, obj->getNameInDocument()); - appLink->LinkedObject.setValue(obj); - appLink->Label.setValue(obj->Label.getValue()); - addObject(appLink); - link = appLink; + App::DocumentObject* newObj = doc->addObject("App::Link", obj->getNameInDocument()); + auto* newLink = static_cast(newObj); + newLink->LinkedObject.setValue(obj); + newLink->Label.setValue(obj->Label.getValue()); + addObject(newLink); + link = newLink; } } objLinkMap[obj] = link; - // If the assemblyLink is rigid, then we keep the placement synchronized. - if (isRigid()) { - auto* plcProp = - dynamic_cast(obj->getPropertyByName("Placement")); - auto* plcProp2 = - dynamic_cast(link->getPropertyByName("Placement")); - if (plcProp && plcProp2) { - if (!plcProp->getValue().isSame(plcProp2->getValue())) { - plcProp2->setValue(plcProp->getValue()); - } - } + } + + // If the assemblyLink is rigid, then we keep all placements synchronized. + if (isRigid()) { + for (const auto& [sourceObj, linkObj] : objLinkMap) { + syncPlacements(sourceObj, linkObj); } } // We check if a component needs to be removed from the AssemblyLink - for (auto* link : assemblyLinkGroup) { + // NOTE: this is not being executed when a src link is deleted, because the link + // is then in error, and so AssemblyLink::execute() does not get called. + std::set validLinks; + for (const auto& pair : objLinkMap) { + validLinks.insert(pair.second); + } + for (auto* obj : assemblyLinkGroup) { // We don't need to update assemblyLinkGroup after the addition since we're not removing // something we just added. - - if (objLinkMap.find(link) != objLinkMap.end()) { - doc->removeObject(link->getNameInDocument()); + if (!obj->isDerivedFrom() && !obj->isDerivedFrom() + && !obj->isDerivedFrom()) { + continue; + } + if (validLinks.find(obj) == validLinks.end()) { + doc->removeObject(obj->getNameInDocument()); } } } diff --git a/src/Mod/Assembly/App/AssemblyUtils.cpp b/src/Mod/Assembly/App/AssemblyUtils.cpp index 86786484b3..cf5fe6d30e 100644 --- a/src/Mod/Assembly/App/AssemblyUtils.cpp +++ b/src/Mod/Assembly/App/AssemblyUtils.cpp @@ -726,5 +726,17 @@ App::DocumentObject* getMovingPartFromRef(const AssemblyObject* assemblyObject, return getMovingPartFromRef(assemblyObject, prop); } +void syncPlacements(App::DocumentObject* src, App::DocumentObject* to) +{ + auto* plcPropSource = + dynamic_cast(src->getPropertyByName("Placement")); + auto* plcPropLink = dynamic_cast(to->getPropertyByName("Placement")); + + if (plcPropSource && plcPropLink) { + if (!plcPropSource->getValue().isSame(plcPropLink->getValue())) { + plcPropLink->setValue(plcPropSource->getValue()); + } + } +} } // namespace Assembly diff --git a/src/Mod/Assembly/App/AssemblyUtils.h b/src/Mod/Assembly/App/AssemblyUtils.h index 6fe1e22912..068ffabee0 100644 --- a/src/Mod/Assembly/App/AssemblyUtils.h +++ b/src/Mod/Assembly/App/AssemblyUtils.h @@ -174,6 +174,7 @@ AssemblyExport App::DocumentObject* getMovingPartFromRef(const AssemblyObject* a AssemblyExport std::vector getSubAsList(const App::PropertyXLinkSub* prop); AssemblyExport std::vector getSubAsList(const App::DocumentObject* joint, const char* propName); +AssemblyExport void syncPlacements(App::DocumentObject* src, App::DocumentObject* to); } // namespace Assembly