| 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 |