pxmlw6n2f/Gazebo_Distributed_TCP/gazebo/gui/ApplyWrenchDialog.cc

1310 lines
41 KiB
C++

/*
* Copyright 2015 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifdef _WIN32
// Ensure that Winsock2.h is included before Windows.h, which can get
// pulled in by anybody (e.g., Boost).
#include <Winsock2.h>
#endif
#include "gazebo/transport/Node.hh"
#include "gazebo/transport/Publisher.hh"
#include "gazebo/rendering/UserCamera.hh"
#include "gazebo/rendering/Scene.hh"
#include "gazebo/rendering/COMVisual.hh"
#include "gazebo/rendering/Visual.hh"
#include "gazebo/rendering/SelectionObj.hh"
#include "gazebo/rendering/ApplyWrenchVisual.hh"
#include "gazebo/gui/Actions.hh"
#include "gazebo/gui/MainWindow.hh"
#include "gazebo/gui/MouseEventHandler.hh"
#include "gazebo/gui/GuiIface.hh"
#include "gazebo/gui/ApplyWrenchDialogPrivate.hh"
#include "gazebo/gui/ApplyWrenchDialog.hh"
using namespace gazebo;
using namespace gui;
/////////////////////////////////////////////////
ApplyWrenchDialog::ApplyWrenchDialog(QWidget *_parent)
: QDialog(_parent), dataPtr(new ApplyWrenchDialogPrivate)
{
this->setObjectName("ApplyWrenchDialog");
this->dataPtr->mainWindow = gui::get_main_window();
this->setWindowTitle(tr("Apply Force and Torque"));
this->setWindowFlags(Qt::Window | Qt::WindowCloseButtonHint |
Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint);
this->setWindowModality(Qt::NonModal);
this->setStyleSheet(
"QPushButton {\
border-radius: 5px;\
border-radius: 5px;\
}");
this->dataPtr->modelLabel = new QLabel();
// Links list
QHBoxLayout *linkLayout = new QHBoxLayout();
QLabel *linkLabel = new QLabel(tr("<b>Apply to link:<b> "));
this->dataPtr->linksComboBox = new QComboBox();
this->dataPtr->linksComboBox->installEventFilter(this);
this->dataPtr->linksComboBox->setMinimumWidth(200);
connect(this->dataPtr->linksComboBox, SIGNAL(currentIndexChanged(QString)),
this, SLOT(SetLink(QString)));
linkLayout->addWidget(linkLabel);
linkLayout->addWidget(this->dataPtr->linksComboBox);
// Force
QLabel *forceLabel = new QLabel(tr(
"<font size=4>Force</font>"));
forceLabel->setObjectName("forceLabel");
forceLabel->setStyleSheet(
"QLabel#forceLabel {\
background-color: #444;\
border-radius: 5px;\
padding-left: 10px;\
min-height: 40px;\
}");
// Force vector layout
QGridLayout *forceVectorLayout = new QGridLayout();
// Force Vector
this->dataPtr->forceXSpin = new QDoubleSpinBox();
this->dataPtr->forceYSpin = new QDoubleSpinBox();
this->dataPtr->forceZSpin = new QDoubleSpinBox();
std::vector<QDoubleSpinBox *> forceSpins;
forceSpins.push_back(this->dataPtr->forceXSpin);
forceSpins.push_back(this->dataPtr->forceYSpin);
forceSpins.push_back(this->dataPtr->forceZSpin);
for (unsigned int i = 0; i < forceSpins.size(); ++i)
{
QLabel *forceElementLabel = new QLabel();
if (i == 0)
forceElementLabel->setText(tr("X:"));
else if (i == 1)
forceElementLabel->setText(tr("Y:"));
else if (i == 2)
forceElementLabel->setText(tr("Z:"));
QLabel *forceUnitLabel = new QLabel(tr("N"));
forceSpins[i]->setRange(-GZ_DBL_MAX, GZ_DBL_MAX);
forceSpins[i]->setSingleStep(100);
forceSpins[i]->setDecimals(3);
forceSpins[i]->setValue(0);
forceSpins[i]->setMaximumWidth(100);
forceSpins[i]->installEventFilter(this);
connect(forceSpins[i], SIGNAL(valueChanged(double)), this,
SLOT(OnForceChanged(double)));
forceVectorLayout->addWidget(forceElementLabel, i, 0, Qt::AlignRight);
forceVectorLayout->addWidget(forceSpins[i], i, 1);
forceVectorLayout->addWidget(forceUnitLabel, i, 2);
}
// Force total
QLabel *forceMagLabel = new QLabel(tr("Mag:"));
QLabel *forceMagUnitLabel = new QLabel(tr("N"));
this->dataPtr->forceMagSpin = new QDoubleSpinBox();
this->dataPtr->forceMagSpin->setRange(0, GZ_DBL_MAX);
this->dataPtr->forceMagSpin->setSingleStep(100);
this->dataPtr->forceMagSpin->setDecimals(3);
this->dataPtr->forceMagSpin->setValue(0);
this->dataPtr->forceMagSpin->setMaximumWidth(100);
this->dataPtr->forceMagSpin->installEventFilter(this);
connect(this->dataPtr->forceMagSpin, SIGNAL(valueChanged(double)), this,
SLOT(OnForceMagChanged(double)));
forceVectorLayout->addWidget(forceMagLabel, 3, 0, Qt::AlignRight);
forceVectorLayout->addWidget(this->dataPtr->forceMagSpin, 3, 1);
forceVectorLayout->addWidget(forceMagUnitLabel, 3, 2);
// Clear force
QPushButton *forceClearButton = new QPushButton(tr("Clear"));
connect(forceClearButton, SIGNAL(clicked()), this, SLOT(OnForceClear()));
forceVectorLayout->addWidget(forceClearButton, 4, 0, 1, 3, Qt::AlignLeft);
// Vertical separator
QFrame *separator = new QFrame();
separator->setFrameShape(QFrame::VLine);
separator->setLineWidth(10);
// Force Position
QLabel *forcePosLabel = new QLabel(tr("Application Point:"));
forcePosLabel->setObjectName("forcePosLabel");
forcePosLabel->setStyleSheet(
"QLabel#forcePosLabel {\
max-height: 15px;\
}");
// CoM
QLabel *comLabel = new QLabel(tr("Center of mass"));
QLabel *comPixLabel = new QLabel();
QPixmap comPixmap(":images/com.png");
comPixmap = comPixmap.scaled(QSize(20, 20));
comPixLabel->setPixmap(comPixmap);
comPixLabel->setMask(comPixmap.mask());
QHBoxLayout *comLabelLayout = new QHBoxLayout();
comLabelLayout->addWidget(comLabel);
comLabelLayout->addWidget(comPixLabel);
this->dataPtr->comRadio = new QRadioButton();
this->dataPtr->forcePosRadio = new QRadioButton();
this->dataPtr->comRadio->setChecked(true);
connect(this->dataPtr->comRadio, SIGNAL(toggled(bool)), this,
SLOT(ToggleComRadio(bool)));
// Force Position layout
QGridLayout *forcePosLayout = new QGridLayout();
forcePosLayout->setContentsMargins(0, 0, 0, 0);
forcePosLayout->addWidget(forcePosLabel, 0, 0, 1, 4, Qt::AlignLeft);
forcePosLayout->addWidget(this->dataPtr->comRadio, 1, 0);
forcePosLayout->addLayout(comLabelLayout, 1, 1, 1, 3, Qt::AlignLeft);
forcePosLayout->addWidget(this->dataPtr->forcePosRadio, 2, 0);
// Force Position Vector
this->dataPtr->forcePosXSpin = new QDoubleSpinBox();
this->dataPtr->forcePosYSpin = new QDoubleSpinBox();
this->dataPtr->forcePosZSpin = new QDoubleSpinBox();
std::vector<QDoubleSpinBox *> forcePosSpins;
forcePosSpins.push_back(this->dataPtr->forcePosXSpin);
forcePosSpins.push_back(this->dataPtr->forcePosYSpin);
forcePosSpins.push_back(this->dataPtr->forcePosZSpin);
for (unsigned int i = 0; i < forcePosSpins.size(); ++i)
{
QLabel *forcePosElementLabel = new QLabel();
if (i == 0)
forcePosElementLabel->setText(tr("X:"));
else if (i == 1)
forcePosElementLabel->setText(tr("Y:"));
else if (i == 2)
forcePosElementLabel->setText(tr("Z:"));
QLabel *forcePosUnitLabel = new QLabel(tr("m"));
forcePosSpins[i]->setRange(-GZ_DBL_MAX, GZ_DBL_MAX);
forcePosSpins[i]->setSingleStep(0.1);
forcePosSpins[i]->setDecimals(3);
forcePosSpins[i]->setValue(0);
forcePosSpins[i]->setMaximumWidth(100);
forcePosSpins[i]->installEventFilter(this);
connect(forcePosSpins[i], SIGNAL(valueChanged(double)), this,
SLOT(OnForcePosChanged(double)));
forcePosLayout->addWidget(forcePosElementLabel, i+2, 1, Qt::AlignRight);
forcePosLayout->addWidget(forcePosSpins[i], i+2, 2);
forcePosLayout->addWidget(forcePosUnitLabel, i+2, 3);
}
// Apply force
QPushButton *applyForceButton = new QPushButton("Apply Force");
connect(applyForceButton, SIGNAL(clicked()), this, SLOT(OnApplyForce()));
// Force layout
QGridLayout *forceLayout = new QGridLayout();
forceLayout->setContentsMargins(0, 0, 0, 0);
forceLayout->addWidget(forceLabel, 0, 0, 1, 5);
forceLayout->addItem(new QSpacerItem(10, 10), 1, 0, 1, 5);
forceLayout->addLayout(forceVectorLayout, 2, 1);
forceLayout->addWidget(separator, 2, 2);
forceLayout->addLayout(forcePosLayout, 2, 3);
forceLayout->addItem(new QSpacerItem(10, 10), 3, 0, 1, 5);
forceLayout->addWidget(applyForceButton, 4, 1, 1, 3, Qt::AlignRight);
forceLayout->addItem(new QSpacerItem(5, 10), 5, 0);
forceLayout->addItem(new QSpacerItem(7, 10), 5, 4);
QFrame *forceFrame = new QFrame();
forceFrame->setLayout(forceLayout);
forceFrame->setObjectName("forceLayout");
forceFrame->setFrameShape(QFrame::StyledPanel);
forceFrame->setStyleSheet(
"QFrame#forceLayout {\
background-color: #666;\
border-radius: 10px;\
}");
QGraphicsDropShadowEffect *forceEffect = new QGraphicsDropShadowEffect;
forceEffect->setBlurRadius(5);
forceEffect->setXOffset(5);
forceEffect->setYOffset(5);
forceEffect->setColor(Qt::black);
forceFrame->setGraphicsEffect(forceEffect);
// Torque
QLabel *torqueLabel = new QLabel(tr("<font size=4>Torque</font>"));
torqueLabel->setObjectName("torqueLabel");
torqueLabel->setStyleSheet(
"QLabel#torqueLabel {\
background-color: #444;\
border-radius: 5px;\
padding-left: 10px;\
min-height: 40px;\
}");
// Torque vector layout
QGridLayout *torqueVectorLayout = new QGridLayout();
// Torque Vector
this->dataPtr->torqueXSpin = new QDoubleSpinBox();
this->dataPtr->torqueYSpin = new QDoubleSpinBox();
this->dataPtr->torqueZSpin = new QDoubleSpinBox();
std::vector<QDoubleSpinBox *> torqueSpins;
torqueSpins.push_back(this->dataPtr->torqueXSpin);
torqueSpins.push_back(this->dataPtr->torqueYSpin);
torqueSpins.push_back(this->dataPtr->torqueZSpin);
for (unsigned int i = 0; i < torqueSpins.size(); ++i)
{
QLabel *torqueElementLabel = new QLabel();
if (i == 0)
torqueElementLabel->setText(tr("X:"));
else if (i == 1)
torqueElementLabel->setText(tr("Y:"));
else if (i == 2)
torqueElementLabel->setText(tr("Z:"));
QLabel *torqueUnitLabel = new QLabel(tr("Nm"));
torqueSpins[i]->setRange(-GZ_DBL_MAX, GZ_DBL_MAX);
torqueSpins[i]->setSingleStep(100);
torqueSpins[i]->setDecimals(3);
torqueSpins[i]->setValue(0);
torqueSpins[i]->setMaximumWidth(100);
torqueSpins[i]->installEventFilter(this);
connect(torqueSpins[i], SIGNAL(valueChanged(double)), this,
SLOT(OnTorqueChanged(double)));
torqueVectorLayout->addWidget(torqueElementLabel, i, 0, Qt::AlignRight);
torqueVectorLayout->addWidget(torqueSpins[i], i, 1);
torqueVectorLayout->addWidget(torqueUnitLabel, i, 2);
}
// Torque magnitude
QLabel *torqueMagLabel = new QLabel(tr("Mag:"));
QLabel *torqueMagUnitLabel = new QLabel(tr("Nm"));
this->dataPtr->torqueMagSpin = new QDoubleSpinBox();
this->dataPtr->torqueMagSpin->setRange(0, GZ_DBL_MAX);
this->dataPtr->torqueMagSpin->setSingleStep(100);
this->dataPtr->torqueMagSpin->setDecimals(3);
this->dataPtr->torqueMagSpin->setValue(0);
this->dataPtr->torqueMagSpin->setMaximumWidth(100);
this->dataPtr->torqueMagSpin->installEventFilter(this);
connect(this->dataPtr->torqueMagSpin, SIGNAL(valueChanged(double)), this,
SLOT(OnTorqueMagChanged(double)));
torqueVectorLayout->addWidget(torqueMagLabel, 3, 0, Qt::AlignRight);
torqueVectorLayout->addWidget(this->dataPtr->torqueMagSpin, 3, 1);
torqueVectorLayout->addWidget(torqueMagUnitLabel, 3, 2);
// Clear torque
QPushButton *torqueClearButton = new QPushButton(tr("Clear"));
connect(torqueClearButton, SIGNAL(clicked()), this, SLOT(OnTorqueClear()));
torqueVectorLayout->addWidget(torqueClearButton, 4, 0, 1, 3, Qt::AlignLeft);
// Apply torque
QPushButton *applyTorqueButton = new QPushButton("Apply Torque");
connect(applyTorqueButton, SIGNAL(clicked()), this, SLOT(OnApplyTorque()));
// Torque layout
QGridLayout *torqueLayout = new QGridLayout();
torqueLayout->setContentsMargins(0, 0, 0, 0);
torqueLayout->addWidget(torqueLabel, 0, 0, 1, 3);
torqueLayout->addItem(new QSpacerItem(10, 10), 1, 0, 1, 3);
torqueLayout->addLayout(torqueVectorLayout, 2, 1);
torqueLayout->addItem(new QSpacerItem(10, 10), 3, 0, 1, 3);
torqueLayout->addWidget(applyTorqueButton, 4, 1, 1, 1, Qt::AlignRight);
torqueLayout->addItem(new QSpacerItem(5, 10), 5, 0);
torqueLayout->addItem(new QSpacerItem(5, 10), 5, 2);
QFrame *torqueFrame = new QFrame();
torqueFrame->setLayout(torqueLayout);
torqueFrame->setObjectName("torqueLayout");
torqueFrame->setFrameShape(QFrame::StyledPanel);
torqueFrame->setStyleSheet(
"QFrame#torqueLayout {\
background-color: #666;\
border-radius: 10px;\
}");
QGraphicsDropShadowEffect *torqueEffect = new QGraphicsDropShadowEffect;
torqueEffect->setBlurRadius(5);
torqueEffect->setXOffset(5);
torqueEffect->setYOffset(5);
torqueEffect->setColor(Qt::black);
torqueFrame->setGraphicsEffect(torqueEffect);
// Buttons
QPushButton *cancelButton = new QPushButton(tr("Cancel"));
connect(cancelButton, SIGNAL(clicked()), this, SLOT(OnCancel()));
QPushButton *applyAllButton = new QPushButton("Apply All");
applyAllButton->setDefault(true);
connect(applyAllButton, SIGNAL(clicked()), this, SLOT(OnApplyAll()));
QHBoxLayout *buttonsLayout = new QHBoxLayout;
buttonsLayout->addWidget(cancelButton);
buttonsLayout->addWidget(applyAllButton);
// Main layout
QGridLayout *mainLayout = new QGridLayout();
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
mainLayout->addWidget(this->dataPtr->modelLabel, 0, 0, 1, 2, Qt::AlignLeft);
mainLayout->addLayout(linkLayout, 1, 0, 1, 2, Qt::AlignLeft);
mainLayout->addWidget(forceFrame, 2, 0);
mainLayout->addWidget(torqueFrame, 2, 1);
mainLayout->addLayout(buttonsLayout, 3, 0, 1, 2, Qt::AlignRight);
this->setLayout(mainLayout);
// Transport
this->dataPtr->node = transport::NodePtr(new transport::Node());
this->dataPtr->node->TryInit(common::Time::Maximum());
this->dataPtr->userCmdPub =
this->dataPtr->node->Advertise<msgs::UserCmd>("~/user_cmd");
this->dataPtr->comVector = math::Vector3::Zero;
this->dataPtr->forceVector = math::Vector3::Zero;
this->dataPtr->torqueVector = math::Vector3::Zero;
}
/////////////////////////////////////////////////
ApplyWrenchDialog::~ApplyWrenchDialog()
{
this->Fini();
delete this->dataPtr;
this->dataPtr = NULL;
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::Init(const std::string &_modelName,
const std::string &_linkName)
{
if (!this->SetModel(_modelName))
{
this->Fini();
return;
}
if (!this->SetLink(_linkName))
{
this->Fini();
return;
}
connect(this, SIGNAL(rejected()), this, SLOT(OnCancel()));
if (g_rotateAct)
connect(g_rotateAct, SIGNAL(triggered()), this, SLOT(OnManipulation()));
if (g_translateAct)
connect(g_translateAct, SIGNAL(triggered()), this, SLOT(OnManipulation()));
if (g_scaleAct)
connect(g_scaleAct, SIGNAL(triggered()), this, SLOT(OnManipulation()));
this->move(QCursor::pos());
this->show();
this->ActivateWindow();
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::Fini()
{
if (this->dataPtr->mainWindow)
this->dataPtr->mainWindow->removeEventFilter(this);
this->dataPtr->userCmdPub.reset();
this->dataPtr->node->Fini();
this->dataPtr->connections.clear();
if (this->dataPtr->applyWrenchVisual)
{
MouseEventHandler::Instance()->RemoveReleaseFilter(
"dialog_"+this->dataPtr->applyWrenchVisual->GetName());
MouseEventHandler::Instance()->RemovePressFilter(
"dialog_"+this->dataPtr->applyWrenchVisual->GetName());
MouseEventHandler::Instance()->RemoveMoveFilter(
"dialog_"+this->dataPtr->applyWrenchVisual->GetName());
this->dataPtr->applyWrenchVisual->Fini();
}
this->dataPtr->applyWrenchVisual.reset();
this->deleteLater();
}
/////////////////////////////////////////////////
bool ApplyWrenchDialog::SetModel(const std::string &_modelName)
{
if (!gui::get_active_camera() || !gui::get_active_camera()->GetScene())
return false;
rendering::VisualPtr vis = gui::get_active_camera()->GetScene()->
GetVisual(_modelName);
if (!vis)
{
gzerr << "Model [" << _modelName << "] could not be found." << std::endl;
return false;
}
this->dataPtr->modelName = _modelName;
// Check if model/link hasn't been deleted on PreRender
this->dataPtr->connections.push_back(
event::Events::ConnectPreRender(
boost::bind(&ApplyWrenchDialog::OnPreRender, this)));
this->dataPtr->modelLabel->setText(("<b>Model:</b> " + _modelName).c_str());
// Don't fire signals while inserting items
this->dataPtr->linksComboBox->blockSignals(true);
this->dataPtr->linksComboBox->clear();
for (unsigned int i = 0; i < vis->GetChildCount(); ++i)
{
rendering::VisualPtr childVis = vis->GetChild(i);
std::string linkName = childVis->GetName();
// Issue #1553: This is failing to get real links sometimes:
// uint32_t flags = childVis->GetVisibilityFlags();
// if (!((flags != GZ_VISIBILITY_ALL) && (flags & GZ_VISIBILITY_GUI)))
if (linkName.find("_GL_MANIP_") == std::string::npos)
{
std::string unscopedLinkName = linkName.substr(linkName.find("::") + 2);
this->dataPtr->linksComboBox->addItem(
QString::fromStdString(unscopedLinkName));
// Get CoM from link's COMVisual
for (unsigned int j = 0; j < childVis->GetChildCount(); ++j)
{
rendering::COMVisualPtr comVis =
std::dynamic_pointer_cast<rendering::COMVisual>(
childVis->GetChild(j));
if (comVis)
{
this->dataPtr->linkToCOMMap[linkName] = comVis->GetInertiaPose().pos;
break;
}
}
}
}
// Sort alphabetically
QSortFilterProxyModel *proxy = new QSortFilterProxyModel(
this->dataPtr->linksComboBox);
proxy->setSourceModel(this->dataPtr->linksComboBox->model());
this->dataPtr->linksComboBox->model()->setParent(proxy);
this->dataPtr->linksComboBox->setModel(proxy);
this->dataPtr->linksComboBox->model()->sort(0);
this->dataPtr->linksComboBox->blockSignals(false);
if (this->dataPtr->linksComboBox->count() > 0)
return true;
gzerr << "Couldn't find links in model ' [" << _modelName << "]."
<< std::endl;
return false;
}
/////////////////////////////////////////////////
bool ApplyWrenchDialog::SetLink(const std::string &_linkName)
{
if (!gui::get_active_camera() || !gui::get_active_camera()->GetScene())
return false;
// Select on combo box
std::string unscopedLinkName = _linkName.substr(_linkName.find("::") + 2);
int index = -1;
for (int i = 0; i < this->dataPtr->linksComboBox->count(); ++i)
{
if ((this->dataPtr->linksComboBox->itemText(i)).toStdString() ==
unscopedLinkName)
{
index = i;
break;
}
}
if (index == -1)
{
gzerr << "Link [" << _linkName << "] could not be found in the combo box."
<< std::endl;
return false;
}
this->dataPtr->linksComboBox->setCurrentIndex(index);
// Visual
this->dataPtr->linkName = _linkName;
rendering::VisualPtr vis = gui::get_active_camera()->GetScene()->
GetVisual(this->dataPtr->linkName);
if (!vis)
{
gzerr << "A visual named [" << this->dataPtr->linkName
<< "] could not be found." << std::endl;
return false;
}
this->dataPtr->linkVisual = vis;
this->AttachVisuals();
// Filter main window activate events
if (this->dataPtr->mainWindow)
this->dataPtr->mainWindow->installEventFilter(this);
// MouseRelease filter to gain focus
if (this->dataPtr->applyWrenchVisual)
{
MouseEventHandler::Instance()->AddReleaseFilter(
"dialog_"+this->dataPtr->applyWrenchVisual->GetName(),
boost::bind(&ApplyWrenchDialog::OnMouseRelease, this, _1));
}
return true;
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::SetLink(const QString _linkName)
{
// Remove previous link's filter
if (this->dataPtr->applyWrenchVisual)
{
MouseEventHandler::Instance()->RemoveReleaseFilter(
"dialog_"+this->dataPtr->applyWrenchVisual->GetName());
}
if (!this->SetLink(this->dataPtr->modelName + "::" + _linkName.toStdString()))
this->Fini();
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::OnApplyAll()
{
// Publish wrench message
msgs::Wrench msg;
msgs::Set(msg.mutable_force(), this->dataPtr->forceVector.Ign());
msgs::Set(msg.mutable_torque(), this->dataPtr->torqueVector.Ign());
msgs::Set(msg.mutable_force_offset(), this->dataPtr->forcePosVector.Ign());
// Register user command on server
// The wrench will be applied from the server
msgs::UserCmd userCmdMsg;
userCmdMsg.set_description("Apply wrench to [" + this->dataPtr->linkName +
"]");
userCmdMsg.set_entity_name(this->dataPtr->linkName);
userCmdMsg.set_type(msgs::UserCmd::WRENCH);
userCmdMsg.mutable_wrench()->CopyFrom(msg);
this->dataPtr->userCmdPub->Publish(userCmdMsg);
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::OnApplyForce()
{
// Publish wrench message
msgs::Wrench msg;
msgs::Set(msg.mutable_force(), this->dataPtr->forceVector.Ign());
msgs::Set(msg.mutable_torque(), ignition::math::Vector3d::Zero);
msgs::Set(msg.mutable_force_offset(), this->dataPtr->forcePosVector.Ign());
// Register user command on server
// The wrench will be applied from the server
msgs::UserCmd userCmdMsg;
userCmdMsg.set_description("Apply force to [" + this->dataPtr->linkName +
"]");
userCmdMsg.set_entity_name(this->dataPtr->linkName);
userCmdMsg.set_type(msgs::UserCmd::WRENCH);
userCmdMsg.mutable_wrench()->CopyFrom(msg);
this->dataPtr->userCmdPub->Publish(userCmdMsg);
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::OnApplyTorque()
{
// Publish wrench message
msgs::Wrench msg;
msgs::Set(msg.mutable_force(), ignition::math::Vector3d::Zero);
msgs::Set(msg.mutable_torque(), this->dataPtr->torqueVector.Ign());
// Register user command on server
// The wrench will be applied from the server
msgs::UserCmd userCmdMsg;
userCmdMsg.set_description("Apply torque to [" + this->dataPtr->linkName +
"]");
userCmdMsg.set_entity_name(this->dataPtr->linkName);
userCmdMsg.set_type(msgs::UserCmd::WRENCH);
userCmdMsg.mutable_wrench()->CopyFrom(msg);
this->dataPtr->userCmdPub->Publish(userCmdMsg);
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::OnCancel()
{
this->Fini();
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::OnForcePosChanged(double /*_value*/)
{
// Update forcePos vector with values from XYZ spins
this->SetForcePos(
math::Vector3(this->dataPtr->forcePosXSpin->value(),
this->dataPtr->forcePosYSpin->value(),
this->dataPtr->forcePosZSpin->value()));
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::OnForceMagChanged(double /*_magnitude*/)
{
// Update force vector proportionally
// Normalize current vector
math::Vector3 v = this->dataPtr->forceVector;
if (v == math::Vector3::Zero)
v = math::Vector3::UnitX;
else
v.Normalize();
// Multiply by new magnitude
this->SetForce(v * this->dataPtr->forceMagSpin->value());
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::OnForceChanged(double /*_value*/)
{
// Update force vector with values from XYZ spins
this->SetForce(math::Vector3(this->dataPtr->forceXSpin->value(),
this->dataPtr->forceYSpin->value(),
this->dataPtr->forceZSpin->value()));
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::OnForceClear()
{
this->SetForce(math::Vector3::Zero);
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::OnTorqueMagChanged(double /*_magnitude*/)
{
// Update torque vector proportionally
// Normalize current vector
math::Vector3 v = this->dataPtr->torqueVector;
if (v == math::Vector3::Zero)
v = math::Vector3::UnitX;
else
v.Normalize();
// Multiply by new magnitude
this->SetTorque(v * this->dataPtr->torqueMagSpin->value());
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::OnTorqueChanged(double /*_value*/)
{
// Update torque vector with values from XYZ spins
this->SetTorque(math::Vector3(this->dataPtr->torqueXSpin->value(),
this->dataPtr->torqueYSpin->value(),
this->dataPtr->torqueZSpin->value()));
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::OnTorqueClear()
{
this->SetTorque(math::Vector3::Zero);
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::ToggleComRadio(bool _checked)
{
if (_checked)
{
this->SetForcePos(this->dataPtr->comVector);
}
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::SetSpinValue(QDoubleSpinBox *_spin, double _value)
{
_spin->blockSignals(true);
_spin->setValue(_value);
_spin->blockSignals(false);
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::SetForcePos(const math::Vector3 &_forcePos)
{
this->dataPtr->forcePosVector = _forcePos;
// Spins
this->SetSpinValue(this->dataPtr->forcePosXSpin, _forcePos.x);
this->SetSpinValue(this->dataPtr->forcePosYSpin, _forcePos.y);
this->SetSpinValue(this->dataPtr->forcePosZSpin, _forcePos.z);
// Check COM box
if (_forcePos == this->dataPtr->comVector)
{
this->dataPtr->comRadio->setChecked(true);
}
else
{
this->dataPtr->forcePosRadio->setChecked(true);
}
// Visuals
if (!this->dataPtr->applyWrenchVisual)
{
gzwarn << "No wrench visual found, so it won't be updated" << std::endl;
return;
}
this->dataPtr->applyWrenchVisual->SetForcePos(
this->dataPtr->forcePosVector.Ign());
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::SetForce(const math::Vector3 &_force,
const bool _rotatedByMouse)
{
// This can be called from the dialog or the mouse
std::lock_guard<std::mutex> lock(this->dataPtr->mutex);
this->dataPtr->forceVector = _force;
// Spins
this->SetSpinValue(this->dataPtr->forceXSpin, _force.x);
this->SetSpinValue(this->dataPtr->forceYSpin, _force.y);
this->SetSpinValue(this->dataPtr->forceZSpin, _force.z);
this->SetSpinValue(this->dataPtr->forceMagSpin, _force.GetLength());
// Mode
if (_force == math::Vector3::Zero)
{
if (this->dataPtr->torqueVector == math::Vector3::Zero)
this->SetMode(Mode::NONE);
else
this->SetMode(Mode::TORQUE);
}
else
{
this->SetMode(Mode::FORCE);
}
// Visuals
if (!this->dataPtr->applyWrenchVisual)
{
gzwarn << "No wrench visual found, so it won't be updated" << std::endl;
return;
}
this->dataPtr->applyWrenchVisual->SetForce(_force.Ign(), _rotatedByMouse);
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::SetTorque(const math::Vector3 &_torque,
const bool _rotatedByMouse)
{
// This can be called from the dialog or the mouse
std::lock_guard<std::mutex> lock(this->dataPtr->mutex);
this->dataPtr->torqueVector = _torque;
// Spins
this->SetSpinValue(this->dataPtr->torqueXSpin, _torque.x);
this->SetSpinValue(this->dataPtr->torqueYSpin, _torque.y);
this->SetSpinValue(this->dataPtr->torqueZSpin, _torque.z);
this->SetSpinValue(this->dataPtr->torqueMagSpin, _torque.GetLength());
// Mode
if (_torque == math::Vector3::Zero)
{
if (this->dataPtr->forceVector == math::Vector3::Zero)
this->SetMode(Mode::NONE);
else
this->SetMode(Mode::FORCE);
}
else
{
this->SetMode(Mode::TORQUE);
}
// Visuals
if (!this->dataPtr->applyWrenchVisual)
{
gzwarn << "No wrench visual found, so it won't be updated" << std::endl;
return;
}
this->dataPtr->applyWrenchVisual->SetTorque(_torque.Ign(), _rotatedByMouse);
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::SetCoM(const math::Vector3 &_com)
{
this->dataPtr->comVector = _com;
// Visuals
if (!this->dataPtr->applyWrenchVisual)
{
gzwarn << "No wrench visual found, so it won't be updated" << std::endl;
return;
}
this->dataPtr->applyWrenchVisual->SetCoM(this->dataPtr->comVector.Ign());
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::OnPreRender()
{
if (!gui::get_active_camera() || !gui::get_active_camera()->GetScene())
return;
rendering::VisualPtr vis = gui::get_active_camera()->GetScene()->
GetVisual(this->dataPtr->linkName);
// Close dialog in case visual has been deleted
if (!vis)
this->Fini();
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::AttachVisuals()
{
if (!gui::get_active_camera() || !gui::get_active_camera()->GetScene())
{
gzerr << "Camera or scene missing" << std::endl;
return;
}
if (!this->dataPtr->linkVisual)
{
gzerr << "No link visual specified." << std::endl;
return;
}
// Attaching for the first time
if (!this->dataPtr->applyWrenchVisual)
{
// Generate unique name
std::string visNameBase = this->dataPtr->modelName + "__APPLY_WRENCH__";
rendering::VisualPtr vis = gui::get_active_camera()->GetScene()->
GetVisual(visNameBase);
std::string visName(visNameBase);
int count = 0;
while (vis)
{
visName = visNameBase + std::to_string(count);
vis = gui::get_active_camera()->GetScene()->GetVisual(visName);
++count;
}
this->dataPtr->applyWrenchVisual.reset(new rendering::ApplyWrenchVisual(
visName, this->dataPtr->linkVisual));
this->dataPtr->applyWrenchVisual->Load();
}
// Different link
else if (!this->dataPtr->applyWrenchVisual->GetParent() ||
this->dataPtr->applyWrenchVisual->GetParent() !=
this->dataPtr->linkVisual)
{
this->dataPtr->linkVisual->AttachVisual(this->dataPtr->applyWrenchVisual);
this->dataPtr->applyWrenchVisual->Resize();
}
if (!this->dataPtr->applyWrenchVisual)
{
gzwarn << "Failed to attach wrench visual. " <<
"Dialog will work without it." << std::endl;
}
// Set COM
this->SetCoM(this->dataPtr->linkToCOMMap[this->dataPtr->linkName]);
// Apply force at com by default
this->SetForcePos(this->dataPtr->comVector);
this->SetTorque(this->dataPtr->torqueVector);
this->SetForce(this->dataPtr->forceVector);
}
/////////////////////////////////////////////////
bool ApplyWrenchDialog::OnMousePress(const common::MouseEvent &_event)
{
rendering::UserCameraPtr userCamera = gui::get_active_camera();
if (!userCamera || !this->dataPtr->applyWrenchVisual)
return false;
this->dataPtr->draggingTool = false;
rendering::VisualPtr vis = userCamera->GetVisual(_event.Pos(),
this->dataPtr->manipState);
if (vis)
return false;
// If on top of a circle handle
if (this->dataPtr->manipState == "rot_z" ||
this->dataPtr->manipState == "rot_y")
{
this->dataPtr->draggingTool = true;
// Highlight dragged circle
this->dataPtr->applyWrenchVisual->GetRotTool()->SetState(
this->dataPtr->manipState);
// Register rotTool pose at drag start
this->dataPtr->dragStartPose =
this->dataPtr->applyWrenchVisual->GetRotTool()->GetWorldPose();
}
return false;
}
/////////////////////////////////////////////////
bool ApplyWrenchDialog::OnMouseRelease(const common::MouseEvent &_event)
{
rendering::UserCameraPtr userCamera = gui::get_active_camera();
if (!userCamera || !this->dataPtr->applyWrenchVisual)
return false;
rendering::VisualPtr vis = userCamera->GetVisual(_event.Pos(),
this->dataPtr->manipState);
if (!vis || _event.Dragging())
return false;
// Force/torque clicked: activate dialog and prevent event propagation
if (vis == this->dataPtr->applyWrenchVisual->GetForceVisual())
{
this->ActivateWindow();
// Activate visual, can't attach rot tool with zero vector, UnitX by default
if (this->dataPtr->forceVector == math::Vector3::Zero)
this->SetForce(math::Vector3::UnitX);
else
this->SetForce(this->dataPtr->forceVector);
return true;
}
else if (vis == this->dataPtr->applyWrenchVisual->GetTorqueVisual())
{
this->ActivateWindow();
// Activate visual, can't attach rot tool with zero vector, UnitX by default
if (this->dataPtr->torqueVector == math::Vector3::Zero)
this->SetTorque(math::Vector3::UnitX);
else
this->SetTorque(this->dataPtr->torqueVector);
return true;
}
return false;
}
/////////////////////////////////////////////////
bool ApplyWrenchDialog::OnMouseMove(const common::MouseEvent &_event)
{
rendering::UserCameraPtr userCamera = gui::get_active_camera();
if (!userCamera || !this->dataPtr->applyWrenchVisual)
return false;
// Must make a Qt check as well because Gazebo event is not working with test
bool isDragging = _event.Dragging() ||
QApplication::mouseButtons() != Qt::NoButton;
bool isLeftButton = _event.Button() == common::MouseEvent::LEFT ||
QApplication::mouseButtons() == Qt::LeftButton;
// Dragging tool, adapted from ModelManipulator::RotateEntity
if (isDragging && isLeftButton && this->dataPtr->draggingTool)
{
ignition::math::Vector3d normal;
ignition::math::Vector3d axis;
if (this->dataPtr->manipState == "rot_z")
{
normal = this->dataPtr->dragStartPose.rot.GetZAxis().Ign();
axis = ignition::math::Vector3d::UnitZ;
}
else if (this->dataPtr->manipState == "rot_y")
{
normal = this->dataPtr->dragStartPose.rot.GetYAxis().Ign();
axis = ignition::math::Vector3d::UnitY;
}
else
{
gzerr << "Dragging tool on wrong manip state, this shouldn't happen" <<
std::endl;
return false;
}
double offset = this->dataPtr->dragStartPose.pos.Ign().Dot(normal);
ignition::math::Vector3d pressPoint;
userCamera->WorldPointOnPlane(_event.PressPos().X(),
_event.PressPos().Y(),
ignition::math::Planed(normal, offset), pressPoint);
ignition::math::Vector3d newPoint;
userCamera->WorldPointOnPlane(_event.Pos().X(), _event.Pos().Y(),
ignition::math::Planed(normal, offset), newPoint);
ignition::math::Vector3d v1 = pressPoint -
this->dataPtr->dragStartPose.pos.Ign();
ignition::math::Vector3d v2 = newPoint -
this->dataPtr->dragStartPose.pos.Ign();
v1 = v1.Normalize();
v2 = v2.Normalize();
double signTest = v1.Cross(v2).Dot(normal);
double angle = atan2((v1.Cross(v2)).Length(), v1.Dot(v2));
if (signTest < 0)
angle *= -1;
if (QApplication::keyboardModifiers() & Qt::ControlModifier)
angle = rint(angle / (M_PI * 0.25)) * (M_PI * 0.25);
ignition::math::Quaterniond rot(axis, angle);
rot = this->dataPtr->dragStartPose.rot.Ign() * rot;
// Must rotate the tool here to make sure we have proper roll,
// once the rotation gets transformed into a vector we lose a DOF
this->dataPtr->applyWrenchVisual->GetRotTool()->SetWorldRotation(rot);
// Get direction from tool orientation
ignition::math::Vector3d vec;
ignition::math::Vector3d rotEuler;
rotEuler = rot.Euler();
vec.X(cos(rotEuler.Z())*cos(rotEuler.Y()));
vec.Y(sin(rotEuler.Z())*cos(rotEuler.Y()));
vec.Z(-sin(rotEuler.Y()));
// To local frame
vec = this->dataPtr->linkVisual->GetWorldPose().rot.RotateVectorReverse(
vec).Ign();
if (this->GetMode() == Mode::FORCE)
{
this->NewForceDirection(vec);
}
else if (this->GetMode() == Mode::TORQUE)
{
this->NewTorqueDirection(vec);
}
return true;
}
// Highlight hovered tools
else
{
userCamera->GetVisual(_event.Pos(), this->dataPtr->manipState);
if (this->dataPtr->manipState == "rot_z" ||
this->dataPtr->manipState == "rot_y")
{
this->dataPtr->applyWrenchVisual->GetRotTool()->SetState(
this->dataPtr->manipState);
}
else
{
this->dataPtr->applyWrenchVisual->GetRotTool()->SetState("");
}
}
return false;
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::SetMode(Mode _mode)
{
this->dataPtr->mode = _mode;
}
/////////////////////////////////////////////////
ApplyWrenchDialog::Mode ApplyWrenchDialog::GetMode() const
{
return this->dataPtr->mode;
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::NewForceDirection(const math::Vector3 &_dir)
{
// Normalize direction
math::Vector3 v = _dir;
if (v == math::Vector3::Zero)
v = math::Vector3::UnitX;
else
v.Normalize();
// Multiply by magnitude
this->SetForce(v * this->dataPtr->forceMagSpin->value(), true);
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::NewTorqueDirection(const math::Vector3 &_dir)
{
// Normalize direction
math::Vector3 v = _dir;
if (v == math::Vector3::Zero)
v = math::Vector3::UnitX;
else
v.Normalize();
// Multiply by magnitude
this->SetTorque(v * this->dataPtr->torqueMagSpin->value(), true);
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::SetActive(bool _active)
{
if (!this->dataPtr->applyWrenchVisual)
{
gzerr << "No apply wrench visual." << std::endl;
this->Fini();
return;
}
if (_active)
{
// Set visible
this->dataPtr->applyWrenchVisual->SetMode(
static_cast<rendering::ApplyWrenchVisual::Mode>(this->GetMode()));
// Set selected
event::Events::setSelectedEntity(this->dataPtr->linkName, "normal");
// Set arrow mode
if (g_arrowAct)
g_arrowAct->trigger();
MouseEventHandler::Instance()->AddPressFilter(
"dialog_"+this->dataPtr->applyWrenchVisual->GetName(),
boost::bind(&ApplyWrenchDialog::OnMousePress, this, _1));
MouseEventHandler::Instance()->AddMoveFilter(
"dialog_"+this->dataPtr->applyWrenchVisual->GetName(),
boost::bind(&ApplyWrenchDialog::OnMouseMove, this, _1));
}
else
{
this->dataPtr->applyWrenchVisual->SetMode(
rendering::ApplyWrenchVisual::Mode::NONE);
MouseEventHandler::Instance()->RemovePressFilter(
"dialog_"+this->dataPtr->applyWrenchVisual->GetName());
MouseEventHandler::Instance()->RemoveMoveFilter(
"dialog_"+this->dataPtr->applyWrenchVisual->GetName());
}
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::OnManipulation()
{
this->SetActive(false);
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::ActivateWindow()
{
if (!this->isActiveWindow())
{
// Clear focus before activating not to trigger FucusIn
QWidget *focusedWidget = this->focusWidget();
if (focusedWidget)
focusedWidget->clearFocus();
this->activateWindow();
}
}
/////////////////////////////////////////////////
bool ApplyWrenchDialog::eventFilter(QObject *_object, QEvent *_event)
{
// Attach rotation tool to focused mode
if (_event->type() == QEvent::FocusIn)
{
if (_object == this->dataPtr->forceMagSpin ||
_object == this->dataPtr->forceXSpin ||
_object == this->dataPtr->forceYSpin ||
_object == this->dataPtr->forceZSpin ||
_object == this->dataPtr->forcePosXSpin ||
_object == this->dataPtr->forcePosYSpin ||
_object == this->dataPtr->forcePosZSpin ||
(_object == this->dataPtr->linksComboBox &&
this->dataPtr->mode != Mode::TORQUE))
{
this->SetForce(this->dataPtr->forceVector);
}
else if (_object == this->dataPtr->torqueMagSpin ||
_object == this->dataPtr->torqueXSpin ||
_object == this->dataPtr->torqueYSpin ||
_object == this->dataPtr->torqueZSpin ||
(_object == this->dataPtr->linksComboBox &&
this->dataPtr->mode == Mode::TORQUE))
{
this->SetTorque(this->dataPtr->torqueVector);
}
}
// Deactivate this when another dialog is focused
else if (_event->type() == QEvent::ActivationChange)
{
if (!this->dataPtr->mainWindow)
return false;
if (_object == this->dataPtr->mainWindow)
{
if (!this->isActiveWindow() &&
!this->dataPtr->mainWindow->isActiveWindow())
{
this->SetActive(false);
}
}
}
// Activate when changing spinboxes with mousewheel
else if (_event->type() == QEvent::Wheel)
{
this->ActivateWindow();
}
return false;
}
/////////////////////////////////////////////////
void ApplyWrenchDialog::changeEvent(QEvent *_event)
{
// Focus in this dialog
if (_event->type() == QEvent::ActivationChange)
{
// During tests it seems not to find main window, so this is true by default
bool mainWindowActive = true;
if (!this->dataPtr->mainWindow || (this->dataPtr->mainWindow &&
!this->dataPtr->mainWindow->isActiveWindow()))
{
mainWindowActive = false;
}
this->SetActive(this->isActiveWindow() || mainWindowActive);
}
}