diff options
author | 2022-08-27 05:48:51 +0000 | |
---|---|---|
committer | 2022-09-07 16:25:32 -0700 | |
commit | 37acf6e3b7ff4b30567eb3210560db3d73c20a58 (patch) | |
tree | dc84a2ffd7f3ae5f1c9540c9499f5d8f20d6b11c | |
parent | 89eca3248127fbe97a8669725bd2649163dd7831 (diff) |
Make VelocityTracker 1D
Currently, VelocityTracker is strictly tied to X and Y axes. It's APIs
act on both axes, and its component structs (e.g. Position, Estimator)
are tied to both X and Y axes. As a step towards supporting more axes
for velocity tracking, this change:
- removes the Position struct: stores/processes data as pure floats, one
axis at a time
- makes Estimator and Strategy specific to a single axis, instead of
dealing with both/only X and Y at the same time
Furthermore, we have pulled into VelocityTracker the logic to compute
all velocity. This helps making the immediate JNI layer light-weight in
addition to allowing us to test the logic (which is non-trivial and
benefits from tests).
Bug: 32830165
Test: VelocityTracker_test unaffected (atest libinput_tests)
Change-Id: I181af7a033eb647e9cb97db9b86a36ae972290a5
-rw-r--r-- | include/input/VelocityControl.h | 2 | ||||
-rw-r--r-- | include/input/VelocityTracker.h | 119 | ||||
-rw-r--r-- | libs/input/VelocityControl.cpp | 26 | ||||
-rw-r--r-- | libs/input/VelocityTracker.cpp | 313 | ||||
-rw-r--r-- | libs/input/tests/VelocityTracker_test.cpp | 107 | ||||
-rw-r--r-- | services/inputflinger/reader/mapper/TouchInputMapper.cpp | 20 |
6 files changed, 353 insertions, 234 deletions
diff --git a/include/input/VelocityControl.h b/include/input/VelocityControl.h index 1acc2aef70..f4c7061ad1 100644 --- a/include/input/VelocityControl.h +++ b/include/input/VelocityControl.h @@ -98,7 +98,7 @@ private: VelocityControlParameters mParameters; nsecs_t mLastMovementTime; - VelocityTracker::Position mRawPosition; + float mRawPositionX, mRawPositionY; VelocityTracker mVelocityTracker; }; diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h index 886f1f7753..6f2fcf4ce4 100644 --- a/include/input/VelocityTracker.h +++ b/include/input/VelocityTracker.h @@ -20,6 +20,8 @@ #include <input/Input.h> #include <utils/BitSet.h> #include <utils/Timers.h> +#include <map> +#include <set> namespace android { @@ -46,18 +48,14 @@ public: MAX = LEGACY, }; - struct Position { - float x, y; - }; - struct Estimator { static const size_t MAX_DEGREE = 4; // Estimator time base. nsecs_t time; - // Polynomial coefficients describing motion in X and Y. - float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1]; + // Polynomial coefficients describing motion. + float coeff[MAX_DEGREE + 1]; // Polynomial degree (number of coefficients), or zero if no information is // available. @@ -71,14 +69,40 @@ public: degree = 0; confidence = 0; for (size_t i = 0; i <= MAX_DEGREE; i++) { - xCoeff[i] = 0; - yCoeff[i] = 0; + coeff[i] = 0; + } + } + }; + + /* + * Contains all available velocity data from a VelocityTracker. + */ + struct ComputedVelocity { + inline std::optional<float> getVelocity(int32_t axis, uint32_t id) const { + const auto& axisVelocities = mVelocities.find(axis); + if (axisVelocities == mVelocities.end()) { + return {}; + } + + const auto& axisIdVelocity = axisVelocities->second.find(id); + if (axisIdVelocity == axisVelocities->second.end()) { + return {}; } + + return axisIdVelocity->second; + } + + inline void addVelocity(int32_t axis, uint32_t id, float velocity) { + mVelocities[axis][id] = velocity; } + + private: + std::map<int32_t /*axis*/, std::map<int32_t /*pointerId*/, float /*velocity*/>> mVelocities; }; - // Creates a velocity tracker using the specified strategy. + // Creates a velocity tracker using the specified strategy for each supported axis. // If strategy is not provided, uses the default strategy for the platform. + // TODO(b/32830165): support axis-specific strategies. VelocityTracker(const Strategy strategy = Strategy::DEFAULT); ~VelocityTracker(); @@ -92,45 +116,57 @@ public: void clearPointers(BitSet32 idBits); // Adds movement information for a set of pointers. - // The idBits bitfield specifies the pointer ids of the pointers whose positions + // The idBits bitfield specifies the pointer ids of the pointers whose data points // are included in the movement. - // The positions array contains position information for each pointer in order by - // increasing id. Its size should be equal to the number of one bits in idBits. - void addMovement(nsecs_t eventTime, BitSet32 idBits, const std::vector<Position>& positions); + // The positions map contains a mapping of an axis to positions array. + // The positions arrays contain information for each pointer in order by increasing id. + // Each array's size should be equal to the number of one bits in idBits. + void addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::map<int32_t, std::vector<float>>& positions); // Adds movement information for all pointers in a MotionEvent, including historical samples. void addMovement(const MotionEvent* event); - // Gets the velocity of the specified pointer id in position units per second. - // Returns false and sets the velocity components to zero if there is - // insufficient movement information for the pointer. - bool getVelocity(uint32_t id, float* outVx, float* outVy) const; + // Returns the velocity of the specified pointer id and axis in position units per second. + // Returns empty optional if there is insufficient movement information for the pointer, or if + // the given axis is not supported for velocity tracking. + std::optional<float> getVelocity(int32_t axis, uint32_t id) const; - // Gets an estimator for the recent movements of the specified pointer id. + // Populates a ComputedVelocity instance with all available velocity data, using the given units + // (reference: units == 1 means "per millisecond"), and clamping each velocity between + // [-maxVelocity, maxVelocity], inclusive. + void populateComputedVelocity(ComputedVelocity& computedVelocity, int32_t units, + float maxVelocity); + + // Gets an estimator for the recent movements of the specified pointer id for the given axis. // Returns false and clears the estimator if there is no information available // about the pointer. - bool getEstimator(uint32_t id, Estimator* outEstimator) const; + bool getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const; // Gets the active pointer id, or -1 if none. inline int32_t getActivePointerId() const { return mActivePointerId; } - // Gets a bitset containing all pointer ids from the most recent movement. - inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; } - private: // The default velocity tracker strategy. // Although other strategies are available for testing and comparison purposes, // this is the strategy that applications will actually use. Be very careful // when adjusting the default strategy because it can dramatically affect // (often in a bad way) the user experience. + // TODO(b/32830165): define default strategy per axis. static const Strategy DEFAULT_STRATEGY = Strategy::LSQ2; + // Set of all axes supported for velocity tracking. + static const std::set<int32_t> SUPPORTED_AXES; + + // Axes specifying location on a 2D plane (i.e. X and Y). + static const std::set<int32_t> PLANAR_AXES; + nsecs_t mLastEventTime; BitSet32 mCurrentPointerIdBits; int32_t mActivePointerId; - std::unique_ptr<VelocityTrackerStrategy> mStrategy; + std::map<int32_t /*axis*/, std::unique_ptr<VelocityTrackerStrategy>> mStrategies; - bool configureStrategy(const Strategy strategy); + void configureStrategy(int32_t axis, const Strategy strategy); static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy); }; @@ -149,7 +185,7 @@ public: virtual void clear() = 0; virtual void clearPointers(BitSet32 idBits) = 0; virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) = 0; + const std::vector<float>& positions) = 0; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0; }; @@ -181,7 +217,7 @@ public: virtual void clear(); virtual void clearPointers(BitSet32 idBits); void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) override; + const std::vector<float>& positions) override; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: @@ -196,11 +232,9 @@ private: struct Movement { nsecs_t eventTime; BitSet32 idBits; - VelocityTracker::Position positions[MAX_POINTERS]; + float positions[MAX_POINTERS]; - inline const VelocityTracker::Position& getPosition(uint32_t id) const { - return positions[idBits.getIndexOfBit(id)]; - } + inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; } }; float chooseWeight(uint32_t index) const; @@ -224,7 +258,7 @@ public: virtual void clear(); virtual void clearPointers(BitSet32 idBits); void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) override; + const std::vector<float>& positions) override; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: @@ -233,16 +267,15 @@ private: nsecs_t updateTime; uint32_t degree; - float xpos, xvel, xaccel; - float ypos, yvel, yaccel; + float pos, vel, accel; }; const uint32_t mDegree; BitSet32 mPointerIdBits; State mPointerState[MAX_POINTER_ID + 1]; - void initState(State& state, nsecs_t eventTime, float xpos, float ypos) const; - void updateState(State& state, nsecs_t eventTime, float xpos, float ypos) const; + void initState(State& state, nsecs_t eventTime, float pos) const; + void updateState(State& state, nsecs_t eventTime, float pos) const; void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const; }; @@ -258,7 +291,7 @@ public: virtual void clear(); virtual void clearPointers(BitSet32 idBits); void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) override; + const std::vector<float>& positions) override; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: @@ -274,11 +307,9 @@ private: struct Movement { nsecs_t eventTime; BitSet32 idBits; - VelocityTracker::Position positions[MAX_POINTERS]; + float positions[MAX_POINTERS]; - inline const VelocityTracker::Position& getPosition(uint32_t id) const { - return positions[idBits.getIndexOfBit(id)]; - } + inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; } }; uint32_t mIndex; @@ -293,7 +324,7 @@ public: virtual void clear(); virtual void clearPointers(BitSet32 idBits); void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) override; + const std::vector<float>& positions) override; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: @@ -308,11 +339,9 @@ private: struct Movement { nsecs_t eventTime; BitSet32 idBits; - VelocityTracker::Position positions[MAX_POINTERS]; + float positions[MAX_POINTERS]; - inline const VelocityTracker::Position& getPosition(uint32_t id) const { - return positions[idBits.getIndexOfBit(id)]; - } + inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; } }; size_t mIndex; diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp index 6e991e98bb..e2bfb508e1 100644 --- a/libs/input/VelocityControl.cpp +++ b/libs/input/VelocityControl.cpp @@ -44,8 +44,8 @@ void VelocityControl::setParameters(const VelocityControlParameters& parameters) void VelocityControl::reset() { mLastMovementTime = LLONG_MIN; - mRawPosition.x = 0; - mRawPosition.y = 0; + mRawPositionX = 0; + mRawPositionY = 0; mVelocityTracker.clear(); } @@ -61,17 +61,20 @@ void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { mLastMovementTime = eventTime; if (deltaX) { - mRawPosition.x += *deltaX; + mRawPositionX += *deltaX; } if (deltaY) { - mRawPosition.y += *deltaY; + mRawPositionY += *deltaY; } - mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), {mRawPosition}); + mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), + {{AMOTION_EVENT_AXIS_X, {mRawPositionX}}, + {AMOTION_EVENT_AXIS_Y, {mRawPositionY}}}); - float vx, vy; + std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0); + std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0); float scale = mParameters.scale; - if (mVelocityTracker.getVelocity(0, &vx, &vy)) { - float speed = hypotf(vx, vy) * scale; + if (vx && vy) { + float speed = hypotf(*vx, *vy) * scale; if (speed >= mParameters.highThreshold) { // Apply full acceleration above the high speed threshold. scale *= mParameters.acceleration; @@ -85,10 +88,9 @@ void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { if (DEBUG_ACCELERATION) { ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): " - "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f", - mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, - mParameters.acceleration, - vx, vy, speed, scale / mParameters.scale); + "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f", + mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, + mParameters.acceleration, *vx, *vy, speed, scale / mParameters.scale); } } else { diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index 76aaf61da7..4f91af14ea 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -125,29 +125,39 @@ static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool r // --- VelocityTracker --- +const std::set<int32_t> VelocityTracker::SUPPORTED_AXES = {AMOTION_EVENT_AXIS_X, + AMOTION_EVENT_AXIS_Y}; + +const std::set<int32_t> VelocityTracker::PLANAR_AXES = {AMOTION_EVENT_AXIS_X, AMOTION_EVENT_AXIS_Y}; + VelocityTracker::VelocityTracker(const Strategy strategy) : mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) { - // Configure the strategy. - if (!configureStrategy(strategy)) { - ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy); - if (!configureStrategy(VelocityTracker::DEFAULT_STRATEGY)) { - LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%" PRId32 - "'!", - strategy); - } + // Configure the strategy for each axis. + for (int32_t axis : SUPPORTED_AXES) { + configureStrategy(axis, strategy); } } VelocityTracker::~VelocityTracker() { } -bool VelocityTracker::configureStrategy(Strategy strategy) { +void VelocityTracker::configureStrategy(int32_t axis, const Strategy strategy) { + std::unique_ptr<VelocityTrackerStrategy> createdStrategy; + if (strategy == VelocityTracker::Strategy::DEFAULT) { - mStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY); + createdStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY); } else { - mStrategy = createStrategy(strategy); + createdStrategy = createStrategy(strategy); + } + + if (createdStrategy == nullptr) { + ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy); + createdStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY); + LOG_ALWAYS_FATAL_IF(createdStrategy == nullptr, + "Could not create the default velocity tracker strategy '%" PRId32 "'!", + strategy); } - return mStrategy != nullptr; + mStrategies[axis] = std::move(createdStrategy); } std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy( @@ -201,8 +211,9 @@ std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy( void VelocityTracker::clear() { mCurrentPointerIdBits.clear(); mActivePointerId = -1; - - mStrategy->clear(); + for (int32_t axis : SUPPORTED_AXES) { + mStrategies[axis]->clear(); + } } void VelocityTracker::clearPointers(BitSet32 idBits) { @@ -213,14 +224,13 @@ void VelocityTracker::clearPointers(BitSet32 idBits) { mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1; } - mStrategy->clearPointers(idBits); + for (int32_t axis : SUPPORTED_AXES) { + mStrategies[axis]->clearPointers(idBits); + } } void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) { - LOG_ALWAYS_FATAL_IF(idBits.count() != positions.size(), - "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu", - idBits.count(), positions.size()); + const std::map<int32_t /*axis*/, std::vector<float>>& positions) { while (idBits.count() > MAX_POINTERS) { idBits.clearLastMarkedBit(); } @@ -232,7 +242,9 @@ void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, // We have not received any movements for too long. Assume that all pointers // have stopped. - mStrategy->clear(); + for (const auto& [_, strategy] : mStrategies) { + strategy->clear(); + } } mLastEventTime = eventTime; @@ -241,29 +253,37 @@ void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit(); } - mStrategy->addMovement(eventTime, idBits, positions); + for (const auto& [axis, positionValues] : positions) { + LOG_ALWAYS_FATAL_IF(idBits.count() != positionValues.size(), + "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu", + idBits.count(), positionValues.size()); + mStrategies[axis]->addMovement(eventTime, idBits, positionValues); + } if (DEBUG_VELOCITY) { ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", idBits=0x%08x, activePointerId=%d", eventTime, idBits.value, mActivePointerId); - for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) { - uint32_t id = iterBits.firstMarkedBit(); - uint32_t index = idBits.getIndexOfBit(id); - iterBits.clearBit(id); - Estimator estimator; - getEstimator(id, &estimator); - ALOGD(" %d: position (%0.3f, %0.3f), " - "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)", - id, positions[index].x, positions[index].y, int(estimator.degree), - vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(), - vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(), - estimator.confidence); + for (const auto& positionsEntry : positions) { + for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) { + uint32_t id = iterBits.firstMarkedBit(); + uint32_t index = idBits.getIndexOfBit(id); + iterBits.clearBit(id); + Estimator estimator; + getEstimator(positionsEntry.first, id, &estimator); + ALOGD(" %d: axis=%d, position=%0.3f, " + "estimator (degree=%d, coeff=%s, confidence=%f)", + id, positionsEntry.first, positionsEntry.second[index], int(estimator.degree), + vectorToString(estimator.coeff, estimator.degree + 1).c_str(), + estimator.confidence); + } } } } void VelocityTracker::addMovement(const MotionEvent* event) { + // Stores data about which axes to process based on the incoming motion event. + std::set<int32_t> axesToProcess; int32_t actionMasked = event->getActionMasked(); switch (actionMasked) { @@ -271,6 +291,9 @@ void VelocityTracker::addMovement(const MotionEvent* event) { case AMOTION_EVENT_ACTION_HOVER_ENTER: // Clear all pointers on down before adding the new movement. clear(); + for (int32_t axis : PLANAR_AXES) { + axesToProcess.insert(axis); + } break; case AMOTION_EVENT_ACTION_POINTER_DOWN: { // Start a new movement trace for a pointer that just went down. @@ -279,10 +302,16 @@ void VelocityTracker::addMovement(const MotionEvent* event) { BitSet32 downIdBits; downIdBits.markBit(event->getPointerId(event->getActionIndex())); clearPointers(downIdBits); + for (int32_t axis : PLANAR_AXES) { + axesToProcess.insert(axis); + } break; } case AMOTION_EVENT_ACTION_MOVE: case AMOTION_EVENT_ACTION_HOVER_MOVE: + for (int32_t axis : PLANAR_AXES) { + axesToProcess.insert(axis); + } break; case AMOTION_EVENT_ACTION_POINTER_UP: case AMOTION_EVENT_ACTION_UP: { @@ -293,7 +322,9 @@ void VelocityTracker::addMovement(const MotionEvent* event) { toString(delaySinceLastEvent).c_str()); // We have not received any movements for too long. Assume that all pointers // have stopped. - mStrategy->clear(); + for (int32_t axis : PLANAR_AXES) { + mStrategies[axis]->clear(); + } } // These actions because they do not convey any new information about // pointer movement. We also want to preserve the last known velocity of the pointers. @@ -325,37 +356,54 @@ void VelocityTracker::addMovement(const MotionEvent* event) { pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i)); } - std::vector<Position> positions; - positions.resize(pointerCount); + std::map<int32_t, std::vector<float>> positions; + for (int32_t axis : axesToProcess) { + positions[axis].resize(pointerCount); + } size_t historySize = event->getHistorySize(); for (size_t h = 0; h <= historySize; h++) { nsecs_t eventTime = event->getHistoricalEventTime(h); - for (size_t i = 0; i < pointerCount; i++) { - uint32_t index = pointerIndex[i]; - positions[index].x = event->getHistoricalX(i, h); - positions[index].y = event->getHistoricalY(i, h); + for (int32_t axis : axesToProcess) { + for (size_t i = 0; i < pointerCount; i++) { + positions[axis][pointerIndex[i]] = event->getHistoricalAxisValue(axis, i, h); + } } addMovement(eventTime, idBits, positions); } } -bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const { +std::optional<float> VelocityTracker::getVelocity(int32_t axis, uint32_t id) const { Estimator estimator; - if (getEstimator(id, &estimator) && estimator.degree >= 1) { - *outVx = estimator.xCoeff[1]; - *outVy = estimator.yCoeff[1]; - return true; + bool validVelocity = getEstimator(axis, id, &estimator) && estimator.degree >= 1; + if (validVelocity) { + return estimator.coeff[1]; } - *outVx = 0; - *outVy = 0; - return false; + return {}; } -bool VelocityTracker::getEstimator(uint32_t id, Estimator* outEstimator) const { - return mStrategy->getEstimator(id, outEstimator); +void VelocityTracker::populateComputedVelocity(ComputedVelocity& computedVelocity, int32_t units, + float maxVelocity) { + for (int32_t axis : SUPPORTED_AXES) { + BitSet32 copyIdBits = BitSet32(mCurrentPointerIdBits); + while (!copyIdBits.isEmpty()) { + uint32_t id = copyIdBits.clearFirstMarkedBit(); + std::optional<float> velocity = getVelocity(axis, id); + if (velocity) { + float adjustedVelocity = + std::clamp(*velocity * units / 1000, -maxVelocity, maxVelocity); + computedVelocity.addVelocity(axis, id, adjustedVelocity); + } + } + } } +bool VelocityTracker::getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const { + if (SUPPORTED_AXES.find(axis) == SUPPORTED_AXES.end()) { + return false; + } + return mStrategies.at(axis)->getEstimator(id, outEstimator); +} // --- LeastSquaresVelocityTrackerStrategy --- @@ -378,9 +426,8 @@ void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { mMovements[mIndex].idBits = remainingIdBits; } -void LeastSquaresVelocityTrackerStrategy::addMovement( - nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) { +void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::vector<float>& positions) { if (mMovements[mIndex].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include @@ -627,8 +674,7 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, outEstimator->clear(); // Iterate over movement samples in reverse time order and collect samples. - std::vector<float> x; - std::vector<float> y; + std::vector<float> positions; std::vector<float> w; std::vector<float> time; @@ -645,15 +691,13 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, break; } - const VelocityTracker::Position& position = movement.getPosition(id); - x.push_back(position.x); - y.push_back(position.y); + positions.push_back(movement.getPosition(id)); w.push_back(chooseWeight(index)); time.push_back(-age * 0.000000001f); index = (index == 0 ? HISTORY_SIZE : index) - 1; - } while (x.size() < HISTORY_SIZE); + } while (positions.size() < HISTORY_SIZE); - const size_t m = x.size(); + const size_t m = positions.size(); if (m == 0) { return false; // no data } @@ -666,39 +710,36 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, if (degree == 2 && mWeighting == WEIGHTING_NONE) { // Optimize unweighted, quadratic polynomial fit - std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x); - std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y); - if (xCoeff && yCoeff) { + std::optional<std::array<float, 3>> coeff = + solveUnweightedLeastSquaresDeg2(time, positions); + if (coeff) { outEstimator->time = newestMovement.eventTime; outEstimator->degree = 2; outEstimator->confidence = 1; for (size_t i = 0; i <= outEstimator->degree; i++) { - outEstimator->xCoeff[i] = (*xCoeff)[i]; - outEstimator->yCoeff[i] = (*yCoeff)[i]; + outEstimator->coeff[i] = (*coeff)[i]; } return true; } } else if (degree >= 1) { // General case for an Nth degree polynomial fit - float xdet, ydet; + float det; uint32_t n = degree + 1; - if (solveLeastSquares(time, x, w, n, outEstimator->xCoeff, &xdet) && - solveLeastSquares(time, y, w, n, outEstimator->yCoeff, &ydet)) { + if (solveLeastSquares(time, positions, w, n, outEstimator->coeff, &det)) { outEstimator->time = newestMovement.eventTime; outEstimator->degree = degree; - outEstimator->confidence = xdet * ydet; + outEstimator->confidence = det; - ALOGD_IF(DEBUG_STRATEGY, "estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f", - int(outEstimator->degree), vectorToString(outEstimator->xCoeff, n).c_str(), - vectorToString(outEstimator->yCoeff, n).c_str(), outEstimator->confidence); + ALOGD_IF(DEBUG_STRATEGY, "estimate: degree=%d, coeff=%s, confidence=%f", + int(outEstimator->degree), vectorToString(outEstimator->coeff, n).c_str(), + outEstimator->confidence); return true; } } // No velocity data available for this pointer, but we do have its current position. - outEstimator->xCoeff[0] = x[0]; - outEstimator->yCoeff[0] = y[0]; + outEstimator->coeff[0] = positions[0]; outEstimator->time = newestMovement.eventTime; outEstimator->degree = 0; outEstimator->confidence = 1; @@ -790,18 +831,17 @@ void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { mPointerIdBits.value &= ~idBits.value; } -void IntegratingVelocityTrackerStrategy::addMovement( - nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) { +void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::vector<float>& positions) { uint32_t index = 0; for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) { uint32_t id = iterIdBits.clearFirstMarkedBit(); State& state = mPointerState[id]; - const VelocityTracker::Position& position = positions[index++]; + const float position = positions[index++]; if (mPointerIdBits.hasBit(id)) { - updateState(state, eventTime, position.x, position.y); + updateState(state, eventTime, position); } else { - initState(state, eventTime, position.x, position.y); + initState(state, eventTime, position); } } @@ -821,21 +861,18 @@ bool IntegratingVelocityTrackerStrategy::getEstimator(uint32_t id, return false; } -void IntegratingVelocityTrackerStrategy::initState(State& state, - nsecs_t eventTime, float xpos, float ypos) const { +void IntegratingVelocityTrackerStrategy::initState(State& state, nsecs_t eventTime, + float pos) const { state.updateTime = eventTime; state.degree = 0; - state.xpos = xpos; - state.xvel = 0; - state.xaccel = 0; - state.ypos = ypos; - state.yvel = 0; - state.yaccel = 0; + state.pos = pos; + state.accel = 0; + state.vel = 0; } -void IntegratingVelocityTrackerStrategy::updateState(State& state, - nsecs_t eventTime, float xpos, float ypos) const { +void IntegratingVelocityTrackerStrategy::updateState(State& state, nsecs_t eventTime, + float pos) const { const nsecs_t MIN_TIME_DELTA = 2 * NANOS_PER_MS; const float FILTER_TIME_CONSTANT = 0.010f; // 10 milliseconds @@ -846,34 +883,26 @@ void IntegratingVelocityTrackerStrategy::updateState(State& state, float dt = (eventTime - state.updateTime) * 0.000000001f; state.updateTime = eventTime; - float xvel = (xpos - state.xpos) / dt; - float yvel = (ypos - state.ypos) / dt; + float vel = (pos - state.pos) / dt; if (state.degree == 0) { - state.xvel = xvel; - state.yvel = yvel; + state.vel = vel; state.degree = 1; } else { float alpha = dt / (FILTER_TIME_CONSTANT + dt); if (mDegree == 1) { - state.xvel += (xvel - state.xvel) * alpha; - state.yvel += (yvel - state.yvel) * alpha; + state.vel += (vel - state.vel) * alpha; } else { - float xaccel = (xvel - state.xvel) / dt; - float yaccel = (yvel - state.yvel) / dt; + float accel = (vel - state.vel) / dt; if (state.degree == 1) { - state.xaccel = xaccel; - state.yaccel = yaccel; + state.accel = accel; state.degree = 2; } else { - state.xaccel += (xaccel - state.xaccel) * alpha; - state.yaccel += (yaccel - state.yaccel) * alpha; + state.accel += (accel - state.accel) * alpha; } - state.xvel += (state.xaccel * dt) * alpha; - state.yvel += (state.yaccel * dt) * alpha; + state.vel += (state.accel * dt) * alpha; } } - state.xpos = xpos; - state.ypos = ypos; + state.pos = pos; } void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state, @@ -881,12 +910,9 @@ void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state, outEstimator->time = state.updateTime; outEstimator->confidence = 1.0f; outEstimator->degree = state.degree; - outEstimator->xCoeff[0] = state.xpos; - outEstimator->xCoeff[1] = state.xvel; - outEstimator->xCoeff[2] = state.xaccel / 2; - outEstimator->yCoeff[0] = state.ypos; - outEstimator->yCoeff[1] = state.yvel; - outEstimator->yCoeff[2] = state.yaccel / 2; + outEstimator->coeff[0] = state.pos; + outEstimator->coeff[1] = state.vel; + outEstimator->coeff[2] = state.accel / 2; } @@ -909,9 +935,8 @@ void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { mMovements[mIndex].idBits = remainingIdBits; } -void LegacyVelocityTrackerStrategy::addMovement( - nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) { +void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::vector<float>& positions) { if (++mIndex == HISTORY_SIZE) { mIndex = 0; } @@ -959,12 +984,11 @@ bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, // 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. - float accumVx = 0; - float accumVy = 0; + float accumV = 0; uint32_t index = oldestIndex; uint32_t samplesUsed = 0; const Movement& oldestMovement = mMovements[oldestIndex]; - const VelocityTracker::Position& oldestPosition = oldestMovement.getPosition(id); + float oldestPosition = oldestMovement.getPosition(id); nsecs_t lastDuration = 0; while (numTouches-- > 1) { @@ -978,26 +1002,22 @@ bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, // the velocity. Consequently, we impose a minimum duration constraint on the // samples that we include in the calculation. if (duration >= MIN_DURATION) { - const VelocityTracker::Position& position = movement.getPosition(id); + float position = movement.getPosition(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); + float v = (position - oldestPosition) * scale; + accumV = (accumV * lastDuration + v * duration) / (duration + lastDuration); lastDuration = duration; samplesUsed += 1; } } // Report velocity. - const VelocityTracker::Position& newestPosition = newestMovement.getPosition(id); + float newestPosition = newestMovement.getPosition(id); outEstimator->time = newestMovement.eventTime; outEstimator->confidence = 1; - outEstimator->xCoeff[0] = newestPosition.x; - outEstimator->yCoeff[0] = newestPosition.y; + outEstimator->coeff[0] = newestPosition; if (samplesUsed) { - outEstimator->xCoeff[1] = accumVx; - outEstimator->yCoeff[1] = accumVy; + outEstimator->coeff[1] = accumV; outEstimator->degree = 1; } else { outEstimator->degree = 0; @@ -1024,9 +1044,8 @@ void ImpulseVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { mMovements[mIndex].idBits = remainingIdBits; } -void ImpulseVelocityTrackerStrategy::addMovement( - nsecs_t eventTime, BitSet32 idBits, - const std::vector<VelocityTracker::Position>& positions) { +void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::vector<float>& positions) { if (mMovements[mIndex].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include @@ -1163,8 +1182,7 @@ bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id, outEstimator->clear(); // Iterate over movement samples in reverse time order and collect samples. - float x[HISTORY_SIZE]; - float y[HISTORY_SIZE]; + float positions[HISTORY_SIZE]; nsecs_t time[HISTORY_SIZE]; size_t m = 0; // number of points that will be used for fitting size_t index = mIndex; @@ -1180,9 +1198,7 @@ bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id, break; } - const VelocityTracker::Position& position = movement.getPosition(id); - x[m] = position.x; - y[m] = position.y; + positions[m] = movement.getPosition(id); time[m] = movement.eventTime; index = (index == 0 ? HISTORY_SIZE : index) - 1; } while (++m < HISTORY_SIZE); @@ -1190,33 +1206,30 @@ bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id, if (m == 0) { return false; // no data } - outEstimator->xCoeff[0] = 0; - outEstimator->yCoeff[0] = 0; - outEstimator->xCoeff[1] = calculateImpulseVelocity(time, x, m); - outEstimator->yCoeff[1] = calculateImpulseVelocity(time, y, m); - outEstimator->xCoeff[2] = 0; - outEstimator->yCoeff[2] = 0; + outEstimator->coeff[0] = 0; + outEstimator->coeff[1] = calculateImpulseVelocity(time, positions, m); + outEstimator->coeff[2] = 0; + outEstimator->time = newestMovement.eventTime; outEstimator->degree = 2; // similar results to 2nd degree fit outEstimator->confidence = 1; - ALOGD_IF(DEBUG_STRATEGY, "velocity: (%.1f, %.1f)", outEstimator->xCoeff[1], - outEstimator->yCoeff[1]); + ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", outEstimator->coeff[1]); if (DEBUG_IMPULSE) { // TODO(b/134179997): delete this block once the switch to 'impulse' is complete. - // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons + // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons. + // X axis chosen arbitrarily for velocity comparisons. VelocityTracker lsq2(VelocityTracker::Strategy::LSQ2); BitSet32 idBits; const uint32_t pointerId = 0; idBits.markBit(pointerId); for (ssize_t i = m - 1; i >= 0; i--) { - lsq2.addMovement(time[i], idBits, {{x[i], y[i]}}); + lsq2.addMovement(time[i], idBits, {{AMOTION_EVENT_AXIS_X, {positions[i]}}}); } - float outVx = 0, outVy = 0; - const bool computed = lsq2.getVelocity(pointerId, &outVx, &outVy); - if (computed) { - ALOGD("lsq2 velocity: (%.1f, %.1f)", outVx, outVy); + std::optional<float> v = lsq2.getVelocity(AMOTION_EVENT_AXIS_X, pointerId); + if (v) { + ALOGD("lsq2 velocity: %.1f", *v); } else { ALOGD("lsq2 velocity: could not compute velocity"); } diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 4a445de3ac..0a37318d3d 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -16,9 +16,10 @@ #define LOG_TAG "VelocityTracker_test" +#include <math.h> #include <array> #include <chrono> -#include <math.h> +#include <limits> #include <android-base/stringprintf.h> #include <attestation/HmacKeyManager.h> @@ -198,25 +199,13 @@ static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy, const std::vector<MotionEventEntry>& motions, int32_t axis, float targetVelocity, uint32_t pointerId = DEFAULT_POINTER_ID) { VelocityTracker vt(strategy); - float Vx, Vy; std::vector<MotionEvent> events = createMotionEventStream(motions); for (MotionEvent event : events) { vt.addMovement(&event); } - vt.getVelocity(pointerId, &Vx, &Vy); - - switch (axis) { - case AMOTION_EVENT_AXIS_X: - checkVelocity(Vx, targetVelocity); - break; - case AMOTION_EVENT_AXIS_Y: - checkVelocity(Vy, targetVelocity); - break; - default: - FAIL() << "Axis must be either AMOTION_EVENT_AXIS_X or AMOTION_EVENT_AXIS_Y"; - } + checkVelocity(vt.getVelocity(axis, pointerId).value_or(0), targetVelocity); } static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions, @@ -226,17 +215,99 @@ static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry> for (MotionEvent event : events) { vt.addMovement(&event); } - VelocityTracker::Estimator estimator; - EXPECT_TRUE(vt.getEstimator(0, &estimator)); + VelocityTracker::Estimator estimatorX; + VelocityTracker::Estimator estimatorY; + EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_X, 0, &estimatorX)); + EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_Y, 0, &estimatorY)); for (size_t i = 0; i< coefficients.size(); i++) { - checkCoefficient(estimator.xCoeff[i], coefficients[i]); - checkCoefficient(estimator.yCoeff[i], coefficients[i]); + checkCoefficient(estimatorX.coeff[i], coefficients[i]); + checkCoefficient(estimatorY.coeff[i], coefficients[i]); } } /* * ================== VelocityTracker tests generated manually ===================================== */ +TEST_F(VelocityTrackerTest, TestComputedVelocity) { + VelocityTracker::ComputedVelocity computedVelocity; + + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 0 /*id*/, 200 /*velocity*/); + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 26U /*id*/, 400 /*velocity*/); + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 27U /*id*/, 650 /*velocity*/); + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID, 750 /*velocity*/); + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 0 /*id*/, 1000 /*velocity*/); + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 26U /*id*/, 2000 /*velocity*/); + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 27U /*id*/, 3000 /*velocity*/); + computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID, 4000 /*velocity*/); + + // Check the axes/indices with velocity. + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 0U /*id*/)), 200); + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 26U /*id*/)), 400); + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 27U /*id*/)), 650); + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID)), 750); + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 0U /*id*/)), 1000); + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 26U /*id*/)), 2000); + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 27U /*id*/)), 3000); + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID)), 4000); + for (uint32_t id = 0; id < 32; id++) { + // Since no data was added for AXIS_SCROLL, expect empty value for the axis for any id. + EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_SCROLL, id)) + << "Empty scroll data expected at id=" << id; + if (id == 0 || id == 26U || id == 27U || id == MAX_POINTER_ID) { + // Already checked above; continue. + continue; + } + // No data was added to X/Y for this id, expect empty value. + EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, id)) + << "Empty X data expected at id=" << id; + EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, id)) + << "Empty Y data expected at id=" << id; + } + // Out-of-bounds ids should given empty values. + EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, -1)); + EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID + 1)); +} + +TEST_F(VelocityTrackerTest, TestPopulateComputedVelocity) { + std::vector<MotionEventEntry> motions = { + {235089067457000ns, {{528.00, 0}}}, {235089084684000ns, {{527.00, 0}}}, + {235089093349000ns, {{527.00, 0}}}, {235089095677625ns, {{527.00, 0}}}, + {235089101859000ns, {{527.00, 0}}}, {235089110378000ns, {{528.00, 0}}}, + {235089112497111ns, {{528.25, 0}}}, {235089118760000ns, {{531.00, 0}}}, + {235089126686000ns, {{535.00, 0}}}, {235089129316820ns, {{536.33, 0}}}, + {235089135199000ns, {{540.00, 0}}}, {235089144297000ns, {{546.00, 0}}}, + {235089146136443ns, {{547.21, 0}}}, {235089152923000ns, {{553.00, 0}}}, + {235089160784000ns, {{559.00, 0}}}, {235089162955851ns, {{560.66, 0}}}, + {235089162955851ns, {{560.66, 0}}}, // ACTION_UP + }; + VelocityTracker vt(VelocityTracker::Strategy::IMPULSE); + std::vector<MotionEvent> events = createMotionEventStream(motions); + for (const MotionEvent& event : events) { + vt.addMovement(&event); + } + + float maxFloat = std::numeric_limits<float>::max(); + VelocityTracker::ComputedVelocity computedVelocity; + vt.populateComputedVelocity(computedVelocity, 1000 /* units */, maxFloat); + checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)), + 764.345703); + + // Expect X velocity to be scaled with respective to provided units. + vt.populateComputedVelocity(computedVelocity, 1000000 /* units */, maxFloat); + checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)), + 764345.703); + + // Expect X velocity to be clamped by provided max velocity. + vt.populateComputedVelocity(computedVelocity, 1000000 /* units */, 1000); + checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)), 1000); + + // All 0 data for Y; expect 0 velocity. + EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, DEFAULT_POINTER_ID)), 0); + + // No data for scroll-axis; expect empty velocity. + EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_SCROLL, DEFAULT_POINTER_ID)); +} + TEST_F(VelocityTrackerTest, ThreePointsPositiveVelocityTest) { // Same coordinate is reported 2 times in a row // It is difficult to determine the correct answer here, but at least the direction diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 539e24a098..8c241f2f09 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -2712,17 +2712,18 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Update the velocity tracker. { - std::vector<VelocityTracker::Position> positions; + std::vector<float> positionsX; + std::vector<float> positionsY; for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) { uint32_t id = idBits.clearFirstMarkedBit(); const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); - float x = pointer.x * mPointerXMovementScale; - float y = pointer.y * mPointerYMovementScale; - positions.push_back({x, y}); + positionsX.push_back(pointer.x * mPointerXMovementScale); + positionsY.push_back(pointer.y * mPointerYMovementScale); } mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits, - positions); + {{AMOTION_EVENT_AXIS_X, positionsX}, + {AMOTION_EVENT_AXIS_Y, positionsY}}); } // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning @@ -2829,9 +2830,12 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed; for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) { uint32_t id = idBits.clearFirstMarkedBit(); - float vx, vy; - if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) { - float speed = hypotf(vx, vy); + std::optional<float> vx = + mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, id); + std::optional<float> vy = + mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, id); + if (vx && vy) { + float speed = hypotf(*vx, *vy); if (speed > bestSpeed) { bestId = id; bestSpeed = speed; |