velocity_tracker.cc revision a02191e04bc25c4935f804f2c080ae28663d096d
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
12using base::TimeDelta;
13using base::TimeTicks;
14
15namespace ui {
16
17// Implements a particular velocity tracker algorithm.
18class 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
33namespace {
34
35COMPILE_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.
41const int kAssumePointerStoppedTimeMs = 40;
42
43struct Position {
44  float x, y;
45};
46
47struct 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
75float 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
83float 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.
93class 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.
151class 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
190VelocityTrackerStrategy* 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
220VelocityTracker::VelocityTracker()
221    : current_pointer_id_bits_(0),
222      active_pointer_id_(-1),
223      strategy_(CreateStrategy(STRATEGY_DEFAULT)) {}
224
225VelocityTracker::VelocityTracker(Strategy strategy)
226    : current_pointer_id_bits_(0),
227      active_pointer_id_(-1),
228      strategy_(CreateStrategy(strategy)) {}
229
230VelocityTracker::~VelocityTracker() {}
231
232void VelocityTracker::Clear() {
233  current_pointer_id_bits_.clear();
234  active_pointer_id_ = -1;
235  strategy_->Clear();
236}
237
238void 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
251void 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
274void 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
343bool 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
357void 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
374bool VelocityTracker::GetEstimator(uint32_t id,
375                                   Estimator* out_estimator) const {
376  return strategy_->GetEstimator(id, out_estimator);
377}
378
379// --- LeastSquaresVelocityTrackerStrategy ---
380
381LeastSquaresVelocityTrackerStrategy::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
389LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {}
390
391void 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 */
449static 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
552void 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
557bool 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
618float 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
687IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(
688    uint32_t degree)
689    : degree_(degree) {
690  DCHECK_LT(degree_, static_cast<uint32_t>(Estimator::MAX_DEGREE));
691}
692
693IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() {}
694
695void IntegratingVelocityTrackerStrategy::Clear() { pointer_id_bits_.clear(); }
696
697void IntegratingVelocityTrackerStrategy::ClearPointers(BitSet32 id_bits) {
698  pointer_id_bits_.value &= ~id_bits.value;
699}
700
701void 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
719bool 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
733void 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
747void 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
791void 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