summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMinh Nguyễn <mxn@1ec5.org>2016-01-10 17:28:21 -0800
committerMinh Nguyễn <mxn@1ec5.org>2016-01-14 00:39:18 -0800
commitd26b956a48dc2da9333b40d8d2c4371c63b372a2 (patch)
tree630a714a50e23cc0338fda687fddcfa832fa62a5 /src
parent27b308a23482660d705eb82457a21213bc12599e (diff)
downloadqtlocation-mapboxgl-d26b956a48dc2da9333b40d8d2c4371c63b372a2.tar.gz
[core] Cleaned up Transform
Eliminated many redundant methods on Transform. Minimized usage of TransformState::x and TransformState::y. Added convenient constructors for AnimationOptions. When scaling, passing in view’s origin as the anchor no longer anchors the scaling operation at the view’s center. To specify no anchor, use NaN. Increased precision of worldSize(), now that it’s used much more heavily in transform methods.
Diffstat (limited to 'src')
-rw-r--r--src/mbgl/map/transform.cpp298
-rw-r--r--src/mbgl/map/transform.hpp15
-rw-r--r--src/mbgl/map/transform_state.cpp4
-rw-r--r--src/mbgl/map/transform_state.hpp2
4 files changed, 143 insertions, 176 deletions
diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp
index 5dae77770c..da99d64fc2 100644
--- a/src/mbgl/map/transform.cpp
+++ b/src/mbgl/map/transform.cpp
@@ -62,64 +62,29 @@ bool Transform::resize(const std::array<uint16_t, 2> size) {
#pragma mark - Position
-/*
+/**
* Change any combination of center, zoom, bearing, and pitch, without
* a transition. The map will retain the current values for any options
* not included in `options`.
*/
void Transform::jumpTo(const CameraOptions& camera) {
- easeTo(camera, {});
+ easeTo(camera);
}
-/*
- * Change any combination of center, zoom, bearing, and pitch, with a smooth animation
- * between old and new values. The map will retain the current values for any options
- * not included in `options`.
- */
-void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& animation) {
- CameraOptions easeCamera(camera);
- LatLng latLng = easeCamera.center ? *easeCamera.center : getLatLng();
- double zoom = easeCamera.zoom ? *easeCamera.zoom : getZoom();
- double angle = easeCamera.angle ? *easeCamera.angle : getAngle();
- if (!latLng || std::isnan(zoom) || std::isnan(angle)) {
+void Transform::moveBy(const PrecisionPoint& offset, const Duration& duration) {
+ if (!offset) {
return;
}
- double new_scale = state.zoomScale(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));
-
- easeCamera.center.reset();
- easeCamera.zoom.reset();
- easeCamera.angle.reset();
- _easeTo(easeCamera, animation, new_scale, angle, xn, yn);
-}
-
-void Transform::moveBy(const PrecisionPoint& point, const Duration& duration) {
- if (!point) {
- return;
- }
-
- _moveBy(point, duration);
-}
-
-void Transform::_moveBy(const PrecisionPoint& point, const Duration& duration) {
- double x = state.x + std::cos(state.angle) * point.x + std::sin( state.angle) * point.y;
- double y = state.y + std::cos(state.angle) * point.y + std::sin(-state.angle) * point.x;
-
- state.constrain(state.scale, x, y);
+ PrecisionPoint centerOffset = {
+ offset.x,
+ -offset.y,
+ };
+ PrecisionPoint centerPoint = state.latLngToPoint(state.getLatLng()) - centerOffset;
- AnimationOptions animation;
- animation.duration = duration;
- _easeTo({}, animation, state.scale, state.angle, x, y);
+ CameraOptions camera;
+ camera.center = state.pointToLatLng(centerPoint);
+ easeTo(camera, duration);
}
void Transform::setLatLng(const LatLng& latLng, const Duration& duration) {
@@ -129,9 +94,7 @@ void Transform::setLatLng(const LatLng& latLng, const Duration& duration) {
CameraOptions camera;
camera.center = latLng;
- AnimationOptions animation;
- animation.duration = duration;
- easeTo(camera, animation);
+ easeTo(camera, duration);
}
void Transform::setLatLng(const LatLng& latLng, const PrecisionPoint& point, const Duration& duration) {
@@ -163,44 +126,23 @@ void Transform::setLatLngZoom(const LatLng& latLng, double zoom, const Duration&
CameraOptions camera;
camera.center = latLng;
camera.zoom = zoom;
- AnimationOptions animation;
- animation.duration = duration;
- easeTo(camera, animation);
+ easeTo(camera, duration);
}
#pragma mark - Zoom
void Transform::scaleBy(double ds, const PrecisionPoint& center, const Duration& duration) {
- if (std::isnan(ds) || !center) {
- return;
- }
-
- // clamp scale to min/max values
- double new_scale = state.scale * ds;
- if (new_scale < state.min_scale) {
- new_scale = state.min_scale;
- } else if (new_scale > state.max_scale) {
- new_scale = state.max_scale;
- }
-
- _setScale(new_scale, center, duration);
-}
-
-void Transform::setScale(double scale, const PrecisionPoint& center, const Duration& duration) {
- if (std::isnan(scale) || !center) {
+ if (std::isnan(ds)) {
return;
}
- _setScale(scale, center, duration);
+ double scale = util::clamp(state.scale * ds, state.min_scale, state.max_scale);
+ setScale(scale, center, duration);
}
void Transform::setZoom(double zoom, const Duration& duration) {
- if (std::isnan(zoom)) {
- return;
- }
-
- _setScale(std::pow(2.0, zoom), { 0, 0 }, duration);
+ setScale(state.zoomScale(zoom), {NAN, NAN}, duration);
}
double Transform::getZoom() const {
@@ -211,52 +153,80 @@ double Transform::getScale() const {
return state.scale;
}
-void Transform::_setScale(double new_scale, const PrecisionPoint& center, const Duration& duration) {
- const double factor = new_scale / state.scale;
- double dx = 0;
- double dy = 0;
-
- if (center.x > 0 || center.y > 0) {
- auto coord = state.pointToCoordinate({ center.x, state.getHeight() - center.y }).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);
+void Transform::setScale(double scale, const PrecisionPoint& anchor, const Duration& duration) {
+ if (std::isnan(scale)) {
+ return;
+ }
+
+ const double factor = scale / state.scale;
+ PrecisionPoint offset = { 0, 0 };
+ PrecisionPoint center = {
+ state.width / 2.0f,
+ state.height / 2.0f,
+ };
+ if (anchor) {
+ offset = {
+ anchor.x - center.x,
+ center.y - anchor.y,
+ };
}
- const double xn = state.x * factor - dx;
- const double yn = state.y * factor - dy;
-
- AnimationOptions animation;
- animation.duration = duration;
- _easeTo({}, animation, new_scale, state.angle, xn, yn);
+ CameraOptions camera;
+ camera.center = state.pointToLatLng(center + offset / factor);
+ camera.zoom = state.scaleZoom(scale);
+ easeTo(camera, duration);
}
-void Transform::_easeTo(const CameraOptions& camera, const AnimationOptions& animation,
- double new_scale, double new_angle, double xn, 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, x, y);
+/**
+ * Change any combination of center, zoom, bearing, and pitch, with a smooth animation
+ * between old and new values. The map will retain the current values for any options
+ * not included in `options`.
+ */
+void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& animation) {
+ LatLng latLng = camera.center ? *camera.center : getLatLng();
+ double zoom = camera.zoom ? *camera.zoom : getZoom();
+ double angle = camera.angle ? *camera.angle : getAngle();
+ double pitch = camera.pitch ? *camera.pitch : getPitch();
- double angle = _normalizeAngle(new_angle, state.angle);
- state.angle = _normalizeAngle(state.angle, angle);
- double pitch = camera.pitch ? *camera.pitch : state.pitch;
+ if (!latLng || std::isnan(zoom)) {
+ return;
+ }
+
+ // If a path crossing the antemeridian would be shorter, extend the final
+ // coordinate so that interpolating between the two endpoints will cross it.
+ LatLng startLatLng = getLatLng();
+ if (std::abs(startLatLng.longitude) + std::abs(latLng.longitude) > 180) {
+ if (startLatLng.longitude > 0 && latLng.longitude < 0) {
+ latLng.longitude += 360;
+ } else if (startLatLng.longitude < 0 && latLng.longitude > 0) {
+ latLng.longitude -= 360;
+ }
+ }
+
+ const PrecisionPoint startPoint = {
+ state.lngX(startLatLng.longitude),
+ state.latY(startLatLng.latitude),
+ };
+ const PrecisionPoint endPoint = {
+ state.lngX(latLng.longitude),
+ state.latY(latLng.latitude),
+ };
+
+ Update update = state.getZoom() == zoom ? Update::Repaint : Update::Zoom;
+
+ // Constrain camera options.
+ zoom = util::clamp(zoom, state.getMinZoom(), state.getMaxZoom());
pitch = util::clamp(pitch, 0., util::PITCH_MAX);
+
+ // Minimize rotation by taking the shorter path around the circle.
+ angle = _normalizeAngle(angle, state.angle);
+ state.angle = _normalizeAngle(state.angle, angle);
Duration duration = animation.duration ? *animation.duration : Duration::zero();
if (duration == Duration::zero()) {
view.notifyMapChange(MapChangeRegionWillChange);
- state.scale = scale;
- state.x = x;
- state.y = y;
- const double s = state.scale * util::tileSize;
- state.Bc = s / 360;
- state.Cc = s / util::M2PI;
-
+ state.setLatLngZoom(latLng, zoom);
state.angle = angle;
state.pitch = pitch;
@@ -266,15 +236,18 @@ void Transform::_easeTo(const CameraOptions& camera, const AnimationOptions& ani
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;
+
+ const double startWorldSize = state.worldSize();
+ state.Bc = startWorldSize / 360;
+ state.Cc = startWorldSize / util::M2PI;
+
+ const double scale = state.zoomScale(zoom);
+ const double startScale = state.scale;
+ const double startAngle = state.angle;
+ const double startPitch = state.pitch;
+ state.panning = latLng != startLatLng;
+ state.scaling = scale != startScale;
+ state.rotating = angle != startAngle;
startTransition(
[=](double t) {
@@ -282,14 +255,24 @@ void Transform::_easeTo(const CameraOptions& camera, const AnimationOptions& ani
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);
+ // Calculate the current point and zoom level along the flight path.
+ PrecisionPoint framePoint = util::interpolate(startPoint, endPoint, t);
+ double frameScale = util::interpolate(startScale, scale, t);
+
+ // Convert to geographic coordinates and set the new viewpoint.
+ LatLng frameLatLng = {
+ state.yLat(framePoint.y, startWorldSize),
+ state.xLng(framePoint.x, startWorldSize),
+ };
+ state.setLatLngZoom(frameLatLng, state.scaleZoom(frameScale));
+
+ if (angle != startAngle) {
+ state.angle = util::wrap(util::interpolate(startAngle, angle, t), -M_PI, M_PI);
+ }
+ if (pitch != startPitch) {
+ state.pitch = util::interpolate(startPitch, pitch, t);
+ }
+
// At t = 1.0, a DidChangeAnimated notification should be sent from finish().
if (t < 1.0) {
if (animation.transitionFrameFn) {
@@ -321,17 +304,17 @@ void Transform::_easeTo(const CameraOptions& camera, const AnimationOptions& ani
variable or function in van Wijk (2003). */
void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &animation) {
LatLng latLng = camera.center ? *camera.center : getLatLng();
- LatLng startLatLng = getLatLng();
double zoom = camera.zoom ? *camera.zoom : getZoom();
double angle = camera.angle ? *camera.angle : getAngle();
double pitch = camera.pitch ? *camera.pitch : getPitch();
- if (std::isnan(latLng.latitude) || std::isnan(latLng.longitude) || std::isnan(zoom)) {
+ if (!latLng || std::isnan(zoom)) {
return;
}
// If a path crossing the antemeridian would be shorter, extend the final
// coordinate so that interpolating between the two endpoints will cross it.
+ LatLng startLatLng = getLatLng();
if (std::abs(startLatLng.longitude) + std::abs(latLng.longitude) > 180) {
if (startLatLng.longitude > 0 && latLng.longitude < 0) {
latLng.longitude += 360;
@@ -350,12 +333,11 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
};
zoom = util::clamp(zoom, state.getMinZoom(), state.getMaxZoom());
+ pitch = util::clamp(pitch, 0., util::PITCH_MAX);
// Minimize rotation by taking the shorter path around the circle.
- double normalizedAngle = _normalizeAngle(angle, state.angle);
- state.angle = _normalizeAngle(state.angle, normalizedAngle);
-
- pitch = util::clamp(pitch, 0., util::PITCH_MAX);
+ angle = _normalizeAngle(angle, state.angle);
+ state.angle = _normalizeAngle(state.angle, angle);
const double startZoom = state.scaleZoom(state.scale);
const double startAngle = state.angle;
@@ -469,7 +451,7 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
double us = u(s);
// Calculate the current point and zoom level along the flight path.
- PrecisionPoint framePoint = startPoint + (endPoint - startPoint) * us;
+ PrecisionPoint framePoint = util::interpolate(startPoint, endPoint, us);
double frameZoom = startZoom + state.scaleZoom(1 / w(s));
// Convert to geographic coordinates and set the new viewpoint.
@@ -480,7 +462,7 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
state.setLatLngZoom(frameLatLng, frameZoom);
if (angle != startAngle) {
- state.angle = util::wrap(util::interpolate(startAngle, normalizedAngle, k), -M_PI, M_PI);
+ state.angle = util::wrap(util::interpolate(startAngle, angle, k), -M_PI, M_PI);
}
if (pitch != startPitch) {
state.pitch = util::interpolate(startPitch, pitch, k);
@@ -531,45 +513,37 @@ void Transform::rotateBy(const PrecisionPoint& first, const PrecisionPoint& seco
const PrecisionPoint newFirst = first - center;
const PrecisionPoint newSecond = second - center;
const double ang = state.angle + util::angle_between(newFirst.x, newFirst.y, newSecond.x, newSecond.y);
-
- _setAngle(ang, duration);
+
+ CameraOptions camera;
+ camera.angle = ang;
+ easeTo(camera, duration);
}
-void Transform::setAngle(double new_angle, const Duration& duration) {
- if (std::isnan(new_angle)) {
- return;
- }
-
- _setAngle(new_angle, duration);
+void Transform::setAngle(double angle, const Duration& duration) {
+ setAngle(angle, {NAN, NAN}, duration);
}
-void Transform::setAngle(double new_angle, const PrecisionPoint& center) {
- if (std::isnan(new_angle) || !center) {
+void Transform::setAngle(double angle, const PrecisionPoint& center, const Duration& duration) {
+ if (std::isnan(angle)) {
return;
}
LatLng rotationCenter;
- if (center.x > 0 || center.y > 0) {
+ if (center) {
rotationCenter = state.pointToLatLng(center);
- setLatLng(rotationCenter, Duration::zero());
+ setLatLng(rotationCenter);
}
+
+ CameraOptions camera;
+ camera.angle = angle;
+ easeTo(camera, duration);
- _setAngle(new_angle);
-
- if (center.x > 0 && center.y > 0) {
- setLatLng(rotationCenter, center, Duration::zero());
+ if (center) {
+ setLatLng(rotationCenter, center);
}
}
-void Transform::_setAngle(double new_angle, const Duration& duration) {
- CameraOptions camera;
- camera.angle = new_angle;
- AnimationOptions animation;
- animation.duration = duration;
- easeTo(camera, animation);
-}
-
double Transform::getAngle() const {
return state.angle;
}
@@ -583,9 +557,7 @@ void Transform::setPitch(double pitch, const Duration& duration) {
CameraOptions camera;
camera.pitch = pitch;
- AnimationOptions animation;
- animation.duration = duration;
- easeTo(camera, animation);
+ easeTo(camera, duration);
}
double Transform::getPitch() const {
diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp
index 7e0e85c2be..52e3b3e627 100644
--- a/src/mbgl/map/transform.hpp
+++ b/src/mbgl/map/transform.hpp
@@ -25,10 +25,10 @@ public:
bool resize(std::array<uint16_t, 2> size);
void jumpTo(const CameraOptions&);
- void easeTo(const CameraOptions&, const AnimationOptions&);
+ void easeTo(const CameraOptions&, const AnimationOptions& = {});
/** Smoothly zoom out, pan, and zoom back into the given camera along a
great circle, as though the viewer is aboard a supersonic jetcopter. */
- void flyTo(const CameraOptions&, const AnimationOptions&);
+ void flyTo(const CameraOptions&, const AnimationOptions& = {});
// Position
void moveBy(const PrecisionPoint&, const Duration& = Duration::zero());
@@ -38,8 +38,8 @@ public:
LatLng getLatLng() const { return state.getLatLng(); }
// Zoom
- void scaleBy(double ds, const PrecisionPoint& center = { 0, 0 }, const Duration& = Duration::zero());
- void setScale(double scale, const PrecisionPoint& center = { 0, 0 }, const Duration& = Duration::zero());
+ void scaleBy(double ds, const PrecisionPoint& center = {NAN, NAN}, const Duration& = Duration::zero());
+ void setScale(double scale, const PrecisionPoint& center = {NAN, NAN}, const Duration& = Duration::zero());
void setZoom(double zoom, const Duration& = Duration::zero());
double getZoom() const;
double getScale() const;
@@ -47,7 +47,7 @@ public:
// Angle
void rotateBy(const PrecisionPoint& first, const PrecisionPoint& second, const Duration& = Duration::zero());
void setAngle(double angle, const Duration& = Duration::zero());
- void setAngle(double angle, const PrecisionPoint& center);
+ void setAngle(double angle, const PrecisionPoint& center, const Duration& = Duration::zero());
double getAngle() const;
// Pitch
@@ -74,11 +74,6 @@ public:
bool isPanning() const { return state.isPanning(); }
private:
- void _moveBy(const PrecisionPoint&, const Duration& = Duration::zero());
- void _setScale(double scale, const PrecisionPoint& center, const Duration& = Duration::zero());
- void _easeTo(const CameraOptions&, const AnimationOptions&, double new_scale, double new_angle, double xn, double yn);
- void _setAngle(double angle, const Duration& = Duration::zero());
-
View &view;
TransformState state;
diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp
index 2a594cef1a..376424c74c 100644
--- a/src/mbgl/map/transform_state.cpp
+++ b/src/mbgl/map/transform_state.cpp
@@ -248,8 +248,8 @@ double TransformState::scaleZoom(double s) const {
return ::log2(s);
}
-float TransformState::worldSize() const {
- return util::tileSize * scale;
+double TransformState::worldSize() const {
+ return scale * util::tileSize;
}
PrecisionPoint TransformState::latLngToPoint(const LatLng& latLng) const {
diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp
index 9ae2f62a46..2aa031fa16 100644
--- a/src/mbgl/map/transform_state.hpp
+++ b/src/mbgl/map/transform_state.hpp
@@ -96,7 +96,7 @@ private:
double latY(double lat) const;
double zoomScale(double zoom) const;
double scaleZoom(double scale) const;
- float worldSize() const;
+ double worldSize() const;
mat4 coordinatePointMatrix(double z) const;
mat4 getPixelMatrix() const;