• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <input/Input.h>
20 #include <utils/BitSet.h>
21 #include <utils/Timers.h>
22 #include <map>
23 #include <set>
24 
25 namespace android {
26 
27 class VelocityTrackerStrategy;
28 
29 /*
30  * Calculates the velocity of pointer movements over time.
31  */
32 class VelocityTracker {
33 public:
34     enum class Strategy : int32_t {
35         DEFAULT = -1,
36         MIN = 0,
37         IMPULSE = 0,
38         LSQ1 = 1,
39         LSQ2 = 2,
40         LSQ3 = 3,
41         WLSQ2_DELTA = 4,
42         WLSQ2_CENTRAL = 5,
43         WLSQ2_RECENT = 6,
44         INT1 = 7,
45         INT2 = 8,
46         LEGACY = 9,
47         MAX = LEGACY,
48         ftl_last = LEGACY,
49     };
50 
51     struct Estimator {
52         static const size_t MAX_DEGREE = 4;
53 
54         // Estimator time base.
55         nsecs_t time = 0;
56 
57         // Polynomial coefficients describing motion.
58         std::array<float, MAX_DEGREE + 1> coeff{};
59 
60         // Polynomial degree (number of coefficients), or zero if no information is
61         // available.
62         uint32_t degree = 0;
63 
64         // Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit).
65         float confidence = 0;
66     };
67 
68     /*
69      * Contains all available velocity data from a VelocityTracker.
70      */
71     struct ComputedVelocity {
getVelocityComputedVelocity72         inline std::optional<float> getVelocity(int32_t axis, int32_t id) const {
73             const auto& axisVelocities = mVelocities.find(axis);
74             if (axisVelocities == mVelocities.end()) {
75                 return {};
76             }
77 
78             const auto& axisIdVelocity = axisVelocities->second.find(id);
79             if (axisIdVelocity == axisVelocities->second.end()) {
80                 return {};
81             }
82 
83             return axisIdVelocity->second;
84         }
85 
addVelocityComputedVelocity86         inline void addVelocity(int32_t axis, int32_t id, float velocity) {
87             mVelocities[axis][id] = velocity;
88         }
89 
90     private:
91         std::map<int32_t /*axis*/, std::map<int32_t /*pointerId*/, float /*velocity*/>> mVelocities;
92     };
93 
94     // Creates a velocity tracker using the specified strategy for each supported axis.
95     // If strategy is not provided, uses the default strategy for the platform.
96     // TODO(b/32830165): support axis-specific strategies.
97     VelocityTracker(const Strategy strategy = Strategy::DEFAULT);
98 
99     /** Return true if the axis is supported for velocity tracking, false otherwise. */
100     static bool isAxisSupported(int32_t axis);
101 
102     // Resets the velocity tracker state.
103     void clear();
104 
105     // Resets the velocity tracker state for a specific pointer.
106     // Call this method when some pointers have changed and may be reusing
107     // an id that was assigned to a different pointer earlier.
108     void clearPointer(int32_t pointerId);
109 
110     // Adds movement information for a pointer for a specific axis
111     void addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis, float position);
112 
113     // Adds movement information for all pointers in a MotionEvent, including historical samples.
114     void addMovement(const MotionEvent* event);
115 
116     // Returns the velocity of the specified pointer id and axis in position units per second.
117     // Returns empty optional if there is insufficient movement information for the pointer, or if
118     // the given axis is not supported for velocity tracking.
119     std::optional<float> getVelocity(int32_t axis, int32_t pointerId) const;
120 
121     // Returns a ComputedVelocity instance with all available velocity data, using the given units
122     // (reference: units == 1 means "per millisecond"), and clamping each velocity between
123     // [-maxVelocity, maxVelocity], inclusive.
124     ComputedVelocity getComputedVelocity(int32_t units, float maxVelocity);
125 
126     // Gets an estimator for the recent movements of the specified pointer id for the given axis.
127     // Returns false and clears the estimator if there is no information available
128     // about the pointer.
129     std::optional<Estimator> getEstimator(int32_t axis, int32_t pointerId) const;
130 
131     // Gets the active pointer id, or -1 if none.
getActivePointerId()132     inline int32_t getActivePointerId() const { return mActivePointerId.value_or(-1); }
133 
134 private:
135     nsecs_t mLastEventTime;
136     BitSet32 mCurrentPointerIdBits;
137     std::optional<int32_t> mActivePointerId;
138 
139     // An override strategy passed in the constructor to be used for all axes.
140     // This strategy will apply to all axes, unless the default strategy is specified here.
141     // When default strategy is specified, then each axis will use a potentially different strategy
142     // based on a hardcoded mapping.
143     const Strategy mOverrideStrategy;
144     // Maps axes to their respective VelocityTrackerStrategy instances.
145     // Note that, only axes that have had MotionEvents (and not all supported axes) will be here.
146     std::map<int32_t /*axis*/, std::unique_ptr<VelocityTrackerStrategy>> mConfiguredStrategies;
147 
148     void configureStrategy(int32_t axis);
149 
150     // Generates a VelocityTrackerStrategy instance for the given Strategy type.
151     // The `deltaValues` parameter indicates whether or not the created strategy should treat motion
152     // values as deltas (and not as absolute values). This the parameter is applicable only for
153     // strategies that support differential axes.
154     static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy,
155                                                                    bool deltaValues);
156 };
157 
158 
159 /*
160  * Implements a particular velocity tracker algorithm.
161  */
162 class VelocityTrackerStrategy {
163 protected:
VelocityTrackerStrategy()164     VelocityTrackerStrategy() { }
165 
166 public:
~VelocityTrackerStrategy()167     virtual ~VelocityTrackerStrategy() { }
168 
169     virtual void clearPointer(int32_t pointerId) = 0;
170     virtual void addMovement(nsecs_t eventTime, int32_t pointerId, float position) = 0;
171     virtual std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const = 0;
172 };
173 
174 
175 /*
176  * Velocity tracker algorithm based on least-squares linear regression.
177  */
178 class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy {
179 public:
180     enum class Weighting {
181         // No weights applied.  All data points are equally reliable.
182         NONE,
183 
184         // Weight by time delta.  Data points clustered together are weighted less.
185         DELTA,
186 
187         // Weight such that points within a certain horizon are weighed more than those
188         // outside of that horizon.
189         CENTRAL,
190 
191         // Weight such that points older than a certain amount are weighed less.
192         RECENT,
193     };
194 
195     // Degree must be no greater than Estimator::MAX_DEGREE.
196     LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = Weighting::NONE);
197     ~LeastSquaresVelocityTrackerStrategy() override;
198 
199     void clearPointer(int32_t pointerId) override;
200     void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
201     std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
202 
203 private:
204     // Sample horizon.
205     // We don't use too much history by default since we want to react to quick
206     // changes in direction.
207     static const nsecs_t HORIZON = 100 * 1000000; // 100 ms
208 
209     // Number of samples to keep.
210     static const uint32_t HISTORY_SIZE = 20;
211 
212     struct Movement {
213         nsecs_t eventTime;
214         float position;
215     };
216 
217     float chooseWeight(int32_t pointerId, uint32_t index) const;
218 
219     const uint32_t mDegree;
220     const Weighting mWeighting;
221     std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex;
222     std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements;
223 };
224 
225 
226 /*
227  * Velocity tracker algorithm that uses an IIR filter.
228  */
229 class IntegratingVelocityTrackerStrategy : public VelocityTrackerStrategy {
230 public:
231     // Degree must be 1 or 2.
232     IntegratingVelocityTrackerStrategy(uint32_t degree);
233     ~IntegratingVelocityTrackerStrategy() override;
234 
235     void clearPointer(int32_t pointerId) override;
236     void addMovement(nsecs_t eventTime, int32_t pointerId, float positions) override;
237     std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
238 
239 private:
240     // Current state estimate for a particular pointer.
241     struct State {
242         nsecs_t updateTime;
243         uint32_t degree;
244 
245         float pos, vel, accel;
246     };
247 
248     const uint32_t mDegree;
249     BitSet32 mPointerIdBits;
250     State mPointerState[MAX_POINTER_ID + 1];
251 
252     void initState(State& state, nsecs_t eventTime, float pos) const;
253     void updateState(State& state, nsecs_t eventTime, float pos) const;
254     void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const;
255 };
256 
257 
258 /*
259  * Velocity tracker strategy used prior to ICS.
260  */
261 class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy {
262 public:
263     LegacyVelocityTrackerStrategy();
264     ~LegacyVelocityTrackerStrategy() override;
265 
266     void clearPointer(int32_t pointerId) override;
267     void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
268     std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
269 
270 private:
271     // Oldest sample to consider when calculating the velocity.
272     static const nsecs_t HORIZON = 200 * 1000000; // 100 ms
273 
274     // Number of samples to keep.
275     static const uint32_t HISTORY_SIZE = 20;
276 
277     // The minimum duration between samples when estimating velocity.
278     static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
279 
280     struct Movement {
281         nsecs_t eventTime;
282         float position;
283     };
284 
285     std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex;
286     std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements;
287 };
288 
289 class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy {
290 public:
291     ImpulseVelocityTrackerStrategy(bool deltaValues);
292     ~ImpulseVelocityTrackerStrategy() override;
293 
294     void clearPointer(int32_t pointerId) override;
295     void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
296     std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
297 
298 private:
299     // Sample horizon.
300     // We don't use too much history by default since we want to react to quick
301     // changes in direction.
302     static constexpr nsecs_t HORIZON = 100 * 1000000; // 100 ms
303 
304     // Number of samples to keep.
305     static constexpr size_t HISTORY_SIZE = 20;
306 
307     struct Movement {
308         nsecs_t eventTime;
309         float position;
310     };
311 
312     // Whether or not the input movement values for the strategy come in the form of delta values.
313     // If the input values are not deltas, the strategy needs to calculate deltas as part of its
314     // velocity calculation.
315     const bool mDeltaValues;
316 
317     std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex;
318     std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements;
319 };
320 
321 } // namespace android
322