From db0bb1b170e0f28cc79214913ec72c75a147a53e Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Mon, 24 Aug 2015 16:44:46 -0400 Subject: [PATCH] improve gestures in perspective view The location under your finger stays under your finger as you pan. The location you double tap to zoom stays at the same point. The location at the center of a pinch zoom stays at the center. The location at the center of a rotation stays at the center. --- android/cpp/jni.cpp | 2 +- include/mbgl/map/map.hpp | 1 + platform/default/glfw_view.cpp | 5 ++- platform/ios/MGLMapView.mm | 11 +++++-- src/mbgl/map/map.cpp | 5 +++ src/mbgl/map/transform.cpp | 60 +++++++++++++++++++++------------- src/mbgl/map/transform.hpp | 1 + 7 files changed, 59 insertions(+), 26 deletions(-) diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp index d0d3ea2970f..5808dff9c8c 100644 --- a/android/cpp/jni.cpp +++ b/android/cpp/jni.cpp @@ -637,7 +637,7 @@ void JNICALL nativeSetLatLng(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, j return; } - nativeMapView->getMap().setLatLng(mbgl::LatLng(latitude, longitude), std::chrono::milliseconds(duration)); + nativeMapView->getMap().setLatLng(mbgl::LatLng(latitude, longitude), mbgl::Duration(duration)); } jobject JNICALL nativeGetLatLng(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) { diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 65f988b5bfa..52136aeb4bd 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -98,6 +98,7 @@ class Map : private util::noncopyable { // Position void moveBy(double dx, double dy, const Duration& = Duration::zero()); + void setLatLng(LatLng latLng, vec2 point, const Duration& = Duration::zero()); void setLatLng(LatLng latLng, const Duration& = Duration::zero()); LatLng getLatLng() const; void resetPosition(); diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp index cbc872313e9..bda9939af7f 100644 --- a/platform/default/glfw_view.cpp +++ b/platform/default/glfw_view.cpp @@ -329,7 +329,10 @@ void GLFWView::onMouseMove(GLFWwindow *window, double x, double y) { double dx = x - view->lastX; double dy = y - view->lastY; if (dx || dy) { - view->map->moveBy(dx, dy); + double flippedY = view->height - y; + view->map->setLatLng( + view->map->latLngForPixel(mbgl::vec2(x - dx, flippedY + dy)), + mbgl::vec2(x, flippedY)); } } else if (view->rotating) { view->map->rotateBy(view->lastX, view->lastY, x, y); diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index ce1ecfd6d1c..45a537a5514 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -879,7 +879,14 @@ - (void)handlePanGesture:(UIPanGestureRecognizer *)pan CGPoint delta = CGPointMake([pan translationInView:pan.view].x - self.centerPoint.x, [pan translationInView:pan.view].y - self.centerPoint.y); - _mbglMap->moveBy(delta.x, delta.y); + double flippedY = self.bounds.size.height - [pan locationInView:pan.view].y; + _mbglMap->setLatLng( + _mbglMap->latLngForPixel(mbgl::vec2( + [pan locationInView:pan.view].x - delta.x, + flippedY + delta.y)), + mbgl::vec2( + [pan locationInView:pan.view].x, + flippedY)); self.centerPoint = CGPointMake(self.centerPoint.x + delta.x, self.centerPoint.y + delta.y); @@ -1052,7 +1059,7 @@ - (void)handleRotateGesture:(UIRotationGestureRecognizer *)rotate _mbglMap->setBearing(newDegrees, [rotate locationInView:rotate.view].x, - [rotate locationInView:rotate.view].y); + self.bounds.size.height - [rotate locationInView:rotate.view].y); [self notifyMapChange:mbgl::MapChangeRegionIsChanging]; } diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 729726837d9..a1823dc8e17 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -166,6 +166,11 @@ void Map::setLatLng(LatLng latLng, const Duration& duration) { update(Update::Repaint); } +void Map::setLatLng(LatLng latLng, vec2 point, const Duration& duration) { + transform->setLatLng(latLng, point, duration); + update(Update::Repaint); +} + LatLng Map::getLatLng() const { return transform->getLatLng(); } diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index bb559091492..87e86ea3e65 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -113,6 +114,27 @@ void Transform::setLatLng(const LatLng latLng, const Duration& duration) { easeTo(options); } +void Transform::setLatLng(const LatLng latLng, vec2 point, const Duration& duration) { + if (std::isnan(latLng.latitude) || std::isnan(latLng.longitude)) { + return; + } + + auto coord = state.latLngToCoordinate(latLng); + auto coordAtPoint = state.pointToCoordinate(point); + auto coordCenter = state.pointToCoordinate({ state.width / 2.0f, state.height / 2.0f }); + + float columnDiff = coordAtPoint.column - coord.column; + float rowDiff = coordAtPoint.row - coord.row; + + auto newLatLng = state.coordinateToLatLng({ + coordCenter.column - columnDiff, + coordCenter.row - rowDiff, + coordCenter.zoom + }); + + setLatLng(newLatLng, duration); +} + void Transform::setLatLngZoom(const LatLng latLng, const double zoom, const Duration& duration) { CameraOptions options; options.center = latLng; @@ -173,25 +195,20 @@ void Transform::_setScale(double new_scale, double cx, double cy, const Duration new_scale = state.max_scale; } - // Zoom in on the center if we don't have click or gesture anchor coordinates. - if (cx < 0 || cy < 0) { - cx = static_cast(state.width) / 2.0; - cy = static_cast(state.height) / 2.0; - } - - // Account for the x/y offset from the center (= where the user clicked or pinched) const double factor = new_scale / state.scale; - const double dx = (cx - static_cast(state.width) / 2.0) * (1.0 - factor); - const double dy = (cy - static_cast(state.height) / 2.0) * (1.0 - factor); - - // Account for angle - const double angle_sin = std::sin(-state.angle); - const double angle_cos = std::cos(-state.angle); - const double ax = angle_cos * dx - angle_sin * dy; - const double ay = angle_sin * dx + angle_cos * dy; + double dx = 0; + double dy = 0; + + if (cx > 0 || cy > 0) { + auto coord = state.pointToCoordinate({ cx, state.getHeight() - cy }).zoomTo(state.getZoom()); + auto centerCoord = state.pointToCoordinate({ state.width / 2.0f, state.height / 2.0f }).zoomTo(state.getZoom()); + auto coordDiff = centerCoord - coord; + dx = coordDiff.column * util::tileSize * (1.0 - factor); + dy = coordDiff.row * util::tileSize * (1.0 - factor); + } - const double xn = state.x * factor + ax; - const double yn = state.y * factor + ay; + const double xn = state.x * factor - dx; + const double yn = state.y * factor - dy; _setScaleXY(new_scale, xn, yn, duration); } @@ -318,18 +335,17 @@ void Transform::setAngle(const double new_angle, const double cx, const double c return; } - double dx = 0, dy = 0; + LatLng rotationCenter; if (cx >= 0 && cy >= 0) { - dx = (static_cast(state.width) / 2.0) - cx; - dy = (static_cast(state.height) / 2.0) - cy; - _moveBy(dx, dy, Duration::zero()); + rotationCenter = state.pointToLatLng({ cx, cy }); + setLatLng(rotationCenter, Duration::zero()); } _setAngle(new_angle); if (cx >= 0 && cy >= 0) { - _moveBy(-dx, -dy, Duration::zero()); + setLatLng(rotationCenter, { cx, cy }, Duration::zero()); } } diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp index 545d8a3b214..a72e9f24eff 100644 --- a/src/mbgl/map/transform.hpp +++ b/src/mbgl/map/transform.hpp @@ -29,6 +29,7 @@ class Transform : private util::noncopyable { // Position void moveBy(double dx, double dy, const Duration& = Duration::zero()); void setLatLng(LatLng latLng, const Duration& = Duration::zero()); + void setLatLng(LatLng latLng, vec2 point, const Duration& duration = Duration::zero()); void setLatLngZoom(LatLng latLng, double zoom, const Duration& = Duration::zero()); inline const LatLng getLatLng() const { return state.getLatLng(); }