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