GraphViewer.cpp
Go to the documentation of this file.
00001 /*
00002 Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
00003 All rights reserved.
00004 
00005 Redistribution and use in source and binary forms, with or without
00006 modification, are permitted provided that the following conditions are met:
00007     * Redistributions of source code must retain the above copyright
00008       notice, this list of conditions and the following disclaimer.
00009     * Redistributions in binary form must reproduce the above copyright
00010       notice, this list of conditions and the following disclaimer in the
00011       documentation and/or other materials provided with the distribution.
00012     * Neither the name of the Universite de Sherbrooke nor the
00013       names of its contributors may be used to endorse or promote products
00014       derived from this software without specific prior written permission.
00015 
00016 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00017 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00018 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00019 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
00020 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00021 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00022 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00023 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00024 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00025 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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         // in meter
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         // in meter
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         // add referential
00218         _originReferential = new QGraphicsItemGroup();
00219         this->scene()->addItem(_originReferential); // ownership transfered
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         // current pose
00230         _referential = new QGraphicsItemGroup();
00231         this->scene()->addItem(_referential); // ownership transfered
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         //Hide nodes and links
00286         for(QMap<int, NodeItem*>::iterator iter = _nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
00287         {
00288                 iter.value()->hide();
00289                 iter.value()->setColor(_nodeColor); // reset color
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                                 // create node item
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                 // make the first id the smallest one
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                                         //create a link item
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                                 // erase small links
00376                                 _linkItems.erase(itemIter);
00377                                 delete linkItem;
00378                                 linkItem = 0;
00379                         }
00380 
00381                         if(linkItem)
00382                         {
00383                                 //update color
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                                 //rejected loop closures
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         //remove not used nodes and links
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());  // Re-shrink the scene to it's bounding contents
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         //Hide nodes and links
00501         for(QMap<int, NodeItem*>::iterator iter = _gtNodeItems.begin(); iter!=_gtNodeItems.end(); ++iter)
00502         {
00503                 iter.value()->hide();
00504                 iter.value()->setColor(_gtPathColor); // reset color
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                                 // create node item
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                                         //create a link item
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         //remove not used nodes and links
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());  // Re-shrink the scene to it's bounding contents
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());  // Re-shrink the scene to it's bounding contents
00645         }
00646         else
00647         {
00648                 this->clearMap();
00649         }
00650 }
00651 
00652 void GraphViewer::updatePosterior(const std::map<int, float> & posterior)
00653 {
00654         //find max
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)); //0=red 240=blue
00672                         }
00673                         else
00674                         {
00675                                 iter.value()->setColor(QColor::fromHsvF(240.0f/360.0f, 1, 1, 1)); // blue
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                         //create a link item
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                 // transform the global path in the goal referential
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                                         //create a link item
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); // just over the global path
00780                                         item->setParentItem(_localPathRoot);
00781                                         _localPathLinkItems.insert(idFrom, item);
00782                                 }
00783                         }
00784                 }
00785         }
00786 
00787         // remove not used links
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());  // Re-shrink the scene to it's bounding contents
00821 }
00822 
00823 void GraphViewer::clearMap()
00824 {
00825         _gridMap->setPixmap(QPixmap());
00826         _gridCellSize = 0.0f;
00827         this->scene()->setSceneRect(this->scene()->itemsBoundingRect());  // Re-shrink the scene to it's bounding contents
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); // 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         // Links
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); // grid map precision (for 5cm grid cell, x20 to have 1pix/5cm)
01392                         }
01393                         else
01394                         {
01395                                 _root->setScale(this->transform().m11()); // current view
01396                         }
01397 
01398                         this->scene()->clearSelection();                                  // Selections would also render to the file
01399                         this->scene()->setSceneRect(this->scene()->itemsBoundingRect());  // Re-shrink the scene to it's bounding contents
01400                         QSize sceneSize = this->scene()->sceneRect().size().toSize();
01401 
01402                         if(isPNG)
01403                         {
01404                                 QImage image(sceneSize, QImage::Format_ARGB32);  // Create the image with the exact size of the shrunk scene
01405                                 image.fill(Qt::transparent);                     // Start all pixels 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                         //reset scale
01427                         _root->setScale(1.0f);
01428                         this->scene()->setSceneRect(this->scene()->itemsBoundingRect());  // Re-shrink the scene to it's bounding contents
01429 
01430 
01431                         QDesktopServices::openUrl(QUrl::fromLocalFile(targetDir + name));
01432                 }
01433                 return; // without emitting configChanged
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 //if(r == aChangeNeighborColor)
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 //if(r == aChangeNeighborColor)
01586                         {
01587                                 this->setNeighborColor(color);
01588                         }
01589                 }
01590                 else
01591                 {
01592                         return; // without emitting configChanged
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 } /* namespace rtabmap */


rtabmap
Author(s): Mathieu Labbe
autogenerated on Sat Jul 23 2016 11:44:16