26 #include <QDoubleSpinBox>
27 #include <QFormLayout>
28 #include <QHBoxLayout>
31 #include <QPushButton>
32 #include <QRadioButton>
33 #include <QSignalBlocker>
35 #include <SimoxUtility/math/convert/deg_to_rad.h>
36 #include <SimoxUtility/math/convert/mat4f_to_rpy.h>
37 #include <SimoxUtility/math/convert/rad_to_deg.h>
38 #include <SimoxUtility/math/convert/rpy_to_mat4f.h>
39 #include <SimoxUtility/math/pose/pose.h>
40 #include <VirtualRobot/VirtualRobot.h>
41 #include <VirtualRobot/XML/RobotIO.h>
57 setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
59 name =
new QLineEdit(
this);
60 name->setReadOnly(
true);
61 locationID =
new QLineEdit(
this);
62 locationID->setReadOnly(
true);
64 agent =
new QComboBox(
this);
65 frame =
new QComboBox(
this);
66 removeInstanceButton =
new QPushButton(
"Remove Instance");
67 removeInstanceButton->setToolTip(
68 "Remove the instance id from the agent and make this location relative to an object "
69 "class instead of a specific object instance.");
71 x =
new QDoubleSpinBox(
this);
72 y =
new QDoubleSpinBox(
this);
73 z =
new QDoubleSpinBox(
this);
75 roll =
new QDoubleSpinBox(
this);
76 pitch =
new QDoubleSpinBox(
this);
77 yaw =
new QDoubleSpinBox(
this);
79 angleUnitDeg =
new QRadioButton(
"Degrees");
80 angleUnitRad =
new QRadioButton(
"Radians");
82 _useCurrentRobotPose =
new QPushButton(
"Use Current Robot Pose");
83 _useCurrentRobotPose->setEnabled(
false);
84 _useCurrentRobotPose->setToolTip(
85 "Use the robot's current global pose (requires connection to robot state).");
88 for (QDoubleSpinBox* pos : _positionSpinBoxes())
90 pos->setSuffix(
" mm");
91 pos->setMinimum(-1e6);
92 pos->setMaximum(+1e6);
93 pos->setSingleStep(50);
95 for (QDoubleSpinBox* spinBox : _allSpinBoxes())
98 spinBox->setAlignment(Qt::AlignRight);
101 QHBoxLayout* angleUnitLayout =
new QHBoxLayout();
102 for (QRadioButton* angleUnit : {angleUnitDeg, angleUnitRad})
104 angleUnitLayout->addWidget(angleUnit);
107 QFormLayout* layout =
new QFormLayout();
108 this->setLayout(layout);
110 layout->addRow(
"Name", name);
111 layout->addRow(
"Entity ID", locationID);
112 layout->addRow(
"Agent", agent);
113 layout->addRow(
"Frame", frame);
114 layout->addRow(removeInstanceButton);
115 layout->addRow(
"X", x);
116 layout->addRow(
"Y", y);
117 layout->addRow(
"Z", z);
118 layout->addRow(
"Roll", roll);
119 layout->addRow(
"Pitch", pitch);
120 layout->addRow(
"Yaw", yaw);
121 layout->addRow(
"Angle Units", angleUnitLayout);
122 layout->addRow(_useCurrentRobotPose);
127 for (QRadioButton* angleUnit : {angleUnitDeg, angleUnitRad})
129 connect(angleUnit, &QRadioButton::toggled,
this, &This::_updateAngleUnit);
133 QOverload<int>::of(&QComboBox::currentIndexChanged),
135 &This::_updateVertexAgent);
137 QOverload<int>::of(&QComboBox::currentIndexChanged),
139 &This::_updateVertexFrame);
140 connect(removeInstanceButton, &QPushButton::pressed,
this, &This::_removeInstance);
142 for (QDoubleSpinBox* spinBox : _allSpinBoxes())
145 QOverload<qreal>::of(&QDoubleSpinBox::valueChanged),
147 &This::_updateVertexAttribs);
150 connect(_useCurrentRobotPose, &QPushButton::pressed,
this, &This::_setFromCurrentRobotPose);
153 angleUnitDeg->click();
156 std::optional<GuiGraph::Vertex>
165 QSignalBlocker blocker(
this);
175 _vertex = std::nullopt;
182 return {x->value(), y->value(), z->value()};
188 Eigen::Vector3d raw = _rpyRaw();
189 if (angleUnitRad->isChecked())
202 Eigen::Vector3d raw = _rpyRaw();
203 if (angleUnitDeg->isChecked())
205 return simox::math::deg_to_rad(raw);
222 this->_robotConnection = connection;
223 _useCurrentRobotPose->setEnabled(_robotConnection !=
nullptr);
229 this->_objectPosesConnection = connection;
230 if (_vertex.has_value())
232 QSignalBlocker blocker(
this);
234 _loadFramedPoseFromVertex(_vertex.value());
239 VertexDataWidget::_setFromVertex(
const GuiGraph::Vertex& vertex)
243 name->setText(QString::fromStdString(attrib.
getName()));
245 QString::fromStdString(aron::fromAron<armem::MemoryID>(attrib.
aron.locationID).str()));
247 _loadFramedPoseFromVertex(
vertex);
253 VertexDataWidget::_getToVertex(GuiGraph::Vertex& vertex)
255 VertexData& attrib =
vertex.attrib();
261 frame->currentText().toStdString(),
262 agent->currentText().toStdString());
263 attrib.setPose(framed);
267 VertexDataWidget::_updateAngleUnit()
269 std::function<double(
double)> convertValue;
270 QString suffix =
" \u00b0";
276 if (angleUnitRad->isChecked())
278 convertValue = [](
double deg) {
return simox::math::deg_to_rad(deg); };
280 min = simox::math::deg_to_rad(
min);
281 max = simox::math::deg_to_rad(
max);
282 step = simox::math::deg_to_rad(step);
288 convertValue = [](
double rad) {
return simox::math::rad_to_deg(rad); };
291 std::vector<QSignalBlocker> blockers;
292 for (QDoubleSpinBox*
angle : _angleSpinBoxes())
294 blockers.emplace_back(
angle);
296 angle->setSuffix(suffix);
299 angle->setSingleStep(step);
300 angle->setDecimals(decimals);
302 if (_vertex.has_value())
305 simox::math::mat4f_to_rpy(_vertex->attrib().getPose().toEigen().cast<qreal>()));
312 VertexDataWidget::_updateVertexAttribs()
314 if (not signalsBlocked() and _vertex.has_value())
316 _getToVertex(_vertex.value());
317 _vertex->attrib().changed =
true;
323 VertexDataWidget::_updateVertexAgent()
325 if (not signalsBlocked() and _vertex.has_value() and _objectPosesConnection !=
nullptr)
328 QSignalBlocker blocker(
this);
331 frame->setCurrentIndex(0);
333 _framedPoseCoordinateConversion();
335 _updateVertexAttribs();
340 VertexDataWidget::_updateVertexFrame()
342 if (not signalsBlocked() and _vertex.has_value() and _objectPosesConnection !=
nullptr)
345 QSignalBlocker blocker(
this);
347 _framedPoseCoordinateConversion();
349 _updateVertexAttribs();
354 VertexDataWidget::_removeInstance()
356 if (not signalsBlocked() and _vertex.has_value())
359 if (agent->currentText().isEmpty())
365 QSignalBlocker blocker(
this);
367 ObjectID currentAgent(agent->currentText().toStdString());
368 QString newAgent = QString::fromStdString(currentAgent.getClassID().str());
369 if (agent->findText(newAgent) < 0)
371 agent->insertItem(0, newAgent);
373 agent->setCurrentText(newAgent);
375 _updateVertexAttribs();
379 std::vector<QDoubleSpinBox*>
380 VertexDataWidget::_positionSpinBoxes()
385 std::vector<QDoubleSpinBox*>
386 VertexDataWidget::_angleSpinBoxes()
388 return {roll, pitch, yaw};
391 std::vector<QDoubleSpinBox*>
392 VertexDataWidget::_allSpinBoxes()
394 return {x, y, z, roll, pitch, yaw};
400 _setXyz(simox::math::position(
pose.matrix()));
401 _setRpyRad(simox::math::mat4f_to_rpy(
pose.matrix()));
405 VertexDataWidget::_setXyz(
const Eigen::Vector3d& xyz)
407 x->setValue(
xyz.x());
408 y->setValue(
xyz.y());
409 z->setValue(
xyz.z());
413 VertexDataWidget::_rpyRaw()
const
415 return {roll->value(), pitch->value(), yaw->value()};
419 VertexDataWidget::_setRpyRaw(
const Eigen::Vector3d& rpy)
const
421 roll->setValue(rpy(0));
422 pitch->setValue(rpy(1));
423 yaw->setValue(rpy(2));
427 VertexDataWidget::_setRpyDeg(
const Eigen::Vector3d& rpyDeg)
const
429 if (angleUnitDeg->isChecked())
433 else if (angleUnitRad->isChecked())
435 _setRpyRaw(simox::math::deg_to_rad(
rpyDeg));
440 VertexDataWidget::_setRpyRad(
const Eigen::Vector3d& rpyRad)
const
442 if (angleUnitDeg->isChecked())
444 _setRpyRaw(simox::math::rad_to_deg(
rpyRad));
446 else if (angleUnitRad->isChecked())
453 VertexDataWidget::_setFromCurrentRobotPose()
457 QSignalBlocker blocker(
this);
460 _updateVertexAttribs();
464 VertexDataWidget::_loadFramedPoseFromVertex(
const GuiGraph::Vertex& vertex)
467 QString currAgent = QString::fromStdString(
vertex.attrib().getPose().agent);
468 if (agent->findText(currAgent) < 0)
470 agent->insertItem(0, currAgent);
472 agent->setCurrentText(currAgent);
476 QString currFrame = QString::fromStdString(
vertex.attrib().getPose().frame);
477 if (frame->findText(currFrame) < 0)
479 frame->insertItem(0, currFrame);
481 frame->setCurrentText(currFrame);
485 VertexDataWidget::_updateAgentMenu()
489 if (_objectPosesConnection ==
nullptr)
494 agent->addItem(QString::fromAscii(
""));
496 for (
const auto& [
id,
object] : map)
498 agent->addItem(QString::fromStdString(
id.
str()));
503 VertexDataWidget::_updateFrameMenu()
507 if (_objectPosesConnection ==
nullptr)
512 const auto nodes = [&]() -> std::optional<std::vector<std::string>>
514 const auto& currentObj = agent->currentText().toStdString();
515 if (currentObj.empty())
522 std::optional<ObjectInfo> info = finder.
findObject(currentObj);
523 if (!info.has_value())
528 std::optional<PackageFileLocation> articulatedModel = info->articulatedSimoxXML();
529 if (!articulatedModel.has_value())
535 articulatedModel.value().relativePath);
536 const auto robot = VirtualRobot::RobotIO::loadRobot(
537 modelPath.toSystemPath(), VirtualRobot::BaseIO::eStructure);
538 if (robot ==
nullptr)
543 return robot->getRobotNodeNames();
546 std::vector<std::string> frames = nodes().value_or(std::vector<std::string>{
"root"});
548 for (
const auto& i : frames)
550 frame->addItem(QString::fromStdString(i));
555 VertexDataWidget::_framedPoseCoordinateConversion()
558 std::vector<ObjectInfo> objectInfo = _objectPosesConnection->
getObjectInfo();
561 const auto globalPose =
563 if (!globalPose.pose.has_value())
567 const core::Pose_d global_T_location = globalPose.pose.value().cast<
double>();
570 const std::string newAgent = agent->currentText().toStdString();
571 const std::string newFrame = frame->currentText().toStdString();
575 if (!
object.
pose.has_value())
579 const core::Pose_d global_T_object =
object.pose.value().cast<
double>();
580 const core::Pose_d object_T_global = global_T_object.inverse();
582 const core::Pose_d object_T_location = object_T_global * global_T_location;
583 _setPose(object_T_location);