1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/gestures/velocity_tracker.h"
17
18 namespace OHOS::Ace {
19 int32_t VelocityTracker::POINT_NUMBER = SystemProperties::GetVelocityTrackerPointNumber();
20
21 namespace {
22 static constexpr int32_t MAX_INDEX = 4;
23
CheckExtremePoint(const LeastSquareImpl & axis,double extremX,uint32_t valSize)24 void CheckExtremePoint(const LeastSquareImpl& axis, double extremX, uint32_t valSize)
25 {
26 const auto& x = axis.GetXVals();
27 const auto& y = axis.GetYVals();
28 auto count = axis.GetTrackNum();
29
30 // filter quiver
31 if (LessNotEqual(std::fabs(y[count - 1] - y[count - 2]), 100)) { // 2: const, 100: quiver threshold
32 return;
33 }
34 // check if extrem point exists between axis's points.
35 if (GreatNotEqual(extremX, x[x.size() - valSize]) && LessNotEqual(extremX, x.back())) {
36 LOGI("Extrem point %{public}f exists between tracker points.", extremX);
37 }
38 // dump points
39 int32_t i = static_cast<int32_t>(x.size());
40 for (int32_t cnt = VelocityTracker::POINT_NUMBER; i > 0 && cnt > 0; --cnt) {
41 --i;
42 LOGI("Last tracker points[%{public}d] x=%{public}f y=%{public}f", cnt, x[i], y[i]);
43 }
44 }
45
46 // true for increasing, false for decreasing, nullopt for nonmonotonic
GetMononicity(const std::vector<double> & vals,uint32_t valSize)47 std::optional<bool> GetMononicity(const std::vector<double>& vals, uint32_t valSize)
48 {
49 std::optional<bool> compareResult;
50 for (uint32_t i = vals.size() - valSize + 1; i < vals.size(); ++i) {
51 double delta = vals[i] - vals[i - 1];
52 if (NearZero(delta)) {
53 continue;
54 }
55 bool isIncreasing = Positive(delta);
56 if (compareResult.value_or(isIncreasing) != isIncreasing) {
57 return std::nullopt;
58 }
59 compareResult = isIncreasing;
60 }
61 return compareResult;
62 }
63
GetLinearSlope(const LeastSquareImpl & axis)64 double GetLinearSlope(const LeastSquareImpl& axis)
65 {
66 const auto& x = axis.GetXVals();
67 const auto& y = axis.GetYVals();
68 auto count = axis.GetTrackNum();
69 int32_t index = 2;
70 while (index <= MAX_INDEX && count >= index) {
71 if (!NearEqual(x[count - 1], x[count - index])) {
72 break;
73 }
74 [[maybe_unused]] auto previousIndex = count - index;
75 [[maybe_unused]] auto lastIndex = count - 1;
76 TAG_LOGW(AceLogTag::ACE_INPUTKEYFLOW, SEC_PLD(,
77 "GetLinearSlope points time is same y[%{public}d]: %{public}f y[%{public}d]: %{public}f x[%{public}d]: "
78 "%{public}f x[%{public}d]: %{public}f"),
79 SEC_PARAM(previousIndex, y[previousIndex], lastIndex, y[lastIndex], previousIndex,
80 x[previousIndex], lastIndex, x[lastIndex]));
81 index++;
82 }
83 if (index > MAX_INDEX || index > count) {
84 return 0.0;
85 }
86 return (y[count - 1] - y[count - index]) / (x[count - 1] - x[count - index]); // 2: const
87 }
88
CorrectMonotonicAxisVelocity(const LeastSquareImpl & axis,double & v,double extremX)89 void CorrectMonotonicAxisVelocity(const LeastSquareImpl& axis, double& v, double extremX)
90 {
91 const auto& yVals = axis.GetYVals();
92 uint32_t valSize = std::min(static_cast<int32_t>(yVals.size()), VelocityTracker::POINT_NUMBER);
93 auto mononicity = GetMononicity(yVals, valSize);
94 if (!mononicity.has_value()) {
95 return;
96 }
97
98 // velocity is still, no need to do correction
99 if (mononicity.value() ? GreatOrEqual(v, 0) : LessOrEqual(v, 0)) {
100 return;
101 }
102
103 // Do correction
104 v = GetLinearSlope(axis);
105 CheckExtremePoint(axis, extremX, valSize);
106 }
107 } // namespace
108
UpdateAxisVelocity(LeastSquareImpl & axisRaw)109 double VelocityTracker::UpdateAxisVelocity(LeastSquareImpl& axisRaw)
110 {
111 LeastSquareImpl axis = axisRaw;
112 if (SystemProperties::IsVelocityWithinTimeWindow()) {
113 auto xTimes = axis.GetXVals();
114 auto timeThreshold = xTimes.back() - VelocityTracker::DURATION_LONGEST_THRESHOLD;
115 int32_t cnt = (std::lower_bound(xTimes.begin(), xTimes.end(), timeThreshold) - xTimes.begin());
116 while (cnt) {
117 axis.PopFrontPoint();
118 --cnt;
119 }
120 }
121 std::vector<double> param(VelocityTracker::LEAST_SQUARE_PARAM_NUM, 0);
122 auto x = axis.GetXVals().back();
123 // the velocity is 2 * param[0] * x + param[1]; with param[2] unused
124 double velocity = 0.0;
125 if (axis.GetLeastSquareParams(param)) {
126 velocity = 2 * param[0] * x + param[1]; // 2: const of formula
127 double extremX = -0.5 * param[1] / param[0]; // 0.5: const of formula
128 CorrectMonotonicAxisVelocity(axis, velocity, extremX);
129 } else { // Use linear velocity instead
130 velocity = GetLinearSlope(axis);
131 }
132 return velocity;
133 }
134
UpdateTouchPoint(const TouchEvent & event,bool end,float range)135 void VelocityTracker::UpdateTouchPoint(const TouchEvent& event, bool end, float range)
136 {
137 if (end && SystemProperties::IsVelocityWithoutUpPoint()) {
138 return;
139 }
140 if (isFirstPoint_) {
141 firstTrackPoint_ = event;
142 isFirstPoint_ = false;
143 } else {
144 delta_ = event.GetOffset() - lastPosition_;
145 lastPosition_ = event.GetOffset();
146 }
147 TouchEvent lastTrackPoint(currentTrackPoint_);
148 currentTrackPoint_ = event;
149 isVelocityDone_ = false;
150 std::chrono::duration<double> diffTime = event.time - lastTimePoint_;
151 lastTimePoint_ = event.time;
152 lastPosition_ = event.GetOffset();
153 if (end) {
154 Offset oriDelta;
155 if (isFirstPoint_) {
156 oriDelta = delta_;
157 } else {
158 Offset lastMoveEvent = lastTrackPoint.GetOffset();
159 Offset upEvent = event.GetOffset();
160 oriDelta = upEvent - lastMoveEvent;
161 }
162 if (oriDelta.IsZero() && (diffTime.count() < range)) {
163 return;
164 }
165 }
166 // nanoseconds duration to seconds.
167 std::chrono::duration<double> duration = event.time - firstTrackPoint_.time;
168 auto seconds = duration.count();
169 xAxis_.UpdatePoint(seconds, event.x);
170 yAxis_.UpdatePoint(seconds, event.y);
171 }
172
UpdateTrackerPoint(double x,double y,const TimeStamp & time,bool end)173 void VelocityTracker::UpdateTrackerPoint(double x, double y, const TimeStamp& time, bool end)
174 {
175 Offset trackerPoint(x, y);
176 isVelocityDone_ = false;
177 if (isFirstPoint_) {
178 firstPointTime_ = time;
179 isFirstPoint_ = false;
180 } else {
181 delta_ = trackerPoint - lastPosition_;
182 lastPosition_ = trackerPoint;
183 }
184 std::chrono::duration<double> diffTime = time - lastTimePoint_;
185 lastTimePoint_ = time;
186 lastPosition_ = trackerPoint;
187 // judge duration is 500ms.
188 static const double range = 0.5;
189 if (delta_.IsZero() && end && (diffTime.count() < range)) {
190 return;
191 }
192 // nanoseconds duration to seconds.
193 std::chrono::duration<double> duration = time - firstPointTime_;
194 auto seconds = duration.count();
195 xAxis_.UpdatePoint(seconds, x);
196 yAxis_.UpdatePoint(seconds, y);
197 }
198
UpdateVelocity()199 void VelocityTracker::UpdateVelocity()
200 {
201 if (isVelocityDone_) {
202 return;
203 }
204 if (xAxis_.GetTrackNum() < 2) { // Velocity is calculated from at least 2 points.
205 return;
206 }
207
208 double xVelocity = UpdateAxisVelocity(xAxis_);
209 double yVelocity = UpdateAxisVelocity(yAxis_);
210 velocity_.SetOffsetPerSecond({ xVelocity, yVelocity });
211 isVelocityDone_ = true;
212 }
213
DumpVelocityPoints() const214 void VelocityTracker::DumpVelocityPoints() const
215 {
216 auto func = [](const LeastSquareImpl &axis, const char* str) {
217 const auto& xVal = axis.GetXVals();
218 const auto& yVal = axis.GetYVals();
219 if (xVal.size() == 0 || yVal.size() == 0)
220 return;
221 int32_t i = static_cast<int32_t>(xVal.size());
222 auto baseVal = yVal[0];
223 std::stringstream oss;
224 oss << std::string(str);
225 for (int32_t cnt = VelocityTracker::POINT_NUMBER; i > 0 && cnt > 0; --cnt) {
226 --i;
227 if (SystemProperties::GetDebugEnabled()) {
228 TAG_LOGI(AceLogTag::ACE_GESTURE, "%{public}s last tracker point[%{public}d] x=%{public}f y=%{public}f",
229 str, cnt, xVal[i], yVal[i] - baseVal);
230 } else {
231 oss << " [" << std::to_string(cnt) << "] x " << std::to_string(xVal[i]) <<
232 " y " << std::to_string(yVal[i] - baseVal);
233 }
234 }
235 TAG_LOGI(AceLogTag::ACE_GESTURE, "%{public}s", oss.str().c_str());
236 };
237 func(xAxis_, "xAxis");
238 func(yAxis_, "yAxis");
239 }
240 } // namespace OHOS::Ace
241