Home | History | Annotate | Download | only in gesture_detection
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/events/gesture_detection/velocity_tracker.h"
      6 
      7 #include <cmath>
      8 
      9 #include "base/logging.h"
     10 #include "ui/events/gesture_detection/motion_event.h"
     11 
     12 using base::TimeDelta;
     13 using base::TimeTicks;
     14 
     15 namespace ui {
     16 
     17 // Implements a particular velocity tracker algorithm.
     18 class VelocityTrackerStrategy {
     19  public:
     20   virtual ~VelocityTrackerStrategy() {}
     21 
     22   virtual void Clear() = 0;
     23   virtual void ClearPointers(BitSet32 id_bits) = 0;
     24   virtual void AddMovement(const base::TimeTicks& event_time,
     25                            BitSet32 id_bits,
     26                            const Position* positions) = 0;
     27   virtual bool GetEstimator(uint32_t id, Estimator* out_estimator) const = 0;
     28 
     29  protected:
     30   VelocityTrackerStrategy() {}
     31 };
     32 
     33 namespace {
     34 
     35 COMPILE_ASSERT(MotionEvent::MAX_POINTER_ID < 32, max_pointer_id_too_large);
     36 
     37 // Threshold for determining that a pointer has stopped moving.
     38 // Some input devices do not send ACTION_MOVE events in the case where a pointer
     39 // has stopped.  We need to detect this case so that we can accurately predict
     40 // the velocity after the pointer starts moving again.
     41 const int kAssumePointerStoppedTimeMs = 40;
     42 
     43 struct Position {
     44   float x, y;
     45 };
     46 
     47 struct Estimator {
     48   enum { MAX_DEGREE = 4 };
     49 
     50   // Estimator time base.
     51   TimeTicks time;
     52 
     53   // Polynomial coefficients describing motion in X and Y.
     54   float xcoeff[MAX_DEGREE + 1], ycoeff[MAX_DEGREE + 1];
     55 
     56   // Polynomial degree (number of coefficients), or zero if no information is
     57   // available.
     58   uint32_t degree;
     59 
     60   // Confidence (coefficient of determination), between 0 (no fit)
     61   // and 1 (perfect fit).
     62   float confidence;
     63 
     64   inline void Clear() {
     65     time = TimeTicks();
     66     degree = 0;
     67     confidence = 0;
     68     for (size_t i = 0; i <= MAX_DEGREE; i++) {
     69       xcoeff[i] = 0;
     70       ycoeff[i] = 0;
     71     }
     72   }
     73 };
     74 
     75 float VectorDot(const float* a, const float* b, uint32_t m) {
     76   float r = 0;
     77   while (m--) {
     78     r += *(a++) * *(b++);
     79   }
     80   return r;
     81 }
     82 
     83 float VectorNorm(const float* a, uint32_t m) {
     84   float r = 0;
     85   while (m--) {
     86     float t = *(a++);
     87     r += t * t;
     88   }
     89   return sqrtf(r);
     90 }
     91 
     92 // Velocity tracker algorithm based on least-squares linear regression.
     93 class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy {
     94  public:
     95   enum Weighting {
     96     // No weights applied.  All data points are equally reliable.
     97     WEIGHTING_NONE,
     98 
     99     // Weight by time delta.  Data points clustered together are weighted less.
    100     WEIGHTING_DELTA,
    101 
    102     // Weight such that points within a certain horizon are weighed more than
    103     // those outside of that horizon.
    104     WEIGHTING_CENTRAL,
    105 
    106     // Weight such that points older than a certain amount are weighed less.
    107     WEIGHTING_RECENT,
    108   };
    109 
    110   // Number of samples to keep.
    111   enum { HISTORY_SIZE = 20 };
    112 
    113   // Degree must be no greater than Estimator::MAX_DEGREE.
    114   LeastSquaresVelocityTrackerStrategy(uint32_t degree,
    115                                       Weighting weighting = WEIGHTING_NONE);
    116   virtual ~LeastSquaresVelocityTrackerStrategy();
    117 
    118   virtual void Clear() OVERRIDE;
    119   virtual void ClearPointers(BitSet32 id_bits) OVERRIDE;
    120   virtual void AddMovement(const TimeTicks& event_time,
    121                            BitSet32 id_bits,
    122                            const Position* positions) OVERRIDE;
    123   virtual bool GetEstimator(uint32_t id,
    124                             Estimator* out_estimator) const OVERRIDE;
    125 
    126  private:
    127   // Sample horizon.
    128   // We don't use too much history by default since we want to react to quick
    129   // changes in direction.
    130   enum { HORIZON_MS = 100 };
    131 
    132   struct Movement {
    133     TimeTicks event_time;
    134     BitSet32 id_bits;
    135     Position positions[VelocityTracker::MAX_POINTERS];
    136 
    137     inline const Position& GetPosition(uint32_t id) const {
    138       return positions[id_bits.get_index_of_bit(id)];
    139     }
    140   };
    141 
    142   float ChooseWeight(uint32_t index) const;
    143 
    144   const uint32_t degree_;
    145   const Weighting weighting_;
    146   uint32_t index_;
    147   Movement movements_[HISTORY_SIZE];
    148 };
    149 
    150 // Velocity tracker algorithm that uses an IIR filter.
    151 class IntegratingVelocityTrackerStrategy : public VelocityTrackerStrategy {
    152  public:
    153   // Degree must be 1 or 2.
    154   explicit IntegratingVelocityTrackerStrategy(uint32_t degree);
    155   virtual ~IntegratingVelocityTrackerStrategy();
    156 
    157   virtual void Clear() OVERRIDE;
    158   virtual void ClearPointers(BitSet32 id_bits) OVERRIDE;
    159   virtual void AddMovement(const TimeTicks& event_time,
    160                            BitSet32 id_bits,
    161                            const Position* positions) OVERRIDE;
    162   virtual bool GetEstimator(uint32_t id,
    163                             Estimator* out_estimator) const OVERRIDE;
    164 
    165  private:
    166   // Current state estimate for a particular pointer.
    167   struct State {
    168     TimeTicks update_time;
    169     uint32_t degree;
    170 
    171     float xpos, xvel, xaccel;
    172     float ypos, yvel, yaccel;
    173   };
    174 
    175   const uint32_t degree_;
    176   BitSet32 pointer_id_bits_;
    177   State mPointerState[MotionEvent::MAX_POINTER_ID + 1];
    178 
    179   void InitState(State& state,
    180                  const TimeTicks& event_time,
    181                  float xpos,
    182                  float ypos) const;
    183   void UpdateState(State& state,
    184                    const TimeTicks& event_time,
    185                    float xpos,
    186                    float ypos) const;
    187   void PopulateEstimator(const State& state, Estimator* out_estimator) const;
    188 };
    189 
    190 VelocityTrackerStrategy* CreateStrategy(VelocityTracker::Strategy strategy) {
    191   switch (strategy) {
    192     case VelocityTracker::LSQ1:
    193       return new LeastSquaresVelocityTrackerStrategy(1);
    194     case VelocityTracker::LSQ2:
    195       return new LeastSquaresVelocityTrackerStrategy(2);
    196     case VelocityTracker::LSQ3:
    197       return new LeastSquaresVelocityTrackerStrategy(3);
    198     case VelocityTracker::WLSQ2_DELTA:
    199       return new LeastSquaresVelocityTrackerStrategy(
    200           2, LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA);
    201     case VelocityTracker::WLSQ2_CENTRAL:
    202       return new LeastSquaresVelocityTrackerStrategy(
    203           2, LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL);
    204     case VelocityTracker::WLSQ2_RECENT:
    205       return new LeastSquaresVelocityTrackerStrategy(
    206           2, LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT);
    207     case VelocityTracker::INT1:
    208       return new IntegratingVelocityTrackerStrategy(1);
    209     case VelocityTracker::INT2:
    210       return new IntegratingVelocityTrackerStrategy(2);
    211   }
    212   NOTREACHED() << "Unrecognized velocity tracker strategy: " << strategy;
    213   return CreateStrategy(VelocityTracker::STRATEGY_DEFAULT);
    214 }
    215 
    216 }  // namespace
    217 
    218 // --- VelocityTracker ---
    219 
    220 VelocityTracker::VelocityTracker()
    221     : current_pointer_id_bits_(0),
    222       active_pointer_id_(-1),
    223       strategy_(CreateStrategy(STRATEGY_DEFAULT)) {}
    224 
    225 VelocityTracker::VelocityTracker(Strategy strategy)
    226     : current_pointer_id_bits_(0),
    227       active_pointer_id_(-1),
    228       strategy_(CreateStrategy(strategy)) {}
    229 
    230 VelocityTracker::~VelocityTracker() {}
    231 
    232 void VelocityTracker::Clear() {
    233   current_pointer_id_bits_.clear();
    234   active_pointer_id_ = -1;
    235   strategy_->Clear();
    236 }
    237 
    238 void VelocityTracker::ClearPointers(BitSet32 id_bits) {
    239   BitSet32 remaining_id_bits(current_pointer_id_bits_.value & ~id_bits.value);
    240   current_pointer_id_bits_ = remaining_id_bits;
    241 
    242   if (active_pointer_id_ >= 0 && id_bits.has_bit(active_pointer_id_)) {
    243     active_pointer_id_ = !remaining_id_bits.is_empty()
    244                              ? remaining_id_bits.first_marked_bit()
    245                              : -1;
    246   }
    247 
    248   strategy_->ClearPointers(id_bits);
    249 }
    250 
    251 void VelocityTracker::AddMovement(const TimeTicks& event_time,
    252                                   BitSet32 id_bits,
    253                                   const Position* positions) {
    254   while (id_bits.count() > MAX_POINTERS)
    255     id_bits.clear_last_marked_bit();
    256 
    257   if ((current_pointer_id_bits_.value & id_bits.value) &&
    258       event_time >= (last_event_time_ + base::TimeDelta::FromMilliseconds(
    259                                             kAssumePointerStoppedTimeMs))) {
    260     // We have not received any movements for too long.  Assume that all
    261     // pointers
    262     // have stopped.
    263     strategy_->Clear();
    264   }
    265   last_event_time_ = event_time;
    266 
    267   current_pointer_id_bits_ = id_bits;
    268   if (active_pointer_id_ < 0 || !id_bits.has_bit(active_pointer_id_))
    269     active_pointer_id_ = id_bits.is_empty() ? -1 : id_bits.first_marked_bit();
    270 
    271   strategy_->AddMovement(event_time, id_bits, positions);
    272 }
    273 
    274 void VelocityTracker::AddMovement(const MotionEvent& event) {
    275   int32_t actionMasked = event.GetAction();
    276 
    277   switch (actionMasked) {
    278     case MotionEvent::ACTION_DOWN:
    279       // case MotionEvent::HOVER_ENTER:
    280       // Clear all pointers on down before adding the new movement.
    281       Clear();
    282       break;
    283     case MotionEvent::ACTION_POINTER_DOWN: {
    284       // Start a new movement trace for a pointer that just went down.
    285       // We do this on down instead of on up because the client may want to
    286       // query the final velocity for a pointer that just went up.
    287       BitSet32 downIdBits;
    288       downIdBits.mark_bit(event.GetPointerId(event.GetActionIndex()));
    289       ClearPointers(downIdBits);
    290       break;
    291     }
    292     case MotionEvent::ACTION_MOVE:
    293       // case MotionEvent::ACTION_HOVER_MOVE:
    294       break;
    295     default:
    296       // Ignore all other actions because they do not convey any new information
    297       // about pointer movement.  We also want to preserve the last known
    298       // velocity of the pointers.
    299       // Note that ACTION_UP and ACTION_POINTER_UP always report the last known
    300       // position of the pointers that went up.  ACTION_POINTER_UP does include
    301       // the new position of pointers that remained down but we will also
    302       // receive an ACTION_MOVE with this information if any of them actually
    303       // moved.  Since we don't know how many pointers will be going up at once
    304       // it makes sense to just wait for the following ACTION_MOVE before adding
    305       // the movement.
    306       return;
    307   }
    308 
    309   size_t pointer_count = event.GetPointerCount();
    310   if (pointer_count > MAX_POINTERS) {
    311     pointer_count = MAX_POINTERS;
    312   }
    313 
    314   BitSet32 id_bits;
    315   for (size_t i = 0; i < pointer_count; i++) {
    316     id_bits.mark_bit(event.GetPointerId(i));
    317   }
    318 
    319   uint32_t pointer_index[MAX_POINTERS];
    320   for (size_t i = 0; i < pointer_count; i++) {
    321     pointer_index[i] = id_bits.get_index_of_bit(event.GetPointerId(i));
    322   }
    323 
    324   Position positions[MAX_POINTERS];
    325   size_t historySize = event.GetHistorySize();
    326   for (size_t h = 0; h < historySize; h++) {
    327     for (size_t i = 0; i < pointer_count; i++) {
    328       uint32_t index = pointer_index[i];
    329       positions[index].x = event.GetHistoricalX(i, h);
    330       positions[index].y = event.GetHistoricalY(i, h);
    331     }
    332     AddMovement(event.GetHistoricalEventTime(h), id_bits, positions);
    333   }
    334 
    335   for (size_t i = 0; i < pointer_count; i++) {
    336     uint32_t index = pointer_index[i];
    337     positions[index].x = event.GetX(i);
    338     positions[index].y = event.GetY(i);
    339   }
    340   AddMovement(event.GetEventTime(), id_bits, positions);
    341 }
    342 
    343 bool VelocityTracker::GetVelocity(uint32_t id,
    344                                   float* out_vx,
    345                                   float* out_vy) const {
    346   Estimator estimator;
    347   if (GetEstimator(id, &estimator) && estimator.degree >= 1) {
    348     *out_vx = estimator.xcoeff[1];
    349     *out_vy = estimator.ycoeff[1];
    350     return true;
    351   }
    352   *out_vx = 0;
    353   *out_vy = 0;
    354   return false;
    355 }
    356 
    357 void LeastSquaresVelocityTrackerStrategy::AddMovement(
    358     const TimeTicks& event_time,
    359     BitSet32 id_bits,
    360     const Position* positions) {
    361   if (++index_ == HISTORY_SIZE) {
    362     index_ = 0;
    363   }
    364 
    365   Movement& movement = movements_[index_];
    366   movement.event_time = event_time;
    367   movement.id_bits = id_bits;
    368   uint32_t count = id_bits.count();
    369   for (uint32_t i = 0; i < count; i++) {
    370     movement.positions[i] = positions[i];
    371   }
    372 }
    373 
    374 bool VelocityTracker::GetEstimator(uint32_t id,
    375                                    Estimator* out_estimator) const {
    376   return strategy_->GetEstimator(id, out_estimator);
    377 }
    378 
    379 // --- LeastSquaresVelocityTrackerStrategy ---
    380 
    381 LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(
    382     uint32_t degree,
    383     Weighting weighting)
    384     : degree_(degree), weighting_(weighting) {
    385   DCHECK_LT(degree_, static_cast<uint32_t>(Estimator::MAX_DEGREE));
    386   Clear();
    387 }
    388 
    389 LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {}
    390 
    391 void LeastSquaresVelocityTrackerStrategy::Clear() {
    392   index_ = 0;
    393   movements_[0].id_bits.clear();
    394 }
    395 
    396 /**
    397  * Solves a linear least squares problem to obtain a N degree polynomial that
    398  * fits the specified input data as nearly as possible.
    399  *
    400  * Returns true if a solution is found, false otherwise.
    401  *
    402  * The input consists of two vectors of data points X and Y with indices 0..m-1
    403  * along with a weight vector W of the same size.
    404  *
    405  * The output is a vector B with indices 0..n that describes a polynomial
    406  * that fits the data, such the sum of W[i] * W[i] * abs(Y[i] - (B[0] + B[1]
    407  * X[i] * + B[2] X[i]^2 ... B[n] X[i]^n)) for all i between 0 and m-1 is
    408  * minimized.
    409  *
    410  * Accordingly, the weight vector W should be initialized by the caller with the
    411  * reciprocal square root of the variance of the error in each input data point.
    412  * In other words, an ideal choice for W would be W[i] = 1 / var(Y[i]) = 1 /
    413  * stddev(Y[i]).
    414  * The weights express the relative importance of each data point.  If the
    415  * weights are* all 1, then the data points are considered to be of equal
    416  * importance when fitting the polynomial.  It is a good idea to choose weights
    417  * that diminish the importance of data points that may have higher than usual
    418  * error margins.
    419  *
    420  * Errors among data points are assumed to be independent.  W is represented
    421  * here as a vector although in the literature it is typically taken to be a
    422  * diagonal matrix.
    423  *
    424  * That is to say, the function that generated the input data can be
    425  * approximated by y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n.
    426  *
    427  * The coefficient of determination (R^2) is also returned to describe the
    428  * goodness of fit of the model for the given data.  It is a value between 0
    429  * and 1, where 1 indicates perfect correspondence.
    430  *
    431  * This function first expands the X vector to a m by n matrix A such that
    432  * A[i][0] = 1, A[i][1] = X[i], A[i][2] = X[i]^2, ..., A[i][n] = X[i]^n, then
    433  * multiplies it by w[i]./
    434  *
    435  * Then it calculates the QR decomposition of A yielding an m by m orthonormal
    436  * matrix Q and an m by n upper triangular matrix R.  Because R is upper
    437  * triangular (lower part is all zeroes), we can simplify the decomposition into
    438  * an m by n matrix Q1 and a n by n matrix R1 such that A = Q1 R1.
    439  *
    440  * Finally we solve the system of linear equations given by
    441  * R1 B = (Qtranspose W Y) to find B.
    442  *
    443  * For efficiency, we lay out A and Q column-wise in memory because we
    444  * frequently operate on the column vectors.  Conversely, we lay out R row-wise.
    445  *
    446  * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares
    447  * http://en.wikipedia.org/wiki/Gram-Schmidt
    448  */
    449 static bool SolveLeastSquares(const float* x,
    450                               const float* y,
    451                               const float* w,
    452                               uint32_t m,
    453                               uint32_t n,
    454                               float* out_b,
    455                               float* out_det) {
    456   // MSVC does not support variable-length arrays (used by the original Android
    457   // implementation of this function).
    458 #if defined(COMPILER_MSVC)
    459   enum {
    460     M_ARRAY_LENGTH = LeastSquaresVelocityTrackerStrategy::HISTORY_SIZE,
    461     N_ARRAY_LENGTH = Estimator::MAX_DEGREE
    462   };
    463   DCHECK_LE(m, static_cast<uint32_t>(M_ARRAY_LENGTH));
    464   DCHECK_LE(n, static_cast<uint32_t>(N_ARRAY_LENGTH));
    465 #else
    466   const uint32_t M_ARRAY_LENGTH = m;
    467   const uint32_t N_ARRAY_LENGTH = n;
    468 #endif
    469 
    470   // Expand the X vector to a matrix A, pre-multiplied by the weights.
    471   float a[N_ARRAY_LENGTH][M_ARRAY_LENGTH];  // column-major order
    472   for (uint32_t h = 0; h < m; h++) {
    473     a[0][h] = w[h];
    474     for (uint32_t i = 1; i < n; i++) {
    475       a[i][h] = a[i - 1][h] * x[h];
    476     }
    477   }
    478 
    479   // Apply the Gram-Schmidt process to A to obtain its QR decomposition.
    480 
    481   // Orthonormal basis, column-major order.
    482   float q[N_ARRAY_LENGTH][M_ARRAY_LENGTH];
    483   // Upper triangular matrix, row-major order.
    484   float r[N_ARRAY_LENGTH][N_ARRAY_LENGTH];
    485   for (uint32_t j = 0; j < n; j++) {
    486     for (uint32_t h = 0; h < m; h++) {
    487       q[j][h] = a[j][h];
    488     }
    489     for (uint32_t i = 0; i < j; i++) {
    490       float dot = VectorDot(&q[j][0], &q[i][0], m);
    491       for (uint32_t h = 0; h < m; h++) {
    492         q[j][h] -= dot * q[i][h];
    493       }
    494     }
    495 
    496     float norm = VectorNorm(&q[j][0], m);
    497     if (norm < 0.000001f) {
    498       // vectors are linearly dependent or zero so no solution
    499       return false;
    500     }
    501 
    502     float invNorm = 1.0f / norm;
    503     for (uint32_t h = 0; h < m; h++) {
    504       q[j][h] *= invNorm;
    505     }
    506     for (uint32_t i = 0; i < n; i++) {
    507       r[j][i] = i < j ? 0 : VectorDot(&q[j][0], &a[i][0], m);
    508     }
    509   }
    510 
    511   // Solve R B = Qt W Y to find B.  This is easy because R is upper triangular.
    512   // We just work from bottom-right to top-left calculating B's coefficients.
    513   float wy[M_ARRAY_LENGTH];
    514   for (uint32_t h = 0; h < m; h++) {
    515     wy[h] = y[h] * w[h];
    516   }
    517   for (uint32_t i = n; i-- != 0;) {
    518     out_b[i] = VectorDot(&q[i][0], wy, m);
    519     for (uint32_t j = n - 1; j > i; j--) {
    520       out_b[i] -= r[i][j] * out_b[j];
    521     }
    522     out_b[i] /= r[i][i];
    523   }
    524 
    525   // Calculate the coefficient of determination as 1 - (SSerr / SStot) where
    526   // SSerr is the residual sum of squares (variance of the error),
    527   // and SStot is the total sum of squares (variance of the data) where each
    528   // has been weighted.
    529   float ymean = 0;
    530   for (uint32_t h = 0; h < m; h++) {
    531     ymean += y[h];
    532   }
    533   ymean /= m;
    534 
    535   float sserr = 0;
    536   float sstot = 0;
    537   for (uint32_t h = 0; h < m; h++) {
    538     float err = y[h] - out_b[0];
    539     float term = 1;
    540     for (uint32_t i = 1; i < n; i++) {
    541       term *= x[h];
    542       err -= term * out_b[i];
    543     }
    544     sserr += w[h] * w[h] * err * err;
    545     float var = y[h] - ymean;
    546     sstot += w[h] * w[h] * var * var;
    547   }
    548   *out_det = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
    549   return true;
    550 }
    551 
    552 void LeastSquaresVelocityTrackerStrategy::ClearPointers(BitSet32 id_bits) {
    553   BitSet32 remaining_id_bits(movements_[index_].id_bits.value & ~id_bits.value);
    554   movements_[index_].id_bits = remaining_id_bits;
    555 }
    556 
    557 bool LeastSquaresVelocityTrackerStrategy::GetEstimator(
    558     uint32_t id,
    559     Estimator* out_estimator) const {
    560   out_estimator->Clear();
    561 
    562   // Iterate over movement samples in reverse time order and collect samples.
    563   float x[HISTORY_SIZE];
    564   float y[HISTORY_SIZE];
    565   float w[HISTORY_SIZE];
    566   float time[HISTORY_SIZE];
    567   uint32_t m = 0;
    568   uint32_t index = index_;
    569   const base::TimeDelta horizon = base::TimeDelta::FromMilliseconds(HORIZON_MS);
    570   const Movement& newest_movement = movements_[index_];
    571   do {
    572     const Movement& movement = movements_[index];
    573     if (!movement.id_bits.has_bit(id))
    574       break;
    575 
    576     TimeDelta age = newest_movement.event_time - movement.event_time;
    577     if (age > horizon)
    578       break;
    579 
    580     const Position& position = movement.GetPosition(id);
    581     x[m] = position.x;
    582     y[m] = position.y;
    583     w[m] = ChooseWeight(index);
    584     time[m] = -age.InSecondsF();
    585     index = (index == 0 ? HISTORY_SIZE : index) - 1;
    586   } while (++m < HISTORY_SIZE);
    587 
    588   if (m == 0)
    589     return false;  // no data
    590 
    591   // Calculate a least squares polynomial fit.
    592   uint32_t degree = degree_;
    593   if (degree > m - 1)
    594     degree = m - 1;
    595 
    596   if (degree >= 1) {
    597     float xdet, ydet;
    598     uint32_t n = degree + 1;
    599     if (SolveLeastSquares(time, x, w, m, n, out_estimator->xcoeff, &xdet) &&
    600         SolveLeastSquares(time, y, w, m, n, out_estimator->ycoeff, &ydet)) {
    601       out_estimator->time = newest_movement.event_time;
    602       out_estimator->degree = degree;
    603       out_estimator->confidence = xdet * ydet;
    604       return true;
    605     }
    606   }
    607 
    608   // No velocity data available for this pointer, but we do have its current
    609   // position.
    610   out_estimator->xcoeff[0] = x[0];
    611   out_estimator->ycoeff[0] = y[0];
    612   out_estimator->time = newest_movement.event_time;
    613   out_estimator->degree = 0;
    614   out_estimator->confidence = 1;
    615   return true;
    616 }
    617 
    618 float LeastSquaresVelocityTrackerStrategy::ChooseWeight(uint32_t index) const {
    619   switch (weighting_) {
    620     case WEIGHTING_DELTA: {
    621       // Weight points based on how much time elapsed between them and the next
    622       // point so that points that "cover" a shorter time span are weighed less.
    623       //   delta  0ms: 0.5
    624       //   delta 10ms: 1.0
    625       if (index == index_) {
    626         return 1.0f;
    627       }
    628       uint32_t next_index = (index + 1) % HISTORY_SIZE;
    629       float delta_millis =
    630           static_cast<float>((movements_[next_index].event_time -
    631                               movements_[index].event_time).InMillisecondsF());
    632       if (delta_millis < 0)
    633         return 0.5f;
    634       if (delta_millis < 10)
    635         return 0.5f + delta_millis * 0.05;
    636 
    637       return 1.0f;
    638     }
    639 
    640     case WEIGHTING_CENTRAL: {
    641       // Weight points based on their age, weighing very recent and very old
    642       // points less.
    643       //   age  0ms: 0.5
    644       //   age 10ms: 1.0
    645       //   age 50ms: 1.0
    646       //   age 60ms: 0.5
    647       float age_millis =
    648           static_cast<float>((movements_[index_].event_time -
    649                               movements_[index].event_time).InMillisecondsF());
    650       if (age_millis < 0)
    651         return 0.5f;
    652       if (age_millis < 10)
    653         return 0.5f + age_millis * 0.05;
    654       if (age_millis < 50)
    655         return 1.0f;
    656       if (age_millis < 60)
    657         return 0.5f + (60 - age_millis) * 0.05;
    658 
    659       return 0.5f;
    660     }
    661 
    662     case WEIGHTING_RECENT: {
    663       // Weight points based on their age, weighing older points less.
    664       //   age   0ms: 1.0
    665       //   age  50ms: 1.0
    666       //   age 100ms: 0.5
    667       float age_millis =
    668           static_cast<float>((movements_[index_].event_time -
    669                               movements_[index].event_time).InMillisecondsF());
    670       if (age_millis < 50) {
    671         return 1.0f;
    672       }
    673       if (age_millis < 100) {
    674         return 0.5f + (100 - age_millis) * 0.01f;
    675       }
    676       return 0.5f;
    677     }
    678 
    679     case WEIGHTING_NONE:
    680     default:
    681       return 1.0f;
    682   }
    683 }
    684 
    685 // --- IntegratingVelocityTrackerStrategy ---
    686 
    687 IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(
    688     uint32_t degree)
    689     : degree_(degree) {
    690   DCHECK_LT(degree_, static_cast<uint32_t>(Estimator::MAX_DEGREE));
    691 }
    692 
    693 IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() {}
    694 
    695 void IntegratingVelocityTrackerStrategy::Clear() { pointer_id_bits_.clear(); }
    696 
    697 void IntegratingVelocityTrackerStrategy::ClearPointers(BitSet32 id_bits) {
    698   pointer_id_bits_.value &= ~id_bits.value;
    699 }
    700 
    701 void IntegratingVelocityTrackerStrategy::AddMovement(
    702     const TimeTicks& event_time,
    703     BitSet32 id_bits,
    704     const Position* positions) {
    705   uint32_t index = 0;
    706   for (BitSet32 iter_id_bits(id_bits); !iter_id_bits.is_empty();) {
    707     uint32_t id = iter_id_bits.clear_first_marked_bit();
    708     State& state = mPointerState[id];
    709     const Position& position = positions[index++];
    710     if (pointer_id_bits_.has_bit(id))
    711       UpdateState(state, event_time, position.x, position.y);
    712     else
    713       InitState(state, event_time, position.x, position.y);
    714   }
    715 
    716   pointer_id_bits_ = id_bits;
    717 }
    718 
    719 bool IntegratingVelocityTrackerStrategy::GetEstimator(
    720     uint32_t id,
    721     Estimator* out_estimator) const {
    722   out_estimator->Clear();
    723 
    724   if (pointer_id_bits_.has_bit(id)) {
    725     const State& state = mPointerState[id];
    726     PopulateEstimator(state, out_estimator);
    727     return true;
    728   }
    729 
    730   return false;
    731 }
    732 
    733 void IntegratingVelocityTrackerStrategy::InitState(State& state,
    734                                                    const TimeTicks& event_time,
    735                                                    float xpos,
    736                                                    float ypos) const {
    737   state.update_time = event_time;
    738   state.degree = 0;
    739   state.xpos = xpos;
    740   state.xvel = 0;
    741   state.xaccel = 0;
    742   state.ypos = ypos;
    743   state.yvel = 0;
    744   state.yaccel = 0;
    745 }
    746 
    747 void IntegratingVelocityTrackerStrategy::UpdateState(
    748     State& state,
    749     const TimeTicks& event_time,
    750     float xpos,
    751     float ypos) const {
    752   const base::TimeDelta MIN_TIME_DELTA = TimeDelta::FromMicroseconds(2);
    753   const float FILTER_TIME_CONSTANT = 0.010f;  // 10 milliseconds
    754 
    755   if (event_time <= state.update_time + MIN_TIME_DELTA)
    756     return;
    757 
    758   float dt = static_cast<float>((event_time - state.update_time).InSecondsF());
    759   state.update_time = event_time;
    760 
    761   float xvel = (xpos - state.xpos) / dt;
    762   float yvel = (ypos - state.ypos) / dt;
    763   if (state.degree == 0) {
    764     state.xvel = xvel;
    765     state.yvel = yvel;
    766     state.degree = 1;
    767   } else {
    768     float alpha = dt / (FILTER_TIME_CONSTANT + dt);
    769     if (degree_ == 1) {
    770       state.xvel += (xvel - state.xvel) * alpha;
    771       state.yvel += (yvel - state.yvel) * alpha;
    772     } else {
    773       float xaccel = (xvel - state.xvel) / dt;
    774       float yaccel = (yvel - state.yvel) / dt;
    775       if (state.degree == 1) {
    776         state.xaccel = xaccel;
    777         state.yaccel = yaccel;
    778         state.degree = 2;
    779       } else {
    780         state.xaccel += (xaccel - state.xaccel) * alpha;
    781         state.yaccel += (yaccel - state.yaccel) * alpha;
    782       }
    783       state.xvel += (state.xaccel * dt) * alpha;
    784       state.yvel += (state.yaccel * dt) * alpha;
    785     }
    786   }
    787   state.xpos = xpos;
    788   state.ypos = ypos;
    789 }
    790 
    791 void IntegratingVelocityTrackerStrategy::PopulateEstimator(
    792     const State& state,
    793     Estimator* out_estimator) const {
    794   out_estimator->time = state.update_time;
    795   out_estimator->confidence = 1.0f;
    796   out_estimator->degree = state.degree;
    797   out_estimator->xcoeff[0] = state.xpos;
    798   out_estimator->xcoeff[1] = state.xvel;
    799   out_estimator->xcoeff[2] = state.xaccel / 2;
    800   out_estimator->ycoeff[0] = state.ypos;
    801   out_estimator->ycoeff[1] = state.yvel;
    802   out_estimator->ycoeff[2] = state.yaccel / 2;
    803 }
    804 
    805 }  // namespace ui
    806