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 namespace {
20 static constexpr int32_t MAX_INDEX = 4;
21
CheckExtremePoint(const LeastSquareImpl & axis,double extremX,uint32_t valSize)22 void CheckExtremePoint(const LeastSquareImpl& axis, double extremX, uint32_t valSize)
23 {
24 const auto& x = axis.GetXVals();
25 const auto& y = axis.GetYVals();
26 auto count = axis.GetTrackNum();
27
28 // filter quiver
29 if (LessNotEqual(std::fabs(y[count - 1] - y[count - 2]), 100)) { // 2: const, 100: quiver threshold
30 return;
31 }
32 // check if extrem point exists between axis's points.
33 if (GreatNotEqual(extremX, x[x.size() - valSize]) && LessNotEqual(extremX, x.back())) {
34 LOGI("Extrem point %{public}f exists between tracker points.", extremX);
35 }
36 // dump points
37 int32_t i = static_cast<int32_t>(x.size());
38 for (int32_t cnt = VelocityTracker::POINT_NUMBER; i > 0 && cnt > 0; --cnt) {
39 --i;
40 LOGI("Last tracker points[%{public}d] x=%{public}f y=%{public}f", cnt, x[i], y[i]);
41 }
42 }
43
44 // true for increasing, false for decreasing, nullopt for nonmonotonic
GetMononicity(const std::vector<double> & vals,uint32_t valSize)45 std::optional<bool> GetMononicity(const std::vector<double>& vals, uint32_t valSize)
46 {
47 std::optional<bool> compareResult;
48 for (uint32_t i = vals.size() - valSize + 1; i < vals.size(); ++i) {
49 double delta = vals[i] - vals[i - 1];
50 if (NearZero(delta)) {
51 continue;
52 }
53 bool isIncreasing = Positive(delta);
54 if (compareResult.value_or(isIncreasing) != isIncreasing) {
55 return std::nullopt;
56 }
57 compareResult = isIncreasing;
58 }
59 return compareResult;
60 }
61
GetLinearSlope(const LeastSquareImpl & axis)62 double GetLinearSlope(const LeastSquareImpl& axis)
63 {
64 const auto& x = axis.GetXVals();
65 const auto& y = axis.GetYVals();
66 auto count = axis.GetTrackNum();
67 int32_t index = 2;
68 while (index <= MAX_INDEX && count >= index) {
69 if (!NearEqual(x[count - 1], x[count - index])) {
70 break;
71 }
72 [[maybe_unused]] auto previousIndex = count - index;
73 [[maybe_unused]] auto lastIndex = count - 1;
74 TAG_LOGW(AceLogTag::ACE_INPUTKEYFLOW, SEC_PLD(,
75 "GetLinearSlope points time is same y[%{public}d]: %{public}f y[%{public}d]: %{public}f x[%{public}d]: "
76 "%{public}f x[%{public}d]: %{public}f"),
77 SEC_PARAM(previousIndex, y[previousIndex], lastIndex, y[lastIndex], previousIndex,
78 x[previousIndex], lastIndex, x[lastIndex]));
79 index++;
80 }
81 if (index > MAX_INDEX || index > count) {
82 return 0.0;
83 }
84 return (y[count - 1] - y[count - index]) / (x[count - 1] - x[count - index]); // 2: const
85 }
86
CorrectMonotonicAxisVelocity(const LeastSquareImpl & axis,double & v,double extremX)87 void CorrectMonotonicAxisVelocity(const LeastSquareImpl& axis, double& v, double extremX)
88 {
89 const auto& yVals = axis.GetYVals();
90 uint32_t valSize = std::min(static_cast<int32_t>(yVals.size()), VelocityTracker::POINT_NUMBER);
91 auto mononicity = GetMononicity(yVals, valSize);
92 if (!mononicity.has_value()) {
93 return;
94 }
95
96 // velocity is still, no need to do correction
97 if (mononicity.value() ? GreatOrEqual(v, 0) : LessOrEqual(v, 0)) {
98 return;
99 }
100
101 // Do correction
102 v = GetLinearSlope(axis);
103 CheckExtremePoint(axis, extremX, valSize);
104 }
105
UpdateAxisVelocity(LeastSquareImpl & axis)106 double UpdateAxisVelocity(LeastSquareImpl& axis)
107 {
108 std::vector<double> param(VelocityTracker::LEAST_SQUARE_PARAM_NUM, 0);
109 auto x = axis.GetXVals().back();
110 // curve is param[0] * x^2 + param[1] * x + param[2]
111 // the velocity is 2 * param[0] * x + param[1];
112 double velocity = 0.0;
113 if (axis.GetLeastSquareParams(param)) {
114 velocity = 2 * param[0] * x + param[1]; // 2: const of formula
115 double extremX = -0.5 * param[1] / param[0]; // 0.5: const of formula
116 CorrectMonotonicAxisVelocity(axis, velocity, extremX);
117 } else { // Use linear velocity instead
118 velocity = GetLinearSlope(axis);
119 }
120 return velocity;
121 }
122 } // namespace
123
UpdateTouchPoint(const TouchEvent & event,bool end)124 void VelocityTracker::UpdateTouchPoint(const TouchEvent& event, bool end)
125 {
126 if (isFirstPoint_) {
127 firstTrackPoint_ = event;
128 isFirstPoint_ = false;
129 } else {
130 delta_ = event.GetOffset() - lastPosition_;
131 lastPosition_ = event.GetOffset();
132 }
133 TouchEvent lastTrackPoint(currentTrackPoint_);
134 currentTrackPoint_ = event;
135 isVelocityDone_ = false;
136 std::chrono::duration<double> diffTime = event.time - lastTimePoint_;
137 lastTimePoint_ = event.time;
138 lastPosition_ = event.GetOffset();
139 // judge duration is 500ms.
140 static const double range = 0.5;
141 if (end) {
142 Offset oriDelta;
143 if (isFirstPoint_) {
144 oriDelta = delta_;
145 } else {
146 Offset lastMoveEvent = lastTrackPoint.GetOffset();
147 Offset upEvent = event.GetOffset();
148 oriDelta = upEvent - lastMoveEvent;
149 }
150 if (oriDelta.IsZero() && (diffTime.count() < range)) {
151 return;
152 }
153 }
154 // nanoseconds duration to seconds.
155 std::chrono::duration<double> duration = event.time - firstTrackPoint_.time;
156 auto seconds = duration.count();
157 xAxis_.UpdatePoint(seconds, event.x);
158 yAxis_.UpdatePoint(seconds, event.y);
159 }
160
UpdateTrackerPoint(double x,double y,const TimeStamp & time,bool end)161 void VelocityTracker::UpdateTrackerPoint(double x, double y, const TimeStamp& time, bool end)
162 {
163 Offset trackerPoint(x, y);
164 isVelocityDone_ = false;
165 if (isFirstPoint_) {
166 firstPointTime_ = time;
167 isFirstPoint_ = false;
168 } else {
169 delta_ = trackerPoint - lastPosition_;
170 lastPosition_ = trackerPoint;
171 }
172 std::chrono::duration<double> diffTime = time - lastTimePoint_;
173 lastTimePoint_ = time;
174 lastPosition_ = trackerPoint;
175 // judge duration is 500ms.
176 static const double range = 0.5;
177 if (delta_.IsZero() && end && (diffTime.count() < range)) {
178 return;
179 }
180 // nanoseconds duration to seconds.
181 std::chrono::duration<double> duration = time - firstPointTime_;
182 auto seconds = duration.count();
183 xAxis_.UpdatePoint(seconds, x);
184 yAxis_.UpdatePoint(seconds, y);
185 }
186
UpdateVelocity()187 void VelocityTracker::UpdateVelocity()
188 {
189 if (isVelocityDone_) {
190 return;
191 }
192 if (xAxis_.GetTrackNum() < 2) { // Velocity is calculated from at least 2 points.
193 return;
194 }
195
196 double xVelocity = UpdateAxisVelocity(xAxis_);
197 double yVelocity = UpdateAxisVelocity(yAxis_);
198 velocity_.SetOffsetPerSecond({ xVelocity, yVelocity });
199 isVelocityDone_ = true;
200 }
201
DumpVelocityPoints() const202 void VelocityTracker::DumpVelocityPoints() const
203 {
204 auto func = [](const LeastSquareImpl &axis, const char* str) {
205 const auto& xVal = axis.GetXVals();
206 const auto& yVal = axis.GetYVals();
207 int32_t i = static_cast<int32_t>(xVal.size());
208 for (int32_t cnt = VelocityTracker::POINT_NUMBER; i > 0 && cnt > 0; --cnt) {
209 --i;
210 LOGI("%{public}s last tracker points[%{public}d] x=%{public}f y=%{public}f", str, cnt, xVal[i], yVal[i]);
211 }
212 };
213 func(xAxis_, "xAxis");
214 func(yAxis_, "yAxis");
215 }
216 } // namespace OHOS::Ace
217