Skip to content

Commit e358853

Browse files
committed
flickable: calcualte touch velocity
1 parent 34237f5 commit e358853

2 files changed

Lines changed: 99 additions & 6 deletions

File tree

src/ruis/widget/base/touch/flickable.cpp

Lines changed: 97 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
2222
#include "flickable.hpp"
2323

2424
#include <utki/time.hpp>
25+
#include <utki/views.hpp>
2526

2627
using namespace ruis::touch;
2728

@@ -185,17 +186,26 @@ void flickable::update(uint32_t dt_ms)
185186
// TODO:
186187
}
187188

189+
namespace{
190+
constexpr auto max_history_records = 10;
191+
}
192+
188193
void flickable::push_touch_move_to_history(touch_move_info tm){
189-
constexpr auto max_history_records = 10;
190194
constexpr auto max_history_age_ms = 200;
191195
constexpr auto min_time_between_points_ms = 1;
192196

197+
// drop too old records
198+
while(!this->touch_history.empty() && tm.timestamp_ms - this->touch_history.front().timestamp_ms > max_history_age_ms){
199+
this->touch_history.pop_front();
200+
}
201+
193202
if(this->touch_history.empty()){
194203
this->touch_history.push_back(std::move(tm));
195204
return;
196205
}
197206

198207
utki::assert(!this->touch_history.empty(), SL);
208+
199209
auto& last_record = this->touch_history.back();
200210

201211
if(tm.timestamp_ms - last_record.timestamp_ms <= min_time_between_points_ms){
@@ -213,7 +223,7 @@ void flickable::push_touch_move_to_history(touch_move_info tm){
213223

214224
ruis::vec2 flickable::calculate_touch_velocity(){
215225
if(this->touch_history.size() < 2){
216-
std::cout << "flickable::calculate_touch_velocity(): return 0. this->touch_history.size() = " << this->touch_history.size() << std::endl;
226+
// std::cout << "flickable::calculate_touch_velocity(): return 0. this->touch_history.size() = " << this->touch_history.size() << std::endl;
217227
return {0};
218228
}
219229

@@ -228,13 +238,94 @@ ruis::vec2 flickable::calculate_touch_velocity(){
228238

229239
auto vel = dp / ruis::real(dt_ms);
230240

231-
std::cout << "flickable::calculate_touch_velocity(): return " << vel << ". this->touch_history.size() = " << this->touch_history.size() << std::endl;
241+
// std::cout << "flickable::calculate_touch_velocity(): return " << vel << ". this->touch_history.size() = " << this->touch_history.size() << std::endl;
232242
return vel;
233243
}
234244

235-
// TODO:
236-
ruis::vec2 vel = 0;
245+
// use Ordinary Least Squares method to fit a quadratic curve to the points of touch history
246+
ruis::vec2 vel = calculate_touch_velocity_for_at_least_3_points_using_ols_method();
237247

238-
std::cout << "flickable::calculate_touch_velocity(): return " << vel << ". this->touch_history.size() = " << this->touch_history.size() << std::endl;
248+
// std::cout << "flickable::calculate_touch_velocity(): return " << vel << ". this->touch_history.size() = " << this->touch_history.size() << std::endl;
239249
return vel;
240250
}
251+
252+
ruis::vec2 flickable::calculate_touch_velocity_for_at_least_3_points_using_ols_method(){
253+
// Ordinary Least Squares method fits quadratic curve y(t)=a*t^2+b*t+c to a set of n >= 3 points.
254+
// Coefficients a, b and c can be found by solving the following system of linear equations
255+
//
256+
// / sum(t^4) sum(t^3) sum(t^2) \ / a \ / sum(y * t^2) \
257+
// | sum(t^3) sum(t^2) sum(t) | * | b | = | sum(y * t) |
258+
// \ sum(t^2) sum(t) n / \ c / \ sum(y) /
259+
//
260+
// The touch velocity is effectively v = dy/dt = 2*a*t + b
261+
//
262+
// If we bias t foe each point to the last point, i.e. last point will be at t=0 and previous
263+
// points will be at some negative times, then v(0) = b.
264+
// Effecively the touch velocity is the b coefficient.
265+
266+
// need at least 3 points for quadratic curve fitting
267+
utki::assert(this->touch_history.size() >= 3, SL);
268+
269+
uint32_t time_bias_ms = this->touch_history.back().timestamp_ms;
270+
271+
ruis::real time_sum = 0;
272+
ruis::real time_pow2_sum = 0;
273+
ruis::real time_pow3_sum = 0;
274+
ruis::real time_pow4_sum = 0;
275+
ruis::vec2 pos_time_pow2_sum = 0;
276+
ruis::vec2 pos_time_sum = 0;
277+
ruis::vec2 pos_sum = 0;
278+
279+
for(const auto& rec : this->touch_history){
280+
// Here we still do arithmetics in uint32_t space.
281+
// The time_bias_ms is the latest timestamp, so subtract from it to get positive biased time.
282+
uint32_t shifted_time = time_bias_ms - rec.timestamp_ms;
283+
284+
// since we bias to lates timestamp, the actual biased time should be negative
285+
ruis::real t = -ruis::real(shifted_time);
286+
287+
ruis::real t_pow2 = t * t;
288+
ruis::real t_pow3 = t_pow2 * t;
289+
ruis::real t_pow4 = t_pow3 * t;
290+
291+
// std::cout << "shifted time = " << t << std::endl;
292+
293+
time_sum += t;
294+
time_pow2_sum += t_pow2;
295+
time_pow3_sum += t_pow3;
296+
time_pow4_sum += t_pow4;
297+
298+
pos_sum += rec.position;
299+
pos_time_sum += rec.position * t;
300+
pos_time_pow2_sum += rec.position * t_pow2;
301+
}
302+
303+
// clang-format off
304+
ruis::mat3 m{
305+
{time_pow4_sum, time_pow3_sum, time_pow2_sum},
306+
{time_pow3_sum, time_pow2_sum, time_sum},
307+
{time_pow2_sum, time_sum, this->touch_history.size()}
308+
};
309+
// clang-format on
310+
311+
auto det = m.det();
312+
313+
constexpr auto epsilon = ruis::real(1e-9);
314+
315+
using std::abs;
316+
if(abs(det) < epsilon){
317+
// the matrix is not invertible
318+
return {0};
319+
}
320+
321+
// replace 2nd column with right-hand side vector and calculate determinant
322+
ruis::vec2 det_b;
323+
for(auto [p0, p1, p2, out] : utki::views::zip(pos_time_pow2_sum, pos_time_sum, pos_sum, det_b)){
324+
m[0][1] = p0;
325+
m[1][1] = p1;
326+
m[2][1] = p2;
327+
out = m.det();
328+
}
329+
330+
return det_b.comp_div(det);
331+
}

src/ruis/widget/base/touch/flickable.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class flickable :
6060
void push_touch_move_to_history(touch_move_info tm);
6161
vec2 calculate_touch_velocity();
6262

63+
ruis::vec2 calculate_touch_velocity_for_at_least_3_points_using_ols_method();
64+
6365
public:
6466
event_status on_mouse_button(const mouse_button_event& event) override;
6567
event_status on_mouse_move(const mouse_move_event& event) override;

0 commit comments

Comments
 (0)