Fixed follow-and-rotate animation

This commit is contained in:
r.kuznetsov 2015-09-11 15:18:02 +03:00
parent 095da84f12
commit a0b0b029a0
8 changed files with 174 additions and 65 deletions

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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;