From bdfcb6bfb21236d2782cb20bc0438143707d7045 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Tue, 24 Sep 2024 16:59:22 +0200 Subject: [PATCH] Assembly: Fix lag during dragging of large assemblies. Bundle fix joints together. Show only the movingJoint during dragging. Do not recompute joints during dragging. --- src/Mod/Assembly/App/AssemblyObject.cpp | 153 +++++++++++++----- src/Mod/Assembly/App/AssemblyObject.h | 14 +- src/Mod/Assembly/Gui/ViewProviderAssembly.cpp | 38 +++-- src/Mod/Assembly/Gui/ViewProviderAssembly.h | 2 +- 4 files changed, 158 insertions(+), 49 deletions(-) diff --git a/src/Mod/Assembly/App/AssemblyObject.cpp b/src/Mod/Assembly/App/AssemblyObject.cpp index ce1a631a7d..858e0354dd 100644 --- a/src/Mod/Assembly/App/AssemblyObject.cpp +++ b/src/Mod/Assembly/App/AssemblyObject.cpp @@ -117,6 +117,7 @@ PROPERTY_SOURCE(Assembly::AssemblyObject, App::Part) AssemblyObject::AssemblyObject() : mbdAssembly(std::make_shared()) + , bundleFixed(false) {} AssemblyObject::~AssemblyObject() = default; @@ -182,11 +183,16 @@ int AssemblyObject::solve(bool enableRedo, bool updateJCS) void AssemblyObject::preDrag(std::vector dragParts) { + bundleFixed = true; solve(); + bundleFixed = false; dragMbdParts.clear(); for (auto part : dragParts) { - dragMbdParts.push_back(getMbDPart(part)); + auto mbdPart = getMbDPart(part); + if (std::find(dragMbdParts.begin(), dragMbdParts.end(), mbdPart) == dragMbdParts.end()) { + dragMbdParts.push_back(mbdPart); + } } mbdAssembly->runPreDrag(); @@ -197,21 +203,26 @@ void AssemblyObject::doDragStep() try { for (auto& mbdPart : dragMbdParts) { App::DocumentObject* part = nullptr; + + // Find the corresponding DocumentObject for the mbdPart for (auto& pair : objectPartMap) { - if (pair.second == mbdPart) { + if (pair.second.part == mbdPart) { part = pair.first; break; } } + if (!part) { continue; } + // Update the MBD part's position Base::Placement plc = getPlacementFromProp(part, "Placement"); Base::Vector3d pos = plc.getPosition(); mbdPart->updateMbDFromPosition3D( std::make_shared>(ListD {pos.x, pos.y, pos.z})); + // Update the MBD part's rotation Base::Rotation rot = plc.getRotation(); Base::Matrix4D mat; rot.getValue(mat); @@ -222,11 +233,21 @@ void AssemblyObject::doDragStep() ->updateMbDFromRotationMatrix(r0.x, r0.y, r0.z, r1.x, r1.y, r1.z, r2.x, r2.y, r2.z); } + // Timing mbdAssembly->runDragStep() auto dragPartsVec = std::make_shared>>(dragMbdParts); mbdAssembly->runDragStep(dragPartsVec); + + // Timing the validation and placement setting if (validateNewPlacements()) { setNewPlacements(); - redrawJointPlacements(getJoints()); + + auto joints = getJoints(false); + for (auto* joint : joints) { + if (joint->Visibility.getValue()) { + // redraw only the moving joint as its quite slow as its python code. + redrawJointPlacement(joint); + } + } } } catch (...) { @@ -258,8 +279,12 @@ bool AssemblyObject::validateNewPlacements() auto it = objectPartMap.find(obj); if (it != objectPartMap.end()) { - std::shared_ptr mbdPart = it->second; + std::shared_ptr mbdPart = it->second.part; Base::Placement newPlacement = getMbdPlacement(mbdPart); + if (!it->second.offsetPlc.isIdentity()) { + newPlacement = newPlacement * it->second.offsetPlc; + } + if (!oldPlc.isSame(newPlacement)) { Base::Console().Warning( "Assembly : Ignoring bad solve, a grounded object moved.\n"); @@ -353,7 +378,7 @@ void AssemblyObject::setNewPlacements() { for (auto& pair : objectPartMap) { App::DocumentObject* obj = pair.first; - std::shared_ptr mbdPart = pair.second; + std::shared_ptr mbdPart = pair.second.part; if (!obj || !mbdPart) { continue; @@ -366,8 +391,15 @@ void AssemblyObject::setNewPlacements() continue; } - propPlacement->setValue(getMbdPlacement(mbdPart)); - obj->purgeTouched(); + + Base::Placement newPlacement = getMbdPlacement(mbdPart); + if (!pair.second.offsetPlc.isIdentity()) { + newPlacement = newPlacement * pair.second.offsetPlc; + } + if (!propPlacement->getValue().isSame(newPlacement)) { + propPlacement->setValue(newPlacement); + obj->purgeTouched(); + } } } @@ -375,20 +407,24 @@ void AssemblyObject::redrawJointPlacements(std::vector joi { // Notify the joint objects that the transform of the coin object changed. for (auto* joint : joints) { - auto* propPlacement = - dynamic_cast(joint->getPropertyByName("Placement1")); - if (propPlacement) { - propPlacement->setValue(propPlacement->getValue()); - } - propPlacement = - dynamic_cast(joint->getPropertyByName("Placement2")); - if (propPlacement) { - propPlacement->setValue(propPlacement->getValue()); - } - joint->purgeTouched(); + redrawJointPlacement(joint); } } +void AssemblyObject::redrawJointPlacement(App::DocumentObject* joint) +{ + // Notify the joint object that the transform of the coin object changed. + auto* pPlc = dynamic_cast(joint->getPropertyByName("Placement1")); + if (pPlc) { + pPlc->setValue(pPlc->getValue()); + } + pPlc = dynamic_cast(joint->getPropertyByName("Placement2")); + if (pPlc) { + pPlc->setValue(pPlc->getValue()); + } + joint->purgeTouched(); +} + void AssemblyObject::recomputeJointPlacements(std::vector joints) { // The Placement1 and Placement2 of each joint needs to be updated as the parts moved. @@ -884,6 +920,9 @@ std::shared_ptr AssemblyObject::makeMbdJointOfType(App::DocumentObjec JointType type) { if (type == JointType::Fixed) { + if (bundleFixed) { + return nullptr; + } return CREATE::With(); } else if (type == JointType::Revolute) { @@ -1288,7 +1327,8 @@ std::string AssemblyObject::handleOneSideOfJoint(App::DocumentObject* joint, return ""; } - std::shared_ptr mbdPart = getMbDPart(part); + MbDPartData data = getMbDData(part); + std::shared_ptr mbdPart = data.part; Base::Placement plc = getPlacementFromProp(joint, propPlcName); // Now we have plc which is the JCS placement, but its relative to the Object, not to the // containing Part. @@ -1308,6 +1348,10 @@ std::string AssemblyObject::handleOneSideOfJoint(App::DocumentObject* joint, Base::Placement part_global_plc = getGlobalPlacement(part, ref); plc = part_global_plc.inverse() * plc; } + // check if we need to add an offset in case of bundled parts. + if (!data.offsetPlc.isIdentity()) { + plc = data.offsetPlc * plc; + } std::string markerName = joint->getFullName(); auto mbdMarker = makeMbdMarker(markerName, plc); @@ -1387,17 +1431,21 @@ void AssemblyObject::getRackPinionMarkers(App::DocumentObject* joint, plc1.setRotation(adjustedRotation); // Then end of processing similar to handleOneSideOfJoint : - + MbDPartData data1 = getMbDData(part1); + std::shared_ptr mbdPart = data1.part; if (obj1->getNameInDocument() != part1->getNameInDocument()) { plc1 = rack_global_plc * plc1; Base::Placement part_global_plc = getGlobalPlacement(part1, ref1); plc1 = part_global_plc.inverse() * plc1; } + // check if we need to add an offset in case of bundled parts. + if (!data1.offsetPlc.isIdentity()) { + plc1 = data1.offsetPlc * plc1; + } std::string markerName = joint->getFullName(); auto mbdMarker = makeMbdMarker(markerName, plc1); - std::shared_ptr mbdPart = getMbDPart(part1); mbdPart->addMarker(mbdMarker); markerNameI = "/OndselAssembly/" + mbdPart->name + "/" + markerName; @@ -1449,26 +1497,57 @@ int AssemblyObject::slidingPartIndex(App::DocumentObject* joint) return slidingFound; } -std::shared_ptr AssemblyObject::getMbDPart(App::DocumentObject* obj) +AssemblyObject::MbDPartData AssemblyObject::getMbDData(App::DocumentObject* part) { - std::shared_ptr mbdPart; - - Base::Placement plc = getPlacementFromProp(obj, "Placement"); - - auto it = objectPartMap.find(obj); + auto it = objectPartMap.find(part); if (it != objectPartMap.end()) { - // obj has been associated with an ASMTPart before - mbdPart = it->second; - } - else { - // obj has not been associated with an ASMTPart before - std::string str = obj->getFullName(); - mbdPart = makeMbdPart(str, plc); - mbdAssembly->addPart(mbdPart); - objectPartMap[obj] = mbdPart; // Store the association + // part has been associated with an ASMTPart before + return it->second; } - return mbdPart; + // part has not been associated with an ASMTPart before + std::string str = part->getFullName(); + Base::Placement plc = getPlacementFromProp(part, "Placement"); + std::shared_ptr mbdPart = makeMbdPart(str, plc); + mbdAssembly->addPart(mbdPart); + MbDPartData data = {mbdPart, Base::Placement()}; + objectPartMap[part] = data; // Store the association + + // Associate other objects conneted with fixed joints + if (bundleFixed) { + auto addConnectedFixedParts = [&](App::DocumentObject* currentPart, auto& self) -> void { + std::vector joints = getJointsOfPart(currentPart); + for (auto* joint : joints) { + JointType jointType = getJointType(joint); + if (jointType == JointType::Fixed) { + App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1"); + App::DocumentObject* part2 = getMovingPartFromRef(joint, "Reference2"); + App::DocumentObject* partToAdd = currentPart == part1 ? part2 : part1; + + if (objectPartMap.find(partToAdd) != objectPartMap.end()) { + // already added + continue; + } + + Base::Placement plci = getPlacementFromProp(partToAdd, "Placement"); + MbDPartData partData = {mbdPart, plc.inverse() * plci}; + objectPartMap[partToAdd] = partData; // Store the association + + // Recursively call for partToAdd + self(partToAdd, self); + } + } + }; + + addConnectedFixedParts(part, addConnectedFixedParts); + } + + return data; +} + +std::shared_ptr AssemblyObject::getMbDPart(App::DocumentObject* part) +{ + return getMbDData(part).part; } std::shared_ptr diff --git a/src/Mod/Assembly/App/AssemblyObject.h b/src/Mod/Assembly/App/AssemblyObject.h index 4fc2185460..1ead41f729 100644 --- a/src/Mod/Assembly/App/AssemblyObject.h +++ b/src/Mod/Assembly/App/AssemblyObject.h @@ -170,6 +170,7 @@ public: void setNewPlacements(); static void recomputeJointPlacements(std::vector joints); static void redrawJointPlacements(std::vector joints); + static void redrawJointPlacement(App::DocumentObject* joint); // This makes sure that LinkGroups or sub-assemblies have identity placements. void ensureIdentityPlacements(); @@ -179,6 +180,16 @@ public: std::shared_ptr makeMbdPart(std::string& name, Base::Placement plc = Base::Placement(), double mass = 1.0); std::shared_ptr getMbDPart(App::DocumentObject* obj); + // To help the solver, during dragging, we are bundling parts connected by a fixed joint. + // So several assembly components are bundled in a single ASMTPart. + // So we need to store the plc of each bundled object relative to the bundle origin (first obj + // of objectPartMap). + struct MbDPartData + { + std::shared_ptr part; + Base::Placement offsetPlc; // This is the offset within the bundled parts + }; + MbDPartData getMbDData(App::DocumentObject* obj); std::shared_ptr makeMbdMarker(std::string& name, Base::Placement& plc); std::vector> makeMbdJoint(App::DocumentObject* joint); std::shared_ptr makeMbdJointOfType(App::DocumentObject* joint, @@ -236,12 +247,13 @@ public: private: std::shared_ptr mbdAssembly; - std::unordered_map> objectPartMap; + std::unordered_map objectPartMap; std::vector> objMasses; std::vector> dragMbdParts; std::vector> previousPositions; + bool bundleFixed; // void handleChangedPropertyType(Base::XMLReader &reader, const char *TypeName, App::Property // *prop) override; diff --git a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp index 45abfeffd8..7e775a9a0e 100644 --- a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp +++ b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp @@ -99,9 +99,9 @@ ViewProviderAssembly::ViewProviderAssembly() , enableMovement(true) , moveOnlyPreselected(false) , moveInCommand(true) - , jointVisibilityBackup(false) , ctrlPressed(false) , lastClickTime(0) + , jointVisibilitiesBackup({}) , docsToMove({}) {} @@ -783,11 +783,6 @@ ViewProviderAssembly::DragMode ViewProviderAssembly::findDragMode() assemblyPart->getDownstreamParts(docsToMove[0].obj, movingJoint); addPartsToMove(downstreamParts); - jointVisibilityBackup = movingJoint->Visibility.getValue(); - if (!jointVisibilityBackup) { - movingJoint->Visibility.setValue(true); - } - if (jointType == JointType::Revolute) { return DragMode::RotationOnPlane; } @@ -818,6 +813,26 @@ void ViewProviderAssembly::initMove(const SbVec2s& cursorPos, Gui::View3DInvento return; } + auto* assemblyPart = static_cast(getObject()); + // When the user drag parts, we switch off all joints visibility and only show the movingjoint + jointVisibilitiesBackup.clear(); + auto joints = assemblyPart->getJoints(); + for (auto* joint : joints) { + if (!joint) { + continue; + } + bool visible = joint->Visibility.getValue(); + jointVisibilitiesBackup.push_back({joint, visible}); + if (movingJoint == joint) { + if (!visible) { + joint->Visibility.setValue(true); + } + } + else if (visible) { + joint->Visibility.setValue(false); + } + } + SbVec3f vec; if (dragMode == DragMode::RotationOnPlane) { vec = viewer->getPointOnXYPlaneOfPlacement(cursorPos, jcsGlobalPlc); @@ -860,7 +875,6 @@ void ViewProviderAssembly::initMove(const SbVec2s& cursorPos, Gui::View3DInvento // prevent selection while moving viewer->setSelectionEnabled(false); - auto* assemblyPart = static_cast(getObject()); ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Assembly"); bool solveOnMove = hGrp->GetBool("SolveOnMove", true); @@ -888,8 +902,13 @@ void ViewProviderAssembly::endMove() partMoving = false; canStartDragging = false; - if (movingJoint && !jointVisibilityBackup) { - movingJoint->Visibility.setValue(false); + auto* assemblyPart = static_cast(getObject()); + auto joints = assemblyPart->getJoints(); + for (auto pair : jointVisibilitiesBackup) { + bool visible = pair.first->Visibility.getValue(); + if (visible != pair.second) { + pair.first->Visibility.setValue(pair.second); + } } movingJoint = nullptr; @@ -904,7 +923,6 @@ void ViewProviderAssembly::endMove() "User parameter:BaseApp/Preferences/Mod/Assembly"); bool solveOnMove = hGrp->GetBool("SolveOnMove", true); if (solveOnMove) { - auto* assemblyPart = static_cast(getObject()); assemblyPart->postDrag(); assemblyPart->setObjMasses({}); } diff --git a/src/Mod/Assembly/Gui/ViewProviderAssembly.h b/src/Mod/Assembly/Gui/ViewProviderAssembly.h index 29295fe341..6c7665e54e 100644 --- a/src/Mod/Assembly/Gui/ViewProviderAssembly.h +++ b/src/Mod/Assembly/Gui/ViewProviderAssembly.h @@ -203,7 +203,6 @@ public: bool enableMovement; bool moveOnlyPreselected; bool moveInCommand; - bool jointVisibilityBackup; bool ctrlPressed; long lastClickTime; // Store last click time as milliseconds @@ -218,6 +217,7 @@ public: App::DocumentObject* movingJoint; + std::vector> jointVisibilitiesBackup; std::vector> objectMasses; std::vector docsToMove;