Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(47)

Side by Side Diff: ui/events/gesture_detection/velocity_tracker.cc

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

Powered by Google App Engine
This is Rietveld 408576698