• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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