OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/events/gesture_detection/velocity_tracker.h" | 5 #include "ui/events/gesture_detection/velocity_tracker.h" |
6 | 6 |
7 #include <cmath> | 7 #include <cmath> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "ui/events/gesture_detection/motion_event.h" | 10 #include "ui/events/gesture_detection/motion_event.h" |
(...skipping 16 matching lines...) Expand all Loading... |
27 virtual bool GetEstimator(uint32_t id, Estimator* out_estimator) const = 0; | 27 virtual bool GetEstimator(uint32_t id, Estimator* out_estimator) const = 0; |
28 | 28 |
29 protected: | 29 protected: |
30 VelocityTrackerStrategy() {} | 30 VelocityTrackerStrategy() {} |
31 }; | 31 }; |
32 | 32 |
33 namespace { | 33 namespace { |
34 | 34 |
35 COMPILE_ASSERT(MotionEvent::MAX_POINTER_ID < 32, max_pointer_id_too_large); | 35 COMPILE_ASSERT(MotionEvent::MAX_POINTER_ID < 32, max_pointer_id_too_large); |
36 | 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 |
37 struct Position { | 43 struct Position { |
38 float x, y; | 44 float x, y; |
39 }; | 45 }; |
40 | 46 |
41 struct Estimator { | 47 struct Estimator { |
42 enum { MAX_DEGREE = 4 }; | 48 enum { MAX_DEGREE = 4 }; |
43 | 49 |
44 // Estimator time base. | 50 // Estimator time base. |
45 TimeTicks time; | 51 TimeTicks time; |
46 | 52 |
(...skipping 12 matching lines...) Expand all Loading... |
59 time = TimeTicks(); | 65 time = TimeTicks(); |
60 degree = 0; | 66 degree = 0; |
61 confidence = 0; | 67 confidence = 0; |
62 for (size_t i = 0; i <= MAX_DEGREE; i++) { | 68 for (size_t i = 0; i <= MAX_DEGREE; i++) { |
63 xcoeff[i] = 0; | 69 xcoeff[i] = 0; |
64 ycoeff[i] = 0; | 70 ycoeff[i] = 0; |
65 } | 71 } |
66 } | 72 } |
67 }; | 73 }; |
68 | 74 |
69 // Threshold for determining that a pointer has stopped moving. | 75 float VectorDot(const float* a, const float* b, uint32_t m) { |
70 // Some input devices do not send ACTION_MOVE events in the case where a pointer | |
71 // hasstopped. We need to detect this case so that we can accurately predict | |
72 // the velocity after the pointer starts moving again. | |
73 const TimeDelta ASSUME_POINTER_STOPPED_TIME = TimeDelta::FromMilliseconds(40); | |
74 | |
75 static float VectorDot(const float* a, const float* b, uint32_t m) { | |
76 float r = 0; | 76 float r = 0; |
77 while (m--) { | 77 while (m--) { |
78 r += *(a++) * *(b++); | 78 r += *(a++) * *(b++); |
79 } | 79 } |
80 return r; | 80 return r; |
81 } | 81 } |
82 | 82 |
83 static float VectorNorm(const float* a, uint32_t m) { | 83 float VectorNorm(const float* a, uint32_t m) { |
84 float r = 0; | 84 float r = 0; |
85 while (m--) { | 85 while (m--) { |
86 float t = *(a++); | 86 float t = *(a++); |
87 r += t * t; | 87 r += t * t; |
88 } | 88 } |
89 return sqrtf(r); | 89 return sqrtf(r); |
90 } | 90 } |
91 | 91 |
92 // Velocity tracker algorithm based on least-squares linear regression. | 92 // Velocity tracker algorithm based on least-squares linear regression. |
93 class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy { | 93 class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy { |
(...skipping 26 matching lines...) Expand all Loading... |
120 virtual void AddMovement(const TimeTicks& event_time, | 120 virtual void AddMovement(const TimeTicks& event_time, |
121 BitSet32 id_bits, | 121 BitSet32 id_bits, |
122 const Position* positions) OVERRIDE; | 122 const Position* positions) OVERRIDE; |
123 virtual bool GetEstimator(uint32_t id, | 123 virtual bool GetEstimator(uint32_t id, |
124 Estimator* out_estimator) const OVERRIDE; | 124 Estimator* out_estimator) const OVERRIDE; |
125 | 125 |
126 private: | 126 private: |
127 // Sample horizon. | 127 // Sample horizon. |
128 // We don't use too much history by default since we want to react to quick | 128 // We don't use too much history by default since we want to react to quick |
129 // changes in direction. | 129 // changes in direction. |
130 static const TimeDelta HORIZON; | 130 enum { HORIZON_MS = 100 }; |
131 | 131 |
132 struct Movement { | 132 struct Movement { |
133 TimeTicks event_time; | 133 TimeTicks event_time; |
134 BitSet32 id_bits; | 134 BitSet32 id_bits; |
135 Position positions[VelocityTracker::MAX_POINTERS]; | 135 Position positions[VelocityTracker::MAX_POINTERS]; |
136 | 136 |
137 inline const Position& GetPosition(uint32_t id) const { | 137 inline const Position& GetPosition(uint32_t id) const { |
138 return positions[id_bits.get_index_of_bit(id)]; | 138 return positions[id_bits.get_index_of_bit(id)]; |
139 } | 139 } |
140 }; | 140 }; |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
248 strategy_->ClearPointers(id_bits); | 248 strategy_->ClearPointers(id_bits); |
249 } | 249 } |
250 | 250 |
251 void VelocityTracker::AddMovement(const TimeTicks& event_time, | 251 void VelocityTracker::AddMovement(const TimeTicks& event_time, |
252 BitSet32 id_bits, | 252 BitSet32 id_bits, |
253 const Position* positions) { | 253 const Position* positions) { |
254 while (id_bits.count() > MAX_POINTERS) | 254 while (id_bits.count() > MAX_POINTERS) |
255 id_bits.clear_last_marked_bit(); | 255 id_bits.clear_last_marked_bit(); |
256 | 256 |
257 if ((current_pointer_id_bits_.value & id_bits.value) && | 257 if ((current_pointer_id_bits_.value & id_bits.value) && |
258 event_time >= (last_event_time_ + ASSUME_POINTER_STOPPED_TIME)) { | 258 event_time >= (last_event_time_ + base::TimeDelta::FromMilliseconds( |
| 259 kAssumePointerStoppedTimeMs))) { |
259 // We have not received any movements for too long. Assume that all | 260 // We have not received any movements for too long. Assume that all |
260 // pointers | 261 // pointers |
261 // have stopped. | 262 // have stopped. |
262 strategy_->Clear(); | 263 strategy_->Clear(); |
263 } | 264 } |
264 last_event_time_ = event_time; | 265 last_event_time_ = event_time; |
265 | 266 |
266 current_pointer_id_bits_ = id_bits; | 267 current_pointer_id_bits_ = id_bits; |
267 if (active_pointer_id_ < 0 || !id_bits.has_bit(active_pointer_id_)) | 268 if (active_pointer_id_ < 0 || !id_bits.has_bit(active_pointer_id_)) |
268 active_pointer_id_ = id_bits.is_empty() ? -1 : id_bits.first_marked_bit(); | 269 active_pointer_id_ = id_bits.is_empty() ? -1 : id_bits.first_marked_bit(); |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
370 } | 371 } |
371 } | 372 } |
372 | 373 |
373 bool VelocityTracker::GetEstimator(uint32_t id, | 374 bool VelocityTracker::GetEstimator(uint32_t id, |
374 Estimator* out_estimator) const { | 375 Estimator* out_estimator) const { |
375 return strategy_->GetEstimator(id, out_estimator); | 376 return strategy_->GetEstimator(id, out_estimator); |
376 } | 377 } |
377 | 378 |
378 // --- LeastSquaresVelocityTrackerStrategy --- | 379 // --- LeastSquaresVelocityTrackerStrategy --- |
379 | 380 |
380 const TimeDelta LeastSquaresVelocityTrackerStrategy::HORIZON = | |
381 TimeDelta::FromMilliseconds(100); | |
382 | |
383 LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy( | 381 LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy( |
384 uint32_t degree, | 382 uint32_t degree, |
385 Weighting weighting) | 383 Weighting weighting) |
386 : degree_(degree), weighting_(weighting) { | 384 : degree_(degree), weighting_(weighting) { |
387 DCHECK_LT(degree_, static_cast<uint32_t>(Estimator::MAX_DEGREE)); | 385 DCHECK_LT(degree_, static_cast<uint32_t>(Estimator::MAX_DEGREE)); |
388 Clear(); | 386 Clear(); |
389 } | 387 } |
390 | 388 |
391 LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {} | 389 LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {} |
392 | 390 |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
561 Estimator* out_estimator) const { | 559 Estimator* out_estimator) const { |
562 out_estimator->Clear(); | 560 out_estimator->Clear(); |
563 | 561 |
564 // Iterate over movement samples in reverse time order and collect samples. | 562 // Iterate over movement samples in reverse time order and collect samples. |
565 float x[HISTORY_SIZE]; | 563 float x[HISTORY_SIZE]; |
566 float y[HISTORY_SIZE]; | 564 float y[HISTORY_SIZE]; |
567 float w[HISTORY_SIZE]; | 565 float w[HISTORY_SIZE]; |
568 float time[HISTORY_SIZE]; | 566 float time[HISTORY_SIZE]; |
569 uint32_t m = 0; | 567 uint32_t m = 0; |
570 uint32_t index = index_; | 568 uint32_t index = index_; |
| 569 const base::TimeDelta horizon = base::TimeDelta::FromMilliseconds(HORIZON_MS); |
571 const Movement& newest_movement = movements_[index_]; | 570 const Movement& newest_movement = movements_[index_]; |
572 do { | 571 do { |
573 const Movement& movement = movements_[index]; | 572 const Movement& movement = movements_[index]; |
574 if (!movement.id_bits.has_bit(id)) | 573 if (!movement.id_bits.has_bit(id)) |
575 break; | 574 break; |
576 | 575 |
577 TimeDelta age = newest_movement.event_time - movement.event_time; | 576 TimeDelta age = newest_movement.event_time - movement.event_time; |
578 if (age > HORIZON) | 577 if (age > horizon) |
579 break; | 578 break; |
580 | 579 |
581 const Position& position = movement.GetPosition(id); | 580 const Position& position = movement.GetPosition(id); |
582 x[m] = position.x; | 581 x[m] = position.x; |
583 y[m] = position.y; | 582 y[m] = position.y; |
584 w[m] = ChooseWeight(index); | 583 w[m] = ChooseWeight(index); |
585 time[m] = -age.InSecondsF(); | 584 time[m] = -age.InSecondsF(); |
586 index = (index == 0 ? HISTORY_SIZE : index) - 1; | 585 index = (index == 0 ? HISTORY_SIZE : index) - 1; |
587 } while (++m < HISTORY_SIZE); | 586 } while (++m < HISTORY_SIZE); |
588 | 587 |
(...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
797 out_estimator->degree = state.degree; | 796 out_estimator->degree = state.degree; |
798 out_estimator->xcoeff[0] = state.xpos; | 797 out_estimator->xcoeff[0] = state.xpos; |
799 out_estimator->xcoeff[1] = state.xvel; | 798 out_estimator->xcoeff[1] = state.xvel; |
800 out_estimator->xcoeff[2] = state.xaccel / 2; | 799 out_estimator->xcoeff[2] = state.xaccel / 2; |
801 out_estimator->ycoeff[0] = state.ypos; | 800 out_estimator->ycoeff[0] = state.ypos; |
802 out_estimator->ycoeff[1] = state.yvel; | 801 out_estimator->ycoeff[1] = state.yvel; |
803 out_estimator->ycoeff[2] = state.yaccel / 2; | 802 out_estimator->ycoeff[2] = state.yaccel / 2; |
804 } | 803 } |
805 | 804 |
806 } // namespace ui | 805 } // namespace ui |
OLD | NEW |