forked from organicmaps/organicmaps
Fixed follow-and-rotate animation
This commit is contained in:
parent
095da84f12
commit
a0b0b029a0
8 changed files with 174 additions and 65 deletions
|
@ -27,37 +27,6 @@ m2::AnyRectD ModelViewAnimation::GetTargetRect(ScreenBase const & screen) const
|
|||
return GetRect(GetDuration());
|
||||
}
|
||||
|
||||
FixedPointAnimation::FixedPointAnimation(m2::AnyRectD const & startRect, m2::AnyRectD const & endRect,
|
||||
double aDuration, double mDuration, double sDuration,
|
||||
m2::PointD const & pixelPoint, m2::PointD const & globalPoint)
|
||||
: ModelViewAnimation(startRect, endRect, aDuration, mDuration, sDuration)
|
||||
, m_pixelPoint(pixelPoint)
|
||||
, m_globalPoint(globalPoint)
|
||||
{
|
||||
}
|
||||
|
||||
void FixedPointAnimation::ApplyFixedPoint(ScreenBase const & screen, m2::AnyRectD & rect) const
|
||||
{
|
||||
ScreenBase s = screen;
|
||||
s.SetFromRect(rect);
|
||||
m2::PointD const p = s.PtoG(m_pixelPoint);
|
||||
rect.Offset(m_globalPoint - p);
|
||||
}
|
||||
|
||||
m2::AnyRectD FixedPointAnimation::GetCurrentRect(ScreenBase const & screen) const
|
||||
{
|
||||
m2::AnyRectD r = GetRect(GetElapsedTime());
|
||||
ApplyFixedPoint(screen, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
m2::AnyRectD FixedPointAnimation::GetTargetRect(ScreenBase const & screen) const
|
||||
{
|
||||
m2::AnyRectD r = GetRect(GetDuration());
|
||||
ApplyFixedPoint(screen, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
@ -122,4 +91,68 @@ double ModelViewAnimation::GetScaleDuration(double startSize, double endSize)
|
|||
return CalcAnimSpeedDuration(endSize / startSize, pixelSpeed);
|
||||
}
|
||||
|
||||
FixedPointAnimation::FixedPointAnimation(m2::AnyRectD const & startRect, m2::AnyRectD const & endRect,
|
||||
double aDuration, double mDuration, double sDuration,
|
||||
m2::PointD const & pixelPoint, m2::PointD const & globalPoint)
|
||||
: ModelViewAnimation(startRect, endRect, aDuration, mDuration, sDuration)
|
||||
, m_pixelPoint(pixelPoint)
|
||||
, m_globalPoint(globalPoint)
|
||||
{
|
||||
}
|
||||
|
||||
void FixedPointAnimation::ApplyFixedPoint(ScreenBase const & screen, m2::AnyRectD & rect) const
|
||||
{
|
||||
ScreenBase s = screen;
|
||||
s.SetFromRect(rect);
|
||||
m2::PointD const p = s.PtoG(m_pixelPoint);
|
||||
rect.Offset(m_globalPoint - p);
|
||||
}
|
||||
|
||||
m2::AnyRectD FixedPointAnimation::GetCurrentRect(ScreenBase const & screen) const
|
||||
{
|
||||
m2::AnyRectD r = GetRect(GetElapsedTime());
|
||||
ApplyFixedPoint(screen, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
m2::AnyRectD FixedPointAnimation::GetTargetRect(ScreenBase const & screen) const
|
||||
{
|
||||
m2::AnyRectD r = GetRect(GetDuration());
|
||||
ApplyFixedPoint(screen, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
FollowAndRotateAnimation::FollowAndRotateAnimation(m2::AnyRectD const & startRect, m2::PointD const & userPos,
|
||||
double newCenterOffset, double oldCenterOffset,
|
||||
double azimuth, double duration)
|
||||
: BaseModelViewAnimation(duration)
|
||||
, m_angleInterpolator(startRect.Angle().val(), azimuth)
|
||||
, m_rect(startRect.GetLocalRect())
|
||||
, m_userPos(userPos)
|
||||
, m_newCenterOffset(newCenterOffset)
|
||||
, m_oldCenterOffset(oldCenterOffset)
|
||||
{}
|
||||
|
||||
m2::AnyRectD FollowAndRotateAnimation::GetCurrentRect(ScreenBase const & screen) const
|
||||
{
|
||||
return GetRect(GetElapsedTime());
|
||||
}
|
||||
|
||||
m2::AnyRectD FollowAndRotateAnimation::GetTargetRect(ScreenBase const & screen) const
|
||||
{
|
||||
return GetRect(GetDuration());
|
||||
}
|
||||
|
||||
m2::AnyRectD FollowAndRotateAnimation::GetRect(double elapsedTime) const
|
||||
{
|
||||
double const t = GetSafeT(elapsedTime, GetDuration());
|
||||
double const azimuth = m_angleInterpolator.Interpolate(t);
|
||||
double const centerOffset = InterpolateDouble(m_oldCenterOffset, m_newCenterOffset, t);
|
||||
|
||||
m2::PointD viewVector = m_userPos.Move(1.0, azimuth + math::pi2) - m_userPos;
|
||||
viewVector.Normalize();
|
||||
m2::PointD centerPos = m_userPos + (viewVector * centerOffset);
|
||||
return m2::AnyRectD(centerPos, azimuth, m_rect);
|
||||
}
|
||||
|
||||
} // namespace df
|
||||
|
|
|
@ -63,4 +63,24 @@ private:
|
|||
m2::PointD m_globalPoint;
|
||||
};
|
||||
|
||||
class FollowAndRotateAnimation : public BaseModelViewAnimation
|
||||
{
|
||||
public:
|
||||
FollowAndRotateAnimation(m2::AnyRectD const & startRect, m2::PointD const & userPos,
|
||||
double newCenterOffset, double oldCenterOffset,
|
||||
double azimuth, double duration);
|
||||
|
||||
m2::AnyRectD GetCurrentRect(ScreenBase const & screen) const override;
|
||||
m2::AnyRectD GetTargetRect(ScreenBase const & screen) const override;
|
||||
|
||||
private:
|
||||
m2::AnyRectD GetRect(double elapsedTime) const;
|
||||
|
||||
InerpolateAngle m_angleInterpolator;
|
||||
m2::RectD m_rect;
|
||||
m2::PointD m_userPos;
|
||||
double m_newCenterOffset;
|
||||
double m_oldCenterOffset;
|
||||
};
|
||||
|
||||
} // namespace df
|
||||
|
|
|
@ -897,24 +897,33 @@ void FrontendRenderer::ChangeModelView(m2::RectD const & rect)
|
|||
}
|
||||
|
||||
void FrontendRenderer::ChangeModelView(m2::PointD const & userPos, double azimuth,
|
||||
m2::PointD const & pxZero, bool animate)
|
||||
m2::PointD const & pxZero)
|
||||
{
|
||||
ScreenBase const & screen = m_userEventStream.GetCurrentScreen();
|
||||
m2::RectD const & pixelRect = screen.PixelRect();
|
||||
|
||||
m2::PointD formingVector = pixelRect.Center() - pxZero;
|
||||
formingVector.x /= pixelRect.SizeX();
|
||||
formingVector.y /= pixelRect.SizeY();
|
||||
|
||||
m2::AnyRectD targetRect = m_userEventStream.GetTargetRect();
|
||||
formingVector.x *= targetRect.GetLocalRect().SizeX();
|
||||
formingVector.y *= targetRect.GetLocalRect().SizeY();
|
||||
|
||||
auto calculateOffset = [&pixelRect, &targetRect](m2::PointD const & pixelPos)
|
||||
{
|
||||
m2::PointD formingVector = pixelPos;
|
||||
formingVector.x /= pixelRect.SizeX();
|
||||
formingVector.y /= pixelRect.SizeY();
|
||||
formingVector.x *= targetRect.GetLocalRect().SizeX();
|
||||
formingVector.y *= targetRect.GetLocalRect().SizeY();
|
||||
|
||||
return formingVector.Length();
|
||||
};
|
||||
|
||||
double const newCenterOffset = calculateOffset(pixelRect.Center() - pxZero);
|
||||
double const oldCenterOffset = calculateOffset(pixelRect.Center() - screen.GtoP(userPos));
|
||||
|
||||
m2::PointD viewVector = userPos.Move(1.0, -azimuth + math::pi2) - userPos;
|
||||
viewVector.Normalize();
|
||||
|
||||
AddUserEvent(SetAnyRectEvent(m2::AnyRectD(userPos + (viewVector * formingVector.Length()), -azimuth,
|
||||
targetRect.GetLocalRect()), animate));
|
||||
m2::AnyRectD const rect = m2::AnyRectD(viewVector * newCenterOffset + userPos,
|
||||
-azimuth, targetRect.GetLocalRect());
|
||||
|
||||
AddUserEvent(FollowAndRotateEvent(rect, userPos, newCenterOffset, oldCenterOffset, -azimuth, true));
|
||||
}
|
||||
|
||||
ScreenBase const & FrontendRenderer::UpdateScene(bool & modelViewChanged)
|
||||
|
|
|
@ -119,7 +119,7 @@ public:
|
|||
void ChangeModelView(m2::PointD const & center) override;
|
||||
void ChangeModelView(double azimuth) override;
|
||||
void ChangeModelView(m2::RectD const & rect) override;
|
||||
void ChangeModelView(m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero, bool animate) override;
|
||||
void ChangeModelView(m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero) override;
|
||||
|
||||
protected:
|
||||
virtual void AcceptMessage(ref_ptr<Message> message);
|
||||
|
|
|
@ -467,26 +467,19 @@ void MyPositionController::ChangeModelView(m2::RectD const & rect)
|
|||
}
|
||||
|
||||
void MyPositionController::ChangeModelView(m2::PointD const & userPos, double azimuth,
|
||||
m2::PointD const & pxZero, bool animate)
|
||||
m2::PointD const & pxZero)
|
||||
{
|
||||
if (m_listener)
|
||||
m_listener->ChangeModelView(userPos, azimuth, pxZero, animate);
|
||||
m_listener->ChangeModelView(userPos, azimuth, pxZero);
|
||||
}
|
||||
|
||||
void MyPositionController::Follow()
|
||||
{
|
||||
location::EMyPositionMode currentMode = GetMode();
|
||||
if (currentMode == location::MODE_FOLLOW)
|
||||
{
|
||||
ChangeModelView(m_position);
|
||||
}
|
||||
else if (currentMode == location::MODE_ROTATE_AND_FOLLOW)
|
||||
{
|
||||
bool animate = true;
|
||||
if (!IsInRouting() && m_anim != nullptr)
|
||||
animate = !m_anim->IsRotatingActive();
|
||||
ChangeModelView(m_position, m_drawDirection, GetRaFPixelBinding(), animate);
|
||||
}
|
||||
ChangeModelView(m_position, m_drawDirection, GetRaFPixelBinding());
|
||||
}
|
||||
|
||||
m2::PointD MyPositionController::GetRaFPixelBinding() const
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
/// Somehow show map that "rect" will see
|
||||
virtual void ChangeModelView(m2::RectD const & rect) = 0;
|
||||
/// Show map where "usePos" (mercator) placed in "pxZero" on screen and map rotated around "userPos"
|
||||
virtual void ChangeModelView(m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero, bool animate) = 0;
|
||||
virtual void ChangeModelView(m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero) = 0;
|
||||
};
|
||||
|
||||
// Render bits
|
||||
|
@ -101,8 +101,7 @@ private:
|
|||
void ChangeModelView(m2::PointD const & center);
|
||||
void ChangeModelView(double azimuth);
|
||||
void ChangeModelView(m2::RectD const & rect);
|
||||
void ChangeModelView(m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero,
|
||||
bool animate);
|
||||
void ChangeModelView(m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero);
|
||||
|
||||
void Follow();
|
||||
m2::PointD GetRaFPixelBinding() const;
|
||||
|
|
|
@ -16,10 +16,12 @@ namespace df
|
|||
namespace
|
||||
{
|
||||
|
||||
uint64_t const DOUBLE_TAP_PAUSE = 250;
|
||||
uint64_t const LONG_TOUCH_MS = 1000;
|
||||
uint64_t const kDoubleTapPauseMs = 250;
|
||||
uint64_t const kLongTouchMs = 1000;
|
||||
uint64_t const kKineticDelayMs = 500;
|
||||
|
||||
double const kMaxAnimationTime = 1.5; // in seconds
|
||||
|
||||
size_t GetValidTouchesCount(array<Touch, 2> const & touches)
|
||||
{
|
||||
size_t result = 0;
|
||||
|
@ -167,6 +169,12 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChange, bool &
|
|||
breakAnim = SetRect(dstRect, true);
|
||||
}
|
||||
break;
|
||||
case UserEvent::EVENT_FOLLOW_AND_ROTATE:
|
||||
breakAnim = SetFollowAndRotate(e.m_followAndRotate.m_targetRect, e.m_followAndRotate.m_userPos,
|
||||
e.m_followAndRotate.m_newCenterOffset,e.m_followAndRotate.m_oldCenterOffset,
|
||||
e.m_followAndRotate.m_azimuth, e.m_followAndRotate.m_isAnim);
|
||||
TouchCancel(m_touches, 0.0);
|
||||
break;
|
||||
default:
|
||||
ASSERT(false, ());
|
||||
break;
|
||||
|
@ -271,10 +279,7 @@ bool UserEventStream::SetRect(m2::AnyRectD const & rect, bool isAnim, TAnimation
|
|||
double const moveDuration = ModelViewAnimation::GetMoveDuration(startRect.GlobalZero(), rect.GlobalZero(), screen);
|
||||
double const scaleDuration = ModelViewAnimation::GetScaleDuration(startRect.GetLocalRect().SizeX(),
|
||||
rect.GetLocalRect().SizeX());
|
||||
|
||||
double const MAX_ANIMATION_TIME = 1.5; // in seconds
|
||||
|
||||
if (max(max(angleDuration, moveDuration), scaleDuration) < MAX_ANIMATION_TIME)
|
||||
if (max(max(angleDuration, moveDuration), scaleDuration) < kMaxAnimationTime)
|
||||
{
|
||||
ASSERT(animCreator != nullptr, ());
|
||||
animCreator(startRect, rect, angleDuration, moveDuration, scaleDuration);
|
||||
|
@ -290,6 +295,31 @@ bool UserEventStream::SetRect(m2::AnyRectD const & rect, bool isAnim, TAnimation
|
|||
return true;
|
||||
}
|
||||
|
||||
bool UserEventStream::SetFollowAndRotate(m2::AnyRectD const & rect, m2::PointD const & userPos,
|
||||
double newCenterOffset, double oldCenterOffset, double azimuth, bool isAnim)
|
||||
{
|
||||
if (isAnim)
|
||||
{
|
||||
ScreenBase const & screen = m_navigator.Screen();
|
||||
m2::AnyRectD const startRect = GetCurrentRect();
|
||||
double const angleDuration = ModelViewAnimation::GetRotateDuration(startRect.Angle().val(), azimuth);
|
||||
double const moveDuration = ModelViewAnimation::GetMoveDuration(startRect.GlobalZero(), rect.GlobalZero(), screen);
|
||||
double const duration = max(angleDuration, moveDuration);
|
||||
if (duration > 0.0 && duration < kMaxAnimationTime)
|
||||
{
|
||||
m_animation.reset(new FollowAndRotateAnimation(startRect, userPos, newCenterOffset, oldCenterOffset, azimuth, duration));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_animation.reset();
|
||||
}
|
||||
}
|
||||
|
||||
m_navigator.SetFromRect(rect);
|
||||
return true;
|
||||
}
|
||||
|
||||
m2::AnyRectD UserEventStream::GetCurrentRect() const
|
||||
{
|
||||
return m_navigator.Screen().GlobalRect();
|
||||
|
@ -615,7 +645,7 @@ void UserEventStream::BeginTapDetector()
|
|||
|
||||
void UserEventStream::DetectShortTap(Touch const & touch)
|
||||
{
|
||||
if (m_touchTimer.ElapsedMillis() > DOUBLE_TAP_PAUSE)
|
||||
if (m_touchTimer.ElapsedMillis() > kDoubleTapPauseMs)
|
||||
{
|
||||
m_state = STATE_EMPTY;
|
||||
if (m_listener)
|
||||
|
@ -626,7 +656,7 @@ void UserEventStream::DetectShortTap(Touch const & touch)
|
|||
void UserEventStream::DetectLongTap(Touch const & touch)
|
||||
{
|
||||
ASSERT_EQUAL(m_state, STATE_TAP_DETECTION, ());
|
||||
if (m_touchTimer.ElapsedMillis() > LONG_TOUCH_MS)
|
||||
if (m_touchTimer.ElapsedMillis() > kLongTouchMs)
|
||||
{
|
||||
TEST_CALL(LONG_TAP_DETECTED);
|
||||
m_state = STATE_EMPTY;
|
||||
|
@ -637,7 +667,7 @@ void UserEventStream::DetectLongTap(Touch const & touch)
|
|||
|
||||
bool UserEventStream::DetectDoubleTap(Touch const & touch)
|
||||
{
|
||||
if (m_state != STATE_WAIT_DOUBLE_TAP || m_touchTimer.ElapsedMillis() > DOUBLE_TAP_PAUSE)
|
||||
if (m_state != STATE_WAIT_DOUBLE_TAP || m_touchTimer.ElapsedMillis() > kDoubleTapPauseMs)
|
||||
return false;
|
||||
|
||||
m_state = STATE_EMPTY;
|
||||
|
|
|
@ -125,6 +125,26 @@ struct SetAnyRectEvent
|
|||
bool m_isAnim;
|
||||
};
|
||||
|
||||
struct FollowAndRotateEvent
|
||||
{
|
||||
FollowAndRotateEvent(m2::AnyRectD const & targetRect, m2::PointD const & userPos,
|
||||
double newCenterOffset, double oldCenterOffset, double azimuth, bool isAnim)
|
||||
: m_targetRect(targetRect)
|
||||
, m_userPos(userPos)
|
||||
, m_newCenterOffset(newCenterOffset)
|
||||
, m_oldCenterOffset(oldCenterOffset)
|
||||
, m_azimuth(azimuth)
|
||||
, m_isAnim(isAnim)
|
||||
{}
|
||||
|
||||
m2::AnyRectD m_targetRect;
|
||||
m2::PointD m_userPos;
|
||||
double m_newCenterOffset;
|
||||
double m_oldCenterOffset;
|
||||
double m_azimuth;
|
||||
bool m_isAnim;
|
||||
};
|
||||
|
||||
struct RotateEvent
|
||||
{
|
||||
RotateEvent(double targetAzimut) : m_targetAzimut(targetAzimut) {}
|
||||
|
@ -150,7 +170,8 @@ struct UserEvent
|
|||
EVENT_SET_RECT,
|
||||
EVENT_SET_ANY_RECT,
|
||||
EVENT_RESIZE,
|
||||
EVENT_ROTATE
|
||||
EVENT_ROTATE,
|
||||
EVENT_FOLLOW_AND_ROTATE
|
||||
};
|
||||
|
||||
UserEvent(TouchEvent const & e) : m_type(EVENT_TOUCH) { m_touchEvent = e; }
|
||||
|
@ -160,6 +181,7 @@ struct UserEvent
|
|||
UserEvent(SetAnyRectEvent const & e) : m_type(EVENT_SET_ANY_RECT) { m_anyRect = e; }
|
||||
UserEvent(ResizeEvent const & e) : m_type(EVENT_RESIZE) { m_resize = e; }
|
||||
UserEvent(RotateEvent const & e) : m_type(EVENT_ROTATE) { m_rotate = e; }
|
||||
UserEvent(FollowAndRotateEvent const & e) : m_type(EVENT_FOLLOW_AND_ROTATE) { m_followAndRotate = e; }
|
||||
|
||||
EEventType m_type;
|
||||
union
|
||||
|
@ -171,6 +193,7 @@ struct UserEvent
|
|||
SetAnyRectEvent m_anyRect;
|
||||
ResizeEvent m_resize;
|
||||
RotateEvent m_rotate;
|
||||
FollowAndRotateEvent m_followAndRotate;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -231,6 +254,8 @@ private:
|
|||
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::AnyRectD const & rect, m2::PointD const & userPos,
|
||||
double newCenterOffset, double oldCenterOffset, double azimuth, bool isAnim);
|
||||
|
||||
m2::AnyRectD GetCurrentRect() const;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue