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