diff --git a/anim/angle_interpolation.cpp b/anim/angle_interpolation.cpp index acb588cae6..0755df78d5 100644 --- a/anim/angle_interpolation.cpp +++ b/anim/angle_interpolation.cpp @@ -10,15 +10,17 @@ namespace anim double end, double speed, double & out) - : m_startAngle(start), - m_outAngle(out) + : m_outAngle(out) { - m_speed = speed; - m_startTime = 0; - m_dist = ang::GetShortestDistance(start, end); - m_curAngle = m_startAngle; - m_endAngle = m_startAngle + m_dist; - m_interval = fabs(m_dist) / (2 * math::pi) * m_speed; + CalcParams(start, end, speed); + } + + void AngleInterpolation::Reset(double start, double end, double speed) + { + CalcParams(start, end, speed); + m_startTime = GetController()->GetCurrentTime(); + SetState(EReady); + Start(); } void AngleInterpolation::OnStart(double ts) @@ -60,8 +62,16 @@ namespace anim void AngleInterpolation::SetEndAngle(double val) { m_startTime = GetController()->GetCurrentTime(); - m_startAngle = m_curAngle; - m_dist = ang::GetShortestDistance(m_startAngle, val); + CalcParams(m_curAngle, val, m_speed); + } + + void AngleInterpolation::CalcParams(double start, double end, double speed) + { + m_startAngle = start; + m_speed = speed; + m_startTime = 0; + m_dist = ang::GetShortestDistance(start, end); + m_curAngle = m_startAngle; m_endAngle = m_startAngle + m_dist; m_interval = fabs(m_dist) / (2 * math::pi) * m_speed; } diff --git a/anim/angle_interpolation.hpp b/anim/angle_interpolation.hpp index 063fee2266..0e580183f5 100644 --- a/anim/angle_interpolation.hpp +++ b/anim/angle_interpolation.hpp @@ -24,11 +24,16 @@ namespace anim double speed, double & out); + void Reset(double start, double end, double speed); + void OnStart(double ts); void OnStep(double ts); void OnEnd(double ts); double EndAngle() const; void SetEndAngle(double val); + + private: + void CalcParams(double start, double end, double speed); }; } diff --git a/anim/segment_interpolation.cpp b/anim/segment_interpolation.cpp index 2ab1d3a9bd..f0ddd57b81 100644 --- a/anim/segment_interpolation.cpp +++ b/anim/segment_interpolation.cpp @@ -1,5 +1,7 @@ #include "segment_interpolation.hpp" +#include "controller.hpp" + namespace anim { SegmentInterpolation::SegmentInterpolation(m2::PointD const & startPt, @@ -12,6 +14,17 @@ namespace anim m_interval(interval) {} + void SegmentInterpolation::Reset(m2::PointD const & start, m2::PointD const & end, double interval) + { + m_startPt = start; + m_outPt = start; + m_endPt = end; + m_interval = interval; + m_startTime = GetController()->GetCurrentTime(); + SetState(EReady); + Start(); + } + void SegmentInterpolation::OnStart(double ts) { m_startTime = ts; diff --git a/anim/segment_interpolation.hpp b/anim/segment_interpolation.hpp index 02e8db6164..b99c8c15ac 100644 --- a/anim/segment_interpolation.hpp +++ b/anim/segment_interpolation.hpp @@ -22,6 +22,8 @@ namespace anim double interval, m2::PointD & outPt); + void Reset(m2::PointD const & start, m2::PointD const & end, double interval); + void OnStart(double ts); void OnStep(double ts); void OnEnd(double ts); diff --git a/map/location_state.cpp b/map/location_state.cpp index 64e12b2912..1ea9a1e595 100644 --- a/map/location_state.cpp +++ b/map/location_state.cpp @@ -8,6 +8,8 @@ #include "../anim/controller.hpp" #include "../anim/task.hpp" +#include "../anim/angle_interpolation.hpp" +#include "../anim/segment_interpolation.hpp" #include "../gui/controller.hpp" @@ -19,13 +21,14 @@ #include "../geometry/rect2d.hpp" #include "../geometry/transformations.hpp" - namespace location { namespace { +static const int POSITION_Y_OFFSET = 120; + uint16_t IncludeModeBit(uint16_t mode, uint16_t bit) { return mode | bit; @@ -51,6 +54,102 @@ bool TestModeBit(uint16_t mode, uint16_t bit) return (mode & bit) != 0; } +class RotateAndFollowAnim : public anim::Task +{ +public: + RotateAndFollowAnim(Framework * fw, m2::PointD const & srcPos, double srcAngle) + : m_fw(fw) + , m_currentPosition(srcPos) + , m_currentAngle(srcAngle) + { + m2::RectD pixelRect = m_fw->GetNavigator().Screen().PixelRect(); + m2::PointD pxCenter = pixelRect.Center(); + m2::PointD dstPxBinging(pxCenter.x, pixelRect.maxY() - POSITION_Y_OFFSET * m_fw->GetVisualScale()); + anim::SegmentInterpolation * pxBindingAnim = new anim::SegmentInterpolation(m_fw->GetPixelCenter(), dstPxBinging, + 0.5, m_pxCurrentBinding); + + m_fw->GetAnimController()->AddTask(shared_ptr(pxBindingAnim)); + } + + void SetDestinationParams(m2::PointD const & dstPos, double dstAngle) + { + double posSpeed = m_fw->GetNavigator().ComputeMoveSpeed(m_currentPosition, dstPos); + double angleSpeed = m_fw->GetAnimator().GetRotationSpeed(); + + UpdateInnerAnim(m_currentPosition, dstPos, posSpeed, + m_currentAngle, dstAngle, angleSpeed); + } + + virtual void OnStart(double ts) + { + ASSERT(m_angleAnim != nullptr, ()); + ASSERT(m_posAnim != nullptr, ()); + if (m_angleAnim->IsReady()) + { + m_angleAnim->Start(); + m_angleAnim->OnStart(ts); + } + + if (m_posAnim->IsReady()) + { + m_posAnim->Start(); + m_posAnim->OnStart(ts); + } + } + + virtual void OnStep(double ts) + { + ASSERT(m_angleAnim != nullptr, ()); + ASSERT(m_posAnim != nullptr, ()); + + if (!m_angleAnim->IsEnded()) + m_angleAnim->OnStep(ts); + if (!m_posAnim->IsEnded()) + m_posAnim->OnStep(ts); + + UpdateViewport(); + } + +private: + void UpdateViewport() + { + m2::RectD const pixelRect = m_fw->GetNavigator().Screen().PixelRect(); + m2::PointD const pxCenter = pixelRect.Center(); + + double const glbLength = (m_fw->PtoG(pxCenter) - m_fw->PtoG(m_pxCurrentBinding)).Length(); + m2::PointD const newScreenCenter = m_currentPosition.Move(glbLength, m_currentAngle + my::DegToRad(90.0)); + + m_fw->SetViewportCenter(newScreenCenter); + m_fw->GetNavigator().SetAngle(m_currentAngle); + m_fw->Invalidate(); + } + + void UpdateInnerAnim(m2::PointD const & srcPos, m2::PointD const & dstPos, double posSpeed, + double srcAngle, double dstAngle, double angleSpeed) + { + if (m_angleAnim == nullptr) + m_angleAnim.reset(new anim::AngleInterpolation(srcAngle, dstAngle, angleSpeed, m_currentAngle)); + else + m_angleAnim->Reset(srcAngle, dstAngle, angleSpeed); + + if (m_posAnim == nullptr) + m_posAnim.reset(new anim::SegmentInterpolation(srcPos, dstPos, posSpeed, m_currentPosition)); + else + m_posAnim->Reset(srcPos, dstPos, posSpeed); + } + +private: + Framework * m_fw; + + shared_ptr m_angleAnim; + shared_ptr m_posAnim; + + m2::PointD m_currentPosition; + double m_currentAngle; + + m2::PointD m_pxCurrentBinding; +}; + } State::Params::Params() @@ -164,7 +263,7 @@ void State::OnLocationUpdate(location::GpsInfo const & info) if (GetMode() == PendingPosition) SetModeInfo(ChangeMode(m_modeInfo, Follow)); - else if (GetMode() > NotFollow) + else AnimateFollow(); CallPositionChangedListeners(m_position); @@ -180,7 +279,7 @@ void State::OnCompassUpdate(location::CompassInfo const & info) else m_drawDirection = info.m_magneticHeading; - FollowCompass(); + AnimateFollow(); invalidate(); } @@ -374,15 +473,34 @@ bool State::IsInRouting() const return TestModeBit(m_modeInfo, RoutingSessionBit); } -void State::FollowCompass() +bool State::FollowCompass() { - if (!IsRotationActive() || GetMode() != RotateAndFollow) - return; + if (!IsRotationActive() || GetMode() != RotateAndFollow || m_animTask == nullptr) + return false; - anim::Controller::Guard guard(m_framework->GetAnimController()); + RotateAndFollowAnim * task = static_cast(m_animTask.get()); + task->SetDestinationParams(Position(), -m_drawDirection); + return true; +} - m_framework->GetAnimator().RotateScreen(m_framework->GetNavigator().Screen().GetAngle(), - -m_drawDirection); +void State::CreateAnimTask() +{ + EndAnimation(); + + RotateAndFollowAnim * task = new RotateAndFollowAnim(m_framework, Position(), + m_framework->GetNavigator().Screen().GetAngle()); + + m_animTask.reset(task); + m_framework->GetAnimController()->AddTask(m_animTask); +} + +void State::EndAnimation() +{ + if (m_animTask != nullptr) + { + m_animTask->End(); + m_animTask.reset(); + } } void State::SetModeInfo(uint16_t modeInfo) @@ -510,32 +628,32 @@ void State::AnimateStateTransition(Mode oldMode, Mode newMode) { ASSERT(ValidateTransition(oldMode, newMode), ("from", oldMode, "to", newMode)); + if (oldMode == RotateAndFollow) + EndAnimation(); + if (oldMode == PendingPosition && newMode == Follow) { m2::PointD const size(m_errorRadius, m_errorRadius); m_framework->ShowRectExVisibleScale(m2::RectD(m_position - size, m_position + size), scales::GetUpperComfortScale()); } - else if (oldMode == NotFollow && newMode == Follow) - { - m_framework->SetViewportCenterAnimated(Position()); - } else if (newMode == RotateAndFollow) { - if (oldMode == NotFollow) - m_framework->SetViewportCenterAnimated(Position()); - FollowCompass(); + CreateAnimTask(); } else if (oldMode == RotateAndFollow && newMode == UnknownPosition) - { m_framework->GetAnimator().RotateScreen(m_framework->GetNavigator().Screen().GetAngle(), 0.0); - } + + AnimateFollow(); } void State::AnimateFollow() { - m_framework->SetViewportCenterAnimated(Position()); - FollowCompass(); + if (!IsModeChangeViewport()) + return; + + if (!FollowCompass()) + m_framework->SetViewportCenterAnimated(Position()); } } diff --git a/map/location_state.hpp b/map/location_state.hpp index fc63293b8a..adea91354b 100644 --- a/map/location_state.hpp +++ b/map/location_state.hpp @@ -13,6 +13,7 @@ class Framework; namespace graphics { class DisplayList; } +namespace anim { class Task;} namespace location { @@ -116,7 +117,6 @@ namespace location bool IsDirectionKnown() const; bool IsInRouting() const; - void FollowCompass(); void SetModeInfo(uint16_t modeInfo); private: @@ -142,12 +142,20 @@ namespace location TPositionListeners m_positionListeners; int m_currentSlotID; - /// @nameCompass Rendering Parameters + /// @name Compass Rendering Parameters //@{ unique_ptr m_positionArrow; unique_ptr m_locationMarkDL; unique_ptr m_positionMarkDL; graphics::Color m_locationAreaColor; //@} + + /// @name Rotation mode animation + //@{ + shared_ptr m_animTask; + bool FollowCompass(); + void CreateAnimTask(); + void EndAnimation(); + //@} }; } diff --git a/map/routing_session.cpp b/map/routing_session.cpp index b16855d136..124aba2c1f 100644 --- a/map/routing_session.cpp +++ b/map/routing_session.cpp @@ -30,10 +30,13 @@ void RoutingSession::RebuildRoute(m2::PointD const & startPoint, IRouter::ReadyC m_router->CalculateRoute(startPoint, [this, callback](Route const & route) { + if (route.GetPoly().GetSize() < 2) + return; + m_state = RouteNotStarted; m_route = route; callback(route); - }); + }); } bool RoutingSession::IsActive() const