From adfac6f0615b7f79da2c80d0580428a92e9f0089 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Tue, 17 Nov 2015 17:19:54 +0200 Subject: [core] Added ConstrainMode::{HeightOnly,WidthAndHeight} ConstrainMode gives flexibility to our engine to choose between constraining both vertically and horizontally, or just vertically (default behavior). Constrain in both axis means we can no longer pan the map beyond the map boundaries. This fixes an issue where e.g. annotations disappear upon crossing the map boundaries. --- include/mbgl/map/map.hpp | 3 ++- include/mbgl/map/mode.hpp | 7 +++++ src/mbgl/map/map.cpp | 4 +-- src/mbgl/map/transform.cpp | 9 ++++--- src/mbgl/map/transform.hpp | 7 ++--- src/mbgl/map/transform_state.cpp | 29 ++++++++++++-------- src/mbgl/map/transform_state.hpp | 7 +++-- test/miscellaneous/transform.cpp | 58 +++++++++++++++++++++++++++++++++------- test/style/resource_loading.cpp | 2 +- 9 files changed, 93 insertions(+), 33 deletions(-) diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index f2e605141b..c190088088 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -45,7 +45,8 @@ class Map : private util::noncopyable { public: explicit Map(View&, FileSource&, MapMode mapMode = MapMode::Continuous, - GLContextMode contextMode = GLContextMode::Unique); + GLContextMode contextMode = GLContextMode::Unique, + ConstrainMode constrainMode = ConstrainMode::HeightOnly); ~Map(); // Pauses the render thread. The render thread will stop running but will not be terminated and will not lose state until resumed. diff --git a/include/mbgl/map/mode.hpp b/include/mbgl/map/mode.hpp index 6ad9af5c74..8b65baf99f 100644 --- a/include/mbgl/map/mode.hpp +++ b/include/mbgl/map/mode.hpp @@ -19,6 +19,13 @@ enum class GLContextMode : uint8_t { Shared, }; +// We can choose to constrain the map both horizontally or vertically, or only +// vertically e.g. while panning. +enum class ConstrainMode : uint8_t { + HeightOnly, + WidthAndHeight, +}; + } // namespace mbgl #endif // MBGL_MAP_MODE diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 69931ed29d..713222fe54 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -14,9 +14,9 @@ namespace mbgl { -Map::Map(View& view_, FileSource& fileSource, MapMode mapMode, GLContextMode contextMode) +Map::Map(View& view_, FileSource& fileSource, MapMode mapMode, GLContextMode contextMode, ConstrainMode constrainMode) : view(view_), - transform(std::make_unique(view)), + transform(std::make_unique(view, constrainMode)), data(std::make_unique(mapMode, contextMode, view.getPixelRatio())), context(std::make_unique>(util::ThreadContext{"Map", util::ThreadType::Map, util::ThreadPriority::Regular}, view, fileSource, *data)) { diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index 91fe87791e..55a492c493 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -34,8 +34,9 @@ static double _normalizeAngle(double angle, double anchorAngle) return angle; } -Transform::Transform(View &view_) +Transform::Transform(View &view_, ConstrainMode constrainMode) : view(view_) + , state(constrainMode) { } @@ -48,7 +49,7 @@ bool Transform::resize(const std::array size) { state.width = size[0]; state.height = size[1]; - state.constrain(state.scale, state.y); + state.constrain(state.scale, state.x, state.y); view.notifyMapChange(MapChangeRegionDidChange); @@ -105,7 +106,7 @@ 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, y); + state.constrain(state.scale, x, y); CameraOptions options; options.duration = duration; @@ -239,7 +240,7 @@ void Transform::_easeTo(const CameraOptions& options, double new_scale, double n double x = xn; double y = yn; - state.constrain(scale, y); + state.constrain(scale, x, y); double angle = _normalizeAngle(new_angle, state.angle); state.angle = _normalizeAngle(state.angle, angle); diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp index 0fc900dac7..7f424eba2b 100644 --- a/src/mbgl/map/transform.hpp +++ b/src/mbgl/map/transform.hpp @@ -1,10 +1,11 @@ #ifndef MBGL_MAP_TRANSFORM #define MBGL_MAP_TRANSFORM -#include #include -#include +#include +#include #include +#include #include #include @@ -18,7 +19,7 @@ class View; class Transform : private util::noncopyable { public: - Transform(View&); + Transform(View&, ConstrainMode); // Map view bool resize(std::array size); diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index f58bfde8df..44a4c29402 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -7,7 +7,8 @@ using namespace mbgl; -TransformState::TransformState() +TransformState::TransformState(ConstrainMode constrainMode_) + : constrainMode(constrainMode_) { } @@ -143,8 +144,9 @@ double TransformState::getScale() const { double TransformState::getMinZoom() const { double test_scale = scale; - double test_y = y; - constrain(test_scale, test_y); + double unused_x = x; + double unused_y = y; + constrain(test_scale, unused_x, unused_y); return ::log2(::fmin(min_scale, test_scale)); } @@ -316,17 +318,22 @@ mat4 TransformState::getPixelMatrix() const { #pragma mark - (private helper functions) -void TransformState::constrain(double& scale_, double& y_) const { - // Constrain minimum zoom to avoid zooming out far enough to show off-world areas. - if (scale_ < height / util::tileSize) { - scale_ = height / util::tileSize; +void TransformState::constrain(double& scale_, double& x_, double& y_) const { + // Constrain minimum scale to avoid zooming out far enough to show off-world areas. + if (constrainMode == ConstrainMode::WidthAndHeight) { + scale_ = std::max(scale_, static_cast(width / util::tileSize)); } - // Constrain min/max vertical pan to avoid showing off-world areas. - double max_y = ((scale_ * util::tileSize) - height) / 2; + scale_ = std::max(scale_, static_cast(height / util::tileSize)); - if (y_ > max_y) y_ = max_y; - if (y_ < -max_y) y_ = -max_y; + // Constrain min/max pan to avoid showing off-world areas. + if (constrainMode == ConstrainMode::WidthAndHeight) { + double max_x = (scale_ * util::tileSize - width) / 2; + x_ = std::max(-max_x, std::min(x_, max_x)); + } + + double max_y = (scale_ * util::tileSize - height) / 2; + y_ = std::max(-max_y, std::min(y_, max_y)); } diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp index 0373798f00..37ea7fa0f4 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -1,6 +1,7 @@ #ifndef MBGL_MAP_TRANSFORM_STATE #define MBGL_MAP_TRANSFORM_STATE +#include #include #include #include @@ -21,7 +22,7 @@ class TransformState { friend class Transform; public: - TransformState(); + TransformState(ConstrainMode = ConstrainMode::HeightOnly); // Matrix void matrixFor(mat4& matrix, const TileID& id, const int8_t z) const; @@ -73,7 +74,7 @@ public: TileCoordinate pointToCoordinate(const PrecisionPoint&) const; private: - void constrain(double& scale, double& y) const; + void constrain(double& scale, double& x, double& y) const; // Limit the amount of zooming possible on the map. double min_scale = std::pow(2, 0); @@ -93,6 +94,8 @@ private: mat4 getPixelMatrix() const; private: + ConstrainMode constrainMode; + // animation state bool rotating = false; bool scaling = false; diff --git a/test/miscellaneous/transform.cpp b/test/miscellaneous/transform.cpp index 426e9e534d..a5c91b84ef 100644 --- a/test/miscellaneous/transform.cpp +++ b/test/miscellaneous/transform.cpp @@ -7,13 +7,13 @@ using namespace mbgl; TEST(Transform, InvalidScale) { MockView view; - Transform transform(view); + Transform transform(view, ConstrainMode::HeightOnly); ASSERT_DOUBLE_EQ(0, transform.getLatLng().latitude); ASSERT_DOUBLE_EQ(0, transform.getLatLng().longitude); ASSERT_DOUBLE_EQ(1, transform.getScale()); - transform.setScale(2); + transform.setScale(2 << 0); ASSERT_DOUBLE_EQ(0, transform.getLatLng().latitude); ASSERT_DOUBLE_EQ(0, transform.getLatLng().longitude); @@ -47,13 +47,13 @@ TEST(Transform, InvalidScale) { TEST(Transform, InvalidLatLng) { MockView view; - Transform transform(view); + Transform transform(view, ConstrainMode::HeightOnly); ASSERT_DOUBLE_EQ(0, transform.getLatLng().latitude); ASSERT_DOUBLE_EQ(0, transform.getLatLng().longitude); ASSERT_DOUBLE_EQ(1, transform.getScale()); - transform.setScale(2); + transform.setScale(2 << 0); transform.setLatLng({ 8, 10 }); ASSERT_DOUBLE_EQ(8, transform.getLatLng().latitude); @@ -83,13 +83,13 @@ TEST(Transform, InvalidLatLng) { TEST(Transform, InvalidBearing) { MockView view; - Transform transform(view); + Transform transform(view, ConstrainMode::HeightOnly); ASSERT_DOUBLE_EQ(0, transform.getLatLng().latitude); ASSERT_DOUBLE_EQ(0, transform.getLatLng().longitude); ASSERT_DOUBLE_EQ(1, transform.getScale()); - transform.setScale(2); + transform.setScale(2 << 0); transform.setAngle(2); ASSERT_DOUBLE_EQ(0, transform.getLatLng().latitude); @@ -108,10 +108,10 @@ TEST(Transform, InvalidBearing) { TEST(Transform, PerspectiveProjection) { MockView view; - Transform transform(view); + Transform transform(view, ConstrainMode::HeightOnly); transform.resize({{ 1000, 1000 }}); - transform.setScale(1024); + transform.setScale(2 << 9); transform.setPitch(0.9); transform.setLatLng(LatLng(38, -77)); @@ -129,7 +129,7 @@ TEST(Transform, PerspectiveProjection) { ASSERT_NEAR(-76.75823239205641, loc.longitude, 0.0001); ASSERT_NEAR(37.692872969426375, loc.latitude, 0.0001); - vec2 point = transform.getState().latLngToPoint({38.74661326302018, -77.59198961199148}); + PrecisionPoint point = transform.getState().latLngToPoint({38.74661326302018, -77.59198961199148}); ASSERT_NEAR(point.x, 0, 0.01); ASSERT_NEAR(point.y, 1000, 0.01); @@ -137,3 +137,43 @@ TEST(Transform, PerspectiveProjection) { ASSERT_NEAR(point.x, 1000, 0.02); ASSERT_NEAR(point.y, 0, 0.02); } + +TEST(Transform, ConstrainHeightOnly) { + MockView view; + LatLng loc; + LatLngBounds bounds; + + Transform transform(view, ConstrainMode::HeightOnly); + transform.resize({{ 1000, 1000 }}); + transform.setScale(1024); + + transform.setLatLng(bounds.sw); + loc = transform.getState().pointToLatLng({ 500, 500 }); + ASSERT_NEAR(-85.021422866378714, loc.latitude, 0.0001); + ASSERT_NEAR(180, std::abs(loc.longitude), 0.0001); + + transform.setLatLng(bounds.ne); + loc = transform.getState().pointToLatLng({ 500, 500 }); + ASSERT_NEAR(85.021422866378742, loc.latitude, 0.0001); + ASSERT_NEAR(180, std::abs(loc.longitude), 0.0001); +} + +TEST(Transform, ConstrainWidthAndHeight) { + MockView view; + LatLng loc; + LatLngBounds bounds; + + Transform transform(view, ConstrainMode::WidthAndHeight); + transform.resize({{ 1000, 1000 }}); + transform.setScale(1024); + + transform.setLatLng(bounds.sw); + loc = transform.getState().pointToLatLng({ 500, 500 }); + ASSERT_NEAR(-85.021422866378714, loc.latitude, 0.0001); + ASSERT_NEAR(-179.65667724609375, loc.longitude, 0.0001); + + transform.setLatLng(bounds.ne); + loc = transform.getState().pointToLatLng({ 500, 500 }); + ASSERT_NEAR(85.021422866378742, loc.latitude, 0.0001); + ASSERT_NEAR(179.65667724609358, std::abs(loc.longitude), 0.0001); +} diff --git a/test/style/resource_loading.cpp b/test/style/resource_loading.cpp index c6f1c1dfae..cdc84e96b2 100644 --- a/test/style/resource_loading.cpp +++ b/test/style/resource_loading.cpp @@ -24,7 +24,7 @@ public: FileSource& fileSource, const std::function& callback) : data_(MapMode::Still, GLContextMode::Unique, view.getPixelRatio()), - transform_(view), + transform_(view, ConstrainMode::HeightOnly), callback_(callback) { util::ThreadContext::setFileSource(&fileSource); -- cgit v1.2.1