mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-03 01:50:24 +08:00
Faster graph zoom by using a snapshot-preview (#1728)
* Faster graph zoom by using a snapshot-preview * Added "Fast Zooming..." menu option --------- Co-authored-by: matlabbe <matlabbe@gmail.com>
This commit is contained in:
@@ -33,6 +33,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include <QGraphicsView>
|
#include <QGraphicsView>
|
||||||
#include <QtCore/QMap>
|
#include <QtCore/QMap>
|
||||||
#include <QtCore/QSettings>
|
#include <QtCore/QSettings>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QGraphicsPixmapItem>
|
||||||
#include <rtabmap/core/Link.h>
|
#include <rtabmap/core/Link.h>
|
||||||
#include <rtabmap/core/GPS.h>
|
#include <rtabmap/core/GPS.h>
|
||||||
#include <opencv2/opencv.hpp>
|
#include <opencv2/opencv.hpp>
|
||||||
@@ -130,6 +132,7 @@ public:
|
|||||||
bool isOrientationENU() const;
|
bool isOrientationENU() const;
|
||||||
ViewPlane getViewPlane() const;
|
ViewPlane getViewPlane() const;
|
||||||
bool isEnsureFrameVisible() const;
|
bool isEnsureFrameVisible() const;
|
||||||
|
int getFastZoomMinNodes() const;
|
||||||
|
|
||||||
// setters
|
// setters
|
||||||
void setWorkingDirectory(const QString & path);
|
void setWorkingDirectory(const QString & path);
|
||||||
@@ -170,6 +173,7 @@ public:
|
|||||||
void setOrientationENU(bool enabled);
|
void setOrientationENU(bool enabled);
|
||||||
void setViewPlane(ViewPlane plane);
|
void setViewPlane(ViewPlane plane);
|
||||||
void setEnsureFrameVisible(bool visible);
|
void setEnsureFrameVisible(bool visible);
|
||||||
|
void setFastZoomMinNodes(int value);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void configChanged();
|
void configChanged();
|
||||||
@@ -184,11 +188,14 @@ protected:
|
|||||||
virtual void wheelEvent ( QWheelEvent * event );
|
virtual void wheelEvent ( QWheelEvent * event );
|
||||||
virtual void mouseMoveEvent(QMouseEvent * event);
|
virtual void mouseMoveEvent(QMouseEvent * event);
|
||||||
virtual void mousePressEvent(QMouseEvent * event);
|
virtual void mousePressEvent(QMouseEvent * event);
|
||||||
|
virtual void mouseReleaseEvent(QMouseEvent * event);
|
||||||
virtual void contextMenuEvent(QContextMenuEvent * event);
|
virtual void contextMenuEvent(QContextMenuEvent * event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupGraphicsScene();
|
void setupGraphicsScene();
|
||||||
|
void startZoomOverlay();
|
||||||
|
void updateZoomOverlay();
|
||||||
|
void stopZoomOverlay();
|
||||||
private:
|
private:
|
||||||
QString _workingDirectory;
|
QString _workingDirectory;
|
||||||
QColor _nodeColor;
|
QColor _nodeColor;
|
||||||
@@ -248,6 +255,11 @@ private:
|
|||||||
ViewPlane _viewPlane;
|
ViewPlane _viewPlane;
|
||||||
bool _ensureFrameVisible;
|
bool _ensureFrameVisible;
|
||||||
QPoint _previousMousePos;
|
QPoint _previousMousePos;
|
||||||
|
QPoint _initialMousePos;
|
||||||
|
QTimer _zoomDebounceTimer;
|
||||||
|
QGraphicsPixmapItem * _zoomOverlayItem;
|
||||||
|
bool _zoomOverlayActive;
|
||||||
|
int _fastZoomMinNodes;
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace rtabmap */
|
} /* namespace rtabmap */
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include <QGraphicsScene>
|
#include <QGraphicsScene>
|
||||||
#include <QGraphicsEllipseItem>
|
#include <QGraphicsEllipseItem>
|
||||||
#include <QGraphicsRectItem>
|
#include <QGraphicsRectItem>
|
||||||
|
#include <QGraphicsPixmapItem>
|
||||||
#include <QtGui/QWheelEvent>
|
#include <QtGui/QWheelEvent>
|
||||||
#include <QGraphicsSceneHoverEvent>
|
#include <QGraphicsSceneHoverEvent>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
@@ -341,10 +342,18 @@ GraphViewer::GraphViewer(QWidget * parent) :
|
|||||||
_orientationENU(false),
|
_orientationENU(false),
|
||||||
_mouseTracking(false),
|
_mouseTracking(false),
|
||||||
_viewPlane(XY),
|
_viewPlane(XY),
|
||||||
_ensureFrameVisible(true)
|
_ensureFrameVisible(true),
|
||||||
|
_previousMousePos(-1,-1),
|
||||||
|
_initialMousePos(-1,-1),
|
||||||
|
_zoomDebounceTimer(this),
|
||||||
|
_zoomOverlayItem(0),
|
||||||
|
_zoomOverlayActive(false),
|
||||||
|
_fastZoomMinNodes(0)
|
||||||
{
|
{
|
||||||
this->setDragMode(QGraphicsView::ScrollHandDrag);
|
this->setDragMode(QGraphicsView::ScrollHandDrag);
|
||||||
_workingDirectory = QDir::homePath();
|
_workingDirectory = QDir::homePath();
|
||||||
|
_zoomDebounceTimer.setSingleShot(true);
|
||||||
|
connect(&_zoomDebounceTimer, &QTimer::timeout, this, &GraphViewer::stopZoomOverlay);
|
||||||
|
|
||||||
setupGraphicsScene();
|
setupGraphicsScene();
|
||||||
|
|
||||||
@@ -355,6 +364,9 @@ GraphViewer::GraphViewer(QWidget * parent) :
|
|||||||
this->restoreDefaults();
|
this->restoreDefaults();
|
||||||
|
|
||||||
this->fitInView(this->sceneRect(), Qt::KeepAspectRatio);
|
this->fitInView(this->sceneRect(), Qt::KeepAspectRatio);
|
||||||
|
|
||||||
|
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
|
||||||
|
setResizeAnchor(QGraphicsView::AnchorUnderMouse);
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphViewer::~GraphViewer()
|
GraphViewer::~GraphViewer()
|
||||||
@@ -363,11 +375,20 @@ GraphViewer::~GraphViewer()
|
|||||||
|
|
||||||
void GraphViewer::setupGraphicsScene()
|
void GraphViewer::setupGraphicsScene()
|
||||||
{
|
{
|
||||||
|
_zoomDebounceTimer.stop();
|
||||||
|
_zoomOverlayActive = false;
|
||||||
|
_zoomOverlayItem = 0;
|
||||||
if(this->scene())
|
if(this->scene())
|
||||||
{
|
{
|
||||||
delete this->scene();
|
delete this->scene();
|
||||||
}
|
}
|
||||||
this->setScene(new QGraphicsScene(this));
|
this->setScene(new QGraphicsScene(this));
|
||||||
|
_zoomOverlayItem = this->scene()->addPixmap(QPixmap());
|
||||||
|
_zoomOverlayItem->setZValue(1000000);
|
||||||
|
_zoomOverlayItem->setVisible(false);
|
||||||
|
_zoomOverlayItem->setAcceptedMouseButtons(Qt::NoButton);
|
||||||
|
_zoomOverlayItem->setAcceptHoverEvents(false);
|
||||||
|
|
||||||
_world = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
|
_world = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
|
||||||
_root = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
|
_root = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
|
||||||
_root->setParentItem(_world);
|
_root->setParentItem(_world);
|
||||||
@@ -507,6 +528,62 @@ void GraphViewer::setupGraphicsScene()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GraphViewer::startZoomOverlay()
|
||||||
|
{
|
||||||
|
if(_fastZoomMinNodes==0 || _nodeItems.size() < _fastZoomMinNodes)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_zoomOverlayActive || !_zoomOverlayItem)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QRect viewportRect = this->viewport()->rect();
|
||||||
|
if(viewportRect.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QPixmap snapshot = this->viewport()->grab();
|
||||||
|
if(snapshot.isNull())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QRectF sourceRect = this->mapToScene(viewportRect).boundingRect();
|
||||||
|
const qreal sx = sourceRect.width() / qreal(snapshot.width());
|
||||||
|
const qreal sy = sourceRect.height() / qreal(snapshot.height());
|
||||||
|
|
||||||
|
_zoomOverlayItem->setPixmap(snapshot);
|
||||||
|
_zoomOverlayItem->setTransform(QTransform::fromScale(sx, sy));
|
||||||
|
_zoomOverlayItem->setPos(sourceRect.topLeft());
|
||||||
|
_zoomOverlayItem->show();
|
||||||
|
_world->setVisible(false);
|
||||||
|
_zoomOverlayActive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphViewer::stopZoomOverlay()
|
||||||
|
{
|
||||||
|
_zoomDebounceTimer.stop();
|
||||||
|
if(!_zoomOverlayActive)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_zoomOverlayActive = false;
|
||||||
|
if(_zoomOverlayItem)
|
||||||
|
{
|
||||||
|
_zoomOverlayItem->hide();
|
||||||
|
}
|
||||||
|
if(_world)
|
||||||
|
{
|
||||||
|
_world->setVisible(true);
|
||||||
|
}
|
||||||
|
this->viewport()->update();
|
||||||
|
}
|
||||||
|
|
||||||
void GraphViewer::setWorldMapRotation(const float & theta)
|
void GraphViewer::setWorldMapRotation(const float & theta)
|
||||||
{
|
{
|
||||||
_worldMapRotation = theta;
|
_worldMapRotation = theta;
|
||||||
@@ -1432,6 +1509,7 @@ void GraphViewer::clearNodeColorByValue()
|
|||||||
|
|
||||||
void GraphViewer::clearAll()
|
void GraphViewer::clearAll()
|
||||||
{
|
{
|
||||||
|
stopZoomOverlay();
|
||||||
clearMap();
|
clearMap();
|
||||||
clearGraph();
|
clearGraph();
|
||||||
|
|
||||||
@@ -1489,6 +1567,7 @@ void GraphViewer::saveSettings(QSettings & settings, const QString & group) cons
|
|||||||
settings.setValue("orientation_ENU", this->isOrientationENU());
|
settings.setValue("orientation_ENU", this->isOrientationENU());
|
||||||
settings.setValue("view_plane", (int)this->getViewPlane());
|
settings.setValue("view_plane", (int)this->getViewPlane());
|
||||||
settings.setValue("ensure_frame_visible", (int)this->isEnsureFrameVisible());
|
settings.setValue("ensure_frame_visible", (int)this->isEnsureFrameVisible());
|
||||||
|
settings.setValue("fast_zoom_min_nodes", this->getFastZoomMinNodes());
|
||||||
if(!group.isEmpty())
|
if(!group.isEmpty())
|
||||||
{
|
{
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
@@ -1540,6 +1619,7 @@ void GraphViewer::loadSettings(QSettings & settings, const QString & group)
|
|||||||
this->setOrientationENU(settings.value("orientation_ENU", this->isOrientationENU()).toBool());
|
this->setOrientationENU(settings.value("orientation_ENU", this->isOrientationENU()).toBool());
|
||||||
this->setViewPlane((ViewPlane)settings.value("view_plane", (int)this->getViewPlane()).toInt());
|
this->setViewPlane((ViewPlane)settings.value("view_plane", (int)this->getViewPlane()).toInt());
|
||||||
this->setEnsureFrameVisible(settings.value("ensure_frame_visible", this->isEnsureFrameVisible()).toBool());
|
this->setEnsureFrameVisible(settings.value("ensure_frame_visible", this->isEnsureFrameVisible()).toBool());
|
||||||
|
this->setFastZoomMinNodes(settings.value("fast_zoom_min_nodes", this->getFastZoomMinNodes()).toInt());
|
||||||
if(!group.isEmpty())
|
if(!group.isEmpty())
|
||||||
{
|
{
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
@@ -1602,6 +1682,10 @@ bool GraphViewer::isEnsureFrameVisible() const
|
|||||||
{
|
{
|
||||||
return _ensureFrameVisible;
|
return _ensureFrameVisible;
|
||||||
}
|
}
|
||||||
|
int GraphViewer::getFastZoomMinNodes() const
|
||||||
|
{
|
||||||
|
return _fastZoomMinNodes;
|
||||||
|
}
|
||||||
|
|
||||||
void GraphViewer::setWorkingDirectory(const QString & path)
|
void GraphViewer::setWorkingDirectory(const QString & path)
|
||||||
{
|
{
|
||||||
@@ -1967,6 +2051,11 @@ void GraphViewer::setEnsureFrameVisible(bool visible)
|
|||||||
_ensureFrameVisible = visible;
|
_ensureFrameVisible = visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GraphViewer::setFastZoomMinNodes(int value)
|
||||||
|
{
|
||||||
|
_fastZoomMinNodes = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void GraphViewer::restoreDefaults()
|
void GraphViewer::restoreDefaults()
|
||||||
{
|
{
|
||||||
@@ -1997,18 +2086,26 @@ void GraphViewer::restoreDefaults()
|
|||||||
setGlobalPathVisible(true);
|
setGlobalPathVisible(true);
|
||||||
setLocalPathVisible(true);
|
setLocalPathVisible(true);
|
||||||
setGtGraphVisible(true);
|
setGtGraphVisible(true);
|
||||||
|
setFastZoomMinNodes(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphViewer::wheelEvent ( QWheelEvent * event )
|
void GraphViewer::wheelEvent(QWheelEvent * event)
|
||||||
{
|
{
|
||||||
if(event->angleDelta().y() < 0)
|
if(event->angleDelta().y() == 0)
|
||||||
{
|
{
|
||||||
this->scale(0.95, 0.95);
|
event->ignore();
|
||||||
}
|
return;
|
||||||
else
|
|
||||||
{
|
|
||||||
this->scale(1.05, 1.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start buffering zoom interaction with a raster snapshot.
|
||||||
|
startZoomOverlay();
|
||||||
|
|
||||||
|
const qreal factor = event->angleDelta().y() < 0 ? 0.95 : 1.05;
|
||||||
|
this->scale(factor, factor);
|
||||||
|
|
||||||
|
// When wheel stops for 400 ms, restore the real scene and let it redraw once.
|
||||||
|
_zoomDebounceTimer.start(400);
|
||||||
|
event->accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphViewer::mouseMoveEvent(QMouseEvent * event)
|
void GraphViewer::mouseMoveEvent(QMouseEvent * event)
|
||||||
@@ -2029,14 +2126,16 @@ void GraphViewer::mouseMoveEvent(QMouseEvent * event)
|
|||||||
if (event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier) && event->buttons() & Qt::LeftButton) {
|
if (event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier) && event->buttons() & Qt::LeftButton) {
|
||||||
// same modifiers than 3D view, change zoom
|
// same modifiers than 3D view, change zoom
|
||||||
if(_previousMousePos.y()!=0) {
|
if(_previousMousePos.y()!=0) {
|
||||||
if(event->pos().y() - _previousMousePos.y() > 0)
|
qreal factor = (event->pos().y() - _previousMousePos.y() > 0) ? 0.98 : 1.02;
|
||||||
{
|
|
||||||
this->scale(0.98, 0.98);
|
QGraphicsView::ViewportAnchor oldAnchor = this->transformationAnchor();
|
||||||
}
|
this->setTransformationAnchor(QGraphicsView::NoAnchor);
|
||||||
else
|
QPointF scenePointBefore = this->mapToScene(_initialMousePos);
|
||||||
{
|
this->scale(factor, factor);
|
||||||
this->scale(1.02, 1.02);
|
QPointF scenePointAfter = this->mapToScene(_initialMousePos);
|
||||||
}
|
QPointF delta = scenePointAfter - scenePointBefore;
|
||||||
|
this->translate(delta.x(), delta.y());
|
||||||
|
this->setTransformationAnchor(oldAnchor);
|
||||||
}
|
}
|
||||||
_previousMousePos = event->pos();
|
_previousMousePos = event->pos();
|
||||||
return;
|
return;
|
||||||
@@ -2047,11 +2146,21 @@ void GraphViewer::mouseMoveEvent(QMouseEvent * event)
|
|||||||
|
|
||||||
void GraphViewer::mousePressEvent(QMouseEvent * event)
|
void GraphViewer::mousePressEvent(QMouseEvent * event)
|
||||||
{
|
{
|
||||||
QGraphicsItem *item = this->scene()->itemAt(mapToScene(event->pos()), QTransform());
|
if((event->button() == Qt::LeftButton) &&
|
||||||
|
(event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)))
|
||||||
|
{
|
||||||
|
startZoomOverlay();
|
||||||
|
_previousMousePos = event->pos();
|
||||||
|
_initialMousePos = event->pos();
|
||||||
|
event->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QGraphicsItem * item = this->scene()->itemAt(mapToScene(event->pos()), QTransform());
|
||||||
if(item)
|
if(item)
|
||||||
{
|
{
|
||||||
NodeItem *nodeItem = qgraphicsitem_cast<NodeItem*>(item);
|
NodeItem * nodeItem = qgraphicsitem_cast<NodeItem*>(item);
|
||||||
LinkItem *linkItem = qgraphicsitem_cast<LinkItem*>(item);
|
LinkItem * linkItem = qgraphicsitem_cast<LinkItem*>(item);
|
||||||
if(nodeItem && nodeItem->parentItem() == _graphRoot && nodeItem->id() != 0)
|
if(nodeItem && nodeItem->parentItem() == _graphRoot && nodeItem->id() != 0)
|
||||||
{
|
{
|
||||||
Q_EMIT nodeSelected(nodeItem->id());
|
Q_EMIT nodeSelected(nodeItem->id());
|
||||||
@@ -2071,6 +2180,19 @@ void GraphViewer::mousePressEvent(QMouseEvent * event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GraphViewer::mouseReleaseEvent(QMouseEvent * event)
|
||||||
|
{
|
||||||
|
_previousMousePos = QPoint(-1, -1);
|
||||||
|
_initialMousePos = QPoint(-1, -1);
|
||||||
|
|
||||||
|
if(_zoomOverlayActive)
|
||||||
|
{
|
||||||
|
stopZoomOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
QGraphicsView::mouseReleaseEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
QIcon createIcon(const QColor & color)
|
QIcon createIcon(const QColor & color)
|
||||||
{
|
{
|
||||||
QPixmap pixmap(50, 50);
|
QPixmap pixmap(50, 50);
|
||||||
@@ -2167,6 +2289,7 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
|
|||||||
QAction * aShowHideOdomCacheOverlay;
|
QAction * aShowHideOdomCacheOverlay;
|
||||||
QAction * aOrientationENU;
|
QAction * aOrientationENU;
|
||||||
QAction * aMouseTracking;
|
QAction * aMouseTracking;
|
||||||
|
QAction * aFastZooming;
|
||||||
QAction * aViewPlaneXY;
|
QAction * aViewPlaneXY;
|
||||||
QAction * aViewPlaneXZ;
|
QAction * aViewPlaneXZ;
|
||||||
QAction * aViewPlaneYZ;
|
QAction * aViewPlaneYZ;
|
||||||
@@ -2269,6 +2392,7 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
|
|||||||
aMouseTracking->setCheckable(true);
|
aMouseTracking->setCheckable(true);
|
||||||
aMouseTracking->setChecked(_mouseTracking);
|
aMouseTracking->setChecked(_mouseTracking);
|
||||||
aMouseTracking->setEnabled(_viewPlane == XY);
|
aMouseTracking->setEnabled(_viewPlane == XY);
|
||||||
|
aFastZooming = menu.addAction(tr("Fast Zooming..."));
|
||||||
aShowHideGraph->setEnabled(_viewPlane == XY);
|
aShowHideGraph->setEnabled(_viewPlane == XY);
|
||||||
aShowHideGlobalPath->setEnabled(_globalPathLinkItems.size());
|
aShowHideGlobalPath->setEnabled(_globalPathLinkItems.size());
|
||||||
aShowHideLocalPath->setEnabled(_localPathLinkItems.size());
|
aShowHideLocalPath->setEnabled(_localPathLinkItems.size());
|
||||||
@@ -2714,6 +2838,15 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
|
|||||||
{
|
{
|
||||||
_mouseTracking = aMouseTracking->isChecked();
|
_mouseTracking = aMouseTracking->isChecked();
|
||||||
}
|
}
|
||||||
|
else if(r == aFastZooming)
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
double value = QInputDialog::getInt(this, tr("Fast Zooming"), tr("Minimum number of nodes to raster the graph\nwhile zooming (0=disabled):"), _fastZoomMinNodes, 0, 2147483647, 100, &ok);
|
||||||
|
if(ok)
|
||||||
|
{
|
||||||
|
setFastZoomMinNodes(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
else if(r == aViewPlaneXY)
|
else if(r == aViewPlaneXY)
|
||||||
{
|
{
|
||||||
this->setViewPlane(XY);
|
this->setViewPlane(XY);
|
||||||
|
|||||||
Reference in New Issue
Block a user