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