From a3486b4dd25f9c07ff8a1b88fda5b634743198a7 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Mon, 2 Feb 2026 17:43:45 +0100 Subject: [PATCH] Assembly: Insert flexible assembly grounds the correct part (#27206) * Assembly: Insert flexible assembly grounds the correct part * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Assembly: Migrate ObjectToGround to PropertyLinkGlobal to support assembly links * Update JointObject.py * Assembly: ViewProviderAssembly fix assembly link deletion issue * Assembly: ViewProviderAssembly: make sure no duplicates in canDelete * Assembly CommandInsertLink fix typo * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/Mod/Assembly/CommandInsertLink.py | 38 ++++++++++++++++++- src/Mod/Assembly/Gui/ViewProviderAssembly.cpp | 20 ++++++---- src/Mod/Assembly/JointObject.py | 20 +++++++++- 3 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/Mod/Assembly/CommandInsertLink.py b/src/Mod/Assembly/CommandInsertLink.py index 4e7ee93167..2c0d4afaa3 100644 --- a/src/Mod/Assembly/CommandInsertLink.py +++ b/src/Mod/Assembly/CommandInsertLink.py @@ -495,7 +495,43 @@ class TaskAssemblyInsertLink(QtCore.QObject): if len(self.insertionStack) != 1: return - self.groundedObj = self.insertionStack[0]["addedObject"] + targetObj = self.insertionStack[0]["addedObject"] + + # If the object is a flexible AssemblyLink, we should ground its internal 'base' part + if targetObj.isDerivedFrom("Assembly::AssemblyLink") and not targetObj.Rigid: + linkedAsm = targetObj.LinkedObject + if linkedAsm and hasattr(linkedAsm, "Group"): + srcGrounded = None + # Attempt to find the grounded joint in the source assembly + # We look for a joint where JointType is 'Grounded' + for obj in linkedAsm.InListRecursive: + if hasattr(obj, "ObjectToGround"): + srcGrounded = obj.ObjectToGround + break + + # Search the sub-assembly group for the link pointing to the source grounded object + # Fallback to the first valid part if no grounded joint was found in source + candidate = None + for child in targetObj.Group: + if not candidate and ( + child.isDerivedFrom("App::Link") or child.isDerivedFrom("Part::Feature") + ): + candidate = child + + if ( + srcGrounded + and hasattr(child, "LinkedObject") + and child.LinkedObject == srcGrounded + ): + candidate = child + break + + if not candidate: # Nothing to ground + return + + targetObj = candidate + + self.groundedObj = targetObj self.groundedJoint = CommandCreateJoint.createGroundedJoint(self.groundedObj) def increment_counter(self, item): diff --git a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp index 945cb6cdce..99ea82ca0a 100644 --- a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp +++ b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp @@ -1345,20 +1345,24 @@ bool ViewProviderAssembly::canDelete(App::DocumentObject* objBeingDeleted) const continue; } - if (dynamic_cast(parent->getPropertyByName("ObjectToGround"))) { - objToDel.push_back(parent); + if (parent->getPropertyByName("ObjectToGround")) { + if (std::ranges::find(objToDel, parent) == objToDel.end()) { + objToDel.push_back(parent); + } } } } // Deletes them. for (auto* joint : objToDel) { - Gui::Command::doCommand( - Gui::Command::Doc, - "App.getDocument(\"%s\").removeObject(\"%s\")", - joint->getDocument()->getName(), - joint->getNameInDocument() - ); + if (joint && joint->getNameInDocument() != nullptr) { + Gui::Command::doCommand( + Gui::Command::Doc, + "App.getDocument(\"%s\").removeObject(\"%s\")", + joint->getDocument()->getName(), + joint->getNameInDocument() + ); + } } } return res; diff --git a/src/Mod/Assembly/JointObject.py b/src/Mod/Assembly/JointObject.py index 7bae57aab6..865a040a27 100644 --- a/src/Mod/Assembly/JointObject.py +++ b/src/Mod/Assembly/JointObject.py @@ -1211,16 +1211,32 @@ class GroundedJoint: joint.Proxy = self self.joint = joint + self.createObjectToGroundProperty(joint, obj_to_ground) + + def createObjectToGroundProperty(self, joint, obj_to_ground): joint.addProperty( - "App::PropertyLink", + "App::PropertyLinkGlobal", "ObjectToGround", "Ground", QT_TRANSLATE_NOOP("App::Property", "The object to ground"), locked=True, ) - joint.ObjectToGround = obj_to_ground + def onDocumentRestored(self, joint): + self.migrationScript(joint) + + def migrationScript(self, joint): + if ( + hasattr(joint, "ObjectToGround") + and joint.getTypeIdOfProperty("ObjectToGround") == "App::PropertyLink" + ): + obj_to_ground = joint.ObjectToGround + joint.setPropertyStatus("ObjectToGround", "-LockDynamic") + joint.removeProperty("ObjectToGround") + + self.createObjectToGroundProperty(joint, obj_to_ground) + def dumps(self): return None