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 auto previousIndex = count - index;
73 auto lastIndex = count - 1;
74 TAG_LOGW(AceLogTag::ACE_INPUTKEYFLOW,
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]: "
77 "%{public}f",
78 previousIndex, y[previousIndex], lastIndex, y[lastIndex], previousIndex, x[previousIndex], lastIndex,
79 x[lastIndex]);
80 index++;
81 }
82 if (index > MAX_INDEX || index > count) {
83 return 0.0;
84 }
85 return (y[count - 1] - y[count - index]) / (x[count - 1] - x[count - index]); // 2: const
86 }
87
CorrectMonotonicAxisVelocity(const LeastSquareImpl & axis,double & v,double extremX)88 void CorrectMonotonicAxisVelocity(const LeastSquareImpl& axis, double& v, double extremX)
89 {
90 const auto& yVals = axis.GetYVals();
91 uint32_t valSize = std::min(static_cast<int32_t>(yVals.size()), VelocityTracker::POINT_NUMBER);
92 auto mononicity = GetMononicity(yVals, valSize);
93 if (!mononicity.has_value()) {
94 return;
95 }
96
97 // velocity is still, no need to do correction
98 if (mononicity.value() ? GreatOrEqual(v, 0) : LessOrEqual(v, 0)) {
99 return;
100 }
101
102 // Do correction
103 v = GetLinearSlope(axis);
104 CheckExtremePoint(axis, extremX, valSize);
105 }
106
UpdateAxisVelocity(LeastSquareImpl & axis)107 double UpdateAxisVelocity(LeastSquareImpl& axis)
108 {
109 std::vector<double> param(VelocityTracker::LEAST_SQUARE_PARAM_NUM, 0);
110 auto x = axis.GetXVals().back();
111 // curve is param[0] * x^2 + param[1] * x + param[2]
112 // the velocity is 2 * param[0] * x + param[1];
113 double velocity = 0.0;
114 if (axis.GetLeastSquareParams(param)) {
115 velocity = 2 * param[0] * x + param[1]; // 2: const of formula
116 double extremX = -0.5 * param[1] / param[0]; // 0.5: const of formula
117 CorrectMonotonicAxisVelocity(axis, velocity, extremX);
118 } else { // Use linear velocity instead
119 velocity = GetLinearSlope(axis);
120 }
121 return velocity;
122 }
123 } // namespace
124
UpdateTouchPoint(const TouchEvent & event,bool end)125 void VelocityTracker::UpdateTouchPoint(const TouchEvent& event, bool end)
126 {
127 if (isFirstPoint_) {
128 firstTrackPoint_ = event;
129 isFirstPoint_ = false;
130 } else {
131 delta_ = event.GetOffset() - lastPosition_;
132 lastPosition_ = event.GetOffset();
133 }
134 TouchEvent lastTrackPoint(currentTrackPoint_);
135 currentTrackPoint_ = event;
136 isVelocityDone_ = false;
137 std::chrono::duration<double> diffTime = event.time - lastTimePoint_;
138 lastTimePoint_ = event.time;
139 lastPosition_ = event.GetOffset();
140 // judge duration is 500ms.
141 static const double range = 0.5;
142 if (end) {
143 Offset oriDelta;
144 if (isFirstPoint_) {
145 oriDelta = delta_;
146 } else {
147 Offset lastMoveEvent = lastTrackPoint.GetOffset();
148 Offset upEvent = event.GetOffset();
149 oriDelta = upEvent - lastMoveEvent;
150 }
151 if (oriDelta.IsZero() && (diffTime.count() < range)) {
152 return;
153 }
154 }
155 // nanoseconds duration to seconds.
156 std::chrono::duration<double> duration = event.time - firstTrackPoint_.time;
157 auto seconds = duration.count();
158 xAxis_.UpdatePoint(seconds, event.x);
159 yAxis_.UpdatePoint(seconds, event.y);
160 }
161
UpdateTrackerPoint(double x,double y,const TimeStamp & time,bool end)162 void VelocityTracker::UpdateTrackerPoint(double x, double y, const TimeStamp& time, bool end)
163 {
164 Offset trackerPoint(x, y);
165 isVelocityDone_ = false;
166 if (isFirstPoint_) {
167 firstPointTime_ = time;
168 isFirstPoint_ = false;
169 } else {
170 delta_ = trackerPoint - lastPosition_;
171 lastPosition_ = trackerPoint;
172 }
173 std::chrono::duration<double> diffTime = time - lastTimePoint_;
174 lastTimePoint_ = time;
175 lastPosition_ = trackerPoint;
176 // judge duration is 500ms.
177 static const double range = 0.5;
178 if (delta_.IsZero() && end && (diffTime.count() < range)) {
179 return;
180 }
181 // nanoseconds duration to seconds.
182 std::chrono::duration<double> duration = time - firstPointTime_;
183 auto seconds = duration.count();
184 xAxis_.UpdatePoint(seconds, x);
185 yAxis_.UpdatePoint(seconds, y);
186 }
187
UpdateVelocity()188 void VelocityTracker::UpdateVelocity()
189 {
190 if (isVelocityDone_) {
191 return;
192 }
193 if (xAxis_.GetTrackNum() < 2) { // Velocity is calculated from at least 2 points.
194 return;
195 }
196
197 double xVelocity = UpdateAxisVelocity(xAxis_);
198 double yVelocity = UpdateAxisVelocity(yAxis_);
199 velocity_.SetOffsetPerSecond({ xVelocity, yVelocity });
200 isVelocityDone_ = true;
201 }
202
DumpVelocityPoints() const203 void VelocityTracker::DumpVelocityPoints() const
204 {
205 auto func = [](const LeastSquareImpl &axis, const char* str) {
206 const auto& xVal = axis.GetXVals();
207 const auto& yVal = axis.GetYVals();
208 int32_t i = static_cast<int32_t>(xVal.size());
209 for (int32_t cnt = VelocityTracker::POINT_NUMBER; i > 0 && cnt > 0; --cnt) {
210 --i;
211 LOGI("%{public}s last tracker points[%{public}d] x=%{public}f y=%{public}f", str, cnt, xVal[i], yVal[i]);
212 }
213 };
214 func(xAxis_, "xAxis");
215 func(yAxis_, "yAxis");
216 }
217 } // namespace OHOS::Ace
218