00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "rtabmap/gui/GraphViewer.h"
00029
00030 #include <QGraphicsView>
00031 #include <QVBoxLayout>
00032 #include <QGraphicsScene>
00033 #include <QGraphicsEllipseItem>
00034 #include <QtGui/QWheelEvent>
00035 #include <QGraphicsSceneHoverEvent>
00036 #include <QMenu>
00037 #include <QtGui/QDesktopServices>
00038 #include <QtGui/QContextMenuEvent>
00039 #include <QColorDialog>
00040 #include <QtSvg/QSvgGenerator>
00041 #include <QInputDialog>
00042
00043 #include <QtCore/QDir>
00044 #include <QtCore/QDateTime>
00045 #include <QtCore/QUrl>
00046
00047 #include <rtabmap/core/util3d.h>
00048 #include <rtabmap/utilite/UCv2Qt.h>
00049 #include <rtabmap/utilite/UStl.h>
00050 #include <rtabmap/utilite/ULogger.h>
00051
00052 namespace rtabmap {
00053
00054 class NodeItem: public QGraphicsEllipseItem
00055 {
00056 public:
00057
00058 NodeItem(int id, int mapId, const Transform & pose, float radius) :
00059 QGraphicsEllipseItem(QRectF(-radius,-radius,radius*2.0f,radius*2.0f)),
00060 _id(id),
00061 _mapId(mapId),
00062 _pose(pose)
00063 {
00064 this->setPos(-pose.y(),-pose.x());
00065 this->setBrush(pen().color());
00066 this->setAcceptHoverEvents(true);
00067 }
00068 virtual ~NodeItem() {}
00069
00070 void setColor(const QColor & color)
00071 {
00072 QPen p = this->pen();
00073 p.setColor(color);
00074 this->setPen(p);
00075 QBrush b = this->brush();
00076 b.setColor(color);
00077 this->setBrush(b);
00078 }
00079
00080 const Transform & pose() const {return _pose;}
00081 void setPose(const Transform & pose) {this->setPos(-pose.y(),-pose.x()); _pose=pose;}
00082
00083 protected:
00084 virtual void hoverEnterEvent ( QGraphicsSceneHoverEvent * event )
00085 {
00086 this->setToolTip(QString("%1 [%2] %3").arg(_id).arg(_mapId).arg(_pose.prettyPrint().c_str()));
00087 this->setScale(2);
00088 QGraphicsEllipseItem::hoverEnterEvent(event);
00089 }
00090
00091 virtual void hoverLeaveEvent ( QGraphicsSceneHoverEvent * event )
00092 {
00093 this->setScale(1);
00094 QGraphicsEllipseItem::hoverEnterEvent(event);
00095 }
00096
00097 private:
00098 int _id;
00099 int _mapId;
00100 Transform _pose;
00101 };
00102
00103 class LinkItem: public QGraphicsLineItem
00104 {
00105 public:
00106
00107 LinkItem(int from, int to, const Transform & poseA, const Transform & poseB, const Link & link, bool interSessionClosure) :
00108 QGraphicsLineItem(-poseA.y(), -poseA.x(), -poseB.y(), -poseB.x()),
00109 _from(from),
00110 _to(to),
00111 _poseA(poseA),
00112 _poseB(poseB),
00113 _link(link),
00114 _interSession(interSessionClosure)
00115 {
00116 this->setAcceptHoverEvents(true);
00117 }
00118 virtual ~LinkItem() {}
00119
00120 void setColor(const QColor & color)
00121 {
00122 QPen p = this->pen();
00123 p.setColor(color);
00124 this->setPen(p);
00125 }
00126
00127 void setPoses(const Transform & poseA, const Transform & poseB)
00128 {
00129 this->setLine(-poseA.y(), -poseA.x(), -poseB.y(), -poseB.x());
00130 _poseA = poseA;
00131 _poseB = poseB;
00132 }
00133
00134 const Transform & getPoseA() const
00135 {
00136 return _poseA;
00137 }
00138 const Transform & getPoseB() const
00139 {
00140 return _poseB;
00141 }
00142
00143 Link::Type linkType() const {return _link.type();}
00144 bool isInterSession() const {return _interSession;}
00145 int from() const {return _from;}
00146 int to() const {return _to;}
00147
00148 protected:
00149 virtual void hoverEnterEvent ( QGraphicsSceneHoverEvent * event )
00150 {
00151 QString str = QString("%1->%2 (%3 m)").arg(_from).arg(_to).arg(_poseA.getDistance(_poseB));
00152 if(!_link.transform().isNull())
00153 {
00154 str.append(QString("\n%1\n%2 %3").arg(_link.transform().prettyPrint().c_str()).arg(_link.transVariance()).arg(_link.rotVariance()));
00155 }
00156 this->setToolTip(str);
00157 QPen pen = this->pen();
00158 pen.setWidthF(pen.widthF()+0.02);
00159 this->setPen(pen);
00160 QGraphicsLineItem::hoverEnterEvent(event);
00161 }
00162
00163 virtual void hoverLeaveEvent ( QGraphicsSceneHoverEvent * event )
00164 {
00165 QPen pen = this->pen();
00166 pen.setWidthF(pen.widthF()-0.02);
00167 this->setPen(pen);
00168 QGraphicsLineItem::hoverEnterEvent(event);
00169 }
00170
00171 private:
00172 int _from;
00173 int _to;
00174 Transform _poseA;
00175 Transform _poseB;
00176 Link _link;
00177 bool _interSession;
00178 };
00179
00180 GraphViewer::GraphViewer(QWidget * parent) :
00181 QGraphicsView(parent),
00182 _nodeColor(Qt::blue),
00183 _currentGoalColor(Qt::darkMagenta),
00184 _neighborColor(Qt::blue),
00185 _loopClosureColor(Qt::red),
00186 _loopClosureLocalColor(Qt::yellow),
00187 _loopClosureUserColor(Qt::red),
00188 _loopClosureVirtualColor(Qt::magenta),
00189 _neighborMergedColor(QColor(255,170,0)),
00190 _loopClosureRejectedColor(Qt::black),
00191 _localPathColor(Qt::cyan),
00192 _globalPathColor(Qt::darkMagenta),
00193 _gtPathColor(Qt::gray),
00194 _loopIntraSessionColor(Qt::red),
00195 _loopInterSessionColor(Qt::green),
00196 _intraInterSessionColors(false),
00197 _root(0),
00198 _graphRoot(0),
00199 _globalPathRoot(0),
00200 _nodeRadius(0.01f),
00201 _linkWidth(0),
00202 _gridMap(0),
00203 _referential(0),
00204 _originReferential(0),
00205 _gridCellSize(0.0f),
00206 _localRadius(0),
00207 _loopClosureOutlierThr(0),
00208 _maxLinkLength(0.02f)
00209 {
00210 this->setScene(new QGraphicsScene(this));
00211 this->setDragMode(QGraphicsView::ScrollHandDrag);
00212 _workingDirectory = QDir::homePath();
00213
00214 this->scene()->clear();
00215 _root = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
00216
00217
00218 _originReferential = new QGraphicsItemGroup();
00219 this->scene()->addItem(_originReferential);
00220 QGraphicsLineItem * item = this->scene()->addLine(0,0,0,-1, QPen(QBrush(Qt::red), _linkWidth));
00221 item->setZValue(100);
00222 item->setParentItem(_root);
00223 _originReferential->addToGroup(item);
00224 item = this->scene()->addLine(0,0,-1,0, QPen(QBrush(Qt::green), _linkWidth));
00225 item->setZValue(100);
00226 item->setParentItem(_root);
00227 _originReferential->addToGroup(item);
00228
00229
00230 _referential = new QGraphicsItemGroup();
00231 this->scene()->addItem(_referential);
00232 item = this->scene()->addLine(0,0,0,-0.5, QPen(QBrush(Qt::red), _linkWidth));
00233 item->setZValue(100);
00234 item->setParentItem(_root);
00235 _referential->addToGroup(item);
00236 item = this->scene()->addLine(0,0,-0.5,0, QPen(QBrush(Qt::green), _linkWidth));
00237 item->setZValue(100);
00238 item->setParentItem(_root);
00239 _referential->addToGroup(item);
00240
00241 _localRadius = this->scene()->addEllipse(-0.0001,-0.0001,0.0001,0.0001);
00242 _localRadius->setZValue(1);
00243 _localRadius->setParentItem(_root);
00244 _localRadius->setVisible(false);
00245 _localRadius->setPen(QPen(Qt::DashLine));
00246
00247 _gridMap = this->scene()->addPixmap(QPixmap());
00248 _gridMap->setZValue(0);
00249 _gridMap->setParentItem(_root);
00250
00251 _graphRoot = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
00252 _graphRoot->setZValue(2);
00253 _graphRoot->setParentItem(_root);
00254
00255 _globalPathRoot = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
00256 _globalPathRoot->setZValue(3);
00257 _globalPathRoot->setParentItem(_root);
00258
00259 _localPathRoot = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
00260 _localPathRoot->setZValue(4);
00261 _localPathRoot->setParentItem(_root);
00262
00263 _gtGraphRoot = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
00264 _gtGraphRoot->setZValue(2);
00265 _gtGraphRoot->setParentItem(_root);
00266
00267 this->restoreDefaults();
00268
00269 this->fitInView(this->sceneRect(), Qt::KeepAspectRatio);
00270 }
00271
00272 GraphViewer::~GraphViewer()
00273 {
00274 }
00275
00276 void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
00277 const std::multimap<int, Link> & constraints,
00278 const std::map<int, int> & mapIds)
00279 {
00280 bool wasVisible = _graphRoot->isVisible();
00281 _graphRoot->show();
00282
00283 bool wasEmpty = _nodeItems.size() == 0 && _linkItems.size() == 0;
00284 UDEBUG("poses=%d constraints=%d", (int)poses.size(), (int)constraints.size());
00285
00286 for(QMap<int, NodeItem*>::iterator iter = _nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
00287 {
00288 iter.value()->hide();
00289 iter.value()->setColor(_nodeColor);
00290 }
00291 for(QMultiMap<int, LinkItem*>::iterator iter = _linkItems.begin(); iter!=_linkItems.end(); ++iter)
00292 {
00293 iter.value()->hide();
00294 }
00295
00296 for(std::map<int, Transform>::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
00297 {
00298 if(!iter->second.isNull())
00299 {
00300 QMap<int, NodeItem*>::iterator itemIter = _nodeItems.find(iter->first);
00301 if(itemIter != _nodeItems.end())
00302 {
00303 itemIter.value()->setPose(iter->second);
00304 itemIter.value()->show();
00305 }
00306 else
00307 {
00308
00309 const Transform & pose = iter->second;
00310 NodeItem * item = new NodeItem(iter->first, uContains(mapIds, iter->first)?mapIds.at(iter->first):-1, pose, _nodeRadius);
00311 this->scene()->addItem(item);
00312 item->setZValue(20);
00313 item->setColor(_nodeColor);
00314 item->setParentItem(_graphRoot);
00315 _nodeItems.insert(iter->first, item);
00316 }
00317 }
00318 }
00319
00320 for(std::multimap<int, Link>::const_iterator iter=constraints.begin(); iter!=constraints.end(); ++iter)
00321 {
00322
00323 int idFrom = iter->first<iter->second.to()?iter->first:iter->second.to();
00324 int idTo = iter->first<iter->second.to()?iter->second.to():iter->first;
00325
00326 std::map<int, Transform>::const_iterator jterA = poses.find(idFrom);
00327 std::map<int, Transform>::const_iterator jterB = poses.find(idTo);
00328 LinkItem * linkItem = 0;
00329 if(jterA != poses.end() && jterB != poses.end() &&
00330 _nodeItems.contains(iter->first) && _nodeItems.contains(idTo))
00331 {
00332 const Transform & poseA = jterA->second;
00333 const Transform & poseB = jterB->second;
00334
00335 QMultiMap<int, LinkItem*>::iterator itemIter = _linkItems.end();
00336 if(_linkItems.contains(idFrom))
00337 {
00338 itemIter = _linkItems.find(iter->first);
00339 while(itemIter.key() == idFrom && itemIter != _linkItems.end())
00340 {
00341 if(itemIter.value()->to() == idTo)
00342 {
00343 itemIter.value()->setPoses(poseA, poseB);
00344 itemIter.value()->show();
00345 linkItem = itemIter.value();
00346 break;
00347 }
00348 ++itemIter;
00349 }
00350 }
00351
00352 bool interSessionClosure = false;
00353 if(uContains(mapIds, jterA->first) && uContains(mapIds, jterB->first))
00354 {
00355 interSessionClosure = mapIds.at(jterA->first) != mapIds.at(jterB->first);
00356 }
00357
00358 if(poseA.getDistance(poseB) > _maxLinkLength)
00359 {
00360 if(linkItem == 0)
00361 {
00362
00363 linkItem = new LinkItem(idFrom, idTo, poseA, poseB, iter->second, interSessionClosure);
00364 QPen p = linkItem->pen();
00365 p.setWidthF(_linkWidth);
00366 linkItem->setPen(p);
00367 linkItem->setZValue(10);
00368 this->scene()->addItem(linkItem);
00369 linkItem->setParentItem(_graphRoot);
00370 _linkItems.insert(idFrom, linkItem);
00371 }
00372 }
00373 else if(linkItem && itemIter != _linkItems.end())
00374 {
00375
00376 _linkItems.erase(itemIter);
00377 delete linkItem;
00378 linkItem = 0;
00379 }
00380
00381 if(linkItem)
00382 {
00383
00384 if(iter->second.type() == Link::kNeighbor)
00385 {
00386 linkItem->setColor(_neighborColor);
00387 }
00388 else if(iter->second.type() == Link::kVirtualClosure)
00389 {
00390 linkItem->setColor(_loopClosureVirtualColor);
00391 }
00392 else if(iter->second.type() == Link::kNeighborMerged)
00393 {
00394 linkItem->setColor(_neighborMergedColor);
00395 }
00396 else if(iter->second.type() == Link::kUserClosure)
00397 {
00398 linkItem->setColor(_loopClosureUserColor);
00399 }
00400 else if(iter->second.type() == Link::kLocalSpaceClosure || iter->second.type() == Link::kLocalTimeClosure)
00401 {
00402 if(_intraInterSessionColors)
00403 {
00404 linkItem->setColor(interSessionClosure?_loopInterSessionColor:_loopIntraSessionColor);
00405 linkItem->setZValue(interSessionClosure?8:9);
00406 }
00407 else
00408 {
00409 linkItem->setColor(_loopClosureLocalColor);
00410 }
00411 }
00412 else
00413 {
00414 if(_intraInterSessionColors)
00415 {
00416 linkItem->setColor(interSessionClosure?_loopInterSessionColor:_loopIntraSessionColor);
00417 linkItem->setZValue(interSessionClosure?8:9);
00418 }
00419 else
00420 {
00421 linkItem->setColor(_loopClosureColor);
00422 }
00423 }
00424
00425
00426 if(_loopClosureOutlierThr > 0.0f)
00427 {
00428 Transform t = poseA.inverse()*poseB;
00429 if(iter->second.to() != idTo)
00430 {
00431 t = t.inverse();
00432 }
00433 if(iter->second.type() != Link::kNeighbor &&
00434 iter->second.type() != Link::kNeighborMerged)
00435 {
00436 float linearError = uMax3(
00437 fabs(iter->second.transform().x() - t.x()),
00438 fabs(iter->second.transform().y() - t.y()),
00439 fabs(iter->second.transform().z() - t.z()));
00440 if(linearError > _loopClosureOutlierThr)
00441 {
00442 linkItem->setColor(_loopClosureRejectedColor);
00443 }
00444 }
00445 }
00446 }
00447 }
00448 }
00449
00450
00451 for(QMap<int, NodeItem*>::iterator iter = _nodeItems.begin(); iter!=_nodeItems.end();)
00452 {
00453 if(!iter.value()->isVisible())
00454 {
00455 delete iter.value();
00456 iter = _nodeItems.erase(iter);
00457 }
00458 else
00459 {
00460 ++iter;
00461 }
00462 }
00463 for(QMultiMap<int, LinkItem*>::iterator iter = _linkItems.begin(); iter!=_linkItems.end();)
00464 {
00465 if(!iter.value()->isVisible())
00466 {
00467 delete iter.value();
00468 iter = _linkItems.erase(iter);
00469 }
00470 else
00471 {
00472 ++iter;
00473 }
00474 }
00475
00476 if(_nodeItems.size())
00477 {
00478 (--_nodeItems.end()).value()->setColor(Qt::green);
00479 }
00480
00481 this->scene()->setSceneRect(this->scene()->itemsBoundingRect());
00482
00483 if(wasEmpty)
00484 {
00485 QRectF rect = this->scene()->itemsBoundingRect();
00486 this->fitInView(rect.adjusted(-rect.width()/2.0f, -rect.height()/2.0f, rect.width()/2.0f, rect.height()/2.0f), Qt::KeepAspectRatio);
00487 }
00488
00489 _graphRoot->setVisible(wasVisible);
00490
00491 UDEBUG("_nodeItems=%d, _linkItems=%d", _nodeItems.size(), _linkItems.size());
00492 }
00493
00494 void GraphViewer::updateGTGraph(const std::map<int, Transform> & poses)
00495 {
00496 bool wasVisible = _gtGraphRoot->isVisible();
00497 _gtGraphRoot->show();
00498 bool wasEmpty = _gtNodeItems.size() == 0 && _gtLinkItems.size() == 0;
00499 UDEBUG("poses=%d", (int)poses.size());
00500
00501 for(QMap<int, NodeItem*>::iterator iter = _gtNodeItems.begin(); iter!=_gtNodeItems.end(); ++iter)
00502 {
00503 iter.value()->hide();
00504 iter.value()->setColor(_gtPathColor);
00505 }
00506 for(QMultiMap<int, LinkItem*>::iterator iter = _gtLinkItems.begin(); iter!=_gtLinkItems.end(); ++iter)
00507 {
00508 iter.value()->hide();
00509 }
00510
00511 for(std::map<int, Transform>::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
00512 {
00513 if(!iter->second.isNull())
00514 {
00515 QMap<int, NodeItem*>::iterator itemIter = _gtNodeItems.find(iter->first);
00516 if(itemIter != _gtNodeItems.end())
00517 {
00518 itemIter.value()->setPose(iter->second);
00519 itemIter.value()->show();
00520 }
00521 else
00522 {
00523
00524 const Transform & pose = iter->second;
00525 NodeItem * item = new NodeItem(iter->first, -1, pose, _nodeRadius);
00526 this->scene()->addItem(item);
00527 item->setZValue(20);
00528 item->setColor(_gtPathColor);
00529 item->setParentItem(_gtGraphRoot);
00530 _gtNodeItems.insert(iter->first, item);
00531 }
00532
00533 if(iter!=poses.begin())
00534 {
00535 std::map<int, Transform>::const_iterator iterPrevious = iter;
00536 --iterPrevious;
00537 Transform previousPose = iterPrevious->second;
00538 Transform currentPose = iter->second;
00539
00540 LinkItem * linkItem = 0;
00541 QMultiMap<int, LinkItem*>::iterator linkIter = _gtLinkItems.end();
00542 if(_gtLinkItems.contains(iterPrevious->first))
00543 {
00544 linkIter = _gtLinkItems.find(iter->first);
00545 while(linkIter.key() == iterPrevious->first && linkIter != _gtLinkItems.end())
00546 {
00547 if(linkIter.value()->to() == iter->first)
00548 {
00549 linkIter.value()->setPoses(previousPose, currentPose);
00550 linkIter.value()->show();
00551 linkItem = linkIter.value();
00552 break;
00553 }
00554 ++linkIter;
00555 }
00556 }
00557 if(linkItem == 0)
00558 {
00559
00560 linkItem = new LinkItem(iterPrevious->first, iter->first, previousPose, currentPose, Link(), 1);
00561 QPen p = linkItem->pen();
00562 p.setWidthF(_linkWidth);
00563 linkItem->setPen(p);
00564 linkItem->setZValue(10);
00565 this->scene()->addItem(linkItem);
00566 linkItem->setParentItem(_gtGraphRoot);
00567 _gtLinkItems.insert(iterPrevious->first, linkItem);
00568 }
00569 if(linkItem)
00570 {
00571 linkItem->setColor(_gtPathColor);
00572 }
00573 }
00574 }
00575 }
00576
00577
00578 for(QMap<int, NodeItem*>::iterator iter = _gtNodeItems.begin(); iter!=_gtNodeItems.end();)
00579 {
00580 if(!iter.value()->isVisible())
00581 {
00582 delete iter.value();
00583 iter = _gtNodeItems.erase(iter);
00584 }
00585 else
00586 {
00587 ++iter;
00588 }
00589 }
00590 for(QMultiMap<int, LinkItem*>::iterator iter = _gtLinkItems.begin(); iter!=_gtLinkItems.end();)
00591 {
00592 if(!iter.value()->isVisible())
00593 {
00594 delete iter.value();
00595 iter = _gtLinkItems.erase(iter);
00596 }
00597 else
00598 {
00599 ++iter;
00600 }
00601 }
00602
00603 if(_gtNodeItems.size() || _gtLinkItems.size())
00604 {
00605 this->scene()->setSceneRect(this->scene()->itemsBoundingRect());
00606
00607 if(wasEmpty)
00608 {
00609 QRectF rect = this->scene()->itemsBoundingRect();
00610 this->fitInView(rect.adjusted(-rect.width()/2.0f, -rect.height()/2.0f, rect.width()/2.0f, rect.height()/2.0f), Qt::KeepAspectRatio);
00611 }
00612 }
00613
00614 _gtGraphRoot->setVisible(wasVisible);
00615
00616 UDEBUG("_gtNodeItems=%d, _gtLinkItems=%d", _gtNodeItems.size(), _gtLinkItems.size());
00617 }
00618
00619 void GraphViewer::updateReferentialPosition(const Transform & t)
00620 {
00621 QTransform qt(t.r11(), t.r12(), t.r21(), t.r22(), -t.o24(), -t.o14());
00622 _referential->setTransform(qt);
00623 _localRadius->setTransform(qt);
00624
00625 this->ensureVisible(_referential);
00626 if(_localRadius->isVisible())
00627 {
00628 this->ensureVisible(_localRadius, 0, 0);
00629 }
00630 }
00631
00632 void GraphViewer::updateMap(const cv::Mat & map8U, float resolution, float xMin, float yMin)
00633 {
00634 UASSERT(map8U.empty() || (!map8U.empty() && resolution > 0.0f));
00635 if(!map8U.empty())
00636 {
00637 _gridCellSize = resolution;
00638 QImage image = uCvMat2QImage(map8U, false);
00639 _gridMap->resetTransform();
00640 _gridMap->setTransform(QTransform::fromScale(resolution, -resolution), true);
00641 _gridMap->setRotation(90);
00642 _gridMap->setPixmap(QPixmap::fromImage(image));
00643 _gridMap->setPos(-yMin, -xMin);
00644 this->scene()->setSceneRect(this->scene()->itemsBoundingRect());
00645 }
00646 else
00647 {
00648 this->clearMap();
00649 }
00650 }
00651
00652 void GraphViewer::updatePosterior(const std::map<int, float> & posterior)
00653 {
00654
00655 float max = 0.0f;
00656 for(std::map<int, float>::const_iterator iter = posterior.begin(); iter!=posterior.end(); ++iter)
00657 {
00658 if(iter->first > 0 && iter->second>max)
00659 {
00660 max = iter->second;
00661 }
00662 }
00663 if(max > 0.0f)
00664 {
00665 for(QMap<int, NodeItem*>::iterator iter = _nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
00666 {
00667 std::map<int,float>::const_iterator jter = posterior.find(iter.key());
00668 if(jter != posterior.end())
00669 {
00670 UDEBUG("id=%d max=%f hyp=%f color = %f", iter.key(), max, jter->second, (1-jter->second/max)*240.0f/360.0f);
00671 iter.value()->setColor(QColor::fromHsvF((1-jter->second/max)*240.0f/360.0f, 1, 1, 1));
00672 }
00673 else
00674 {
00675 iter.value()->setColor(QColor::fromHsvF(240.0f/360.0f, 1, 1, 1));
00676 }
00677 }
00678 }
00679 }
00680
00681 void GraphViewer::setGlobalPath(const std::vector<std::pair<int, Transform> > & globalPath)
00682 {
00683 UDEBUG("Set global path size=%d", (int)globalPath.size());
00684 qDeleteAll(_globalPathLinkItems);
00685 _globalPathLinkItems.clear();
00686
00687 if(globalPath.size() >= 2)
00688 {
00689 for(unsigned int i=0; i<globalPath.size()-1; ++i)
00690 {
00691
00692 int idFrom = globalPath[i].first;
00693 int idTo = globalPath[i+1].first;
00694 LinkItem * item = new LinkItem(idFrom, idTo, globalPath[i].second, globalPath[i+1].second, Link(), false);
00695 QPen p = item->pen();
00696 p.setWidthF(_linkWidth);
00697 item->setPen(p);
00698 item->setColor(_globalPathColor);
00699 this->scene()->addItem(item);
00700 item->setZValue(15);
00701 item->setParentItem(_globalPathRoot);
00702 _globalPathLinkItems.insert(idFrom, item);
00703 }
00704 }
00705 }
00706
00707 void GraphViewer::setCurrentGoalID(int id, const Transform & pose)
00708 {
00709 NodeItem * node = _nodeItems.value(id, 0);
00710 if(node)
00711 {
00712 node->setColor(_currentGoalColor);
00713 }
00714 else
00715 {
00716 UWARN("Current goal %d not found in the graph", id);
00717 }
00718
00719 if(!pose.isNull() && _globalPathLinkItems.size() && _globalPathLinkItems.contains(id))
00720 {
00721
00722 const LinkItem * oldPose = _globalPathLinkItems.value(id);
00723 Transform t = pose * oldPose->getPoseA().inverse();
00724 for(QMultiMap<int, LinkItem*>::iterator iter=_globalPathLinkItems.begin(); iter!=_globalPathLinkItems.end(); ++iter)
00725 {
00726 iter.value()->setPoses(t*iter.value()->getPoseA(), t*iter.value()->getPoseB());
00727 }
00728 }
00729 }
00730
00731 void GraphViewer::setLocalRadius(float radius)
00732 {
00733 _localRadius->setRect(-radius, -radius, radius*2, radius*2);
00734 }
00735
00736 void GraphViewer::updateLocalPath(const std::vector<int> & localPath)
00737 {
00738 bool wasVisible = _localPathRoot->isVisible();
00739 _localPathRoot->show();
00740
00741 for(QMultiMap<int, LinkItem*>::iterator iter = _localPathLinkItems.begin(); iter!=_localPathLinkItems.end(); ++iter)
00742 {
00743 iter.value()->hide();
00744 }
00745
00746 if(localPath.size() > 1)
00747 {
00748 for(unsigned int i=0; i<localPath.size()-1; ++i)
00749 {
00750 int idFrom = localPath[i]<localPath[i+1]?localPath[i]:localPath[i+1];
00751 int idTo = localPath[i]<localPath[i+1]?localPath[i+1]:localPath[i];
00752 if(_nodeItems.contains(idFrom) && _nodeItems.contains(idTo))
00753 {
00754 bool updated = false;
00755 if(_localPathLinkItems.contains(idFrom))
00756 {
00757 QMultiMap<int, LinkItem*>::iterator itemIter = _localPathLinkItems.find(idFrom);
00758 while(itemIter.key() == idFrom && itemIter != _localPathLinkItems.end())
00759 {
00760 if(itemIter.value()->to() == idTo)
00761 {
00762 itemIter.value()->setPoses(_nodeItems.value(idFrom)->pose(), _nodeItems.value(idTo)->pose());
00763 itemIter.value()->show();
00764 updated = true;
00765 break;
00766 }
00767 ++itemIter;
00768 }
00769 }
00770 if(!updated)
00771 {
00772
00773 LinkItem * item = new LinkItem(idFrom, idTo, _nodeItems.value(idFrom)->pose(), _nodeItems.value(idTo)->pose(), Link(), false);
00774 QPen p = item->pen();
00775 p.setWidthF(_linkWidth);
00776 item->setPen(p);
00777 item->setColor(_localPathColor);
00778 this->scene()->addItem(item);
00779 item->setZValue(16);
00780 item->setParentItem(_localPathRoot);
00781 _localPathLinkItems.insert(idFrom, item);
00782 }
00783 }
00784 }
00785 }
00786
00787
00788 for(QMultiMap<int, LinkItem*>::iterator iter = _localPathLinkItems.begin(); iter!=_localPathLinkItems.end();)
00789 {
00790 if(!iter.value()->isVisible())
00791 {
00792 delete iter.value();
00793 iter = _localPathLinkItems.erase(iter);
00794 }
00795 else
00796 {
00797 ++iter;
00798 }
00799 }
00800 _localPathRoot->setVisible(wasVisible);
00801 }
00802
00803 void GraphViewer::clearGraph()
00804 {
00805 qDeleteAll(_nodeItems);
00806 _nodeItems.clear();
00807 qDeleteAll(_linkItems);
00808 _linkItems.clear();
00809 qDeleteAll(_localPathLinkItems);
00810 _localPathLinkItems.clear();
00811 qDeleteAll(_globalPathLinkItems);
00812 _globalPathLinkItems.clear();
00813 qDeleteAll(_gtNodeItems);
00814 _gtNodeItems.clear();
00815 qDeleteAll(_gtLinkItems);
00816 _gtLinkItems.clear();
00817
00818 _referential->resetTransform();
00819 _localRadius->resetTransform();
00820 this->scene()->setSceneRect(this->scene()->itemsBoundingRect());
00821 }
00822
00823 void GraphViewer::clearMap()
00824 {
00825 _gridMap->setPixmap(QPixmap());
00826 _gridCellSize = 0.0f;
00827 this->scene()->setSceneRect(this->scene()->itemsBoundingRect());
00828 }
00829
00830 void GraphViewer::clearPosterior()
00831 {
00832 for(QMap<int, NodeItem*>::iterator iter = _nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
00833 {
00834 iter.value()->setColor(Qt::blue);
00835 }
00836 }
00837
00838 void GraphViewer::clearAll()
00839 {
00840 clearMap();
00841 clearGraph();
00842 }
00843
00844 void GraphViewer::saveSettings(QSettings & settings, const QString & group) const
00845 {
00846 if(!group.isEmpty())
00847 {
00848 settings.beginGroup(group);
00849 }
00850 settings.setValue("node_radius", (double)this->getNodeRadius());
00851 settings.setValue("link_width", (double)this->getLinkWidth());
00852 settings.setValue("node_color", this->getNodeColor());
00853 settings.setValue("current_goal_color", this->getCurrentGoalColor());
00854 settings.setValue("neighbor_color", this->getNeighborColor());
00855 settings.setValue("global_color", this->getGlobalLoopClosureColor());
00856 settings.setValue("local_color", this->getLocalLoopClosureColor());
00857 settings.setValue("user_color", this->getUserLoopClosureColor());
00858 settings.setValue("virtual_color", this->getVirtualLoopClosureColor());
00859 settings.setValue("neighbor_merged_color", this->getNeighborMergedColor());
00860 settings.setValue("rejected_color", this->getRejectedLoopClosureColor());
00861 settings.setValue("local_path_color", this->getLocalPathColor());
00862 settings.setValue("global_path_color", this->getGlobalPathColor());
00863 settings.setValue("gt_color", this->getGTColor());
00864 settings.setValue("intra_session_color", this->getIntraSessionLoopColor());
00865 settings.setValue("inter_session_color", this->getInterSessionLoopColor());
00866 settings.setValue("intra_inter_session_colors_enabled", this->isIntraInterSessionColorsEnabled());
00867 settings.setValue("grid_visible", this->isGridMapVisible());
00868 settings.setValue("origin_visible", this->isOriginVisible());
00869 settings.setValue("referential_visible", this->isReferentialVisible());
00870 settings.setValue("local_radius_visible", this->isLocalRadiusVisible());
00871 settings.setValue("loop_closure_outlier_thr", this->getLoopClosureOutlierThr());
00872 settings.setValue("max_link_length", this->getMaxLinkLength());
00873 settings.setValue("graph_visible", this->isGraphVisible());
00874 settings.setValue("global_path_visible", this->isGlobalPathVisible());
00875 settings.setValue("local_path_visible", this->isLocalPathVisible());
00876 settings.setValue("gt_graph_visible", this->isGtGraphVisible());
00877 if(!group.isEmpty())
00878 {
00879 settings.endGroup();
00880 }
00881 }
00882
00883 void GraphViewer::loadSettings(QSettings & settings, const QString & group)
00884 {
00885 if(!group.isEmpty())
00886 {
00887 settings.beginGroup(group);
00888 }
00889 this->setNodeRadius(settings.value("node_radius", this->getNodeRadius()).toDouble());
00890 this->setLinkWidth(settings.value("link_width", this->getLinkWidth()).toDouble());
00891 this->setNodeColor(settings.value("node_color", this->getNodeColor()).value<QColor>());
00892 this->setCurrentGoalColor(settings.value("current_goal_color", this->getCurrentGoalColor()).value<QColor>());
00893 this->setNeighborColor(settings.value("neighbor_color", this->getNeighborColor()).value<QColor>());
00894 this->setGlobalLoopClosureColor(settings.value("global_color", this->getGlobalLoopClosureColor()).value<QColor>());
00895 this->setLocalLoopClosureColor(settings.value("local_color", this->getLocalLoopClosureColor()).value<QColor>());
00896 this->setUserLoopClosureColor(settings.value("user_color", this->getUserLoopClosureColor()).value<QColor>());
00897 this->setVirtualLoopClosureColor(settings.value("virtual_color", this->getVirtualLoopClosureColor()).value<QColor>());
00898 this->setNeighborMergedColor(settings.value("neighbor_merged_color", this->getNeighborMergedColor()).value<QColor>());
00899 this->setRejectedLoopClosureColor(settings.value("rejected_color", this->getRejectedLoopClosureColor()).value<QColor>());
00900 this->setLocalPathColor(settings.value("local_path_color", this->getLocalPathColor()).value<QColor>());
00901 this->setGlobalPathColor(settings.value("global_path_color", this->getGlobalPathColor()).value<QColor>());
00902 this->setGTColor(settings.value("gt_color", this->getGTColor()).value<QColor>());
00903 this->setIntraSessionLoopColor(settings.value("intra_session_color", this->getIntraSessionLoopColor()).value<QColor>());
00904 this->setInterSessionLoopColor(settings.value("inter_session_color", this->getInterSessionLoopColor()).value<QColor>());
00905 this->setGridMapVisible(settings.value("grid_visible", this->isGridMapVisible()).toBool());
00906 this->setOriginVisible(settings.value("origin_visible", this->isOriginVisible()).toBool());
00907 this->setReferentialVisible(settings.value("referential_visible", this->isReferentialVisible()).toBool());
00908 this->setLocalRadiusVisible(settings.value("local_radius_visible", this->isLocalRadiusVisible()).toBool());
00909 this->setIntraInterSessionColorsEnabled(settings.value("intra_inter_session_colors_enabled", this->isIntraInterSessionColorsEnabled()).toBool());
00910 this->setLoopClosureOutlierThr(settings.value("loop_closure_outlier_thr", this->getLoopClosureOutlierThr()).toDouble());
00911 this->setMaxLinkLength(settings.value("max_link_length", this->getMaxLinkLength()).toDouble());
00912 this->setGraphVisible(settings.value("graph_visible", this->isGraphVisible()).toBool());
00913 this->setGlobalPathVisible(settings.value("global_path_visible", this->isGlobalPathVisible()).toBool());
00914 this->setLocalPathVisible(settings.value("local_path_visible", this->isLocalPathVisible()).toBool());
00915 this->setGtGraphVisible(settings.value("gt_graph_visible", this->isGtGraphVisible()).toBool());
00916 if(!group.isEmpty())
00917 {
00918 settings.endGroup();
00919 }
00920 }
00921
00922 bool GraphViewer::isGridMapVisible() const
00923 {
00924 return _gridMap->isVisible();
00925 }
00926 bool GraphViewer::isOriginVisible() const
00927 {
00928 return _originReferential->isVisible();
00929 }
00930 bool GraphViewer::isReferentialVisible() const
00931 {
00932 return _referential->isVisible();
00933 }
00934 bool GraphViewer::isLocalRadiusVisible() const
00935 {
00936 return _localRadius->isVisible();
00937 }
00938 bool GraphViewer::isGraphVisible() const
00939 {
00940 return _graphRoot->isVisible();
00941 }
00942 bool GraphViewer::isGlobalPathVisible() const
00943 {
00944 return _globalPathRoot->isVisible();
00945 }
00946 bool GraphViewer::isLocalPathVisible() const
00947 {
00948 return _localPathRoot->isVisible();
00949 }
00950 bool GraphViewer::isGtGraphVisible() const
00951 {
00952 return _gtGraphRoot->isVisible();
00953 }
00954
00955 void GraphViewer::setWorkingDirectory(const QString & path)
00956 {
00957 _workingDirectory = path;
00958 }
00959 void GraphViewer::setNodeRadius(float radius)
00960 {
00961 _nodeRadius = radius;
00962 for(QMap<int, NodeItem*>::iterator iter=_nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
00963 {
00964 iter.value()->setRect(-_nodeRadius, -_nodeRadius, _nodeRadius*2.0f, _nodeRadius*2.0f);
00965 }
00966 for(QMap<int, NodeItem*>::iterator iter=_gtNodeItems.begin(); iter!=_gtNodeItems.end(); ++iter)
00967 {
00968 iter.value()->setRect(-_nodeRadius, -_nodeRadius, _nodeRadius*2.0f, _nodeRadius*2.0f);
00969 }
00970 }
00971 void GraphViewer::setLinkWidth(float width)
00972 {
00973 _linkWidth = width;
00974 QList<QGraphicsItem*> items = this->scene()->items();
00975 for(int i=0; i<items.size(); ++i)
00976 {
00977 QGraphicsLineItem * line = qgraphicsitem_cast<QGraphicsLineItem *>(items[i]);
00978 if(line)
00979 {
00980 QPen pen = line->pen();
00981 pen.setWidthF(_linkWidth);
00982 line->setPen(pen);
00983 }
00984 }
00985 }
00986 void GraphViewer::setNodeColor(const QColor & color)
00987 {
00988 _nodeColor = color;
00989 for(QMap<int, NodeItem*>::iterator iter=_nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
00990 {
00991 iter.value()->setColor(_nodeColor);
00992 }
00993 }
00994 void GraphViewer::setCurrentGoalColor(const QColor & color)
00995 {
00996 _currentGoalColor = color;
00997 }
00998 void GraphViewer::setNeighborColor(const QColor & color)
00999 {
01000 _neighborColor = color;
01001 for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
01002 {
01003 if(iter.value()->linkType() == Link::kNeighbor)
01004 {
01005 iter.value()->setColor(_neighborColor);
01006 }
01007 }
01008 }
01009 void GraphViewer::setGlobalLoopClosureColor(const QColor & color)
01010 {
01011 _loopClosureColor = color;
01012 if(!_intraInterSessionColors)
01013 {
01014 for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
01015 {
01016 if(iter.value()->linkType() == Link::kGlobalClosure)
01017 {
01018 iter.value()->setColor(_loopClosureColor);
01019 iter.value()->setZValue(10);
01020 }
01021 }
01022 }
01023 }
01024 void GraphViewer::setLocalLoopClosureColor(const QColor & color)
01025 {
01026 _loopClosureLocalColor = color;
01027 if(!_intraInterSessionColors)
01028 {
01029 for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
01030 {
01031 if(iter.value()->linkType() == Link::kLocalSpaceClosure ||
01032 iter.value()->linkType() == Link::kLocalTimeClosure)
01033 {
01034 iter.value()->setColor(_loopClosureLocalColor);
01035 iter.value()->setZValue(10);
01036 }
01037 }
01038 }
01039 }
01040 void GraphViewer::setUserLoopClosureColor(const QColor & color)
01041 {
01042 _loopClosureUserColor = color;
01043 for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
01044 {
01045 if(iter.value()->linkType() == Link::kUserClosure)
01046 {
01047 iter.value()->setColor(_loopClosureUserColor);
01048 }
01049 }
01050 }
01051 void GraphViewer::setVirtualLoopClosureColor(const QColor & color)
01052 {
01053 _loopClosureVirtualColor = color;
01054 for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
01055 {
01056 if(iter.value()->linkType() == Link::kVirtualClosure)
01057 {
01058 iter.value()->setColor(_loopClosureVirtualColor);
01059 }
01060 }
01061 }
01062 void GraphViewer::setNeighborMergedColor(const QColor & color)
01063 {
01064 _neighborMergedColor = color;
01065 for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
01066 {
01067 if(iter.value()->linkType() == Link::kNeighborMerged)
01068 {
01069 iter.value()->setColor(_neighborMergedColor);
01070 }
01071 }
01072 }
01073 void GraphViewer::setRejectedLoopClosureColor(const QColor & color)
01074 {
01075 _loopClosureRejectedColor = color;
01076 }
01077 void GraphViewer::setLocalPathColor(const QColor & color)
01078 {
01079 _localPathColor = color;
01080 }
01081 void GraphViewer::setGlobalPathColor(const QColor & color)
01082 {
01083 _globalPathColor = color;
01084 }
01085 void GraphViewer::setGTColor(const QColor & color)
01086 {
01087 _gtPathColor = color;
01088 for(QMap<int, NodeItem*>::iterator iter=_gtNodeItems.begin(); iter!=_gtNodeItems.end(); ++iter)
01089 {
01090 iter.value()->setColor(_gtPathColor);
01091 }
01092 for(QMultiMap<int, LinkItem*>::iterator iter=_gtLinkItems.begin(); iter!=_gtLinkItems.end(); ++iter)
01093 {
01094 iter.value()->setColor(_gtPathColor);
01095 }
01096 }
01097 void GraphViewer::setIntraSessionLoopColor(const QColor & color)
01098 {
01099 _loopIntraSessionColor = color;
01100 if(_intraInterSessionColors)
01101 {
01102 for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
01103 {
01104 if((iter.value()->linkType() == Link::kGlobalClosure ||
01105 iter.value()->linkType() == Link::kLocalSpaceClosure ||
01106 iter.value()->linkType() == Link::kLocalTimeClosure) &&
01107 !iter.value()->isInterSession())
01108 {
01109 iter.value()->setColor(_loopIntraSessionColor);
01110 iter.value()->setZValue(9);
01111 }
01112 }
01113 }
01114 }
01115 void GraphViewer::setInterSessionLoopColor(const QColor & color)
01116 {
01117 _loopInterSessionColor = color;
01118 if(_intraInterSessionColors)
01119 {
01120 for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
01121 {
01122 if((iter.value()->linkType() == Link::kGlobalClosure ||
01123 iter.value()->linkType() == Link::kLocalSpaceClosure ||
01124 iter.value()->linkType() == Link::kLocalTimeClosure) &&
01125 iter.value()->isInterSession())
01126 {
01127 iter.value()->setColor(_loopInterSessionColor);
01128 iter.value()->setZValue(8);
01129 }
01130 }
01131 }
01132 }
01133
01134 void GraphViewer::setIntraInterSessionColorsEnabled(bool enabled)
01135 {
01136 _intraInterSessionColors = enabled;
01137 if(_intraInterSessionColors)
01138 {
01139 this->setIntraSessionLoopColor(_loopIntraSessionColor);
01140 this->setInterSessionLoopColor(_loopInterSessionColor);
01141 }
01142 else
01143 {
01144 this->setGlobalLoopClosureColor(_loopClosureColor);
01145 this->setLocalLoopClosureColor(_loopClosureLocalColor);
01146 }
01147 }
01148
01149 void GraphViewer::setGridMapVisible(bool visible)
01150 {
01151 _gridMap->setVisible(visible);
01152 }
01153 void GraphViewer::setOriginVisible(bool visible)
01154 {
01155 _originReferential->setVisible(visible);
01156 }
01157 void GraphViewer::setReferentialVisible(bool visible)
01158 {
01159 _referential->setVisible(visible);
01160 }
01161 void GraphViewer::setLocalRadiusVisible(bool visible)
01162 {
01163 _localRadius->setVisible(visible);
01164 }
01165 void GraphViewer::setLoopClosureOutlierThr(float value)
01166 {
01167 _loopClosureOutlierThr = value;
01168 }
01169 void GraphViewer::setMaxLinkLength(float value)
01170 {
01171 _maxLinkLength = value;
01172 }
01173 void GraphViewer::setGraphVisible(bool visible)
01174 {
01175 _graphRoot->setVisible(!_graphRoot->isVisible());
01176 }
01177 void GraphViewer::setGlobalPathVisible(bool visible)
01178 {
01179 _globalPathRoot->setVisible(!_globalPathRoot->isVisible());
01180 }
01181 void GraphViewer::setLocalPathVisible(bool visible)
01182 {
01183 _localPathRoot->setVisible(!_localPathRoot->isVisible());
01184 }
01185 void GraphViewer::setGtGraphVisible(bool visible)
01186 {
01187 _gtGraphRoot->setVisible(!_gtGraphRoot->isVisible());
01188 }
01189
01190 void GraphViewer::restoreDefaults()
01191 {
01192 setNodeRadius(0.01f);
01193 setLinkWidth(0.0f);
01194 setNodeColor(Qt::blue);
01195 setNeighborColor(Qt::blue);
01196 setGlobalLoopClosureColor(Qt::red);
01197 setLocalLoopClosureColor(Qt::yellow);
01198 setUserLoopClosureColor(Qt::red);
01199 setVirtualLoopClosureColor(Qt::magenta);
01200 setNeighborMergedColor(QColor(255,170,0));
01201 setGridMapVisible(true);
01202 setGraphVisible(true);
01203 setGlobalPathVisible(true);
01204 setLocalPathVisible(true);
01205 setGtGraphVisible(true);
01206 }
01207
01208 void GraphViewer::wheelEvent ( QWheelEvent * event )
01209 {
01210 if(event->delta() < 0)
01211 {
01212 this->scale(0.95, 0.95);
01213 }
01214 else
01215 {
01216 this->scale(1.05, 1.05);
01217 }
01218 }
01219
01220 QIcon createIcon(const QColor & color)
01221 {
01222 QPixmap pixmap(50, 50);
01223 pixmap.fill(color);
01224 return QIcon(pixmap);
01225 }
01226
01227 void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
01228 {
01229 QMenu menu;
01230 QAction * aScreenShotPNG = menu.addAction(tr("Take a screenshot (PNG)"));
01231 QAction * aScreenShotSVG = menu.addAction(tr("Take a screenshot (SVG)"));
01232 menu.addSeparator();
01233
01234 QAction * aChangeNodeColor = menu.addAction(createIcon(_nodeColor), tr("Set node color..."));
01235 QAction * aChangeCurrentGoalColor = menu.addAction(createIcon(_currentGoalColor), tr("Set current goal color..."));
01236 aChangeNodeColor->setIconVisibleInMenu(true);
01237 aChangeCurrentGoalColor->setIconVisibleInMenu(true);
01238
01239
01240 QMenu * menuLink = menu.addMenu(tr("Set link color..."));
01241 QAction * aChangeNeighborColor = menuLink->addAction(tr("Neighbor"));
01242 QAction * aChangeGlobalLoopColor = menuLink->addAction(tr("Global loop closure"));
01243 QAction * aChangeLocalLoopColor = menuLink->addAction(tr("Local loop closure"));
01244 QAction * aChangeUserLoopColor = menuLink->addAction(tr("User loop closure"));
01245 QAction * aChangeVirtualLoopColor = menuLink->addAction(tr("Virtual loop closure"));
01246 QAction * aChangeNeighborMergedColor = menuLink->addAction(tr("Neighbor merged"));
01247 QAction * aChangeRejectedLoopColor = menuLink->addAction(tr("Outlier loop closure"));
01248 QAction * aChangeRejectedLoopThr = menuLink->addAction(tr("Set outlier threshold..."));
01249 QAction * aChangeLocalPathColor = menuLink->addAction(tr("Local path"));
01250 QAction * aChangeGlobalPathColor = menuLink->addAction(tr("Global path"));
01251 QAction * aChangeGTColor = menuLink->addAction(tr("Ground truth"));
01252 menuLink->addSeparator();
01253 QAction * aSetIntraInterSessionColors = menuLink->addAction(tr("Enable intra/inter-session colors"));
01254 QAction * aChangeIntraSessionLoopColor = menuLink->addAction(tr("Intra-session loop closure"));
01255 QAction * aChangeInterSessionLoopColor = menuLink->addAction(tr("Inter-session loop closure"));
01256 aChangeNeighborColor->setIcon(createIcon(_neighborColor));
01257 aChangeGlobalLoopColor->setIcon(createIcon(_loopClosureColor));
01258 aChangeLocalLoopColor->setIcon(createIcon(_loopClosureLocalColor));
01259 aChangeUserLoopColor->setIcon(createIcon(_loopClosureUserColor));
01260 aChangeVirtualLoopColor->setIcon(createIcon(_loopClosureVirtualColor));
01261 aChangeNeighborMergedColor->setIcon(createIcon(_neighborMergedColor));
01262 aChangeRejectedLoopColor->setIcon(createIcon(_loopClosureRejectedColor));
01263 aChangeLocalPathColor->setIcon(createIcon(_localPathColor));
01264 aChangeGlobalPathColor->setIcon(createIcon(_globalPathColor));
01265 aChangeGTColor->setIcon(createIcon(_gtPathColor));
01266 aChangeIntraSessionLoopColor->setIcon(createIcon(_loopIntraSessionColor));
01267 aChangeInterSessionLoopColor->setIcon(createIcon(_loopInterSessionColor));
01268 aChangeNeighborColor->setIconVisibleInMenu(true);
01269 aChangeGlobalLoopColor->setIconVisibleInMenu(true);
01270 aChangeLocalLoopColor->setIconVisibleInMenu(true);
01271 aChangeUserLoopColor->setIconVisibleInMenu(true);
01272 aChangeVirtualLoopColor->setIconVisibleInMenu(true);
01273 aChangeNeighborMergedColor->setIconVisibleInMenu(true);
01274 aChangeRejectedLoopColor->setIconVisibleInMenu(true);
01275 aChangeLocalPathColor->setIconVisibleInMenu(true);
01276 aChangeGlobalPathColor->setIconVisibleInMenu(true);
01277 aChangeGTColor->setIconVisibleInMenu(true);
01278 aChangeIntraSessionLoopColor->setIconVisibleInMenu(true);
01279 aChangeInterSessionLoopColor->setIconVisibleInMenu(true);
01280 aSetIntraInterSessionColors->setCheckable(true);
01281 aSetIntraInterSessionColors->setChecked(_intraInterSessionColors);
01282
01283 menu.addSeparator();
01284 QAction * aSetNodeSize = menu.addAction(tr("Set node radius..."));
01285 QAction * aSetLinkSize = menu.addAction(tr("Set link width..."));
01286 QAction * aChangeMaxLinkLength = menu.addAction(tr("Set maximum link length..."));
01287 menu.addSeparator();
01288 QAction * aShowHideGridMap;
01289 QAction * aShowHideGraph;
01290 QAction * aShowHideOrigin;
01291 QAction * aShowHideReferential;
01292 QAction * aShowHideLocalRadius;
01293 QAction * aShowHideGlobalPath;
01294 QAction * aShowHideLocalPath;
01295 QAction * aShowHideGtGraph;
01296 if(_gridMap->isVisible())
01297 {
01298 aShowHideGridMap = menu.addAction(tr("Hide grid map"));
01299 }
01300 else
01301 {
01302 aShowHideGridMap = menu.addAction(tr("Show grid map"));
01303 }
01304 if(_originReferential->isVisible())
01305 {
01306 aShowHideOrigin = menu.addAction(tr("Hide origin referential"));
01307 }
01308 else
01309 {
01310 aShowHideOrigin = menu.addAction(tr("Show origin referential"));
01311 }
01312 if(_referential->isVisible())
01313 {
01314 aShowHideReferential = menu.addAction(tr("Hide current referential"));
01315 }
01316 else
01317 {
01318 aShowHideReferential = menu.addAction(tr("Show current referential"));
01319 }
01320 if(_localRadius->isVisible())
01321 {
01322 aShowHideLocalRadius = menu.addAction(tr("Hide local radius"));
01323 }
01324 else
01325 {
01326 aShowHideLocalRadius = menu.addAction(tr("Show local radius"));
01327 }
01328 if(_graphRoot->isVisible())
01329 {
01330 aShowHideGraph = menu.addAction(tr("Hide graph"));
01331 }
01332 else
01333 {
01334 aShowHideGraph = menu.addAction(tr("Show graph"));
01335 }
01336 if(_globalPathRoot->isVisible())
01337 {
01338 aShowHideGlobalPath = menu.addAction(tr("Hide global path"));
01339 }
01340 else
01341 {
01342 aShowHideGlobalPath = menu.addAction(tr("Show global path"));
01343 }
01344 if(_localPathRoot->isVisible())
01345 {
01346 aShowHideLocalPath = menu.addAction(tr("Hide local path"));
01347 }
01348 else
01349 {
01350 aShowHideLocalPath = menu.addAction(tr("Show local path"));
01351 }
01352 if(_gtGraphRoot->isVisible())
01353 {
01354 aShowHideGtGraph = menu.addAction(tr("Hide ground truth graph"));
01355 }
01356 else
01357 {
01358 aShowHideGtGraph = menu.addAction(tr("Show ground truth graph"));
01359 }
01360 aShowHideGridMap->setEnabled(!_gridMap->pixmap().isNull());
01361 aShowHideGraph->setEnabled(_nodeItems.size());
01362 aShowHideGlobalPath->setEnabled(_globalPathLinkItems.size());
01363 aShowHideLocalPath->setEnabled(_localPathLinkItems.size());
01364 aShowHideGtGraph->setEnabled(_gtNodeItems.size());
01365 menu.addSeparator();
01366 QAction * aRestoreDefaults = menu.addAction(tr("Restore defaults"));
01367
01368 QAction * r = menu.exec(event->globalPos());
01369 if(r == aScreenShotPNG || r == aScreenShotSVG)
01370 {
01371 if(_root)
01372 {
01373 QString targetDir = _workingDirectory + "/ScreensCaptured";
01374 QDir dir;
01375 if(!dir.exists(targetDir))
01376 {
01377 dir.mkdir(targetDir);
01378 }
01379 targetDir += "/";
01380 targetDir += "Graph_view";
01381 if(!dir.exists(targetDir))
01382 {
01383 dir.mkdir(targetDir);
01384 }
01385 targetDir += "/";
01386 bool isPNG = r == aScreenShotPNG;
01387 QString name = (QDateTime::currentDateTime().toString("yyMMddhhmmsszzz") + (isPNG?".png":".svg"));
01388
01389 if(_gridCellSize)
01390 {
01391 _root->setScale(1.0f/_gridCellSize);
01392 }
01393 else
01394 {
01395 _root->setScale(this->transform().m11());
01396 }
01397
01398 this->scene()->clearSelection();
01399 this->scene()->setSceneRect(this->scene()->itemsBoundingRect());
01400 QSize sceneSize = this->scene()->sceneRect().size().toSize();
01401
01402 if(isPNG)
01403 {
01404 QImage image(sceneSize, QImage::Format_ARGB32);
01405 image.fill(Qt::transparent);
01406 QPainter painter(&image);
01407
01408 this->scene()->render(&painter);
01409 image.save(targetDir + name);
01410 }
01411 else
01412 {
01413 QSvgGenerator svgGen;
01414
01415 svgGen.setFileName( targetDir + name );
01416 svgGen.setSize(sceneSize);
01417 svgGen.setViewBox(QRect(0, 0, sceneSize.width(), sceneSize.height()));
01418 svgGen.setTitle(tr("RTAB-Map graph"));
01419 svgGen.setDescription(tr("RTAB-Map map and graph"));
01420
01421 QPainter painter( &svgGen );
01422
01423 this->scene()->render(&painter);
01424 }
01425
01426
01427 _root->setScale(1.0f);
01428 this->scene()->setSceneRect(this->scene()->itemsBoundingRect());
01429
01430
01431 QDesktopServices::openUrl(QUrl::fromLocalFile(targetDir + name));
01432 }
01433 return;
01434 }
01435 else if(r == aSetIntraInterSessionColors)
01436 {
01437 setIntraInterSessionColorsEnabled(aSetIntraInterSessionColors->isChecked());
01438 }
01439 else if(r == aChangeRejectedLoopThr)
01440 {
01441 bool ok;
01442 double value = QInputDialog::getDouble(this, tr("Loop closure outlier threshold"), tr("Value (m)"), _loopClosureOutlierThr, 0.0, 1000.0, 2, &ok);
01443 if(ok)
01444 {
01445 setLoopClosureOutlierThr(value);
01446 }
01447 }
01448 else if(r == aChangeMaxLinkLength)
01449 {
01450 bool ok;
01451 double value = QInputDialog::getDouble(this, tr("Maximum link length to be shown"), tr("Value (m)"), _maxLinkLength, 0.0, 1000.0, 3, &ok);
01452 if(ok)
01453 {
01454 setMaxLinkLength(value);
01455 }
01456 }
01457 else if(r == aChangeNodeColor ||
01458 r == aChangeCurrentGoalColor ||
01459 r == aChangeNeighborColor ||
01460 r == aChangeGlobalLoopColor ||
01461 r == aChangeLocalLoopColor ||
01462 r == aChangeUserLoopColor ||
01463 r == aChangeVirtualLoopColor ||
01464 r == aChangeNeighborMergedColor ||
01465 r == aChangeRejectedLoopColor ||
01466 r == aChangeLocalPathColor ||
01467 r == aChangeGlobalPathColor ||
01468 r == aChangeGTColor ||
01469 r == aChangeIntraSessionLoopColor ||
01470 r == aChangeInterSessionLoopColor)
01471 {
01472 QColor color;
01473 if(r == aChangeNodeColor)
01474 {
01475 color = _nodeColor;
01476 }
01477 else if(r == aChangeCurrentGoalColor)
01478 {
01479 color = _currentGoalColor;
01480 }
01481 else if(r == aChangeGlobalLoopColor)
01482 {
01483 color = _loopClosureColor;
01484 }
01485 else if(r == aChangeLocalLoopColor)
01486 {
01487 color = _loopClosureLocalColor;
01488 }
01489 else if(r == aChangeUserLoopColor)
01490 {
01491 color = _loopClosureUserColor;
01492 }
01493 else if(r == aChangeVirtualLoopColor)
01494 {
01495 color = _loopClosureVirtualColor;
01496 }
01497 else if(r == aChangeNeighborMergedColor)
01498 {
01499 color = _neighborMergedColor;
01500 }
01501 else if(r == aChangeRejectedLoopColor)
01502 {
01503 color = _loopClosureRejectedColor;
01504 }
01505 else if(r == aChangeLocalPathColor)
01506 {
01507 color = _localPathColor;
01508 }
01509 else if(r == aChangeGlobalPathColor)
01510 {
01511 color = _globalPathColor;
01512 }
01513 else if(r == aChangeGTColor)
01514 {
01515 color = _gtPathColor;
01516 }
01517 else if(r == aChangeIntraSessionLoopColor)
01518 {
01519 color = _loopIntraSessionColor;
01520 }
01521 else if(r == aChangeInterSessionLoopColor)
01522 {
01523 color = _loopInterSessionColor;
01524 }
01525 else
01526 {
01527 color = _neighborColor;
01528 }
01529 color = QColorDialog::getColor(color, this);
01530 if(color.isValid())
01531 {
01532
01533 if(r == aChangeNodeColor)
01534 {
01535 this->setNodeColor(color);
01536 }
01537 else if(r == aChangeCurrentGoalColor)
01538 {
01539 this->setCurrentGoalColor(color);
01540 }
01541 else if(r == aChangeGlobalLoopColor)
01542 {
01543 this->setGlobalLoopClosureColor(color);
01544 }
01545 else if(r == aChangeLocalLoopColor)
01546 {
01547 this->setLocalLoopClosureColor(color);
01548 }
01549 else if(r == aChangeUserLoopColor)
01550 {
01551 this->setUserLoopClosureColor(color);
01552 }
01553 else if(r == aChangeVirtualLoopColor)
01554 {
01555 this->setVirtualLoopClosureColor(color);
01556 }
01557 else if(r == aChangeNeighborMergedColor)
01558 {
01559 this->setNeighborMergedColor(color);
01560 }
01561 else if(r == aChangeRejectedLoopColor)
01562 {
01563 this->setRejectedLoopClosureColor(color);
01564 }
01565 else if(r == aChangeLocalPathColor)
01566 {
01567 this->setLocalPathColor(color);
01568 }
01569 else if(r == aChangeGlobalPathColor)
01570 {
01571 this->setGlobalPathColor(color);
01572 }
01573 else if(r == aChangeGTColor)
01574 {
01575 this->setGTColor(color);
01576 }
01577 else if(r == aChangeIntraSessionLoopColor)
01578 {
01579 this->setIntraSessionLoopColor(color);
01580 }
01581 else if(r == aChangeInterSessionLoopColor)
01582 {
01583 this->setInterSessionLoopColor(color);
01584 }
01585 else
01586 {
01587 this->setNeighborColor(color);
01588 }
01589 }
01590 else
01591 {
01592 return;
01593 }
01594 }
01595 else if(r == aSetNodeSize)
01596 {
01597 bool ok;
01598 double value = QInputDialog::getDouble(this, tr("Node radius"), tr("Radius (m)"), _nodeRadius, 0.001, 100, 3, &ok);
01599 if(ok)
01600 {
01601 setNodeRadius(value);
01602 }
01603 }
01604 else if(r == aSetLinkSize)
01605 {
01606 bool ok;
01607 double value = QInputDialog::getDouble(this, tr("Link width"), tr("Width (m)"), _linkWidth, 0, 100, 2, &ok);
01608 if(ok)
01609 {
01610 setLinkWidth(value);
01611 }
01612 }
01613 else if(r == aShowHideGridMap)
01614 {
01615 this->setGridMapVisible(!this->isGridMapVisible());
01616 }
01617 else if(r == aShowHideOrigin)
01618 {
01619 this->setOriginVisible(!this->isOriginVisible());
01620 }
01621 else if(r == aShowHideReferential)
01622 {
01623 this->setReferentialVisible(!this->isReferentialVisible());
01624 }
01625 else if(r == aShowHideLocalRadius)
01626 {
01627 this->setLocalRadiusVisible(!this->isLocalRadiusVisible());
01628 }
01629 else if(r == aRestoreDefaults)
01630 {
01631 this->restoreDefaults();
01632 }
01633 else if(r == aShowHideGraph)
01634 {
01635 this->setGraphVisible(!this->isGraphVisible());
01636 }
01637 else if(r == aShowHideGlobalPath)
01638 {
01639 this->setGlobalPathVisible(!this->isGlobalPathVisible());
01640 }
01641 else if(r == aShowHideLocalPath)
01642 {
01643 this->setLocalPathVisible(!this->isLocalPathVisible());
01644 }
01645 else if(r == aShowHideGtGraph)
01646 {
01647 this->setGtGraphVisible(!this->isGtGraphVisible());
01648 }
01649 if(r)
01650 {
01651 emit configChanged();
01652 }
01653 }
01654
01655 }