summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMinh Nguyễn <mxn@1ec5.org>2015-07-30 15:34:49 -0700
committerJustin R. Miller <incanus@codesorcery.net>2015-09-07 09:22:52 -0700
commit0a172a21fdc2a87473560fd7d45f4d495d95de91 (patch)
treee4b0ccd7ee2fecdfe63ead975cbb646493ff2bb4 /src
parent584c36f348d586bd07d27e6a4c9a7f318a98044c (diff)
downloadqtlocation-mapboxgl-0a172a21fdc2a87473560fd7d45f4d495d95de91.tar.gz
CameraOptions
Plumbed camera options all the way through to MGLMapView. Added a method that lets you specify a direction in addition to center point and zoom level. Added Map::jumpTo() for parity with mapbox-gl-js. Replaced usage of Map::setLatLng() and Map::setLatLngZoom() with Map::jumpTo() or Map::easeTo() within MGLMapView. Replaced MGLMapView.pitch with MGLMapCamera for setting all supported degrees of freedom simultaneously. Simultaneously move and rotate with course. Support customizable timing functions on iOS. iosapp now persists an archived MGLMapCamera instead of separate viewpoint properties and also synchronizes user defaults on termination. This change implements persistence entirely in Objective-C, eliminating the use of the Objective-C++ implementation. Fixes #1643, fixes #1834. Ref #1581.
Diffstat (limited to 'src')
-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