GraphViewer.cpp
Go to the documentation of this file.
00001 /*
00002 Copyright (c) 2010-2014, 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 "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/gui/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, const Transform & pose, float radius) :
00059                 QGraphicsEllipseItem(QRectF(-radius,-radius,radius*2.0f,radius*2.0f)),
00060                 _id(id),
00061                 _pose(pose)
00062         {
00063                 this->setPos(-pose.y(),-pose.x());
00064                 this->setBrush(pen().color());
00065                 this->setAcceptHoverEvents(true);
00066         }
00067 
00068         void setColor(const QColor & color)
00069         {
00070                 QPen p = this->pen();
00071                 p.setColor(color);
00072                 this->setPen(p);
00073                 QBrush b = this->brush();
00074                 b.setColor(color);
00075                 this->setBrush(b);
00076         }
00077 
00078         const Transform & pose() const {return _pose;}
00079         void setPose(const Transform & pose) {this->setPos(-pose.y(),-pose.x()); _pose=pose;}
00080 
00081 protected:
00082         virtual void hoverEnterEvent ( QGraphicsSceneHoverEvent * event )
00083         {
00084                 this->setToolTip(QString("[%1] %2").arg(_id).arg(_pose.prettyPrint().c_str()));
00085                 this->setScale(2);
00086                 QGraphicsEllipseItem::hoverEnterEvent(event);
00087         }
00088 
00089         virtual void hoverLeaveEvent ( QGraphicsSceneHoverEvent * event )
00090         {
00091                 this->setScale(1);
00092                 QGraphicsEllipseItem::hoverEnterEvent(event);
00093         }
00094 
00095 private:
00096         int _id;
00097         Transform _pose;
00098 };
00099 
00100 class LinkItem: public QGraphicsLineItem
00101 {
00102 public:
00103         // in meter
00104         LinkItem(int from, int to, const Transform & poseA, const Transform & poseB, Link::Type type) :
00105                 QGraphicsLineItem(-poseA.y(), -poseA.x(), -poseB.y(), -poseB.x()),
00106                 _from(from),
00107                 _to(to),
00108                 _poseA(poseA),
00109                 _poseB(poseB),
00110                 _type(type)
00111         {
00112                 this->setAcceptHoverEvents(true);
00113         }
00114 
00115         void setColor(const QColor & color)
00116         {
00117                 QPen p = this->pen();
00118                 p.setColor(color);
00119                 this->setPen(p);
00120         }
00121 
00122         void setPoses(const Transform & poseA, const Transform & poseB)
00123         {
00124                 this->setLine(-poseA.y(), -poseA.x(), -poseB.y(), -poseB.x());
00125                 _poseA = poseA;
00126                 _poseB = poseB;
00127         }
00128 
00129         Link::Type linkType() const {return _type;}
00130         int from() const {return _from;}
00131         int to() const {return _to;}
00132 
00133 protected:
00134         virtual void hoverEnterEvent ( QGraphicsSceneHoverEvent * event )
00135         {
00136                 this->setToolTip(QString("%1->%2 %3 m").arg(_from).arg(_to).arg(_poseA.getDistance(_poseB)));
00137                 QPen pen = this->pen();
00138                 pen.setWidthF(pen.widthF()+0.02);
00139                 this->setPen(pen);
00140                 QGraphicsLineItem::hoverEnterEvent(event);
00141         }
00142 
00143         virtual void hoverLeaveEvent ( QGraphicsSceneHoverEvent * event )
00144         {
00145                 QPen pen = this->pen();
00146                 pen.setWidthF(pen.widthF()-0.02);
00147                 this->setPen(pen);
00148                 QGraphicsLineItem::hoverEnterEvent(event);
00149         }
00150 
00151 private:
00152         int _from;
00153         int _to;
00154         Transform _poseA;
00155         Transform _poseB;
00156         Link::Type _type;
00157 };
00158 
00159 GraphViewer::GraphViewer(QWidget * parent) :
00160                 QGraphicsView(parent),
00161                 _nodeColor(Qt::blue),
00162                 _currentGoalColor(Qt::darkMagenta),
00163                 _neighborColor(Qt::blue),
00164                 _loopClosureColor(Qt::red),
00165                 _loopClosureLocalColor(Qt::yellow),
00166                 _loopClosureUserColor(Qt::red),
00167                 _loopClosureVirtualColor(Qt::magenta),
00168                 _localPathColor(Qt::cyan),
00169                 _globalPathColor(Qt::darkMagenta),
00170                 _root(0),
00171                 _nodeRadius(0.01),
00172                 _linkWidth(0),
00173                 _gridMap(0),
00174                 _referential(0),
00175                 _gridCellSize(0.0f),
00176                 _localRadius(0)
00177 {
00178         this->setScene(new QGraphicsScene(this));
00179         this->setDragMode(QGraphicsView::ScrollHandDrag);
00180         _workingDirectory = QDir::homePath();
00181 
00182         this->scene()->clear();
00183         _root = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
00184 
00185         // add referential
00186         _originReferential = new QGraphicsItemGroup();
00187         this->scene()->addItem(_originReferential); // ownership transfered
00188         QGraphicsLineItem * item = this->scene()->addLine(0,0,0,-1, QPen(QBrush(Qt::red), _linkWidth));
00189         item->setZValue(100);
00190         item->setParentItem(_root);
00191         _originReferential->addToGroup(item);
00192         item = this->scene()->addLine(0,0,-1,0, QPen(QBrush(Qt::green), _linkWidth));
00193         item->setZValue(100);
00194         item->setParentItem(_root);
00195         _originReferential->addToGroup(item);
00196 
00197         // current pose
00198         _referential = new QGraphicsItemGroup();
00199         this->scene()->addItem(_referential); // ownership transfered
00200         item = this->scene()->addLine(0,0,0,-0.5, QPen(QBrush(Qt::red), _linkWidth));
00201         item->setZValue(100);
00202         item->setParentItem(_root);
00203         _referential->addToGroup(item);
00204         item = this->scene()->addLine(0,0,-0.5,0, QPen(QBrush(Qt::green), _linkWidth));
00205         item->setZValue(100);
00206         item->setParentItem(_root);
00207         _referential->addToGroup(item);
00208 
00209         _localRadius = this->scene()->addEllipse(-0.0001,-0.0001,0.0001,0.0001);
00210         _localRadius->setZValue(1);
00211         _localRadius->setParentItem(_root);
00212         _localRadius->setVisible(false);
00213         _localRadius->setPen(QPen(Qt::DashLine));
00214 
00215         _gridMap = this->scene()->addPixmap(QPixmap());
00216         _gridMap->setZValue(0);
00217         _gridMap->setParentItem(_root);
00218 
00219         this->restoreDefaults();
00220 
00221         this->fitInView(this->sceneRect(), Qt::KeepAspectRatio);
00222 }
00223 
00224 GraphViewer::~GraphViewer()
00225 {
00226 }
00227 
00228 void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
00229                                  const std::multimap<int, Link> & constraints)
00230 {
00231         bool wasEmpty = _nodeItems.size() == 0 && _linkItems.size() == 0;
00232         UDEBUG("poses=%d constraints=%d", (int)poses.size(), (int)constraints.size());
00233         //Hide nodes and links
00234         for(QMap<int, NodeItem*>::iterator iter = _nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
00235         {
00236                 iter.value()->hide();
00237                 iter.value()->setColor(_nodeColor); // reset color
00238         }
00239         for(QMultiMap<int, LinkItem*>::iterator iter = _linkItems.begin(); iter!=_linkItems.end(); ++iter)
00240         {
00241                 iter.value()->hide();
00242         }
00243 
00244         for(std::map<int, Transform>::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
00245         {
00246                 if(!iter->second.isNull())
00247                 {
00248                         QMap<int, NodeItem*>::iterator itemIter = _nodeItems.find(iter->first);
00249                         if(itemIter != _nodeItems.end())
00250                         {
00251                                 itemIter.value()->setPose(iter->second);
00252                                 itemIter.value()->show();
00253                         }
00254                         else
00255                         {
00256                                 // create node item
00257                                 const Transform & pose = iter->second;
00258                                 NodeItem * item = new NodeItem(iter->first, pose, _nodeRadius);
00259                                 this->scene()->addItem(item);
00260                                 item->setZValue(20);
00261                                 item->setColor(_nodeColor);
00262                                 item->setParentItem(_root);
00263                                 _nodeItems.insert(iter->first, item);
00264                         }
00265                 }
00266         }
00267 
00268         for(std::multimap<int, Link>::const_iterator iter=constraints.begin(); iter!=constraints.end(); ++iter)
00269         {
00270                 // make the first id the smallest one
00271                 int idFrom = iter->first<iter->second.to()?iter->first:iter->second.to();
00272                 int idTo = iter->first<iter->second.to()?iter->second.to():iter->first;
00273 
00274                 std::map<int, Transform>::const_iterator jterA = poses.find(idFrom);
00275                 std::map<int, Transform>::const_iterator jterB = poses.find(idTo);
00276                 if(jterA != poses.end() && jterB != poses.end() &&
00277                    _nodeItems.contains(iter->first) && _nodeItems.contains(idTo))
00278                 {
00279                         const Transform & poseA = jterA->second;
00280                         const Transform & poseB = jterB->second;
00281 
00282                         bool added = false;
00283                         if(_linkItems.contains(idFrom))
00284                         {
00285                                 QMultiMap<int, LinkItem*>::iterator itemIter = _linkItems.find(iter->first);
00286                                 while(itemIter.key() == idFrom && itemIter != _linkItems.end())
00287                                 {
00288                                         if(itemIter.value()->to() == idTo)
00289                                         {
00290                                                 itemIter.value()->setPoses(poseA, poseB);
00291                                                 itemIter.value()->show();
00292                                                 added = true;
00293                                                 // reset color
00294                                                 if(iter->second.type() == Link::kNeighbor)
00295                                                 {
00296                                                         itemIter.value()->setColor(_neighborColor);
00297                                                 }
00298                                                 else if(iter->second.type() == Link::kVirtualClosure)
00299                                                 {
00300                                                         itemIter.value()->setColor(_loopClosureVirtualColor);
00301                                                 }
00302                                                 else if(iter->second.type() == Link::kUserClosure)
00303                                                 {
00304                                                         itemIter.value()->setColor(_loopClosureUserColor);
00305                                                 }
00306                                                 else if(iter->second.type() == Link::kLocalSpaceClosure || iter->second.type() == Link::kLocalTimeClosure)
00307                                                 {
00308                                                         itemIter.value()->setColor(_loopClosureLocalColor);
00309                                                 }
00310                                                 else
00311                                                 {
00312                                                         itemIter.value()->setColor(_loopClosureColor);
00313                                                 }
00314                                                 break;
00315                                         }
00316                                         ++itemIter;
00317                                 }
00318                         }
00319                         if(!added)
00320                         {
00321                                 //create a link item
00322                                 LinkItem * item = new LinkItem(idFrom, idTo, poseA, poseB, iter->second.type());
00323                                 QPen p = item->pen();
00324                                 p.setWidthF(_linkWidth);
00325                                 item->setPen(p);
00326                                 if(iter->second.type() == Link::kNeighbor)
00327                                 {
00328                                         item->setColor(_neighborColor);
00329                                 }
00330                                 else if(iter->second.type() == Link::kVirtualClosure)
00331                                 {
00332                                         item->setColor(_loopClosureVirtualColor);
00333                                 }
00334                                 else if(iter->second.type() == Link::kUserClosure)
00335                                 {
00336                                         item->setColor(_loopClosureUserColor);
00337                                 }
00338                                 else if(iter->second.type() == Link::kLocalSpaceClosure || iter->second.type() == Link::kLocalTimeClosure)
00339                                 {
00340                                         item->setColor(_loopClosureLocalColor);
00341                                 }
00342                                 else
00343                                 {
00344                                         item->setColor(_loopClosureColor);
00345                                 }
00346                                 this->scene()->addItem(item);
00347                                 item->setZValue(10);
00348                                 item->setParentItem(_root);
00349                                 _linkItems.insert(idFrom, item);
00350                         }
00351                 }
00352         }
00353 
00354         //remove not used nodes and links
00355         for(QMap<int, NodeItem*>::iterator iter = _nodeItems.begin(); iter!=_nodeItems.end();)
00356         {
00357                 if(!iter.value()->isVisible())
00358                 {
00359                         delete iter.value();
00360                         iter = _nodeItems.erase(iter);
00361                 }
00362                 else
00363                 {
00364                         ++iter;
00365                 }
00366         }
00367         for(QMultiMap<int, LinkItem*>::iterator iter = _linkItems.begin(); iter!=_linkItems.end();)
00368         {
00369                 if(!iter.value()->isVisible())
00370                 {
00371                         delete iter.value();
00372                         iter = _linkItems.erase(iter);
00373                 }
00374                 else
00375                 {
00376                         ++iter;
00377                 }
00378         }
00379 
00380         if(_nodeItems.size())
00381         {
00382                 (--_nodeItems.end()).value()->setColor(Qt::green);
00383         }
00384 
00385         this->scene()->setSceneRect(this->scene()->itemsBoundingRect());  // Re-shrink the scene to it's bounding contents
00386 
00387         if(wasEmpty)
00388         {
00389                 this->fitInView(this->scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
00390         }
00391 }
00392 
00393 void GraphViewer::updateReferentialPosition(const Transform & t)
00394 {
00395         QTransform qt(t.r11(), t.r12(), t.r21(), t.r22(), -t.o24(), -t.o14());
00396         _referential->setTransform(qt);
00397         _localRadius->setTransform(qt);
00398 
00399         this->ensureVisible(_referential);
00400         if(_localRadius->isVisible())
00401         {
00402                 this->ensureVisible(_localRadius, 0, 0);
00403         }
00404 }
00405 
00406 void GraphViewer::updateMap(const cv::Mat & map8U, float resolution, float xMin, float yMin)
00407 {
00408         UASSERT(map8U.empty() || (!map8U.empty() && resolution > 0.0f));
00409         if(!map8U.empty())
00410         {
00411                 _gridCellSize = resolution;
00412                 QImage image = uCvMat2QImage(map8U, false);
00413                 _gridMap->resetTransform();
00414                 _gridMap->setTransform(QTransform::fromScale(resolution, -resolution), true);
00415                 _gridMap->setRotation(90);
00416                 _gridMap->setPixmap(QPixmap::fromImage(image));
00417                 _gridMap->setPos(-yMin, -xMin);
00418                 this->scene()->setSceneRect(this->scene()->itemsBoundingRect());  // Re-shrink the scene to it's bounding contents
00419         }
00420         else
00421         {
00422                 this->clearMap();
00423         }
00424 }
00425 
00426 void GraphViewer::updatePosterior(const std::map<int, float> & posterior)
00427 {
00428         //find max
00429         float max = 0.0f;
00430         for(std::map<int, float>::const_iterator iter = posterior.begin(); iter!=posterior.end(); ++iter)
00431         {
00432                 if(iter->first > 0 && iter->second>max)
00433                 {
00434                         max = iter->second;
00435                 }
00436         }
00437         if(max > 0.0f)
00438         {
00439                 for(QMap<int, NodeItem*>::iterator iter = _nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
00440                 {
00441                         std::map<int,float>::const_iterator jter = posterior.find(iter.key());
00442                         if(jter != posterior.end())
00443                         {
00444                                 UDEBUG("id=%d max=%f hyp=%f color = %f", iter.key(), max, jter->second, (1-jter->second/max)*240.0f/360.0f);
00445                                 iter.value()->setColor(QColor::fromHsvF((1-jter->second/max)*240.0f/360.0f, 1, 1, 1)); //0=red 240=blue
00446                         }
00447                         else
00448                         {
00449                                 iter.value()->setColor(QColor::fromHsvF(240.0f/360.0f, 1, 1, 1)); // blue
00450                         }
00451                 }
00452         }
00453 }
00454 
00455 void GraphViewer::setGlobalPath(const std::vector<std::pair<int, Transform> > & globalPath)
00456 {
00457         UDEBUG("Set global path size=%d", (int)globalPath.size());
00458         qDeleteAll(_globalPathLinkItems);
00459         _globalPathLinkItems.clear();
00460 
00461         if(globalPath.size() >= 2)
00462         {
00463                 for(unsigned int i=0; i<globalPath.size()-1; ++i)
00464                 {
00465                         //create a link item
00466                         int idFrom = globalPath[i].first;
00467                         int idTo = globalPath[i+1].first;
00468                         LinkItem * item = new LinkItem(idFrom, idTo, globalPath[i].second, globalPath[i+1].second, Link::kUndef);
00469                         QPen p = item->pen();
00470                         p.setWidthF(_linkWidth);
00471                         item->setPen(p);
00472                         item->setColor(_globalPathColor);
00473                         this->scene()->addItem(item);
00474                         item->setZValue(15);
00475                         item->setParentItem(_root);
00476                         _globalPathLinkItems.insert(idFrom, item);
00477                 }
00478         }
00479 }
00480 
00481 void GraphViewer::setCurrentGoalID(int id)
00482 {
00483         NodeItem * node = _nodeItems.value(id, 0);
00484         if(node)
00485         {
00486                 node->setColor(_currentGoalColor);
00487         }
00488         else
00489         {
00490                 UWARN("Curent goal %d not found in the graph", id);
00491         }
00492 }
00493 
00494 void GraphViewer::setLocalRadius(float radius)
00495 {
00496         _localRadius->setRect(-radius, -radius, radius*2, radius*2);
00497 }
00498 
00499 void GraphViewer::updateLocalPath(const std::vector<int> & localPath)
00500 {
00501         for(QMultiMap<int, LinkItem*>::iterator iter = _localPathLinkItems.begin(); iter!=_localPathLinkItems.end(); ++iter)
00502         {
00503                 iter.value()->hide();
00504         }
00505 
00506         if(localPath.size() > 1)
00507         {
00508                 for(unsigned int i=0; i<localPath.size()-1; ++i)
00509                 {
00510                         int idFrom = localPath[i]<localPath[i+1]?localPath[i]:localPath[i+1];
00511                         int idTo = localPath[i]<localPath[i+1]?localPath[i+1]:localPath[i];
00512                         if(_nodeItems.contains(idFrom) && _nodeItems.contains(idTo))
00513                         {
00514                                 bool updated = false;
00515                                 if(_localPathLinkItems.contains(idFrom))
00516                                 {
00517                                         QMultiMap<int, LinkItem*>::iterator itemIter = _localPathLinkItems.find(idFrom);
00518                                         while(itemIter.key() == idFrom && itemIter != _localPathLinkItems.end())
00519                                         {
00520                                                 if(itemIter.value()->to() == idTo)
00521                                                 {
00522                                                         itemIter.value()->setPoses(_nodeItems.value(idFrom)->pose(), _nodeItems.value(idTo)->pose());
00523                                                         itemIter.value()->show();
00524                                                         updated = true;
00525                                                         break;
00526                                                 }
00527                                                 ++itemIter;
00528                                         }
00529                                 }
00530                                 if(!updated)
00531                                 {
00532                                         //create a link item
00533                                         LinkItem * item = new LinkItem(idFrom, idTo, _nodeItems.value(idFrom)->pose(), _nodeItems.value(idTo)->pose(), Link::kUndef);
00534                                         QPen p = item->pen();
00535                                         p.setWidthF(_linkWidth);
00536                                         item->setPen(p);
00537                                         item->setColor(_localPathColor);
00538                                         this->scene()->addItem(item);
00539                                         item->setZValue(16); // just over the global path
00540                                         item->setParentItem(_root);
00541                                         _localPathLinkItems.insert(idFrom, item);
00542                                 }
00543                         }
00544                 }
00545         }
00546 
00547         // remove not used links
00548         for(QMultiMap<int, LinkItem*>::iterator iter = _localPathLinkItems.begin(); iter!=_localPathLinkItems.end();)
00549         {
00550                 if(!iter.value()->isVisible())
00551                 {
00552                         delete iter.value();
00553                         iter = _localPathLinkItems.erase(iter);
00554                 }
00555                 else
00556                 {
00557                         ++iter;
00558                 }
00559         }
00560 }
00561 
00562 void GraphViewer::clearGraph()
00563 {
00564         qDeleteAll(_nodeItems);
00565         _nodeItems.clear();
00566         qDeleteAll(_linkItems);
00567         _linkItems.clear();
00568         qDeleteAll(_localPathLinkItems);
00569         _localPathLinkItems.clear();
00570         qDeleteAll(_globalPathLinkItems);
00571         _globalPathLinkItems.clear();
00572 
00573         _referential->resetTransform();
00574         _localRadius->resetTransform();
00575         this->scene()->setSceneRect(this->scene()->itemsBoundingRect());  // Re-shrink the scene to it's bounding contents
00576 }
00577 
00578 void GraphViewer::clearMap()
00579 {
00580         _gridMap->setPixmap(QPixmap());
00581         _gridCellSize = 0.0f;
00582         this->scene()->setSceneRect(this->scene()->itemsBoundingRect());  // Re-shrink the scene to it's bounding contents
00583 }
00584 
00585 void GraphViewer::clearPosterior()
00586 {
00587         for(QMap<int, NodeItem*>::iterator iter = _nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
00588         {
00589                 iter.value()->setColor(Qt::blue); // blue
00590         }
00591 }
00592 
00593 void GraphViewer::clearAll()
00594 {
00595         clearMap();
00596         clearGraph();
00597 }
00598 
00599 void GraphViewer::saveSettings(QSettings & settings, const QString & group) const
00600 {
00601         if(!group.isEmpty())
00602         {
00603                 settings.beginGroup(group);
00604         }
00605         settings.setValue("node_radius", (double)this->getNodeRadius());
00606         settings.setValue("link_width", (double)this->getLinkWidth());
00607         settings.setValue("node_color", this->getNodeColor());
00608         settings.setValue("current_goal_color", this->getCurrentGoalColor());
00609         settings.setValue("neighbor_color", this->getNeighborColor());
00610         settings.setValue("global_color", this->getGlobalLoopClosureColor());
00611         settings.setValue("local_color", this->getLocalLoopClosureColor());
00612         settings.setValue("user_color", this->getUserLoopClosureColor());
00613         settings.setValue("virtual_color", this->getVirtualLoopClosureColor());
00614         settings.setValue("local_path_color", this->getLocalPathColor());
00615         settings.setValue("global_path_color", this->getGlobalPathColor());
00616         settings.setValue("grid_visible", this->isGridMapVisible());
00617         settings.setValue("origin_visible", this->isOriginVisible());
00618         settings.setValue("referential_visible", this->isReferentialVisible());
00619         settings.setValue("local_radius_visible", this->isLocalRadiusVisible());
00620         if(!group.isEmpty())
00621         {
00622                 settings.endGroup();
00623         }
00624 }
00625 
00626 void GraphViewer::loadSettings(QSettings & settings, const QString & group)
00627 {
00628         if(!group.isEmpty())
00629         {
00630                 settings.beginGroup(group);
00631         }
00632         this->setNodeRadius(settings.value("node_radius", this->getNodeRadius()).toDouble());
00633         this->setLinkWidth(settings.value("link_width", this->getLinkWidth()).toDouble());
00634         this->setNodeColor(settings.value("node_color", this->getNodeColor()).value<QColor>());
00635         this->setCurrentGoalColor(settings.value("current_goal_color", this->getCurrentGoalColor()).value<QColor>());
00636         this->setNeighborColor(settings.value("neighbor_color", this->getNeighborColor()).value<QColor>());
00637         this->setGlobalLoopClosureColor(settings.value("global_color", this->getGlobalLoopClosureColor()).value<QColor>());
00638         this->setLocalLoopClosureColor(settings.value("local_color", this->getLocalLoopClosureColor()).value<QColor>());
00639         this->setUserLoopClosureColor(settings.value("user_color", this->getUserLoopClosureColor()).value<QColor>());
00640         this->setVirtualLoopClosureColor(settings.value("virtual_color", this->getVirtualLoopClosureColor()).value<QColor>());
00641         this->setLocalPathColor(settings.value("local_path_color", this->getLocalPathColor()).value<QColor>());
00642         this->setGlobalPathColor(settings.value("global_path_color", this->getGlobalPathColor()).value<QColor>());
00643         this->setGridMapVisible(settings.value("grid_visible", this->isGridMapVisible()).toBool());
00644         this->setOriginVisible(settings.value("origin_visible", this->isOriginVisible()).toBool());
00645         this->setReferentialVisible(settings.value("referential_visible", this->isReferentialVisible()).toBool());
00646         this->setLocalRadiusVisible(settings.value("local_radius_visible", this->isLocalRadiusVisible()).toBool());
00647         if(!group.isEmpty())
00648         {
00649                 settings.endGroup();
00650         }
00651 }
00652 
00653 bool GraphViewer::isGridMapVisible() const
00654 {
00655         return _gridMap->isVisible();
00656 }
00657 bool GraphViewer::isOriginVisible() const
00658 {
00659         return _originReferential->isVisible();
00660 }
00661 bool GraphViewer::isReferentialVisible() const
00662 {
00663         return _referential->isVisible();
00664 }
00665 bool GraphViewer::isLocalRadiusVisible() const
00666 {
00667         return _localRadius->isVisible();
00668 }
00669 
00670 void GraphViewer::setWorkingDirectory(const QString & path)
00671 {
00672         _workingDirectory = path;
00673 }
00674 void GraphViewer::setNodeRadius(float radius)
00675 {
00676         _nodeRadius = radius;
00677         for(QMap<int, NodeItem*>::iterator iter=_nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
00678         {
00679                 iter.value()->setRect(-_nodeRadius, -_nodeRadius, _nodeRadius*2.0f, _nodeRadius*2.0f);
00680         }
00681 }
00682 void GraphViewer::setLinkWidth(float width)
00683 {
00684         _linkWidth = width;
00685         QList<QGraphicsItem*> items = this->scene()->items();
00686         for(int i=0; i<items.size(); ++i)
00687         {
00688                 QGraphicsLineItem * line = qgraphicsitem_cast<QGraphicsLineItem *>(items[i]);
00689                 if(line)
00690                 {
00691                         QPen pen = line->pen();
00692                         pen.setWidthF(_linkWidth);
00693                         line->setPen(pen);
00694                 }
00695         }
00696 }
00697 void GraphViewer::setNodeColor(const QColor & color)
00698 {
00699         _nodeColor = color;
00700         for(QMap<int, NodeItem*>::iterator iter=_nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
00701         {
00702                 iter.value()->setColor(_nodeColor);
00703         }
00704 }
00705 void GraphViewer::setCurrentGoalColor(const QColor & color)
00706 {
00707         _currentGoalColor = color;
00708 }
00709 void GraphViewer::setNeighborColor(const QColor & color)
00710 {
00711         _neighborColor = color;
00712         for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
00713         {
00714                 if(iter.value()->linkType() == Link::kNeighbor)
00715                 {
00716                         iter.value()->setColor(_neighborColor);
00717                 }
00718         }
00719 }
00720 void GraphViewer::setGlobalLoopClosureColor(const QColor & color)
00721 {
00722         _loopClosureColor = color;
00723         for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
00724         {
00725                 if(iter.value()->linkType() != Link::kNeighbor &&
00726                         iter.value()->linkType() != Link::kLocalSpaceClosure &&
00727                         iter.value()->linkType() != Link::kLocalTimeClosure &&
00728                         iter.value()->linkType() != Link::kUserClosure &&
00729                         iter.value()->linkType() != Link::kVirtualClosure)
00730                 {
00731                         iter.value()->setColor(_loopClosureColor);
00732                 }
00733         }
00734 }
00735 void GraphViewer::setLocalLoopClosureColor(const QColor & color)
00736 {
00737         _loopClosureLocalColor = color;
00738         for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
00739         {
00740                 if(iter.value()->linkType() == Link::kLocalSpaceClosure ||
00741                    iter.value()->linkType() == Link::kLocalTimeClosure)
00742                 {
00743                         iter.value()->setColor(_loopClosureLocalColor);
00744                 }
00745         }
00746 }
00747 void GraphViewer::setUserLoopClosureColor(const QColor & color)
00748 {
00749         _loopClosureUserColor = color;
00750         for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
00751         {
00752                 if(iter.value()->linkType() == Link::kUserClosure)
00753                 {
00754                         iter.value()->setColor(_loopClosureUserColor);
00755                 }
00756         }
00757 }
00758 void GraphViewer::setVirtualLoopClosureColor(const QColor & color)
00759 {
00760         _loopClosureVirtualColor = color;
00761         for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
00762         {
00763                 if(iter.value()->linkType() == Link::kVirtualClosure)
00764                 {
00765                         iter.value()->setColor(_loopClosureVirtualColor);
00766                 }
00767         }
00768 }
00769 void GraphViewer::setLocalPathColor(const QColor & color)
00770 {
00771         _localPathColor = color;
00772 }
00773 void GraphViewer::setGlobalPathColor(const QColor & color)
00774 {
00775         _globalPathColor = color;
00776 }
00777 void GraphViewer::setGridMapVisible(bool visible)
00778 {
00779         _gridMap->setVisible(visible);
00780 }
00781 void GraphViewer::setOriginVisible(bool visible)
00782 {
00783         _originReferential->setVisible(visible);
00784 }
00785 void GraphViewer::setReferentialVisible(bool visible)
00786 {
00787         _referential->setVisible(visible);
00788 }
00789 void GraphViewer::setLocalRadiusVisible(bool visible)
00790 {
00791         _localRadius->setVisible(visible);
00792 }
00793 
00794 void GraphViewer::restoreDefaults()
00795 {
00796         setNodeRadius(0.01f);
00797         setLinkWidth(0.0f);
00798         setNodeColor(Qt::blue);
00799         setNeighborColor(Qt::blue);
00800         setGlobalLoopClosureColor(Qt::red);
00801         setLocalLoopClosureColor(Qt::yellow);
00802         setUserLoopClosureColor(Qt::red);
00803         setVirtualLoopClosureColor(Qt::magenta);
00804         setGridMapVisible(true);
00805 }
00806 
00807 
00808 void GraphViewer::wheelEvent ( QWheelEvent * event )
00809 {
00810         if(event->delta() < 0)
00811         {
00812                 this->scale(0.95, 0.95);
00813         }
00814         else
00815         {
00816                 this->scale(1.05, 1.05);
00817         }
00818 }
00819 
00820 QIcon createIcon(const QColor & color)
00821 {
00822         QPixmap pixmap(50, 50);
00823         pixmap.fill(color);
00824         return QIcon(pixmap);
00825 }
00826 
00827 void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
00828 {
00829         QMenu menu;
00830         QAction * aScreenShotPNG = menu.addAction(tr("Take a screenshot (PNG)"));
00831         QAction * aScreenShotSVG = menu.addAction(tr("Take a screenshot (SVG)"));
00832         menu.addSeparator();
00833 
00834         QAction * aChangeNodeColor = menu.addAction(createIcon(_nodeColor), tr("Set node color..."));
00835         QAction * aChangeCurrentGoalColor = menu.addAction(createIcon(_currentGoalColor), tr("Set current goal color..."));
00836         aChangeNodeColor->setIconVisibleInMenu(true);
00837         aChangeCurrentGoalColor->setIconVisibleInMenu(true);
00838 
00839         // Links
00840         QMenu * menuLink = menu.addMenu(tr("Set link color..."));
00841         QAction * aChangeNeighborColor = menuLink->addAction(tr("Neighbor"));
00842         QAction * aChangeGlobalLoopColor = menuLink->addAction(tr("Global loop closure"));
00843         QAction * aChangeLocalLoopColor = menuLink->addAction(tr("Local loop closure"));
00844         QAction * aChangeUserLoopColor = menuLink->addAction(tr("User loop closure"));
00845         QAction * aChangeVirtualLoopColor = menuLink->addAction(tr("Virtual loop closure"));
00846         QAction * aChangeLocalPathColor = menuLink->addAction(tr("Local path"));
00847         QAction * aChangeGlobalPathColor = menuLink->addAction(tr("Global path"));
00848         aChangeNeighborColor->setIcon(createIcon(_neighborColor));
00849         aChangeGlobalLoopColor->setIcon(createIcon(_loopClosureColor));
00850         aChangeLocalLoopColor->setIcon(createIcon(_loopClosureLocalColor));
00851         aChangeUserLoopColor->setIcon(createIcon(_loopClosureUserColor));
00852         aChangeVirtualLoopColor->setIcon(createIcon(_loopClosureVirtualColor));
00853         aChangeLocalPathColor->setIcon(createIcon(_localPathColor));
00854         aChangeGlobalPathColor->setIcon(createIcon(_globalPathColor));
00855         aChangeNeighborColor->setIconVisibleInMenu(true);
00856         aChangeGlobalLoopColor->setIconVisibleInMenu(true);
00857         aChangeLocalLoopColor->setIconVisibleInMenu(true);
00858         aChangeUserLoopColor->setIconVisibleInMenu(true);
00859         aChangeVirtualLoopColor->setIconVisibleInMenu(true);
00860         aChangeLocalPathColor->setIconVisibleInMenu(true);
00861         aChangeGlobalPathColor->setIconVisibleInMenu(true);
00862 
00863         menu.addSeparator();
00864         QAction * aSetNodeSize = menu.addAction(tr("Set node radius..."));
00865         QAction * aSetLinkSize = menu.addAction(tr("Set link width..."));
00866         menu.addSeparator();
00867         QAction * aShowHideGridMap;
00868         QAction * aShowHideOrigin;
00869         QAction * aShowHideReferential;
00870         QAction * aShowHideLocalRadius;
00871         QAction * aClearGlobalPath;
00872         if(_gridMap->isVisible())
00873         {
00874                 aShowHideGridMap = menu.addAction(tr("Hide grid map"));
00875         }
00876         else
00877         {
00878                 aShowHideGridMap = menu.addAction(tr("Show grid map"));
00879         }
00880         if(_originReferential->isVisible())
00881         {
00882                 aShowHideOrigin = menu.addAction(tr("Hide origin referential"));
00883         }
00884         else
00885         {
00886                 aShowHideOrigin = menu.addAction(tr("Show origin referential"));
00887         }
00888         if(_referential->isVisible())
00889         {
00890                 aShowHideReferential = menu.addAction(tr("Hide current referential"));
00891         }
00892         else
00893         {
00894                 aShowHideReferential = menu.addAction(tr("Show current referential"));
00895         }
00896         if(_localRadius->isVisible())
00897         {
00898                 aShowHideLocalRadius = menu.addAction(tr("Hide local radius"));
00899         }
00900         else
00901         {
00902                 aShowHideLocalRadius = menu.addAction(tr("Show local radius"));
00903         }
00904         if(_globalPathLinkItems.size() && _globalPathLinkItems.begin().value()->isVisible())
00905         {
00906                 aClearGlobalPath = menu.addAction(tr("Hide global path"));
00907         }
00908         else
00909         {
00910                 aClearGlobalPath = menu.addAction(tr("Show global path"));
00911         }
00912         aClearGlobalPath->setEnabled(_globalPathLinkItems.size());
00913         menu.addSeparator();
00914         QAction * aRestoreDefaults = menu.addAction(tr("Restore defaults"));
00915 
00916         QAction * r = menu.exec(event->globalPos());
00917         if(r == aScreenShotPNG || r == aScreenShotSVG)
00918         {
00919                 if(_root)
00920                 {
00921                         QString targetDir = _workingDirectory + "/ScreensCaptured";
00922                         QDir dir;
00923                         if(!dir.exists(targetDir))
00924                         {
00925                                 dir.mkdir(targetDir);
00926                         }
00927                         targetDir += "/";
00928                         targetDir += "Graph_view";
00929                         if(!dir.exists(targetDir))
00930                         {
00931                                 dir.mkdir(targetDir);
00932                         }
00933                         targetDir += "/";
00934                         bool isPNG = r == aScreenShotPNG;
00935                         QString name = (QDateTime::currentDateTime().toString("yyMMddhhmmsszzz") + (isPNG?".png":".svg"));
00936 
00937                         if(_gridCellSize)
00938                         {
00939                                 _root->setScale(1.0f/_gridCellSize); // grid map precision (for 5cm grid cell, x20 to have 1pix/5cm)
00940                         }
00941                         else
00942                         {
00943                                 _root->setScale(this->transform().m11()); // current view
00944                         }
00945 
00946                         this->scene()->clearSelection();                                  // Selections would also render to the file
00947                         this->scene()->setSceneRect(this->scene()->itemsBoundingRect());  // Re-shrink the scene to it's bounding contents
00948                         QSize sceneSize = this->scene()->sceneRect().size().toSize();
00949 
00950                         if(isPNG)
00951                         {
00952                                 QImage image(sceneSize, QImage::Format_ARGB32);  // Create the image with the exact size of the shrunk scene
00953                                 image.fill(Qt::transparent);                     // Start all pixels transparent
00954                                 QPainter painter(&image);
00955 
00956                                 this->scene()->render(&painter);
00957                                 image.save(targetDir + name);
00958                         }
00959                         else
00960                         {
00961                                 QSvgGenerator svgGen;
00962 
00963                                 svgGen.setFileName( targetDir + name );
00964                                 svgGen.setSize(sceneSize);
00965                                 svgGen.setViewBox(QRect(0, 0, sceneSize.width(), sceneSize.height()));
00966                                 svgGen.setTitle(tr("RTAB-Map graph"));
00967                                 svgGen.setDescription(tr("RTAB-Map map and graph"));
00968 
00969                                 QPainter painter( &svgGen );
00970 
00971                                 this->scene()->render(&painter);
00972                         }
00973 
00974                         //reset scale
00975                         _root->setScale(1.0f);
00976                         this->scene()->setSceneRect(this->scene()->itemsBoundingRect());  // Re-shrink the scene to it's bounding contents
00977 
00978 
00979                         QDesktopServices::openUrl(QUrl::fromLocalFile(targetDir + name));
00980                 }
00981                 return; // without emitting configChanged
00982         }
00983         else if(r == aChangeNodeColor ||
00984                         r == aChangeCurrentGoalColor ||
00985                         r == aChangeNeighborColor ||
00986                         r == aChangeGlobalLoopColor ||
00987                         r == aChangeLocalLoopColor ||
00988                         r == aChangeUserLoopColor ||
00989                         r == aChangeVirtualLoopColor ||
00990                         r == aChangeLocalPathColor ||
00991                         r == aChangeGlobalPathColor)
00992         {
00993                 QColor color;
00994                 if(r == aChangeNodeColor)
00995                 {
00996                         color = _nodeColor;
00997                 }
00998                 else if(r == aChangeCurrentGoalColor)
00999                 {
01000                         color = _currentGoalColor;
01001                 }
01002                 else if(r == aChangeGlobalLoopColor)
01003                 {
01004                         color = _loopClosureColor;
01005                 }
01006                 else if(r == aChangeLocalLoopColor)
01007                 {
01008                         color = _loopClosureLocalColor;
01009                 }
01010                 else if(r == aChangeUserLoopColor)
01011                 {
01012                         color = _loopClosureUserColor;
01013                 }
01014                 else if(r == aChangeVirtualLoopColor)
01015                 {
01016                         color = _loopClosureVirtualColor;
01017                 }
01018                 else if(r == aChangeLocalPathColor)
01019                 {
01020                         color = _localPathColor;
01021                 }
01022                 else if(r == aChangeGlobalPathColor)
01023                 {
01024                         color = _globalPathColor;
01025                 }
01026                 else //if(r == aChangeNeighborColor)
01027                 {
01028                         color = _neighborColor;
01029                 }
01030                 color = QColorDialog::getColor(color, this);
01031                 if(color.isValid())
01032                 {
01033 
01034                         if(r == aChangeNodeColor)
01035                         {
01036                                 this->setNodeColor(color);
01037                         }
01038                         else if(r == aChangeCurrentGoalColor)
01039                         {
01040                                 this->setCurrentGoalColor(color);
01041                         }
01042                         else if(r == aChangeGlobalLoopColor)
01043                         {
01044                                 this->setGlobalLoopClosureColor(color);
01045                         }
01046                         else if(r == aChangeLocalLoopColor)
01047                         {
01048                                 this->setLocalLoopClosureColor(color);
01049                         }
01050                         else if(r == aChangeUserLoopColor)
01051                         {
01052                                 this->setUserLoopClosureColor(color);
01053                         }
01054                         else if(r == aChangeVirtualLoopColor)
01055                         {
01056                                 this->setVirtualLoopClosureColor(color);
01057                         }
01058                         else if(r == aChangeLocalPathColor)
01059                         {
01060                                 this->setLocalPathColor(color);
01061                         }
01062                         else //if(r == aChangeNeighborColor)
01063                         {
01064                                 this->setNeighborColor(color);
01065                         }
01066                 }
01067                 else
01068                 {
01069                         return; // without emitting configChanged
01070                 }
01071         }
01072         else if(r == aSetNodeSize)
01073         {
01074                 bool ok;
01075                 double value = QInputDialog::getDouble(this, tr("Node radius"), tr("Radius (m)"), _nodeRadius, 0.001, 100, 3, &ok);
01076                 if(ok)
01077                 {
01078                         setNodeRadius(value);
01079                 }
01080         }
01081         else if(r == aSetLinkSize)
01082         {
01083                 bool ok;
01084                 double value = QInputDialog::getDouble(this, tr("Link width"), tr("Width (m)"), _linkWidth, 0, 100, 2, &ok);
01085                 if(ok)
01086                 {
01087                         setLinkWidth(value);
01088                 }
01089         }
01090         else if(r == aShowHideGridMap)
01091         {
01092                 this->setGridMapVisible(!this->isGridMapVisible());
01093         }
01094         else if(r == aShowHideOrigin)
01095         {
01096                 this->setOriginVisible(!this->isOriginVisible());
01097         }
01098         else if(r == aShowHideReferential)
01099         {
01100                 this->setReferentialVisible(!this->isReferentialVisible());
01101         }
01102         else if(r == aShowHideLocalRadius)
01103         {
01104                 this->setLocalRadiusVisible(!this->isLocalRadiusVisible());
01105         }
01106         else if(r == aRestoreDefaults)
01107         {
01108                 this->restoreDefaults();
01109         }
01110         else if(r == aClearGlobalPath)
01111         {
01112                 for(QMap<int, LinkItem*>::iterator iter=_globalPathLinkItems.begin(); iter!=_globalPathLinkItems.end(); ++iter)
01113                 {
01114                         iter.value()->setVisible(!iter.value()->isVisible());
01115                 }
01116         }
01117         if(r)
01118         {
01119                 emit configChanged();
01120         }
01121 }
01122 
01123 } /* namespace rtabmap */


rtabmap
Author(s): Mathieu Labbe
autogenerated on Fri Aug 28 2015 12:51:31