860 lines
32 KiB
C++
860 lines
32 KiB
C++
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
/****************************************************************************
|
|
* *
|
|
* Copyright (c) 2023 Ondsel <development@ondsel.com> *
|
|
* *
|
|
* This file is part of FreeCAD. *
|
|
* *
|
|
* FreeCAD is free software: you can redistribute it and/or modify it *
|
|
* under the terms of the GNU Lesser General Public License as *
|
|
* published by the Free Software Foundation, either version 2.1 of the *
|
|
* License, or (at your option) any later version. *
|
|
* *
|
|
* FreeCAD is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* Lesser General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Lesser General Public *
|
|
* License along with FreeCAD. If not, see *
|
|
* <https://www.gnu.org/licenses/>. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "PreCompiled.h"
|
|
|
|
#ifndef _PreComp_
|
|
#include <QMessageBox>
|
|
#include <vector>
|
|
#include <sstream>
|
|
#include <iostream>
|
|
#include <Inventor/nodes/SoSeparator.h>
|
|
#include <Inventor/draggers/SoDragger.h>
|
|
#include <Inventor/events/SoKeyboardEvent.h>
|
|
#include <Inventor/nodes/SoSwitch.h>
|
|
#include <Inventor/nodes/SoTransform.h>
|
|
#include <Inventor/sensors/SoFieldSensor.h>
|
|
#include <Inventor/sensors/SoSensor.h>
|
|
#endif
|
|
|
|
#include <App/Link.h>
|
|
#include <App/Document.h>
|
|
#include <App/DocumentObject.h>
|
|
#include <App/Part.h>
|
|
|
|
#include <Gui/Application.h>
|
|
#include <Gui/BitmapFactory.h>
|
|
#include <Gui/CommandT.h>
|
|
#include <Gui/MDIView.h>
|
|
#include <Gui/SoFCCSysDragger.h>
|
|
#include <Gui/View3DInventor.h>
|
|
#include <Gui/View3DInventorViewer.h>
|
|
#include <Gui/ViewParams.h>
|
|
|
|
#include <Mod/Assembly/App/AssemblyObject.h>
|
|
#include <Mod/Assembly/App/AssemblyUtils.h>
|
|
#include <Mod/Assembly/App/JointGroup.h>
|
|
#include <Mod/PartDesign/App/Body.h>
|
|
|
|
#include "ViewProviderAssembly.h"
|
|
#include "ViewProviderAssemblyPy.h"
|
|
|
|
|
|
using namespace Assembly;
|
|
using namespace AssemblyGui;
|
|
|
|
void printPlacement(Base::Placement plc, const char* name)
|
|
{
|
|
Base::Vector3d pos = plc.getPosition();
|
|
Base::Vector3d axis;
|
|
double angle;
|
|
Base::Rotation rot = plc.getRotation();
|
|
rot.getRawValue(axis, angle);
|
|
Base::Console().Warning(
|
|
"placement %s : position (%.1f, %.1f, %.1f) - axis (%.1f, %.1f, %.1f) angle %.1f\n",
|
|
name,
|
|
pos.x,
|
|
pos.y,
|
|
pos.z,
|
|
axis.x,
|
|
axis.y,
|
|
axis.z,
|
|
angle);
|
|
}
|
|
|
|
PROPERTY_SOURCE(AssemblyGui::ViewProviderAssembly, Gui::ViewProviderPart)
|
|
|
|
ViewProviderAssembly::ViewProviderAssembly()
|
|
: SelectionObserver(true)
|
|
, dragMode(DragMode::None)
|
|
, canStartDragging(false)
|
|
, partMoving(false)
|
|
, enableMovement(true)
|
|
, jointVisibilityBackup(false)
|
|
, docsToMove({})
|
|
{}
|
|
|
|
ViewProviderAssembly::~ViewProviderAssembly() = default;
|
|
|
|
QIcon ViewProviderAssembly::getIcon() const
|
|
{
|
|
return Gui::BitmapFactory().pixmap("Geoassembly.svg");
|
|
}
|
|
|
|
bool ViewProviderAssembly::doubleClicked()
|
|
{
|
|
if (isInEditMode()) {
|
|
// Part is already 'Active' so we exit edit mode.
|
|
// Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()");
|
|
Gui::Application::Instance->activeDocument()->resetEdit();
|
|
}
|
|
else {
|
|
// assure the Assembly workbench
|
|
if (App::GetApplication()
|
|
.GetUserParameter()
|
|
.GetGroup("BaseApp")
|
|
->GetGroup("Preferences")
|
|
->GetGroup("Mod/Assembly")
|
|
->GetBool("SwitchToWB", true)) {
|
|
Gui::Command::assureWorkbench("AssemblyWorkbench");
|
|
}
|
|
|
|
// Part is not 'Active' so we enter edit mode to make it so.
|
|
Gui::Application::Instance->activeDocument()->setEdit(this);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ViewProviderAssembly::canDragObject(App::DocumentObject* obj) const
|
|
{
|
|
// The user should not be able to drag the joint group out of the assembly
|
|
if (!obj || obj->getTypeId() == Assembly::JointGroup::getClassTypeId()) {
|
|
return false;
|
|
}
|
|
|
|
// else if a solid is removed, remove associated joints if any.
|
|
bool prompted = false;
|
|
auto* assemblyPart = static_cast<AssemblyObject*>(getObject());
|
|
std::vector<App::DocumentObject*> joints = assemblyPart->getJoints();
|
|
|
|
// Combine the joints and groundedJoints vectors into one for simplicity.
|
|
std::vector<App::DocumentObject*> allJoints = assemblyPart->getJoints();
|
|
std::vector<App::DocumentObject*> groundedJoints = assemblyPart->getGroundedJoints();
|
|
allJoints.insert(allJoints.end(), groundedJoints.begin(), groundedJoints.end());
|
|
|
|
Gui::Command::openCommand(tr("Delete associated joints").toStdString().c_str());
|
|
for (auto joint : allJoints) {
|
|
// getLinkObjFromProp returns nullptr if the property doesn't exist.
|
|
App::DocumentObject* obj1 = AssemblyObject::getObjFromNameProp(joint, "Object1", "Part1");
|
|
App::DocumentObject* obj2 = AssemblyObject::getObjFromNameProp(joint, "Object2", "Part2");
|
|
App::DocumentObject* part1 = AssemblyObject::getLinkObjFromProp(joint, "Part1");
|
|
App::DocumentObject* part2 = AssemblyObject::getLinkObjFromProp(joint, "Part2");
|
|
App::DocumentObject* obj3 = AssemblyObject::getLinkObjFromProp(joint, "ObjectToGround");
|
|
if (obj == obj1 || obj == obj2 || obj == part1 || obj == part2 || obj == obj3) {
|
|
if (!prompted) {
|
|
prompted = true;
|
|
QMessageBox msgBox;
|
|
msgBox.setText(tr("The object is associated to one or more joints."));
|
|
msgBox.setInformativeText(
|
|
tr("Do you want to move the object and delete associated joints?"));
|
|
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
|
msgBox.setDefaultButton(QMessageBox::No);
|
|
int ret = msgBox.exec();
|
|
|
|
if (ret == QMessageBox::No) {
|
|
return false;
|
|
}
|
|
}
|
|
Gui::Command::doCommand(Gui::Command::Gui,
|
|
"App.activeDocument().removeObject('%s')",
|
|
joint->getNameInDocument());
|
|
}
|
|
}
|
|
Gui::Command::commitCommand();
|
|
|
|
// Remove grounded tag if any. (as it is not done in jointObject.py onDelete)
|
|
std::string label = obj->Label.getValue();
|
|
|
|
if (label.size() >= 4 && label.substr(label.size() - 2) == " 🔒") {
|
|
label = label.substr(0, label.size() - 2);
|
|
obj->Label.setValue(label.c_str());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ViewProviderAssembly::setEdit(int ModNum)
|
|
{
|
|
Q_UNUSED(ModNum);
|
|
|
|
// Set the part as 'Activated' ie bold in the tree.
|
|
Gui::Command::doCommand(Gui::Command::Gui,
|
|
"Gui.ActiveDocument.ActiveView.setActiveObject('%s', "
|
|
"App.getDocument('%s').getObject('%s'))",
|
|
PARTKEY,
|
|
this->getObject()->getDocument()->getName(),
|
|
this->getObject()->getNameInDocument());
|
|
|
|
setDragger();
|
|
|
|
return true;
|
|
}
|
|
|
|
void ViewProviderAssembly::unsetEdit(int ModNum)
|
|
{
|
|
Q_UNUSED(ModNum);
|
|
canStartDragging = false;
|
|
partMoving = false;
|
|
docsToMove = {};
|
|
|
|
unsetDragger();
|
|
|
|
// Check if the view is still active before trying to deactivate the assembly.
|
|
auto doc = getDocument();
|
|
if (!doc) {
|
|
return;
|
|
}
|
|
auto activeView = doc->getActiveView();
|
|
if (!activeView) {
|
|
return;
|
|
}
|
|
|
|
// Set the part as not 'Activated' ie not bold in the tree.
|
|
Gui::Command::doCommand(Gui::Command::Gui,
|
|
"appDoc = App.getDocument('%s')\n"
|
|
"Gui.getDocument(appDoc).ActiveView.setActiveObject('%s', None)",
|
|
this->getObject()->getDocument()->getName(),
|
|
PARTKEY);
|
|
}
|
|
|
|
void ViewProviderAssembly::setDragger()
|
|
{
|
|
// Create the dragger coin object
|
|
assert(!asmDragger);
|
|
asmDragger = new Gui::SoFCCSysDragger();
|
|
asmDragger->setAxisColors(Gui::ViewParams::instance()->getAxisXColor(),
|
|
Gui::ViewParams::instance()->getAxisYColor(),
|
|
Gui::ViewParams::instance()->getAxisZColor());
|
|
asmDragger->draggerSize.setValue(0.05f);
|
|
|
|
asmDraggerSwitch = new SoSwitch(SO_SWITCH_NONE);
|
|
asmDraggerSwitch->addChild(asmDragger);
|
|
|
|
pcRoot->insertChild(asmDraggerSwitch, 0);
|
|
asmDraggerSwitch->ref();
|
|
asmDragger->ref();
|
|
}
|
|
|
|
void ViewProviderAssembly::unsetDragger()
|
|
{
|
|
pcRoot->removeChild(asmDraggerSwitch);
|
|
asmDragger->unref();
|
|
asmDragger = nullptr;
|
|
asmDraggerSwitch->unref();
|
|
asmDraggerSwitch = nullptr;
|
|
}
|
|
|
|
void ViewProviderAssembly::setEditViewer(Gui::View3DInventorViewer* viewer, int ModNum)
|
|
{
|
|
ViewProviderPart::setEditViewer(viewer, ModNum);
|
|
|
|
if (asmDragger && viewer) {
|
|
asmDragger->setUpAutoScale(viewer->getSoRenderManager()->getCamera());
|
|
}
|
|
}
|
|
|
|
bool ViewProviderAssembly::isInEditMode() const
|
|
{
|
|
App::DocumentObject* activePart = getActivePart();
|
|
if (!activePart) {
|
|
return false;
|
|
}
|
|
|
|
return activePart == this->getObject();
|
|
}
|
|
|
|
App::DocumentObject* ViewProviderAssembly::getActivePart() const
|
|
{
|
|
auto activeDoc = Gui::Application::Instance->activeDocument();
|
|
if (!activeDoc) {
|
|
activeDoc = getDocument();
|
|
}
|
|
auto activeView = activeDoc->getActiveView();
|
|
if (!activeView) {
|
|
return nullptr;
|
|
}
|
|
|
|
return activeView->getActiveObject<App::DocumentObject*>(PARTKEY);
|
|
}
|
|
|
|
bool ViewProviderAssembly::keyPressed(bool pressed, int key)
|
|
{
|
|
Q_UNUSED(pressed);
|
|
|
|
if (key == SoKeyboardEvent::ESCAPE) {
|
|
if (isInEditMode()) {
|
|
|
|
ParameterGrp::handle hPgr = App::GetApplication().GetParameterGroupByPath(
|
|
"User parameter:BaseApp/Preferences/Mod/Assembly");
|
|
|
|
return !hPgr->GetBool("LeaveEditWithEscape", true);
|
|
}
|
|
}
|
|
|
|
return false; // handle all other key events
|
|
}
|
|
|
|
bool ViewProviderAssembly::mouseMove(const SbVec2s& cursorPos, Gui::View3DInventorViewer* viewer)
|
|
{
|
|
// Initialize or cancel the dragging of parts
|
|
if (canStartDragging) {
|
|
canStartDragging = false;
|
|
|
|
if (enableMovement && getSelectedObjectsWithinAssembly()) {
|
|
initMove(cursorPos, viewer);
|
|
}
|
|
}
|
|
|
|
// Do the dragging of parts
|
|
if (partMoving) {
|
|
Base::Vector3d newPos, newPosRot;
|
|
if (dragMode == DragMode::RotationOnPlane) {
|
|
SbVec3f vec = viewer->getPointOnXYPlaneOfPlacement(cursorPos, jcsGlobalPlc);
|
|
newPosRot = Base::Vector3d(vec[0], vec[1], vec[2]);
|
|
}
|
|
else if (dragMode == DragMode::TranslationOnAxis) {
|
|
Base::Vector3d zAxis = jcsGlobalPlc.getRotation().multVec(Base::Vector3d(0., 0., 1.));
|
|
Base::Vector3d pos = jcsGlobalPlc.getPosition();
|
|
SbVec3f axisCenter(pos.x, pos.y, pos.z);
|
|
SbVec3f axis(zAxis.x, zAxis.y, zAxis.z);
|
|
SbVec3f vec = viewer->getPointOnLine(cursorPos, axisCenter, axis);
|
|
newPos = Base::Vector3d(vec[0], vec[1], vec[2]);
|
|
}
|
|
else if (dragMode == DragMode::TranslationOnAxisAndRotationOnePlane) {
|
|
SbVec3f vec = viewer->getPointOnXYPlaneOfPlacement(cursorPos, jcsGlobalPlc);
|
|
newPosRot = Base::Vector3d(vec[0], vec[1], vec[2]);
|
|
|
|
Base::Vector3d zAxis = jcsGlobalPlc.getRotation().multVec(Base::Vector3d(0., 0., 1.));
|
|
Base::Vector3d pos = jcsGlobalPlc.getPosition();
|
|
SbVec3f axisCenter(pos.x, pos.y, pos.z);
|
|
SbVec3f axis(zAxis.x, zAxis.y, zAxis.z);
|
|
vec = viewer->getPointOnLine(cursorPos, axisCenter, axis);
|
|
newPos = Base::Vector3d(vec[0], vec[1], vec[2]);
|
|
}
|
|
else if (dragMode == DragMode::TranslationOnPlane) {
|
|
SbVec3f vec = viewer->getPointOnXYPlaneOfPlacement(cursorPos, jcsGlobalPlc);
|
|
newPos = Base::Vector3d(vec[0], vec[1], vec[2]);
|
|
}
|
|
else {
|
|
SbVec3f vec = viewer->getPointOnFocalPlane(cursorPos);
|
|
newPos = Base::Vector3d(vec[0], vec[1], vec[2]);
|
|
}
|
|
|
|
|
|
for (auto& pair : docsToMove) {
|
|
App::DocumentObject* obj = pair.first;
|
|
auto* propPlacement =
|
|
dynamic_cast<App::PropertyPlacement*>(obj->getPropertyByName("Placement"));
|
|
if (propPlacement) {
|
|
Base::Placement plc = pair.second;
|
|
// Base::Console().Warning("newPos %f %f %f\n", newPos.x, newPos.y, newPos.z);
|
|
|
|
if (dragMode == DragMode::RotationOnPlane) {
|
|
Base::Vector3d center = jcsGlobalPlc.getPosition();
|
|
Base::Vector3d norm =
|
|
jcsGlobalPlc.getRotation().multVec(Base::Vector3d(0., 0., -1.));
|
|
double angle =
|
|
(newPosRot - center).GetAngleOriented(initialPositionRot - center, norm);
|
|
// Base::Console().Warning("angle %f\n", angle);
|
|
Base::Rotation zRotation = Base::Rotation(Base::Vector3d(0., 0., 1.), angle);
|
|
Base::Placement rotatedGlovalJcsPlc =
|
|
jcsGlobalPlc * Base::Placement(Base::Vector3d(), zRotation);
|
|
Base::Placement jcsPlcRelativeToPart = plc.inverse() * jcsGlobalPlc;
|
|
plc = rotatedGlovalJcsPlc * jcsPlcRelativeToPart.inverse();
|
|
}
|
|
else if (dragMode == DragMode::TranslationOnAxis) {
|
|
Base::Vector3d pos = plc.getPosition() + (newPos - initialPosition);
|
|
plc.setPosition(pos);
|
|
}
|
|
else if (dragMode == DragMode::TranslationOnAxisAndRotationOnePlane) {
|
|
Base::Vector3d pos = plc.getPosition() + (newPos - initialPosition);
|
|
plc.setPosition(pos);
|
|
|
|
Base::Placement newJcsGlobalPlc = jcsGlobalPlc;
|
|
newJcsGlobalPlc.setPosition(jcsGlobalPlc.getPosition()
|
|
+ (newPos - initialPosition));
|
|
|
|
Base::Vector3d center = newJcsGlobalPlc.getPosition();
|
|
Base::Vector3d norm =
|
|
newJcsGlobalPlc.getRotation().multVec(Base::Vector3d(0., 0., -1.));
|
|
|
|
Base::Vector3d projInitialPositionRot =
|
|
initialPositionRot.ProjectToPlane(newJcsGlobalPlc.getPosition(), norm);
|
|
double angle =
|
|
(newPosRot - center).GetAngleOriented(initialPositionRot - center, norm);
|
|
// Base::Console().Warning("angle %f\n", angle);
|
|
Base::Rotation zRotation = Base::Rotation(Base::Vector3d(0., 0., 1.), angle);
|
|
Base::Placement rotatedGlovalJcsPlc =
|
|
newJcsGlobalPlc * Base::Placement(Base::Vector3d(), zRotation);
|
|
Base::Placement jcsPlcRelativeToPart = plc.inverse() * newJcsGlobalPlc;
|
|
plc = rotatedGlovalJcsPlc * jcsPlcRelativeToPart.inverse();
|
|
}
|
|
else if (dragMode == DragMode::TranslationOnPlane) {
|
|
Base::Vector3d pos = plc.getPosition() + (newPos - initialPosition);
|
|
plc.setPosition(pos);
|
|
}
|
|
else { // DragMode::Translation
|
|
Base::Vector3d delta = newPos - prevPosition;
|
|
prevPosition = newPos;
|
|
|
|
Base::Vector3d pos = propPlacement->getValue().getPosition() + delta;
|
|
// Base::Vector3d pos = newPos + (plc.getPosition() - initialPosition);
|
|
plc.setPosition(pos);
|
|
}
|
|
propPlacement->setValue(plc);
|
|
}
|
|
}
|
|
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
|
|
"User parameter:BaseApp/Preferences/Mod/Assembly");
|
|
bool solveOnMove = hGrp->GetBool("SolveOnMove", true);
|
|
if (solveOnMove) {
|
|
auto* assemblyPart = static_cast<AssemblyObject*>(getObject());
|
|
assemblyPart->solve();
|
|
// assemblyPart->doDragStep();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ViewProviderAssembly::mouseButtonPressed(int Button,
|
|
bool pressed,
|
|
const SbVec2s& cursorPos,
|
|
const Gui::View3DInventorViewer* viewer)
|
|
{
|
|
Q_UNUSED(cursorPos);
|
|
Q_UNUSED(viewer);
|
|
|
|
// Left Mouse button ****************************************************
|
|
if (Button == 1) {
|
|
if (pressed) {
|
|
canStartDragging = true;
|
|
}
|
|
else { // Button 1 released
|
|
// release event is not received when user click on a part for selection.
|
|
// So we use SelectionObserver to know if something got selected.
|
|
|
|
canStartDragging = false;
|
|
if (partMoving) {
|
|
endMove();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ViewProviderAssembly::getSelectedObjectsWithinAssembly()
|
|
{
|
|
// check the current selection, and check if any of the selected objects are within this
|
|
// App::Part
|
|
// If any, put them into the vector docsToMove and return true.
|
|
// Get the document
|
|
Gui::Document* doc = Gui::Application::Instance->activeDocument();
|
|
|
|
if (!doc) {
|
|
return false;
|
|
}
|
|
|
|
// Get the assembly object for this ViewProvider
|
|
AssemblyObject* assemblyPart = static_cast<AssemblyObject*>(getObject());
|
|
|
|
if (!assemblyPart) {
|
|
return false;
|
|
}
|
|
|
|
for (auto& selObj : Gui::Selection().getSelectionEx("",
|
|
App::DocumentObject::getClassTypeId(),
|
|
Gui::ResolveMode::NoResolve)) {
|
|
// getSubNames() returns ["Body001.Pad.Face14", "Body002.Pad.Face7"]
|
|
// if you have several objects within the same assembly selected.
|
|
|
|
std::vector<std::string> objsSubNames = selObj.getSubNames();
|
|
for (auto& subNamesStr : objsSubNames) {
|
|
std::vector<std::string> subNames = parseSubNames(subNamesStr);
|
|
|
|
App::DocumentObject* obj = getObjectFromSubNames(subNames);
|
|
if (!obj) {
|
|
continue;
|
|
}
|
|
|
|
// Check if the selected object is a child of the assembly
|
|
if (assemblyPart->hasObject(obj, true)) {
|
|
auto* propPlacement =
|
|
dynamic_cast<App::PropertyPlacement*>(obj->getPropertyByName("Placement"));
|
|
if (propPlacement) {
|
|
docsToMove.emplace_back(obj, propPlacement->getValue());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// This function is called before the selection is updated. So if a user click and drag a part
|
|
// it is not selected at that point. So we need to get the preselection too.
|
|
if (Gui::Selection().hasPreselection()) {
|
|
|
|
// Base::Console().Warning("Gui::Selection().getPreselection().pSubName %s\n",
|
|
// Gui::Selection().getPreselection().pSubName);
|
|
|
|
std::string subNamesStr = Gui::Selection().getPreselection().pSubName;
|
|
std::vector<std::string> subNames = parseSubNames(subNamesStr);
|
|
|
|
App::DocumentObject* preselectedObj = getObjectFromSubNames(subNames);
|
|
if (preselectedObj && assemblyPart->hasObject(preselectedObj, true)) {
|
|
bool alreadyIn = false;
|
|
for (auto& pair : docsToMove) {
|
|
App::DocumentObject* obj = pair.first;
|
|
if (obj == preselectedObj) {
|
|
alreadyIn = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!alreadyIn) {
|
|
auto* propPlacement = dynamic_cast<App::PropertyPlacement*>(
|
|
preselectedObj->getPropertyByName("Placement"));
|
|
if (propPlacement) {
|
|
docsToMove.emplace_back(preselectedObj, propPlacement->getValue());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return !docsToMove.empty();
|
|
}
|
|
|
|
std::vector<std::string> ViewProviderAssembly::parseSubNames(std::string& subNamesStr)
|
|
{
|
|
std::vector<std::string> subNames;
|
|
std::string subName;
|
|
std::istringstream subNameStream(subNamesStr);
|
|
while (std::getline(subNameStream, subName, '.')) {
|
|
subNames.push_back(subName);
|
|
}
|
|
return subNames;
|
|
}
|
|
|
|
App::DocumentObject* ViewProviderAssembly::getObjectFromSubNames(std::vector<std::string>& subNames)
|
|
{
|
|
App::Document* appDoc = App::GetApplication().getActiveDocument();
|
|
|
|
std::string objName;
|
|
if (subNames.size() < 2) {
|
|
return nullptr;
|
|
}
|
|
else if (subNames.size() == 2) {
|
|
// If two subnames then it can't be a body and the object we want is the first one
|
|
// For example we want box in "box.face1"
|
|
return appDoc->getObject(subNames[0].c_str());
|
|
}
|
|
|
|
// From here subnames is at least 3 and can be more. There are several cases to consider :
|
|
// bodyOrLink.pad.face1 -> bodyOrLink should be the moving entity
|
|
// partOrLink.bodyOrLink.pad.face1 -> partOrLink should be the moving entity
|
|
// partOrLink.box.face1 -> partOrLink should be the moving entity
|
|
// partOrLink1...ParOrLinkn.bodyOrLink.pad.face1 -> partOrLink1 should be the moving entity
|
|
// assembly1.partOrLink1...ParOrLinkn.bodyOrLink.pad.face1 -> partOrLink1 should be the moving
|
|
// entity assembly1.boxOrLink1.face1 -> boxOrLink1 should be the moving entity
|
|
|
|
for (auto objName : subNames) {
|
|
App::DocumentObject* obj = appDoc->getObject(objName.c_str());
|
|
if (!obj) {
|
|
continue;
|
|
}
|
|
|
|
if (obj->getTypeId().isDerivedFrom(AssemblyObject::getClassTypeId())) {
|
|
continue;
|
|
}
|
|
else if (obj->getTypeId().isDerivedFrom(App::Part::getClassTypeId())
|
|
|| obj->getTypeId().isDerivedFrom(PartDesign::Body::getClassTypeId())) {
|
|
return obj;
|
|
}
|
|
else if (obj->getTypeId().isDerivedFrom(App::Link::getClassTypeId())) {
|
|
App::Link* link = dynamic_cast<App::Link*>(obj);
|
|
|
|
App::DocumentObject* linkedObj = link->getLinkedObject(true);
|
|
if (!linkedObj) {
|
|
continue;
|
|
}
|
|
|
|
if (linkedObj->getTypeId().isDerivedFrom(App::Part::getClassTypeId())
|
|
|| linkedObj->getTypeId().isDerivedFrom(PartDesign::Body::getClassTypeId())) {
|
|
return obj;
|
|
}
|
|
}
|
|
}
|
|
|
|
// then its neither a part or body or a link to a part or body. So it is something like
|
|
// assembly.box.face1
|
|
objName = subNames[subNames.size() - 2];
|
|
return appDoc->getObject(objName.c_str());
|
|
}
|
|
|
|
ViewProviderAssembly::DragMode ViewProviderAssembly::findDragMode()
|
|
{
|
|
if (docsToMove.size() == 1) {
|
|
auto* assemblyPart = static_cast<AssemblyObject*>(getObject());
|
|
std::string partPropName;
|
|
movingJoint =
|
|
assemblyPart->getJointOfPartConnectingToGround(docsToMove[0].first, partPropName);
|
|
|
|
if (!movingJoint) {
|
|
return DragMode::Translation;
|
|
}
|
|
|
|
JointType jointType = AssemblyObject::getJointType(movingJoint);
|
|
if (jointType == JointType::Fixed) {
|
|
// If fixed joint we need to find the upstream joint to find move mode.
|
|
// For example : Gnd -(revolute)- A -(fixed)- B : if user try to move B, then we should
|
|
// actually move A
|
|
App::DocumentObject* upstreamPart =
|
|
assemblyPart->getUpstreamMovingPart(docsToMove[0].first);
|
|
docsToMove.clear();
|
|
if (!upstreamPart) {
|
|
return DragMode::None;
|
|
}
|
|
|
|
auto* propPlacement =
|
|
dynamic_cast<App::PropertyPlacement*>(upstreamPart->getPropertyByName("Placement"));
|
|
if (propPlacement) {
|
|
docsToMove.emplace_back(upstreamPart, propPlacement->getValue());
|
|
}
|
|
|
|
movingJoint =
|
|
assemblyPart->getJointOfPartConnectingToGround(docsToMove[0].first, partPropName);
|
|
if (!movingJoint) {
|
|
return DragMode::Translation;
|
|
}
|
|
jointType = AssemblyObject::getJointType(movingJoint);
|
|
}
|
|
|
|
const char* plcPropName = (partPropName == "Part1") ? "Placement1" : "Placement2";
|
|
const char* objPropName = (partPropName == "Part1") ? "Object1" : "Object2";
|
|
|
|
// jcsPlc is relative to the Object
|
|
jcsPlc = AssemblyObject::getPlacementFromProp(movingJoint, plcPropName);
|
|
|
|
// Make jcsGlobalPlc relative to the origin of the doc
|
|
Base::Placement global_plc =
|
|
AssemblyObject::getGlobalPlacement(movingJoint, objPropName, partPropName.c_str());
|
|
jcsGlobalPlc = global_plc * jcsPlc;
|
|
|
|
// Add downstream parts so that they move together
|
|
auto downstreamParts = assemblyPart->getDownstreamParts(docsToMove[0].first, movingJoint);
|
|
for (auto part : downstreamParts) {
|
|
auto* propPlacement =
|
|
dynamic_cast<App::PropertyPlacement*>(part->getPropertyByName("Placement"));
|
|
if (propPlacement) {
|
|
docsToMove.emplace_back(part, propPlacement->getValue());
|
|
}
|
|
}
|
|
|
|
jointVisibilityBackup = movingJoint->Visibility.getValue();
|
|
if (!jointVisibilityBackup) {
|
|
movingJoint->Visibility.setValue(true);
|
|
}
|
|
|
|
if (jointType == JointType::Revolute) {
|
|
return DragMode::RotationOnPlane;
|
|
}
|
|
else if (jointType == JointType::Slider) {
|
|
return DragMode::TranslationOnAxis;
|
|
}
|
|
else if (jointType == JointType::Cylindrical) {
|
|
return DragMode::TranslationOnAxisAndRotationOnePlane;
|
|
}
|
|
else if (jointType == JointType::Ball) {
|
|
// return DragMode::Ball;
|
|
}
|
|
else if (jointType == JointType::Distance) {
|
|
// depends on the type of distance. For example plane-plane:
|
|
DistanceType distanceType = AssemblyObject::getDistanceType(movingJoint);
|
|
if (distanceType == DistanceType::PlanePlane || distanceType == DistanceType::Other) {
|
|
return DragMode::TranslationOnPlane;
|
|
}
|
|
}
|
|
}
|
|
return DragMode::Translation;
|
|
}
|
|
|
|
void ViewProviderAssembly::initMove(const SbVec2s& cursorPos, Gui::View3DInventorViewer* viewer)
|
|
{
|
|
dragMode = findDragMode();
|
|
if (dragMode == DragMode::None) {
|
|
return;
|
|
}
|
|
|
|
SbVec3f vec;
|
|
if (dragMode == DragMode::RotationOnPlane) {
|
|
vec = viewer->getPointOnXYPlaneOfPlacement(cursorPos, jcsGlobalPlc);
|
|
initialPositionRot = Base::Vector3d(vec[0], vec[1], vec[2]);
|
|
}
|
|
else if (dragMode == DragMode::TranslationOnAxis) {
|
|
Base::Vector3d zAxis = jcsGlobalPlc.getRotation().multVec(Base::Vector3d(0., 0., 1.));
|
|
Base::Vector3d pos = jcsGlobalPlc.getPosition();
|
|
SbVec3f axisCenter(pos.x, pos.y, pos.z);
|
|
SbVec3f axis(zAxis.x, zAxis.y, zAxis.z);
|
|
vec = viewer->getPointOnLine(cursorPos, axisCenter, axis);
|
|
initialPosition = Base::Vector3d(vec[0], vec[1], vec[2]);
|
|
}
|
|
else if (dragMode == DragMode::TranslationOnAxisAndRotationOnePlane) {
|
|
vec = viewer->getPointOnXYPlaneOfPlacement(cursorPos, jcsGlobalPlc);
|
|
initialPositionRot = Base::Vector3d(vec[0], vec[1], vec[2]);
|
|
|
|
Base::Vector3d zAxis = jcsGlobalPlc.getRotation().multVec(Base::Vector3d(0., 0., 1.));
|
|
Base::Vector3d pos = jcsGlobalPlc.getPosition();
|
|
SbVec3f axisCenter(pos.x, pos.y, pos.z);
|
|
SbVec3f axis(zAxis.x, zAxis.y, zAxis.z);
|
|
vec = viewer->getPointOnLine(cursorPos, axisCenter, axis);
|
|
initialPosition = Base::Vector3d(vec[0], vec[1], vec[2]);
|
|
}
|
|
else if (dragMode == DragMode::TranslationOnPlane) {
|
|
vec = viewer->getPointOnXYPlaneOfPlacement(cursorPos, jcsGlobalPlc);
|
|
initialPosition = Base::Vector3d(vec[0], vec[1], vec[2]);
|
|
}
|
|
else {
|
|
vec = viewer->getPointOnFocalPlane(cursorPos);
|
|
initialPosition = Base::Vector3d(vec[0], vec[1], vec[2]);
|
|
prevPosition = initialPosition;
|
|
}
|
|
|
|
|
|
Gui::Command::openCommand(tr("Move part").toStdString().c_str());
|
|
partMoving = true;
|
|
|
|
// prevent selection while moving
|
|
viewer->setSelectionEnabled(false);
|
|
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
|
|
"User parameter:BaseApp/Preferences/Mod/Assembly");
|
|
bool solveOnMove = hGrp->GetBool("SolveOnMove", true);
|
|
if (solveOnMove) {
|
|
objectMasses.clear();
|
|
for (auto& pair : docsToMove) {
|
|
objectMasses.push_back({pair.first, 10.0});
|
|
}
|
|
|
|
auto* assemblyPart = static_cast<AssemblyObject*>(getObject());
|
|
assemblyPart->setObjMasses(objectMasses);
|
|
/*std::vector<App::DocumentObject*> dragParts;
|
|
for (auto& pair : docsToMove) {
|
|
dragParts.push_back(pair.first);
|
|
}
|
|
assemblyPart->preDrag(dragParts);*/
|
|
}
|
|
}
|
|
|
|
void ViewProviderAssembly::endMove()
|
|
{
|
|
docsToMove = {};
|
|
partMoving = false;
|
|
canStartDragging = false;
|
|
|
|
if (movingJoint && !jointVisibilityBackup) {
|
|
movingJoint->Visibility.setValue(false);
|
|
}
|
|
|
|
movingJoint = nullptr;
|
|
|
|
// enable selection after the move
|
|
auto* view = dynamic_cast<Gui::View3DInventor*>(
|
|
Gui::Application::Instance->editDocument()->getActiveView());
|
|
if (view) {
|
|
Gui::View3DInventorViewer* viewerNotConst;
|
|
viewerNotConst = static_cast<Gui::View3DInventor*>(view)->getViewer();
|
|
viewerNotConst->setSelectionEnabled(true);
|
|
}
|
|
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
|
|
"User parameter:BaseApp/Preferences/Mod/Assembly");
|
|
bool solveOnMove = hGrp->GetBool("SolveOnMove", true);
|
|
if (solveOnMove) {
|
|
auto* assemblyPart = static_cast<AssemblyObject*>(getObject());
|
|
// assemblyPart->postDrag();
|
|
assemblyPart->setObjMasses({});
|
|
}
|
|
|
|
Gui::Command::commitCommand();
|
|
}
|
|
|
|
void ViewProviderAssembly::onSelectionChanged(const Gui::SelectionChanges& msg)
|
|
{
|
|
if (msg.Type == Gui::SelectionChanges::AddSelection
|
|
|| msg.Type == Gui::SelectionChanges::ClrSelection
|
|
|| msg.Type == Gui::SelectionChanges::RmvSelection) {
|
|
canStartDragging = false;
|
|
}
|
|
}
|
|
|
|
bool ViewProviderAssembly::onDelete(const std::vector<std::string>& subNames)
|
|
{
|
|
// Delete the joingroup when assembly is deleted
|
|
for (auto obj : getObject()->getOutList()) {
|
|
if (obj->getTypeId() == Assembly::JointGroup::getClassTypeId()) {
|
|
obj->getDocument()->removeObject(obj->getNameInDocument());
|
|
}
|
|
}
|
|
|
|
return ViewProviderPart::onDelete(subNames);
|
|
}
|
|
|
|
void ViewProviderAssembly::setDraggerVisibility(bool val)
|
|
{
|
|
asmDraggerSwitch->whichChild = val ? SO_SWITCH_ALL : SO_SWITCH_NONE;
|
|
}
|
|
bool ViewProviderAssembly::getDraggerVisibility()
|
|
{
|
|
return asmDraggerSwitch->whichChild.getValue() == SO_SWITCH_ALL;
|
|
}
|
|
|
|
void ViewProviderAssembly::setDraggerPlacement(Base::Placement plc)
|
|
{
|
|
double q0, q1, q2, q3;
|
|
plc.getRotation().getValue(q0, q1, q2, q3);
|
|
Base::Vector3d pos = plc.getPosition();
|
|
asmDragger->rotation.setValue(q0, q1, q2, q3);
|
|
asmDragger->translation.setValue(pos.x, pos.y, pos.z);
|
|
}
|
|
|
|
Base::Placement ViewProviderAssembly::getDraggerPlacement()
|
|
{
|
|
Base::Placement plc;
|
|
SbVec3f pos = asmDragger->translation.getValue();
|
|
plc.setPosition(Base::Vector3d(pos[0], pos[1], pos[2]));
|
|
|
|
SbVec3f axis;
|
|
float angle;
|
|
asmDragger->rotation.getValue(axis, angle);
|
|
Base::Vector3d axisV = Base::Vector3d(axis[0], axis[1], axis[2]);
|
|
Base::Rotation rot(axisV, angle);
|
|
plc.setRotation(rot);
|
|
|
|
return plc;
|
|
}
|
|
|
|
Gui::SoFCCSysDragger* ViewProviderAssembly::getDragger()
|
|
{
|
|
return asmDragger;
|
|
}
|
|
|
|
PyObject* ViewProviderAssembly::getPyObject()
|
|
{
|
|
if (!pyViewObject) {
|
|
pyViewObject = new ViewProviderAssemblyPy(this);
|
|
}
|
|
pyViewObject->IncRef();
|
|
return pyViewObject;
|
|
}
|