diff --git a/drape_frontend/animation/model_view_animation.cpp b/drape_frontend/animation/model_view_animation.cpp index 905a2ea879..68124798c3 100644 --- a/drape_frontend/animation/model_view_animation.cpp +++ b/drape_frontend/animation/model_view_animation.cpp @@ -5,7 +5,7 @@ namespace df ModelViewAnimation::ModelViewAnimation(m2::AnyRectD const & startRect, m2::AnyRectD const & endRect, double aDuration, double mDuration, double sDuration) - : BaseInterpolator(max(max(aDuration, mDuration), sDuration)) + : BaseModelViewAnimation(max(max(aDuration, mDuration), sDuration)) , m_angleInterpolator(startRect.Angle().val(), endRect.Angle().val()) , m_startZero(startRect.GlobalZero()) , m_endZero(endRect.GlobalZero()) diff --git a/drape_frontend/animation/model_view_animation.hpp b/drape_frontend/animation/model_view_animation.hpp index f24b33b089..2419c7e33d 100644 --- a/drape_frontend/animation/model_view_animation.hpp +++ b/drape_frontend/animation/model_view_animation.hpp @@ -9,7 +9,16 @@ namespace df { -class ModelViewAnimation : public BaseInterpolator +class BaseModelViewAnimation : public BaseInterpolator +{ +public: + BaseModelViewAnimation(double duration, double delay = 0) : BaseInterpolator(duration, delay) {} + + virtual m2::AnyRectD GetCurrentRect() const = 0; + virtual m2::AnyRectD GetTargetRect() const = 0; +}; + +class ModelViewAnimation : public BaseModelViewAnimation { public: static double GetRotateDuration(double startAngle, double endAngle); @@ -21,8 +30,8 @@ public: /// sDuration - scaleDuration ModelViewAnimation(m2::AnyRectD const & startRect, m2::AnyRectD const & endRect, double aDuration, double mDuration, double sDuration); - m2::AnyRectD GetCurrentRect() const; - m2::AnyRectD GetTargetRect() const; + m2::AnyRectD GetCurrentRect() const override; + m2::AnyRectD GetTargetRect() const override; private: m2::AnyRectD GetRect(double elapsedTime) const; diff --git a/drape_frontend/drape_frontend.pro b/drape_frontend/drape_frontend.pro index 55357dcb76..70a1ef95cb 100755 --- a/drape_frontend/drape_frontend.pro +++ b/drape_frontend/drape_frontend.pro @@ -73,6 +73,7 @@ SOURCES += \ user_marks_provider.cpp \ viewport.cpp \ visual_params.cpp \ + kinetic_scroller.cpp \ HEADERS += \ animation/base_interpolator.hpp \ @@ -144,3 +145,4 @@ HEADERS += \ user_marks_provider.hpp \ viewport.hpp \ visual_params.hpp \ + kinetic_scroller.hpp \ diff --git a/drape_frontend/kinetic_scroller.cpp b/drape_frontend/kinetic_scroller.cpp new file mode 100644 index 0000000000..8cc1e86566 --- /dev/null +++ b/drape_frontend/kinetic_scroller.cpp @@ -0,0 +1,92 @@ +#include "kinetic_scroller.hpp" +#include "visual_params.hpp" +#include "base/logging.hpp" + +namespace df +{ + +class KineticScrollAnimation : public BaseModelViewAnimation +{ +public: + KineticScrollAnimation(m2::AnyRectD const & startRect, m2::PointD const & direction, double duration) + : BaseModelViewAnimation(duration) + , m_targetCenter(startRect.GlobalCenter() + direction) + , m_angle(startRect.Angle()) + , m_localRect(startRect.GetLocalRect()) + , m_direction(direction) + { + } + + m2::AnyRectD GetCurrentRect() const override + { + m2::PointD center = -m_direction * exp(-GetT()); + m2::AnyRectD rect(m_targetCenter + center, m_angle, m_localRect); + return rect; + } + + m2::AnyRectD GetTargetRect() const override + { + return GetCurrentRect(); + } + +private: + m2::PointD m_targetCenter; + ang::AngleD m_angle; + m2::RectD m_localRect; + m2::PointD m_direction; +}; + +KineticScroller::KineticScroller() + : m_lastTimestamp(-1.0) + , m_direction(m2::PointD::Zero()) +{ +} + +void KineticScroller::InitGrab(ScreenBase const & modelView, double timeStamp) +{ + ASSERT_LESS(m_lastTimestamp, 0.0, ()); + m_lastTimestamp = timeStamp; + m_lastRect = modelView.GlobalRect(); +} + +void KineticScroller::GrabViewRect(ScreenBase const & modelView, double timeStamp) +{ + ASSERT_GREATER(m_lastTimestamp, 0.0, ()); + ASSERT_GREATER(timeStamp, m_lastTimestamp, ()); + double elapsed = timeStamp - m_lastTimestamp; + + m2::PointD lastCenter = m_lastRect.GlobalCenter(); + m2::PointD currentCenter = modelView.GlobalRect().GlobalCenter(); + double pxDeltaLength = (modelView.GtoP(currentCenter) - modelView.GtoP(lastCenter)).Length(); + m2::PointD delta = (currentCenter - lastCenter); + if (!delta.IsAlmostZero()) + delta = delta.Normalize(); + + double v = pxDeltaLength / elapsed; + m_direction = delta * 0.8 * v + m_direction * 0.2; + + m_lastTimestamp = timeStamp; + m_lastRect = modelView.GlobalRect(); +} + +void KineticScroller::CancelGrab() +{ + m_lastTimestamp = -1; + m_direction = m2::PointD::Zero(); +} + +unique_ptr KineticScroller::CreateKineticAnimation(ScreenBase const & modelView) +{ + static double VELOCITY_THRESHOLD = 10.0 * VisualParams::Instance().GetVisualScale(); + if (m_direction.Length() < VELOCITY_THRESHOLD) + return unique_ptr(); + + double const KINETIC_DURATION = 0.375; + m2::PointD center = m_lastRect.GlobalCenter(); + double glbLength = 0.5 * (modelView.PtoG(modelView.GtoP(center) + m_direction) - center).Length(); + return unique_ptr(new KineticScrollAnimation(m_lastRect, + m_direction.Normalize() * glbLength, + KINETIC_DURATION)); +} + +} // namespace df diff --git a/drape_frontend/kinetic_scroller.hpp b/drape_frontend/kinetic_scroller.hpp new file mode 100644 index 0000000000..29edb08426 --- /dev/null +++ b/drape_frontend/kinetic_scroller.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "animation/model_view_animation.hpp" + +#include "geometry/any_rect2d.hpp" + +namespace df +{ + +class KineticScroller +{ +public: + KineticScroller(); + + void InitGrab(ScreenBase const & modelView, double timeStamp); + void GrabViewRect(ScreenBase const & modelView, double timeStamp); + void CancelGrab(); + unique_ptr CreateKineticAnimation(ScreenBase const & modelView); + +private: + double m_lastTimestamp; + m2::AnyRectD m_lastRect; + m2::PointD m_direction; +}; + +} // namespace df diff --git a/drape_frontend/user_event_stream.cpp b/drape_frontend/user_event_stream.cpp index 1959c228cc..bdc13426d1 100644 --- a/drape_frontend/user_event_stream.cpp +++ b/drape_frontend/user_event_stream.cpp @@ -233,15 +233,30 @@ bool UserEventStream::ProcessTouch(TouchEvent const & touch) { case TouchEvent::TOUCH_DOWN: isMapTouch |= TouchDown(touches); + if (isMapTouch) + m_scroller.InitGrab(m_navigator.Screen(), touch.m_timeStamp); break; case TouchEvent::TOUCH_MOVE: isMapTouch |= TouchMove(touches); + if (isMapTouch) + m_scroller.GrabViewRect(m_navigator.Screen(), touch.m_timeStamp); break; case TouchEvent::TOUCH_CANCEL: isMapTouch |= TouchCancel(touches); + if (isMapTouch) + m_scroller.CancelGrab(); break; case TouchEvent::TOUCH_UP: - isMapTouch |= TouchUp(touches); + { + isMapTouch |= TouchUp(touches); + if (isMapTouch) + { + m_scroller.GrabViewRect(m_navigator.Screen(), touch.m_timeStamp); + m_animation = m_scroller.CreateKineticAnimation(m_navigator.Screen()); + isMapTouch = false; + m_scroller.CancelGrab(); + } + } break; default: ASSERT(false, ()); diff --git a/drape_frontend/user_event_stream.hpp b/drape_frontend/user_event_stream.hpp index 08a579ed6e..1aecd4a277 100644 --- a/drape_frontend/user_event_stream.hpp +++ b/drape_frontend/user_event_stream.hpp @@ -1,5 +1,6 @@ #pragma once +#include "drape_frontend/kinetic_scroller.hpp" #include "drape_frontend/navigator.hpp" #include "drape_frontend/animation/model_view_animation.hpp" @@ -30,6 +31,7 @@ struct TouchEvent { TouchEvent() : m_type(TOUCH_CANCEL) + , m_timeStamp(my::Timer::LocalTime()) { } @@ -43,6 +45,7 @@ struct TouchEvent ETouchType m_type; array m_touches; + double m_timeStamp; // seconds }; struct ScaleEvent @@ -257,13 +260,15 @@ private: array m_touches; size_t m_validTouchesCount; - unique_ptr m_animation; + unique_ptr m_animation; ref_ptr m_listener; #ifdef DEBUG TTestBridge m_testFn; #endif m2::PointD m_startDragOrg; + + KineticScroller m_scroller; }; }