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