diff --git a/drape_frontend/animation_system.cpp b/drape_frontend/animation_system.cpp index a2a74a2b48..7a6530d262 100644 --- a/drape_frontend/animation_system.cpp +++ b/drape_frontend/animation_system.cpp @@ -93,12 +93,16 @@ Interpolator::Interpolator(double duration, double delay) : m_elapsedTime(0.0) , m_duration(duration) , m_delay(delay) + , m_isActive(false) { ASSERT_GREATER_OR_EQUAL(m_duration, 0.0, ()); } bool Interpolator::IsFinished() const { + if (!IsActive()) + return true; + return m_elapsedTime > (m_duration + m_delay); } @@ -112,6 +116,16 @@ void Interpolator::Finish() m_elapsedTime = m_duration + m_delay + 1.0; } +bool Interpolator::IsActive() const +{ + return m_isActive; +} + +void Interpolator::SetActive(bool active) +{ + m_isActive = active; +} + void Interpolator::SetMaxDuration(double maxDuration) { m_duration = min(m_duration, maxDuration); @@ -140,6 +154,10 @@ double Interpolator::GetDuration() const return m_duration; } +PositionInterpolator::PositionInterpolator() + : PositionInterpolator(0.0 /* duration */, 0.0 /* delay */, m2::PointD(), m2::PointD()) +{} + PositionInterpolator::PositionInterpolator(double duration, double delay, m2::PointD const & startPosition, m2::PointD const & endPosition) @@ -162,7 +180,9 @@ PositionInterpolator::PositionInterpolator(double delay, m2::PointD const & star , m_startPosition(startPosition) , m_endPosition(endPosition) , m_position(startPosition) -{} +{ + SetActive(m_startPosition != m_endPosition); +} PositionInterpolator::PositionInterpolator(m2::PointD const & startPosition, m2::PointD const & endPosition, @@ -176,7 +196,9 @@ PositionInterpolator::PositionInterpolator(double delay, m2::PointD const & star , m_startPosition(startPosition) , m_endPosition(endPosition) , m_position(startPosition) -{} +{ + SetActive(m_startPosition != m_endPosition); +} //static double PositionInterpolator::GetMoveDuration(m2::PointD const & startPosition, @@ -221,6 +243,10 @@ void PositionInterpolator::Finish() m_position = m_endPosition; } +ScaleInterpolator::ScaleInterpolator() + : ScaleInterpolator(1.0 /* startScale */, 1.0 /* endScale */) +{} + ScaleInterpolator::ScaleInterpolator(double startScale, double endScale) : ScaleInterpolator(0.0 /* delay */, startScale, endScale) {} @@ -230,7 +256,9 @@ ScaleInterpolator::ScaleInterpolator(double delay, double startScale, double end , m_startScale(startScale) , m_endScale(endScale) , m_scale(startScale) -{} +{ + SetActive(m_startScale != m_endScale); +} // static double ScaleInterpolator::GetScaleDuration(double startScale, double endScale) @@ -256,6 +284,10 @@ void ScaleInterpolator::Finish() m_scale = m_endScale; } +AngleInterpolator::AngleInterpolator() + : AngleInterpolator(0.0 /* startAngle */, 0.0 /* endAngle */) +{} + AngleInterpolator::AngleInterpolator(double startAngle, double endAngle) : AngleInterpolator(0.0 /* delay */, startAngle, endAngle) {} @@ -265,14 +297,18 @@ AngleInterpolator::AngleInterpolator(double delay, double startAngle, double end , m_startAngle(ang::AngleIn2PI(startAngle)) , m_endAngle(ang::AngleIn2PI(endAngle)) , m_angle(m_startAngle) -{} +{ + SetActive(m_startAngle != m_endAngle); +} AngleInterpolator::AngleInterpolator(double delay, double duration, double startAngle, double endAngle) : Interpolator(duration, delay) , m_startAngle(ang::AngleIn2PI(startAngle)) , m_endAngle(ang::AngleIn2PI(endAngle)) , m_angle(m_startAngle) -{} +{ + SetActive(m_startAngle != m_endAngle); +} // static double AngleInterpolator::GetRotateDuration(double startAngle, double endAngle) @@ -299,11 +335,20 @@ MapLinearAnimation::MapLinearAnimation(m2::PointD const & startPos, m2::PointD c double startAngle, double endAngle, double startScale, double endScale, ScreenBase const & convertor) : Animation(true /* couldBeInterrupted */, false /* couldBeBlended */) + , m_angleInterpolator(startAngle, endAngle) + , m_positionInterpolator(startPos, endPos, convertor) + , m_scaleInterpolator(startScale, endScale) { m_objects.insert(Animation::MapPlane); - SetMove(startPos, endPos, convertor); - SetRotate(startAngle, endAngle); - SetScale(startScale, endScale); + + if (m_positionInterpolator.IsActive()) + m_properties.insert(Animation::Position); + + if (m_angleInterpolator.IsActive()) + m_properties.insert(Animation::Angle); + + if (m_scaleInterpolator.IsActive()) + m_properties.insert(Animation::Scale); } MapLinearAnimation::MapLinearAnimation() @@ -315,29 +360,23 @@ MapLinearAnimation::MapLinearAnimation() void MapLinearAnimation::SetMove(m2::PointD const & startPos, m2::PointD const & endPos, ScreenBase const & convertor) { - if (startPos != endPos) - { - m_positionInterpolator = make_unique_dp(startPos, endPos, convertor); + m_positionInterpolator = PositionInterpolator(startPos, endPos, convertor); + if (m_positionInterpolator.IsActive()) m_properties.insert(Animation::Position); - } } void MapLinearAnimation::SetRotate(double startAngle, double endAngle) { - if (startAngle != endAngle) - { - m_angleInterpolator = make_unique_dp(startAngle, endAngle); + m_angleInterpolator = AngleInterpolator(startAngle, endAngle); + if (m_angleInterpolator.IsActive()) m_properties.insert(Animation::Angle); - } } void MapLinearAnimation::SetScale(double startScale, double endScale) { - if (startScale != endScale) - { - m_scaleInterpolator = make_unique_dp(startScale, endScale); + m_scaleInterpolator = ScaleInterpolator(startScale, endScale); + if (m_scaleInterpolator.IsActive()) m_properties.insert(Animation::Scale); - } } Animation::TObjectProperties const & MapLinearAnimation::GetProperties(TObject object) const @@ -353,58 +392,63 @@ bool MapLinearAnimation::HasProperty(TObject object, TProperty property) const void MapLinearAnimation::Advance(double elapsedSeconds) { - if (m_angleInterpolator != nullptr) - m_angleInterpolator->Advance(elapsedSeconds); - if (m_scaleInterpolator != nullptr) - m_scaleInterpolator->Advance(elapsedSeconds); - if (m_positionInterpolator != nullptr) - m_positionInterpolator->Advance(elapsedSeconds); + if (m_angleInterpolator.IsActive()) + m_angleInterpolator.Advance(elapsedSeconds); + + if (m_scaleInterpolator.IsActive()) + m_scaleInterpolator.Advance(elapsedSeconds); + + if (m_positionInterpolator.IsActive()) + m_positionInterpolator.Advance(elapsedSeconds); } void MapLinearAnimation::Finish() { - if (m_angleInterpolator != nullptr) - m_angleInterpolator->Finish(); - if (m_scaleInterpolator != nullptr) - m_scaleInterpolator->Finish(); - if (m_positionInterpolator != nullptr) - m_positionInterpolator->Finish(); + if (m_angleInterpolator.IsActive()) + m_angleInterpolator.Finish(); + + if (m_scaleInterpolator.IsActive()) + m_scaleInterpolator.Finish(); + + if (m_positionInterpolator.IsActive()) + m_positionInterpolator.Finish(); + Animation::Finish(); } void MapLinearAnimation::SetMaxDuration(double maxDuration) { - if (m_angleInterpolator != nullptr) - m_angleInterpolator->SetMaxDuration(maxDuration); - if (m_scaleInterpolator != nullptr) - m_scaleInterpolator->SetMaxDuration(maxDuration); - if (m_positionInterpolator != nullptr) - m_positionInterpolator->SetMaxDuration(maxDuration); + if (m_angleInterpolator.IsActive()) + m_angleInterpolator.SetMaxDuration(maxDuration); + + if (m_positionInterpolator.IsActive()) + m_positionInterpolator.SetMaxDuration(maxDuration); + + SetMaxScaleDuration(maxDuration); } void MapLinearAnimation::SetMaxScaleDuration(double maxDuration) { - if (m_scaleInterpolator != nullptr) - m_scaleInterpolator->SetMaxDuration(maxDuration); + if (m_scaleInterpolator.IsActive()) + m_scaleInterpolator.SetMaxDuration(maxDuration); } double MapLinearAnimation::GetDuration() const { double duration = 0.0; - if (m_angleInterpolator != nullptr) - duration = m_angleInterpolator->GetDuration(); - if (m_scaleInterpolator != nullptr) - duration = max(duration, m_scaleInterpolator->GetDuration()); - if (m_positionInterpolator != nullptr) - duration = max(duration, m_positionInterpolator->GetDuration()); + if (m_angleInterpolator.IsActive()) + duration = m_angleInterpolator.GetDuration(); + if (m_scaleInterpolator.IsActive()) + duration = max(duration, m_scaleInterpolator.GetDuration()); + if (m_positionInterpolator.IsActive()) + duration = max(duration, m_positionInterpolator.GetDuration()); return duration; } bool MapLinearAnimation::IsFinished() const { - return ((m_angleInterpolator == nullptr || m_angleInterpolator->IsFinished()) && - (m_scaleInterpolator == nullptr || m_scaleInterpolator->IsFinished()) && - (m_positionInterpolator == nullptr || m_positionInterpolator->IsFinished())); + return m_angleInterpolator.IsFinished() && m_scaleInterpolator.IsFinished() && + m_positionInterpolator.IsFinished(); } bool MapLinearAnimation::GetProperty(TObject object, TProperty property, PropertyValue & value) const @@ -414,26 +458,23 @@ bool MapLinearAnimation::GetProperty(TObject object, TProperty property, Propert switch (property) { case Animation::Position: - ASSERT(m_positionInterpolator != nullptr, ()); - if (m_positionInterpolator != nullptr) + if (m_positionInterpolator.IsActive()) { - value = PropertyValue(m_positionInterpolator->GetPosition()); + value = PropertyValue(m_positionInterpolator.GetPosition()); return true; } return false; case Animation::Scale: - ASSERT(m_scaleInterpolator != nullptr, ()); - if (m_scaleInterpolator != nullptr) + if (m_scaleInterpolator.IsActive()) { - value = PropertyValue(m_scaleInterpolator->GetScale()); + value = PropertyValue(m_scaleInterpolator.GetScale()); return true; } return false; case Animation::Angle: - ASSERT(m_angleInterpolator != nullptr, ()); - if (m_angleInterpolator != nullptr) + if (m_angleInterpolator.IsActive()) { - value = PropertyValue(m_angleInterpolator->GetAngle()); + value = PropertyValue(m_angleInterpolator.GetAngle()); return true; } return false; @@ -626,6 +667,11 @@ bool MapFollowAnimation::GetProperty(TObject object, TProperty property, Propert return false; } +bool MapFollowAnimation::HasScale() const +{ + return m_scaleInterpolator.IsActive(); +} + PerspectiveSwitchAnimation::PerspectiveSwitchAnimation(double startAngle, double endAngle, double angleFOV) : Animation(false /* couldBeInterrupted */, true /* couldBeBlended */) , m_angleInterpolator(GetRotateDuration(startAngle, endAngle), startAngle, endAngle) @@ -902,10 +948,6 @@ void SequenceAnimation::ObtainObjectProperties() } } -AnimationSystem::AnimationSystem() -{ -} - bool AnimationSystem::GetRect(ScreenBase const & currentScreen, m2::AnyRectD & rect) { m_lastScreen = currentScreen; diff --git a/drape_frontend/animation_system.hpp b/drape_frontend/animation_system.hpp index e61162a166..754d580583 100644 --- a/drape_frontend/animation_system.hpp +++ b/drape_frontend/animation_system.hpp @@ -166,6 +166,8 @@ public: virtual void Advance(double elapsedSeconds); virtual void Finish(); + bool IsActive() const; + bool IsFinished() const; void SetMaxDuration(double maxDuration); void SetMinDuration(double minDuration); @@ -174,11 +176,13 @@ public: protected: double GetT() const; double GetElapsedTime() const; + void SetActive(bool active); private: double m_elapsedTime; double m_duration; double m_delay; + bool m_isActive; }; class PositionInterpolator: public Interpolator @@ -186,6 +190,7 @@ class PositionInterpolator: public Interpolator using TBase = Interpolator; public: + PositionInterpolator(); PositionInterpolator(double duration, double delay, m2::PointD const & startPosition, m2::PointD const & endPosition); @@ -211,7 +216,7 @@ public: void Advance(double elapsedSeconds) override; void Finish() override; - virtual m2::PointD GetPosition() const { return m_position; } + m2::PointD GetPosition() const { return m_position; } private: m2::PointD m_startPosition; @@ -224,6 +229,7 @@ class ScaleInterpolator: public Interpolator using TBase = Interpolator; public: + ScaleInterpolator(); ScaleInterpolator(double startScale, double endScale); ScaleInterpolator(double delay, double startScale, double endScale); @@ -233,11 +239,11 @@ public: void Advance(double elapsedSeconds) override; void Finish() override; - virtual double GetScale() const { return m_scale; } + double GetScale() const { return m_scale; } private: - double const m_startScale; - double const m_endScale; + double m_startScale; + double m_endScale; double m_scale; }; @@ -246,6 +252,7 @@ class AngleInterpolator: public Interpolator using TBase = Interpolator; public: + AngleInterpolator(); AngleInterpolator(double startAngle, double endAngle); AngleInterpolator(double delay, double startAngle, double endAngle); AngleInterpolator(double delay, double duration, double startAngle, double endAngle); @@ -256,11 +263,11 @@ public: void Advance(double elapsedSeconds) override; void Finish() override; - virtual double GetAngle() const { return m_angle; } + double GetAngle() const { return m_angle; } private: - double const m_startAngle; - double const m_endAngle; + double m_startAngle; + double m_endAngle; double m_angle; }; @@ -389,9 +396,9 @@ public: void SetMaxScaleDuration(double maxDuration); private: - drape_ptr m_angleInterpolator; - drape_ptr m_positionInterpolator; - drape_ptr m_scaleInterpolator; + AngleInterpolator m_angleInterpolator; + PositionInterpolator m_positionInterpolator; + ScaleInterpolator m_scaleInterpolator; TObjectProperties m_properties; TAnimObjects m_objects; }; @@ -474,6 +481,8 @@ public: bool GetProperty(TObject object, TProperty property, PropertyValue & value) const override; + bool HasScale() const; + private: double CalculateDuration() const; @@ -572,13 +581,29 @@ public: void FinishAnimations(Animation::Type type, bool rewind, bool finishAll); void FinishObjectAnimations(Animation::TObject object, bool rewind, bool finishAll); + template T const * FindAnimation(Animation::Type type) const + { + for (auto & animations : m_animationChain) + { + for (auto const & anim : animations) + { + if (anim->GetType() == type) + { + ASSERT(dynamic_cast(anim.get()) != nullptr, ()); + return static_cast(anim.get()); + } + } + } + return nullptr; + } + void Advance(double elapsedSeconds); ScreenBase const & GetLastScreen() { return m_lastScreen; } void SaveAnimationResult(Animation const & animation); private: - AnimationSystem(); + AnimationSystem() = default; bool GetProperty(Animation::TObject object, Animation::TProperty property, Animation::PropertyValue & value) const; diff --git a/drape_frontend/user_event_stream.cpp b/drape_frontend/user_event_stream.cpp index 4f0f9d6000..191fa6ac17 100644 --- a/drape_frontend/user_event_stream.cpp +++ b/drape_frontend/user_event_stream.cpp @@ -365,6 +365,12 @@ bool UserEventStream::SetScale(m2::PointD const & pxScaleCenter, double factor, auto anim = make_unique_dp(startScreen.GetScale(), endScreen.GetScale(), glbScaleCenter, offset); anim->SetMaxDuration(kMaxAnimationTimeSec); + + // Reset follow animation with scaling if we apply scale explicitly. + auto const & followAnim = m_animationSystem.FindAnimation(Animation::MapFollow); + if (followAnim != nullptr && followAnim->HasScale()) + ResetAnimations(Animation::MapFollow); + m_animationSystem.CombineAnimation(move(anim)); return false; } @@ -539,6 +545,16 @@ bool UserEventStream::SetFollowAndRotate(m2::PointD const & userPos, m2::PointD }; double const startScale = CalculateScale(screen, GetCurrentRect()); + + // Reset current follow-and-rotate animation if possible. + auto const & followAnim = m_animationSystem.FindAnimation(Animation::MapFollow); + if (followAnim != nullptr) + { + if (followAnim->CouldBeInterrupted()) + ResetAnimations(Animation::MapFollow); + else + return false; + } // Run pretty move animation if we are far from userPos. m2::PointD const startPt = GetCurrentRect().GlobalCenter();