1865 lines
53 KiB
C++
1865 lines
53 KiB
C++
/*
|
|
* Copyright (C) 2014 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.
|
|
*
|
|
*/
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "gazebo/common/MouseEvent.hh"
|
|
|
|
#include "gazebo/rendering/ogre_gazebo.h"
|
|
#include "gazebo/rendering/DynamicLines.hh"
|
|
#include "gazebo/rendering/Visual.hh"
|
|
#include "gazebo/rendering/JointVisual.hh"
|
|
#include "gazebo/rendering/UserCamera.hh"
|
|
#include "gazebo/rendering/Scene.hh"
|
|
#include "gazebo/rendering/RenderTypes.hh"
|
|
|
|
#include "gazebo/gui/GuiIface.hh"
|
|
#include "gazebo/gui/KeyEventHandler.hh"
|
|
#include "gazebo/gui/MouseEventHandler.hh"
|
|
#include "gazebo/gui/GuiEvents.hh"
|
|
#include "gazebo/gui/MainWindow.hh"
|
|
#include "gazebo/gui/ModelAlign.hh"
|
|
|
|
#include "gazebo/gui/model/JointCreationDialog.hh"
|
|
#include "gazebo/gui/model/JointInspector.hh"
|
|
#include "gazebo/gui/model/ModelEditorEvents.hh"
|
|
#include "gazebo/gui/model/JointMaker.hh"
|
|
#include "gazebo/gui/model/JointMakerPrivate.hh"
|
|
|
|
using namespace gazebo;
|
|
using namespace gui;
|
|
|
|
std::map<JointMaker::JointType, std::string> JointMaker::jointTypes;
|
|
std::map<JointMaker::JointType, std::string> JointMaker::jointMaterials;
|
|
std::vector<ignition::math::Vector3d> JointMaker::unitVectors;
|
|
|
|
/////////////////////////////////////////////////
|
|
JointMaker::JointMaker() : dataPtr(new JointMakerPrivate())
|
|
{
|
|
this->unitVectors.push_back(ignition::math::Vector3d::UnitX);
|
|
this->unitVectors.push_back(ignition::math::Vector3d::UnitY);
|
|
this->unitVectors.push_back(ignition::math::Vector3d::UnitZ);
|
|
|
|
this->dataPtr->newJoint = NULL;
|
|
this->dataPtr->modelSDF.reset();
|
|
this->dataPtr->jointType = JointMaker::JOINT_NONE;
|
|
this->dataPtr->jointCounter = 0;
|
|
|
|
this->jointMaterials[JOINT_FIXED] = "Gazebo/Red";
|
|
this->jointMaterials[JOINT_HINGE] = "Gazebo/Orange";
|
|
this->jointMaterials[JOINT_HINGE2] = "Gazebo/DarkYellow";
|
|
this->jointMaterials[JOINT_SLIDER] = "Gazebo/Green";
|
|
this->jointMaterials[JOINT_SCREW] = "Gazebo/DarkGrey";
|
|
this->jointMaterials[JOINT_UNIVERSAL] = "Gazebo/Blue";
|
|
this->jointMaterials[JOINT_BALL] = "Gazebo/Purple";
|
|
this->jointMaterials[JOINT_GEARBOX] = "Gazebo/Indigo";
|
|
|
|
this->jointTypes[JOINT_FIXED] = "fixed";
|
|
this->jointTypes[JOINT_HINGE] = "revolute";
|
|
this->jointTypes[JOINT_HINGE2] = "revolute2";
|
|
this->jointTypes[JOINT_SLIDER] = "prismatic";
|
|
this->jointTypes[JOINT_SCREW] = "screw";
|
|
this->jointTypes[JOINT_UNIVERSAL] = "universal";
|
|
this->jointTypes[JOINT_BALL] = "ball";
|
|
this->jointTypes[JOINT_GEARBOX] = "gearbox";
|
|
this->jointTypes[JOINT_NONE] = "none";
|
|
|
|
this->dataPtr->jointCreationDialog = NULL;
|
|
|
|
this->dataPtr->connections.push_back(
|
|
event::Events::ConnectPreRender(
|
|
std::bind(&JointMaker::Update, this)));
|
|
|
|
this->dataPtr->connections.push_back(
|
|
gui::model::Events::ConnectOpenJointInspector(
|
|
std::bind(&JointMaker::OpenInspector, this, std::placeholders::_1)));
|
|
|
|
this->dataPtr->connections.push_back(
|
|
gui::model::Events::ConnectShowJointContextMenu(
|
|
std::bind(&JointMaker::ShowContextMenu, this, std::placeholders::_1)));
|
|
|
|
this->dataPtr->connections.push_back(
|
|
gui::model::Events::ConnectSetSelectedJoint(
|
|
std::bind(&JointMaker::OnSetSelectedJoint, this, std::placeholders::_1,
|
|
std::placeholders::_2)));
|
|
|
|
this->dataPtr->connections.push_back(
|
|
event::Events::ConnectSetSelectedEntity(
|
|
std::bind(&JointMaker::OnSetSelectedEntity, this, std::placeholders::_1,
|
|
std::placeholders::_2)));
|
|
|
|
this->dataPtr->inspectAct = new QAction(tr("Open Joint Inspector"), this);
|
|
connect(this->dataPtr->inspectAct, SIGNAL(triggered()), this,
|
|
SLOT(OnOpenInspector()));
|
|
|
|
// Gazebo event connections
|
|
this->dataPtr->connections.push_back(
|
|
gui::model::Events::ConnectLinkInserted(
|
|
std::bind(&JointMaker::OnLinkInserted, this, std::placeholders::_1)));
|
|
|
|
this->dataPtr->connections.push_back(
|
|
gui::model::Events::ConnectLinkRemoved(
|
|
std::bind(&JointMaker::OnLinkRemoved, this, std::placeholders::_1)));
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
JointMaker::~JointMaker()
|
|
{
|
|
if (this->dataPtr->newJoint)
|
|
{
|
|
delete this->dataPtr->newJoint;
|
|
this->dataPtr->newJoint = NULL;
|
|
}
|
|
|
|
{
|
|
std::lock_guard<std::recursive_mutex> lock(this->dataPtr->updateMutex);
|
|
while (this->dataPtr->joints.size() > 0)
|
|
{
|
|
std::string jointName = this->dataPtr->joints.begin()->first;
|
|
this->RemoveJoint(jointName);
|
|
}
|
|
this->dataPtr->joints.clear();
|
|
}
|
|
|
|
if (this->dataPtr->jointCreationDialog)
|
|
delete this->dataPtr->jointCreationDialog;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::Reset()
|
|
{
|
|
std::lock_guard<std::recursive_mutex> lock(this->dataPtr->updateMutex);
|
|
if (this->dataPtr->newJoint)
|
|
{
|
|
delete this->dataPtr->newJoint;
|
|
this->dataPtr->newJoint = NULL;
|
|
}
|
|
|
|
this->dataPtr->jointType = JointMaker::JOINT_NONE;
|
|
this->dataPtr->hoverVis.reset();
|
|
this->dataPtr->inspectName = "";
|
|
this->dataPtr->selectedJoints.clear();
|
|
|
|
this->dataPtr->scopedLinkedNames.clear();
|
|
|
|
while (!this->dataPtr->joints.empty())
|
|
{
|
|
std::string jointId = this->dataPtr->joints.begin()->first;
|
|
this->RemoveJoint(jointId);
|
|
}
|
|
this->dataPtr->joints.clear();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::EnableEventHandlers()
|
|
{
|
|
MouseEventHandler::Instance()->AddDoubleClickFilter("model_joint",
|
|
std::bind(&JointMaker::OnMouseDoubleClick, this, std::placeholders::_1));
|
|
|
|
MouseEventHandler::Instance()->AddReleaseFilter("model_joint",
|
|
std::bind(&JointMaker::OnMouseRelease, this, std::placeholders::_1));
|
|
|
|
MouseEventHandler::Instance()->AddPressFilter("model_joint",
|
|
std::bind(&JointMaker::OnMousePress, this, std::placeholders::_1));
|
|
|
|
MouseEventHandler::Instance()->AddMoveFilter("model_joint",
|
|
std::bind(&JointMaker::OnMouseMove, this, std::placeholders::_1));
|
|
|
|
KeyEventHandler::Instance()->AddPressFilter("model_joint",
|
|
std::bind(&JointMaker::OnKeyPress, this, std::placeholders::_1));
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::DisableEventHandlers()
|
|
{
|
|
MouseEventHandler::Instance()->RemoveDoubleClickFilter("model_joint");
|
|
MouseEventHandler::Instance()->RemoveReleaseFilter("model_joint");
|
|
MouseEventHandler::Instance()->RemovePressFilter("model_joint");
|
|
MouseEventHandler::Instance()->RemoveMoveFilter("model_joint");
|
|
KeyEventHandler::Instance()->RemovePressFilter("model_joint");
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::RemoveJoint(const std::string &_jointId)
|
|
{
|
|
std::lock_guard<std::recursive_mutex> lock(this->dataPtr->updateMutex);
|
|
|
|
std::string jointId = _jointId;
|
|
JointData *joint = NULL;
|
|
|
|
auto jointIt = this->dataPtr->joints.find(_jointId);
|
|
|
|
// Existing joint
|
|
if (jointIt != this->dataPtr->joints.end())
|
|
{
|
|
joint = jointIt->second;
|
|
}
|
|
// Joint being created
|
|
else if (this->dataPtr->newJoint)
|
|
{
|
|
joint = this->dataPtr->newJoint;
|
|
// Already has hotspot
|
|
if (joint->hotspot)
|
|
jointId = joint->hotspot->GetName();
|
|
// Still only line
|
|
else
|
|
jointId = "";
|
|
}
|
|
|
|
if (!joint)
|
|
return;
|
|
|
|
if (jointId != "")
|
|
{
|
|
this->dataPtr->joints.erase(jointId);
|
|
gui::model::Events::jointRemoved(jointId);
|
|
}
|
|
|
|
auto scene = rendering::get_scene();
|
|
if (!scene)
|
|
return;
|
|
|
|
if (joint->handles)
|
|
{
|
|
scene->OgreSceneManager()->destroyBillboardSet(joint->handles);
|
|
joint->handles = NULL;
|
|
}
|
|
|
|
if (joint->hotspot)
|
|
{
|
|
scene->RemoveVisual(joint->hotspot);
|
|
joint->hotspot->Fini();
|
|
}
|
|
|
|
if (joint->visual)
|
|
{
|
|
scene->RemoveVisual(joint->visual);
|
|
joint->visual->Fini();
|
|
}
|
|
|
|
if (joint->jointVisual)
|
|
{
|
|
auto parentAxisVis = joint->jointVisual->GetParentAxisVisual();
|
|
if (parentAxisVis)
|
|
{
|
|
parentAxisVis->GetParent()->DetachVisual(parentAxisVis->GetName());
|
|
scene->RemoveVisual(parentAxisVis);
|
|
}
|
|
joint->jointVisual->GetParent()->DetachVisual(
|
|
joint->jointVisual->GetName());
|
|
scene->RemoveVisual(joint->jointVisual);
|
|
}
|
|
|
|
if (joint->inspector)
|
|
{
|
|
joint->inspector->hide();
|
|
delete joint->inspector;
|
|
joint->inspector = NULL;
|
|
}
|
|
|
|
this->dataPtr->newJoint = NULL;
|
|
joint->hotspot.reset();
|
|
joint->visual.reset();
|
|
joint->jointVisual.reset();
|
|
joint->parent.reset();
|
|
joint->child.reset();
|
|
delete joint->inspector;
|
|
delete joint;
|
|
gui::model::Events::modelChanged();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::RemoveJointsByLink(const std::string &_linkName)
|
|
{
|
|
std::vector<std::string> toDelete;
|
|
for (auto it : this->dataPtr->joints)
|
|
{
|
|
JointData *joint = it.second;
|
|
|
|
if (joint->child->GetName() == _linkName ||
|
|
joint->parent->GetName() == _linkName)
|
|
{
|
|
toDelete.push_back(it.first);
|
|
}
|
|
}
|
|
|
|
for (unsigned i = 0; i < toDelete.size(); ++i)
|
|
this->RemoveJoint(toDelete[i]);
|
|
|
|
toDelete.clear();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
std::vector<JointData *> JointMaker::JointDataByLink(
|
|
const std::string &_linkName) const
|
|
{
|
|
std::vector<JointData *> linkJoints;
|
|
for (auto jointIt : this->dataPtr->joints)
|
|
{
|
|
JointData *jointData = jointIt.second;
|
|
|
|
if (jointData->child->GetName() == _linkName ||
|
|
jointData->parent->GetName() == _linkName)
|
|
{
|
|
linkJoints.push_back(jointData);
|
|
}
|
|
}
|
|
return linkJoints;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
bool JointMaker::OnMousePress(const common::MouseEvent &_event)
|
|
{
|
|
rendering::UserCameraPtr camera = gui::get_active_camera();
|
|
if (_event.Button() == common::MouseEvent::MIDDLE)
|
|
{
|
|
QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
|
|
camera->HandleMouseEvent(_event);
|
|
return true;
|
|
}
|
|
else if (_event.Button() != common::MouseEvent::LEFT)
|
|
return false;
|
|
|
|
if (this->dataPtr->jointType != JointMaker::JOINT_NONE)
|
|
return false;
|
|
|
|
// intercept mouse press events when user clicks on the joint hotspot visual
|
|
rendering::VisualPtr vis = camera->GetVisual(_event.Pos());
|
|
if (vis)
|
|
{
|
|
if (this->dataPtr->joints.find(vis->GetName()) !=
|
|
this->dataPtr->joints.end())
|
|
{
|
|
// stop event propagation as we don't want users to manipulate the
|
|
// hotspot
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
bool JointMaker::OnMouseRelease(const common::MouseEvent &_event)
|
|
{
|
|
rendering::UserCameraPtr camera = gui::get_active_camera();
|
|
|
|
// Not in the process of selecting joint links with mouse
|
|
// Handle joint selection
|
|
if (this->dataPtr->jointType == JointMaker::JOINT_NONE ||
|
|
(this->dataPtr->newJoint && this->dataPtr->newJoint->parent &&
|
|
this->dataPtr->newJoint->child))
|
|
{
|
|
rendering::VisualPtr vis = camera->GetVisual(_event.Pos());
|
|
if (vis)
|
|
{
|
|
if (this->dataPtr->joints.find(vis->GetName()) !=
|
|
this->dataPtr->joints.end())
|
|
{
|
|
// trigger joint inspector on right click
|
|
if (_event.Button() == common::MouseEvent::RIGHT)
|
|
{
|
|
this->dataPtr->inspectName = vis->GetName();
|
|
this->ShowContextMenu(this->dataPtr->inspectName);
|
|
return true;
|
|
}
|
|
else if (_event.Button() == common::MouseEvent::LEFT)
|
|
{
|
|
// Not in multi-selection mode.
|
|
if (!(QApplication::keyboardModifiers() & Qt::ControlModifier))
|
|
{
|
|
this->DeselectAll();
|
|
this->SetSelected(vis, true);
|
|
}
|
|
// Multi-selection mode
|
|
else
|
|
{
|
|
auto it = std::find(this->dataPtr->selectedJoints.begin(),
|
|
this->dataPtr->selectedJoints.end(), vis);
|
|
// Highlight and select clicked joint if not already selected
|
|
// Otherwise deselect if already selected
|
|
this->SetSelected(vis, it == this->dataPtr->selectedJoints.end());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
this->DeselectAll();
|
|
return false;
|
|
}
|
|
}
|
|
// Still selecting parent/child during new joint creation
|
|
else
|
|
{
|
|
if (_event.Button() == common::MouseEvent::LEFT)
|
|
{
|
|
if (this->dataPtr->hoverVis)
|
|
{
|
|
if (this->dataPtr->hoverVis->IsPlane())
|
|
{
|
|
QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
|
|
camera->HandleMouseEvent(_event);
|
|
return true;
|
|
}
|
|
|
|
// Pressed parent link
|
|
if (!this->dataPtr->newJoint)
|
|
{
|
|
if (!this->SetParentLink(this->dataPtr->hoverVis))
|
|
return false;
|
|
|
|
if (this->dataPtr->jointCreationDialog)
|
|
{
|
|
this->dataPtr->jointCreationDialog->SetParent(
|
|
this->dataPtr->newJoint->parent->GetName());
|
|
}
|
|
}
|
|
// Pressed child link
|
|
else if (this->dataPtr->newJoint &&
|
|
this->dataPtr->newJoint->parent != this->dataPtr->hoverVis)
|
|
{
|
|
if (!this->SetChildLink(this->dataPtr->hoverVis))
|
|
return false;
|
|
|
|
if (this->dataPtr->jointCreationDialog)
|
|
{
|
|
this->dataPtr->jointCreationDialog->SetChild(
|
|
this->dataPtr->newJoint->child->GetName());
|
|
}
|
|
}
|
|
|
|
if (this->dataPtr->hoverVis)
|
|
{
|
|
this->dataPtr->hoverVis->SetEmissive(common::Color(0, 0, 0));
|
|
this->dataPtr->hoverVis.reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
|
|
camera->HandleMouseEvent(_event);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
JointData *JointMaker::CreateJointLine(const std::string &_name,
|
|
const rendering::VisualPtr &_parent)
|
|
{
|
|
rendering::VisualPtr jointVis(
|
|
new rendering::Visual(_name, _parent->GetParent(), false));
|
|
jointVis->Load();
|
|
rendering::DynamicLines *jointLine =
|
|
jointVis->CreateDynamicLine(rendering::RENDERING_LINE_LIST);
|
|
auto origin = _parent->GetWorldPose().Ign().Pos()
|
|
- _parent->GetParent()->GetWorldPose().Ign().Pos();
|
|
jointLine->AddPoint(origin);
|
|
jointLine->AddPoint(origin + ignition::math::Vector3d(0, 0, 0.1));
|
|
jointVis->GetSceneNode()->setInheritScale(false);
|
|
jointVis->GetSceneNode()->setInheritOrientation(false);
|
|
|
|
std::string jointVisName = jointVis->GetName();
|
|
std::string leafName = jointVisName;
|
|
size_t pIdx = jointVisName.rfind("::");
|
|
if (pIdx != std::string::npos)
|
|
leafName = jointVisName.substr(pIdx+2);
|
|
|
|
JointData *jointData = new JointData();
|
|
jointData->dirty = false;
|
|
jointData->name = leafName;
|
|
jointData->visual = jointVis;
|
|
jointData->parent = _parent;
|
|
jointData->line = jointLine;
|
|
jointData->type = this->dataPtr->jointType;
|
|
jointData->line->setMaterial(this->jointMaterials[jointData->type]);
|
|
|
|
return jointData;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
JointData *JointMaker::CreateJoint(const rendering::VisualPtr &_parent,
|
|
const rendering::VisualPtr &_child)
|
|
{
|
|
std::stringstream ss;
|
|
ss << _parent->GetName() << "_JOINT_" << this->dataPtr->jointCounter++;
|
|
|
|
JointData *jointData = this->CreateJointLine(ss.str(), _parent);
|
|
jointData->child = _child;
|
|
|
|
// Inspector
|
|
jointData->inspector = new JointInspector(this);
|
|
jointData->inspector->setModal(false);
|
|
connect(jointData->inspector, SIGNAL(Applied()), jointData, SLOT(OnApply()));
|
|
|
|
MainWindow *mainWindow = gui::get_main_window();
|
|
if (mainWindow)
|
|
{
|
|
connect(gui::get_main_window(), SIGNAL(Close()), jointData->inspector,
|
|
SLOT(close()));
|
|
}
|
|
|
|
// setup the joint msg
|
|
jointData->UpdateMsg();
|
|
|
|
jointData->inspector->Update(jointData->jointMsg);
|
|
return jointData;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
JointMaker::JointType JointMaker::ConvertJointType(const std::string &_type)
|
|
{
|
|
for (auto iter : jointTypes)
|
|
if (iter.second == _type)
|
|
return iter.first;
|
|
|
|
return JOINT_NONE;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
std::string JointMaker::JointMaterial(const std::string &_type)
|
|
{
|
|
auto it = jointMaterials.find(ConvertJointType(_type));
|
|
if (it != jointMaterials.end())
|
|
return it->second;
|
|
else
|
|
return "";
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::AddJoint(const std::string &_type)
|
|
{
|
|
this->AddJoint(this->ConvertJointType(_type));
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::AddJoint(const JointMaker::JointType _type)
|
|
{
|
|
this->dataPtr->jointType = _type;
|
|
// Start joint creation
|
|
if (_type != JointMaker::JOINT_NONE)
|
|
{
|
|
if (!this->dataPtr->jointCreationDialog)
|
|
{
|
|
auto mainWindow = gui::get_main_window();
|
|
this->dataPtr->jointCreationDialog =
|
|
new JointCreationDialog(this, mainWindow);
|
|
}
|
|
this->dataPtr->jointCreationDialog->Open(_type);
|
|
}
|
|
// End joint creation
|
|
else
|
|
{
|
|
// signal the end of a joint action.
|
|
emit JointAdded();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::Stop()
|
|
{
|
|
// Reset links
|
|
if (this->dataPtr->newJoint)
|
|
{
|
|
if (this->dataPtr->newJoint->parent)
|
|
{
|
|
this->dataPtr->newJoint->parent->SetWorldPose(
|
|
this->dataPtr->parentLinkOriginalPose);
|
|
this->SetVisualMoved(this->dataPtr->newJoint->parent, false);
|
|
}
|
|
if (this->dataPtr->newJoint->child)
|
|
{
|
|
this->dataPtr->newJoint->child->SetWorldPose(
|
|
this->dataPtr->childLinkOriginalPose);
|
|
this->SetVisualMoved(this->dataPtr->newJoint->child, false);
|
|
}
|
|
}
|
|
|
|
this->RemoveJoint("");
|
|
|
|
this->AddJoint(JointMaker::JOINT_NONE);
|
|
if (this->dataPtr->hoverVis)
|
|
this->dataPtr->hoverVis->SetEmissive(common::Color(0, 0, 0));
|
|
this->dataPtr->hoverVis.reset();
|
|
|
|
if (this->dataPtr->jointCreationDialog &&
|
|
this->dataPtr->jointCreationDialog->isVisible())
|
|
{
|
|
this->dataPtr->jointCreationDialog->reject();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
bool JointMaker::OnMouseMove(const common::MouseEvent &_event)
|
|
{
|
|
// Only handle mouse move events during joint creation
|
|
if (this->dataPtr->jointType == JointMaker::JOINT_NONE ||
|
|
(this->dataPtr->newJoint && this->dataPtr->newJoint->child))
|
|
{
|
|
return false;
|
|
}
|
|
QApplication::setOverrideCursor(Qt::CrossCursor);
|
|
|
|
// Get the active camera and scene.
|
|
rendering::UserCameraPtr camera = gui::get_active_camera();
|
|
|
|
if (_event.Dragging())
|
|
{
|
|
// this enables the joint maker to pan while connecting joints
|
|
QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
|
|
camera->HandleMouseEvent(_event);
|
|
return true;
|
|
}
|
|
|
|
rendering::VisualPtr vis = camera->GetVisual(_event.Pos());
|
|
|
|
// Highlight visual on hover
|
|
if (vis)
|
|
{
|
|
if (this->dataPtr->hoverVis)
|
|
this->dataPtr->hoverVis->SetEmissive(common::Color(0.0, 0.0, 0.0));
|
|
|
|
// only highlight editor links by making sure it's not an item in the
|
|
// gui tree widget or a joint hotspot.
|
|
rendering::VisualPtr rootVis = vis->GetRootVisual();
|
|
if (rootVis->IsPlane())
|
|
this->dataPtr->hoverVis = vis->GetParent();
|
|
else if (!gui::get_entity_id(rootVis->GetName()) &&
|
|
vis->GetName().find("_UNIQUE_ID_") == std::string::npos)
|
|
{
|
|
this->dataPtr->hoverVis = vis->GetParent();
|
|
if (!this->dataPtr->newJoint || (this->dataPtr->newJoint->parent &&
|
|
this->dataPtr->hoverVis != this->dataPtr->newJoint->parent))
|
|
{
|
|
this->dataPtr->hoverVis->SetEmissive(common::Color(0.5, 0.5, 0.5));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Case when a parent link is already selected and currently
|
|
// extending the joint line to a child link
|
|
if (this->dataPtr->newJoint && this->dataPtr->newJoint->parent &&
|
|
this->dataPtr->hoverVis && this->dataPtr->newJoint->line)
|
|
{
|
|
ignition::math::Vector3d parentPos;
|
|
|
|
// Set end point to center of child link
|
|
if (!this->dataPtr->hoverVis->IsPlane())
|
|
{
|
|
parentPos = this->LinkWorldCentroid(this->dataPtr->newJoint->parent)
|
|
- this->dataPtr->newJoint->line->Point(0);
|
|
}
|
|
// Set end point to mouse plane intersection
|
|
else
|
|
{
|
|
ignition::math::Vector3d pt;
|
|
camera->WorldPointOnPlane(_event.Pos().X(), _event.Pos().Y(),
|
|
ignition::math::Planed(ignition::math::Vector3d(0, 0, 1)), pt);
|
|
|
|
parentPos = this->LinkWorldCentroid(this->dataPtr->newJoint->parent)
|
|
- this->dataPtr->newJoint->line->Point(0) - pt;
|
|
}
|
|
this->dataPtr->newJoint->line->SetPoint(1,
|
|
(this->LinkWorldCentroid(this->dataPtr->hoverVis) - parentPos));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::OnOpenInspector()
|
|
{
|
|
if (this->dataPtr->inspectName.empty())
|
|
return;
|
|
|
|
this->OpenInspector(this->dataPtr->inspectName);
|
|
this->dataPtr->inspectName = "";
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::OpenInspector(const std::string &_jointId)
|
|
{
|
|
JointData *joint = this->dataPtr->joints[_jointId];
|
|
if (!joint)
|
|
{
|
|
gzerr << "Joint [" << _jointId << "] not found." << std::endl;
|
|
return;
|
|
}
|
|
joint->OpenInspector();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
bool JointMaker::OnMouseDoubleClick(const common::MouseEvent &_event)
|
|
{
|
|
rendering::UserCameraPtr camera = gui::get_active_camera();
|
|
rendering::VisualPtr vis = camera->GetVisual(_event.Pos());
|
|
|
|
if (vis)
|
|
{
|
|
if (this->dataPtr->joints.find(vis->GetName()) !=
|
|
this->dataPtr->joints.end())
|
|
{
|
|
this->OpenInspector(vis->GetName());
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
bool JointMaker::OnKeyPress(const common::KeyEvent &_event)
|
|
{
|
|
if (_event.key == Qt::Key_Delete)
|
|
{
|
|
if (!this->dataPtr->selectedJoints.empty())
|
|
{
|
|
for (auto jointVis : this->dataPtr->selectedJoints)
|
|
{
|
|
this->RemoveJoint(jointVis->GetName());
|
|
}
|
|
this->DeselectAll();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::OnDelete()
|
|
{
|
|
if (this->dataPtr->inspectName.empty())
|
|
return;
|
|
|
|
this->RemoveJoint(this->dataPtr->inspectName);
|
|
this->dataPtr->inspectName = "";
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
std::string JointMaker::CreateHotSpot(JointData *_joint)
|
|
{
|
|
if (!_joint)
|
|
return "";
|
|
|
|
rendering::UserCameraPtr camera = gui::get_active_camera();
|
|
|
|
// Joint hotspot visual name is the JointId for easy access when clicking
|
|
std::string jointId = _joint->visual->GetName() + "_UNIQUE_ID_";
|
|
rendering::VisualPtr hotspotVisual(
|
|
new rendering::Visual(jointId, _joint->visual, false));
|
|
hotspotVisual->Load();
|
|
|
|
// create a cylinder to represent the joint
|
|
hotspotVisual->InsertMesh("unit_cylinder");
|
|
Ogre::MovableObject *hotspotObj =
|
|
(Ogre::MovableObject*)(
|
|
camera->GetScene()->OgreSceneManager()->createEntity(
|
|
_joint->visual->GetName(), "unit_cylinder"));
|
|
hotspotObj->getUserObjectBindings().setUserAny(Ogre::Any(jointId));
|
|
hotspotVisual->GetSceneNode()->attachObject(hotspotObj);
|
|
hotspotVisual->SetMaterial(this->jointMaterials[_joint->type]);
|
|
hotspotVisual->SetTransparency(0.7);
|
|
|
|
// create a handle at the parent end
|
|
Ogre::BillboardSet *handleSet =
|
|
camera->GetScene()->OgreSceneManager()->createBillboardSet(1);
|
|
handleSet->setAutoUpdate(true);
|
|
handleSet->setMaterialName("Gazebo/PointHandle");
|
|
Ogre::MaterialPtr mat =
|
|
Ogre::MaterialManager::getSingleton().getByName(
|
|
this->jointMaterials[_joint->type]);
|
|
Ogre::ColourValue color = mat->getTechnique(0)->getPass(0)->getDiffuse();
|
|
color.a = 0.5;
|
|
|
|
double linkSize = std::min(0.1,
|
|
_joint->parent->GetBoundingBox().GetSize().GetLength()*0.05);
|
|
linkSize = std::max(linkSize, 0.01);
|
|
|
|
double dimension = linkSize;
|
|
handleSet->setDefaultDimensions(dimension, dimension);
|
|
Ogre::Billboard *parentHandle = handleSet->createBillboard(0, 0, 0);
|
|
parentHandle->setColour(color);
|
|
Ogre::SceneNode *handleNode =
|
|
hotspotVisual->GetSceneNode()->createChildSceneNode();
|
|
handleNode->attachObject(handleSet);
|
|
handleNode->setInheritScale(false);
|
|
handleNode->setInheritOrientation(false);
|
|
_joint->handles = handleSet;
|
|
|
|
hotspotVisual->SetVisibilityFlags(GZ_VISIBILITY_GUI |
|
|
GZ_VISIBILITY_SELECTABLE);
|
|
hotspotVisual->GetSceneNode()->setInheritScale(false);
|
|
|
|
this->dataPtr->joints[jointId] = _joint;
|
|
|
|
_joint->hotspot = hotspotVisual;
|
|
_joint->inspector->SetJointId(_joint->hotspot->GetName());
|
|
|
|
_joint->dirty = true;
|
|
|
|
return jointId;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::Update()
|
|
{
|
|
std::lock_guard<std::recursive_mutex> lock(this->dataPtr->updateMutex);
|
|
// Update each joint
|
|
for (auto it : this->dataPtr->joints)
|
|
{
|
|
JointData *joint = it.second;
|
|
if (joint->hotspot)
|
|
{
|
|
if (joint->child && joint->parent)
|
|
{
|
|
bool poseUpdate = false;
|
|
if (joint->parentPose != joint->parent->GetWorldPose().Ign() ||
|
|
joint->childPose != joint->child->GetWorldPose().Ign() ||
|
|
joint->childScale != joint->child->GetScale().Ign())
|
|
{
|
|
joint->parentPose = joint->parent->GetWorldPose().Ign();
|
|
joint->childPose = joint->child->GetWorldPose().Ign();
|
|
joint->childScale = joint->child->GetScale().Ign();
|
|
poseUpdate = true;
|
|
|
|
// Highlight links connected to joint being created if they have
|
|
// been moved to another position
|
|
if (joint == this->dataPtr->newJoint)
|
|
{
|
|
// Parent
|
|
this->SetVisualMoved(joint->parent,
|
|
joint->parent->GetWorldPose().Ign() !=
|
|
this->dataPtr->parentLinkOriginalPose);
|
|
|
|
// Child
|
|
this->SetVisualMoved(joint->child,
|
|
joint->child->GetWorldPose().Ign() !=
|
|
this->dataPtr->childLinkOriginalPose);
|
|
}
|
|
}
|
|
|
|
// Create / update joint visual
|
|
if (joint->dirty || poseUpdate)
|
|
{
|
|
joint->Update();
|
|
|
|
if (joint == this->dataPtr->newJoint &&
|
|
this->dataPtr->newJoint->parent && this->dataPtr->newJoint->child
|
|
&& this->dataPtr->jointCreationDialog &&
|
|
this->dataPtr->jointCreationDialog->isVisible())
|
|
{
|
|
// Get poses as homogeneous transforms
|
|
ignition::math::Matrix4d parentWorld(
|
|
this->dataPtr->newJoint->parent->GetWorldPose().Ign());
|
|
ignition::math::Matrix4d childWorld(
|
|
this->dataPtr->newJoint->child->GetWorldPose().Ign());
|
|
|
|
// w_T_c = w_T_p * p_T_c
|
|
// w_T_p^-1 * w_T_c = p_T_c
|
|
ignition::math::Matrix4d childParent = parentWorld.Inverse() *
|
|
childWorld;
|
|
|
|
this->dataPtr->jointCreationDialog->UpdateRelativePose(
|
|
childParent.Pose());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::AddScopedLinkName(const std::string &_name)
|
|
{
|
|
this->dataPtr->scopedLinkedNames.push_back(_name);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
std::string JointMaker::ScopedLinkName(const std::string &_name)
|
|
{
|
|
for (unsigned int i = 0; i < this->dataPtr->scopedLinkedNames.size(); ++i)
|
|
{
|
|
std::string scopedName = this->dataPtr->scopedLinkedNames[i];
|
|
size_t idx = scopedName.find("::" + _name);
|
|
if (idx != std::string::npos)
|
|
return scopedName;
|
|
}
|
|
return _name;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::GenerateSDF()
|
|
{
|
|
this->dataPtr->modelSDF.reset(new sdf::Element);
|
|
sdf::initFile("model.sdf", this->dataPtr->modelSDF);
|
|
this->dataPtr->modelSDF->ClearElements();
|
|
|
|
// update joint visuals as the model pose may have changed when
|
|
// generating model sdf
|
|
for (auto jointsIt : this->dataPtr->joints)
|
|
{
|
|
JointData *joint = jointsIt.second;
|
|
joint->dirty = true;
|
|
this->Update();
|
|
}
|
|
|
|
// loop through all joints
|
|
for (auto jointsIt : this->dataPtr->joints)
|
|
{
|
|
JointData *joint = jointsIt.second;
|
|
sdf::ElementPtr jointElem = this->dataPtr->modelSDF->AddElement("joint");
|
|
|
|
msgs::JointPtr jointMsg = joint->jointMsg;
|
|
unsigned int axisCount = this->JointAxisCount(joint->type);
|
|
for (unsigned int i = axisCount; i < 2u; ++i)
|
|
{
|
|
if (i == 0u)
|
|
jointMsg->clear_axis1();
|
|
else if (i == 1u)
|
|
jointMsg->clear_axis2();
|
|
}
|
|
jointElem = msgs::JointToSDF(*jointMsg.get(), jointElem);
|
|
|
|
sdf::ElementPtr parentElem = jointElem->GetElement("parent");
|
|
std::string parentName = joint->parent->GetName();
|
|
size_t pIdx = parentName.find("::");
|
|
if (pIdx != std::string::npos)
|
|
parentName = parentName.substr(pIdx+2);
|
|
parentElem->Set(parentName);
|
|
|
|
sdf::ElementPtr childElem = jointElem->GetElement("child");
|
|
std::string childName = joint->child->GetName();
|
|
size_t cIdx = childName.find("::");
|
|
if (cIdx != std::string::npos)
|
|
childName = childName.substr(cIdx+2);
|
|
childElem->Set(childName);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
sdf::ElementPtr JointMaker::SDF() const
|
|
{
|
|
return this->dataPtr->modelSDF;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
std::string JointMaker::TypeAsString(const JointMaker::JointType _type)
|
|
{
|
|
std::string jointTypeStr = "";
|
|
|
|
auto iter = jointTypes.find(_type);
|
|
if (iter != jointTypes.end())
|
|
jointTypeStr = iter->second;
|
|
|
|
return jointTypeStr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
unsigned int JointMaker::JointAxisCount(const JointMaker::JointType _type)
|
|
{
|
|
if (_type == JOINT_FIXED)
|
|
{
|
|
return 0;
|
|
}
|
|
else if (_type == JOINT_HINGE)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (_type == JOINT_HINGE2)
|
|
{
|
|
return 2;
|
|
}
|
|
else if (_type == JOINT_SLIDER)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (_type == JOINT_SCREW)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (_type == JOINT_UNIVERSAL)
|
|
{
|
|
return 2;
|
|
}
|
|
else if (_type == JOINT_BALL)
|
|
{
|
|
return 0;
|
|
}
|
|
else if (_type == JOINT_GEARBOX)
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
JointMaker::JointType JointMaker::State() const
|
|
{
|
|
return this->dataPtr->jointType;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
ignition::math::Vector3d JointMaker::LinkWorldCentroid(
|
|
const rendering::VisualPtr &_visual)
|
|
{
|
|
ignition::math::Vector3d centroid;
|
|
int count = 0;
|
|
for (unsigned int i = 0; i < _visual->GetChildCount(); ++i)
|
|
{
|
|
if (_visual->GetChild(i)->GetName().find("_JOINT_VISUAL_") ==
|
|
std::string::npos)
|
|
{
|
|
centroid += _visual->GetChild(i)->GetWorldPose().Ign().Pos();
|
|
count++;
|
|
}
|
|
}
|
|
centroid /= count;
|
|
return centroid;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
unsigned int JointMaker::JointCount()
|
|
{
|
|
return this->dataPtr->joints.size();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointData::OnApply()
|
|
{
|
|
// Get data from inspector
|
|
msgs::Joint *inspectorMsg = this->inspector->Data();
|
|
if (!inspectorMsg)
|
|
return;
|
|
|
|
this->jointMsg->CopyFrom(*inspectorMsg);
|
|
|
|
// Name
|
|
if (this->name != this->jointMsg->name())
|
|
gui::model::Events::jointNameChanged(this->hotspot->GetName(),
|
|
this->jointMsg->name());
|
|
this->name = this->jointMsg->name();
|
|
|
|
// Type
|
|
this->type = JointMaker::ConvertJointType(
|
|
msgs::ConvertJointType(this->jointMsg->type()));
|
|
|
|
// Get scoped names
|
|
std::string parentOldName = this->parent->GetName();
|
|
std::string parentScope = parentOldName;
|
|
size_t parentIdx = parentOldName.find("::");
|
|
if (parentIdx != std::string::npos)
|
|
parentScope = parentOldName.substr(0, parentIdx+2);
|
|
std::string childOldName = this->child->GetName();
|
|
std::string childScope = childOldName;
|
|
size_t childIdx = childOldName.find("::");
|
|
if (childIdx != std::string::npos)
|
|
childScope = childOldName.substr(0, childIdx+2);
|
|
|
|
std::string parentName = parentScope + this->jointMsg->parent();
|
|
std::string childName = childScope + this->jointMsg->child();
|
|
|
|
// Parent
|
|
if (parentName != this->jointMsg->parent())
|
|
{
|
|
rendering::VisualPtr parentVis = gui::get_active_camera()->GetScene()
|
|
->GetVisual(parentName);
|
|
if (parentVis)
|
|
this->parent = parentVis;
|
|
else
|
|
gzwarn << "Invalid parent, keeping old parent" << std::endl;
|
|
}
|
|
|
|
// Child
|
|
if (childName != this->jointMsg->child())
|
|
{
|
|
rendering::VisualPtr childVis = gui::get_active_camera()->GetScene()
|
|
->GetVisual(childName);
|
|
if (childVis)
|
|
{
|
|
this->child = childVis;
|
|
if (this->jointVisual)
|
|
childVis->AttachVisual(this->jointVisual);
|
|
}
|
|
else
|
|
gzwarn << "Invalid child, keeping old child" << std::endl;
|
|
}
|
|
|
|
this->dirty = true;
|
|
gui::model::Events::modelChanged();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointData::OnOpenInspector()
|
|
{
|
|
this->OpenInspector();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointData::OpenInspector()
|
|
{
|
|
this->inspector->Update(this->jointMsg);
|
|
this->inspector->Open();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointData::Update()
|
|
{
|
|
// Material
|
|
std::string material = JointMaker::jointMaterials[this->type];
|
|
|
|
// Hotspot and parent handle
|
|
if (this->parent && this->child && this->hotspot && this->handles)
|
|
{
|
|
auto parentOrigin = this->parent->GetWorldPose().Ign().Pos();
|
|
auto childOrigin = this->child->GetWorldPose().Ign().Pos();
|
|
|
|
// Hotspot position
|
|
auto dPos = childOrigin - parentOrigin;
|
|
auto center = dPos * 0.5;
|
|
double length = std::max(dPos.Length(), 0.001);
|
|
this->hotspot->SetScale(ignition::math::Vector3d(0.008, 0.008, length));
|
|
this->hotspot->SetWorldPosition(parentOrigin + center);
|
|
|
|
// Hotspot orientation
|
|
auto u = dPos.Normalize();
|
|
auto v = ignition::math::Vector3d::UnitZ;
|
|
double cosTheta = v.Dot(u);
|
|
double angle = acos(cosTheta);
|
|
auto w = (v.Cross(u)).Normalize();
|
|
ignition::math::Quaterniond q;
|
|
q.Axis(w, angle);
|
|
this->hotspot->SetWorldRotation(q);
|
|
|
|
// Parent handle position
|
|
this->handles->getBillboard(0)->setPosition(
|
|
rendering::Conversions::Convert(parentOrigin -
|
|
this->hotspot->GetWorldPose().Ign().Pos()));
|
|
this->handles->_updateBounds();
|
|
|
|
// set new material if joint type has changed
|
|
if (this->hotspot->GetMaterialName() != material)
|
|
{
|
|
// Note: issue setting material when there is a billboard child,
|
|
// seems to hang so detach before setting and re-attach later.
|
|
Ogre::SceneNode *handleNode = this->handles->getParentSceneNode();
|
|
this->handles->detachFromParent();
|
|
this->hotspot->SetMaterial(material);
|
|
this->hotspot->SetTransparency(0.7);
|
|
handleNode->attachObject(this->handles);
|
|
Ogre::MaterialPtr mat =
|
|
Ogre::MaterialManager::getSingleton().getByName(material);
|
|
Ogre::ColourValue color = mat->getTechnique(0)->getPass(0)->getDiffuse();
|
|
color.a = 0.5;
|
|
this->handles->getBillboard(0)->setColour(color);
|
|
}
|
|
}
|
|
|
|
// Joint message and joint visual
|
|
if (this->jointMsg)
|
|
{
|
|
this->UpdateMsg();
|
|
|
|
// Update existing visual
|
|
if (this->jointVisual)
|
|
{
|
|
this->jointVisual->UpdateFromMsg(this->jointMsg);
|
|
}
|
|
// Create joint visual
|
|
else if (this->child)
|
|
{
|
|
std::string childName = this->child->GetName();
|
|
std::string jointVisName = childName;
|
|
size_t idx = childName.find("::");
|
|
if (idx != std::string::npos)
|
|
jointVisName = childName.substr(0, idx+2);
|
|
jointVisName += "_JOINT_VISUAL_";
|
|
|
|
gazebo::rendering::JointVisualPtr jointVis(
|
|
new gazebo::rendering::JointVisual(jointVisName, this->child));
|
|
jointVis->Load(this->jointMsg);
|
|
this->jointVisual = jointVis;
|
|
}
|
|
}
|
|
|
|
// Line
|
|
if (this->line)
|
|
{
|
|
this->line->setMaterial(material);
|
|
|
|
// Parent - child
|
|
if (this->child && this->jointVisual)
|
|
{
|
|
this->line->SetPoint(0, (this->child->GetWorldPose().pos
|
|
- this->child->GetParent()->GetWorldPose().pos).Ign());
|
|
this->line->SetPoint(1,
|
|
(this->jointVisual->GetWorldPose().pos
|
|
- this->child->GetParent()->GetWorldPose().pos).Ign());
|
|
}
|
|
// Parent - mouse
|
|
else if (this->parent && this->parent->GetParent())
|
|
{
|
|
ignition::math::Vector3d origin = (this->parent->GetWorldPose().pos
|
|
- this->parent->GetParent()->GetWorldPose().pos).Ign();
|
|
this->line->SetPoint(0, origin);
|
|
}
|
|
}
|
|
|
|
// Notify joint changes
|
|
if (this->parent && this->child && this->hotspot)
|
|
{
|
|
std::string parentName = this->parent->GetName();
|
|
std::string childName = this->child->GetName();
|
|
gui::model::Events::jointChanged(this->hotspot->GetName(), this->name,
|
|
JointMaker::jointTypes[this->type], parentName, childName);
|
|
}
|
|
|
|
this->dirty = false;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointData::UpdateMsg()
|
|
{
|
|
// Some values are only stored in the msg, so we keep those
|
|
msgs::JointPtr oldMsg(new msgs::Joint);
|
|
if (this->jointMsg)
|
|
{
|
|
oldMsg->CopyFrom(*this->jointMsg);
|
|
}
|
|
|
|
// Reset
|
|
this->jointMsg.reset(new msgs::Joint);
|
|
|
|
// Name
|
|
this->jointMsg->set_name(this->name);
|
|
|
|
// Parent
|
|
if (this->parent)
|
|
{
|
|
std::string jointParentName = this->parent->GetName();
|
|
std::string unscopedName = jointParentName;
|
|
size_t pIdx = jointParentName.find("::");
|
|
if (pIdx != std::string::npos)
|
|
unscopedName = jointParentName.substr(pIdx+2);
|
|
|
|
this->jointMsg->set_parent(unscopedName);
|
|
this->jointMsg->set_parent_id(this->parent->GetId());
|
|
}
|
|
|
|
// Child
|
|
if (this->child)
|
|
{
|
|
std::string jointChildName = this->child->GetName();
|
|
std::string unscopedName = jointChildName;
|
|
size_t pIdx = jointChildName.find("::");
|
|
if (pIdx != std::string::npos)
|
|
unscopedName = jointChildName.substr(pIdx+2);
|
|
|
|
this->jointMsg->set_child(unscopedName);
|
|
this->jointMsg->set_child_id(this->child->GetId());
|
|
}
|
|
|
|
// Pose
|
|
if (oldMsg && oldMsg->has_pose())
|
|
{
|
|
this->jointMsg->mutable_pose()->CopyFrom(*(oldMsg->mutable_pose()));
|
|
}
|
|
else
|
|
{
|
|
msgs::Set(this->jointMsg->mutable_pose(), ignition::math::Pose3d::Zero);
|
|
}
|
|
|
|
// Type
|
|
this->jointMsg->set_type(
|
|
msgs::ConvertJointType(JointMaker::TypeAsString(this->type)));
|
|
|
|
// Axes
|
|
unsigned int axisCount = JointMaker::JointAxisCount(this->type);
|
|
for (unsigned int i = 0; i < axisCount; ++i)
|
|
{
|
|
msgs::Axis *axisMsg;
|
|
msgs::Axis *oldAxisMsg = NULL;
|
|
if (i == 0u)
|
|
{
|
|
axisMsg = this->jointMsg->mutable_axis1();
|
|
if (oldMsg && oldMsg->has_axis1())
|
|
oldAxisMsg = oldMsg->mutable_axis1();
|
|
}
|
|
else if (i == 1u)
|
|
{
|
|
axisMsg = this->jointMsg->mutable_axis2();
|
|
if (oldMsg && oldMsg->has_axis2())
|
|
oldAxisMsg = oldMsg->mutable_axis2();
|
|
}
|
|
else
|
|
{
|
|
gzerr << "Invalid axis index["
|
|
<< i
|
|
<< "]"
|
|
<< std::endl;
|
|
continue;
|
|
}
|
|
// Keep axis from previous msg if possible
|
|
if (oldAxisMsg)
|
|
{
|
|
axisMsg->CopyFrom(*oldAxisMsg);
|
|
}
|
|
else
|
|
{
|
|
if (this->type == JointMaker::JOINT_GEARBOX)
|
|
{
|
|
msgs::Set(axisMsg->mutable_xyz(), ignition::math::Vector3d::UnitZ);
|
|
}
|
|
else
|
|
{
|
|
if (this->axes.size() < i+1)
|
|
{
|
|
this->axes.push_back(
|
|
JointMaker::unitVectors[i%JointMaker::unitVectors.size()]);
|
|
}
|
|
msgs::Set(axisMsg->mutable_xyz(), this->axes[i]);
|
|
}
|
|
axisMsg->set_use_parent_model_frame(false);
|
|
axisMsg->set_limit_lower(-GZ_DBL_MAX);
|
|
axisMsg->set_limit_upper(GZ_DBL_MAX);
|
|
axisMsg->set_limit_effort(-1);
|
|
axisMsg->set_limit_velocity(-1);
|
|
axisMsg->set_damping(0);
|
|
}
|
|
|
|
// Add angle field after we've checked that index i is valid
|
|
this->jointMsg->add_angle(0);
|
|
}
|
|
|
|
// Others
|
|
if (oldMsg && oldMsg->has_limit_erp())
|
|
{
|
|
this->jointMsg->set_limit_erp(oldMsg->limit_erp());
|
|
}
|
|
else
|
|
this->jointMsg->set_limit_erp(0.2);
|
|
|
|
if (oldMsg && oldMsg->has_suspension_erp())
|
|
{
|
|
this->jointMsg->set_suspension_erp(oldMsg->suspension_erp());
|
|
}
|
|
else
|
|
this->jointMsg->set_suspension_erp(0.2);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::ShowContextMenu(const std::string &_name)
|
|
{
|
|
auto it = this->dataPtr->joints.find(_name);
|
|
if (it == this->dataPtr->joints.end())
|
|
return;
|
|
|
|
QMenu menu;
|
|
if (this->dataPtr->inspectAct)
|
|
menu.addAction(this->dataPtr->inspectAct);
|
|
|
|
this->dataPtr->inspectName = _name;
|
|
QAction *deleteAct = new QAction(tr("Delete"), this);
|
|
connect(deleteAct, SIGNAL(triggered()), this, SLOT(OnDelete()));
|
|
menu.addAction(deleteAct);
|
|
|
|
menu.exec(QCursor::pos());
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::OnSetSelectedEntity(const std::string &/*_name*/,
|
|
const std::string &/*_mode*/)
|
|
{
|
|
this->DeselectAll();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::OnSetSelectedJoint(const std::string &_name,
|
|
const bool _selected)
|
|
{
|
|
this->SetSelected(_name, _selected);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::SetSelected(const std::string &_name,
|
|
const bool _selected)
|
|
{
|
|
auto it = this->dataPtr->joints.find(_name);
|
|
if (it == this->dataPtr->joints.end())
|
|
return;
|
|
|
|
this->SetSelected((*it).second->hotspot, _selected);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::SetSelected(const rendering::VisualPtr &_jointVis,
|
|
const bool _selected)
|
|
{
|
|
if (!_jointVis)
|
|
return;
|
|
|
|
_jointVis->SetHighlighted(_selected);
|
|
auto it = std::find(this->dataPtr->selectedJoints.begin(),
|
|
this->dataPtr->selectedJoints.end(), _jointVis);
|
|
if (_selected)
|
|
{
|
|
if (it == this->dataPtr->selectedJoints.end())
|
|
{
|
|
this->dataPtr->selectedJoints.push_back(_jointVis);
|
|
model::Events::setSelectedJoint(_jointVis->GetName(), _selected);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (it != this->dataPtr->selectedJoints.end())
|
|
{
|
|
this->dataPtr->selectedJoints.erase(it);
|
|
model::Events::setSelectedJoint(_jointVis->GetName(), _selected);
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::DeselectAll()
|
|
{
|
|
while (!this->dataPtr->selectedJoints.empty())
|
|
{
|
|
rendering::VisualPtr vis = this->dataPtr->selectedJoints[0];
|
|
vis->SetHighlighted(false);
|
|
this->dataPtr->selectedJoints.erase(this->dataPtr->selectedJoints.begin());
|
|
model::Events::setSelectedJoint(vis->GetName(), false);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::CreateJointFromSDF(sdf::ElementPtr _jointElem,
|
|
const std::string &_modelName)
|
|
{
|
|
msgs::Joint jointMsg = msgs::JointFromSDF(_jointElem);
|
|
|
|
// Parent
|
|
std::string parentName = _modelName + "::" + jointMsg.parent();
|
|
rendering::VisualPtr parentVis =
|
|
gui::get_active_camera()->GetScene()->GetVisual(parentName);
|
|
|
|
// Child
|
|
std::string childName = _modelName + "::" + jointMsg.child();
|
|
rendering::VisualPtr childVis =
|
|
gui::get_active_camera()->GetScene()->GetVisual(childName);
|
|
|
|
if (!parentVis || !childVis)
|
|
{
|
|
gzerr << "Unable to load joint. Joint child / parent not found"
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
|
|
JointData *joint = new JointData();
|
|
joint->name = jointMsg.name();
|
|
joint->parent = parentVis;
|
|
joint->child = childVis;
|
|
joint->type = this->ConvertJointType(msgs::ConvertJointType(jointMsg.type()));
|
|
std::string jointVisName = _modelName + "::" + joint->name;
|
|
|
|
joint->jointMsg.reset(new msgs::Joint);
|
|
joint->jointMsg->CopyFrom(jointMsg);
|
|
joint->jointMsg->set_parent_id(joint->parent->GetId());
|
|
joint->jointMsg->set_child_id(joint->child->GetId());
|
|
|
|
// Inspector
|
|
joint->inspector = new JointInspector(this);
|
|
joint->inspector->Update(joint->jointMsg);
|
|
joint->inspector->setModal(false);
|
|
connect(joint->inspector, SIGNAL(Applied()), joint, SLOT(OnApply()));
|
|
|
|
// Visuals
|
|
rendering::VisualPtr jointVis(
|
|
new rendering::Visual(jointVisName, parentVis->GetParent(), false));
|
|
jointVis->Load();
|
|
rendering::DynamicLines *jointLine =
|
|
jointVis->CreateDynamicLine(rendering::RENDERING_LINE_LIST);
|
|
|
|
auto origin = parentVis->GetWorldPose().Ign().Pos()
|
|
- parentVis->GetParent()->GetWorldPose().Ign().Pos();
|
|
jointLine->AddPoint(origin);
|
|
jointLine->AddPoint(origin + ignition::math::Vector3d(0, 0, 0.1));
|
|
|
|
jointVis->GetSceneNode()->setInheritScale(false);
|
|
jointVis->GetSceneNode()->setInheritOrientation(false);
|
|
joint->visual = jointVis;
|
|
joint->line = jointLine;
|
|
joint->dirty = true;
|
|
|
|
auto jointId = this->CreateHotSpot(joint);
|
|
|
|
// Notify other widgets
|
|
if (!jointId.empty())
|
|
{
|
|
gui::model::Events::jointInserted(jointId, joint->name,
|
|
jointTypes[joint->type], joint->parent->GetName(),
|
|
joint->child->GetName());
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::OnLinkInserted(const std::string &_linkName)
|
|
{
|
|
std::string unscopedName = _linkName;
|
|
size_t idx = unscopedName.find("::");
|
|
if (idx != std::string::npos)
|
|
unscopedName = _linkName.substr(idx+2);
|
|
|
|
this->dataPtr->linkList[_linkName] = unscopedName;
|
|
|
|
this->EmitLinkInserted(_linkName);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::OnLinkRemoved(const std::string &_linkName)
|
|
{
|
|
if (this->dataPtr->linkList.erase(_linkName))
|
|
this->EmitLinkRemoved(_linkName);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
std::map<std::string, std::string> JointMaker::LinkList() const
|
|
{
|
|
return this->dataPtr->linkList;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::ShowJoints(bool _show)
|
|
{
|
|
for (auto iter : this->dataPtr->joints)
|
|
{
|
|
rendering::VisualPtr vis = iter.second->hotspot;
|
|
if (vis)
|
|
{
|
|
vis->SetVisible(_show);
|
|
vis->SetHighlighted(false);
|
|
}
|
|
if (iter.second->jointVisual)
|
|
iter.second->jointVisual->SetVisible(_show);
|
|
}
|
|
this->DeselectAll();
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
bool JointMaker::SetParentLink(const rendering::VisualPtr &_parentLink)
|
|
{
|
|
if (!_parentLink)
|
|
{
|
|
gzerr << "Parent link is null" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(this->dataPtr->updateMutex);
|
|
|
|
// Choosing parent for the first time
|
|
if (!this->dataPtr->newJoint)
|
|
{
|
|
// Create new line connecting parent to mouse
|
|
this->dataPtr->newJoint = this->CreateJointLine("JOINT_LINE", _parentLink);
|
|
}
|
|
// Update parent of joint being created
|
|
else if (this->dataPtr->newJoint->parent)
|
|
{
|
|
// Reset previous parent
|
|
this->dataPtr->newJoint->parent->SetWorldPose(
|
|
this->dataPtr->parentLinkOriginalPose);
|
|
this->SetVisualMoved(this->dataPtr->newJoint->parent, false);
|
|
|
|
this->dataPtr->newJoint->parent = _parentLink;
|
|
this->dataPtr->newJoint->dirty = true;
|
|
}
|
|
else
|
|
{
|
|
gzerr << "There's a joint being created but the parent visual hasn't been "
|
|
<< "defined. This should never happen." << std::endl;
|
|
return false;
|
|
}
|
|
|
|
this->dataPtr->parentLinkOriginalPose = _parentLink->GetWorldPose().Ign();
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
bool JointMaker::SetChildLink(const rendering::VisualPtr &_childLink)
|
|
{
|
|
if (!_childLink)
|
|
{
|
|
gzerr << "Child link can't be null" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
if (!this->dataPtr->newJoint || !this->dataPtr->newJoint->parent)
|
|
{
|
|
gzerr << "New joint must have a parent before a child" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(this->dataPtr->updateMutex);
|
|
|
|
// Choosing child for the first time
|
|
if (!this->dataPtr->newJoint->child)
|
|
{
|
|
rendering::VisualPtr parentVis = this->dataPtr->newJoint->parent;
|
|
|
|
// Clear joint line connected to parent
|
|
this->RemoveJoint("");
|
|
|
|
// Create new joint with parent and child
|
|
auto joint = this->CreateJoint(parentVis, _childLink);
|
|
this->dataPtr->newJoint = joint;
|
|
|
|
// Create hotspot visual
|
|
this->CreateHotSpot(this->dataPtr->newJoint);
|
|
}
|
|
// Update child
|
|
else
|
|
{
|
|
// Reset previous child
|
|
this->dataPtr->newJoint->child->SetWorldPose(
|
|
this->dataPtr->childLinkOriginalPose);
|
|
this->SetVisualMoved(this->dataPtr->newJoint->child, false);
|
|
|
|
this->dataPtr->newJoint->child = _childLink;
|
|
this->dataPtr->newJoint->dirty = true;
|
|
_childLink->AttachVisual(this->dataPtr->newJoint->jointVisual);
|
|
}
|
|
|
|
this->dataPtr->childLinkOriginalPose = _childLink->GetWorldPose().Ign();
|
|
|
|
// Change state to not creating joint
|
|
gui::Events::manipMode("select");
|
|
this->dataPtr->jointType = JointMaker::JOINT_NONE;
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::OnType(const int _typeInt)
|
|
{
|
|
this->dataPtr->jointType = static_cast<JointMaker::JointType>(_typeInt);
|
|
|
|
if (this->dataPtr->newJoint && this->dataPtr->jointType != JOINT_NONE)
|
|
{
|
|
this->dataPtr->newJoint->type = this->dataPtr->jointType;
|
|
this->dataPtr->newJoint->dirty = true;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::SetAxis(const std::string &_axis,
|
|
const ignition::math::Vector3d &_value)
|
|
{
|
|
if (this->dataPtr->newJoint && this->dataPtr->newJoint->jointMsg)
|
|
{
|
|
if (_axis == "axis1" && this->dataPtr->newJoint->jointMsg->has_axis1())
|
|
{
|
|
msgs::Set(
|
|
this->dataPtr->newJoint->jointMsg->mutable_axis1()->mutable_xyz(),
|
|
_value);
|
|
this->dataPtr->newJoint->axes[0] = _value;
|
|
}
|
|
else if (_axis == "axis2" &&
|
|
this->dataPtr->newJoint->jointMsg->has_axis2())
|
|
{
|
|
msgs::Set(
|
|
this->dataPtr->newJoint->jointMsg->mutable_axis2()->mutable_xyz(),
|
|
_value);
|
|
this->dataPtr->newJoint->axes[1] = _value;
|
|
}
|
|
this->dataPtr->newJoint->dirty = true;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::SetJointPose(const ignition::math::Pose3d &_pose)
|
|
{
|
|
if (this->dataPtr->newJoint && this->dataPtr->newJoint->jointMsg)
|
|
{
|
|
msgs::Set(this->dataPtr->newJoint->jointMsg->mutable_pose(), _pose);
|
|
this->dataPtr->newJoint->dirty = true;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::SetParentLink(const std::string &_name)
|
|
{
|
|
auto vis = this->LinkVisualFromName(_name);
|
|
|
|
if (vis)
|
|
this->SetParentLink(vis);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::SetChildLink(const std::string &_name)
|
|
{
|
|
auto vis = this->LinkVisualFromName(_name);
|
|
|
|
if (vis)
|
|
this->SetChildLink(vis);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
rendering::VisualPtr JointMaker::LinkVisualFromName(const std::string &_name)
|
|
{
|
|
// Get scoped name
|
|
std::string scopedName;
|
|
for (auto link : this->dataPtr->linkList)
|
|
{
|
|
if (link.second == _name || link.first == _name)
|
|
{
|
|
scopedName = link.first;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (scopedName.empty())
|
|
return NULL;
|
|
|
|
// Get visual
|
|
rendering::ScenePtr scene = rendering::get_scene();
|
|
|
|
if (!scene)
|
|
return NULL;
|
|
|
|
return scene->GetVisual(scopedName);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::SetLinksRelativePose(const ignition::math::Pose3d &_pose,
|
|
const bool _resetAll, const int _resetAxis)
|
|
{
|
|
if (!this->dataPtr->newJoint || !this->dataPtr->newJoint->parent ||
|
|
!this->dataPtr->newJoint->child)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto newChildPose = this->dataPtr->newJoint->child->GetWorldPose().Ign();
|
|
|
|
if (_resetAll)
|
|
{
|
|
newChildPose = this->dataPtr->childLinkOriginalPose;
|
|
this->dataPtr->newJoint->parent->SetWorldPose(
|
|
this->dataPtr->parentLinkOriginalPose);
|
|
}
|
|
else if (_resetAxis == 0)
|
|
{
|
|
newChildPose.Pos().X(this->dataPtr->childLinkOriginalPose.Pos().X());
|
|
}
|
|
else if (_resetAxis == 1)
|
|
{
|
|
newChildPose.Pos().Y(this->dataPtr->childLinkOriginalPose.Pos().Y());
|
|
}
|
|
else if (_resetAxis == 2)
|
|
{
|
|
newChildPose.Pos().Z(this->dataPtr->childLinkOriginalPose.Pos().Z());
|
|
}
|
|
else
|
|
{
|
|
// Get poses as homogeneous transforms
|
|
ignition::math::Matrix4d parent_world(
|
|
this->dataPtr->newJoint->parent->GetWorldPose().Ign());
|
|
ignition::math::Matrix4d child_parent(_pose);
|
|
|
|
// w_T_c = w_T_p * p_T_c
|
|
ignition::math::Matrix4d child_world =
|
|
parent_world * child_parent;
|
|
|
|
newChildPose = child_world.Pose();
|
|
}
|
|
|
|
this->dataPtr->newJoint->child->SetWorldPose(newChildPose);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::AlignLinks(const bool _childToParent,
|
|
const std::string &_axis, const std::string &_config, const bool _reverse)
|
|
{
|
|
if (!this->dataPtr->newJoint || !this->dataPtr->newJoint->parent ||
|
|
!this->dataPtr->newJoint->child)
|
|
{
|
|
gzerr << "Couldn't find new joint's parent and child links to be aligned."
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
|
|
std::vector<rendering::VisualPtr> links;
|
|
links.push_back(this->dataPtr->newJoint->parent);
|
|
links.push_back(this->dataPtr->newJoint->child);
|
|
|
|
std::string target = _childToParent ? "first" : "last";
|
|
|
|
ModelAlign::Instance()->AlignVisuals(links, _axis, _config,
|
|
target, true, _reverse);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::SetVisualMoved(const rendering::VisualPtr &_vis,
|
|
const bool _moved)
|
|
{
|
|
if (_vis->GetChildCount() != 0)
|
|
{
|
|
for (unsigned int j = 0; j < _vis->GetChildCount(); ++j)
|
|
{
|
|
this->SetVisualMoved(_vis->GetChild(j), _moved);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_moved)
|
|
{
|
|
_vis->SetEmissive(common::Color(0, 0, 1, 1));
|
|
}
|
|
else
|
|
{
|
|
_vis->SetEmissive(common::Color(0, 0, 0, 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void JointMaker::FinalizeCreation()
|
|
{
|
|
gui::model::Events::modelChanged();
|
|
this->dataPtr->jointType = JointMaker::JOINT_NONE;
|
|
|
|
// Notify schematic view and palette list
|
|
if (this->dataPtr->newJoint && this->dataPtr->newJoint->hotspot &&
|
|
this->dataPtr->newJoint->child && this->dataPtr->newJoint->parent)
|
|
{
|
|
gui::model::Events::jointInserted(
|
|
this->dataPtr->newJoint->hotspot->GetName(),
|
|
this->dataPtr->newJoint->name,
|
|
this->jointTypes[this->dataPtr->newJoint->type],
|
|
this->dataPtr->newJoint->parent->GetName(),
|
|
this->dataPtr->newJoint->child->GetName());
|
|
|
|
|
|
// Reset visuals
|
|
this->SetVisualMoved(this->dataPtr->newJoint->parent, false);
|
|
this->SetVisualMoved(this->dataPtr->newJoint->child, false);
|
|
}
|
|
this->dataPtr->newJoint = NULL;
|
|
|
|
// Notify ModelEditor to uncheck tool button
|
|
this->JointAdded();
|
|
|
|
this->Stop();
|
|
}
|