diff --git a/include/llmr/map/map.hpp b/include/llmr/map/map.hpp index 7703b77cb..ab1738f96 100644 --- a/include/llmr/map/map.hpp +++ b/include/llmr/map/map.hpp @@ -73,6 +73,8 @@ public: void resetZoom(); void startScaling(); void stopScaling(); + double getMinZoom(); + double getMaxZoom(); // Rotation void rotateBy(double sx, double sy, double ex, double ey, double duration = 0); @@ -82,6 +84,7 @@ public: void resetNorth(); void startRotating(); void stopRotating(); + bool canRotate(); // Debug void setDebug(bool value); diff --git a/include/llmr/map/transform.hpp b/include/llmr/map/transform.hpp index 4226ec7e4..6ab3d4b84 100644 --- a/include/llmr/map/transform.hpp +++ b/include/llmr/map/transform.hpp @@ -37,16 +37,19 @@ public: void setZoom(double zoom, time duration = 0); double getZoom() const; double getScale() const; - void startRotating(); - void stopRotating(); + void startScaling(); + void stopScaling(); + double getMinZoom(); + double getMaxZoom(); // Angle void rotateBy(double sx, double sy, double ex, double ey, time duration = 0); void setAngle(double angle, time duration = 0); void setAngle(double angle, double cx, double cy); double getAngle() const; - void startScaling(); - void stopScaling(); + void startRotating(); + void stopRotating(); + bool canRotate(); // Transitions bool needsTransition() const; @@ -63,7 +66,7 @@ private: void _moveBy(double dx, double dy, time duration = 0); void _setScale(double scale, double cx, double cy, time duration = 0); void _setScaleXY(double new_scale, double xn, double yn, time duration = 0); - void _setAngle(double angle, time duration = 0); + void _setAngle(double angle, time duration = 0, bool disable_interaction = false); void _clearPanning(); void _clearRotating(); void _clearScaling(); @@ -93,6 +96,7 @@ private: std::shared_ptr scale_timeout; std::shared_ptr rotate_timeout; std::shared_ptr pan_timeout; + std::shared_ptr interaction_timeout; }; } diff --git a/include/llmr/map/transform_state.hpp b/include/llmr/map/transform_state.hpp index 29d0f59c4..b3794e909 100644 --- a/include/llmr/map/transform_state.hpp +++ b/include/llmr/map/transform_state.hpp @@ -39,6 +39,9 @@ public: // Changing bool isChanging() const; + // Interactive + bool isInteractive() const; + private: double pixel_x() const; double pixel_y() const; @@ -57,6 +60,7 @@ private: bool rotating = false; bool scaling = false; bool panning = false; + bool interactive = true; // map position double x = 0, y = 0; diff --git a/src/map/map.cpp b/src/map/map.cpp index 3c0fbbcbb..a8ee8d233 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -312,6 +312,14 @@ void Map::stopScaling() { update(); } +double Map::getMinZoom() { + return transform.getMinZoom(); +} + +double Map::getMaxZoom() { + return transform.getMaxZoom(); +} + #pragma mark - Rotation @@ -356,6 +364,10 @@ void Map::stopRotating() { update(); } +bool Map::canRotate() { + return transform.canRotate(); +} + #pragma mark - Toggles diff --git a/src/map/transform.cpp b/src/map/transform.cpp index 23e42b764..2382cac88 100644 --- a/src/map/transform.cpp +++ b/src/map/transform.cpp @@ -33,8 +33,8 @@ bool Transform::resize(const uint16_t w, const uint16_t h, const float ratio, current.pixelRatio = final.pixelRatio = ratio; current.framebuffer[0] = final.framebuffer[0] = fb_w; current.framebuffer[1] = final.framebuffer[1] = fb_h; + if (!canRotate()) _setAngle(0); constrain(current.scale, current.y); - if (current.scale < MIN_ROTATE_SCALE && current.angle) _setAngle(0, 0); return true; } else { return false; @@ -57,6 +57,12 @@ void Transform::_moveBy(const double dx, const double dy, const time duration) { constrain(final.scale, final.y); + // Un-rotate when rotated and panning far enough to show off-world in corners. + double w = final.scale * util::tileSize / 2; + double m = std::sqrt(pow((current.width / 2), 2) + pow((current.height / 2), 2)); + double x = std::abs(sqrt(pow(final.x, 2) + pow(final.y, 2))); + if (current.angle && w - x < m) _setAngle(0, 500_milliseconds, true); + if (duration == 0) { current.x = final.x; current.y = final.y; @@ -197,6 +203,18 @@ void Transform::stopScaling() { _clearScaling(); } +double Transform::getMinZoom() { + double test_scale = current.scale; + double test_y = current.y; + constrain(test_scale, test_y); + + return std::log2(std::fmin(min_scale, test_scale)); +} + +double Transform::getMaxZoom() { + return std::log2(max_scale); +} + void Transform::_clearScaling() { // This is only called internally, so we don't need a lock here. @@ -251,7 +269,7 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl constrain(final.scale, final.y); // Undo rotation at low zooms. - if (final.scale < MIN_ROTATE_SCALE && current.angle) _setAngle(0, 500_milliseconds); + if (!canRotate() && current.angle) _setAngle(0, 500_milliseconds, true); if (duration == 0) { current.scale = final.scale; @@ -344,7 +362,7 @@ void Transform::setAngle(const double new_angle, const double cx, const double c } } -void Transform::_setAngle(double new_angle, const time duration) { +void Transform::_setAngle(double new_angle, const time duration, bool disable_interaction) { // This is only called internally, so we don't need a lock here. while (new_angle > M_PI) @@ -355,14 +373,22 @@ void Transform::_setAngle(double new_angle, const time duration) { final.angle = new_angle; // Prevent rotation at low zooms. - if (final.scale < MIN_ROTATE_SCALE) final.angle = 0; + if (!canRotate()) final.angle = 0; if (duration == 0) { current.angle = final.angle; + transitions.remove(interaction_timeout); + current.interactive = true; } else { time start = util::now(); transitions.emplace_front(std::make_shared>( current.angle, final.angle, current.angle, start, duration)); + if (disable_interaction) { + transitions.remove(interaction_timeout); + current.interactive = false; + interaction_timeout = std::make_shared>(true, current.interactive, start, duration); + transitions.emplace_front(interaction_timeout); + } } } @@ -400,6 +426,10 @@ void Transform::_clearRotating() { } } +bool Transform::canRotate() { + return (current.scale >= MIN_ROTATE_SCALE); +} + #pragma mark - Transition bool Transform::needsTransition() const { diff --git a/src/map/transform_state.cpp b/src/map/transform_state.cpp index c22678a4e..0d495fc6d 100644 --- a/src/map/transform_state.cpp +++ b/src/map/transform_state.cpp @@ -115,13 +115,20 @@ float TransformState::getAngle() const { } -#pragma mark - Rotation +#pragma mark - Changing bool TransformState::isChanging() const { return rotating || scaling || panning; } +#pragma mark - Interactive + +bool TransformState::isInteractive() const { + return interactive; +} + + #pragma mark - (private helper functions)