Commit fd83b5dc authored by VaL Doroshchuk's avatar VaL Doroshchuk Committed by Dennis Nienhüser

Introduced rotation the globe by right mouse button.

Summary: Also applied kinetic spinning like it is with changing position.

Test Plan:
to-check: open a map and push the right button of mouse, the globe is going to be rotated by moving mouse by x and y axis.
Also if you unclick the button, kinetic spinning will be enabled like it is when you use left mouse button.

Reviewers: #marble, nienhueser

Reviewed By: #marble, nienhueser

Subscribers: nienhueser, #marble

Tags: #marble

Differential Revision: https://phabricator.kde.org/D5650
parent 82a06cce
......@@ -467,6 +467,11 @@ namespace Marble
}
}
void MarbleAbstractPresenter::headingOn(qreal heading)
{
map()->setHeading(heading);
}
void MarbleAbstractPresenter::setCenterLatitude(qreal lat, FlyToMode mode)
{
centerOn(centerLongitude(), lat, mode);
......
......@@ -131,6 +131,7 @@ class ViewportParams;
void centerOn(const GeoDataCoordinates &point, bool animated = false);
void centerOn(const GeoDataLatLonBox& box, bool animated = false);
void centerOn(const GeoDataPlacemark& placemark, bool animated = false);
void headingOn(qreal heading);
void setCenterLatitude(qreal lat, FlyToMode mode);
void setCenterLongitude(qreal lon, FlyToMode mode);
......
......@@ -146,8 +146,6 @@ class Q_DECL_HIDDEN MarbleDefaultInputHandler::Private
// Indicates if the left mouse button has been pressed already.
bool m_leftPressed;
// Indicates whether the drag was started by a click above or below the visible pole.
int m_leftPressedDirection;
// Indicates if the middle mouse button has been pressed already.
bool m_midPressed;
// The mouse pointer x position when the left mouse button has been pressed.
......@@ -158,6 +156,17 @@ class Q_DECL_HIDDEN MarbleDefaultInputHandler::Private
int m_midPressedY;
int m_startingRadius;
// Indicates if the right mouse button has been pressed already.
bool m_rightPressed;
// Point where the right mouse button has been pressed on.
QPoint m_rightOrigin;
// Position to calculate the heading.
// Indicates previous position since mouse has been moved.
QPoint m_rightPosition;
// Indicates the heading when the right mouse button has been pressed
// and mouse is moving.
qreal m_heading;
// The center longitude in radian when the left mouse button has been pressed.
qreal m_leftPressedLon;
// The center latitude in radian when the left mouse button has been pressed.
......@@ -180,6 +189,8 @@ class Q_DECL_HIDDEN MarbleDefaultInputHandler::Private
MarbleDefaultInputHandler::Private::Private()
: m_leftPressed(false),
m_midPressed(false),
m_rightPressed(false),
m_heading(0),
m_dragThreshold(MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ? 15 : 3)
{
m_curpmtl.load(QStringLiteral(":/marble/cursor/tl.png"));
......@@ -219,6 +230,8 @@ MarbleDefaultInputHandler::MarbleDefaultInputHandler(MarbleAbstractPresenter *ma
d->m_kineticSpinning.setUpdateInterval(35);
connect(&d->m_kineticSpinning, SIGNAL(positionChanged(qreal,qreal)),
MarbleInputHandler::d->m_marblePresenter, SLOT(centerOn(qreal,qreal)));
connect(&d->m_kineticSpinning, SIGNAL(headingChanged(qreal)),
MarbleInputHandler::d->m_marblePresenter, SLOT(headingOn(qreal)));
connect(&d->m_kineticSpinning, SIGNAL(finished()), SLOT(restoreViewContext()));
// Left and right mouse button signals.
......@@ -481,38 +494,12 @@ void MarbleDefaultInputHandler::handleLeftMouseButtonPress(QMouseEvent *event)
d->m_leftPressedLon = MarbleInputHandler::d->m_marblePresenter->centerLongitude();
d->m_leftPressedLat = MarbleInputHandler::d->m_marblePresenter->centerLatitude();
d->m_leftPressedDirection = 1;
if (MarbleInputHandler::d->m_inertialEarthRotation)
{
d->m_kineticSpinning.stop();
d->m_kineticSpinning.setPosition(d->m_leftPressedLon, d->m_leftPressedLat);
}
// Choose spin direction by taking into account whether we
// drag above or below the visible pole.
if (MarbleInputHandler::d->m_marblePresenter->map()->projection() == Spherical)
{
if (d->m_leftPressedLat >= 0)
{ // The visible pole is the north pole
qreal northPoleX, northPoleY;
MarbleInputHandler::d->m_marblePresenter->map()->screenCoordinates(0.0, 90.0, northPoleX, northPoleY);
if (event->y() < northPoleY)
{
d->m_leftPressedDirection = -1;
}
}
else
{ // The visible pole is the south pole
qreal southPoleX, southPoleY;
MarbleInputHandler::d->m_marblePresenter->map()->screenCoordinates(0.0, -90.0, southPoleX, southPoleY);
if (event->y() > southPoleY)
{
d->m_leftPressedDirection = -1;
}
}
}
if (event->modifiers() & Qt::ControlModifier)
{
mDebug() << Q_FUNC_INFO << "Starting selection";
......@@ -542,7 +529,15 @@ void MarbleDefaultInputHandler::handleMiddleMouseButtonPress(QMouseEvent *event)
void MarbleDefaultInputHandler::handleRightMouseButtonPress(QMouseEvent *event)
{
emit rmbRequest(event->x(), event->y());
d->m_rightPressed = true;
d->m_rightOrigin = event->pos();
d->m_rightPosition = event->pos();
d->m_heading = MarbleInputHandler::d->m_marblePresenter->map()->heading();
if (MarbleInputHandler::d->m_inertialEarthRotation)
{
d->m_kineticSpinning.stop();
d->m_kineticSpinning.setHeading(d->m_heading);
}
}
void MarbleDefaultInputHandler::handleMouseButtonRelease(QMouseEvent *event)
......@@ -573,6 +568,20 @@ void MarbleDefaultInputHandler::handleMouseButtonRelease(QMouseEvent *event)
if (event->type() == QEvent::MouseButtonRelease && event->button() == Qt::RightButton)
{
if (d->m_rightOrigin == event->pos())
{
emit rmbRequest(event->x(), event->y());
}
d->m_rightPressed = false;
if (MarbleInputHandler::d->m_inertialEarthRotation)
{
d->m_kineticSpinning.start();
}
else
{
MarbleInputHandler::d->m_marblePresenter->setViewContext(Still);
}
}
if (event->type() == QEvent::MouseButtonRelease && event->button() == Qt::LeftButton
......@@ -786,20 +795,24 @@ bool MarbleDefaultInputHandler::handleMouseEvent(QMouseEvent *event)
if (d->m_leftPressed && !selectionRubber()->isVisible())
{
qreal radius = (qreal)(MarbleInputHandler::d->m_marblePresenter->radius());
int deltax = event->x() - d->m_leftPressedX;
int deltay = event->y() - d->m_leftPressedY;
qreal deltax = event->x() - d->m_leftPressedX;
qreal deltay = event->y() - d->m_leftPressedY;
if (abs(deltax) > d->m_dragThreshold
|| abs(deltay) > d->m_dragThreshold
if (qAbs(deltax) > d->m_dragThreshold
|| qAbs(deltay) > d->m_dragThreshold
|| !d->m_lmbTimer.isActive())
{
MarbleInputHandler::d->m_marblePresenter->setViewContext(Animation);
d->m_pressAndHoldTimer.stop();
d->m_lmbTimer.stop();
const qreal posLon = d->m_leftPressedLon - 90.0 * d->m_leftPressedDirection * deltax / radius;
const qreal posLat = d->m_leftPressedLat + 90.0 * deltay / radius;
const Quaternion rotation = Quaternion::fromEuler( 0, 0, MarbleInputHandler::d->m_marblePresenter->map()->heading() * DEG2RAD );
Quaternion quat = Quaternion::fromSpherical( - M_PI/2 * deltax / radius, + M_PI/2 * deltay / radius );
quat.rotateAroundAxis( rotation );
qreal lon, lat;
quat.getSpherical( lon, lat );
const qreal posLon = d->m_leftPressedLon + RAD2DEG * lon;
const qreal posLat = d->m_leftPressedLat + RAD2DEG * lat;
MarbleInputHandler::d->m_marblePresenter->centerOn(posLon, posLat);
if (MarbleInputHandler::d->m_inertialEarthRotation)
{
......@@ -815,6 +828,36 @@ bool MarbleDefaultInputHandler::handleMouseEvent(QMouseEvent *event)
MarbleInputHandler::d->m_marblePresenter->setRadius(d->m_startingRadius * pow(1.005, dy));
}
if (d->m_rightPressed)
{
qreal centerX, centerY;
MarbleInputHandler::d->m_marblePresenter->map()->screenCoordinates(
MarbleInputHandler::d->m_marblePresenter->centerLongitude(),
MarbleInputHandler::d->m_marblePresenter->centerLatitude(), centerX, centerY);
// Deltas from previous position.
int dx = event->x() - d->m_rightPosition.x();
int dy = event->y() - d->m_rightPosition.y();
d->m_rightPosition = event->pos();
// Moving on the bottom should be opposite direction.
int sign = event->y() > centerY ? -1 : 1;
// Left top and right bottom sides for y axis should be opposite direction.
if ((event->x() < centerX && event->y() < centerY) || (event->x() > centerX && event->y() > centerY))
{
dy *= -1;
}
const qreal speedFactor = 0.3;
d->m_heading += (dx + dy) * sign * speedFactor;
MarbleInputHandler::d->m_marblePresenter->map()->setHeading(d->m_heading);
if (MarbleInputHandler::d->m_inertialEarthRotation)
{
d->m_kineticSpinning.setHeading(d->m_heading);
}
}
if (selectionRubber()->isVisible())
{
// We change selection.
......
......@@ -1444,6 +1444,19 @@ const StyleBuilder* MarbleMap::styleBuilder() const
return &d->m_styleBuilder;
}
qreal MarbleMap::heading() const
{
return d->m_viewport.heading() * RAD2DEG;
}
void MarbleMap::setHeading( qreal heading )
{
d->m_viewport.setHeading( heading * DEG2RAD );
d->m_textureLayer.setNeedsUpdate();
emit visibleLatLonAltBoxChanged( d->m_viewport.viewLatLonAltBox() );
}
}
#include "moc_MarbleMap.cpp"
......@@ -220,6 +220,8 @@ class MARBLE_EXPORT MarbleMap : public QObject
*/
qreal centerLatitude() const;
qreal heading() const;
/**
* @since 0.26.0
*/
......@@ -445,6 +447,8 @@ class MARBLE_EXPORT MarbleMap : public QObject
*/
void setRadius( int radius );
void setHeading( qreal heading );
/**
* @brief Rotate the view by the two angles phi and theta.
* @param deltaLon an angle that specifies the change in terms of longitude
......
......@@ -1217,6 +1217,16 @@ const StyleBuilder* MarbleWidget::styleBuilder() const
return d->m_map.styleBuilder();
}
void MarbleWidget::setHeading( qreal heading )
{
d->m_map.setHeading( heading );
}
qreal MarbleWidget::heading() const
{
return d->m_map.heading();
}
}
#include "moc_MarbleWidget.cpp"
......@@ -357,6 +357,8 @@ class MARBLE_EXPORT MarbleWidget : public QWidget
*/
qreal centerLatitude() const;
qreal heading() const;
/**
* @brief Return how much the map will move if one of the move slots are called.
* @return The move step.
......@@ -721,6 +723,8 @@ class MARBLE_EXPORT MarbleWidget : public QWidget
*/
void setCenterLongitude( qreal lon, FlyToMode mode = Instant );
void setHeading( qreal heading );
/**
* @brief Move left by the moveStep.
*/
......
......@@ -51,6 +51,7 @@ public:
// Parameters that determine the painting
qreal m_centerLongitude;
qreal m_centerLatitude;
qreal m_heading;
Quaternion m_planetAxis; // Position, coded in a quaternion
matrix m_planetAxisMatrix;
int m_radius; // Zoom level (pixels / globe radius)
......@@ -91,6 +92,7 @@ ViewportParamsPrivate::ViewportParamsPrivate( Projection projection,
m_currentProjection( abstractProjection( projection ) ),
m_centerLongitude( centerLongitude ),
m_centerLatitude( centerLatitude ),
m_heading( 0 ),
m_planetAxis(),
m_planetAxisMatrix(),
m_radius( radius ),
......@@ -255,11 +257,39 @@ void ViewportParams::centerOn( qreal lon, qreal lat )
d->m_centerLongitude = lon;
d->m_centerLatitude = lat;
d->m_planetAxis = Quaternion::fromEuler( -lat, lon, 0.0 );
const Quaternion roll = Quaternion::fromEuler( 0, 0, d->m_heading );
const Quaternion quat = Quaternion::fromEuler( -lat, lon, 0.0 );
d->m_planetAxis = quat * roll;
d->m_planetAxis.normalize();
d->m_dirtyBox = true;
d->m_planetAxis.inverse().toMatrix( d->m_planetAxisMatrix );
d->m_planetAxis.normalize();
}
void ViewportParams::setHeading( qreal heading )
{
d->m_heading = heading;
const Quaternion roll = Quaternion::fromEuler( 0, 0, heading );
const qreal centerLat = centerLatitude();
const qreal centerLon = centerLongitude();
const Quaternion quat = Quaternion::fromEuler( -centerLat, centerLon, 0 );
d->m_planetAxis = quat * roll;
d->m_planetAxis.normalize();
d->m_dirtyBox = true;
d->m_planetAxis.inverse().toMatrix( d->m_planetAxisMatrix );
d->m_planetAxis.normalize();
}
qreal ViewportParams::heading() const
{
return d->m_heading;
}
Quaternion ViewportParams::planetAxis() const
......
......@@ -98,6 +98,7 @@ class MARBLE_EXPORT ViewportParams
void setRadius(int radius);
void centerOn( qreal lon, qreal lat );
void setHeading( qreal heading );
Quaternion planetAxis() const;
const matrix &planetAxisMatrix() const;
......@@ -186,6 +187,7 @@ class MARBLE_EXPORT ViewportParams
qreal &lon, qreal &lat,
GeoDataCoordinates::Unit unit = GeoDataCoordinates::Degree ) const;
qreal heading() const;
bool mapCoversViewport() const;
QPainterPath mapShape() const;
......
......@@ -41,11 +41,16 @@ public:
int duration;
QPointF position;
qreal heading;
QPointF velocity;
qreal velocityHeading;
QPointF deacceleration;
qreal deaccelerationHeading;
QTime timestamp;
QPointF lastPosition;
qreal lastHeading;
bool changingPosition;
KineticModelPrivate();
};
......@@ -53,9 +58,14 @@ public:
KineticModelPrivate::KineticModelPrivate()
: duration(1403)
, position(0, 0)
, heading(0)
, velocity(0, 0)
, velocityHeading(0)
, deacceleration(0, 0)
, deaccelerationHeading(0)
, lastPosition(0, 0)
, lastHeading(0)
, changingPosition(true)
{
}
......@@ -117,6 +127,23 @@ void KineticModel::setPosition(qreal posX, qreal posY)
d_ptr->velocity = 0.2 * lastSpeed + 0.8 * currentSpeed;
d_ptr->lastPosition = d_ptr->position;
d_ptr->changingPosition = true;
d_ptr->timestamp.start();
}
void KineticModel::setHeading(qreal heading)
{
d_ptr->heading = heading;
int elapsed = d_ptr->timestamp.elapsed();
qreal delta = static_cast<qreal>( elapsed ) / 1000.0;
qreal lastSpeed = d_ptr->velocityHeading;
qreal currentSpeed = delta ? ( d_ptr->heading - d_ptr->lastHeading ) / delta : 0;
d_ptr->velocityHeading = 0.5 * lastSpeed + 0.2 * currentSpeed;
d_ptr->lastHeading = d_ptr->heading;
d_ptr->changingPosition = false;
d_ptr->timestamp.start();
}
......@@ -148,6 +175,7 @@ void KineticModel::stop()
d->ticker.stop();
d->timestamp.start();
d->velocity = QPointF(0, 0);
d->velocityHeading = 0;
}
void KineticModel::start()
......@@ -170,6 +198,8 @@ void KineticModel::start()
d->deacceleration.setY( -d->deacceleration.y() );
}
d->deaccelerationHeading = qAbs(d->velocityHeading) * 1000 / ( 1 + d_ptr->duration );
if (!d->ticker.isActive())
d->ticker.start();
}
......@@ -181,30 +211,47 @@ void KineticModel::update()
int elapsed = qMin( d->timestamp.elapsed(), 100 ); // limit to 100msec to reduce catapult effect (bug 294608)
qreal delta = static_cast<qreal>(elapsed) / 1000.0;
d->position += d->velocity * delta;
QPointF vstep = d->deacceleration * delta;
if (d->velocity.x() < vstep.x() && d->velocity.x() >= -vstep.x()) {
d->velocity.setX( 0 );
bool stop = false;
if (d->changingPosition) {
d->position += d->velocity * delta;
QPointF vstep = d->deacceleration * delta;
if (d->velocity.x() < vstep.x() && d->velocity.x() >= -vstep.x()) {
d->velocity.setX( 0 );
} else {
if (d->velocity.x() > 0)
d->velocity.setX( d->velocity.x() - vstep.x() );
else
d->velocity.setX( d->velocity.x() + vstep.x() );
}
if (d->velocity.y() < vstep.y() && d->velocity.y() >= -vstep.y()) {
d->velocity.setY( 0 );
} else {
if (d->velocity.y() > 0)
d->velocity.setY( d->velocity.y() - vstep.y() );
else
d->velocity.setY( d->velocity.y() + vstep.y() );
}
stop = d->velocity.isNull();
emit positionChanged( d->position.x(), d->position.y() );
} else {
if (d->velocity.x() > 0)
d->velocity.setX( d->velocity.x() - vstep.x() );
else
d->velocity.setX( d->velocity.x() + vstep.x() );
}
d->heading += d->velocityHeading * delta;
qreal vstep = d->deaccelerationHeading * delta; // Always positive.
if ((d->velocityHeading < vstep && d->velocityHeading >= -vstep) || !vstep) {
d->velocityHeading = 0;
} else {
d->velocityHeading += d->velocityHeading > 0 ? -1 * vstep : vstep;
}
if (d->velocity.y() < vstep.y() && d->velocity.y() >= -vstep.y()) {
d->velocity.setY( 0 );
} else {
if (d->velocity.y() > 0)
d->velocity.setY( d->velocity.y() - vstep.y() );
else
d->velocity.setY( d->velocity.y() + vstep.y() );
}
stop = !d->velocityHeading;
emit positionChanged( d->position.x(), d->position.y() );
emit headingChanged( d->heading );
}
if (d->velocity.isNull()) {
if (stop) {
emit finished();
d->ticker.stop();
}
......
......@@ -56,6 +56,7 @@ public Q_SLOTS:
void setDuration(int ms);
void setPosition(const QPointF& position);
void setPosition(qreal posX, qreal posY);
void setHeading(qreal heading);
void jumpToPosition(const QPointF& position);
void jumpToPosition(qreal posX, qreal posY);
void setUpdateInterval(int ms);
......@@ -64,6 +65,7 @@ public Q_SLOTS:
Q_SIGNALS:
void positionChanged( qreal lon, qreal lat );
void headingChanged( qreal heading );
void finished();
private Q_SLOTS:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment