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