blob: 09f185a9f146e54178b5f476e0cd437547ac2dbd [file] [log] [blame]
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "TouchpadFlingPlatformGestureCurve.h"
#include "PlatformGestureCurveTarget.h"
#include <math.h>
namespace WebCore {
using namespace std;
// This curve implementation is based on the notion of a single, absolute curve, which starts at
// a large velocity and smoothly decreases to zero. For a given input velocity, we find where on
// the curve this velocity occurs, and start the animation at this point---denoted by (m_timeOffset,
// m_positionOffset).
//
// This has the effect of automatically determining an animation duration that scales with input
// velocity, as faster initial velocities start earlier on the curve and thus take longer to reach the end.
// No complicated time scaling is required.
//
// Since the starting velocity is implicitly determined by our starting point, we only store the
// relative magnitude and direction of both initial x- and y-velocities, and use this to scale the
// computed displacement at any point in time. This guarantees that fling trajectories are straight
// lines when viewed in x-y space. Initial velocities that lie outside the max velocity are constrained
// to start at zero (and thus are implicitly scaled).
//
// The curve is modelled as a 4th order polynomial, starting at t = 0, and ending at t = m_curveDuration.
// Attempts to generate position/velocity estimates outside this range are undefined.
const int TouchpadFlingPlatformGestureCurve::m_maxSearchIterations = 20;
PassOwnPtr<PlatformGestureCurve> TouchpadFlingPlatformGestureCurve::create(const FloatPoint& velocity, IntPoint cumulativeScroll)
{
// The default parameters listed below are a matched set, and should not be changed independently of one another.
return create(velocity, -5.70762e+03, 1.72e+02, 3.7e+00, 0, 0, 1.3, cumulativeScroll);
}
// FIXME: need to remove p3, p4 here and below as they are not used in the exponential curve, but leave in for now to facilitate
// the in-flight patch for https://bugs.webkit.org/show_bug.cgi?id=81663 .
PassOwnPtr<PlatformGestureCurve> TouchpadFlingPlatformGestureCurve::create(const FloatPoint& velocity, float p0, float p1, float p2, float p3, float p4, float curveDuration, IntPoint cumulativeScroll)
{
return adoptPtr(new TouchpadFlingPlatformGestureCurve(velocity, p0, p1, p2, p3, p4, curveDuration, cumulativeScroll));
}
inline double position(double t, float* p)
{
return p[0] * exp(-p[2] * t) - p[1] * t - p[0];
}
inline double velocity(double t, float* p)
{
return -p[0] * p[2] * exp(-p[2] * t) - p[1];
}
TouchpadFlingPlatformGestureCurve::TouchpadFlingPlatformGestureCurve(const FloatPoint& initialVelocity, float p0, float p1, float p2, float p3, float p4, float curveDuration, const IntPoint& cumulativeScroll)
: m_cumulativeScroll(cumulativeScroll)
, m_curveDuration(curveDuration)
{
ASSERT(initialVelocity != FloatPoint::zero());
m_coeffs[0] = p0; // alpha
m_coeffs[1] = p1; // beta
m_coeffs[2] = p2; // gamma
m_coeffs[3] = p3; // not used
m_coeffs[4] = p4; // not used
float maxInitialVelocity = max(fabs(initialVelocity.x()), fabs(initialVelocity.y()));
// Force maxInitialVelocity to lie in the range v(0) to v(curveDuration), and assume that
// the curve parameters define a monotonically decreasing velocity, or else bisection search may
// fail.
if (maxInitialVelocity > velocity(0, m_coeffs))
maxInitialVelocity = velocity(0, m_coeffs);
if (maxInitialVelocity < velocity(m_curveDuration, m_coeffs))
maxInitialVelocity = velocity(m_curveDuration, m_coeffs);
// We keep track of relative magnitudes and directions of the velocity/displacement components here.
m_displacementRatio = FloatPoint(initialVelocity.x() / maxInitialVelocity, initialVelocity.y() / maxInitialVelocity);
// Use basic bisection to estimate where we should start on the curve.
// FIXME: Would Newton's method be better?
const double epsilon = 1; // It is probably good enough to get the start point to within 1 pixel/sec.
double t0 = 0;
double t1 = curveDuration;
int numIterations = 0;
while (t0 < t1 && numIterations < m_maxSearchIterations) {
numIterations++;
m_timeOffset = (t0 + t1) * 0.5;
double vOffset = velocity(m_timeOffset, m_coeffs);
if (fabs(maxInitialVelocity - vOffset) < epsilon)
break;
if (vOffset > maxInitialVelocity)
t0 = m_timeOffset;
else
t1 = m_timeOffset;
}
// Compute curve position at offset time
m_positionOffset = position(m_timeOffset, m_coeffs);
}
TouchpadFlingPlatformGestureCurve::~TouchpadFlingPlatformGestureCurve()
{
}
bool TouchpadFlingPlatformGestureCurve::apply(double time, PlatformGestureCurveTarget* target)
{
float displacement;
if (time < 0)
displacement = 0;
else if (time + m_timeOffset < m_curveDuration)
displacement = position(time + m_timeOffset, m_coeffs) - m_positionOffset;
else
displacement = position(m_curveDuration, m_coeffs) - m_positionOffset;
// Keep track of integer portion of scroll thus far, and prepare increment.
IntPoint scroll(displacement * m_displacementRatio.x(), displacement * m_displacementRatio.y());
IntPoint scrollIncrement(scroll - m_cumulativeScroll);
m_cumulativeScroll = scroll;
if (time + m_timeOffset < m_curveDuration || scrollIncrement != IntPoint::zero()) {
target->scrollBy(scrollIncrement);
return true;
}
return false;
}
} // namespace WebCore