From 137c3c549504a1bf52b85aa983c97f10533c7083 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 9 Sep 2011 15:39:35 -0700 Subject: [PATCH] Tweak VelocityTracker. Bug: 5265529 Calculate the velocity using the most recent touch sample as the point of reference instead of the oldest. This change more heavily weights recent touch samples and reduces the sample time window used for calculation. This significantly improves the accuracy of fling gesture detection. Change-Id: Ib1940933e786e5f6a731552a99bcd9400741d55f --- include/ui/Input.h | 8 +++-- libs/ui/Input.cpp | 90 +++++++++++++++------------------------------- 2 files changed, 35 insertions(+), 63 deletions(-) diff --git a/include/ui/Input.h b/include/ui/Input.h index f1385a039..af899efec 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -660,15 +660,19 @@ private: static const uint32_t HISTORY_SIZE = 10; // Oldest sample to consider when calculating the velocity. - static const nsecs_t MAX_AGE = 200 * 1000000; // 200 ms + static const nsecs_t MAX_AGE = 100 * 1000000; // 100 ms // The minimum duration between samples when estimating velocity. - static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms + static const nsecs_t MIN_DURATION = 5 * 1000000; // 5 ms struct Movement { nsecs_t eventTime; BitSet32 idBits; Position positions[MAX_POINTERS]; + + inline const Position& getPosition(uint32_t id) const { + return positions[idBits.getIndexOfBit(id)]; + } }; uint32_t mIndex; diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index 688b99892..0d258231f 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -752,6 +752,7 @@ void VelocityTracker::addMovement(const MotionEvent* event) { switch (actionMasked) { case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_HOVER_ENTER: // Clear all pointers on down before adding the new movement. clear(); break; @@ -764,12 +765,11 @@ void VelocityTracker::addMovement(const MotionEvent* event) { clearPointers(downIdBits); break; } - case AMOTION_EVENT_ACTION_OUTSIDE: - case AMOTION_EVENT_ACTION_CANCEL: - case AMOTION_EVENT_ACTION_SCROLL: - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_POINTER_UP: - // Ignore these actions because they do not convey any new information about + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_HOVER_MOVE: + break; + default: + // Ignore all other actions because they do not convey any new information about // pointer movement. We also want to preserve the last known velocity of the pointers. // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position // of the pointers that went up. ACTION_POINTER_UP does include the new position of @@ -814,68 +814,36 @@ void VelocityTracker::addMovement(const MotionEvent* event) { bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const { const Movement& newestMovement = mMovements[mIndex]; if (newestMovement.idBits.hasBit(id)) { - // Find the oldest sample that contains the pointer and that is not older than MAX_AGE. - nsecs_t minTime = newestMovement.eventTime - MAX_AGE; - uint32_t oldestIndex = mIndex; - uint32_t numTouches = 1; - do { - uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1; - const Movement& nextOldestMovement = mMovements[nextOldestIndex]; - if (!nextOldestMovement.idBits.hasBit(id) - || nextOldestMovement.eventTime < minTime) { - break; - } - oldestIndex = nextOldestIndex; - } while (++numTouches < HISTORY_SIZE); - - // Calculate an exponentially weighted moving average of the velocity estimate - // at different points in time measured relative to the oldest sample. - // This is essentially an IIR filter. Newer samples are weighted more heavily - // than older samples. Samples at equal time points are weighted more or less - // equally. - // - // One tricky problem is that the sample data may be poorly conditioned. - // Sometimes samples arrive very close together in time which can cause us to - // overestimate the velocity at that time point. Most samples might be measured - // 16ms apart but some consecutive samples could be only 0.5sm apart because - // the hardware or driver reports them irregularly or in bursts. + const Position& newestPosition = newestMovement.getPosition(id); float accumVx = 0; float accumVy = 0; - uint32_t index = oldestIndex; - uint32_t samplesUsed = 0; - const Movement& oldestMovement = mMovements[oldestIndex]; - const Position& oldestPosition = - oldestMovement.positions[oldestMovement.idBits.getIndexOfBit(id)]; - nsecs_t lastDuration = 0; + float duration = 0; - while (numTouches-- > 1) { - if (++index == HISTORY_SIZE) { - index = 0; - } + // Iterate over movement samples in reverse time order and accumulate velocity. + uint32_t index = mIndex; + do { + index = (index == 0 ? HISTORY_SIZE : index) - 1; const Movement& movement = mMovements[index]; - nsecs_t duration = movement.eventTime - oldestMovement.eventTime; - - // If the duration between samples is small, we may significantly overestimate - // the velocity. Consequently, we impose a minimum duration constraint on the - // samples that we include in the calculation. - if (duration >= MIN_DURATION) { - const Position& position = movement.positions[movement.idBits.getIndexOfBit(id)]; - float scale = 1000000000.0f / duration; // one over time delta in seconds - float vx = (position.x - oldestPosition.x) * scale; - float vy = (position.y - oldestPosition.y) * scale; - - accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration); - accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration); - - lastDuration = duration; - samplesUsed += 1; + if (!movement.idBits.hasBit(id)) { + break; } - } + + nsecs_t age = newestMovement.eventTime - movement.eventTime; + if (age > MAX_AGE) { + break; + } + + const Position& position = movement.getPosition(id); + accumVx += newestPosition.x - position.x; + accumVy += newestPosition.y - position.y; + duration += age; + } while (index != mIndex); // Make sure we used at least one sample. - if (samplesUsed != 0) { - *outVx = accumVx; - *outVy = accumVy; + if (duration >= MIN_DURATION) { + float scale = 1000000000.0f / duration; // one over time delta in seconds + *outVx = accumVx * scale; + *outVy = accumVy * scale; return true; } }