diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 6e24d177a9a..fff71a74452 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -97,6 +97,8 @@ class Map : private util::noncopyable { void cancelTransitions(); void setGestureInProgress(bool); + void easeTo(CameraOptions options, const Duration& = Duration::zero()); + // Position void moveBy(double dx, double dy, const Duration& = Duration::zero()); void setLatLng(LatLng latLng, const Duration& = Duration::zero()); diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp index 1f073ded8fb..b7a0e4376ef 100644 --- a/include/mbgl/util/geo.hpp +++ b/include/mbgl/util/geo.hpp @@ -1,6 +1,8 @@ #ifndef MBGL_UTIL_GEO #define MBGL_UTIL_GEO +#include "optional.hpp" + namespace mbgl { class TileID; @@ -61,6 +63,12 @@ struct LatLngBounds { } }; +struct CameraOptions { + mapbox::util::optional center; + mapbox::util::optional zoom; + mapbox::util::optional angle; +}; + } #endif diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 5b96b831993..89a045d8225 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -118,6 +118,13 @@ void Map::setGestureInProgress(bool inProgress) { update(); } +#pragma mark - + +void Map::easeTo(CameraOptions options, const Duration& duration) { + transform->easeTo(options, duration); + update(); +} + #pragma mark - Position void Map::moveBy(double dx, double dy, const Duration& duration) { diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index b39614888bb..c7ddd681bbb 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -53,6 +53,29 @@ bool Transform::resize(const std::array size) { #pragma mark - Position +void Transform::easeTo(const CameraOptions options, const Duration& duration) { + LatLng latLng = options.center ? *options.center : getLatLng(); + double zoom = options.zoom ? *options.zoom : getZoom(); + double angle = options.angle ? *options.angle : getAngle(); + if (std::isnan(latLng.latitude) || std::isnan(latLng.longitude) || std::isnan(zoom)) { + return; + } + + double new_scale = std::pow(2.0, zoom); + + const double s = new_scale * util::tileSize; + state.Bc = s / 360; + state.Cc = s / util::M2PI; + + const double m = 1 - 1e-15; + const double f = std::fmin(std::fmax(std::sin(util::DEG2RAD * latLng.latitude), -m), m); + + double xn = -latLng.longitude * state.Bc; + double yn = 0.5 * state.Cc * std::log((1 + f) / (1 - f)); + + _setScaleXYθ(new_scale, angle, xn, yn, duration); +} + void Transform::moveBy(const double dx, const double dy, const Duration& duration) { if (std::isnan(dx) || std::isnan(dy)) { return; @@ -96,37 +119,13 @@ void Transform::_moveBy(const double dx, const double dy, const Duration& durati } void Transform::setLatLng(const LatLng latLng, const Duration& duration) { - if (std::isnan(latLng.latitude) || std::isnan(latLng.longitude)) { - return; - } - - const double m = 1 - 1e-15; - const double f = std::fmin(std::fmax(std::sin(util::DEG2RAD * latLng.latitude), -m), m); - - double xn = -latLng.longitude * state.Bc; - double yn = 0.5 * state.Cc * std::log((1 + f) / (1 - f)); - - _setScaleXY(state.scale, xn, yn, duration); + CameraOptions options = { latLng, getZoom(), getAngle() }; + easeTo(options, duration); } void Transform::setLatLngZoom(const LatLng latLng, const double zoom, const Duration& duration) { - if (std::isnan(latLng.latitude) || std::isnan(latLng.longitude) || std::isnan(zoom)) { - return; - } - - double new_scale = std::pow(2.0, zoom); - - const double s = new_scale * util::tileSize; - state.Bc = s / 360; - state.Cc = s / util::M2PI; - - const double m = 1 - 1e-15; - const double f = std::fmin(std::fmax(std::sin(util::DEG2RAD * latLng.latitude), -m), m); - - double xn = -latLng.longitude * state.Bc; - double yn = 0.5 * state.Cc * std::log((1 + f) / (1 - f)); - - _setScaleXY(new_scale, xn, yn, duration); + CameraOptions options = { latLng, zoom, getAngle() }; + easeTo(options, duration); } @@ -206,11 +205,19 @@ void Transform::_setScale(double new_scale, double cx, double cy, const Duration void Transform::_setScaleXY(const double new_scale, const double xn, const double yn, const Duration& duration) { + _setScaleXYθ(new_scale, state.angle, xn, yn, duration); +} + +void Transform::_setScaleXYθ(const double new_scale, const double new_angle, const double xn, const double yn, + const Duration& duration) { double scale = new_scale; double x = xn; double y = yn; state.constrain(scale, y); + + double angle = _normalizeAngle(new_angle, state.angle); + state.angle = _normalizeAngle(state.angle, angle); if (duration == Duration::zero()) { view.notifyMapChange(MapChangeRegionWillChange); @@ -221,16 +228,20 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl const double s = state.scale * util::tileSize; state.Bc = s / 360; state.Cc = s / util::M2PI; + + state.angle = angle; view.notifyMapChange(MapChangeRegionDidChange); } else { view.notifyMapChange(MapChangeRegionWillChangeAnimated); const double startS = state.scale; + const double startA = state.angle; const double startX = state.x; const double startY = state.y; state.panning = true; state.scaling = true; + state.rotating = true; startTransition( [=](double t) { @@ -240,12 +251,14 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl const double s = state.scale * util::tileSize; state.Bc = s / 360; state.Cc = s / util::M2PI; + state.angle = util::wrap(util::interpolate(startA, angle, t), -M_PI, M_PI); view.notifyMapChange(MapChangeRegionIsChanging); return Update::Zoom; }, [=] { state.panning = false; state.scaling = false; + state.rotating = false; view.notifyMapChange(MapChangeRegionDidChangeAnimated); }, duration); } @@ -315,32 +328,7 @@ void Transform::setAngle(const double new_angle, const double cx, const double c } void Transform::_setAngle(double new_angle, const Duration& duration) { - double angle = _normalizeAngle(new_angle, state.angle); - state.angle = _normalizeAngle(state.angle, angle); - - if (duration == Duration::zero()) { - view.notifyMapChange(MapChangeRegionWillChange); - - state.angle = angle; - - view.notifyMapChange(MapChangeRegionDidChange); - } else { - view.notifyMapChange(MapChangeRegionWillChangeAnimated); - - const double startA = state.angle; - state.rotating = true; - - startTransition( - [=](double t) { - state.angle = util::wrap(util::interpolate(startA, angle, t), -M_PI, M_PI); - view.notifyMapChange(MapChangeRegionIsChanging); - return Update::Nothing; - }, - [=] { - state.rotating = false; - view.notifyMapChange(MapChangeRegionDidChangeAnimated); - }, duration); - } + easeTo({getLatLng(), getZoom(), new_angle}, duration); } double Transform::getAngle() const { diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp index badb811afbe..61d13915232 100644 --- a/src/mbgl/map/transform.hpp +++ b/src/mbgl/map/transform.hpp @@ -22,6 +22,8 @@ class Transform : private util::noncopyable { // Map view bool resize(std::array size); + void easeTo(const CameraOptions options, const Duration& = Duration::zero()); + // Position void moveBy(double dx, double dy, const Duration& = Duration::zero()); void setLatLng(LatLng latLng, const Duration& = Duration::zero()); @@ -56,6 +58,8 @@ class Transform : private util::noncopyable { void _moveBy(double dx, double dy, const Duration& = Duration::zero()); void _setScale(double scale, double cx, double cy, const Duration& = Duration::zero()); void _setScaleXY(double new_scale, double xn, double yn, const Duration& = Duration::zero()); + void _setScaleXYθ(const double new_scale, const double new_angle, const double xn, const double yn, + const Duration& duration = Duration::zero()); void _setAngle(double angle, const Duration& = Duration::zero()); View &view;