Files
create/src/Gui/ViewProviderDragger.cpp

337 lines
11 KiB
C++

/***************************************************************************
* Copyright (c) 2017 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#include <memory>
#include <string>
#include <QAction>
#include <QMenu>
#include <Inventor/draggers/SoDragger.h>
#include <Inventor/nodes/SoPickStyle.h>
#include <Inventor/nodes/SoTransform.h>
#endif
#include <App/GeoFeature.h>
#include <Base/Placement.h>
#include <Base/Vector3D.h>
#include <Base/Converter.h>
#include "Gui/ViewParams.h"
#include "Application.h"
#include "BitmapFactory.h"
#include "Control.h"
#include "Document.h"
#include "SoFCCSysDragger.h"
#include "SoFCUnifiedSelection.h"
#include "TaskCSysDragger.h"
#include "View3DInventorViewer.h"
#include "ViewProviderDragger.h"
#include "Utilities.h"
#include <App/DocumentObjectGroup.h>
#include <Base/Tools.h>
using namespace Gui;
PROPERTY_SOURCE(Gui::ViewProviderDragger, Gui::ViewProviderDocumentObject)
ViewProviderDragger::ViewProviderDragger()
{
ADD_PROPERTY_TYPE(TransformOrigin, ({}), nullptr, App::Prop_Hidden, nullptr);
};
ViewProviderDragger::~ViewProviderDragger() = default;
void ViewProviderDragger::updateData(const App::Property* prop)
{
if (prop->isDerivedFrom(App::PropertyPlacement::getClassTypeId())
&& strcmp(prop->getName(), "Placement") == 0) {
// Note: If R is the rotation, c the rotation center and t the translation
// vector then Inventor applies the following transformation: R*(x-c)+c+t
// In FreeCAD a placement only has a rotation and a translation part but
// no rotation center. This means that the following equation must be ful-
// filled: R * (x-c) + c + t = R * x + t
// <==> R * x + t - R * c + c = R * x + t
// <==> (I-R) * c = 0 ==> c = 0
// This means that the center point must be the origin!
Base::Placement p = static_cast<const App::PropertyPlacement*>(prop)->getValue();
updateTransform(p, pcTransform);
}
ViewProviderDocumentObject::updateData(prop);
}
void ViewProviderDragger::setTransformOrigin(const Base::Placement& placement)
{
TransformOrigin.setValue(placement);
}
void ViewProviderDragger::resetTransformOrigin()
{
setTransformOrigin({});
}
void ViewProviderDragger::onChanged(const App::Property* property)
{
if (property == &TransformOrigin) {
updateDraggerPosition();
}
ViewProviderDocumentObject::onChanged(property);
}
TaskView::TaskDialog* ViewProviderDragger::getTransformDialog()
{
return new TaskCSysDragger(this, csysDragger);
}
bool ViewProviderDragger::doubleClicked()
{
Gui::Application::Instance->activeDocument()->setEdit(this, (int)ViewProvider::Default);
return true;
}
void ViewProviderDragger::setupContextMenu(QMenu* menu, QObject* receiver, const char* member)
{
QIcon iconObject =
mergeGreyableOverlayIcons(Gui::BitmapFactory().pixmap("Std_TransformManip.svg"));
QAction* act = menu->addAction(iconObject, QObject::tr("Transform"), receiver, member);
act->setData(QVariant((int)ViewProvider::Transform));
ViewProviderDocumentObject::setupContextMenu(menu, receiver, member);
}
ViewProvider* ViewProviderDragger::startEditing(int mode)
{
_linkDragger = nullptr;
auto ret = ViewProviderDocumentObject::startEditing(mode);
if (!ret) {
return ret;
}
return _linkDragger ? _linkDragger : ret;
}
bool ViewProviderDragger::checkLink()
{
// Trying to detect if the editing request is forwarded by a link object,
// usually by doubleClicked(). If so, we route the request back. There shall
// be no risk of infinite recursion, as ViewProviderLink handles
// ViewProvider::Transform request by itself.
ViewProviderDocumentObject* vpParent = nullptr;
std::string subname;
auto doc = Application::Instance->editDocument();
if (!doc) {
return false;
}
doc->getInEdit(&vpParent, &subname);
if (!vpParent) {
return false;
}
auto sobj = vpParent->getObject()->getSubObject(subname.c_str());
if (!sobj || sobj == getObject() || sobj->getLinkedObject(true) != getObject()) {
return false;
}
auto vp = Application::Instance->getViewProvider(sobj);
if (!vp) {
return false;
}
_linkDragger = vp->startEditing(ViewProvider::Transform);
return _linkDragger != nullptr;
}
bool ViewProviderDragger::setEdit(int ModNum)
{
Q_UNUSED(ModNum);
if (checkLink()) {
return true;
}
assert(!csysDragger);
csysDragger = new SoFCCSysDragger();
csysDragger->setAxisColors(Gui::ViewParams::instance()->getAxisXColor(),
Gui::ViewParams::instance()->getAxisYColor(),
Gui::ViewParams::instance()->getAxisZColor());
csysDragger->draggerSize.setValue(ViewParams::instance()->getDraggerScale());
csysDragger->addStartCallback(dragStartCallback, this);
csysDragger->addFinishCallback(dragFinishCallback, this);
csysDragger->addMotionCallback(dragMotionCallback, this);
Gui::Control().showDialog(getTransformDialog());
updateDraggerPosition();
return true;
}
void ViewProviderDragger::unsetEdit(int ModNum)
{
Q_UNUSED(ModNum);
csysDragger.reset();
Gui::Control().closeDialog();
}
void ViewProviderDragger::setEditViewer(Gui::View3DInventorViewer* viewer, int ModNum)
{
Q_UNUSED(ModNum);
if (csysDragger && viewer) {
csysDragger->setUpAutoScale(viewer->getSoRenderManager()->getCamera());
auto mat = viewer->getDocument()->getEditingTransform();
mat *= getObjectPlacement().inverse().toMatrix();
viewer->getDocument()->setEditingTransform(mat);
viewer->setupEditingRoot(csysDragger, &mat);
}
}
void ViewProviderDragger::unsetEditViewer(Gui::View3DInventorViewer* viewer)
{}
void ViewProviderDragger::dragStartCallback(void* data, [[maybe_unused]] SoDragger* d)
{
// This is called when a manipulator has done manipulating
auto vp = static_cast<ViewProviderDragger*>(data);
vp->draggerPlacement = vp->getDraggerPlacement();
vp->csysDragger->clearIncrementCounts();
}
void ViewProviderDragger::dragFinishCallback(void* data, SoDragger* d)
{
// This is called when a manipulator has done manipulating
auto vp = static_cast<ViewProviderDragger*>(data);
vp->draggerPlacement = vp->getDraggerPlacement();
vp->csysDragger->clearIncrementCounts();
vp->updatePlacementFromDragger();
}
void ViewProviderDragger::dragMotionCallback(void* data, SoDragger* d)
{
auto vp = static_cast<ViewProviderDragger*>(data);
vp->updateTransformFromDragger();
}
void ViewProviderDragger::updatePlacementFromDragger()
{
const auto placement = getObject()->getPropertyByName<App::PropertyPlacement>("Placement");
if (!placement) {
return;
}
placement->setValue(getDraggerPlacement() * getTransformOrigin().inverse());
}
void ViewProviderDragger::updateTransformFromDragger()
{
const auto placement = getDraggerPlacement() * getTransformOrigin().inverse();
pcTransform->translation.setValue(Base::convertTo<SbVec3f>(placement.getPosition()));
pcTransform->rotation.setValue(Base::convertTo<SbRotation>(placement.getRotation()));
}
Base::Placement ViewProviderDragger::getObjectPlacement() const
{
if (auto placement = getObject()->getPropertyByName<App::PropertyPlacement>("Placement")) {
return placement->getValue();
}
return {};
}
Base::Placement ViewProviderDragger::getDraggerPlacement() const
{
const double translationStep = csysDragger->translationIncrement.getValue();
const int xSteps = csysDragger->translationIncrementCountX.getValue();
const int ySteps = csysDragger->translationIncrementCountY.getValue();
const int zSteps = csysDragger->translationIncrementCountZ.getValue();
const auto rotation = draggerPlacement.getRotation();
const auto xBase = rotation.multVec(Base::Vector3d(1, 0, 0));
const auto yBase = rotation.multVec(Base::Vector3d(0, 1, 0));
const auto zBase = rotation.multVec(Base::Vector3d(0, 0, 1));
const auto positionIncrement =
xBase * (translationStep * xSteps) +
yBase * (translationStep * ySteps) +
zBase * (translationStep * zSteps);
const double rotationStep = csysDragger->rotationIncrement.getValue();
const int xRotationSteps = csysDragger->rotationIncrementCountX.getValue();
const int yRotationSteps = csysDragger->rotationIncrementCountY.getValue();
const int zRotationSteps = csysDragger->rotationIncrementCountZ.getValue();
auto newRotation = rotation;
newRotation = newRotation * Base::Rotation(Base::Vector3d(1, 0, 0), xRotationSteps * rotationStep);
newRotation = newRotation * Base::Rotation(Base::Vector3d(0, 1, 0), yRotationSteps * rotationStep);
newRotation = newRotation * Base::Rotation(Base::Vector3d(0, 0, 1), zRotationSteps * rotationStep);
return Base::Placement(
draggerPlacement.getPosition() + positionIncrement,
newRotation
);
}
void ViewProviderDragger::setDraggerPlacement(const Base::Placement& placement)
{
csysDragger->translation.setValue(Base::convertTo<SbVec3f>(placement.getPosition()));
csysDragger->rotation.setValue(Base::convertTo<SbRotation>(placement.getRotation()));
draggerPlacement = placement;
csysDragger->clearIncrementCounts();
}
void ViewProviderDragger::updateDraggerPosition()
{
if (!csysDragger) {
return;
}
auto placement = getObjectPlacement() * getTransformOrigin();
setDraggerPlacement(placement);
}
void ViewProviderDragger::updateTransform(const Base::Placement& from, SoTransform* to)
{
to->rotation.setValue(Base::convertTo<SbRotation>(from.getRotation()));
to->translation.setValue(Base::convertTo<SbVec3f>(from.getPosition()));
to->center.setValue(0.0f, 0.0f, 0.0f);
to->scaleFactor.setValue(1.0f, 1.0f, 1.0f);
}