2012-07-20 06:48:25 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set sw=4 ts=8 et tw=80 : */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#include "Axis.h"
|
|
|
|
#include "AsyncPanZoomController.h"
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace layers {
|
|
|
|
|
2012-07-23 01:43:37 +00:00
|
|
|
static const float EPSILON = 0.0001f;
|
2012-07-20 06:48:25 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Maximum acceleration that can happen between two frames. Velocity is
|
|
|
|
* throttled if it's above this. This may happen if a time delta is very low,
|
|
|
|
* or we get a touch point very far away from the previous position for some
|
|
|
|
* reason.
|
|
|
|
*/
|
2012-07-23 01:43:37 +00:00
|
|
|
static const float MAX_EVENT_ACCELERATION = 0.5f;
|
2012-07-20 06:48:25 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Amount of friction applied during flings when going above
|
|
|
|
* VELOCITY_THRESHOLD.
|
|
|
|
*/
|
2012-07-23 01:43:37 +00:00
|
|
|
static const float FLING_FRICTION_FAST = 0.010f;
|
2012-07-20 06:48:25 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Amount of friction applied during flings when going below
|
|
|
|
* VELOCITY_THRESHOLD.
|
|
|
|
*/
|
2012-07-23 01:43:37 +00:00
|
|
|
static const float FLING_FRICTION_SLOW = 0.008f;
|
2012-07-20 06:48:25 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Maximum velocity before fling friction increases.
|
|
|
|
*/
|
2012-07-23 01:43:37 +00:00
|
|
|
static const float VELOCITY_THRESHOLD = 0.5f;
|
2012-07-20 06:48:25 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* When flinging, if the velocity goes below this number, we just stop the
|
|
|
|
* animation completely. This is to prevent asymptotically approaching 0
|
|
|
|
* velocity and rerendering unnecessarily.
|
|
|
|
*/
|
2012-07-23 01:43:37 +00:00
|
|
|
static const float FLING_STOPPED_THRESHOLD = 0.01f;
|
2012-07-20 06:48:25 +00:00
|
|
|
|
|
|
|
Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
|
|
|
|
: mPos(0.0f),
|
|
|
|
mVelocity(0.0f),
|
|
|
|
mAsyncPanZoomController(aAsyncPanZoomController)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-07-23 01:43:37 +00:00
|
|
|
void Axis::UpdateWithTouchAtDevicePoint(PRInt32 aPos, const TimeDuration& aTimeDelta) {
|
|
|
|
float newVelocity = (mPos - aPos) / aTimeDelta.ToMilliseconds();
|
2012-07-20 06:48:25 +00:00
|
|
|
|
2012-07-23 01:43:37 +00:00
|
|
|
bool curVelocityIsLow = fabsf(newVelocity) < 0.01f;
|
2012-07-20 06:48:25 +00:00
|
|
|
bool directionChange = (mVelocity > 0) != (newVelocity != 0);
|
|
|
|
|
|
|
|
// If a direction change has happened, or the current velocity due to this new
|
|
|
|
// touch is relatively low, then just apply it. If not, throttle it.
|
|
|
|
if (curVelocityIsLow || (directionChange && fabs(newVelocity) - EPSILON <= 0.0f)) {
|
|
|
|
mVelocity = newVelocity;
|
|
|
|
} else {
|
2012-07-23 01:43:37 +00:00
|
|
|
float maxChange = fabsf(mVelocity * aTimeDelta.ToMilliseconds() * MAX_EVENT_ACCELERATION);
|
2012-07-20 06:48:25 +00:00
|
|
|
mVelocity = NS_MIN(mVelocity + maxChange, NS_MAX(mVelocity - maxChange, newVelocity));
|
|
|
|
}
|
|
|
|
|
|
|
|
mVelocity = newVelocity;
|
|
|
|
mPos = aPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Axis::StartTouch(PRInt32 aPos) {
|
|
|
|
mStartPos = aPos;
|
|
|
|
mPos = aPos;
|
|
|
|
mVelocity = 0.0f;
|
|
|
|
}
|
|
|
|
|
2012-07-23 01:43:37 +00:00
|
|
|
PRInt32 Axis::GetDisplacementForDuration(float aScale, const TimeDuration& aDelta) {
|
|
|
|
PRInt32 displacement = NS_lround(mVelocity * aScale * aDelta.ToMilliseconds());
|
2012-07-20 06:48:25 +00:00
|
|
|
// If this displacement will cause an overscroll, throttle it. Can potentially
|
|
|
|
// bring it to 0 even if the velocity is high.
|
|
|
|
if (DisplacementWillOverscroll(displacement) != OVERSCROLL_NONE) {
|
|
|
|
displacement -= DisplacementWillOverscrollAmount(displacement);
|
|
|
|
}
|
|
|
|
return displacement;
|
|
|
|
}
|
|
|
|
|
|
|
|
float Axis::PanDistance() {
|
|
|
|
return fabsf(mPos - mStartPos);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Axis::StopTouch() {
|
|
|
|
mVelocity = 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Axis::FlingApplyFrictionOrCancel(const TimeDuration& aDelta) {
|
|
|
|
if (fabsf(mVelocity) <= FLING_STOPPED_THRESHOLD) {
|
|
|
|
// If the velocity is very low, just set it to 0 and stop the fling,
|
|
|
|
// otherwise we'll just asymptotically approach 0 and the user won't
|
|
|
|
// actually see any changes.
|
|
|
|
mVelocity = 0.0f;
|
|
|
|
return false;
|
|
|
|
} else if (fabsf(mVelocity) >= VELOCITY_THRESHOLD) {
|
|
|
|
mVelocity *= 1.0f - FLING_FRICTION_FAST * aDelta.ToMilliseconds();
|
|
|
|
} else {
|
|
|
|
mVelocity *= 1.0f - FLING_FRICTION_SLOW * aDelta.ToMilliseconds();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Axis::Overscroll Axis::GetOverscroll() {
|
|
|
|
// If the current pan takes the viewport to the left of or above the current
|
|
|
|
// page rect.
|
|
|
|
bool minus = GetOrigin() < GetPageStart();
|
|
|
|
// If the current pan takes the viewport to the right of or below the current
|
|
|
|
// page rect.
|
|
|
|
bool plus = GetViewportEnd() > GetPageEnd();
|
|
|
|
if (minus && plus) {
|
|
|
|
return OVERSCROLL_BOTH;
|
|
|
|
}
|
|
|
|
if (minus) {
|
|
|
|
return OVERSCROLL_MINUS;
|
|
|
|
}
|
|
|
|
if (plus) {
|
|
|
|
return OVERSCROLL_PLUS;
|
|
|
|
}
|
|
|
|
return OVERSCROLL_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 Axis::GetExcess() {
|
|
|
|
switch (GetOverscroll()) {
|
|
|
|
case OVERSCROLL_MINUS: return GetOrigin() - GetPageStart();
|
|
|
|
case OVERSCROLL_PLUS: return GetViewportEnd() - GetPageEnd();
|
|
|
|
case OVERSCROLL_BOTH: return (GetViewportEnd() - GetPageEnd()) + (GetPageStart() - GetOrigin());
|
|
|
|
default: return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Axis::Overscroll Axis::DisplacementWillOverscroll(PRInt32 aDisplacement) {
|
|
|
|
// If the current pan plus a displacement takes the viewport to the left of or
|
|
|
|
// above the current page rect.
|
|
|
|
bool minus = GetOrigin() + aDisplacement < GetPageStart();
|
|
|
|
// If the current pan plus a displacement takes the viewport to the right of or
|
|
|
|
// below the current page rect.
|
|
|
|
bool plus = GetViewportEnd() + aDisplacement > GetPageEnd();
|
|
|
|
if (minus && plus) {
|
|
|
|
return OVERSCROLL_BOTH;
|
|
|
|
}
|
|
|
|
if (minus) {
|
|
|
|
return OVERSCROLL_MINUS;
|
|
|
|
}
|
|
|
|
if (plus) {
|
|
|
|
return OVERSCROLL_PLUS;
|
|
|
|
}
|
|
|
|
return OVERSCROLL_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 Axis::DisplacementWillOverscrollAmount(PRInt32 aDisplacement) {
|
|
|
|
switch (DisplacementWillOverscroll(aDisplacement)) {
|
|
|
|
case OVERSCROLL_MINUS: return (GetOrigin() + aDisplacement) - GetPageStart();
|
|
|
|
case OVERSCROLL_PLUS: return (GetViewportEnd() + aDisplacement) - GetPageEnd();
|
|
|
|
// Don't handle overscrolled in both directions; a displacement can't cause
|
|
|
|
// this, it must have already been zoomed out too far.
|
|
|
|
default: return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Axis::Overscroll Axis::ScaleWillOverscroll(float aScale, PRInt32 aFocus) {
|
|
|
|
PRInt32 originAfterScale = NS_lround((GetOrigin() + aFocus) * aScale - aFocus);
|
|
|
|
|
|
|
|
bool both = ScaleWillOverscrollBothSides(aScale);
|
|
|
|
bool minus = originAfterScale < NS_lround(GetPageStart() * aScale);
|
|
|
|
bool plus = (originAfterScale + GetViewportLength()) > NS_lround(GetPageEnd() * aScale);
|
|
|
|
|
|
|
|
if ((minus && plus) || both) {
|
|
|
|
return OVERSCROLL_BOTH;
|
|
|
|
}
|
|
|
|
if (minus) {
|
|
|
|
return OVERSCROLL_MINUS;
|
|
|
|
}
|
|
|
|
if (plus) {
|
|
|
|
return OVERSCROLL_PLUS;
|
|
|
|
}
|
|
|
|
return OVERSCROLL_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 Axis::ScaleWillOverscrollAmount(float aScale, PRInt32 aFocus) {
|
|
|
|
PRInt32 originAfterScale = NS_lround((GetOrigin() + aFocus) * aScale - aFocus);
|
|
|
|
switch (ScaleWillOverscroll(aScale, aFocus)) {
|
|
|
|
case OVERSCROLL_MINUS: return originAfterScale - NS_lround(GetPageStart() * aScale);
|
|
|
|
case OVERSCROLL_PLUS: return (originAfterScale + GetViewportLength()) - NS_lround(GetPageEnd() * aScale);
|
|
|
|
// Don't handle OVERSCROLL_BOTH. Client code is expected to deal with it.
|
|
|
|
default: return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
float Axis::GetVelocity() {
|
|
|
|
return mVelocity;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 Axis::GetViewportEnd() {
|
|
|
|
return GetOrigin() + GetViewportLength();
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 Axis::GetPageEnd() {
|
|
|
|
return GetPageStart() + GetPageLength();
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 Axis::GetOrigin() {
|
|
|
|
nsIntPoint origin = mAsyncPanZoomController->GetFrameMetrics().mViewportScrollOffset;
|
|
|
|
return GetPointOffset(origin);
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 Axis::GetViewportLength() {
|
|
|
|
nsIntRect viewport = mAsyncPanZoomController->GetFrameMetrics().mViewport;
|
|
|
|
return GetRectLength(viewport);
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 Axis::GetPageStart() {
|
|
|
|
nsIntRect pageRect = mAsyncPanZoomController->GetFrameMetrics().mContentRect;
|
|
|
|
return GetRectOffset(pageRect);
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 Axis::GetPageLength() {
|
|
|
|
nsIntRect pageRect = mAsyncPanZoomController->GetFrameMetrics().mContentRect;
|
|
|
|
return GetRectLength(pageRect);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Axis::ScaleWillOverscrollBothSides(float aScale) {
|
|
|
|
const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics();
|
|
|
|
|
|
|
|
float currentScale = metrics.mResolution.width;
|
|
|
|
gfx::Rect cssContentRect = metrics.mCSSContentRect;
|
|
|
|
cssContentRect.ScaleRoundIn(currentScale * aScale);
|
|
|
|
|
|
|
|
nsIntRect contentRect = nsIntRect(cssContentRect.x,
|
|
|
|
cssContentRect.y,
|
|
|
|
cssContentRect.width,
|
|
|
|
cssContentRect.height);
|
|
|
|
|
|
|
|
return GetRectLength(contentRect) < GetRectLength(metrics.mViewport);
|
|
|
|
}
|
|
|
|
|
|
|
|
AxisX::AxisX(AsyncPanZoomController* aAsyncPanZoomController)
|
|
|
|
: Axis(aAsyncPanZoomController)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 AxisX::GetPointOffset(const nsIntPoint& aPoint)
|
|
|
|
{
|
|
|
|
return aPoint.x;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 AxisX::GetRectLength(const nsIntRect& aRect)
|
|
|
|
{
|
|
|
|
return aRect.width;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 AxisX::GetRectOffset(const nsIntRect& aRect)
|
|
|
|
{
|
|
|
|
return aRect.x;
|
|
|
|
}
|
|
|
|
|
|
|
|
AxisY::AxisY(AsyncPanZoomController* aAsyncPanZoomController)
|
|
|
|
: Axis(aAsyncPanZoomController)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 AxisY::GetPointOffset(const nsIntPoint& aPoint)
|
|
|
|
{
|
|
|
|
return aPoint.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 AxisY::GetRectLength(const nsIntRect& aRect)
|
|
|
|
{
|
|
|
|
return aRect.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 AxisY::GetRectOffset(const nsIntRect& aRect)
|
|
|
|
{
|
|
|
|
return aRect.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|