Assembly: Fix lag during dragging of large assemblies. Bundle fix joints together. Show only the movingJoint during dragging. Do not recompute joints during dragging.

This commit is contained in:
PaddleStroke
2024-09-24 16:59:22 +02:00
committed by Yorik van Havre
parent 6e2cd4e733
commit bdfcb6bfb2
4 changed files with 158 additions and 49 deletions

View File

@@ -117,6 +117,7 @@ PROPERTY_SOURCE(Assembly::AssemblyObject, App::Part)
AssemblyObject::AssemblyObject()
: mbdAssembly(std::make_shared<ASMTAssembly>())
, bundleFixed(false)
{}
AssemblyObject::~AssemblyObject() = default;
@@ -182,11 +183,16 @@ int AssemblyObject::solve(bool enableRedo, bool updateJCS)
void AssemblyObject::preDrag(std::vector<App::DocumentObject*> 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<FullColumn<double>>(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<std::vector<std::shared_ptr<ASMTPart>>>(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<MbD::ASMTPart> mbdPart = it->second;
std::shared_ptr<MbD::ASMTPart> 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<ASMTPart> mbdPart = pair.second;
std::shared_ptr<ASMTPart> 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<App::DocumentObject*> joi
{
// Notify the joint objects that the transform of the coin object changed.
for (auto* joint : joints) {
auto* propPlacement =
dynamic_cast<App::PropertyPlacement*>(joint->getPropertyByName("Placement1"));
if (propPlacement) {
propPlacement->setValue(propPlacement->getValue());
}
propPlacement =
dynamic_cast<App::PropertyPlacement*>(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<App::PropertyPlacement*>(joint->getPropertyByName("Placement1"));
if (pPlc) {
pPlc->setValue(pPlc->getValue());
}
pPlc = dynamic_cast<App::PropertyPlacement*>(joint->getPropertyByName("Placement2"));
if (pPlc) {
pPlc->setValue(pPlc->getValue());
}
joint->purgeTouched();
}
void AssemblyObject::recomputeJointPlacements(std::vector<App::DocumentObject*> joints)
{
// The Placement1 and Placement2 of each joint needs to be updated as the parts moved.
@@ -884,6 +920,9 @@ std::shared_ptr<ASMTJoint> AssemblyObject::makeMbdJointOfType(App::DocumentObjec
JointType type)
{
if (type == JointType::Fixed) {
if (bundleFixed) {
return nullptr;
}
return CREATE<ASMTFixedJoint>::With();
}
else if (type == JointType::Revolute) {
@@ -1288,7 +1327,8 @@ std::string AssemblyObject::handleOneSideOfJoint(App::DocumentObject* joint,
return "";
}
std::shared_ptr<ASMTPart> mbdPart = getMbDPart(part);
MbDPartData data = getMbDData(part);
std::shared_ptr<ASMTPart> 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<ASMTPart> 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<ASMTPart> 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<ASMTPart> AssemblyObject::getMbDPart(App::DocumentObject* obj)
AssemblyObject::MbDPartData AssemblyObject::getMbDData(App::DocumentObject* part)
{
std::shared_ptr<ASMTPart> 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<ASMTPart> 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<App::DocumentObject*> 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<ASMTPart> AssemblyObject::getMbDPart(App::DocumentObject* part)
{
return getMbDData(part).part;
}
std::shared_ptr<ASMTPart>

View File

@@ -170,6 +170,7 @@ public:
void setNewPlacements();
static void recomputeJointPlacements(std::vector<App::DocumentObject*> joints);
static void redrawJointPlacements(std::vector<App::DocumentObject*> 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<MbD::ASMTPart>
makeMbdPart(std::string& name, Base::Placement plc = Base::Placement(), double mass = 1.0);
std::shared_ptr<MbD::ASMTPart> 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<MbD::ASMTPart> part;
Base::Placement offsetPlc; // This is the offset within the bundled parts
};
MbDPartData getMbDData(App::DocumentObject* obj);
std::shared_ptr<MbD::ASMTMarker> makeMbdMarker(std::string& name, Base::Placement& plc);
std::vector<std::shared_ptr<MbD::ASMTJoint>> makeMbdJoint(App::DocumentObject* joint);
std::shared_ptr<MbD::ASMTJoint> makeMbdJointOfType(App::DocumentObject* joint,
@@ -236,12 +247,13 @@ public:
private:
std::shared_ptr<MbD::ASMTAssembly> mbdAssembly;
std::unordered_map<App::DocumentObject*, std::shared_ptr<MbD::ASMTPart>> objectPartMap;
std::unordered_map<App::DocumentObject*, MbDPartData> objectPartMap;
std::vector<std::pair<App::DocumentObject*, double>> objMasses;
std::vector<std::shared_ptr<MbD::ASMTPart>> dragMbdParts;
std::vector<std::pair<App::DocumentObject*, Base::Placement>> previousPositions;
bool bundleFixed;
// void handleChangedPropertyType(Base::XMLReader &reader, const char *TypeName, App::Property
// *prop) override;

View File

@@ -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<AssemblyObject*>(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<AssemblyObject*>(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<AssemblyObject*>(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<AssemblyObject*>(getObject());
assemblyPart->postDrag();
assemblyPart->setObjMasses({});
}

View File

@@ -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<std::pair<App::DocumentObject*, bool>> jointVisibilitiesBackup;
std::vector<std::pair<App::DocumentObject*, double>> objectMasses;
std::vector<MovingObject> docsToMove;