26 #include <unordered_set>
32 #include <QDoubleSpinBox>
35 #include <QPushButton>
37 #include <QTableWidget>
38 #include <QTableWidgetItem>
39 #include <QFileDialog>
41 #include <VirtualRobot/Visualization/CoinVisualization/CoinVisualization.h>
42 #include <VirtualRobot/Visualization/CoinVisualization/CoinVisualizationFactory.h>
43 #include <VirtualRobot/Visualization/CoinVisualization/CoinVisualizationNode.h>
44 #include <VirtualRobot/Robot.h>
45 #include <VirtualRobot/RobotNodeSet.h>
51 #include <RobotComponents/interface/components/MotionPlanning/Tasks/MotionPlanningTask.h>
53 #include <VirtualRobot/Visualization/Visualization.h>
54 #include <MotionPlanning/CSpace/CSpaceSampled.h>
57 #include <VirtualRobot/Robot.h>
61 while (QLayoutItem* item = layout->takeAt(0))
65 if (QWidget* widget = item->widget())
67 widget->deleteLater();
70 if (QLayout* childLayout = item->layout())
80 static const float EDGE_WIDTH
84 static const armarx::DrawColor EDGE_COLOR_TREE
88 static const armarx::DrawColor EDGE_COLOR_PATH
101 widget.sliderPathPosition->setScalePosition(QwtSlider::LeadingScale);
102 widget.sliderPathPosition->setScaleStepSize(0.001);
107 widget.loadFromServerLayout->addWidget(motionPlanningServerTaskList);
109 updateCollisionStateTimer.setSingleShot(
true);
116 motionPlanningServerProxyName = settings->value(
"motionPlanningServerProxyName", QString::fromStdString(motionPlanningServerProxyName)).toString().toStdString();
121 settings->setValue(
"motionPlanningServerProxyName", QString::fromStdString(motionPlanningServerProxyName));
126 return "MotionPlanning.SimoxCSpaceVisualizer";
131 visuRoot =
new SoSeparator();
135 visuAgent =
new SoSeparator();
137 visuRoot->addChild(visuAgent);
141 ARMARX_INFO <<
"Creating debug drawer component " << debugDrawerComponentName;
142 debugDrawer = Component::create<armarx::DebugDrawerComponent>(
getIceProperties(), debugDrawerComponentName);
146 debugDrawer->setMutex(
mutex3D);
156 std::unique_lock lock(*
mutex3D);
157 visuRoot->addChild(debugDrawer->getVisualization());
171 dialog->addProxyFinder<MotionPlanningServerInterfacePrx>({
"MotionPlanningServer",
"The MotionPlanningServer (empty for none)",
"*Server"});
173 return qobject_cast<SimpleConfigDialog*>(dialog);
178 motionPlanningServerProxy = getProxy<MotionPlanningServerInterfacePrx>(motionPlanningServerProxyName);
179 motionPlanningServerTaskList->setMotionPlanningServer(motionPlanningServerProxy);
180 motionPlanningServerTaskList->enableAutoUpdate(
true);
188 std::cout <<
"Returning scene = " << visuRoot->getName() << std::endl;
200 debugDrawer->setMutex(
mutex3D);
206 connect(widget.pushButtonLoadTask, SIGNAL(clicked()),
this, SLOT(
loadTask()));
209 connect(widget.checkBoxDrawTrace, SIGNAL(clicked(
bool)),
this, SLOT(
drawEdges(
bool)));
211 connect(widget.doubleSpinBoxTraceStep, SIGNAL(valueChanged(
double)),
this, SLOT(
traceStepChanged()));
212 connect(widget.pushButtonHideAllPaths, SIGNAL(clicked()),
this, SLOT(
hideAllPaths()));
214 connect(widget.pushButtonSetConfig, SIGNAL(clicked()),
this, SLOT(
setCurrentConfig()));
215 connect(widget.spinBoxPathNumber, SIGNAL(valueChanged(
int)),
this, SLOT(
setCurrentPath()));
218 connect(widget.checkBoxCollisionNodeHighlighting, SIGNAL(toggled(
bool)),
this, SLOT(
highlightCollisionNodes(
bool)));
219 startTimer(timerPeriod);
224 if (widget.checkBoxDrawTrace->isChecked())
232 for (std::size_t pIdx = 0; pIdx < paths.size(); ++pIdx)
234 auto&& path = paths.at(pIdx);
248 int newPath = widget.spinBoxPathNumber->value() - 1;
255 widget.sliderPathPosition->setScale(0.0,
getCurrentPath().getLength());
256 widget.sliderPathPosition->setValue(0);
257 widget.sliderPathPosition->setScaleStepSize(
getCurrentPath().getLength() * 0.3);
258 widget.sliderPathPosition->setTotalSteps(100);
259 widget.labelCurrentPathNodeCount->setText(QString::number(
getCurrentPath().path.size()));
260 widget.labelCurrentPathTotalLen->setText(QString::number(
getCurrentPath().getLength()));
266 if (!widget.spinBoxPathNumber->value())
268 widget.labelCurrentPathCurrentLen->setText(QString::number(0));
273 const auto val = widget.sliderPathPosition->value();
274 widget.labelCurrentPathCurrentLen->setText(QString::number(val));
275 auto indexTo =
std::distance(pathAcc.begin(), std::upper_bound(pathAcc.begin(), pathAcc.end(), val));
276 widget.labelCurrentPathCurrentNode->setText(QString::number(indexTo));
278 assert(indexTo >= 0);
279 assert(
static_cast<std::size_t
>(indexTo) <= path.size());
287 if (
static_cast<std::size_t
>(indexTo) == path.size())
293 auto indexFrom = indexTo - 1;
294 auto cfgFrom = path.at(indexFrom);
295 auto cfgTo = path.at(indexTo);
297 auto t = (val - pathAcc.at(indexFrom)) / (pathAcc.at(indexTo) - pathAcc.at(indexFrom));
301 SimoxCSpacePtr simoxcspace = SimoxCSpacePtr::dynamicCast(cspaceUtil);
304 CSpaceAdaptorBasePtr simoxcspaceadapter = CSpaceAdaptorBasePtr::dynamicCast(cspaceUtil);
305 if (simoxcspaceadapter)
307 simoxcspace = SimoxCSpacePtr::dynamicCast(simoxcspaceadapter->getOriginalCSpace());
313 VirtualRobot::RobotNodeSetPtr rns = VirtualRobot::RobotNodeSet::createRobotNodeSet(simoxcspace->getAgentSceneObj(),
"tmp",
314 simoxcspace->getAgentJointNames());
315 Saba::CSpaceSampled tmpCSpace(simoxcspace->getAgentSceneObj(), VirtualRobot::CDManagerPtr(
new VirtualRobot::CDManager(simoxcspace->getCD())), rns);
316 Eigen::VectorXf from = Eigen::Map<Eigen::VectorXf>(cfgFrom.data(), cfgFrom.size());
317 Eigen::VectorXf to = Eigen::Map<Eigen::VectorXf>(cfgTo.data(), cfgTo.size());
318 auto config = tmpCSpace.interpolate(from, to, t);
319 cfgToSet = (VectorXf {config.data(), config.data() + config.rows()});
324 std::transform(cfgFrom.begin(), cfgFrom.end(), cfgTo.begin(), std::back_inserter(cfgToSet),
325 [t](
float from,
float to)
327 return t * to + (1 - t) * from;
341 VectorXf cfg(cspaceVisu->getDimensionality());
343 for (std::size_t i = 0; i < cfg.size(); ++i)
345 cfg.at(i) =
dynamic_cast<QDoubleSpinBox*
>(widget.tableWidgetDims->cellWidget(i, 2))->
value();
350 const float* it = cfg.data();
351 cspaceVisu->setConfig(it);
352 widget.labelCurrentMinObjDist->setText(
"loading...");
353 widget.labelCurrentCollisionState->setText(
"loading...");
354 updateCollisionStateTimer.start(100);
359 widget.labelCurrentMinObjDist->setText(QString::number(cspaceVisu->getCD().getDistance()));
360 widget.labelCurrentCollisionState->setText(cspaceVisu->getCD().isInCollision() ? QString {
"in collision"}: QString {
"no collision"});
366 std::vector<std::string> nodeNames;
367 nodeNames.reserve(robotNodes.size());
368 for (
const auto& node : robotNodes)
372 nodeNames.emplace_back(node.first->getName());
376 for (std::size_t idxPath = 0; idxPath < paths.size(); ++idxPath)
378 const auto& pDat = paths.at(idxPath);
379 const auto& p = pDat.path;
389 const float traceStep = drawTrace ?
static_cast<float>(widget.doubleSpinBoxTraceStep->value()) : std::numeric_limits<float>::infinity();
390 auto traces = cspaceUtil->getTraceVisu(p, nodeNames, traceStep);
393 for (std::size_t i = 0; i < traces.size(); ++i)
395 DebugDrawerLineSet&
trace = traces.at(i);
396 trace.colorFullIntensity = EDGE_COLOR_PATH;
397 trace.colorNoIntensity = EDGE_COLOR_PATH;
398 trace.lineWidth = EDGE_WIDTH;
399 trace.intensities.assign(
trace.points.size() / 2, 1);
400 debugDrawer->setLineSetVisu(layer,
"trace:" + nodeNames.at(i),
trace);
407 auto&& path = paths.at(
index);
408 const bool vis = (state == Qt::Checked);
415 for (
int i = 0;
static_cast<std::size_t
>(i) < paths.size(); ++i)
417 dynamic_cast<QCheckBox*
>(widget.tableWidgetPaths->cellWidget(i, 0))->setChecked(
false);
423 if (!motionPlanningServerProxy)
427 const auto taskId = motionPlanningServerTaskList->getSelectedId();
434 auto taskCSpaceAndPaths = motionPlanningServerProxy->getTaskCSpaceAndPathsById(*taskId);
436 auto& taskCSpace = taskCSpaceAndPaths.cspace;
440 ARMARX_ERROR_S <<
"Task with id " << *taskId <<
" returned an empty cspace!";
445 while (taskCSpace->ice_isA(CSpaceAdaptor::ice_staticId()))
447 if (taskCSpace->ice_isA(ScaledCSpace::ice_staticId()))
449 auto scalingCSpace = ScaledCSpacePtr::dynamicCast(taskCSpace);
450 for (
auto& path : taskCSpaceAndPaths.paths)
452 scalingCSpace->unscalePath(path);
456 taskCSpace = CSpaceAdaptorPtr::dynamicCast(taskCSpace)->getOriginalCSpace();
459 if (taskCSpace->ice_isA(SimoxCSpace::ice_staticId()))
463 setPaths(std::move(taskCSpaceAndPaths.paths));
475 std::unique_lock lock(*
mutex3D);
479 toClear->removeAllChildren();
487 std::unique_lock lock(*
mutex3D);
489 if (!visuRoot || !toRemove)
494 if (visuRoot->findChild(toRemove) >= 0)
496 visuRoot->removeChild(toRemove);
502 std::unique_lock lock(*
mutex3D);
504 if (!visuRoot || !toAdd)
509 if (visuRoot->findChild(toAdd) < 0)
511 visuRoot->addChild(toAdd);
517 std::unique_lock lock(*
mutex3D);
519 if (!visuRoot || !toToggle)
524 if (visuRoot->findChild(toToggle) < 0)
526 visuRoot->addChild(toToggle);
530 visuRoot->removeChild(toToggle);
536 std::unique_lock lock(*
mutex3D);
538 if (!visuRoot || !child)
545 if (visuRoot->findChild(child) < 0)
547 visuRoot->addChild(child);
552 if (!(visuRoot->findChild(child) < 0))
554 visuRoot->removeChild(child);
562 cspaceUtil = SimoxCSpacePtr::dynamicCast(newCSpace->clone());
563 cspaceVisu = SimoxCSpacePtr::dynamicCast(newCSpace->clone(
true));
564 cspaceUtil->initCollisionTest();
565 cspaceVisu->initCollisionTest();
568 const auto&& dimensions = cspaceUtil->getDimensionsBounds();
569 auto jointNames = cspaceUtil->getAgentJointNames();
570 assert(jointNames.size() == dimensions.size());
572 widget.tableWidgetDims->setRowCount(dimensions.size());
574 for (std::size_t i = 0; i < dimensions.size(); ++i)
576 const std::string& name = jointNames.at(i);
577 const FloatRange& bounds = dimensions.at(i);
579 widget.tableWidgetDims->setItem(i, 0,
new QTableWidgetItem {QString::fromStdString(name)});
581 widget.tableWidgetDims->setItem(i, 1,
new QTableWidgetItem {QString::number(bounds.min)});
582 auto spinbox =
new QDoubleSpinBox {};
583 spinbox->setRange(bounds.min, bounds.max);
584 widget.tableWidgetDims->setCellWidget(i, 2, spinbox);
585 widget.tableWidgetDims->setItem(i, 3,
new QTableWidgetItem {QString::number(bounds.max)});
586 spinbox->setSingleStep((bounds.max - bounds.min) / 10000.0);
587 connect(spinbox, SIGNAL(valueChanged(
double)), widget.pushButtonSetConfig, SIGNAL(clicked()));
590 visuObjStat.resize(cspaceVisu->getStationaryObjectSet()->getSize());
593 std::unique_lock lock(*
mutex3D);
594 auto robot = cspaceVisu->getAgentSceneObj();
595 robot->setUpdateVisualization(
true);
596 robotVisu = robot->getVisualization<VirtualRobot::CoinVisualization>();
598 auto robotCoinVisu = robotVisu->getCoinVisualization(
true);
600 visuAgent->removeAllChildren();
601 visuAgent->addChild(robotCoinVisu);
603 auto boxVis =
new QCheckBox {QString::fromStdString(robot->getName())};
604 boxVis->setChecked(
true);
607 widget.scrollAreaWidgetContentsObj->layout()->addWidget(boxVis);
612 for (
int i = 0; i < static_cast<int>(visuObjStat.size()); ++i)
614 auto sceneObj = cspaceVisu->getStationaryObjectSet()->getSceneObject(i);
616 auto& sep = visuObjStat.at(i);
618 sep =
new SoSeparator();
620 sceneObj->setUpdateVisualization(
true);
621 sceneObj->setUpdateCollisionModel(
true);
622 auto* visu =
dynamic_cast<VirtualRobot::CoinVisualizationNode*
>(sceneObj->getVisualization(VirtualRobot::SceneObject::Collision).get());
623 auto coin = visu->getCoinVisualization();
624 ARMARX_INFO << sceneObj->getName() <<
" visu: " << visu <<
" coin: " << coin;
628 auto boxVis =
new IndexedQCheckBox {i, QString::fromStdString(sceneObj->getName())};
629 boxVis->setChecked(
true);
631 widget.scrollAreaWidgetContentsObj->layout()->addWidget(boxVis);
636 auto nodes = cspaceUtil->getAgentSceneObj()->getRobotNodes();
639 nodes.begin(), nodes.end(), std::back_inserter(robotNodes),
640 [](VirtualRobot::RobotNodePtr & nptr)
642 return std::pair<VirtualRobot::RobotNodePtr, bool> {std::move(nptr), false};
647 std::set<std::string> visTcps;
648 const auto& kinematicChainNames = cspaceUtil->getAgent().kinemaicChainNames;
650 kinematicChainNames.begin(), kinematicChainNames.end(), std::inserter(visTcps, visTcps.begin()),
651 [
this](
const std::string & name)
653 return cspaceUtil->getAgentSceneObj()->getRobotNodeSet(name)->getTCP()->getName();
657 for (
int i = 0; i < static_cast<int>(robotNodes.size()); ++i)
659 const auto& node = robotNodes.at(i).first;
660 const auto& name = node->getName();
661 bool visible = visTcps.find(name) != visTcps.end();
662 robotNodes.at(i).second = visible;
664 auto boxVis =
new IndexedQCheckBox {i, QString::fromStdString(node->getName())};
665 boxVis->setChecked(visible);
666 connect(boxVis, SIGNAL(stateChangedIndex(
int, Qt::CheckState)),
this, SLOT(toggleRobotNodePath(
int, Qt::CheckState)));
667 widget.scrollAreaWidgetContentsJoints->layout()->addWidget(boxVis);
671 setEnabledSimoxCSpace();
674 void SimoxCSpaceVisualizerWidgetController::toggleRobotNodePath(
int index, Qt::CheckState)
676 robotNodes.at(
index).second ^= 1;
684 assert(paths.empty());
687 newPaths.begin(), newPaths.end(), std::back_inserter(paths),
691 dat.path = std::move(p.nodes);
694 dat.accPathLength.emplace_back(0);
695 for (std::size_t i = 1; i < dat.path.size(); ++i)
697 const auto& cfg1 = dat.path.at(i - 1);
698 const auto& cfg2 = dat.path.at(i);
700 lengthAcc += euclideanDistance(cfg1.begin(), cfg1.end(), cfg2.begin());
701 dat.accPathLength.emplace_back(lengthAcc);
708 widget.spinBoxPathNumber->setMaximum(paths.size());
710 widget.tableWidgetPaths->setRowCount(paths.size());
711 for (std::size_t i = 0; i < paths.size(); ++i)
713 const auto& p = paths.at(i);
714 widget.tableWidgetPaths->setItem(i, 1,
new QTableWidgetItem {QString::number(p.path.size())});
715 widget.tableWidgetPaths->setItem(i, 2,
new QTableWidgetItem {QString::number(p.getLength())});
717 auto boxVis =
new IndexedQCheckBox {
static_cast<int>(i), QString::fromStdString(newPaths.at(i).pathName)};
718 connect(boxVis, SIGNAL(stateChangedIndex(
int, Qt::CheckState)),
this, SLOT(setVisibilityPath(
int, Qt::CheckState)));
719 boxVis->setChecked(
true);
720 widget.tableWidgetPaths->setCellWidget(i, 0, boxVis);
723 setEnabledPaths(
true);
726 updateEdgeVisibility();
731 if (widget.tableWidgetDims->columnCount() < 2)
735 for (std::size_t i = 0; i < cfg.size() && (
int)i < widget.tableWidgetDims->rowCount(); ++i)
737 auto spinbox =
dynamic_cast<QDoubleSpinBox*
>(widget.tableWidgetDims->cellWidget(i, 2));
740 spinbox->setValue(cfg.at(i));
751 widget.labelCurrentMinObjDist->setText(
"");
752 widget.tableWidgetDims->clearContents();
753 widget.tableWidgetDims->setRowCount(0);
755 clearLayout(widget.scrollAreaWidgetContentsJoints->layout());
756 clearLayout(widget.scrollAreaWidgetContentsObj->layout());
758 for (
auto& sep : visuObjStat)
764 visuAgent->removeAllChildren();
765 debugDrawer->clearAll();
766 cspaceUtil =
nullptr;
767 cspaceVisu =
nullptr;
774 widget.tableWidgetPaths->clearContents();
775 widget.spinBoxPathNumber->setMaximum(0);
776 widget.doubleSpinBoxMovePerMs->setValue(0);
782 widget.groupBoxCurCfg->setEnabled(active);
783 widget.groupBoxVisPath->setEnabled(active);
784 widget.groupBoxVisObj->setEnabled(active);
789 widget.groupBoxPaths->setEnabled(active);
790 widget.groupBoxPathOpt->setEnabled(active);
795 const auto step = widget.doubleSpinBoxMovePerMs->value() * timerPeriod;
797 if (widget.checkBoxAnimate->isChecked() && step != 0)
799 widget.sliderPathPosition->setValue(widget.sliderPathPosition->value() + step);
813 auto robot = cspaceVisu->getAgentSceneObj();
815 std::unique_lock lock(*
mutex3D);
816 robot->setUpdateVisualization(
true);
819 std::set<std::string> nodesToHighlight;
820 for (
auto&
setName : cspaceVisu->getAgent().collisionSetNames)
823 for (
size_t i = 0; i <
set->getSize(); ++i)
825 VirtualRobot::RobotNodePtr node =
set->getNode(i);
826 nodesToHighlight.insert(node->getName());
830 for (
auto& name : nodesToHighlight)
832 auto node = robot->getRobotNode(name);
834 node->highlight(robotVisu,
enabled);