Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5404600a authored by Yeabkal Wubshit's avatar Yeabkal Wubshit Committed by Android (Google) Code Review
Browse files

Merge "Make VelocityTracker 1D"

parents 07f9606b 37acf6e3
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -98,7 +98,7 @@ private:
    VelocityControlParameters mParameters;

    nsecs_t mLastMovementTime;
    VelocityTracker::Position mRawPosition;
    float mRawPositionX, mRawPositionY;
    VelocityTracker mVelocityTracker;
};

+74 −45
Original line number Diff line number Diff line
@@ -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;
            }
        }
    };

    // Creates a velocity tracker using the specified strategy.
    /*
     * 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 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;

    // 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.
    // 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;
+14 −12
Original line number Diff line number Diff line
@@ -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;
@@ -87,8 +90,7 @@ void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) {
                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);
                      mParameters.acceleration, *vx, *vy, speed, scale / mParameters.scale);
            }

        } else {
+163 −150

File changed.

Preview size limit exceeded, changes collapsed.

+89 −18
Original line number Diff line number Diff line
@@ -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
Loading