diff --git a/drape_frontend/animation/model_view_animation.cpp b/drape_frontend/animation/model_view_animation.cpp deleted file mode 100644 index 99c5dd94d6..0000000000 --- a/drape_frontend/animation/model_view_animation.cpp +++ /dev/null @@ -1,192 +0,0 @@ -#include "model_view_animation.hpp" - -namespace df -{ - -ModelViewAnimation::ModelViewAnimation(m2::AnyRectD const & startRect, m2::AnyRectD const & endRect, - double aDuration, double mDuration, double sDuration) - : BaseModelViewAnimation(max(max(aDuration, mDuration), sDuration)) - , m_angleInterpolator(startRect.Angle().val(), endRect.Angle().val()) - , m_startZero(startRect.GlobalZero()) - , m_endZero(endRect.GlobalZero()) - , m_startRect(startRect.GetLocalRect()) - , m_endRect(endRect.GetLocalRect()) - , m_angleDuration(aDuration) - , m_moveDuration(mDuration) - , m_scaleDuration(sDuration) -{ -} - -m2::AnyRectD ModelViewAnimation::GetCurrentRect(ScreenBase const & screen) const -{ - return GetRect(GetElapsedTime()); -} - -m2::AnyRectD ModelViewAnimation::GetTargetRect(ScreenBase const & screen) const -{ - return GetRect(GetDuration()); -} - -namespace -{ - -double GetSafeT(double elapsed, double duration) -{ - if (duration <= 0.0 || elapsed > duration) - return 1.0; - - return elapsed / duration; -} - -} // namespace - -m2::AnyRectD ModelViewAnimation::GetRect(double elapsedTime) const -{ - double dstAngle = m_angleInterpolator.Interpolate(GetSafeT(elapsedTime, m_angleDuration)); - m2::PointD dstZero = InterpolatePoint(m_startZero, m_endZero, GetSafeT(elapsedTime, m_moveDuration)); - m2::RectD dstRect = InterpolateRect(m_startRect, m_endRect, GetSafeT(elapsedTime, m_scaleDuration)); - - return m2::AnyRectD(dstZero, dstAngle, dstRect); -} - -double ModelViewAnimation::GetRotateDuration(double startAngle, double endAngle) -{ - double const kRotateDurationScalar = 0.75; - - return kRotateDurationScalar * fabs(ang::GetShortestDistance(startAngle, endAngle)) / math::pi; -} - -namespace -{ - -double CalcAnimSpeedDuration(double pxDiff, double pxSpeed) -{ - double const kEps = 1e-5; - - if (my::AlmostEqualAbs(pxDiff, 0.0, kEps)) - return 0.0; - - return fabs(pxDiff) / pxSpeed; -} - -} - -double ModelViewAnimation::GetMoveDuration(m2::PointD const & startPt, m2::PointD const & endPt, ScreenBase const & convertor) -{ - double const kMinMoveDuration = 0.2; - double const kMinSpeedScalar = 0.2; - double const kMaxSpeedScalar = 7.0; - double const kEps = 1e-5; - - m2::RectD const & dispPxRect = convertor.PixelRect(); - double const pixelLength = convertor.GtoP(endPt).Length(convertor.GtoP(startPt)); - if (pixelLength < kEps) - return 0.0; - - double const minSize = min(dispPxRect.SizeX(), dispPxRect.SizeY()); - if (pixelLength < kMinSpeedScalar * minSize) - return kMinMoveDuration; - - double const pixelSpeed = kMaxSpeedScalar * minSize; - return CalcAnimSpeedDuration(pixelLength, pixelSpeed); -} - -double ModelViewAnimation::GetScaleDuration(double startSize, double endSize) -{ - // Resize 2.0 times should be done for 0.3 seconds. - double constexpr kPixelSpeed = 2.0 / 0.3; - - if (startSize > endSize) - swap(startSize, endSize); - - return CalcAnimSpeedDuration(endSize / startSize, kPixelSpeed); -} - -ScaleAnimation::ScaleAnimation(m2::AnyRectD const & startRect, m2::AnyRectD const & endRect, - double aDuration, double mDuration, double sDuration, - m2::PointD const & globalPoint, m2::PointD const & pixelOffset) - : ModelViewAnimation(startRect, endRect, aDuration, mDuration, sDuration) - , m_globalPoint(globalPoint) - , m_pixelOffset(pixelOffset) -{ -} - -void ScaleAnimation::ApplyPixelOffset(ScreenBase const & screen, m2::AnyRectD & rect) const -{ - ScreenBase s = screen; - s.SetFromRect(rect); - - m2::PointD const pixelPoint = s.GtoP(m_globalPoint); - m2::PointD const newCenter = s.PtoG(pixelPoint + m_pixelOffset); - - rect = m2::AnyRectD(newCenter, rect.Angle(), rect.GetLocalRect()); -} - -m2::AnyRectD ScaleAnimation::GetCurrentRect(ScreenBase const & screen) const -{ - m2::AnyRectD r = GetRect(GetElapsedTime()); - ApplyPixelOffset(screen, r); - return r; -} - -m2::AnyRectD ScaleAnimation::GetTargetRect(ScreenBase const & screen) const -{ - m2::AnyRectD r = GetRect(GetDuration()); - ApplyPixelOffset(screen, r); - return r; -} - -FollowAndRotateAnimation::FollowAndRotateAnimation(m2::AnyRectD const & startRect, - m2::RectD const & targetLocalRect, - m2::PointD const & userPos, - m2::PointD const & startPixelPos, - m2::PointD const & endPixelPos, - double azimuth, double duration) - : BaseModelViewAnimation(duration) - , m_angleInterpolator(startRect.Angle().val(), -azimuth) - , m_rect(startRect.GetLocalRect()) - , m_target(targetLocalRect) - , m_userPos(userPos) - , m_startPixelPos(startPixelPos) - , m_endPixelPos(endPixelPos) -{} - -m2::AnyRectD FollowAndRotateAnimation::GetCurrentRect(ScreenBase const & screen) const -{ - return GetRect(screen, GetElapsedTime()); -} - -m2::AnyRectD FollowAndRotateAnimation::GetTargetRect(ScreenBase const & screen) const -{ - return GetRect(screen, GetDuration()); -} - -m2::PointD FollowAndRotateAnimation::CalculateCenter(ScreenBase const & screen, m2::PointD const & userPos, - m2::PointD const & pixelPos, double azimuth) -{ - return CalculateCenter(screen.GlobalRect().GetLocalRect(), screen.PixelRect(), userPos, pixelPos, azimuth); -} - -m2::PointD FollowAndRotateAnimation::CalculateCenter(m2::RectD const & localRect, m2::RectD const & pixelRect, - m2::PointD const & userPos, m2::PointD const & pixelPos, - double azimuth) -{ - double const scale = localRect.SizeX() / pixelRect.SizeX(); - m2::PointD formingVector = (pixelRect.Center() - pixelPos) * scale; - formingVector.y = -formingVector.y; - formingVector.Rotate(azimuth); - return userPos + formingVector; -} - -m2::AnyRectD FollowAndRotateAnimation::GetRect(ScreenBase const & screen, double elapsedTime) const -{ - double const t = GetSafeT(elapsedTime, GetDuration()); - double const azimuth = m_angleInterpolator.Interpolate(t); - m2::RectD const currentRect = InterpolateRect(m_rect, m_target, t); - m2::PointD const pixelPos = InterpolatePoint(m_startPixelPos, m_endPixelPos, t); - m2::PointD const centerPos = CalculateCenter(currentRect, screen.PixelRect(), m_userPos, pixelPos, azimuth); - - return m2::AnyRectD(centerPos, azimuth, currentRect); -} - -} // namespace df diff --git a/drape_frontend/animation/model_view_animation.hpp b/drape_frontend/animation/model_view_animation.hpp deleted file mode 100644 index 910a66bbd7..0000000000 --- a/drape_frontend/animation/model_view_animation.hpp +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "drape_frontend/animation/base_interpolator.hpp" -#include "drape_frontend/animation/interpolations.hpp" - -#include "geometry/any_rect2d.hpp" -#include "geometry/screenbase.hpp" - -namespace df -{ - -enum class ModelViewAnimationType -{ - Default, - Scale, - FollowAndRotate, - KineticScroll -}; - -class BaseModelViewAnimation : public BaseInterpolator -{ -public: - BaseModelViewAnimation(double duration, double delay = 0) : BaseInterpolator(duration, delay) {} - - virtual ModelViewAnimationType GetType() const = 0; - virtual m2::AnyRectD GetCurrentRect(ScreenBase const & screen) const = 0; - virtual m2::AnyRectD GetTargetRect(ScreenBase const & screen) const = 0; -}; - -class ModelViewAnimation : public BaseModelViewAnimation -{ -public: - static double GetRotateDuration(double startAngle, double endAngle); - static double GetMoveDuration(m2::PointD const & startPt, m2::PointD const & endPt, ScreenBase const & convertor); - static double GetScaleDuration(double startSize, double endSize); - - /// aDuration - angleDuration - /// mDuration - moveDuration - /// sDuration - scaleDuration - ModelViewAnimation(m2::AnyRectD const & startRect, m2::AnyRectD const & endRect, - double aDuration, double mDuration, double sDuration); - - ModelViewAnimationType GetType() const override { return ModelViewAnimationType::Default; } - m2::AnyRectD GetCurrentRect(ScreenBase const & screen) const override; - m2::AnyRectD GetTargetRect(ScreenBase const & screen) const override; - -protected: - m2::AnyRectD GetRect(double elapsedTime) const; - -private: - InerpolateAngle m_angleInterpolator; - m2::PointD m_startZero, m_endZero; - m2::RectD m_startRect, m_endRect; - - double m_angleDuration; - double m_moveDuration; - double m_scaleDuration; -}; - -class ScaleAnimation : public ModelViewAnimation -{ -public: - ScaleAnimation(m2::AnyRectD const & startRect, m2::AnyRectD const & endRect, - double aDuration, double mDuration, double sDuration, - m2::PointD const & globalPoint, m2::PointD const & pixelOffset); - - ModelViewAnimationType GetType() const override { return ModelViewAnimationType::Scale; } - m2::AnyRectD GetCurrentRect(ScreenBase const & screen) const override; - m2::AnyRectD GetTargetRect(ScreenBase const & screen) const override; - -private: - void ApplyPixelOffset(ScreenBase const & screen, m2::AnyRectD & rect) const; - m2::PointD m_globalPoint; - m2::PointD m_pixelOffset; -}; - -class FollowAndRotateAnimation : public BaseModelViewAnimation -{ -public: - FollowAndRotateAnimation(m2::AnyRectD const & startRect, - m2::RectD const & targetLocalRect, - m2::PointD const & userPos, - m2::PointD const & startPixelPos, - m2::PointD const & endPixelPos, - double azimuth, double duration); - - ModelViewAnimationType GetType() const override { return ModelViewAnimationType::FollowAndRotate; } - m2::AnyRectD GetCurrentRect(ScreenBase const & screen) const override; - m2::AnyRectD GetTargetRect(ScreenBase const & screen) const override; - - static m2::PointD CalculateCenter(ScreenBase const & screen, m2::PointD const & userPos, - m2::PointD const & pixelPos, double azimuth); - static m2::PointD CalculateCenter(m2::RectD const & localRect, m2::RectD const & pixelRect, - m2::PointD const & userPos, m2::PointD const & pixelPos, - double azimuth); -private: - m2::AnyRectD GetRect(ScreenBase const & screen, double elapsedTime) const; - - InerpolateAngle m_angleInterpolator; - m2::RectD m_rect; - m2::RectD m_target; - m2::PointD m_userPos; - m2::PointD m_startPixelPos; - m2::PointD m_endPixelPos; -}; - -} // namespace df diff --git a/drape_frontend/animation/perspective_animation.cpp b/drape_frontend/animation/perspective_animation.cpp deleted file mode 100644 index 7509035fa5..0000000000 --- a/drape_frontend/animation/perspective_animation.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "perspective_animation.hpp" -#include "drape_frontend/animation/interpolations.hpp" - -namespace df -{ - -// static -double PerspectiveAnimation::GetRotateDuration(double startAngle, double endAngle) -{ - return 0.5 * fabs(endAngle - startAngle) / math::pi4; -} - -PerspectiveAnimation::PerspectiveAnimation(double duration, double startRotationAngle, double endRotationAngle) - : PerspectiveAnimation(duration, 0.0 /* delay */, startRotationAngle, endRotationAngle) -{ -} - -PerspectiveAnimation::PerspectiveAnimation(double duration, double delay, double startRotationAngle, double endRotationAngle) - : BaseInterpolator(duration, delay) - , m_startRotationAngle(startRotationAngle) - , m_endRotationAngle(endRotationAngle) - , m_rotationAngle(startRotationAngle) -{ -} - -void PerspectiveAnimation::Advance(double elapsedSeconds) -{ - TBase::Advance(elapsedSeconds); - m_rotationAngle = InterpolateDouble(m_startRotationAngle, m_endRotationAngle, GetT()); -} - -} // namespace df diff --git a/drape_frontend/animation/perspective_animation.hpp b/drape_frontend/animation/perspective_animation.hpp deleted file mode 100644 index 7dc59b9fb9..0000000000 --- a/drape_frontend/animation/perspective_animation.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "drape_frontend/animation/base_interpolator.hpp" - -namespace df -{ - -class PerspectiveAnimation : public BaseInterpolator -{ - using TBase = BaseInterpolator; - -public: - PerspectiveAnimation(double duration, double startRotationAngle, double endRotationAngle); - PerspectiveAnimation(double duration, double delay, double startRotationAngle, double endRotationAngle); - - static double GetRotateDuration(double startAngle, double endAngle); - - void Advance(double elapsedSeconds) override; - double GetRotationAngle() const { return m_rotationAngle; } - -private: - double const m_startRotationAngle; - double const m_endRotationAngle; - double m_rotationAngle; -}; - -} // namespace df diff --git a/drape_frontend/animation_system.cpp b/drape_frontend/animation_system.cpp new file mode 100644 index 0000000000..3039d5314c --- /dev/null +++ b/drape_frontend/animation_system.cpp @@ -0,0 +1,1167 @@ +#include "animation_system.hpp" +#include "animation/interpolations.hpp" + +#include "base/logging.hpp" + +namespace df +{ + +namespace +{ + +double CalcAnimSpeedDuration(double pxDiff, double pxSpeed) +{ + double const kEps = 1e-5; + + if (my::AlmostEqualAbs(pxDiff, 0.0, kEps)) + return 0.0; + + return fabs(pxDiff) / pxSpeed; +} + +class PropertyBlender +{ +public: + PropertyBlender() = default; + + void Blend(Animation::PropertyValue const & value) + { + // Now perspective parameters can't be blended. + if (value.m_type == Animation::PropertyValue::ValuePerspectiveParams) + { + m_value = value; + m_counter = 1; + return; + } + + if (m_counter != 0) + { + // New value type resets current blended value. + if (m_value.m_type != value.m_type) + { + m_value = value; + m_counter = 1; + return; + } + + if (value.m_type == Animation::PropertyValue::ValueD) + m_value.m_valueD += value.m_valueD; + else if (value.m_type == Animation::PropertyValue::ValuePointD) + m_value.m_valuePointD += value.m_valuePointD; + } + else + { + m_value = value; + } + m_counter++; + } + + Animation::PropertyValue Finish() + { + if (m_counter == 0) + return m_value; + + double const scalar = 1.0 / m_counter; + m_counter = 0; + if (m_value.m_type == Animation::PropertyValue::ValueD) + m_value.m_valueD *= scalar; + else if (m_value.m_type == Animation::PropertyValue::ValuePointD) + m_value.m_valuePointD *= scalar; + + return m_value; + } + + bool IsEmpty() const + { + return m_counter == 0; + } + +private: + Animation::PropertyValue m_value; + uint32_t m_counter = 0; +}; + +} // namespace + +bool Animation::CouldBeBlendedWith(Animation const & animation) const +{ + return (GetType() != animation.GetType()) && + m_couldBeBlended && animation.m_couldBeBlended; +} + +Interpolator::Interpolator(double duration, double delay) + : m_elapsedTime(0.0) + , m_duration(duration) + , m_delay(delay) +{ + ASSERT_GREATER_OR_EQUAL(m_duration, 0.0, ()); +} + +bool Interpolator::IsFinished() const +{ + return m_elapsedTime > (m_duration + m_delay); +} + +void Interpolator::Advance(double elapsedSeconds) +{ + m_elapsedTime += elapsedSeconds; +} + +void Interpolator::Finish() +{ + m_elapsedTime = m_duration + m_delay + 1.0; +} + +void Interpolator::SetMaxDuration(double maxDuration) +{ + m_duration = min(m_duration, maxDuration); +} + +void Interpolator::SetMinDuration(double minDuration) +{ + m_duration = max(m_duration, minDuration); +} + +double Interpolator::GetT() const +{ + if (IsFinished()) + return 1.0; + + return max(m_elapsedTime - m_delay, 0.0) / m_duration; +} + +double Interpolator::GetElapsedTime() const +{ + return m_elapsedTime; +} + +double Interpolator::GetDuration() const +{ + return m_duration; +} + +PositionInterpolator::PositionInterpolator(double duration, double delay, + m2::PointD const & startPosition, + m2::PointD const & endPosition) + : Interpolator(duration, delay) + , m_startPosition(startPosition) + , m_endPosition(endPosition) + , m_position(startPosition) +{} + +PositionInterpolator::PositionInterpolator(m2::PointD const & startPosition, + m2::PointD const & endPosition, + ScreenBase const & convertor) + : PositionInterpolator(0.0 /* delay */, startPosition, endPosition, convertor) +{} + +PositionInterpolator::PositionInterpolator(double delay, m2::PointD const & startPosition, + m2::PointD const & endPosition, + ScreenBase const & convertor) + : Interpolator(PositionInterpolator::GetMoveDuration(startPosition, endPosition, convertor), delay) + , m_startPosition(startPosition) + , m_endPosition(endPosition) + , m_position(startPosition) +{} + +PositionInterpolator::PositionInterpolator(m2::PointD const & startPosition, + m2::PointD const & endPosition, + m2::RectD const & pixelRect) + : PositionInterpolator(0.0 /* delay */, startPosition, endPosition, pixelRect) +{} + +PositionInterpolator::PositionInterpolator(double delay, m2::PointD const & startPosition, + m2::PointD const & endPosition, m2::RectD const & pixelRect) + : Interpolator(PositionInterpolator::GetPixelMoveDuration(startPosition, endPosition, pixelRect), delay) + , m_startPosition(startPosition) + , m_endPosition(endPosition) + , m_position(startPosition) +{} + +//static +double PositionInterpolator::GetMoveDuration(m2::PointD const & startPosition, + m2::PointD const & endPosition, + ScreenBase const & convertor) +{ + return GetPixelMoveDuration(convertor.GtoP(startPosition), + convertor.GtoP(endPosition), + convertor.PixelRectIn3d()); +} + +double PositionInterpolator::GetPixelMoveDuration(m2::PointD const & startPosition, + m2::PointD const & endPosition, + m2::RectD const & pixelRect) +{ + 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); + if (pixelLength < kEps) + return 0.0; + + double const minSize = min(pixelRect.SizeX(), pixelRect.SizeY()); + if (pixelLength < kMinSpeedScalar * minSize) + return kMinMoveDuration; + + double const pixelSpeed = kMaxSpeedScalar * minSize; + return CalcAnimSpeedDuration(pixelLength, pixelSpeed); +} + +void PositionInterpolator::Advance(double elapsedSeconds) +{ + TBase::Advance(elapsedSeconds); + m_position = InterpolatePoint(m_startPosition, m_endPosition, GetT()); +} + +void PositionInterpolator::Finish() +{ + TBase::Finish(); + m_position = m_endPosition; +} + +ScaleInterpolator::ScaleInterpolator(double startScale, double endScale) + : ScaleInterpolator(0.0 /* delay */, startScale, endScale) +{} + +ScaleInterpolator::ScaleInterpolator(double delay, double startScale, double endScale) + : Interpolator(ScaleInterpolator::GetScaleDuration(startScale, endScale), delay) + , m_startScale(startScale) + , m_endScale(endScale) + , m_scale(startScale) +{} + +// static +double ScaleInterpolator::GetScaleDuration(double startScale, double endScale) +{ + // Resize 2.0 times should be done for 0.3 seconds. + double constexpr kPixelSpeed = 2.0 / 0.3; + + if (startScale > endScale) + swap(startScale, endScale); + + return CalcAnimSpeedDuration(endScale / startScale, kPixelSpeed); +} + +void ScaleInterpolator::Advance(double elapsedSeconds) +{ + TBase::Advance(elapsedSeconds); + m_scale = InterpolateDouble(m_startScale, m_endScale, GetT()); +} + +void ScaleInterpolator::Finish() +{ + TBase::Finish(); + m_scale = m_endScale; +} + +AngleInterpolator::AngleInterpolator(double startAngle, double endAngle) + : AngleInterpolator(0.0 /* delay */, startAngle, endAngle) +{} + +AngleInterpolator::AngleInterpolator(double delay, double startAngle, double endAngle) + : Interpolator(AngleInterpolator::GetRotateDuration(startAngle, endAngle), delay) + , m_startAngle(ang::AngleIn2PI(startAngle)) + , m_endAngle(ang::AngleIn2PI(endAngle)) + , m_angle(m_startAngle) +{} + +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) +{} + +// static +double AngleInterpolator::GetRotateDuration(double startAngle, double endAngle) +{ + double const kRotateDurationScalar = 0.75; + startAngle = ang::AngleIn2PI(startAngle); + endAngle = ang::AngleIn2PI(endAngle); + return kRotateDurationScalar * fabs(ang::GetShortestDistance(startAngle, endAngle)) / math::pi; +} + +void AngleInterpolator::Advance(double elapsedSeconds) +{ + TBase::Advance(elapsedSeconds); + m_angle = m_startAngle + ang::GetShortestDistance(m_startAngle, m_endAngle) * GetT(); +} + +void AngleInterpolator::Finish() +{ + TBase::Finish(); + m_angle = m_endAngle; +} + +MapLinearAnimation::MapLinearAnimation(m2::PointD const & startPos, m2::PointD const & endPos, + double startAngle, double endAngle, + double startScale, double endScale, ScreenBase const & convertor) + : Animation(true /* couldBeInterrupted */, false /* couldBeBlended */) +{ + m_objects.insert(Animation::MapPlane); + SetMove(startPos, endPos, convertor); + SetRotate(startAngle, endAngle); + SetScale(startScale, endScale); +} + +MapLinearAnimation::MapLinearAnimation() + : Animation(true /* couldBeInterrupted */, false /* couldBeBlended */) +{ + m_objects.insert(Animation::MapPlane); +} + +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_properties.insert(Animation::Position); + } +} + +void MapLinearAnimation::SetRotate(double startAngle, double endAngle) +{ + if (startAngle != endAngle) + { + m_angleInterpolator = make_unique_dp(startAngle, endAngle); + m_properties.insert(Animation::Angle); + } +} + +void MapLinearAnimation::SetScale(double startScale, double endScale) +{ + if (startScale != endScale) + { + m_scaleInterpolator = make_unique_dp(startScale, endScale); + m_properties.insert(Animation::Scale); + } +} + +Animation::TObjectProperties const & MapLinearAnimation::GetProperties(TObject object) const +{ + ASSERT_EQUAL(object, Animation::MapPlane, ()); + return m_properties; +} + +bool MapLinearAnimation::HasProperty(TObject object, TProperty property) const +{ + return HasObject(object) && m_properties.find(property) != m_properties.end(); +} + +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); +} + +void MapLinearAnimation::Finish() +{ + if (m_angleInterpolator != nullptr) + m_angleInterpolator->Finish(); + if (m_scaleInterpolator != nullptr) + m_scaleInterpolator->Finish(); + if (m_positionInterpolator != nullptr) + 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); +} + +void MapLinearAnimation::SetMaxScaleDuration(double maxDuration) +{ + if (m_scaleInterpolator != nullptr) + 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()); + 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())); +} + +bool MapLinearAnimation::GetProperty(TObject object, TProperty property, PropertyValue & value) const +{ + ASSERT_EQUAL(object, Animation::MapPlane, ()); + + switch (property) + { + case Animation::Position: + ASSERT(m_positionInterpolator != nullptr, ()); + if (m_positionInterpolator != nullptr) + { + value = PropertyValue(m_positionInterpolator->GetPosition()); + return true; + } + return false; + case Animation::Scale: + ASSERT(m_scaleInterpolator != nullptr, ()); + if (m_scaleInterpolator != nullptr) + { + value = PropertyValue(m_scaleInterpolator->GetScale()); + return true; + } + return false; + case Animation::Angle: + ASSERT(m_angleInterpolator != nullptr, ()); + if (m_angleInterpolator != nullptr) + { + value = PropertyValue(m_angleInterpolator->GetAngle()); + return true; + } + return false; + default: + ASSERT(false, ("Wrong property:", property)); + } + + return false; +} + +MapScaleAnimation::MapScaleAnimation(double startScale, double endScale, + m2::PointD const & globalPosition, m2::PointD const & offset) + : Animation(true /* couldBeInterrupted */, true /* couldBeBlended */) + , m_scaleInterpolator(startScale, endScale) + , m_pixelOffset(offset) + , m_globalPosition(globalPosition) +{ + m_objects.insert(Animation::MapPlane); + m_properties.insert(Animation::Scale); + m_properties.insert(Animation::Position); +} + +Animation::TObjectProperties const & MapScaleAnimation::GetProperties(TObject object) const +{ + ASSERT_EQUAL(object, Animation::MapPlane, ()); + return m_properties; +} + +bool MapScaleAnimation::HasProperty(TObject object, TProperty property) const +{ + return HasObject(object) && m_properties.find(property) != m_properties.end(); +} + +void MapScaleAnimation::Advance(double elapsedSeconds) +{ + m_scaleInterpolator.Advance(elapsedSeconds); +} + +void MapScaleAnimation::Finish() +{ + m_scaleInterpolator.Finish(); + Animation::Finish(); +} + +void MapScaleAnimation::SetMaxDuration(double maxDuration) +{ + m_scaleInterpolator.SetMaxDuration(maxDuration); +} + +double MapScaleAnimation::GetDuration() const +{ + return m_scaleInterpolator.GetDuration(); +} + +bool MapScaleAnimation::IsFinished() const +{ + return m_scaleInterpolator.IsFinished(); +} + +bool MapScaleAnimation::GetProperty(TObject object, TProperty property, PropertyValue & value) const +{ + if (property == Animation::Position) + { + ScreenBase screen = AnimationSystem::Instance().GetLastScreen(); + screen.SetScale(m_scaleInterpolator.GetScale()); + value = PropertyValue(screen.PtoG(screen.GtoP(m_globalPosition) + m_pixelOffset)); + return true; + } + if (property == Animation::Scale) + { + value = PropertyValue(m_scaleInterpolator.GetScale()); + return true; + } + ASSERT(false, ("Wrong property:", property)); + return false; +} + +MapFollowAnimation::MapFollowAnimation(m2::PointD const & globalPosition, + double startScale, double endScale, + double startAngle, double endAngle, + m2::PointD const & startPixelPosition, + m2::PointD const & endPixelPosition, + m2::RectD const & pixelRect) + : Animation(true /* couldBeInterrupted */, true /* couldBeBlended */) + , m_scaleInterpolator(startScale, endScale) + , m_pixelPosInterpolator(startPixelPosition, endPixelPosition, pixelRect) + , m_angleInterpolator(startAngle, endAngle) + , m_globalPosition(globalPosition) +{ + double const duration = CalculateDuration(); + m_scaleInterpolator.SetMinDuration(duration); + m_angleInterpolator.SetMinDuration(duration); + m_pixelPosInterpolator.SetMinDuration(duration); + + m_objects.insert(Animation::MapPlane); + m_properties.insert(Animation::Scale); + m_properties.insert(Animation::Angle); + m_properties.insert(Animation::Position); +} + +Animation::TObjectProperties const & MapFollowAnimation::GetProperties(TObject object) const +{ + ASSERT_EQUAL(object, Animation::MapPlane, ()); + return m_properties; +} + +bool MapFollowAnimation::HasProperty(TObject object, TProperty property) const +{ + return HasObject(object) && m_properties.find(property) != m_properties.end(); +} + +void MapFollowAnimation::Advance(double elapsedSeconds) +{ + m_angleInterpolator.Advance(elapsedSeconds); + m_scaleInterpolator.Advance(elapsedSeconds); + m_pixelPosInterpolator.Advance(elapsedSeconds); +} + +void MapFollowAnimation::Finish() +{ + m_angleInterpolator.Finish(); + m_scaleInterpolator.Finish(); + m_pixelPosInterpolator.Finish(); + Animation::Finish(); +} + +void MapFollowAnimation::SetMaxDuration(double maxDuration) +{ + m_angleInterpolator.SetMaxDuration(maxDuration); + m_scaleInterpolator.SetMaxDuration(maxDuration); + m_pixelPosInterpolator.SetMaxDuration(maxDuration); +} + +double MapFollowAnimation::GetDuration() const +{ + return CalculateDuration(); +} + +double MapFollowAnimation::CalculateDuration() const +{ + return max(max(m_angleInterpolator.GetDuration(), + m_angleInterpolator.GetDuration()), m_scaleInterpolator.GetDuration()); +} + +bool MapFollowAnimation::IsFinished() const +{ + return m_pixelPosInterpolator.IsFinished() && m_angleInterpolator.IsFinished() && + m_scaleInterpolator.IsFinished(); +} + +// static +m2::PointD MapFollowAnimation::CalculateCenter(ScreenBase const & screen, m2::PointD const & userPos, + m2::PointD const & pixelPos, double azimuth) +{ + double const scale = screen.GlobalRect().GetLocalRect().SizeX() / screen.PixelRect().SizeX(); + return CalculateCenter(scale, screen.PixelRect(), userPos, pixelPos, azimuth); +} + +// static +m2::PointD MapFollowAnimation::CalculateCenter(double scale, m2::RectD const & pixelRect, + m2::PointD const & userPos, m2::PointD const & pixelPos, + double azimuth) +{ + m2::PointD formingVector = (pixelRect.Center() - pixelPos) * scale; + formingVector.y = -formingVector.y; + formingVector.Rotate(azimuth); + return userPos + formingVector; +} + +bool MapFollowAnimation::GetProperty(TObject object, TProperty property, PropertyValue & value) const +{ + if (property == Animation::Position) + { + m2::RectD const pixelRect = AnimationSystem::Instance().GetLastScreen().PixelRect(); + value = PropertyValue(CalculateCenter(m_scaleInterpolator.GetScale(), pixelRect, m_globalPosition, + m_pixelPosInterpolator.GetPosition(), m_angleInterpolator.GetAngle())); + return true; + } + if (property == Animation::Angle) + { + value = PropertyValue(m_angleInterpolator.GetAngle()); + return true; + } + if (property == Animation::Scale) + { + value = PropertyValue(m_scaleInterpolator.GetScale()); + return true; + } + ASSERT(false, ("Wrong property:", property)); + return false; +} + +PerspectiveSwitchAnimation::PerspectiveSwitchAnimation(double startAngle, double endAngle, double angleFOV) + : Animation(false /* couldBeInterrupted */, true /* couldBeBlended */) + , m_angleInterpolator(GetRotateDuration(startAngle, endAngle), startAngle, endAngle) + , m_startAngle(startAngle) + , m_endAngle(endAngle) + , m_angleFOV(angleFOV) + , m_isEnablePerspectiveAnim(m_endAngle > 0.0) + , m_needPerspectiveSwitch(false) +{ + m_objects.insert(Animation::MapPlane); + m_properties.insert(Animation::AnglePerspective); + m_properties.insert(Animation::SwitchPerspective); +} + +// static +double PerspectiveSwitchAnimation::GetRotateDuration(double startAngle, double endAngle) +{ + double const kScalar = 0.5; + return kScalar * fabs(endAngle - startAngle) / math::pi4; +} + +Animation::TObjectProperties const & PerspectiveSwitchAnimation::GetProperties(TObject object) const +{ + ASSERT_EQUAL(object, Animation::MapPlane, ()); + return m_properties; +} + +bool PerspectiveSwitchAnimation::HasProperty(TObject object, TProperty property) const +{ + return HasObject(object) && m_properties.find(property) != m_properties.end(); +} + +void PerspectiveSwitchAnimation::Advance(double elapsedSeconds) +{ + m_angleInterpolator.Advance(elapsedSeconds); +} + +void PerspectiveSwitchAnimation::Finish() +{ + m_angleInterpolator.Finish(); + Animation::Finish(); +} + +void PerspectiveSwitchAnimation::OnStart() +{ + if (m_isEnablePerspectiveAnim) + m_needPerspectiveSwitch = true; + Animation::OnStart(); +} + +void PerspectiveSwitchAnimation::OnFinish() +{ + if (!m_isEnablePerspectiveAnim) + m_needPerspectiveSwitch = true; + Animation::OnFinish(); +} + +void PerspectiveSwitchAnimation::SetMaxDuration(double maxDuration) +{ + m_angleInterpolator.SetMaxDuration(maxDuration); +} + +double PerspectiveSwitchAnimation::GetDuration() const +{ + return m_angleInterpolator.GetDuration(); +} + +bool PerspectiveSwitchAnimation::IsFinished() const +{ + return m_angleInterpolator.IsFinished(); +} + +bool PerspectiveSwitchAnimation::GetProperty(TObject object, TProperty property, PropertyValue & value) const +{ + ASSERT_EQUAL(object, Animation::MapPlane, ()); + + switch (property) + { + case Animation::AnglePerspective: + value = PropertyValue(m_angleInterpolator.GetAngle()); + return true; + case Animation::SwitchPerspective: + if (m_needPerspectiveSwitch) + { + m_needPerspectiveSwitch = false; + value = PropertyValue(SwitchPerspectiveParams(m_isEnablePerspectiveAnim, m_startAngle, m_endAngle, m_angleFOV)); + return true; + } + return false; + default: + ASSERT(false, ("Wrong property:", property)); + } + + return false; +} + +ParallelAnimation::ParallelAnimation() + : Animation(true /* couldBeInterrupted */, true /* couldBeBlended */) +{} + +Animation::TObjectProperties const & ParallelAnimation::GetProperties(TObject object) const +{ + ASSERT(HasObject(object), ()); + return m_properties.find(object)->second; +} + +bool ParallelAnimation::HasProperty(TObject object, TProperty property) const +{ + if (!HasObject(object)) + return false; + TObjectProperties properties = GetProperties(object); + return properties.find(property) != properties.end(); +} + +void ParallelAnimation::AddAnimation(drape_ptr animation) +{ + TAnimObjects const & objects = animation->GetObjects(); + m_objects.insert(objects.begin(), objects.end()); + for (auto const & object : objects) + { + TObjectProperties const & properties = animation->GetProperties(object); + m_properties[object].insert(properties.begin(), properties.end()); + } + m_animations.push_back(move(animation)); +} + +void ParallelAnimation::OnStart() +{ + for (auto & anim : m_animations) + anim->OnStart(); +} + +void ParallelAnimation::OnFinish() +{ + for (auto & anim : m_animations) + anim->OnFinish(); +} + +void ParallelAnimation::Advance(double elapsedSeconds) +{ + auto iter = m_animations.begin(); + while (iter != m_animations.end()) + { + (*iter)->Advance(elapsedSeconds); + if ((*iter)->IsFinished()) + { + (*iter)->OnFinish(); + iter = m_animations.erase(iter); + } + else + { + ++iter; + } + } +} + +void ParallelAnimation::Finish() +{ + for (auto & anim : m_animations) + anim->Finish(); + Animation::Finish(); +} + +SequenceAnimation::SequenceAnimation() + : Animation(true /* couldBeInterrupted */, true /* couldBeBlended */) +{ +} + +Animation::TAnimObjects const & SequenceAnimation::GetObjects() const +{ + return m_objects; +} + +bool SequenceAnimation::HasObject(TObject object) const +{ + ASSERT(!m_animations.empty(), ()); + return m_animations.front()->HasObject(object); +} + +Animation::TObjectProperties const & SequenceAnimation::GetProperties(TObject object) const +{ + ASSERT(HasObject(object), ()); + return m_properties.find(object)->second; +} + +bool SequenceAnimation::HasProperty(TObject object, TProperty property) const +{ + ASSERT(!m_animations.empty(), ()); + return m_animations.front()->HasProperty(object, property); +} + +void SequenceAnimation::SetMaxDuration(double maxDuration) +{ + ASSERT(false, ("Not implemented")); +} + +double SequenceAnimation::GetDuration() const +{ + double duration = 0.0; + for (auto const & anim : m_animations) + duration += anim->GetDuration(); + return duration; +} + +bool SequenceAnimation::IsFinished() const +{ + return m_animations.empty(); +} + +bool SequenceAnimation::GetProperty(TObject object, TProperty property, PropertyValue & value) const +{ + ASSERT(!m_animations.empty(), ()); + return m_animations.front()->GetProperty(object, property, value); +} + +void SequenceAnimation::AddAnimation(drape_ptr animation) +{ + m_animations.push_back(move(animation)); + if (m_animations.size() == 1) + ObtainObjectProperties(); +} + +void SequenceAnimation::OnStart() +{ + if (m_animations.empty()) + return; + m_animations.front()->OnStart(); + Animation::OnStart(); +} + +void SequenceAnimation::OnFinish() +{ + Animation::OnFinish(); +} + +void SequenceAnimation::Advance(double elapsedSeconds) +{ + if (m_animations.empty()) + return; + m_animations.front()->Advance(elapsedSeconds); + if (m_animations.front()->IsFinished()) + { + m_animations.front()->OnFinish(); + AnimationSystem::Instance().SaveAnimationResult(*m_animations.front()); + m_animations.pop_front(); + ObtainObjectProperties(); + } +} + +void SequenceAnimation::Finish() +{ + for (auto & anim : m_animations) + anim->Finish(); + AnimationSystem::Instance().SaveAnimationResult(*m_animations.back()); + m_animations.clear(); + ObtainObjectProperties(); + Animation::Finish(); +} + +void SequenceAnimation::ObtainObjectProperties() +{ + m_objects.clear(); + m_properties.clear(); + + if (m_animations.empty()) + return; + + TAnimObjects const & objects = m_animations.front()->GetObjects(); + m_objects.insert(objects.begin(), objects.end()); + for (auto const & object : objects) + { + TObjectProperties const & properties = m_animations.front()->GetProperties(object); + m_properties[object].insert(properties.begin(), properties.end()); + } +} + +AnimationSystem::AnimationSystem() +{ +} + +bool AnimationSystem::GetRect(ScreenBase const & currentScreen, m2::AnyRectD & rect) +{ + m_lastScreen = currentScreen; + + double scale = currentScreen.GetScale(); + double angle = currentScreen.GetAngle(); + m2::PointD pos = currentScreen.GlobalRect().GlobalZero(); + + Animation::PropertyValue value; + if (GetProperty(Animation::MapPlane, Animation::Scale, value)) + scale = value.m_valueD; + + if (GetProperty(Animation::MapPlane, Animation::Angle, value)) + angle = value.m_valueD; + + if (GetProperty(Animation::MapPlane, Animation::Position, value)) + pos = value.m_valuePointD; + + m2::RectD localRect = currentScreen.PixelRect(); + localRect.Offset(-localRect.Center()); + localRect.Scale(scale); + rect = m2::AnyRectD(pos, angle, localRect); + + return true; +} + +bool AnimationSystem::GetPerspectiveAngle(double & angle) +{ + Animation::PropertyValue value; + if (GetProperty(Animation::MapPlane, Animation::AnglePerspective, value)) + { + angle = value.m_valueD; + return true; + } + return false; +} + +bool AnimationSystem::SwitchPerspective(Animation::SwitchPerspectiveParams & params) +{ + Animation::PropertyValue value; + if (GetProperty(Animation::MapPlane, Animation::SwitchPerspective, value)) + { + params = value.m_valuePerspectiveParams; + return true; + } + return false; +} + +bool AnimationSystem::AnimationExists(Animation::TObject object) const +{ + if (!m_animationChain.empty()) + { + for (auto const & anim : m_animationChain.front()) + { + if (anim->HasObject(object)) + return true; + } + } + for (auto const & prop : m_propertyCache) + { + if (prop.first.first == object) + return true; + } + return false; +} + +bool AnimationSystem::HasAnimations() const +{ + return !m_animationChain.empty(); +} + +AnimationSystem & AnimationSystem::Instance() +{ + static AnimationSystem animSystem; + return animSystem; +} + +void AnimationSystem::CombineAnimation(drape_ptr animation) +{ + for (auto & lst : m_animationChain) + { + bool couldBeBlended = true; + for (auto it = lst.begin(); it != lst.end();) + { + auto & anim = *it; + if (!anim->CouldBeBlendedWith(*animation)) + { + if (!anim->CouldBeInterrupted()) + { + couldBeBlended = false; + break; + } + anim->Interrupt(); + SaveAnimationResult(*anim); + it = lst.erase(it); + } + else + { + ++it; + } + } + if (couldBeBlended) + { + animation->OnStart(); + lst.emplace_back(move(animation)); + return; + } + } + PushAnimation(move(animation)); +} + +void AnimationSystem::PushAnimation(drape_ptr animation) +{ + if (m_animationChain.empty()) + animation->OnStart(); + + TAnimationList list; + list.emplace_back(move(animation)); + + m_animationChain.emplace_back(move(list)); +} + +void AnimationSystem::FinishAnimations(Animation::Type type, bool rewind) +{ + if (m_animationChain.empty()) + return; + + TAnimationList & frontList = m_animationChain.front(); + for (auto it = frontList.begin(); it != frontList.end();) + { + auto & anim = *it; + if (anim->GetType() == type) + { + if (rewind) + anim->Finish(); + SaveAnimationResult(*anim); + it = frontList.erase(it); + } + else + { + ++it; + } + } + if (frontList.empty()) + StartNextAnimations(); +} + +void AnimationSystem::FinishObjectAnimations(Animation::TObject object, bool rewind) +{ + if (m_animationChain.empty()) + return; + + TAnimationList & frontList = m_animationChain.front(); + for (auto it = frontList.begin(); it != frontList.end();) + { + auto & anim = *it; + if (anim->HasObject(object)) + { + if (rewind) + anim->Finish(); + SaveAnimationResult(*anim); + it = frontList.erase(it); + } + else + { + ++it; + } + } + if (frontList.empty()) + StartNextAnimations(); +} + +void AnimationSystem::Advance(double elapsedSeconds) +{ + if (m_animationChain.empty()) + return; + + TAnimationList & frontList = m_animationChain.front(); + for (auto it = frontList.begin(); it != frontList.end();) + { + auto & anim = *it; + anim->Advance(elapsedSeconds); + if (anim->IsFinished()) + { + anim->OnFinish(); + SaveAnimationResult(*anim); + it = frontList.erase(it); + } + else + { + ++it; + } + } + if (frontList.empty()) + StartNextAnimations(); +} + +bool AnimationSystem::GetProperty(Animation::TObject object, Animation::TProperty property, + Animation::PropertyValue & value) const +{ + if (!m_animationChain.empty()) + { + PropertyBlender blender; + for (auto const & anim : m_animationChain.front()) + { + if (anim->HasProperty(object, property)) + { + Animation::PropertyValue val; + if (anim->GetProperty(object, property, val)) + blender.Blend(val); + } + } + if (!blender.IsEmpty()) + { + value = blender.Finish(); + return true; + } + } + + auto it = m_propertyCache.find(make_pair(object, property)); + if (it != m_propertyCache.end()) + { + value = it->second; + m_propertyCache.erase(it); + return true; + } + return false; +} + +void AnimationSystem::SaveAnimationResult(Animation const & animation) +{ + for (auto const & object : animation.GetObjects()) + { + for (auto const & property : animation.GetProperties(object)) + { + Animation::PropertyValue value; + if (animation.GetProperty(object, property, value)) + m_propertyCache[make_pair(object, property)] = value; + } + } +} + +void AnimationSystem::StartNextAnimations() +{ + if (m_animationChain.empty()) + return; + + m_animationChain.pop_front(); + if (!m_animationChain.empty()) + { + for (auto & anim : m_animationChain.front()) + { + //TODO (in future): use propertyCache to load start values to the next animations + anim->OnStart(); + } + } +} + +} // namespace df diff --git a/drape_frontend/animation_system.hpp b/drape_frontend/animation_system.hpp new file mode 100644 index 0000000000..8762c4cb94 --- /dev/null +++ b/drape_frontend/animation_system.hpp @@ -0,0 +1,584 @@ +#pragma once + +#include "drape/pointers.hpp" + +#include "geometry/screenbase.hpp" + +#include "std/deque.hpp" +#include "std/noncopyable.hpp" +#include "std/unordered_set.hpp" + +namespace df +{ + +class Animation +{ +public: + enum Type + { + Sequence, + Parallel, + MapLinear, + MapScale, + MapFollow, + MapPerspective, + Arrow, + KineticScroll + }; + + enum Object + { + MyPositionArrow, + MapPlane, + Selection + }; + + enum ObjectProperty + { + Position, + Scale, + Angle, + AnglePerspective, + SwitchPerspective + }; + + struct SwitchPerspectiveParams + { + SwitchPerspectiveParams() = default; + + SwitchPerspectiveParams(bool enable, + double startAngle, double endAngle, + double angleFOV) + : m_enable(enable) + , m_startAngle(startAngle) + , m_endAngle(endAngle) + , m_angleFOV(angleFOV) + {} + + bool m_enable = false; + double m_startAngle = 0.0; + double m_endAngle = 0.0; + double m_angleFOV = 0.0; + }; + + struct PropertyValue + { + enum Type + { + ValueD, + ValuePointD, + ValuePerspectiveParams + }; + + PropertyValue() + {} + + explicit PropertyValue(double value) + : m_type(ValueD) + , m_valueD(value) + {} + + explicit PropertyValue(m2::PointD const & value) + : m_type(ValuePointD) + , m_valuePointD(value) + {} + + explicit PropertyValue(SwitchPerspectiveParams const & params) + : m_type(ValuePerspectiveParams) + { + m_valuePerspectiveParams = params; + } + + Type m_type; + union + { + m2::PointD m_valuePointD; + double m_valueD; + SwitchPerspectiveParams m_valuePerspectiveParams; + }; + }; + + using TObject = uint32_t; + using TProperty = uint32_t; + using TAnimObjects = unordered_set; + using TObjectProperties = unordered_set; + using TAction = function)>; + + Animation(bool couldBeInterrupted, bool couldBeBlended) + : m_couldBeInterrupted(couldBeInterrupted) + , m_couldBeBlended(couldBeBlended) + {} + + virtual void OnStart() { if (m_onStartAction != nullptr) m_onStartAction(this); } + virtual void OnFinish() { if (m_onFinishAction != nullptr) m_onFinishAction(this); } + virtual void Interrupt() { if (m_onInterruptAction != nullptr) m_onInterruptAction(this); } + + virtual Type GetType() const = 0; + + virtual TAnimObjects const & GetObjects() const = 0; + virtual bool HasObject(TObject object) const = 0; + virtual TObjectProperties const & GetProperties(TObject object) const = 0; + virtual bool HasProperty(TObject object, TProperty property) const = 0; + + virtual void SetMaxDuration(double maxDuration) = 0; + virtual double GetDuration() const = 0; + virtual bool IsFinished() const = 0; + + virtual void Advance(double elapsedSeconds) = 0; + virtual void Finish() { OnFinish(); } + + virtual bool GetProperty(TObject object, TProperty property, PropertyValue & value) const = 0; + + void SetOnStartAction(TAction const & action) { m_onStartAction = action; } + void SetOnFinishAction(TAction const & action) { m_onFinishAction = action; } + void SetOnInterruptAction(TAction const & action) { m_onInterruptAction = action; } + + bool CouldBeInterrupted() const { return m_couldBeInterrupted; } + bool CouldBeBlendedWith(Animation const & animation) const; + +protected: + TAction m_onStartAction; + TAction m_onFinishAction; + TAction m_onInterruptAction; + bool m_couldBeInterrupted; + bool m_couldBeBlended; +}; + +class Interpolator +{ +public: + Interpolator(double duration, double delay = 0); + virtual ~Interpolator(){} + + virtual void Advance(double elapsedSeconds); + virtual void Finish(); + + bool IsFinished() const; + void SetMaxDuration(double maxDuration); + void SetMinDuration(double minDuration); + double GetDuration() const; + +protected: + double GetT() const; + double GetElapsedTime() const; + +private: + double m_elapsedTime; + double m_duration; + double m_delay; +}; + +class PositionInterpolator: public Interpolator +{ + using TBase = Interpolator; + +public: + PositionInterpolator(double duration, double delay, + m2::PointD const & startPosition, + m2::PointD const & endPosition); + PositionInterpolator(m2::PointD const & startPosition, + m2::PointD const & endPosition, + ScreenBase const & convertor); + PositionInterpolator(double delay, m2::PointD const & startPosition, + m2::PointD const & endPosition, + ScreenBase const & convertor); + PositionInterpolator(m2::PointD const & startPosition, + m2::PointD const & endPosition, + m2::RectD const & pixelRect); + PositionInterpolator(double delay, m2::PointD const & startPosition, + m2::PointD const & endPosition, + m2::RectD const & pixelRect); + + static double GetMoveDuration(m2::PointD const & startPosition, + m2::PointD const & endPosition, ScreenBase const & convertor); + static double GetPixelMoveDuration(m2::PointD const & startPosition, + m2::PointD const & endPosition, m2::RectD const & pixelRect); + + // Interpolator overrides: + void Advance(double elapsedSeconds) override; + void Finish() override; + + virtual m2::PointD GetPosition() const { return m_position; } + +private: + m2::PointD m_startPosition; + m2::PointD m_endPosition; + m2::PointD m_position; +}; + +class ScaleInterpolator: public Interpolator +{ + using TBase = Interpolator; + +public: + ScaleInterpolator(double startScale, double endScale); + ScaleInterpolator(double delay, double startScale, double endScale); + + static double GetScaleDuration(double startScale, double endScale); + + // Interpolator overrides: + void Advance(double elapsedSeconds) override; + void Finish() override; + + virtual double GetScale() const { return m_scale; } + +private: + double const m_startScale; + double const m_endScale; + double m_scale; +}; + +class AngleInterpolator: public Interpolator +{ + using TBase = Interpolator; + +public: + AngleInterpolator(double startAngle, double endAngle); + AngleInterpolator(double delay, double startAngle, double endAngle); + AngleInterpolator(double delay, double duration, double startAngle, double endAngle); + + static double GetRotateDuration(double startAngle, double endAngle); + + // Interpolator overrides: + void Advance(double elapsedSeconds) override; + void Finish() override; + + virtual double GetAngle() const { return m_angle; } + +private: + double const m_startAngle; + double const m_endAngle; + double m_angle; +}; + +//TODO (in future): implement arrow animation on new animation system. +/*class ArrowAnimation : public Animation +{ +public: + ArrowAnimation(m2::PointD const & startPos, m2::PointD const & endPos, + double startAngle, double endAngle, ScreenBase const & convertor) + : Animation(false, false) + { + m_positionInterpolator.reset(new PositionInterpolator(startPos, endPos, convertor)); + m_angleInterpolator.reset(new AngleInterpolator(startAngle, endAngle)); + m_objects.insert(Animation::MyPositionArrow); + m_properties.insert(Animation::Position); + m_properties.insert(Animation::Angle); + } + + Animation::Type GetType() const override { return Animation::Arrow; } + + TAnimObjects const & GetObjects() const override + { + return m_objects; + } + + bool HasObject(TObject object) const override + { + return object == Animation::MyPositionArrow; + } + + TObjectProperties const & GetProperties(TObject object) const override; + bool HasProperty(TObject object, TProperty property) const override; + + void Advance(double elapsedSeconds) override; + void Finish() override; + +private: + drape_ptr m_positionInterpolator; + drape_ptr m_angleInterpolator; + TAnimObjects m_objects; + TObjectProperties m_properties; +};*/ + +class PerspectiveSwitchAnimation : public Animation +{ +public: + PerspectiveSwitchAnimation(double startAngle, double endAngle, double angleFOV); + + static double GetRotateDuration(double startAngle, double endAngle); + + Animation::Type GetType() const override { return Animation::MapPerspective; } + + TAnimObjects const & GetObjects() const override + { + return m_objects; + } + + bool HasObject(TObject object) const override + { + return m_objects.find(object) != m_objects.end(); + } + + TObjectProperties const & GetProperties(TObject object) const override; + bool HasProperty(TObject object, TProperty property) const override; + + void Advance(double elapsedSeconds) override; + void Finish() override; + + void OnStart() override; + void OnFinish() override; + + void SetMaxDuration(double maxDuration) override; + double GetDuration() const override; + bool IsFinished() const override; + + bool GetProperty(TObject object, TProperty property, PropertyValue & value) const override; + +private: + AngleInterpolator m_angleInterpolator; + double m_startAngle; + double m_endAngle; + double m_angleFOV; + + bool m_isEnablePerspectiveAnim; + mutable bool m_needPerspectiveSwitch; + TAnimObjects m_objects; + TObjectProperties m_properties; +}; + +class MapLinearAnimation : public Animation +{ +public: + MapLinearAnimation(m2::PointD const & startPos, m2::PointD const & endPos, + double startAngle, double endAngle, + double startScale, double endScale, ScreenBase const & convertor); + MapLinearAnimation(); + + void SetMove(m2::PointD const & startPos, m2::PointD const & endPos, ScreenBase const & convertor); + void SetRotate(double startAngle, double endAngle); + void SetScale(double startScale, double endScale); + + Animation::Type GetType() const override { return Animation::MapLinear; } + + TAnimObjects const & GetObjects() const override + { + return m_objects; + } + + bool HasObject(TObject object) const override + { + return object == Animation::MapPlane; + } + + TObjectProperties const & GetProperties(TObject object) const override; + bool HasProperty(TObject object, TProperty property) const override; + + void Advance(double elapsedSeconds) override; + void Finish() override; + + void SetMaxDuration(double maxDuration) override; + double GetDuration() const override; + bool IsFinished() const override; + + bool GetProperty(TObject object, TProperty property, PropertyValue & value) const override; + + void SetMaxScaleDuration(double maxDuration); + +private: + drape_ptr m_angleInterpolator; + drape_ptr m_positionInterpolator; + drape_ptr m_scaleInterpolator; + TObjectProperties m_properties; + TAnimObjects m_objects; +}; + +class MapScaleAnimation : public Animation +{ +public: + MapScaleAnimation(double startScale, double endScale, + m2::PointD const & globalPosition, m2::PointD const & offset); + + Animation::Type GetType() const override { return Animation::MapScale; } + + TAnimObjects const & GetObjects() const override + { + return m_objects; + } + + bool HasObject(TObject object) const override + { + return object == Animation::MapPlane; + } + + TObjectProperties const & GetProperties(TObject object) const override; + bool HasProperty(TObject object, TProperty property) const override; + + void Advance(double elapsedSeconds) override; + void Finish() override; + + void SetMaxDuration(double maxDuration) override; + double GetDuration() const override; + bool IsFinished() const override; + + bool GetProperty(TObject object, TProperty property, PropertyValue & value) const override; + +private: + ScaleInterpolator m_scaleInterpolator; + m2::PointD const m_pixelOffset; + m2::PointD const m_globalPosition; + TObjectProperties m_properties; + TAnimObjects m_objects; +}; + +class MapFollowAnimation : public Animation +{ +public: + MapFollowAnimation(m2::PointD const & globalPosition, + double startScale, double endScale, + double startAngle, double endAngle, + m2::PointD const & startPixelPosition, + m2::PointD const & endPixelPosition, + m2::RectD const & pixelRect); + + static m2::PointD CalculateCenter(ScreenBase const & screen, m2::PointD const & userPos, + m2::PointD const & pixelPos, double azimuth); + + static m2::PointD CalculateCenter(double scale, m2::RectD const & pixelRect, + m2::PointD const & userPos, m2::PointD const & pixelPos, double azimuth); + + Animation::Type GetType() const override { return Animation::MapFollow; } + + TAnimObjects const & GetObjects() const override + { + return m_objects; + } + + bool HasObject(TObject object) const override + { + return object == Animation::MapPlane; + } + + TObjectProperties const & GetProperties(TObject object) const override; + bool HasProperty(TObject object, TProperty property) const override; + + void Advance(double elapsedSeconds) override; + void Finish() override; + + void SetMaxDuration(double maxDuration) override; + double GetDuration() const override; + bool IsFinished() const override; + + bool GetProperty(TObject object, TProperty property, PropertyValue & value) const override; + +private: + double CalculateDuration() const; + + ScaleInterpolator m_scaleInterpolator; + PositionInterpolator m_pixelPosInterpolator; + AngleInterpolator m_angleInterpolator; + + m2::PointD const m_globalPosition; + + TObjectProperties m_properties; + TAnimObjects m_objects; +}; + +class SequenceAnimation : public Animation +{ +public: + SequenceAnimation(); + Animation::Type GetType() const override { return Animation::Sequence; } + TAnimObjects const & GetObjects() const override; + bool HasObject(TObject object) const override; + TObjectProperties const & GetProperties(TObject object) const override; + bool HasProperty(TObject object, TProperty property) const override; + + void SetMaxDuration(double maxDuration) override; + double GetDuration() const override; + bool IsFinished() const override; + + bool GetProperty(TObject object, TProperty property, PropertyValue &value) const override; + + void AddAnimation(drape_ptr animation); + + void OnStart() override; + void OnFinish() override; + + void Advance(double elapsedSeconds) override; + void Finish() override; + +private: + void ObtainObjectProperties(); + + deque> m_animations; + TAnimObjects m_objects; + map m_properties; +}; + +class ParallelAnimation : public Animation +{ +public: + ParallelAnimation(); + + Animation::Type GetType() const override { return Animation::Parallel; } + + TAnimObjects const & GetObjects() const override + { + return m_objects; + } + + bool HasObject(TObject object) const override + { + return m_objects.find(object) != m_objects.end(); + } + + TObjectProperties const & GetProperties(TObject object) const override; + bool HasProperty(TObject object, TProperty property) const override; + + void AddAnimation(drape_ptr animation); + + void OnStart() override; + void OnFinish() override; + + void Advance(double elapsedSeconds) override; + void Finish() override; + +private: + list> m_animations; + TAnimObjects m_objects; + map m_properties; +}; + +class AnimationSystem : private noncopyable +{ +public: + static AnimationSystem & Instance(); + + bool GetRect(ScreenBase const & currentScreen, m2::AnyRectD & rect); + + bool SwitchPerspective(Animation::SwitchPerspectiveParams & params); + bool GetPerspectiveAngle(double & angle); + + bool AnimationExists(Animation::TObject object) const; + bool HasAnimations() const; + + void CombineAnimation(drape_ptr animation); + void PushAnimation(drape_ptr animation); + + void FinishAnimations(Animation::Type type, bool rewind); + void FinishObjectAnimations(Animation::TObject object, bool rewind); + + void Advance(double elapsedSeconds); + + ScreenBase const & GetLastScreen() { return m_lastScreen; } + void SaveAnimationResult(Animation const & animation); + +private: + bool GetProperty(Animation::TObject object, Animation::TProperty property, Animation::PropertyValue & value) const; + void StartNextAnimations(); + + AnimationSystem(); + +private: + using TAnimationList = list>; + using TAnimationChain = deque; + using TPropertyCache = map, Animation::PropertyValue>; + + TAnimationChain m_animationChain; + mutable TPropertyCache m_propertyCache; + + ScreenBase m_lastScreen; +}; + +} diff --git a/drape_frontend/animation_utils.cpp b/drape_frontend/animation_utils.cpp index f647472a29..09ad468501 100644 --- a/drape_frontend/animation_utils.cpp +++ b/drape_frontend/animation_utils.cpp @@ -4,15 +4,12 @@ #include "indexer/scales.hpp" -// Zoom level before which animation can not be instant. -int const kInstantAnimationZoomLevel = 5; - namespace df { bool IsAnimationAllowed(double duration, ScreenBase const & screen) { - return duration > 0.0 && (duration < kMaxAnimationTimeSec || df::GetDrawTileScale(screen) < kInstantAnimationZoomLevel); + return duration > 0.0 && duration <= kMaxAnimationTimeSec; } } // namespace df diff --git a/drape_frontend/drape_frontend.pro b/drape_frontend/drape_frontend.pro index 1e3aa5482e..279cfd8ea8 100755 --- a/drape_frontend/drape_frontend.pro +++ b/drape_frontend/drape_frontend.pro @@ -15,9 +15,7 @@ SOURCES += \ animation/base_interpolator.cpp \ animation/interpolation_holder.cpp \ animation/interpolations.cpp \ - animation/model_view_animation.cpp \ animation/opacity_animation.cpp \ - animation/perspective_animation.cpp \ animation/show_hide_animation.cpp \ gui/choose_position_mark.cpp \ gui/compass.cpp \ @@ -29,6 +27,7 @@ SOURCES += \ gui/ruler_helper.cpp \ gui/shape.cpp \ gui/skin.cpp \ + animation_system.cpp \ animation_utils.cpp \ apply_feature_functors.cpp \ area_shape.cpp \ @@ -97,9 +96,7 @@ HEADERS += \ animation/base_interpolator.hpp \ animation/interpolation_holder.hpp \ animation/interpolations.hpp \ - animation/model_view_animation.hpp \ animation/opacity_animation.hpp \ - animation/perspective_animation.hpp \ animation/show_hide_animation.hpp \ animation/value_mapping.hpp \ gui/choose_position_mark.hpp \ @@ -113,6 +110,7 @@ HEADERS += \ gui/shape.hpp \ gui/skin.hpp \ animation_constants.hpp \ + animation_system.hpp \ animation_utils.hpp \ apply_feature_functors.hpp \ area_shape.hpp \ diff --git a/drape_frontend/drape_frontend_tests/user_event_stream_tests.cpp b/drape_frontend/drape_frontend_tests/user_event_stream_tests.cpp index dbd259fd12..91865064ab 100644 --- a/drape_frontend/drape_frontend_tests/user_event_stream_tests.cpp +++ b/drape_frontend/drape_frontend_tests/user_event_stream_tests.cpp @@ -36,7 +36,7 @@ public: void CorrectScalePoint(m2::PointD & pt1, m2::PointD & pt2) const override {} void CorrectGlobalScalePoint(m2::PointD & pt) const override {} void OnScaleEnded() override {} - void OnAnimationStarted(ref_ptr /* anim */) override {} + void OnAnimationStarted(ref_ptr /* anim */) override {} void AddUserEvent(df::TouchEvent const & event) { diff --git a/drape_frontend/frontend_renderer.cpp b/drape_frontend/frontend_renderer.cpp index 26a8863c43..ac074b0adc 100755 --- a/drape_frontend/frontend_renderer.cpp +++ b/drape_frontend/frontend_renderer.cpp @@ -1,5 +1,6 @@ #include "drape_frontend/animation/interpolation_holder.hpp" #include "drape_frontend/gui/drape_gui.hpp" +#include "drape_frontend/animation_system.hpp" #include "drape_frontend/framebuffer.hpp" #include "drape_frontend/frontend_renderer.hpp" #include "drape_frontend/message_subclasses.hpp" @@ -746,14 +747,15 @@ void FrontendRenderer::FollowRoute(int preferredZoomLevel, int preferredZoomLeve double rotationAngle, double angleFOV) { + m_myPositionController->ActivateRouting(!m_enablePerspectiveInNavigation ? preferredZoomLevel + : preferredZoomLevelIn3d); + if (m_enablePerspectiveInNavigation) { bool immediatelyStart = !m_myPositionController->IsRotationAvailable(); AddUserEvent(EnablePerspectiveEvent(rotationAngle, angleFOV, true /* animated */, immediatelyStart)); } - m_myPositionController->ActivateRouting(!m_enablePerspectiveInNavigation ? preferredZoomLevel - : preferredZoomLevelIn3d); m_overlayTree->SetFollowingMode(true); } @@ -1389,7 +1391,7 @@ void FrontendRenderer::OnScaleEnded() PullToBoundArea(false /* randomPlace */, false /* applyZoom */); } -void FrontendRenderer::OnAnimationStarted(ref_ptr anim) +void FrontendRenderer::OnAnimationStarted(ref_ptr anim) { m_myPositionController->AnimationStarted(anim); } @@ -1505,14 +1507,15 @@ void FrontendRenderer::Routine::Do() isActiveFrame |= m_renderer.m_texMng->UpdateDynamicTextures(); m_renderer.RenderScene(modelView); - isActiveFrame |= InterpolationHolder::Instance().Advance(frameTime); - if (modelViewChanged) { m_renderer.UpdateScene(modelView); m_renderer.EmitModelViewChanged(modelView); } + isActiveFrame |= InterpolationHolder::Instance().Advance(frameTime); + AnimationSystem::Instance().Advance(frameTime); + isActiveFrame |= m_renderer.m_userEventStream.IsWaitingForActionCompletion(); if (isActiveFrame) diff --git a/drape_frontend/frontend_renderer.hpp b/drape_frontend/frontend_renderer.hpp index 4e9e4d363d..79e1baf00b 100755 --- a/drape_frontend/frontend_renderer.hpp +++ b/drape_frontend/frontend_renderer.hpp @@ -189,7 +189,7 @@ private: void CorrectScalePoint(m2::PointD & pt1, m2::PointD & pt2) const override; void CorrectGlobalScalePoint(m2::PointD & pt) const override; void OnScaleEnded() override; - void OnAnimationStarted(ref_ptr anim) override; + void OnAnimationStarted(ref_ptr anim) override; class Routine : public threads::IRoutine { diff --git a/drape_frontend/kinetic_scroller.cpp b/drape_frontend/kinetic_scroller.cpp index 745c30dc6a..a636166e26 100644 --- a/drape_frontend/kinetic_scroller.cpp +++ b/drape_frontend/kinetic_scroller.cpp @@ -25,39 +25,86 @@ double CalculateKineticMaxSpeed(ScreenBase const & modelView) return (kKineticMaxSpeedStart * lerpCoef + kKineticMaxSpeedEnd * (1.0 - lerpCoef)) * VisualParams::Instance().GetVisualScale(); } -class KineticScrollAnimation : public BaseModelViewAnimation +class KineticScrollAnimation : public Animation { public: // startRect - mercator visible on screen rect in moment when user release fingers. // direction - mercator space direction of moving. length(direction) - mercator distance on wich map will be offset. - 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()) + KineticScrollAnimation(m2::PointD const & startPos, m2::PointD const & direction, double duration) + : Animation(true /* couldBeInterrupted */, true /* couldBeBlended */) + , m_endPos(startPos + direction) , m_direction(direction) + , m_duration(duration) + , m_elapsedTime(0.0) { + m_objects.insert(Animation::MapPlane); + m_properties.insert(Animation::Position); } - ModelViewAnimationType GetType() const override { return ModelViewAnimationType::KineticScroll; } + Animation::Type GetType() const override { return Animation::KineticScroll; } - m2::AnyRectD GetCurrentRect(ScreenBase const & screen) const override + TAnimObjects const & GetObjects() const override { + return m_objects; + } + + bool HasObject(TObject object) const override + { + return m_objects.find(object) != m_objects.end(); + } + + TObjectProperties const & GetProperties(TObject object) const override + { + ASSERT(HasObject(object), ()); + return m_properties; + } + + bool HasProperty(TObject object, TProperty property) const override + { + return HasObject(object) && m_properties.find(property) != m_properties.end(); + } + + void SetMaxDuration(double maxDuration) override + { + if (m_duration > maxDuration) + m_duration = maxDuration; + } + + double GetDuration() const override { return m_duration; } + bool IsFinished() const override { return m_elapsedTime >= m_duration; } + + void Advance(double elapsedSeconds) override + { + m_elapsedTime += elapsedSeconds; + } + + void Finish() override + { + m_elapsedTime = m_duration; + Animation::Finish(); + } + + bool GetProperty(TObject object, TProperty property, PropertyValue & value) const override + { + ASSERT(HasProperty(object, property), ()); // Current position = target position - amplutide * e ^ (elapsed / duration). // We calculate current position not based on start position, but based on target position. - return m2::AnyRectD(m_targetCenter - m_direction * exp(-kKineticFadeoff * GetT()), m_angle, m_localRect); - } - - m2::AnyRectD GetTargetRect(ScreenBase const & screen) const override - { - return GetCurrentRect(screen); + value = PropertyValue(m_endPos - m_direction * exp(-kKineticFadeoff * GetT())); + return true; } private: - m2::PointD m_targetCenter; - ang::AngleD m_angle; - m2::RectD m_localRect; + double GetT() const + { + return IsFinished() ? 1.0 : m_elapsedTime / m_duration; + } + + m2::PointD m_endPos; m2::PointD m_direction; + double m_duration; + double m_elapsedTime; + TAnimObjects m_objects; + TObjectProperties m_properties; }; KineticScroller::KineticScroller() @@ -116,11 +163,11 @@ void KineticScroller::CancelGrab() m_direction = m2::PointD::Zero(); } -drape_ptr KineticScroller::CreateKineticAnimation(ScreenBase const & modelView) +drape_ptr KineticScroller::CreateKineticAnimation(ScreenBase const & modelView) { static double kVelocityThreshold = kKineticThreshold * VisualParams::Instance().GetVisualScale(); if (m_direction.Length() < kVelocityThreshold) - return drape_ptr(); + return drape_ptr(); // Before we start animation we have to convert length(m_direction) from pixel space to mercator space. m2::PointD center = m_lastRect.GlobalCenter(); @@ -129,9 +176,9 @@ drape_ptr KineticScroller::CreateKineticAnimation(Screen m2::PointD const glbDirection = m_direction.Normalize() * glbLength; m2::PointD const targetCenter = center + glbDirection; if (!df::GetWorldRect().IsPointInside(targetCenter)) - return drape_ptr(); + return drape_ptr(); - return make_unique_dp(m_lastRect, glbDirection, kKineticDuration); + return make_unique_dp(center, glbDirection, kKineticDuration); } } // namespace df diff --git a/drape_frontend/kinetic_scroller.hpp b/drape_frontend/kinetic_scroller.hpp index 2d9de55aed..dd8026f3de 100644 --- a/drape_frontend/kinetic_scroller.hpp +++ b/drape_frontend/kinetic_scroller.hpp @@ -1,6 +1,6 @@ #pragma once -#include "animation/model_view_animation.hpp" +#include "animation_system.hpp" #include "drape/pointers.hpp" @@ -18,7 +18,7 @@ public: bool IsActive() const; void GrabViewRect(ScreenBase const & modelView, double timeStamp); void CancelGrab(); - drape_ptr CreateKineticAnimation(ScreenBase const & modelView); + drape_ptr CreateKineticAnimation(ScreenBase const & modelView); private: double m_lastTimestamp; diff --git a/drape_frontend/my_position_controller.cpp b/drape_frontend/my_position_controller.cpp index 23307c0827..864658b032 100644 --- a/drape_frontend/my_position_controller.cpp +++ b/drape_frontend/my_position_controller.cpp @@ -1,9 +1,9 @@ #include "drape_frontend/my_position_controller.hpp" +#include "drape_frontend/animation_system.hpp" #include "drape_frontend/animation_utils.hpp" #include "drape_frontend/visual_params.hpp" #include "drape_frontend/animation/base_interpolator.hpp" #include "drape_frontend/animation/interpolations.hpp" -#include "drape_frontend/animation/model_view_animation.hpp" #include "geometry/mercator.hpp" @@ -323,7 +323,8 @@ void MyPositionController::OnLocationUpdate(location::GpsInfo const & info, bool if (m_mode == location::Follow) ChangeModelView(m_position, kDoNotChangeZoom); else if (m_mode == location::FollowAndRotate) - ChangeModelView(m_position, m_drawDirection, GetRotationPixelCenter(), kDoNotChangeZoom); + ChangeModelView(m_position, m_drawDirection, + m_isInRouting ? m_centerPixelPositionRouting : m_centerPixelPosition, kDoNotChangeZoom); } m_isPositionAssigned = true; @@ -533,16 +534,17 @@ void MyPositionController::UpdateViewport() if (m_mode == location::Follow) ChangeModelView(m_position, kDoNotChangeZoom); else if (m_mode == location::FollowAndRotate) - ChangeModelView(m_position, m_drawDirection, GetRotationPixelCenter(), kDoNotChangeZoom); + ChangeModelView(m_position, m_drawDirection, + m_isInRouting ? m_centerPixelPositionRouting : m_centerPixelPosition, kDoNotChangeZoom); } m2::PointD MyPositionController::GetRotationPixelCenter() const { if (m_mode == location::Follow) - return m_centerPixelPosition; + return m_pixelRect.Center(); if (m_mode == location::FollowAndRotate) - return m_isInRouting ? m_centerPixelPositionRouting : m_centerPixelPosition; + return m_isInRouting ? GetRoutingRotationPixelCenter() : m_pixelRect.Center(); return m2::PointD::Zero(); } @@ -581,11 +583,11 @@ void MyPositionController::CheckAnimFinished() const m_anim.reset(); } -void MyPositionController::AnimationStarted(ref_ptr anim) +void MyPositionController::AnimationStarted(ref_ptr anim) { if (m_isPendingAnimation && m_animCreator != nullptr && anim != nullptr && - (anim->GetType() == ModelViewAnimationType::FollowAndRotate || - anim->GetType() == ModelViewAnimationType::Default)) + (anim->GetType() == Animation::MapFollow || + anim->GetType() == Animation::MapLinear)) { m_isPendingAnimation = false; m_animCreator(); @@ -594,8 +596,8 @@ void MyPositionController::AnimationStarted(ref_ptr anim void MyPositionController::CreateAnim(m2::PointD const & oldPos, double oldAzimut, ScreenBase const & screen) { - double const moveDuration = ModelViewAnimation::GetMoveDuration(oldPos, m_position, screen); - double const rotateDuration = ModelViewAnimation::GetRotateDuration(oldAzimut, m_drawDirection); + double const moveDuration = PositionInterpolator::GetMoveDuration(oldPos, m_position, screen); + double const rotateDuration = AngleInterpolator::GetRotateDuration(oldAzimut, m_drawDirection); if (df::IsAnimationAllowed(max(moveDuration, rotateDuration), screen)) { if (IsModeChangeViewport()) @@ -624,7 +626,7 @@ void MyPositionController::ActivateRouting(int zoomLevel) if (IsRotationAvailable()) { ChangeMode(location::FollowAndRotate); - ChangeModelView(m_position, m_drawDirection, GetRotationPixelCenter(), zoomLevel); + ChangeModelView(m_position, m_drawDirection, m_centerPixelPositionRouting, zoomLevel); } else { diff --git a/drape_frontend/my_position_controller.hpp b/drape_frontend/my_position_controller.hpp index 6b4658fa34..ff52b3f2b9 100644 --- a/drape_frontend/my_position_controller.hpp +++ b/drape_frontend/my_position_controller.hpp @@ -15,7 +15,7 @@ namespace df { -class BaseModelViewAnimation; +class Animation; class MyPositionController { @@ -61,7 +61,7 @@ public: void ScaleStarted(); void ScaleEnded(); - void AnimationStarted(ref_ptr anim); + void AnimationStarted(ref_ptr anim); void Rotated(); diff --git a/drape_frontend/user_event_stream.cpp b/drape_frontend/user_event_stream.cpp index 128a1f5720..ad3a449e95 100644 --- a/drape_frontend/user_event_stream.cpp +++ b/drape_frontend/user_event_stream.cpp @@ -1,5 +1,6 @@ #include "drape_frontend/user_event_stream.hpp" #include "drape_frontend/animation_constants.hpp" +#include "drape_frontend/animation_system.hpp" #include "drape_frontend/animation_utils.hpp" #include "drape_frontend/visual_params.hpp" @@ -28,6 +29,8 @@ uint64_t const kKineticDelayMs = 500; float const kForceTapThreshold = 0.75; +int const kDoNotChangeZoom = -1; + size_t GetValidTouchesCount(array const & touches) { size_t result = 0; @@ -39,6 +42,45 @@ size_t GetValidTouchesCount(array const & touches) return result; } +drape_ptr GetPrettyMoveAnimation(ScreenBase const screen, double startScale, double endScale, + m2::PointD const & startPt, m2::PointD const & endPt, + function)> onStartAnimation) +{ + double const moveDuration = PositionInterpolator::GetMoveDuration(startPt, endPt, screen); + double const scaleFactor = moveDuration / kMaxAnimationTimeSec * 2.0; + + drape_ptr sequenceAnim = make_unique_dp(); + drape_ptr zoomOutAnim = make_unique_dp(); + zoomOutAnim->SetScale(startScale, startScale * scaleFactor); + zoomOutAnim->SetMaxDuration(kMaxAnimationTimeSec * 0.5); + zoomOutAnim->SetOnStartAction(onStartAnimation); + + //TODO (in future): Pass fixed duration instead of screen. + drape_ptr moveAnim = make_unique_dp(); + moveAnim->SetMove(startPt, endPt, screen); + moveAnim->SetMaxDuration(kMaxAnimationTimeSec); + + drape_ptr zoomInAnim = make_unique_dp(); + zoomInAnim->SetScale(startScale * scaleFactor, endScale); + zoomInAnim->SetMaxDuration(kMaxAnimationTimeSec * 0.5); + + sequenceAnim->AddAnimation(move(zoomOutAnim)); + sequenceAnim->AddAnimation(move(moveAnim)); + sequenceAnim->AddAnimation(move(zoomInAnim)); + return sequenceAnim; +} + +double CalculateScale(ScreenBase const & screen, m2::RectD const & localRect) +{ + m2::RectD const pixelRect = screen.PixelRect(); + return max(localRect.SizeX() / pixelRect.SizeX(), localRect.SizeY() / pixelRect.SizeY()); +} + +double CalculateScale(ScreenBase const & screen, m2::AnyRectD const & rect) +{ + return CalculateScale(screen, rect.GetLocalRect()); +} + } // namespace #ifdef DEBUG @@ -116,6 +158,7 @@ void TouchEvent::Swap() UserEventStream::UserEventStream() : m_state(STATE_EMPTY) + , m_animationSystem(AnimationSystem::Instance()) , m_startDragOrg(m2::PointD::Zero()) , m_startDoubleTapAndHold(m2::PointD::Zero()) { @@ -144,7 +187,7 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChange, bool & modelViewChange = !events.empty() || m_state == STATE_SCALE || m_state == STATE_DRAG; for (UserEvent const & e : events) { - if (m_perspectiveAnimation != nullptr && FilterEventWhile3dAnimation(e.m_type)) + if (m_perspectiveAnimation && FilterEventWhile3dAnimation(e.m_type)) continue; bool breakAnim = false; @@ -166,7 +209,7 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChange, bool & TouchCancel(m_touches); break; case UserEvent::EVENT_SET_RECT: - if (m_perspectiveAnimation != nullptr) + if (m_perspectiveAnimation) { m_pendingEvent.reset(new UserEvent(e.m_rectEvent)); break; @@ -187,7 +230,7 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChange, bool & if (screen.isPerspective()) { m2::PointD pt = screen.P3dtoP(screen.PixelRectIn3d().Center()); - breakAnim = SetFollowAndRotate(screen.PtoG(pt), pt, e.m_rotate.m_targetAzimut, -1, true); + breakAnim = SetFollowAndRotate(screen.PtoG(pt), pt, e.m_rotate.m_targetAzimut, kDoNotChangeZoom, true); } else { @@ -198,21 +241,14 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChange, bool & } break; case UserEvent::EVENT_FOLLOW_AND_ROTATE: - m_pendingPerspective = false; 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); TouchCancel(m_touches); break; case UserEvent::EVENT_ENABLE_PERSPECTIVE: - if (!e.m_enable3dMode.m_immediatelyStart) - { - m_pendingPerspective = true; - m_pendingEvent.reset(new UserEvent(e.m_enable3dMode)); - } - else - SetEnable3dMode(e.m_enable3dMode.m_rotationAngle, e.m_enable3dMode.m_angleFOV, - e.m_enable3dMode.m_isAnim, viewportChanged); + SetEnable3dMode(e.m_enable3dMode.m_rotationAngle, e.m_enable3dMode.m_angleFOV, + e.m_enable3dMode.m_isAnim, e.m_enable3dMode.m_immediatelyStart); m_discardedFOV = m_discardedAngle = 0.0; break; case UserEvent::EVENT_DISABLE_PERSPECTIVE: @@ -229,7 +265,7 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChange, bool & } else if (m_discardedFOV > 0.0) { - SetEnable3dMode(m_discardedAngle, m_discardedFOV, true /* isAnim */, viewportChanged); + SetEnable3dMode(m_discardedAngle, m_discardedFOV, true /* isAnim */, true /* immediatelyStart */); m_discardedFOV = m_discardedAngle = 0.0; } break; @@ -239,53 +275,23 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChange, bool & } if (breakAnim) - { - m_animation.reset(); modelViewChange = true; - } } - if (m_animation != nullptr) - { - m2::AnyRectD const rect = m_animation->GetCurrentRect(GetCurrentScreen()); - m_navigator.SetFromRect(rect); - modelViewChange = true; - if (m_animation->IsFinished()) - m_animation.reset(); - } + ApplyAnimations(modelViewChange, viewportChanged); - if (m_pendingEvent != nullptr && - m_pendingEvent->m_type == UserEvent::EVENT_ENABLE_PERSPECTIVE && - !m_pendingPerspective && m_animation == nullptr) + if (m_perspectiveAnimation) { - SetEnable3dMode(m_pendingEvent->m_enable3dMode.m_rotationAngle, - m_pendingEvent->m_enable3dMode.m_angleFOV, - m_pendingEvent->m_enable3dMode.m_isAnim, - viewportChanged); - modelViewChange = true; - m_pendingEvent.reset(); - } - - if (m_perspectiveAnimation != nullptr) - { - double const angle = m_perspectiveAnimation->GetRotationAngle(); - m_navigator.SetRotationIn3dMode(angle); - modelViewChange = true; TouchCancel(m_touches); - - if (m_perspectiveAnimation->IsFinished()) + } + else + { + if (m_pendingEvent != nullptr && m_pendingEvent->m_type == UserEvent::EVENT_SET_RECT) { - if (angle == 0.0) - { - m_navigator.Disable3dMode(); - viewportChanged = true; - - if (m_pendingEvent != nullptr && m_pendingEvent->m_type == UserEvent::EVENT_SET_RECT) - SetRect(m_pendingEvent->m_rectEvent.m_rect, m_pendingEvent->m_rectEvent.m_zoom, - m_pendingEvent->m_rectEvent.m_applyRotation, m_pendingEvent->m_rectEvent.m_isAnim); - } + SetRect(m_pendingEvent->m_rectEvent.m_rect, m_pendingEvent->m_rectEvent.m_zoom, + m_pendingEvent->m_rectEvent.m_applyRotation, m_pendingEvent->m_rectEvent.m_isAnim); m_pendingEvent.reset(); - m_perspectiveAnimation.reset(); + modelViewChange = true; } } @@ -300,6 +306,40 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChange, bool & return m_navigator.Screen(); } +void UserEventStream::ApplyAnimations(bool & modelViewChanged, bool & viewportChanged) +{ + if (m_animationSystem.AnimationExists(Animation::MapPlane)) + { + m2::AnyRectD rect; + if (m_animationSystem.GetRect(GetCurrentScreen(), rect)) + m_navigator.SetFromRect(rect); + + Animation::SwitchPerspectiveParams switchPerspective; + if (m_animationSystem.SwitchPerspective(switchPerspective)) + { + if (switchPerspective.m_enable) + { + m_navigator.Enable3dMode(switchPerspective.m_startAngle, switchPerspective.m_endAngle, + switchPerspective.m_angleFOV); + } + else + { + m_navigator.Disable3dMode(); + } + viewportChanged = true; + } + + double perspectiveAngle; + if (m_animationSystem.GetPerspectiveAngle(perspectiveAngle) && + GetCurrentScreen().isPerspective()) + { + m_navigator.SetRotationIn3dMode(perspectiveAngle); + } + + modelViewChanged = true; + } +} + ScreenBase const & UserEventStream::GetCurrentScreen() const { return m_navigator.Screen(); @@ -313,30 +353,24 @@ bool UserEventStream::SetScale(m2::PointD const & pxScaleCenter, double factor, if (isAnim) { - // Reset current animation if there is any. - ResetCurrentAnimation(); - m2::PointD glbScaleCenter = m_navigator.PtoG(m_navigator.P3dtoP(scaleCenter)); if (m_listener) m_listener->CorrectGlobalScalePoint(glbScaleCenter); m2::PointD const offset = GetCurrentScreen().PixelRect().Center() - m_navigator.P3dtoP(scaleCenter); - auto const creator = [this, &glbScaleCenter, &offset](m2::AnyRectD const & startRect, m2::AnyRectD const & endRect, - double aDuration, double mDuration, double sDuration) - { - m_animation.reset(new ScaleAnimation(startRect, endRect, aDuration, mDuration, - sDuration, glbScaleCenter, offset)); - if (m_listener) - m_listener->OnAnimationStarted(make_ref(m_animation)); - }; + ScreenBase const & startScreen = GetCurrentScreen(); + ScreenBase endScreen = startScreen; + m_navigator.CalculateScale(scaleCenter, factor, endScreen); - ScreenBase screen = GetCurrentScreen(); - m_navigator.CalculateScale(scaleCenter, factor, screen); - - return SetRect(screen.GlobalRect(), true, creator); + auto anim = make_unique_dp(startScreen.GetScale(), endScreen.GetScale(), + glbScaleCenter, offset); + anim->SetMaxDuration(kMaxAnimationTimeSec); + m_animationSystem.CombineAnimation(move(anim)); + return false; } + ResetCurrentAnimations(); m_navigator.Scale(scaleCenter, factor); return true; } @@ -363,7 +397,7 @@ bool UserEventStream::SetCenter(m2::PointD const & center, int zoom, bool isAnim ScreenBase screen = currentScreen; bool finishIn2d = false; bool finishIn3d = false; - if (zoom != -1) + if (zoom != kDoNotChangeZoom) { bool const isScaleAllowableIn3d = IsScaleAllowableIn3d(zoom); finishIn3d = m_discardedFOV > 0.0 && isScaleAllowableIn3d; @@ -377,7 +411,7 @@ bool UserEventStream::SetCenter(m2::PointD const & center, int zoom, bool isAnim double const scale3d = screen.PixelRect().SizeX() / screen.PixelRectIn3d().SizeX(); - if (zoom == -1) + if (zoom == kDoNotChangeZoom) { m2::AnyRectD const r = GetTargetRect(); angle = r.Angle(); @@ -396,25 +430,24 @@ bool UserEventStream::SetCenter(m2::PointD const & center, int zoom, bool isAnim double const aspectRatio = screen.PixelRect().SizeY() / screen.PixelRect().SizeX(); if (aspectRatio > 1.0) - localRect.Inflate(0.0, localRect.SizeY() / 2.0 * aspectRatio); + localRect.Inflate(0.0, localRect.SizeY() * 0.5 * aspectRatio); else - localRect.Inflate(localRect.SizeX() / 2.0 / aspectRatio, 0.0); + localRect.Inflate(localRect.SizeX() * 0.5 / aspectRatio, 0.0); } if (screen.isPerspective()) { - double const centerOffset3d = localRect.SizeY() * (1.0 - 1.0 / (scale3d * cos(screen.GetRotationAngle()))) / 2.0; + double const centerOffset3d = localRect.SizeY() * (1.0 - 1.0 / (scale3d * cos(screen.GetRotationAngle()))) * 0.5; targetCenter = targetCenter.Move(centerOffset3d, angle.cos(), -angle.sin()); } if (finishIn2d || finishIn3d) { - double const scaleToCurrent = - finishIn2d ? currentScreen.PixelRect().SizeX() / currentScreen.PixelRectIn3d().SizeX() - : 1.0 / scale3d; + double const scale = currentScreen.PixelRect().SizeX() / currentScreen.PixelRectIn3d().SizeX(); + double const scaleToCurrent = finishIn2d ? scale : 1.0 / scale3d; double const currentGSizeY = localRect.SizeY() * scaleToCurrent; - targetCenter = targetCenter.Move((currentGSizeY - localRect.SizeY()) / 2.0, + targetCenter = targetCenter.Move((currentGSizeY - localRect.SizeY()) * 0.5, angle.cos(), -angle.sin()); localRect.Scale(scaleToCurrent); } @@ -431,39 +464,47 @@ bool UserEventStream::SetRect(m2::RectD rect, int zoom, bool applyRotation, bool } bool UserEventStream::SetRect(m2::AnyRectD const & rect, bool isAnim) -{ - // Reset current animation if there is any. - ResetCurrentAnimation(); - - return SetRect(rect, isAnim, [this](m2::AnyRectD const & startRect, m2::AnyRectD const & endRect, - double aDuration, double mDuration, double sDuration) - { - m_animation.reset(new ModelViewAnimation(startRect, endRect, aDuration, mDuration, sDuration)); - if (m_listener) - m_listener->OnAnimationStarted(make_ref(m_animation)); - }); -} - -bool UserEventStream::SetRect(m2::AnyRectD const & rect, bool isAnim, TAnimationCreator const & animCreator) { if (isAnim) { - ScreenBase const & screen = m_navigator.Screen(); + ScreenBase const & screen = GetCurrentScreen(); m2::AnyRectD const startRect = GetCurrentRect(); - double const angleDuration = ModelViewAnimation::GetRotateDuration(startRect.Angle().val(), rect.Angle().val()); - double const moveDuration = ModelViewAnimation::GetMoveDuration(startRect.GlobalZero(), rect.GlobalZero(), screen); - double scaleDuration = ModelViewAnimation::GetScaleDuration(startRect.GetLocalRect().SizeX(), rect.GetLocalRect().SizeX()); - if (scaleDuration > kMaxAnimationTimeSec) - scaleDuration = kMaxAnimationTimeSec; - if (df::IsAnimationAllowed(max(max(angleDuration, moveDuration), scaleDuration), screen)) + double const startScale = CalculateScale(screen, startRect); + double const endScale = CalculateScale(screen, rect); + + drape_ptr anim = make_unique_dp(); + anim->SetRotate(startRect.Angle().val(), rect.Angle().val()); + anim->SetMove(startRect.GlobalCenter(), rect.GlobalCenter(), screen); + anim->SetScale(startScale, endScale); + anim->SetMaxScaleDuration(kMaxAnimationTimeSec); + + auto onStartHandler = [this](ref_ptr animation) { - ASSERT(animCreator != nullptr, ()); - animCreator(startRect, rect, angleDuration, moveDuration, scaleDuration); + if (m_listener) + m_listener->OnAnimationStarted(animation); + }; + if (df::IsAnimationAllowed(anim->GetDuration(), screen)) + { + anim->SetOnStartAction(onStartHandler); + m_animationSystem.CombineAnimation(move(anim)); return false; } + else + { + m2::PointD const startPt = startRect.GlobalCenter(); + m2::PointD const endPt = rect.GlobalCenter(); + double const moveDuration = PositionInterpolator::GetMoveDuration(startPt, endPt, screen); + if (moveDuration > kMaxAnimationTimeSec) + { + auto sequenceAnim = GetPrettyMoveAnimation(screen, startScale, endScale, startPt, endPt, + onStartHandler); + m_animationSystem.CombineAnimation(move(sequenceAnim)); + return false; + } + } } - m_animation.reset(); + ResetCurrentAnimations(); m_navigator.SetFromRect(rect); return true; } @@ -474,7 +515,7 @@ bool UserEventStream::SetFollowAndRotate(m2::PointD const & userPos, m2::PointD // Extract target local rect from current animation or calculate it from preferredZoomLevel // to preserve final scale. m2::RectD targetLocalRect; - if (preferredZoomLevel != -1) + if (preferredZoomLevel != kDoNotChangeZoom) { ScreenBase newScreen = GetCurrentScreen(); m2::RectD r = df::GetRectForDrawScale(preferredZoomLevel, m2::PointD::Zero()); @@ -485,82 +526,133 @@ bool UserEventStream::SetFollowAndRotate(m2::PointD const & userPos, m2::PointD } else { - if (m_animation != nullptr) - targetLocalRect = m_animation->GetTargetRect(GetCurrentScreen()).GetLocalRect(); - else - targetLocalRect = GetCurrentRect().GetLocalRect(); + targetLocalRect = GetTargetRect().GetLocalRect(); } if (isAnim) { - // Reset current animation if there is any. - ResetCurrentAnimation(); - ScreenBase const & screen = m_navigator.Screen(); - m2::PointD const newCenter = FollowAndRotateAnimation::CalculateCenter(screen, userPos, pixelPos, -azimuth); - - m2::AnyRectD const startRect = GetCurrentRect(); - double const angleDuration = ModelViewAnimation::GetRotateDuration(startRect.Angle().val(), -azimuth); - double const moveDuration = ModelViewAnimation::GetMoveDuration(startRect.GlobalZero(), newCenter, screen); - double const duration = max(angleDuration, moveDuration); - if (df::IsAnimationAllowed(duration, screen)) + double const targetScale = CalculateScale(screen, targetLocalRect); + auto onStartHandler = [this](ref_ptr animation) { - m_animation.reset(new FollowAndRotateAnimation(startRect, targetLocalRect, userPos, - screen.GtoP(userPos), pixelPos, azimuth, duration)); if (m_listener) - m_listener->OnAnimationStarted(make_ref(m_animation)); + m_listener->OnAnimationStarted(animation); + }; + + double const startScale = CalculateScale(screen, GetCurrentRect()); + + // Run pretty move animation if we are far from userPos. + m2::PointD const startPt = GetCurrentRect().GlobalCenter(); + double const moveDuration = PositionInterpolator::GetMoveDuration(startPt, userPos, screen); + if (moveDuration > kMaxAnimationTimeSec) + { + auto sequenceAnim = GetPrettyMoveAnimation(screen, startScale, targetScale, startPt, userPos, + onStartHandler); + sequenceAnim->SetOnFinishAction([this, userPos, pixelPos, onStartHandler, targetScale, azimuth] + (ref_ptr animation) + { + ScreenBase const & screen = m_navigator.Screen(); + double const startScale = CalculateScale(screen, GetCurrentRect()); + auto anim = make_unique_dp(userPos, startScale, targetScale, + screen.GlobalRect().Angle().val(), -azimuth, + screen.GtoP(userPos), pixelPos, screen.PixelRect()); + anim->SetOnStartAction(onStartHandler); + anim->SetMaxDuration(kMaxAnimationTimeSec); + m_animationSystem.CombineAnimation(move(anim)); + }); + m_animationSystem.CombineAnimation(move(sequenceAnim)); return false; } + + // Run follow-and-rotate animation. + auto anim = make_unique_dp(userPos, startScale, targetScale, + screen.GlobalRect().Angle().val(), -azimuth, + screen.GtoP(userPos), pixelPos, screen.PixelRect()); + anim->SetMaxDuration(kMaxAnimationTimeSec); + anim->SetOnStartAction(onStartHandler); + m_animationSystem.CombineAnimation(move(anim)); + return false; } - m_animation.reset(); - m2::PointD const center = FollowAndRotateAnimation::CalculateCenter(m_navigator.Screen(), userPos, pixelPos, -azimuth); + ResetCurrentAnimations(); + m2::PointD const center = MapFollowAnimation::CalculateCenter(m_navigator.Screen(), userPos, pixelPos, -azimuth); m_navigator.SetFromRect(m2::AnyRectD(center, -azimuth, targetLocalRect)); return true; } bool UserEventStream::FilterEventWhile3dAnimation(UserEvent::EEventType type) const { - return type != UserEvent::EVENT_RESIZE && - type != UserEvent::EVENT_SET_RECT; + return type != UserEvent::EVENT_RESIZE && type != UserEvent::EVENT_SET_RECT; } -void UserEventStream::SetEnable3dMode(double maxRotationAngle, double angleFOV, bool isAnim, bool & viewportChanged) +void UserEventStream::SetEnable3dMode(double maxRotationAngle, double angleFOV, + bool isAnim, bool immediatelyStart) { - bool const finishAnimation = m_animation != nullptr && m_animation->GetType() == ModelViewAnimationType::Default; - ResetCurrentAnimation(finishAnimation); + ResetMapLinearAnimations(); double const startAngle = isAnim ? 0.0 : maxRotationAngle; - if (isAnim) + double const endAngle = maxRotationAngle; + + auto anim = make_unique_dp(startAngle, endAngle, angleFOV); + anim->SetOnStartAction([this, startAngle, endAngle, angleFOV](ref_ptr) { - double const endAngle = maxRotationAngle; - double const rotateDuration = PerspectiveAnimation::GetRotateDuration(startAngle, endAngle); - m_perspectiveAnimation.reset( - new PerspectiveAnimation(rotateDuration, 0.0 /* delay */, startAngle, endAngle)); - } - m_navigator.Enable3dMode(startAngle, maxRotationAngle, angleFOV); - viewportChanged = true; + m_perspectiveAnimation = true; + }); + anim->SetOnFinishAction([this](ref_ptr) + { + m_perspectiveAnimation = false; + }); + if (immediatelyStart) + m_animationSystem.CombineAnimation(move(anim)); + else + m_animationSystem.PushAnimation(move(anim)); } void UserEventStream::SetDisable3dModeAnimation() { - bool const finishAnimation = m_animation != nullptr && m_animation->GetType() == ModelViewAnimationType::Default; - ResetCurrentAnimation(finishAnimation); + ResetMapLinearAnimations(); double const startAngle = m_navigator.Screen().GetRotationAngle(); double const endAngle = 0.0; - double const rotateDuration = PerspectiveAnimation::GetRotateDuration(startAngle, endAngle); - m_perspectiveAnimation.reset(new PerspectiveAnimation(rotateDuration, 0.0 /* delay */, startAngle, endAngle)); + + auto anim = make_unique_dp(startAngle, endAngle, m_navigator.Screen().GetAngleFOV()); + anim->SetOnStartAction([this](ref_ptr) + { + m_perspectiveAnimation = true; + }); + anim->SetOnFinishAction([this](ref_ptr) + { + m_perspectiveAnimation = false; + }); + m_animationSystem.CombineAnimation(move(anim)); } -void UserEventStream::ResetCurrentAnimation(bool finishAnimation) +void UserEventStream::ResetCurrentAnimations() { - if (m_animation) + uint32_t const kMapPlaneIndex = 0; + ResetCurrentAnimations(false /* finishAll */, make_pair(false, kMapPlaneIndex) /* finishAnim */); +} + +void UserEventStream::ResetMapLinearAnimations() +{ + uint32_t const kMapLinearIndex = static_cast(Animation::MapLinear); + ResetCurrentAnimations(false /* finishAll */, make_pair(true, kMapLinearIndex)/* finishAnim */); +} + +void UserEventStream::ResetCurrentAnimations(bool finishAll, pair finishAnim) +{ + bool const hasAnimations = m_animationSystem.HasAnimations(); + + if (finishAnim.first) + m_animationSystem.FinishAnimations((Animation::Type)finishAnim.second, true /* rewind */); + else + m_animationSystem.FinishObjectAnimations(Animation::MapPlane, finishAll); + + if (hasAnimations) { - m2::AnyRectD const rect = finishAnimation ? m_animation->GetTargetRect(GetCurrentScreen()) : - m_animation->GetCurrentRect(GetCurrentScreen()); - m_navigator.SetFromRect(rect); - m_animation.reset(); + m2::AnyRectD rect; + if (m_animationSystem.GetRect(GetCurrentScreen(), rect)) + m_navigator.SetFromRect(rect); } } @@ -571,10 +663,8 @@ m2::AnyRectD UserEventStream::GetCurrentRect() const m2::AnyRectD UserEventStream::GetTargetRect() const { - if (m_animation) - return m_animation->GetTargetRect(GetCurrentScreen()); - else - return GetCurrentRect(); + // TODO: Calculate target rect inside the animation system. + return GetCurrentRect(); } bool UserEventStream::ProcessTouch(TouchEvent const & touch) @@ -869,7 +959,10 @@ void UserEventStream::BeginDrag(Touch const & t, double timestamp) m_navigator.StartDrag(t.m_location); if (m_kineticScrollEnabled && !m_scroller.IsActive()) + { + ResetCurrentAnimations(); m_scroller.InitGrab(m_navigator.Screen(), timestamp); + } } void UserEventStream::Drag(Touch const & t, double timestamp) @@ -901,9 +994,16 @@ bool UserEventStream::EndDrag(Touch const & t, bool cancelled) if (m_kineticScrollEnabled && m_kineticTimer.TimeElapsedAs().count() >= kKineticDelayMs) { - m_animation = m_scroller.CreateKineticAnimation(m_navigator.Screen()); - if (m_listener) - m_listener->OnAnimationStarted(make_ref(m_animation)); + drape_ptr anim = m_scroller.CreateKineticAnimation(m_navigator.Screen()); + if (anim != nullptr) + { + anim->SetOnStartAction([this](ref_ptr animation) + { + if (m_listener) + m_listener->OnAnimationStarted(animation); + }); + m_animationSystem.CombineAnimation(move(anim)); + } m_scroller.CancelGrab(); return false; } @@ -1130,7 +1230,7 @@ bool UserEventStream::IsWaitingForActionCompletion() const bool UserEventStream::IsInPerspectiveAnimation() const { - return m_perspectiveAnimation != nullptr; + return m_perspectiveAnimation; } void UserEventStream::SetKineticScrollEnabled(bool enabled) diff --git a/drape_frontend/user_event_stream.hpp b/drape_frontend/user_event_stream.hpp index e248910851..6c370651fa 100644 --- a/drape_frontend/user_event_stream.hpp +++ b/drape_frontend/user_event_stream.hpp @@ -2,8 +2,6 @@ #include "drape_frontend/kinetic_scroller.hpp" #include "drape_frontend/navigator.hpp" -#include "drape_frontend/animation/model_view_animation.hpp" -#include "drape_frontend/animation/perspective_animation.hpp" #include "drape/pointers.hpp" @@ -257,7 +255,7 @@ public: virtual void CorrectScalePoint(m2::PointD & pt1, m2::PointD & pt2) const = 0; virtual void OnScaleEnded() = 0; - virtual void OnAnimationStarted(ref_ptr anim) = 0; + virtual void OnAnimationStarted(ref_ptr anim) = 0; }; UserEventStream(); @@ -300,17 +298,16 @@ public: #endif private: - using TAnimationCreator = function; bool SetScale(m2::PointD const & pxScaleCenter, double factor, bool isAnim); bool SetCenter(m2::PointD const & center, int zoom, bool isAnim); bool SetRect(m2::RectD rect, int zoom, bool applyRotation, bool isAnim); bool SetRect(m2::AnyRectD const & rect, bool isAnim); - bool SetRect(m2::AnyRectD const & rect, bool isAnim, TAnimationCreator const & animCreator); bool SetFollowAndRotate(m2::PointD const & userPos, m2::PointD const & pixelPos, double azimuth, int preferredZoomLevel, bool isAnim); bool FilterEventWhile3dAnimation(UserEvent::EEventType type) const; - void SetEnable3dMode(double maxRotationAngle, double angleFOV, bool isAnim, bool & viewportChanged); + void SetEnable3dMode(double maxRotationAngle, double angleFOV, + bool isAnim, bool immediatelyStart); void SetDisable3dModeAnimation(); m2::AnyRectD GetCurrentRect() const; @@ -352,7 +349,10 @@ private: void EndFilter(Touch const & t); void CancelFilter(Touch const & t); - void ResetCurrentAnimation(bool finishAnimation = false); + void ApplyAnimations(bool & modelViewChanged, bool & viewportChanged); + void ResetCurrentAnimations(); + void ResetMapLinearAnimations(); + void ResetCurrentAnimations(bool finishAll, pair finishAnim); list m_events; mutable mutex m_lock; @@ -374,10 +374,9 @@ private: array m_touches; - drape_ptr m_animation; + AnimationSystem & m_animationSystem; - unique_ptr m_perspectiveAnimation; - bool m_pendingPerspective = false; + bool m_perspectiveAnimation = false; unique_ptr m_pendingEvent; double m_discardedFOV = 0.0; double m_discardedAngle = 0.0; diff --git a/xcode/drape_frontend/drape_frontend.xcodeproj/project.pbxproj b/xcode/drape_frontend/drape_frontend.xcodeproj/project.pbxproj index 102e59d606..4b4ddde44f 100644 --- a/xcode/drape_frontend/drape_frontend.xcodeproj/project.pbxproj +++ b/xcode/drape_frontend/drape_frontend.xcodeproj/project.pbxproj @@ -9,6 +9,9 @@ /* Begin PBXBuildFile section */ 3492DA0E1CA2D9BF00C1F3B3 /* animation_utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3492DA0C1CA2D9BF00C1F3B3 /* animation_utils.cpp */; }; 3492DA0F1CA2D9BF00C1F3B3 /* animation_utils.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3492DA0D1CA2D9BF00C1F3B3 /* animation_utils.hpp */; }; + 454C19BB1CCE3EC0002A2C86 /* animation_constants.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 454C19B81CCE3EC0002A2C86 /* animation_constants.hpp */; }; + 454C19BC1CCE3EC0002A2C86 /* animation_system.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 454C19B91CCE3EC0002A2C86 /* animation_system.cpp */; }; + 454C19BD1CCE3EC0002A2C86 /* animation_system.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 454C19BA1CCE3EC0002A2C86 /* animation_system.hpp */; }; 56BF56DA1C7608C0006DD7CB /* choose_position_mark.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56BF56D81C7608C0006DD7CB /* choose_position_mark.cpp */; }; 56BF56DB1C7608C0006DD7CB /* choose_position_mark.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56BF56D91C7608C0006DD7CB /* choose_position_mark.hpp */; }; 56D545661C74A44900E3719C /* overlay_batcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56D545641C74A44900E3719C /* overlay_batcher.cpp */; }; @@ -102,8 +105,6 @@ 670948171BDF9C39005014C0 /* interpolation_holder.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 6709480A1BDF9C39005014C0 /* interpolation_holder.hpp */; }; 670948181BDF9C39005014C0 /* interpolations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6709480B1BDF9C39005014C0 /* interpolations.cpp */; }; 670948191BDF9C39005014C0 /* interpolations.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 6709480C1BDF9C39005014C0 /* interpolations.hpp */; }; - 6709481A1BDF9C39005014C0 /* model_view_animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6709480D1BDF9C39005014C0 /* model_view_animation.cpp */; }; - 6709481B1BDF9C39005014C0 /* model_view_animation.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 6709480E1BDF9C39005014C0 /* model_view_animation.hpp */; }; 6709481C1BDF9C39005014C0 /* opacity_animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6709480F1BDF9C39005014C0 /* opacity_animation.cpp */; }; 6709481D1BDF9C39005014C0 /* opacity_animation.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 670948101BDF9C39005014C0 /* opacity_animation.hpp */; }; 6709481E1BDF9C39005014C0 /* show_hide_animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 670948111BDF9C39005014C0 /* show_hide_animation.cpp */; }; @@ -156,8 +157,6 @@ 6743D3701C3A9F090095054B /* framebuffer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 6743D36A1C3A9F090095054B /* framebuffer.hpp */; }; 6743D3711C3A9F090095054B /* transparent_layer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6743D36B1C3A9F090095054B /* transparent_layer.cpp */; }; 6743D3721C3A9F090095054B /* transparent_layer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 6743D36C1C3A9F090095054B /* transparent_layer.hpp */; }; - 6743D3751C3A9F530095054B /* perspective_animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6743D3731C3A9F530095054B /* perspective_animation.cpp */; }; - 6743D3761C3A9F530095054B /* perspective_animation.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 6743D3741C3A9F530095054B /* perspective_animation.hpp */; }; 675D21661BFB785900717E4F /* ruler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 670948331BDF9C48005014C0 /* ruler.cpp */; }; 675D218C1BFB871D00717E4F /* proto_to_styles.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 675D21851BFB871D00717E4F /* proto_to_styles.cpp */; }; 675D218D1BFB871D00717E4F /* proto_to_styles.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 675D21861BFB871D00717E4F /* proto_to_styles.hpp */; }; @@ -187,6 +186,9 @@ /* Begin PBXFileReference section */ 3492DA0C1CA2D9BF00C1F3B3 /* animation_utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = animation_utils.cpp; sourceTree = ""; }; 3492DA0D1CA2D9BF00C1F3B3 /* animation_utils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = animation_utils.hpp; sourceTree = ""; }; + 454C19B81CCE3EC0002A2C86 /* animation_constants.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = animation_constants.hpp; sourceTree = ""; }; + 454C19B91CCE3EC0002A2C86 /* animation_system.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = animation_system.cpp; sourceTree = ""; }; + 454C19BA1CCE3EC0002A2C86 /* animation_system.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = animation_system.hpp; sourceTree = ""; }; 56BF56D81C7608C0006DD7CB /* choose_position_mark.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = choose_position_mark.cpp; sourceTree = ""; }; 56BF56D91C7608C0006DD7CB /* choose_position_mark.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = choose_position_mark.hpp; sourceTree = ""; }; 56D545641C74A44900E3719C /* overlay_batcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = overlay_batcher.cpp; sourceTree = ""; }; @@ -289,8 +291,6 @@ 6709480A1BDF9C39005014C0 /* interpolation_holder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = interpolation_holder.hpp; sourceTree = ""; }; 6709480B1BDF9C39005014C0 /* interpolations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = interpolations.cpp; sourceTree = ""; }; 6709480C1BDF9C39005014C0 /* interpolations.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = interpolations.hpp; sourceTree = ""; }; - 6709480D1BDF9C39005014C0 /* model_view_animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = model_view_animation.cpp; sourceTree = ""; }; - 6709480E1BDF9C39005014C0 /* model_view_animation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = model_view_animation.hpp; sourceTree = ""; }; 6709480F1BDF9C39005014C0 /* opacity_animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opacity_animation.cpp; sourceTree = ""; }; 670948101BDF9C39005014C0 /* opacity_animation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = opacity_animation.hpp; sourceTree = ""; }; 670948111BDF9C39005014C0 /* show_hide_animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = show_hide_animation.cpp; sourceTree = ""; }; @@ -347,8 +347,6 @@ 6743D36A1C3A9F090095054B /* framebuffer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = framebuffer.hpp; sourceTree = ""; }; 6743D36B1C3A9F090095054B /* transparent_layer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = transparent_layer.cpp; sourceTree = ""; }; 6743D36C1C3A9F090095054B /* transparent_layer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = transparent_layer.hpp; sourceTree = ""; }; - 6743D3731C3A9F530095054B /* perspective_animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = perspective_animation.cpp; sourceTree = ""; }; - 6743D3741C3A9F530095054B /* perspective_animation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = perspective_animation.hpp; sourceTree = ""; }; 675D21851BFB871D00717E4F /* proto_to_styles.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = proto_to_styles.cpp; sourceTree = ""; }; 675D21861BFB871D00717E4F /* proto_to_styles.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = proto_to_styles.hpp; sourceTree = ""; }; 675D21871BFB871D00717E4F /* rect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rect.h; sourceTree = ""; }; @@ -396,6 +394,9 @@ 670947411BDF9B99005014C0 /* drape_frontend */ = { isa = PBXGroup; children = ( + 454C19B81CCE3EC0002A2C86 /* animation_constants.hpp */, + 454C19B91CCE3EC0002A2C86 /* animation_system.cpp */, + 454C19BA1CCE3EC0002A2C86 /* animation_system.hpp */, 3492DA0C1CA2D9BF00C1F3B3 /* animation_utils.cpp */, 3492DA0D1CA2D9BF00C1F3B3 /* animation_utils.hpp */, 56D545641C74A44900E3719C /* overlay_batcher.cpp */, @@ -519,16 +520,12 @@ 670948051BDF9C0D005014C0 /* animation */ = { isa = PBXGroup; children = ( - 6743D3731C3A9F530095054B /* perspective_animation.cpp */, - 6743D3741C3A9F530095054B /* perspective_animation.hpp */, 670948071BDF9C39005014C0 /* base_interpolator.cpp */, 670948081BDF9C39005014C0 /* base_interpolator.hpp */, 670948091BDF9C39005014C0 /* interpolation_holder.cpp */, 6709480A1BDF9C39005014C0 /* interpolation_holder.hpp */, 6709480B1BDF9C39005014C0 /* interpolations.cpp */, 6709480C1BDF9C39005014C0 /* interpolations.hpp */, - 6709480D1BDF9C39005014C0 /* model_view_animation.cpp */, - 6709480E1BDF9C39005014C0 /* model_view_animation.hpp */, 6709480F1BDF9C39005014C0 /* opacity_animation.cpp */, 670948101BDF9C39005014C0 /* opacity_animation.hpp */, 670948111BDF9C39005014C0 /* show_hide_animation.cpp */, @@ -642,7 +639,6 @@ 675D218D1BFB871D00717E4F /* proto_to_styles.hpp in Headers */, 670947951BDF9BE1005014C0 /* line_shape_helper.hpp in Headers */, 670948501BDF9C48005014C0 /* skin.hpp in Headers */, - 6709481B1BDF9C39005014C0 /* model_view_animation.hpp in Headers */, 56D545671C74A44900E3719C /* overlay_batcher.hpp in Headers */, 670947B21BDF9BE1005014C0 /* read_mwm_task.hpp in Headers */, 670947C91BDF9BE1005014C0 /* text_shape.hpp in Headers */, @@ -659,13 +655,14 @@ 670948731BDF9C7F005014C0 /* frame_image.hpp in Headers */, 3492DA0F1CA2D9BF00C1F3B3 /* animation_utils.hpp in Headers */, 670947FB1BDF9BF5005014C0 /* backend_renderer.hpp in Headers */, + 454C19BD1CCE3EC0002A2C86 /* animation_system.hpp in Headers */, 670947DF1BDF9BE1005014C0 /* visual_params.hpp in Headers */, 670948171BDF9C39005014C0 /* interpolation_holder.hpp in Headers */, 670947FD1BDF9BF5005014C0 /* base_renderer.hpp in Headers */, 670947D51BDF9BE1005014C0 /* tile_utils.hpp in Headers */, 670947D91BDF9BE1005014C0 /* user_mark_shapes.hpp in Headers */, + 454C19BB1CCE3EC0002A2C86 /* animation_constants.hpp in Headers */, 6709481F1BDF9C39005014C0 /* show_hide_animation.hpp in Headers */, - 6743D3761C3A9F530095054B /* perspective_animation.hpp in Headers */, 670E393B1C46C59000E9C0A6 /* batch_merge_helper.hpp in Headers */, 670947931BDF9BE1005014C0 /* kinetic_scroller.hpp in Headers */, 670947E71BDF9BEC005014C0 /* frontend_renderer.hpp in Headers */, @@ -770,6 +767,7 @@ 67E91C791BDFC85E005CEE88 /* base_renderer.cpp in Sources */, 67E91C7A1BDFC85E005CEE88 /* circle_shape.cpp in Sources */, 67E91C7B1BDFC85E005CEE88 /* my_position_controller.cpp in Sources */, + 454C19BC1CCE3EC0002A2C86 /* animation_system.cpp in Sources */, 67E91C7C1BDFC85E005CEE88 /* selection_shape.cpp in Sources */, 67E91C7D1BDFC85E005CEE88 /* user_marks_provider.cpp in Sources */, 67E91C7E1BDFC85E005CEE88 /* visual_params.cpp in Sources */, @@ -796,7 +794,6 @@ 670948711BDF9C7F005014C0 /* feature_styler.cpp in Sources */, 670948471BDF9C48005014C0 /* layer_render.cpp in Sources */, 6709486C1BDF9C7F005014C0 /* cpu_drawer.cpp in Sources */, - 6743D3751C3A9F530095054B /* perspective_animation.cpp in Sources */, 670947C41BDF9BE1005014C0 /* text_handle.cpp in Sources */, 670947BD1BDF9BE1005014C0 /* rule_drawer.cpp in Sources */, 6709481E1BDF9C39005014C0 /* show_hide_animation.cpp in Sources */, @@ -804,7 +801,6 @@ 6709483D1BDF9C48005014C0 /* copyright_label.cpp in Sources */, 670947C81BDF9BE1005014C0 /* text_shape.cpp in Sources */, 670947CC1BDF9BE1005014C0 /* tile_info.cpp in Sources */, - 6709481A1BDF9C39005014C0 /* model_view_animation.cpp in Sources */, 670947961BDF9BE1005014C0 /* line_shape.cpp in Sources */, 670948681BDF9C7F005014C0 /* agg_curves.cpp in Sources */, 670947A91BDF9BE1005014C0 /* path_symbol_shape.cpp in Sources */,