summaryrefslogtreecommitdiff
path: root/src/mbgl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl')
-rw-r--r--src/mbgl/map/camera.cpp1
-rw-r--r--src/mbgl/map/map.cpp42
-rw-r--r--src/mbgl/map/transform.cpp182
-rw-r--r--src/mbgl/map/transform.hpp11
-rw-r--r--src/mbgl/util/optional.hpp69
-rw-r--r--src/mbgl/util/unitbezier.hpp121
6 files changed, 131 insertions, 295 deletions
diff --git a/src/mbgl/map/camera.cpp b/src/mbgl/map/camera.cpp
new file mode 100644
index 0000000000..4a45e904f8
--- /dev/null
+++ b/src/mbgl/map/camera.cpp
@@ -0,0 +1 @@
+#include <mbgl/map/camera.hpp>
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index 82ab72db2d..9195f6b583 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -9,6 +9,7 @@
#include <mbgl/util/projection.hpp>
#include <mbgl/util/thread.hpp>
+#include <mbgl/util/math.hpp>
namespace mbgl {
@@ -114,6 +115,18 @@ void Map::setGestureInProgress(bool inProgress) {
update(Update::Repaint);
}
+#pragma mark -
+
+void Map::jumpTo(CameraOptions options) {
+ transform->jumpTo(options);
+ update(Update::Repaint);
+}
+
+void Map::easeTo(CameraOptions options) {
+ transform->easeTo(options);
+ update(options.zoom ? Update::Zoom : Update::Repaint);
+}
+
#pragma mark - Position
void Map::moveBy(double dx, double dy, const Duration& duration) {
@@ -131,9 +144,11 @@ LatLng Map::getLatLng() const {
}
void Map::resetPosition() {
- transform->setAngle(0);
- transform->setLatLng(LatLng(0, 0));
- transform->setZoom(0);
+ CameraOptions options;
+ options.angle = 0;
+ options.center = LatLng(0, 0);
+ options.zoom = 0;
+ transform->jumpTo(options);
update(Update::Zoom);
}
@@ -168,25 +183,26 @@ void Map::setLatLngZoom(LatLng latLng, double zoom, const Duration& duration) {
update(Update::Zoom);
}
-void Map::fitBounds(LatLngBounds bounds, EdgeInsets padding, const Duration& duration) {
+CameraOptions Map::cameraForLatLngBounds(LatLngBounds bounds, EdgeInsets padding) {
AnnotationSegment segment = {
{bounds.ne.latitude, bounds.sw.longitude},
bounds.sw,
{bounds.sw.latitude, bounds.ne.longitude},
bounds.ne,
};
- fitBounds(segment, padding, duration);
+ return cameraForLatLngs(segment, padding);
}
-void Map::fitBounds(AnnotationSegment segment, EdgeInsets padding, const Duration& duration) {
- if (segment.empty()) {
- return;
+CameraOptions Map::cameraForLatLngs(std::vector<LatLng> latLngs, EdgeInsets padding) {
+ CameraOptions options;
+ if (latLngs.empty()) {
+ return options;
}
// Calculate the bounds of the possibly rotated shape with respect to the viewport.
vec2<> nePixel = {-INFINITY, -INFINITY};
vec2<> swPixel = {INFINITY, INFINITY};
- for (LatLng latLng : segment) {
+ for (LatLng latLng : latLngs) {
vec2<> pixel = pixelForLatLng(latLng);
swPixel.x = std::min(swPixel.x, pixel.x);
nePixel.x = std::max(nePixel.x, pixel.x);
@@ -214,7 +230,9 @@ void Map::fitBounds(AnnotationSegment segment, EdgeInsets padding, const Duratio
vec2<> centerPixel = (paddedNEPixel + paddedSWPixel) * 0.5;
LatLng centerLatLng = latLngForPixel(centerPixel);
- setLatLngZoom(centerLatLng, zoom, duration);
+ options.center = centerLatLng;
+ options.zoom = zoom;
+ return options;
}
void Map::resetZoom() {
@@ -270,8 +288,8 @@ void Map::resetNorth() {
#pragma mark - Pitch
-void Map::setPitch(double pitch) {
- transform->setPitch(std::min(pitch, 60.0) * M_PI / 180);
+void Map::setPitch(double pitch, const Duration& duration) {
+ transform->setPitch(util::clamp(pitch, 0., 60.) * M_PI / 180, duration);
update(Update::Repaint);
}
diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp
index 60c55c3f0c..bb55909149 100644
--- a/src/mbgl/map/transform.cpp
+++ b/src/mbgl/map/transform.cpp
@@ -1,3 +1,4 @@
+#include <mbgl/map/camera.hpp>
#include <mbgl/map/transform.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/util/constants.hpp>
@@ -53,6 +54,38 @@ bool Transform::resize(const std::array<uint16_t, 2> size) {
#pragma mark - Position
+void Transform::jumpTo(const CameraOptions options) {
+ CameraOptions jumpOptions = options;
+ jumpOptions.duration.reset();
+ easeTo(jumpOptions);
+}
+
+void Transform::easeTo(CameraOptions options) {
+ 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));
+
+ options.center.reset();
+ options.zoom.reset();
+ options.angle.reset();
+ _easeTo(options, new_scale, angle, xn, yn);
+}
+
void Transform::moveBy(const double dx, const double dy, const Duration& duration) {
if (std::isnan(dx) || std::isnan(dy)) {
return;
@@ -62,71 +95,30 @@ void Transform::moveBy(const double dx, const double dy, const Duration& duratio
}
void Transform::_moveBy(const double dx, const double dy, const Duration& duration) {
+
double x = state.x + std::cos(state.angle) * dx + std::sin( state.angle) * dy;
double y = state.y + std::cos(state.angle) * dy + std::sin(-state.angle) * dx;
state.constrain(state.scale, y);
-
- if (duration == Duration::zero()) {
- view.notifyMapChange(MapChangeRegionWillChange);
-
- state.x = x;
- state.y = y;
-
- view.notifyMapChange(MapChangeRegionDidChange);
- } else {
- view.notifyMapChange(MapChangeRegionWillChangeAnimated);
-
- const double startX = state.x;
- const double startY = state.y;
- state.panning = true;
-
- startTransition(
- [=](double t) {
- state.x = util::interpolate(startX, x, t);
- state.y = util::interpolate(startY, y, t);
- view.notifyMapChange(MapChangeRegionIsChanging);
- return Update::Repaint;
- },
- [=] {
- state.panning = false;
- view.notifyMapChange(MapChangeRegionDidChangeAnimated);
- }, duration);
- }
+
+ CameraOptions options;
+ options.duration = duration;
+ _easeTo(options, state.scale, state.angle, x, y);
}
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 = ::fmin(::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;
+ options.center = latLng;
+ options.duration = duration;
+ easeTo(options);
}
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 = ::fmin(::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;
+ options.center = latLng;
+ options.zoom = zoom;
+ options.duration = duration;
+ easeTo(options);
}
@@ -206,13 +198,27 @@ 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) {
+ CameraOptions options;
+ options.duration = duration;
+ _easeTo(options, new_scale, state.angle, xn, yn);
+}
+
+void Transform::_easeTo(CameraOptions options, const double new_scale, const double new_angle, const double xn, const double yn) {
+ Update update = state.scale == new_scale ? Update::Repaint : Update::Zoom;
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);
+ double pitch = options.pitch ? *options.pitch : state.pitch;
- if (duration == Duration::zero()) {
+ if (!options.duration) {
+ options.duration = Duration::zero();
+ }
+ if (!options.duration || *options.duration == Duration::zero()) {
view.notifyMapChange(MapChangeRegionWillChange);
state.scale = scale;
@@ -221,33 +227,46 @@ 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;
+ state.pitch = pitch;
view.notifyMapChange(MapChangeRegionDidChange);
} else {
view.notifyMapChange(MapChangeRegionWillChangeAnimated);
const double startS = state.scale;
+ const double startA = state.angle;
+ const double startP = state.pitch;
const double startX = state.x;
const double startY = state.y;
state.panning = true;
state.scaling = true;
+ state.rotating = true;
startTransition(
[=](double t) {
+ util::UnitBezier ease = options.easing ? *options.easing : util::UnitBezier(0, 0, 0.25, 1);
+ return ease.solve(t, 0.001);
+ },
+ [=](double t) {
state.scale = util::interpolate(startS, scale, t);
state.x = util::interpolate(startX, x, t);
state.y = util::interpolate(startY, y, t);
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);
+ state.pitch = util::interpolate(startP, pitch, t);
view.notifyMapChange(MapChangeRegionIsChanging);
- return Update::Zoom;
+ return update;
},
[=] {
state.panning = false;
state.scaling = false;
+ state.rotating = false;
view.notifyMapChange(MapChangeRegionDidChangeAnimated);
- }, duration);
+ }, *options.duration);
}
}
@@ -307,7 +326,7 @@ void Transform::setAngle(const double new_angle, const double cx, const double c
_moveBy(dx, dy, Duration::zero());
}
- _setAngle(new_angle, Duration::zero());
+ _setAngle(new_angle);
if (cx >= 0 && cy >= 0) {
_moveBy(-dx, -dy, Duration::zero());
@@ -315,32 +334,10 @@ 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::Repaint;
- },
- [=] {
- state.rotating = false;
- view.notifyMapChange(MapChangeRegionDidChangeAnimated);
- }, duration);
- }
+ CameraOptions options;
+ options.angle = new_angle;
+ options.duration = duration;
+ easeTo(options);
}
double Transform::getAngle() const {
@@ -349,8 +346,11 @@ double Transform::getAngle() const {
#pragma mark - Pitch
-void Transform::setPitch(double pitch) {
- state.pitch = pitch;
+void Transform::setPitch(double pitch, const Duration& duration) {
+ CameraOptions options;
+ options.pitch = pitch;
+ options.duration = duration;
+ easeTo(options);
}
double Transform::getPitch() const {
@@ -359,7 +359,8 @@ double Transform::getPitch() const {
#pragma mark - Transition
-void Transform::startTransition(std::function<Update(double)> frame,
+void Transform::startTransition(std::function<double(double)> easing,
+ std::function<Update(double)> frame,
std::function<void()> finish,
const Duration& duration) {
if (transitionFinishFn) {
@@ -369,7 +370,7 @@ void Transform::startTransition(std::function<Update(double)> frame,
transitionStart = Clock::now();
transitionDuration = duration;
- transitionFrameFn = [frame, this](const TimePoint now) {
+ transitionFrameFn = [easing, frame, this](const TimePoint now) {
float t = std::chrono::duration<float>(now - transitionStart) / transitionDuration;
if (t >= 1.0) {
Update result = frame(1.0);
@@ -378,8 +379,7 @@ void Transform::startTransition(std::function<Update(double)> frame,
transitionFinishFn = nullptr;
return result;
} else {
- util::UnitBezier ease(0, 0, 0.25, 1);
- return frame(ease.solve(t, 0.001));
+ return frame(easing(t));
}
};
diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp
index 1671983449..56001fad81 100644
--- a/src/mbgl/map/transform.hpp
+++ b/src/mbgl/map/transform.hpp
@@ -2,6 +2,7 @@
#define MBGL_MAP_TRANSFORM
#include <mbgl/map/transform_state.hpp>
+#include <mbgl/map/camera.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/map/update.hpp>
#include <mbgl/util/geo.hpp>
@@ -22,6 +23,9 @@ public:
// Map view
bool resize(std::array<uint16_t, 2> size);
+ void jumpTo(const CameraOptions options);
+ void easeTo(const CameraOptions options);
+
// Position
void moveBy(double dx, double dy, const Duration& = Duration::zero());
void setLatLng(LatLng latLng, const Duration& = Duration::zero());
@@ -42,7 +46,7 @@ public:
double getAngle() const;
// Pitch
- void setPitch(double pitch);
+ void setPitch(double pitch, const Duration& = Duration::zero());
double getPitch() const;
// Transitions
@@ -60,13 +64,16 @@ private:
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 _easeTo(CameraOptions options, const double new_scale, const double new_angle,
+ const double xn, const double yn);
void _setAngle(double angle, const Duration& = Duration::zero());
View &view;
TransformState state;
- void startTransition(std::function<Update(double)> frame,
+ void startTransition(std::function<double(double)> easing,
+ std::function<Update(double)> frame,
std::function<void()> finish,
const Duration& duration);
diff --git a/src/mbgl/util/optional.hpp b/src/mbgl/util/optional.hpp
deleted file mode 100644
index 8d46eae857..0000000000
--- a/src/mbgl/util/optional.hpp
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef MAPBOX_UTIL_OPTIONAL_HPP
-#define MAPBOX_UTIL_OPTIONAL_HPP
-
-#include <type_traits>
-
-#include <mbgl/util/variant.hpp>
-
-namespace mapbox
-{
-namespace util
-{
-
-template <typename T> class optional
-{
- static_assert(!std::is_reference<T>::value, "optional doesn't support references");
-
- struct none_type
- {
- };
-
- variant<none_type, T> variant_;
-
- public:
- optional() = default;
-
- optional(optional const &rhs)
- {
- if (this != &rhs)
- { // protect against invalid self-assignment
- variant_ = rhs.variant_;
- }
- }
-
- optional(T const &v) { variant_ = v; }
-
- explicit operator bool() const noexcept { return variant_.template is<T>(); }
-
- T const &get() const { return variant_.template get<T>(); }
- T &get() { return variant_.template get<T>(); }
-
- T const &operator*() const { return this->get(); }
- T operator*() { return this->get(); }
-
- optional &operator=(T const &v)
- {
- variant_ = v;
- return *this;
- }
-
- optional &operator=(optional const &rhs)
- {
- if (this != &rhs)
- {
- variant_ = rhs.variant_;
- }
- return *this;
- }
-
- template <typename... Args> void emplace(Args &&... args)
- {
- variant_ = T{std::forward<Args>(args)...};
- }
-
- void reset() { variant_ = none_type{}; }
-};
-}
-}
-
-#endif
diff --git a/src/mbgl/util/unitbezier.hpp b/src/mbgl/util/unitbezier.hpp
deleted file mode 100644
index 095e15f809..0000000000
--- a/src/mbgl/util/unitbezier.hpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2008 Apple Inc. All Rights Reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MBGL_UTIL_UNITBEZIER
-#define MBGL_UTIL_UNITBEZIER
-
-#include <cmath>
-
-namespace mbgl {
-namespace util {
-
-struct UnitBezier {
- UnitBezier(double p1x, double p1y, double p2x, double p2y) {
- // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
- cx = 3.0 * p1x;
- bx = 3.0 * (p2x - p1x) - cx;
- ax = 1.0 - cx - bx;
-
- cy = 3.0 * p1y;
- by = 3.0 * (p2y - p1y) - cy;
- ay = 1.0 - cy - by;
- }
-
- double sampleCurveX(double t) {
- // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
- return ((ax * t + bx) * t + cx) * t;
- }
-
- double sampleCurveY(double t) {
- return ((ay * t + by) * t + cy) * t;
- }
-
- double sampleCurveDerivativeX(double t) {
- return (3.0 * ax * t + 2.0 * bx) * t + cx;
- }
-
- // Given an x value, find a parametric value it came from.
- double solveCurveX(double x, double epsilon) {
- double t0;
- double t1;
- double t2;
- double x2;
- double d2;
- int i;
-
- // First try a few iterations of Newton's method -- normally very fast.
- for (t2 = x, i = 0; i < 8; ++i) {
- x2 = sampleCurveX(t2) - x;
- if (fabs (x2) < epsilon)
- return t2;
- d2 = sampleCurveDerivativeX(t2);
- if (fabs(d2) < 1e-6)
- break;
- t2 = t2 - x2 / d2;
- }
-
- // Fall back to the bisection method for reliability.
- t0 = 0.0;
- t1 = 1.0;
- t2 = x;
-
- if (t2 < t0)
- return t0;
- if (t2 > t1)
- return t1;
-
- while (t0 < t1) {
- x2 = sampleCurveX(t2);
- if (fabs(x2 - x) < epsilon)
- return t2;
- if (x > x2)
- t0 = t2;
- else
- t1 = t2;
- t2 = (t1 - t0) * .5 + t0;
- }
-
- // Failure.
- return t2;
- }
-
- double solve(double x, double epsilon) {
- return sampleCurveY(solveCurveX(x, epsilon));
- }
-
-private:
- double ax;
- double bx;
- double cx;
-
- double ay;
- double by;
- double cy;
-};
-
-}
-}
-
-#endif