diff --git a/drape_frontend/animation/follow_animation.cpp b/drape_frontend/animation/follow_animation.cpp index ccf359c87e..9054257fd0 100644 --- a/drape_frontend/animation/follow_animation.cpp +++ b/drape_frontend/animation/follow_animation.cpp @@ -13,20 +13,26 @@ MapFollowAnimation::MapFollowAnimation(ScreenBase const & screen, m2::PointD const & globalUserPosition, m2::PointD const & endPixelPosition, double startScale, double endScale, - double startAngle, double endAngle) + double startAngle, double endAngle, + bool isAutoZoom) : Animation(true /* couldBeInterrupted */, true /* couldBeBlended */) - , m_scaleInterpolator(startScale, endScale) + , m_isAutoZoom(isAutoZoom) + , m_scaleInterpolator(startScale, endScale, m_isAutoZoom) , m_angleInterpolator(startAngle, endAngle) , m_globalPosition(globalUserPosition) , m_endPixelPosition(endPixelPosition) { + m_offset = screen.PtoG(screen.P3dtoP(m_endPixelPosition)) - m_globalPosition; + double const scale = m_isAutoZoom ? screen.GetScale() : (startScale + endScale) / 2.0; + double const moveDuration = PositionInterpolator::GetMoveDuration(m_offset.Length(), screen.PixelRectIn3d(), scale); + + m_offsetInterpolator = PositionInterpolator(moveDuration, 0.0, m_offset, m2::PointD(0.0, 0.0)); + double const duration = CalculateDuration(); + m_scaleInterpolator.SetMinDuration(duration); m_angleInterpolator.SetMinDuration(duration); - m_offset = screen.PtoG(screen.P3dtoP(m_endPixelPosition)) - m_globalPosition; - m_offsetInterpolator = PositionInterpolator(duration, 0.0, m_offset, m2::PointD(0.0, 0.0)); - m_objects.insert(Animation::MapPlane); if (m_scaleInterpolator.IsActive()) @@ -63,7 +69,12 @@ void MapFollowAnimation::Finish() if (m_angleInterpolator.IsActive()) m_angleInterpolator.Finish(); if (m_scaleInterpolator.IsActive()) - m_scaleInterpolator.Finish(); + { + if (m_isAutoZoom) + m_scaleInterpolator.SetActive(false); + else + m_scaleInterpolator.Finish(); + } if (m_offsetInterpolator.IsActive()) m_offsetInterpolator.Finish(); Animation::Finish(); @@ -73,7 +84,7 @@ void MapFollowAnimation::SetMaxDuration(double maxDuration) { if (m_angleInterpolator.IsActive()) m_angleInterpolator.SetMaxDuration(maxDuration); - if (m_scaleInterpolator.IsActive()) + if (!m_isAutoZoom && m_scaleInterpolator.IsActive()) m_scaleInterpolator.SetMaxDuration(maxDuration); if (m_offsetInterpolator.IsActive()) m_offsetInterpolator.SetMaxDuration(maxDuration); @@ -86,8 +97,10 @@ double MapFollowAnimation::GetDuration() const double MapFollowAnimation::CalculateDuration() const { - return max(m_offsetInterpolator.GetDuration(), - max(m_angleInterpolator.GetDuration(), m_scaleInterpolator.GetDuration())); + double duration = max(m_angleInterpolator.GetDuration(), m_offsetInterpolator.GetDuration()); + if (!m_isAutoZoom) + duration = max(duration, m_scaleInterpolator.GetDuration()); + return duration; } bool MapFollowAnimation::IsFinished() const @@ -112,7 +125,8 @@ bool MapFollowAnimation::GetProperty(TObject object, TProperty property, bool ta ScreenBase tmp = AnimationSystem::Instance().GetLastScreen(); if (targetValue) { - tmp.SetFromParams(m_globalPosition, m_angleInterpolator.GetTargetAngle(), m_scaleInterpolator.GetTargetScale()); + tmp.SetFromParams(m_globalPosition, m_angleInterpolator.GetTargetAngle(), m_isAutoZoom ? m_scaleInterpolator.GetScale() + : m_scaleInterpolator.GetTargetScale()); tmp.MatchGandP3d(m_globalPosition, m_endPixelPosition); } else @@ -136,7 +150,8 @@ bool MapFollowAnimation::GetProperty(TObject object, TProperty property, bool ta } if (property == Animation::Scale) { - value = PropertyValue(targetValue ? m_scaleInterpolator.GetTargetScale() : m_scaleInterpolator.GetScale()); + value = PropertyValue((targetValue && !m_isAutoZoom) ? m_scaleInterpolator.GetTargetScale() + : m_scaleInterpolator.GetScale()); return true; } ASSERT(false, ("Wrong property:", property)); diff --git a/drape_frontend/animation/follow_animation.hpp b/drape_frontend/animation/follow_animation.hpp index a1da305f19..ac17d6e27f 100644 --- a/drape_frontend/animation/follow_animation.hpp +++ b/drape_frontend/animation/follow_animation.hpp @@ -13,7 +13,8 @@ public: m2::PointD const & globalUserPosition, m2::PointD const & endPixelPosition, double startScale, double endScale, - double startAngle, double endAngle); + double startAngle, double endAngle, + bool isAutoZoom); Animation::Type GetType() const override { return Animation::MapFollow; } @@ -40,6 +41,8 @@ public: bool GetProperty(TObject object, TProperty property, PropertyValue & value) const override; bool GetTargetProperty(TObject object, TProperty property, PropertyValue & value) const override; + bool IsAutoZoom() const { return m_isAutoZoom; } + bool HasScale() const; bool HasPixelOffset() const; @@ -47,6 +50,7 @@ private: bool GetProperty(TObject object, TProperty property, bool targetValue, PropertyValue & value) const; double CalculateDuration() const; + bool m_isAutoZoom; ScaleInterpolator m_scaleInterpolator; AngleInterpolator m_angleInterpolator; PositionInterpolator m_offsetInterpolator; diff --git a/drape_frontend/animation/interpolators.cpp b/drape_frontend/animation/interpolators.cpp index f933953be7..f51a010c54 100644 --- a/drape_frontend/animation/interpolators.cpp +++ b/drape_frontend/animation/interpolators.cpp @@ -129,15 +129,14 @@ PositionInterpolator::PositionInterpolator(double delay, } //static -double PositionInterpolator::GetMoveDuration(m2::PointD const & startPosition, m2::PointD const & endPosition, - m2::RectD const & viewportRect, double scale) +double PositionInterpolator::GetMoveDuration(double globalDistance, m2::RectD const & viewportRect, double scale) { double const kMinMoveDuration = 0.2; double const kMinSpeedScalar = 0.2; double const kMaxSpeedScalar = 7.0; double const kEps = 1e-5; - double const pixelLength = endPosition.Length(startPosition) / scale; + double const pixelLength = globalDistance / scale; if (pixelLength < kEps) return 0.0; @@ -149,6 +148,13 @@ double PositionInterpolator::GetMoveDuration(m2::PointD const & startPosition, m return CalcAnimSpeedDuration(pixelLength, pixelSpeed); } +//static +double PositionInterpolator::GetMoveDuration(m2::PointD const & startPosition, m2::PointD const & endPosition, + m2::RectD const & viewportRect, double scale) +{ + return GetMoveDuration(endPosition.Length(startPosition), viewportRect, scale); +} + //static double PositionInterpolator::GetMoveDuration(m2::PointD const & startPosition, m2::PointD const & endPosition, @@ -171,15 +177,15 @@ void PositionInterpolator::Finish() } ScaleInterpolator::ScaleInterpolator() - : ScaleInterpolator(1.0 /* startScale */, 1.0 /* endScale */) + : ScaleInterpolator(1.0 /* startScale */, 1.0 /* endScale */, false /* isAutoZoom */) {} -ScaleInterpolator::ScaleInterpolator(double startScale, double endScale) - : ScaleInterpolator(0.0 /* delay */, startScale, endScale) +ScaleInterpolator::ScaleInterpolator(double startScale, double endScale, bool isAutoZoom) + : ScaleInterpolator(0.0 /* delay */, startScale, endScale, isAutoZoom) {} -ScaleInterpolator::ScaleInterpolator(double delay, double startScale, double endScale) - : Interpolator(ScaleInterpolator::GetScaleDuration(startScale, endScale), delay) +ScaleInterpolator::ScaleInterpolator(double delay, double startScale, double endScale, bool isAutoZoom) + : Interpolator(ScaleInterpolator::GetScaleDuration(startScale, endScale, isAutoZoom), delay) , m_startScale(startScale) , m_endScale(endScale) , m_scale(startScale) @@ -188,10 +194,10 @@ ScaleInterpolator::ScaleInterpolator(double delay, double startScale, double end } // static -double ScaleInterpolator::GetScaleDuration(double startScale, double endScale) +double ScaleInterpolator::GetScaleDuration(double startScale, double endScale, bool isAutoZoom) { // Resize 2.0 times should be done for 0.2 seconds. - double constexpr kPixelSpeed = 2.0 / 0.2; + double const kPixelSpeed = isAutoZoom ? (2.0 / 1.5) : (2.0 / 0.2); if (startScale > endScale) swap(startScale, endScale); diff --git a/drape_frontend/animation/interpolators.hpp b/drape_frontend/animation/interpolators.hpp index 66892f2c4e..d5a044c8eb 100644 --- a/drape_frontend/animation/interpolators.hpp +++ b/drape_frontend/animation/interpolators.hpp @@ -15,6 +15,7 @@ public: virtual void Finish(); bool IsActive() const; + void SetActive(bool active); bool IsFinished() const; void SetMaxDuration(double maxDuration); @@ -24,7 +25,6 @@ public: protected: double GetT() const; double GetElapsedTime() const; - void SetActive(bool active); private: double m_elapsedTime; @@ -56,6 +56,8 @@ public: m2::PointD const & startPosition, m2::PointD const & endPosition, m2::RectD const & viewportRect, double scale); + static double GetMoveDuration(double globalDistance, m2::RectD const & viewportRect, double scale); + static double GetMoveDuration(m2::PointD const & startPosition, m2::PointD const & endPosition, m2::RectD const & viewportRect, double scale); @@ -81,10 +83,10 @@ class ScaleInterpolator: public Interpolator public: ScaleInterpolator(); - ScaleInterpolator(double startScale, double endScale); - ScaleInterpolator(double delay, double startScale, double endScale); + ScaleInterpolator(double startScale, double endScale, bool isAutoZoom); + ScaleInterpolator(double delay, double startScale, double endScale, bool isAutoZoom); - static double GetScaleDuration(double startScale, double endScale); + static double GetScaleDuration(double startScale, double endScale, bool isAutoZoom); // Interpolator overrides: void Advance(double elapsedSeconds) override; diff --git a/drape_frontend/animation/linear_animation.cpp b/drape_frontend/animation/linear_animation.cpp index 048196cfc8..eb4caa8060 100644 --- a/drape_frontend/animation/linear_animation.cpp +++ b/drape_frontend/animation/linear_animation.cpp @@ -11,7 +11,7 @@ MapLinearAnimation::MapLinearAnimation(m2::PointD const & startPos, m2::PointD c : Animation(true /* couldBeInterrupted */, false /* couldBeBlended */) , m_angleInterpolator(startAngle, endAngle) , m_positionInterpolator(startPos, endPos, convertor) - , m_scaleInterpolator(startScale, endScale) + , m_scaleInterpolator(startScale, endScale, false /* isAutoZoom */) { m_objects.insert(Animation::MapPlane); @@ -56,7 +56,7 @@ void MapLinearAnimation::SetRotate(double startAngle, double endAngle) void MapLinearAnimation::SetScale(double startScale, double endScale) { - m_scaleInterpolator = ScaleInterpolator(startScale, endScale); + m_scaleInterpolator = ScaleInterpolator(startScale, endScale, false /* isAutoZoom */); if (m_scaleInterpolator.IsActive()) m_properties.insert(Animation::Scale); } diff --git a/drape_frontend/animation/scale_animation.cpp b/drape_frontend/animation/scale_animation.cpp index e0257e953c..0ee6b12b0b 100644 --- a/drape_frontend/animation/scale_animation.cpp +++ b/drape_frontend/animation/scale_animation.cpp @@ -10,7 +10,7 @@ namespace df MapScaleAnimation::MapScaleAnimation(double startScale, double endScale, m2::PointD const & globalScaleCenter, m2::PointD const & pxScaleCenter) : Animation(true /* couldBeInterrupted */, true /* couldBeBlended */) - , m_scaleInterpolator(startScale, endScale) + , m_scaleInterpolator(startScale, endScale, false /* isAutoZoom */) , m_pxScaleCenter(pxScaleCenter) , m_globalScaleCenter(globalScaleCenter) { diff --git a/drape_frontend/drape_engine.cpp b/drape_frontend/drape_engine.cpp index 8ad2631e88..6301fef199 100644 --- a/drape_frontend/drape_engine.cpp +++ b/drape_frontend/drape_engine.cpp @@ -265,10 +265,10 @@ void DrapeEngine::StopLocationFollow() MessagePriority::High); } -void DrapeEngine::FollowRoute(int preferredZoomLevel, int preferredZoomLevel3d) +void DrapeEngine::FollowRoute(int preferredZoomLevel, int preferredZoomLevel3d, bool enableAutoZoom) { m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, - make_unique_dp(preferredZoomLevel, preferredZoomLevel3d), + make_unique_dp(preferredZoomLevel, preferredZoomLevel3d, enableAutoZoom), MessagePriority::High); } @@ -379,6 +379,13 @@ void DrapeEngine::SetWidgetLayout(gui::TWidgetsLayoutInfo && info) MessagePriority::Normal); } +void DrapeEngine::AllowAutoZoom(bool allowAutoZoom) +{ + m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, + make_unique_dp(allowAutoZoom), + MessagePriority::Normal); +} + void DrapeEngine::Allow3dMode(bool allowPerspectiveInNavigation, bool allow3dBuildings) { m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread, diff --git a/drape_frontend/drape_engine.hpp b/drape_frontend/drape_engine.hpp index b48cdb9d2d..f80ace1eb4 100644 --- a/drape_frontend/drape_engine.hpp +++ b/drape_frontend/drape_engine.hpp @@ -124,12 +124,14 @@ public: void AddRoute(m2::PolylineD const & routePolyline, vector const & turns, df::ColorConstant color, df::RoutePattern pattern = df::RoutePattern()); void RemoveRoute(bool deactivateFollowing); - void FollowRoute(int preferredZoomLevel, int preferredZoomLevel3d); + void FollowRoute(int preferredZoomLevel, int preferredZoomLevel3d, bool enableAutoZoom); void DeactivateRouteFollowing(); void SetRoutePoint(m2::PointD const & position, bool isStart, bool isValid); void SetWidgetLayout(gui::TWidgetsLayoutInfo && info); + void AllowAutoZoom(bool allowAutoZoom); + void Allow3dMode(bool allowPerspectiveInNavigation, bool allow3dBuildings); void EnablePerspective(); diff --git a/drape_frontend/frontend_renderer.cpp b/drape_frontend/frontend_renderer.cpp index 560fe7a4b6..affcbab563 100755 --- a/drape_frontend/frontend_renderer.cpp +++ b/drape_frontend/frontend_renderer.cpp @@ -484,7 +484,8 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) if (m_pendingFollowRoute != nullptr) { - FollowRoute(m_pendingFollowRoute->m_preferredZoomLevel, m_pendingFollowRoute->m_preferredZoomLevelIn3d); + FollowRoute(m_pendingFollowRoute->m_preferredZoomLevel, m_pendingFollowRoute->m_preferredZoomLevelIn3d, + m_pendingFollowRoute->m_enableAutoZoom); m_pendingFollowRoute.reset(); } break; @@ -520,12 +521,12 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) // receive FollowRoute message before FlushRoute message, so we need to postpone its processing. if (m_routeRenderer->GetRouteData() == nullptr) { - m_pendingFollowRoute.reset( - new FollowRouteData(msg->GetPreferredZoomLevel(), msg->GetPreferredZoomLevelIn3d())); + m_pendingFollowRoute.reset(new FollowRouteData(msg->GetPreferredZoomLevel(), msg->GetPreferredZoomLevelIn3d(), + msg->EnableAutoZoom())); break; } - FollowRoute(msg->GetPreferredZoomLevel(), msg->GetPreferredZoomLevelIn3d()); + FollowRoute(msg->GetPreferredZoomLevel(), msg->GetPreferredZoomLevelIn3d(), msg->EnableAutoZoom()); break; } @@ -608,6 +609,13 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) break; } + case Message::AllowAutoZoom: + { + ref_ptr const msg = message; + m_myPositionController->EnableAutoZoomInRouting(msg->AllowAutoZoom()); + break; + } + case Message::EnablePerspective: { AddUserEvent(SetAutoPerspectiveEvent(true /* isAutoPerspective */)); @@ -720,6 +728,7 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) case Message::Invalidate: { m_myPositionController->ResetRoutingNotFollowTimer(); + m_myPositionController->ResetRoutingNotAutoZoomTimer(); break; } @@ -733,11 +742,11 @@ unique_ptr FrontendRenderer::CreateRoutine() return make_unique(*this); } -void FrontendRenderer::FollowRoute(int preferredZoomLevel, int preferredZoomLevelIn3d) +void FrontendRenderer::FollowRoute(int preferredZoomLevel, int preferredZoomLevelIn3d, bool enableAutoZoom) { - m_myPositionController->ActivateRouting(!m_enablePerspectiveInNavigation ? preferredZoomLevel - : preferredZoomLevelIn3d); + m_myPositionController->ActivateRouting(!m_enablePerspectiveInNavigation ? preferredZoomLevel : preferredZoomLevelIn3d, + enableAutoZoom); if (m_enablePerspectiveInNavigation) AddUserEvent(SetAutoPerspectiveEvent(true /* isAutoPerspective */)); @@ -1002,9 +1011,6 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) } } - m_myPositionController->Render(MyPositionController::RenderAccuracy, - modelView, make_ref(m_gpuProgramManager), m_generalUniforms); - if (m_framebuffer->IsSupported()) { m_framebuffer->Enable(); @@ -1049,8 +1055,7 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) m_routeRenderer->RenderRouteSigns(modelView, make_ref(m_gpuProgramManager), m_generalUniforms); - m_myPositionController->Render(MyPositionController::RenderMyPosition, - modelView, make_ref(m_gpuProgramManager), m_generalUniforms); + m_myPositionController->Render(modelView, make_ref(m_gpuProgramManager), m_generalUniforms); if (m_guiRenderer != nullptr) m_guiRenderer->Render(make_ref(m_gpuProgramManager), modelView); @@ -1392,6 +1397,7 @@ void FrontendRenderer::OnScaleEnded() void FrontendRenderer::OnAnimatedScaleEnded() { + m_myPositionController->ResetRoutingNotAutoZoomTimer(); PullToBoundArea(false /* randomPlace */, false /* applyZoom */); } @@ -1624,6 +1630,11 @@ void FrontendRenderer::ChangeModelView(m2::PointD const & userPos, double azimut AddUserEvent(FollowAndRotateEvent(userPos, pxZero, azimuth, preferredZoomLevel, true)); } +void FrontendRenderer::ChangeModelView(double autoScale, m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero) +{ + AddUserEvent(FollowAndRotateEvent(userPos, pxZero, azimuth, autoScale)); +} + ScreenBase const & FrontendRenderer::ProcessEvents(bool & modelViewChanged, bool & viewportChanged) { ScreenBase const & modelView = m_userEventStream.ProcessEvents(modelViewChanged, viewportChanged); diff --git a/drape_frontend/frontend_renderer.hpp b/drape_frontend/frontend_renderer.hpp index 5f7459c7bb..0f08559bc2 100755 --- a/drape_frontend/frontend_renderer.hpp +++ b/drape_frontend/frontend_renderer.hpp @@ -142,6 +142,7 @@ public: void ChangeModelView(m2::RectD const & rect) override; void ChangeModelView(m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero, int preferredZoomLevel) override; + void ChangeModelView(double autoScale, m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero) override; protected: void AcceptMessage(ref_ptr message) override; @@ -221,7 +222,7 @@ private: using TRenderGroupRemovePredicate = function const &)>; void RemoveRenderGroupsLater(TRenderGroupRemovePredicate const & predicate); - void FollowRoute(int preferredZoomLevel, int preferredZoomLevelIn3d); + void FollowRoute(int preferredZoomLevel, int preferredZoomLevelIn3d, bool enableAutoZoom); void InvalidateRect(m2::RectD const & gRect); bool CheckTileGenerations(TileKey const & tileKey); void UpdateCanBeDeletedStatus(); @@ -301,13 +302,16 @@ private: struct FollowRouteData { FollowRouteData(int preferredZoomLevel, - int preferredZoomLevelIn3d) + int preferredZoomLevelIn3d, + bool enableAutoZoom) : m_preferredZoomLevel(preferredZoomLevel) , m_preferredZoomLevelIn3d(preferredZoomLevelIn3d) + , m_enableAutoZoom(enableAutoZoom) {} int m_preferredZoomLevel; int m_preferredZoomLevelIn3d; + bool m_enableAutoZoom; }; unique_ptr m_pendingFollowRoute; diff --git a/drape_frontend/message.hpp b/drape_frontend/message.hpp index 78b89127f5..1be9aaef19 100644 --- a/drape_frontend/message.hpp +++ b/drape_frontend/message.hpp @@ -56,7 +56,8 @@ public: BlockTapEvents, SetTimeInBackground, SetAddNewPlaceMode, - SetDisplacementMode + SetDisplacementMode, + AllowAutoZoom }; virtual ~Message() {} diff --git a/drape_frontend/message_subclasses.hpp b/drape_frontend/message_subclasses.hpp index 67da3f47dd..170da7671d 100644 --- a/drape_frontend/message_subclasses.hpp +++ b/drape_frontend/message_subclasses.hpp @@ -645,18 +645,21 @@ public: class FollowRouteMessage : public Message { public: - FollowRouteMessage(int preferredZoomLevel, int preferredZoomLevelIn3d) + FollowRouteMessage(int preferredZoomLevel, int preferredZoomLevelIn3d, bool enableAutoZoom) : m_preferredZoomLevel(preferredZoomLevel) , m_preferredZoomLevelIn3d(preferredZoomLevelIn3d) + , m_enableAutoZoom(enableAutoZoom) {} Type GetType() const override { return Message::FollowRoute; } int GetPreferredZoomLevel() const { return m_preferredZoomLevel; } int GetPreferredZoomLevelIn3d() const { return m_preferredZoomLevelIn3d; } + bool EnableAutoZoom() const { return m_enableAutoZoom; } private: int const m_preferredZoomLevel; int const m_preferredZoomLevelIn3d; + bool const m_enableAutoZoom; }; class InvalidateTexturesMessage : public BaseBlockingMessage @@ -702,6 +705,20 @@ private: bool const m_allow3dBuildings; }; +class AllowAutoZoomMessage : public Message +{ +public: + AllowAutoZoomMessage(bool allowAutoZoom) + : m_allowAutoZoom(allowAutoZoom) + {} + + Type GetType() const override { return Message::AllowAutoZoom; } + bool AllowAutoZoom() const { return m_allowAutoZoom; } + +private: + bool const m_allowAutoZoom; +}; + class Allow3dBuildingsMessage : public Message { public: diff --git a/drape_frontend/my_position_controller.cpp b/drape_frontend/my_position_controller.cpp index 80d361b3c1..fb5cfa8dee 100644 --- a/drape_frontend/my_position_controller.cpp +++ b/drape_frontend/my_position_controller.cpp @@ -28,9 +28,11 @@ double const kMaxPendingLocationTimeSec = 60.0; double const kMaxTimeInBackgroundSec = 60.0 * 60; double const kMaxNotFollowRoutingTimeSec = 10.0; double const kMaxUpdateLocationInvervalSec = 30.0; +double const kMaxNotAutoZoomRoutingTimeSec = 10.0; int const kZoomThreshold = 10; int const kMaxScaleZoomLevel = 16; +int const kDefaultAutoZoom = 16; string LocationModeStatisticsName(location::EMyPositionMode mode) { @@ -63,7 +65,46 @@ int GetZoomLevel(ScreenBase const & screen, m2::PointD const & position, double return GetZoomLevel(s); } +// Calculate zoom value in meters per pixel +double CalculateZoomBySpeed(double speed) +{ + using TSpeedScale = pair; + static vector scales = { + make_pair(20.0, 0.1), + make_pair(40.0, 0.25), + make_pair(60.0, 0.5), + make_pair(80.0, 0.85), + make_pair(100.0, 1.75), + make_pair(120.0, 3.5), + }; + double const kDefaultSpeed = 80.0; + if (speed < 0.0) + speed = kDefaultSpeed; + else + speed *= 3.6; // convert speed from m/s to km/h + + size_t i = 0; + for (size_t sz = scales.size(); i < sz; ++i) + if (scales[i].first >= speed) + break; + + if (i == 0) + return scales.front().second; + if (i == scales.size()) + return scales.back().second; + + double const minSpeed = scales[i - 1].first; + double const maxSpeed = scales[i].first; + double const k = (speed - minSpeed) / (maxSpeed - minSpeed); + + double const minScale = scales[i - 1].second; + double const maxScale = scales[i].second; + double const zoom = minScale + k * (maxScale - minScale); + double const vs = df::VisualParams::Instance().GetVisualScale(); + + return zoom / vs; +} } // namespace @@ -80,14 +121,19 @@ MyPositionController::MyPositionController(location::EMyPositionMode initMode, d , m_drawDirection(0.0) , m_oldPosition(m2::PointD::Zero()) , m_oldDrawDirection(0.0) + , m_enableAutoZoomInRouting(false) + , m_autoScale(GetScale(kDefaultAutoZoom)) , m_lastGPSBearing(false) , m_lastLocationTimestamp(0.0) , m_positionYOffset(kPositionOffsetY) , m_isVisible(false) , m_isDirtyViewport(false) + , m_isDirtyAutoZoom(false) , m_isPendingAnimation(false) , m_isPositionAssigned(false) , m_isDirectionAssigned(false) + , m_positionIsObsolete(false) + , m_needBlockAutoZoom(false) , m_notFollowAfterPending(false) { if (m_isFirstLaunch) @@ -159,11 +205,13 @@ void MyPositionController::DragEnded(m2::PointD const & distance) void MyPositionController::ScaleStarted() { m_needBlockAnimation = true; + ResetRoutingNotAutoZoomTimer(); } void MyPositionController::ScaleEnded() { m_needBlockAnimation = false; + ResetRoutingNotAutoZoomTimer(); if (m_wasRotationInScaling) { m_wasRotationInScaling = false; @@ -185,6 +233,15 @@ void MyPositionController::ResetRoutingNotFollowTimer() m_routingNotFollowTimer.Reset(); } +void MyPositionController::ResetRoutingNotAutoZoomTimer() +{ + if (m_isInRouting && m_enableAutoZoomInRouting) + { + m_needBlockAutoZoom = true; + m_routingNotAutoZoomTimer.Reset(); + } +} + void MyPositionController::CorrectScalePoint(m2::PointD & pt) const { if (IsModeChangeViewport()) @@ -284,6 +341,9 @@ void MyPositionController::OnLocationUpdate(location::GpsInfo const & info, bool m_position = MercatorBounds::FromLatLon(info.m_latitude, info.m_longitude); m_errorRadius = rect.SizeX() * 0.5; + double const zoom = CalculateZoomBySpeed(info.m_speed); + m_autoScale = zoom * (rect.SizeX() / info.m_horizontalAccuracy); + bool const hasBearing = info.HasBearing(); if ((isNavigable && hasBearing) || (!isNavigable && hasBearing && info.HasSpeed() && info.m_speed > kMinSpeedThresholdMps)) @@ -351,6 +411,7 @@ void MyPositionController::OnLocationUpdate(location::GpsInfo const & info, bool } m_isPositionAssigned = true; + m_positionIsObsolete = false; SetIsVisible(true); double const kEps = 1e-5; @@ -405,8 +466,18 @@ bool MyPositionController::IsInStateWithPosition() const m_mode == location::FollowAndRotate; } -void MyPositionController::Render(uint32_t renderMode, ScreenBase const & screen, - ref_ptr mng, +bool MyPositionController::UpdateViewportWithAutoZoom() +{ + if (m_mode == location::FollowAndRotate && + m_isInRouting && m_enableAutoZoomInRouting && !m_needBlockAutoZoom) + { + ChangeModelView(m_autoScale, m_position, m_drawDirection, GetRoutingRotationPixelCenter()); + return true; + } + return false; +} + +void MyPositionController::Render(ScreenBase const & screen, ref_ptr mng, dp::UniformValuesStorage const & commonUniforms) { if (IsWaitingForLocation()) @@ -424,29 +495,41 @@ void MyPositionController::Render(uint32_t renderMode, ScreenBase const & screen if (m_shape != nullptr && IsVisible() && IsModeHasPosition()) { - if (m_isDirtyViewport && !m_needBlockAnimation) + if (m_needBlockAutoZoom && + m_routingNotAutoZoomTimer.ElapsedSeconds() >= kMaxNotAutoZoomRoutingTimeSec) { - UpdateViewport(kDoNotChangeZoom); + m_needBlockAutoZoom = false; + m_isDirtyAutoZoom = true; + } + + if (!m_positionIsObsolete && + m_updateLocationTimer.ElapsedSeconds() >= kMaxUpdateLocationInvervalSec) + { + m_positionIsObsolete = true; + m_autoScale = GetScale(kDefaultAutoZoom); + m_isDirtyAutoZoom = true; + } + + if ((m_isDirtyViewport || m_isDirtyAutoZoom) && !m_needBlockAnimation) + { + if (!UpdateViewportWithAutoZoom() && m_isDirtyViewport) + UpdateViewport(kDoNotChangeZoom); m_isDirtyViewport = false; + m_isDirtyAutoZoom = false; } if (!IsModeChangeViewport()) m_isPendingAnimation = false; + m_shape->SetPositionObsolete(m_positionIsObsolete); m_shape->SetPosition(GetDrawablePosition()); m_shape->SetAzimuth(GetDrawableAzimut()); m_shape->SetIsValidAzimuth(IsRotationAvailable()); m_shape->SetAccuracy(m_errorRadius); m_shape->SetRoutingMode(IsInRouting()); - double const updateInterval = m_updateLocationTimer.ElapsedSeconds(); - m_shape->SetPositionObsolete(updateInterval >= kMaxUpdateLocationInvervalSec); - - if ((renderMode & RenderAccuracy) != 0) - m_shape->RenderAccuracy(screen, mng, commonUniforms); - - if ((renderMode & RenderMyPosition) != 0) - m_shape->RenderMyPosition(screen, mng, commonUniforms); + m_shape->RenderAccuracy(screen, mng, commonUniforms); + m_shape->RenderMyPosition(screen, mng, commonUniforms); } } @@ -475,6 +558,9 @@ void MyPositionController::SetDirection(double bearing) void MyPositionController::ChangeMode(location::EMyPositionMode newMode) { + if (m_isInRouting && (m_mode != newMode) && (newMode == location::FollowAndRotate)) + ResetRoutingNotAutoZoomTimer(); + m_mode = newMode; if (m_modeChangeCallback != nullptr) m_modeChangeCallback(m_mode, m_isInRouting); @@ -558,6 +644,12 @@ void MyPositionController::ChangeModelView(m2::PointD const & userPos, double az m_listener->ChangeModelView(userPos, azimuth, pxZero, zoomLevel); } +void MyPositionController::ChangeModelView(double autoScale, m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero) +{ + if (m_listener) + m_listener->ChangeModelView(autoScale, userPos, azimuth, pxZero); +} + void MyPositionController::UpdateViewport(int zoomLevel) { if (IsWaitingForLocation()) @@ -648,12 +740,22 @@ void MyPositionController::CreateAnim(m2::PointD const & oldPos, double oldAzimu } } -void MyPositionController::ActivateRouting(int zoomLevel) +void MyPositionController::EnableAutoZoomInRouting(bool enableAutoZoom) +{ + if (m_isInRouting) + { + m_enableAutoZoomInRouting = enableAutoZoom; + ResetRoutingNotAutoZoomTimer(); + } +} + +void MyPositionController::ActivateRouting(int zoomLevel, bool enableAutoZoom) { if (!m_isInRouting) { m_isInRouting = true; m_routingNotFollowTimer.Reset(); + m_enableAutoZoomInRouting = enableAutoZoom; if (IsRotationAvailable()) { diff --git a/drape_frontend/my_position_controller.hpp b/drape_frontend/my_position_controller.hpp index f8516cdd88..9e17642737 100644 --- a/drape_frontend/my_position_controller.hpp +++ b/drape_frontend/my_position_controller.hpp @@ -34,13 +34,8 @@ public: /// Show map where "usePos" (mercator) placed in "pxZero" on screen and map rotated around "userPos" virtual void ChangeModelView(m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero, int zoomLevel) = 0; - }; - - // Render bits - enum RenderMode - { - RenderAccuracy = 0x1, - RenderMyPosition = 0x2 + virtual void ChangeModelView(double autoScale, + m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero) = 0; }; MyPositionController(location::EMyPositionMode initMode, double timeInBackground, @@ -67,6 +62,7 @@ public: void Rotated(); void ResetRoutingNotFollowTimer(); + void ResetRoutingNotAutoZoomTimer(); void CorrectScalePoint(m2::PointD & pt) const; void CorrectScalePoint(m2::PointD & pt1, m2::PointD & pt2) const; @@ -74,9 +70,11 @@ public: void SetRenderShape(drape_ptr && shape); - void ActivateRouting(int zoomLevel); + void ActivateRouting(int zoomLevel, bool enableAutoZoom); void DeactivateRouting(); + void EnableAutoZoomInRouting(bool enableAutoZoom); + void StopLocationFollow(); void NextMode(ScreenBase const & screen); void LoseLocation(); @@ -90,7 +88,7 @@ public: void SetModeListener(location::TMyPositionModeChanged const & fn); - void Render(uint32_t renderMode, ScreenBase const & screen, ref_ptr mng, + void Render(ScreenBase const & screen, ref_ptr mng, dp::UniformValuesStorage const & commonUniforms); bool IsRotationAvailable() const { return m_isDirectionAssigned; } @@ -115,8 +113,10 @@ private: void ChangeModelView(double azimuth); void ChangeModelView(m2::RectD const & rect); void ChangeModelView(m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero, int zoomLevel); + void ChangeModelView(double autoScale, m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero); void UpdateViewport(int zoomLevel); + bool UpdateViewportWithAutoZoom(); m2::PointD GetRotationPixelCenter() const; m2::PointD GetRoutingRotationPixelCenter() const; @@ -146,9 +146,13 @@ private: m2::PointD m_oldPosition; // position in mercator double m_oldDrawDirection; + bool m_enableAutoZoomInRouting; + double m_autoScale; + my::Timer m_lastGPSBearing; my::Timer m_pendingTimer; my::Timer m_routingNotFollowTimer; + my::Timer m_routingNotAutoZoomTimer; my::Timer m_updateLocationTimer; double m_lastLocationTimestamp; @@ -157,6 +161,7 @@ private: bool m_isVisible; bool m_isDirtyViewport; + bool m_isDirtyAutoZoom; bool m_isPendingAnimation; using TAnimationCreator = function; @@ -165,6 +170,9 @@ private: bool m_isPositionAssigned; bool m_isDirectionAssigned; + bool m_positionIsObsolete; + bool m_needBlockAutoZoom; + bool m_notFollowAfterPending; }; diff --git a/drape_frontend/screen_animations.cpp b/drape_frontend/screen_animations.cpp index 3f8dcb7824..b1d06014a9 100644 --- a/drape_frontend/screen_animations.cpp +++ b/drape_frontend/screen_animations.cpp @@ -85,7 +85,8 @@ drape_ptr GetPrettyFollowAnimation(ScreenBase const & startSc auto followAnim = make_unique_dp(tmp, userPos, endPixelPos, tmp.GetScale(), targetScale, - tmp.GetAngle(), targetAngle); + tmp.GetAngle(), targetAngle, + false /* isAutoZoom */); followAnim->SetMaxDuration(kMaxAnimationTimeSec * 0.5); sequenceAnim->AddAnimation(move(zoomOutAnim)); @@ -124,10 +125,11 @@ drape_ptr GetSetRectAnimation(ScreenBase const & screen, } drape_ptr GetFollowAnimation(ScreenBase const & startScreen, m2::PointD const & userPos, - double targetScale, double targetAngle, m2::PointD const & endPixelPos) + double targetScale, double targetAngle, m2::PointD const & endPixelPos, + bool isAutoZoom) { auto anim = make_unique_dp(startScreen, userPos, endPixelPos, startScreen.GetScale(), targetScale, - startScreen.GetAngle(), targetAngle); + startScreen.GetAngle(), targetAngle, isAutoZoom); anim->SetMaxDuration(kMaxAnimationTimeSec); return anim; diff --git a/drape_frontend/screen_animations.hpp b/drape_frontend/screen_animations.hpp index 9ccda398c1..ed81f3bd6f 100644 --- a/drape_frontend/screen_animations.hpp +++ b/drape_frontend/screen_animations.hpp @@ -29,7 +29,7 @@ drape_ptr GetSetRectAnimation(ScreenBase const & screen, m2::AnyRectD const & startRect, m2::AnyRectD const & endRect); drape_ptr GetFollowAnimation(ScreenBase const & startScreen, m2::PointD const & userPos, - double targetScale, double targetAngle, m2::PointD const & endPixelPos); + double targetScale, double targetAngle, m2::PointD const & endPixelPos, bool isAutoZoom); drape_ptr GetScaleAnimation(ScreenBase const & startScreen, m2::PointD pxScaleCenter, m2::PointD glbScaleCenter, double factor); diff --git a/drape_frontend/user_event_stream.cpp b/drape_frontend/user_event_stream.cpp index 83afe9c346..7b05856043 100644 --- a/drape_frontend/user_event_stream.cpp +++ b/drape_frontend/user_event_stream.cpp @@ -188,7 +188,7 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChanged, bool { m2::PointD pt = screen.PixelRectIn3d().Center(); breakAnim = SetFollowAndRotate(screen.PtoG(screen.P3dtoP(pt)), pt, - e.m_rotate.m_targetAzimut, kDoNotChangeZoom, true); + e.m_rotate.m_targetAzimut, kDoNotChangeZoom, -1.0, true, false); } else { @@ -201,7 +201,8 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChanged, bool case UserEvent::EVENT_FOLLOW_AND_ROTATE: breakAnim = SetFollowAndRotate(e.m_followAndRotate.m_userPos, e.m_followAndRotate.m_pixelZero, e.m_followAndRotate.m_azimuth, e.m_followAndRotate.m_preferredZoomLevel, - e.m_followAndRotate.m_isAnim); + e.m_followAndRotate.m_autoScale, + e.m_followAndRotate.m_isAnim, e.m_followAndRotate.m_isAutoScale); TouchCancel(m_touches); break; case UserEvent::EVENT_AUTO_PERSPECTIVE: @@ -267,7 +268,7 @@ bool UserEventStream::SetScale(m2::PointD const & pxScaleCenter, double factor, if (followAnim != nullptr && followAnim->HasScale()) { // Scaling is not possible if current follow animation does pixel offset. - if (followAnim->HasPixelOffset()) + if (followAnim->HasPixelOffset() && !followAnim->IsAutoZoom()) return false; // Reset follow animation with scaling if we apply scale explicitly. @@ -394,19 +395,20 @@ bool UserEventStream::InterruptFollowAnimations(bool force) } bool UserEventStream::SetFollowAndRotate(m2::PointD const & userPos, m2::PointD const & pixelPos, - double azimuth, int preferredZoomLevel, bool isAnim) + double azimuth, int preferredZoomLevel, double autoScale, + bool isAnim, bool isAutoScale) { ScreenBase const & currentScreen = GetCurrentScreen(); ScreenBase screen = currentScreen; - if (preferredZoomLevel == kDoNotChangeZoom) + if (preferredZoomLevel == kDoNotChangeZoom && !isAutoScale) { GetTargetScreen(screen); screen.SetAngle(-azimuth); } else { - screen.SetFromParams(userPos, -azimuth, GetScale(preferredZoomLevel)); + screen.SetFromParams(userPos, -azimuth, isAutoScale ? autoScale : GetScale(preferredZoomLevel)); } screen.MatchGandP3d(userPos, pixelPos); @@ -436,7 +438,7 @@ bool UserEventStream::SetFollowAndRotate(m2::PointD const & userPos, m2::PointD else { // Run follow-and-rotate animation. - anim = GetFollowAnimation(currentScreen, userPos, screen.GetScale(), -azimuth, pixelPos); + anim = GetFollowAnimation(currentScreen, userPos, screen.GetScale(), -azimuth, pixelPos, isAutoScale); } if (preferredZoomLevel != kDoNotChangeZoom) diff --git a/drape_frontend/user_event_stream.hpp b/drape_frontend/user_event_stream.hpp index c3ad87d6a2..edcacce40b 100644 --- a/drape_frontend/user_event_stream.hpp +++ b/drape_frontend/user_event_stream.hpp @@ -129,12 +129,26 @@ struct SetAnyRectEvent struct FollowAndRotateEvent { + FollowAndRotateEvent(m2::PointD const & userPos, m2::PointD const & pixelZero, + double azimuth, double autoScale) + : m_userPos(userPos) + , m_pixelZero(pixelZero) + , m_azimuth(azimuth) + , m_preferredZoomLevel(-1) + , m_autoScale(autoScale) + , m_isAutoScale(true) + , m_isAnim(true) + { + } + FollowAndRotateEvent(m2::PointD const & userPos, m2::PointD const & pixelZero, double azimuth, int preferredZoomLevel, bool isAnim) : m_userPos(userPos) , m_pixelZero(pixelZero) , m_azimuth(azimuth) , m_preferredZoomLevel(preferredZoomLevel) + , m_autoScale(-1.0) + , m_isAutoScale(false) , m_isAnim(isAnim) {} @@ -142,6 +156,8 @@ struct FollowAndRotateEvent m2::PointD m_pixelZero; double m_azimuth; int m_preferredZoomLevel; + double m_autoScale; + bool m_isAutoScale; bool m_isAnim; }; @@ -282,7 +298,8 @@ private: bool SetRect(m2::AnyRectD const & rect, bool isAnim); bool SetScreen(ScreenBase const & screen, bool isAnim); bool SetFollowAndRotate(m2::PointD const & userPos, m2::PointD const & pixelPos, - double azimuth, int preferredZoomLevel, bool isAnim); + double azimuth, int preferredZoomLevel, double autoScale, + bool isAnim, bool isAutoScale); void SetAutoPerspective(bool isAutoPerspective); m2::AnyRectD GetCurrentRect() const; diff --git a/map/framework.cpp b/map/framework.cpp index e93a57d3ba..30644554cd 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -108,6 +108,7 @@ char const kRouterTypeKey[] = "router"; char const kMapStyleKey[] = "MapStyleKeyV1"; char const kAllow3dKey[] = "Allow3d"; char const kAllow3dBuildingsKey[] = "Buildings3d"; +char const kAllowAutoZoom[] = "AutoZoom"; double const kDistEqualQuery = 100.0; @@ -2287,11 +2288,14 @@ void Framework::FollowRoute() if (!m_routingSession.EnableFollowMode()) return; - int const scale = (m_currentRouterType == RouterType::Pedestrian) ? scales::GetPedestrianNavigationScale() - : scales::GetNavigationScale(); - int const scale3d = (m_currentRouterType == RouterType::Pedestrian) ? scales::GetPedestrianNavigation3dScale() - : scales::GetNavigation3dScale(); - m_drapeEngine->FollowRoute(scale, scale3d); + bool const isPedestrianRoute = m_currentRouterType == RouterType::Pedestrian; + int const scale = isPedestrianRoute ? scales::GetPedestrianNavigationScale() + : scales::GetNavigationScale(); + int const scale3d = isPedestrianRoute ? scales::GetPedestrianNavigation3dScale() + : scales::GetNavigation3dScale(); + bool const enableAutoZoom = isPedestrianRoute ? false : LoadAutoZoom(); + + m_drapeEngine->FollowRoute(scale, scale3d, enableAutoZoom); m_drapeEngine->SetRoutePoint(m2::PointD(), true /* isStart */, false /* isValid */); } @@ -2552,6 +2556,23 @@ void Framework::Load3dMode(bool & allow3d, bool & allow3dBuildings) allow3dBuildings = true; } +bool Framework::LoadAutoZoom() +{ + bool allowAutoZoom = false; + settings::Get(kAllowAutoZoom, allowAutoZoom); + return allowAutoZoom; +} + +void Framework::AllowAutoZoom(bool allowAutoZoom) +{ + CallDrapeFunction(bind(&df::DrapeEngine::AllowAutoZoom, _1, allowAutoZoom)); +} + +void Framework::SaveAutoZoom(bool allowAutoZoom) +{ + settings::Set(kAllowAutoZoom, allowAutoZoom); +} + void Framework::EnableChoosePositionMode(bool enable, bool enableBounds, bool applyPosition, m2::PointD const & position) { if (m_drapeEngine != nullptr) diff --git a/map/framework.hpp b/map/framework.hpp index c411734a1b..4bdf0902e8 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -656,6 +656,10 @@ public: void Save3dMode(bool allow3d, bool allow3dBuildings); void Load3dMode(bool & allow3d, bool & allow3dBuildings); + bool LoadAutoZoom(); + void AllowAutoZoom(bool allowAutoZoom); + void SaveAutoZoom(bool allowAutoZoom); + public: /// @name Editor interface. //@{