Commit 33f13940 authored by krisgiesing's avatar krisgiesing

Merge pull request #1865 from krisgiesing/velocity-dart

Move velocity tracker to dart
parents 5ea50bf4 5e9b2fba
...@@ -10,8 +10,10 @@ export 'src/gestures/constants.dart'; ...@@ -10,8 +10,10 @@ export 'src/gestures/constants.dart';
export 'src/gestures/drag.dart'; export 'src/gestures/drag.dart';
export 'src/gestures/events.dart'; export 'src/gestures/events.dart';
export 'src/gestures/long_press.dart'; export 'src/gestures/long_press.dart';
export 'src/gestures/lsq_solver.dart';
export 'src/gestures/pointer_router.dart'; export 'src/gestures/pointer_router.dart';
export 'src/gestures/recognizer.dart'; export 'src/gestures/recognizer.dart';
export 'src/gestures/scale.dart'; export 'src/gestures/scale.dart';
export 'src/gestures/show_press.dart'; export 'src/gestures/show_press.dart';
export 'src/gestures/tap.dart'; export 'src/gestures/tap.dart';
export 'src/gestures/velocity_tracker.dart';
...@@ -8,6 +8,7 @@ import 'arena.dart'; ...@@ -8,6 +8,7 @@ import 'arena.dart';
import 'recognizer.dart'; import 'recognizer.dart';
import 'constants.dart'; import 'constants.dart';
import 'events.dart'; import 'events.dart';
import 'velocity_tracker.dart';
enum DragState { enum DragState {
ready, ready,
...@@ -25,9 +26,7 @@ typedef void GesturePanEndCallback(ui.Offset velocity); ...@@ -25,9 +26,7 @@ typedef void GesturePanEndCallback(ui.Offset velocity);
typedef void _GesturePolymorphicUpdateCallback<T>(T delta); typedef void _GesturePolymorphicUpdateCallback<T>(T delta);
int _eventTime(PointerInputEvent event) => (event.timeStamp * 1000.0).toInt(); // microseconds bool _isFlingGesture(GestureVelocity velocity) {
bool _isFlingGesture(ui.GestureVelocity velocity) {
double velocitySquared = velocity.x * velocity.x + velocity.y * velocity.y; double velocitySquared = velocity.x * velocity.x + velocity.y * velocity.y;
return velocity.isValid && return velocity.isValid &&
velocitySquared > kMinFlingVelocity * kMinFlingVelocity && velocitySquared > kMinFlingVelocity * kMinFlingVelocity &&
...@@ -50,11 +49,11 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecogniz ...@@ -50,11 +49,11 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecogniz
T _getDragDelta(PointerInputEvent event); T _getDragDelta(PointerInputEvent event);
bool get _hasSufficientPendingDragDeltaToAccept; bool get _hasSufficientPendingDragDeltaToAccept;
Map<int, ui.VelocityTracker> _velocityTrackers = new Map<int, ui.VelocityTracker>(); Map<int, VelocityTracker> _velocityTrackers = new Map<int, VelocityTracker>();
void addPointer(PointerInputEvent event) { void addPointer(PointerInputEvent event) {
startTrackingPointer(event.pointer); startTrackingPointer(event.pointer);
_velocityTrackers[event.pointer] = new ui.VelocityTracker(); _velocityTrackers[event.pointer] = new VelocityTracker();
if (_state == DragState.ready) { if (_state == DragState.ready) {
_state = DragState.possible; _state = DragState.possible;
_initialPosition = event.position; _initialPosition = event.position;
...@@ -65,9 +64,9 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecogniz ...@@ -65,9 +64,9 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecogniz
void handleEvent(PointerInputEvent event) { void handleEvent(PointerInputEvent event) {
assert(_state != DragState.ready); assert(_state != DragState.ready);
if (event.type == 'pointermove') { if (event.type == 'pointermove') {
ui.VelocityTracker tracker = _velocityTrackers[event.pointer]; VelocityTracker tracker = _velocityTrackers[event.pointer];
assert(tracker != null); assert(tracker != null);
tracker.addPosition(_eventTime(event), event.x, event.y); tracker.addPosition(event.timeStamp, event.x, event.y);
T delta = _getDragDelta(event); T delta = _getDragDelta(event);
if (_state == DragState.accepted) { if (_state == DragState.accepted) {
if (onUpdate != null) if (onUpdate != null)
...@@ -102,10 +101,10 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecogniz ...@@ -102,10 +101,10 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecogniz
bool wasAccepted = (_state == DragState.accepted); bool wasAccepted = (_state == DragState.accepted);
_state = DragState.ready; _state = DragState.ready;
if (wasAccepted && onEnd != null) { if (wasAccepted && onEnd != null) {
ui.VelocityTracker tracker = _velocityTrackers[pointer]; VelocityTracker tracker = _velocityTrackers[pointer];
assert(tracker != null); assert(tracker != null);
ui.GestureVelocity gestureVelocity = tracker.getVelocity(); GestureVelocity gestureVelocity = tracker.getVelocity();
ui.Offset velocity = ui.Offset.zero; ui.Offset velocity = ui.Offset.zero;
if (_isFlingGesture(gestureVelocity)) if (_isFlingGesture(gestureVelocity))
velocity = new ui.Offset(gestureVelocity.x, gestureVelocity.y); velocity = new ui.Offset(gestureVelocity.x, gestureVelocity.y);
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import "dart:math" as math;
import "dart:typed_data";
class _Vector {
_Vector(int size)
: _offset = 0, _length = size, _elements = new Float64List(size);
_Vector.fromValues(List<double> values)
: _offset = 0, _length = values.length, _elements = values;
_Vector.fromVOL(List<double> values, int offset, int length)
: _offset = offset, _length = length, _elements = values;
int get length => _length;
operator [](int i) => _elements[i + _offset];
operator []=(int i, double value) => _elements[i + _offset] = value;
operator *(_Vector a) {
double result = 0.0;
for (int i = 0; i < _length; i++) {
result += this[i] * a[i];
}
return result;
}
double norm() => math.sqrt(this * this);
String toString() {
String result = "";
for (int i = 0; i < _length; i++) {
if (i > 0)
result += ", ";
result += this[i].toString();
}
return result;
}
final int _offset;
final int _length;
final List<double> _elements;
}
class _Matrix {
_Matrix(int rows, int cols)
: _rows = rows,
_columns = cols,
_elements = new Float64List(rows * cols);
double get(int row, int col) => _elements[row * _columns + col];
void set(int row, int col, double value) {
_elements[row * _columns + col] = value;
}
_Vector getRow(int row) => new _Vector.fromVOL(
_elements,
row * _columns,
_columns
);
String toString() {
String result = "";
for (int i = 0; i < _rows; i++) {
if (i > 0)
result += "; ";
for (int j = 0; j < _columns; j++) {
if (j > 0)
result += ", ";
result += get(i, j).toString();
}
}
return result;
}
final int _rows;
final int _columns;
final List<double> _elements;
}
class PolynomialFit {
PolynomialFit(int degree) : coefficients = new Float64List(degree + 1);
final List<double> coefficients;
double confidence;
}
class LeastSquaresSolver {
LeastSquaresSolver(this.x, this.y, this.w) {
assert(x.length == y.length);
assert(y.length == w.length);
}
final List<double> x;
final List<double> y;
final List<double> w;
PolynomialFit solve(int degree) {
if (degree > x.length) // not enough data to fit a curve
return null;
PolynomialFit result = new PolynomialFit(degree);
// Shorthands for the purpose of notation equivalence to original C++ code
final int m = x.length;
final int n = degree + 1;
// Expand the X vector to a matrix A, pre-multiplied by the weights.
_Matrix a = new _Matrix(n, m);
for (int h = 0; h < m; h++) {
a.set(0, h, w[h]);
for (int i = 1; i < n; i++) {
a.set(i, h, a.get(i - 1, h) * x[h]);
}
}
// Apply the Gram-Schmidt process to A to obtain its QR decomposition.
// Orthonormal basis, column-major ordVectorer.
_Matrix q = new _Matrix(n, m);
// Upper triangular matrix, row-major order.
_Matrix r = new _Matrix(n, n);
for (int j = 0; j < n; j++) {
for (int h = 0; h < m; h++) {
q.set(j, h, a.get(j, h));
}
for (int i = 0; i < j; i++) {
double dot = q.getRow(j) * q.getRow(i);
for (int h = 0; h < m; h++) {
q.set(j, h, q.get(j, h) - dot * q.get(i, h));
}
}
double norm = q.getRow(j).norm();
if (norm < 0.000001) {
// vectors are linearly dependent or zero so no solution
return null;
}
double inverseNorm = 1.0 / norm;
for (int h = 0; h < m; h++) {
q.set(j, h, q.get(j, h) * inverseNorm);
}
for (int i = 0; i < n; i++) {
r.set(j, i, i < j ? 0.0 : q.getRow(j) * a.getRow(i));
}
}
// Solve R B = Qt W Y to find B. This is easy because R is upper triangular.
// We just work from bottom-right to top-left calculating B's coefficients.
_Vector wy = new _Vector(m);
for (int h = 0; h < m; h++) {
wy[h] = y[h] * w[h];
}
for (int i = n; i-- != 0;) {
result.coefficients[i] = q.getRow(i) * wy;
for (int j = n - 1; j > i; j--) {
result.coefficients[i] -= r.get(i, j) * result.coefficients[j];
}
result.coefficients[i] /= r.get(i, i);
}
// Calculate the coefficient of determination (confidence) as:
// 1 - (sumSquaredError / sumSquaredTotal)
// where sumSquaredError is the residual sum of squares (variance of the
// error), and sumSquaredTotal is the total sum of squares (variance of the
// data) where each has been weighted.
double yMean = 0.0;
for (int h = 0; h < m; h++) {
yMean += y[h];
}
yMean /= m;
double sumSquaredError = 0.0;
double sumSquaredTotal = 0.0;
for (int h = 0; h < m; h++) {
double err = y[h] - result.coefficients[0];
double term = 1.0;
for (int i = 1; i < n; i++) {
term *= x[h];
err -= term * result.coefficients[i];
}
sumSquaredError += w[h] * w[h] * err * err;
double v = y[h] - yMean;
sumSquaredTotal += w[h] * w[h] * v * v;
}
result.confidence = sumSquaredTotal > 0.000001 ?
1.0 - (sumSquaredError / sumSquaredTotal) :
1.0;
return result;
}
}
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' as ui;
import 'lsq_solver.dart';
class GestureVelocity {
GestureVelocity({ this.isValid: false, this.x: 0.0, this.y : 0.0 });
final bool isValid;
final double x;
final double y;
}
class _Estimator {
int degree;
double time;
List<double> xCoefficients;
List<double> yCoefficients;
double confidence;
String toString() {
String result = "Estimator(degree: " + degree.toString();
result += ", time: " + time.toString();
result += ", confidence: " + confidence.toString();
result += ", xCoefficients: " + xCoefficients.toString();
result += ", yCoefficients: " + yCoefficients.toString();
return result;
}
}
abstract class _VelocityTrackerStrategy {
void addMovement(double timeStamp, double x, double y);
bool getEstimator(_Estimator estimator);
void clear();
}
enum _Weighting {
weightingNone,
weightingDelta,
weightingCentral,
weightingRecent
}
class _Movement {
double eventTime = 0.0;
ui.Point position = ui.Point.origin;
}
class _LeastSquaresVelocityTrackerStrategy extends _VelocityTrackerStrategy {
static const int kHistorySize = 20;
static const int kHorizonMilliseconds = 100;
_LeastSquaresVelocityTrackerStrategy(this.degree, this.weighting)
: _index = 0, _movements = new List<_Movement>(kHistorySize);
final int degree;
final _Weighting weighting;
final List<_Movement> _movements;
int _index;
void addMovement(double timeStamp, double x, double y) {
if (++_index == kHistorySize)
_index = 0;
_Movement movement = _getMovement(_index);
movement.eventTime = timeStamp;
movement.position = new ui.Point(x, y);
}
bool getEstimator(_Estimator estimator) {
// Iterate over movement samples in reverse time order and collect samples.
List<double> x = new List<double>();
List<double> y = new List<double>();
List<double> w = new List<double>();
List<double> time = new List<double>();
int m = 0;
int index = _index;
_Movement newestMovement = _getMovement(index);
do {
_Movement movement = _getMovement(index);
double age = newestMovement.eventTime - movement.eventTime;
if (age > kHorizonMilliseconds)
break;
ui.Point position = movement.position;
x.add(position.x);
y.add(position.y);
w.add(_chooseWeight(index));
time.add(-age);
index = (index == 0 ? kHistorySize : index) - 1;
} while (++m < kHistorySize);
if (m == 0)
return false; // no data
// Calculate a least squares polynomial fit.
int n = degree;
if (n > m - 1)
n = m - 1;
if (n >= 1) {
LeastSquaresSolver xSolver = new LeastSquaresSolver(time, x, w);
PolynomialFit xFit = xSolver.solve(n);
if (xFit != null) {
LeastSquaresSolver ySolver = new LeastSquaresSolver(time, y, w);
PolynomialFit yFit = ySolver.solve(n);
if (yFit != null) {
estimator.xCoefficients = xFit.coefficients;
estimator.yCoefficients = yFit.coefficients;
estimator.time = newestMovement.eventTime;
estimator.degree = n;
estimator.confidence = xFit.confidence * yFit.confidence;
return true;
}
}
}
// No velocity data available for this pointer, but we do have its current
// position.
estimator.xCoefficients = [ x[0] ];
estimator.yCoefficients = [ y[0] ];
estimator.time = newestMovement.eventTime;
estimator.degree = 0;
estimator.confidence = 1.0;
return true;
}
void clear() {
_index = -1;
}
double _chooseWeight(int index) {
switch (weighting) {
case _Weighting.weightingDelta:
// Weight points based on how much time elapsed between them and the next
// point so that points that "cover" a shorter time span are weighed less.
// delta 0ms: 0.5
// delta 10ms: 1.0
if (index == _index) {
return 1.0;
}
int nextIndex = (index + 1) % kHistorySize;
double deltaMilliseconds = _movements[nextIndex].eventTime -
_movements[index].eventTime;
if (deltaMilliseconds < 0)
return 0.5;
if (deltaMilliseconds < 10)
return 0.5 + deltaMilliseconds * 0.05;
return 1.0;
case _Weighting.weightingCentral:
// Weight points based on their age, weighing very recent and very old
// points less.
// age 0ms: 0.5
// age 10ms: 1.0
// age 50ms: 1.0
// age 60ms: 0.5
double ageMilliseconds = _movements[_index].eventTime -
_movements[index].eventTime;
if (ageMilliseconds < 0)
return 0.5;
if (ageMilliseconds < 10)
return 0.5 + ageMilliseconds * 0.05;
if (ageMilliseconds < 50)
return 1.0;
if (ageMilliseconds < 60)
return 0.5 + (60 - ageMilliseconds) * 0.05;
return 0.5;
case _Weighting.weightingRecent:
// Weight points based on their age, weighing older points less.
// age 0ms: 1.0
// age 50ms: 1.0
// age 100ms: 0.5
double ageMilliseconds = _movements[_index].eventTime -
_movements[index].eventTime;
if (ageMilliseconds < 50) {
return 1.0;
}
if (ageMilliseconds < 100) {
return 0.5 + (100 - ageMilliseconds) * 0.01;
}
return 0.5;
case _Weighting.weightingNone:
default:
return 1.0;
}
}
_Movement _getMovement(int i) {
_Movement result = _movements[i];
if (result == null) {
result = new _Movement();
_movements[i] = result;
}
return result;
}
}
class VelocityTracker {
static const int kAssumePointerMoveStoppedTimeMs = 40;
VelocityTracker() : _lastTimeStamp = 0.0, _strategy = _createStrategy();
double _lastTimeStamp;
_VelocityTrackerStrategy _strategy;
void addPosition(double timeStamp, double x, double y) {
if ((timeStamp - _lastTimeStamp) >= kAssumePointerMoveStoppedTimeMs)
_strategy.clear();
_lastTimeStamp = timeStamp;
_strategy.addMovement(timeStamp, x, y);
}
GestureVelocity getVelocity() {
_Estimator estimator = new _Estimator();
if (_strategy.getEstimator(estimator) && estimator.degree >= 1) {
// convert from pixels/ms to pixels/s
return new GestureVelocity(
isValid: true,
x: estimator.xCoefficients[1] * 1000,
y: estimator.yCoefficients[1] * 1000
);
}
return new GestureVelocity(isValid: false, x: 0.0, y: 0.0);
}
static _VelocityTrackerStrategy _createStrategy() {
return new _LeastSquaresVelocityTrackerStrategy(2, _Weighting.weightingNone);
}
}
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'basic.dart';
import 'framework.dart';
enum EventRecorderMode {
stop,
record
}
typedef void EventsReadyCallback(List<PointerInputEvent> events);
/// EventRecorder is a utility widget that allows input events occurring
/// on the child to be recorded. The widget is initially in the "stop" state
/// by default. When in the "record" state, all pointer input events
/// occurring on the child are recorded into a buffer. When the "stop" state
/// is entered again, the onEventsReady callback is invoked with a list of
/// the recorded events.
class EventRecorder extends StatefulComponent {
EventRecorder({
Key key,
this.child,
this.mode: EventRecorderMode.stop,
this.onEventsReady
});
final Widget child;
final EventRecorderMode mode;
final EventsReadyCallback onEventsReady;
_EventRecorderState createState() => new _EventRecorderState();
}
class _EventRecorderState extends State<EventRecorder> {
List<PointerInputEvent> _events = new List<PointerInputEvent>();
void initState() {
super.initState();
}
void didUpdateConfig(EventRecorder oldConfig) {
if (oldConfig.mode == EventRecorderMode.record &&
config.mode == EventRecorderMode.stop) {
config.onEventsReady(_events);
_events.clear();
}
}
void _recordEvent(PointerInputEvent event) {
if (config.mode == EventRecorderMode.record) {
_events.add(event);
}
}
Widget build(BuildContext context) {
return new Listener(
onPointerDown: _recordEvent,
onPointerMove: _recordEvent,
onPointerUp: _recordEvent,
onPointerCancel: _recordEvent,
child: config.child
);
}
}
...@@ -12,6 +12,7 @@ export 'src/widgets/dismissable.dart'; ...@@ -12,6 +12,7 @@ export 'src/widgets/dismissable.dart';
export 'src/widgets/drag_target.dart'; export 'src/widgets/drag_target.dart';
export 'src/widgets/editable_text.dart'; export 'src/widgets/editable_text.dart';
export 'src/widgets/enter_exit_transition.dart'; export 'src/widgets/enter_exit_transition.dart';
export 'src/widgets/event_recorder.dart';
export 'src/widgets/focus.dart'; export 'src/widgets/focus.dart';
export 'src/widgets/framework.dart'; export 'src/widgets/framework.dart';
export 'src/widgets/gesture_detector.dart'; export 'src/widgets/gesture_detector.dart';
......
Benchmarks
==========
This directory (and its sub-directories) contain benchmarks for Flutter.
The reporting format for benchmarks is not standardized yet, so benchmarks
here are typically run by hand. To run a particular benchmark, use a command
similar to that used to run individual unit tests. For example:
```
sky/tools/run_tests --debug -r expanded benchmark/gestures/velocity_tracker_bench.dart
```
(The `-r expanded` flag prints one line per test, which can be more helpful
than the default format when running individual tests.)
import 'dart:ui' as ui;
import 'package:flutter/gestures.dart';
import 'package:test/test.dart';
import 'velocity_tracker_data.dart';
const int kNumIters = 10000;
const int kBatchSize = 1000;
const int kBatchOffset = 50;
const int kNumMarks = 130;
List<PointerInputEvent> _eventFromMap(List<Map> intermediate) {
List<PointerInputEvent> events = new List<PointerInputEvent>();
for (Map entry in intermediate)
events.add(_eventFor(entry));
return events;
}
PointerInputEvent _eventFor(Map entry) {
PointerInputEvent result = new PointerInputEvent(
type: entry['type'],
timeStamp: entry['timeStamp'],
pointer: entry['pointer'],
x: entry['x'],
y: entry['y']
);
return result;
}
void main() {
List<PointerInputEvent> events = _eventFromMap(velocityEventData);
test('Dart velocity tracker performance', () {
VelocityTracker tracker = new VelocityTracker();
Stopwatch watch = new Stopwatch();
watch.start();
for (int i = 0; i < kNumIters; i++) {
for (PointerInputEvent event in events) {
if (event.type == 'pointerdown' || event.type == 'pointermove')
tracker.addPosition(event.timeStamp, event.x, event.y);
if (event.type == 'pointerup')
tracker.getVelocity();
}
}
watch.stop();
print("Dart tracker: " + watch.elapsed.toString());
});
test('Native velocity tracker performance', () {
ui.VelocityTracker tracker = new ui.VelocityTracker();
Stopwatch watch = new Stopwatch();
watch.start();
for (int i = 0; i < kNumIters; i++) {
for (PointerInputEvent event in events) {
if (event.type == 'pointerdown' || event.type == 'pointermove')
tracker.addPosition((event.timeStamp*1000.0).toInt(), event.x, event.y);
if (event.type == 'pointerup')
tracker.getVelocity();
}
}
watch.stop();
print("Native tracker: " + watch.elapsed.toString());
});
}
final List<Map> velocityEventData = [
{
"type":"pointerdown",
"timeStamp":216690896.0,
"pointer":1,
"x":270.0,
"y":538.2857055664062
},
{
"type":"pointermove",
"timeStamp":216690906.0,
"pointer":1,
"x":270.0,
"y":538.2857055664062
},
{
"type":"pointermove",
"timeStamp":216690951.0,
"pointer":1,
"x":270.0,
"y":530.8571166992188
},
{
"type":"pointermove",
"timeStamp":216690959.0,
"pointer":1,
"x":270.0,
"y":526.8571166992188
},
{
"type":"pointermove",
"timeStamp":216690967.0,
"pointer":1,
"x":270.0,
"y":521.4285888671875
},
{
"type":"pointermove",
"timeStamp":216690975.0,
"pointer":1,
"x":270.0,
"y":515.4285888671875
},
{
"type":"pointermove",
"timeStamp":216690983.0,
"pointer":1,
"x":270.0,
"y":506.8571472167969
},
{
"type":"pointermove",
"timeStamp":216690991.0,
"pointer":1,
"x":268.8571472167969,
"y":496.0
},
{
"type":"pointermove",
"timeStamp":216690998.0,
"pointer":1,
"x":267.4285583496094,
"y":483.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691006.0,
"pointer":1,
"x":266.28570556640625,
"y":469.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691014.0,
"pointer":1,
"x":265.4285583496094,
"y":456.8571472167969
},
{
"type":"pointermove",
"timeStamp":216691021.0,
"pointer":1,
"x":264.28570556640625,
"y":443.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691029.0,
"pointer":1,
"x":264.0,
"y":431.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691036.0,
"pointer":1,
"x":263.4285583496094,
"y":421.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691044.0,
"pointer":1,
"x":263.4285583496094,
"y":412.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691052.0,
"pointer":1,
"x":263.4285583496094,
"y":404.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691060.0,
"pointer":1,
"x":263.4285583496094,
"y":396.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691068.0,
"pointer":1,
"x":264.5714416503906,
"y":390.0
},
{
"type":"pointermove",
"timeStamp":216691075.0,
"pointer":1,
"x":265.1428527832031,
"y":384.8571472167969
},
{
"type":"pointermove",
"timeStamp":216691083.0,
"pointer":1,
"x":266.0,
"y":380.28570556640625
},
{
"type":"pointermove",
"timeStamp":216691091.0,
"pointer":1,
"x":266.5714416503906,
"y":376.28570556640625
},
{
"type":"pointermove",
"timeStamp":216691098.0,
"pointer":1,
"x":267.1428527832031,
"y":373.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691106.0,
"pointer":1,
"x":267.71429443359375,
"y":370.28570556640625
},
{
"type":"pointermove",
"timeStamp":216691114.0,
"pointer":1,
"x":268.28570556640625,
"y":367.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691121.0,
"pointer":1,
"x":268.5714416503906,
"y":366.0
},
{
"type":"pointermove",
"timeStamp":216691130.0,
"pointer":1,
"x":268.8571472167969,
"y":364.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691137.0,
"pointer":1,
"x":269.1428527832031,
"y":363.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691145.0,
"pointer":1,
"x":269.1428527832031,
"y":362.8571472167969
},
{
"type":"pointermove",
"timeStamp":216691153.0,
"pointer":1,
"x":269.4285583496094,
"y":362.8571472167969
},
{
"type":"pointermove",
"timeStamp":216691168.0,
"pointer":1,
"x":268.5714416503906,
"y":365.4285583496094
},
{
"type":"pointermove",
"timeStamp":216691176.0,
"pointer":1,
"x":267.1428527832031,
"y":370.28570556640625
},
{
"type":"pointermove",
"timeStamp":216691183.0,
"pointer":1,
"x":265.4285583496094,
"y":376.8571472167969
},
{
"type":"pointermove",
"timeStamp":216691191.0,
"pointer":1,
"x":263.1428527832031,
"y":385.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691199.0,
"pointer":1,
"x":261.4285583496094,
"y":396.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691207.0,
"pointer":1,
"x":259.71429443359375,
"y":408.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691215.0,
"pointer":1,
"x":258.28570556640625,
"y":419.4285583496094
},
{
"type":"pointermove",
"timeStamp":216691222.0,
"pointer":1,
"x":257.4285583496094,
"y":428.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691230.0,
"pointer":1,
"x":256.28570556640625,
"y":436.0
},
{
"type":"pointermove",
"timeStamp":216691238.0,
"pointer":1,
"x":255.7142791748047,
"y":442.0
},
{
"type":"pointermove",
"timeStamp":216691245.0,
"pointer":1,
"x":255.14285278320312,
"y":447.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691253.0,
"pointer":1,
"x":254.85714721679688,
"y":453.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691261.0,
"pointer":1,
"x":254.57142639160156,
"y":458.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691268.0,
"pointer":1,
"x":254.2857208251953,
"y":463.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691276.0,
"pointer":1,
"x":254.2857208251953,
"y":470.28570556640625
},
{
"type":"pointermove",
"timeStamp":216691284.0,
"pointer":1,
"x":254.2857208251953,
"y":477.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691292.0,
"pointer":1,
"x":255.7142791748047,
"y":487.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691300.0,
"pointer":1,
"x":256.8571472167969,
"y":498.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691307.0,
"pointer":1,
"x":258.28570556640625,
"y":507.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691315.0,
"pointer":1,
"x":259.4285583496094,
"y":516.0
},
{
"type":"pointermove",
"timeStamp":216691323.0,
"pointer":1,
"x":260.28570556640625,
"y":521.7142944335938
},
{
"type":"pointerup",
"timeStamp":216691338.0,
"pointer":1,
"x":260.28570556640625,
"y":521.7142944335938
},
{
"type":"pointerdown",
"timeStamp":216691573.0,
"pointer":2,
"x":266.0,
"y":327.4285583496094
},
{
"type":"pointermove",
"timeStamp":216691588.0,
"pointer":2,
"x":266.0,
"y":327.4285583496094
},
{
"type":"pointermove",
"timeStamp":216691626.0,
"pointer":2,
"x":261.1428527832031,
"y":337.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691634.0,
"pointer":2,
"x":258.28570556640625,
"y":343.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691642.0,
"pointer":2,
"x":254.57142639160156,
"y":354.0
},
{
"type":"pointermove",
"timeStamp":216691650.0,
"pointer":2,
"x":250.2857208251953,
"y":368.28570556640625
},
{
"type":"pointermove",
"timeStamp":216691657.0,
"pointer":2,
"x":247.42857360839844,
"y":382.8571472167969
},
{
"type":"pointermove",
"timeStamp":216691665.0,
"pointer":2,
"x":245.14285278320312,
"y":397.4285583496094
},
{
"type":"pointermove",
"timeStamp":216691673.0,
"pointer":2,
"x":243.14285278320312,
"y":411.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691680.0,
"pointer":2,
"x":242.2857208251953,
"y":426.28570556640625
},
{
"type":"pointermove",
"timeStamp":216691688.0,
"pointer":2,
"x":241.7142791748047,
"y":440.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691696.0,
"pointer":2,
"x":241.7142791748047,
"y":454.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691703.0,
"pointer":2,
"x":242.57142639160156,
"y":467.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691712.0,
"pointer":2,
"x":243.42857360839844,
"y":477.4285583496094
},
{
"type":"pointermove",
"timeStamp":216691720.0,
"pointer":2,
"x":244.85714721679688,
"y":485.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691727.0,
"pointer":2,
"x":246.2857208251953,
"y":493.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691735.0,
"pointer":2,
"x":248.0,
"y":499.71429443359375
},
{
"type":"pointerup",
"timeStamp":216691750.0,
"pointer":2,
"x":248.0,
"y":499.71429443359375
},
{
"type":"pointerdown",
"timeStamp":216692255.0,
"pointer":3,
"x":249.42857360839844,
"y":351.4285583496094
},
{
"type":"pointermove",
"timeStamp":216692270.0,
"pointer":3,
"x":249.42857360839844,
"y":351.4285583496094
},
{
"type":"pointermove",
"timeStamp":216692309.0,
"pointer":3,
"x":246.2857208251953,
"y":361.71429443359375
},
{
"type":"pointermove",
"timeStamp":216692317.0,
"pointer":3,
"x":244.0,
"y":368.5714416503906
},
{
"type":"pointermove",
"timeStamp":216692325.0,
"pointer":3,
"x":241.42857360839844,
"y":377.71429443359375
},
{
"type":"pointermove",
"timeStamp":216692333.0,
"pointer":3,
"x":237.7142791748047,
"y":391.71429443359375
},
{
"type":"pointermove",
"timeStamp":216692340.0,
"pointer":3,
"x":235.14285278320312,
"y":406.5714416503906
},
{
"type":"pointermove",
"timeStamp":216692348.0,
"pointer":3,
"x":232.57142639160156,
"y":421.4285583496094
},
{
"type":"pointermove",
"timeStamp":216692356.0,
"pointer":3,
"x":230.2857208251953,
"y":436.5714416503906
},
{
"type":"pointermove",
"timeStamp":216692363.0,
"pointer":3,
"x":228.2857208251953,
"y":451.71429443359375
},
{
"type":"pointermove",
"timeStamp":216692371.0,
"pointer":3,
"x":227.42857360839844,
"y":466.0
},
{
"type":"pointermove",
"timeStamp":216692378.0,
"pointer":3,
"x":226.2857208251953,
"y":479.71429443359375
},
{
"type":"pointermove",
"timeStamp":216692387.0,
"pointer":3,
"x":225.7142791748047,
"y":491.71429443359375
},
{
"type":"pointermove",
"timeStamp":216692395.0,
"pointer":3,
"x":225.14285278320312,
"y":501.71429443359375
},
{
"type":"pointermove",
"timeStamp":216692402.0,
"pointer":3,
"x":224.85714721679688,
"y":509.1428527832031
},
{
"type":"pointermove",
"timeStamp":216692410.0,
"pointer":3,
"x":224.57142639160156,
"y":514.8571166992188
},
{
"type":"pointermove",
"timeStamp":216692418.0,
"pointer":3,
"x":224.2857208251953,
"y":519.4285888671875
},
{
"type":"pointermove",
"timeStamp":216692425.0,
"pointer":3,
"x":224.0,
"y":523.4285888671875
},
{
"type":"pointermove",
"timeStamp":216692433.0,
"pointer":3,
"x":224.0,
"y":527.1428833007812
},
{
"type":"pointermove",
"timeStamp":216692441.0,
"pointer":3,
"x":224.0,
"y":530.5714111328125
},
{
"type":"pointermove",
"timeStamp":216692448.0,
"pointer":3,
"x":224.0,
"y":533.1428833007812
},
{
"type":"pointermove",
"timeStamp":216692456.0,
"pointer":3,
"x":224.0,
"y":535.4285888671875
},
{
"type":"pointermove",
"timeStamp":216692464.0,
"pointer":3,
"x":223.7142791748047,
"y":536.8571166992188
},
{
"type":"pointermove",
"timeStamp":216692472.0,
"pointer":3,
"x":223.7142791748047,
"y":538.2857055664062
},
{
"type":"pointerup",
"timeStamp":216692487.0,
"pointer":3,
"x":223.7142791748047,
"y":538.2857055664062
},
{
"type":"pointerdown",
"timeStamp":216692678.0,
"pointer":4,
"x":221.42857360839844,
"y":526.2857055664062
},
{
"type":"pointermove",
"timeStamp":216692701.0,
"pointer":4,
"x":220.57142639160156,
"y":514.8571166992188
},
{
"type":"pointermove",
"timeStamp":216692708.0,
"pointer":4,
"x":220.2857208251953,
"y":508.0
},
{
"type":"pointermove",
"timeStamp":216692716.0,
"pointer":4,
"x":220.2857208251953,
"y":498.0
},
{
"type":"pointermove",
"timeStamp":216692724.0,
"pointer":4,
"x":221.14285278320312,
"y":484.28570556640625
},
{
"type":"pointermove",
"timeStamp":216692732.0,
"pointer":4,
"x":221.7142791748047,
"y":469.4285583496094
},
{
"type":"pointermove",
"timeStamp":216692740.0,
"pointer":4,
"x":223.42857360839844,
"y":453.1428527832031
},
{
"type":"pointermove",
"timeStamp":216692748.0,
"pointer":4,
"x":225.7142791748047,
"y":436.28570556640625
},
{
"type":"pointermove",
"timeStamp":216692755.0,
"pointer":4,
"x":229.14285278320312,
"y":418.28570556640625
},
{
"type":"pointermove",
"timeStamp":216692763.0,
"pointer":4,
"x":232.85714721679688,
"y":400.28570556640625
},
{
"type":"pointermove",
"timeStamp":216692770.0,
"pointer":4,
"x":236.85714721679688,
"y":382.5714416503906
},
{
"type":"pointermove",
"timeStamp":216692778.0,
"pointer":4,
"x":241.14285278320312,
"y":366.0
},
{
"type":"pointermove",
"timeStamp":216692786.0,
"pointer":4,
"x":244.85714721679688,
"y":350.28570556640625
},
{
"type":"pointermove",
"timeStamp":216692793.0,
"pointer":4,
"x":249.14285278320312,
"y":335.4285583496094
},
{
"type":"pointerup",
"timeStamp":216692809.0,
"pointer":4,
"x":249.14285278320312,
"y":335.4285583496094
},
{
"type":"pointerdown",
"timeStamp":216693222.0,
"pointer":5,
"x":224.0,
"y":545.4285888671875
},
{
"type":"pointermove",
"timeStamp":216693245.0,
"pointer":5,
"x":224.0,
"y":545.4285888671875
},
{
"type":"pointermove",
"timeStamp":216693275.0,
"pointer":5,
"x":222.85714721679688,
"y":535.1428833007812
},
{
"type":"pointermove",
"timeStamp":216693284.0,
"pointer":5,
"x":222.85714721679688,
"y":528.8571166992188
},
{
"type":"pointermove",
"timeStamp":216693291.0,
"pointer":5,
"x":222.2857208251953,
"y":518.5714111328125
},
{
"type":"pointermove",
"timeStamp":216693299.0,
"pointer":5,
"x":222.0,
"y":503.4285583496094
},
{
"type":"pointermove",
"timeStamp":216693307.0,
"pointer":5,
"x":222.0,
"y":485.4285583496094
},
{
"type":"pointermove",
"timeStamp":216693314.0,
"pointer":5,
"x":221.7142791748047,
"y":464.0
},
{
"type":"pointermove",
"timeStamp":216693322.0,
"pointer":5,
"x":222.2857208251953,
"y":440.28570556640625
},
{
"type":"pointerup",
"timeStamp":216693337.0,
"pointer":5,
"x":222.2857208251953,
"y":440.28570556640625
},
{
"type":"pointerdown",
"timeStamp":216693985.0,
"pointer":6,
"x":208.0,
"y":544.0
},
{
"type":"pointermove",
"timeStamp":216694047.0,
"pointer":6,
"x":208.57142639160156,
"y":532.2857055664062
},
{
"type":"pointermove",
"timeStamp":216694054.0,
"pointer":6,
"x":208.85714721679688,
"y":525.7142944335938
},
{
"type":"pointermove",
"timeStamp":216694062.0,
"pointer":6,
"x":208.85714721679688,
"y":515.1428833007812
},
{
"type":"pointermove",
"timeStamp":216694070.0,
"pointer":6,
"x":208.0,
"y":501.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694077.0,
"pointer":6,
"x":207.42857360839844,
"y":487.1428527832031
},
{
"type":"pointermove",
"timeStamp":216694085.0,
"pointer":6,
"x":206.57142639160156,
"y":472.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694092.0,
"pointer":6,
"x":206.57142639160156,
"y":458.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694100.0,
"pointer":6,
"x":206.57142639160156,
"y":446.0
},
{
"type":"pointermove",
"timeStamp":216694108.0,
"pointer":6,
"x":206.57142639160156,
"y":434.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694116.0,
"pointer":6,
"x":207.14285278320312,
"y":423.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694124.0,
"pointer":6,
"x":208.57142639160156,
"y":412.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694131.0,
"pointer":6,
"x":209.7142791748047,
"y":402.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694139.0,
"pointer":6,
"x":211.7142791748047,
"y":393.1428527832031
},
{
"type":"pointermove",
"timeStamp":216694147.0,
"pointer":6,
"x":213.42857360839844,
"y":385.1428527832031
},
{
"type":"pointermove",
"timeStamp":216694154.0,
"pointer":6,
"x":215.42857360839844,
"y":378.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694162.0,
"pointer":6,
"x":217.42857360839844,
"y":371.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694169.0,
"pointer":6,
"x":219.42857360839844,
"y":366.0
},
{
"type":"pointermove",
"timeStamp":216694177.0,
"pointer":6,
"x":221.42857360839844,
"y":360.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694185.0,
"pointer":6,
"x":223.42857360839844,
"y":356.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694193.0,
"pointer":6,
"x":225.14285278320312,
"y":352.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694201.0,
"pointer":6,
"x":226.85714721679688,
"y":348.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694209.0,
"pointer":6,
"x":228.2857208251953,
"y":346.0
},
{
"type":"pointermove",
"timeStamp":216694216.0,
"pointer":6,
"x":229.14285278320312,
"y":343.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694224.0,
"pointer":6,
"x":230.0,
"y":342.0
},
{
"type":"pointermove",
"timeStamp":216694232.0,
"pointer":6,
"x":230.57142639160156,
"y":340.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694239.0,
"pointer":6,
"x":230.85714721679688,
"y":339.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694247.0,
"pointer":6,
"x":230.85714721679688,
"y":339.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694262.0,
"pointer":6,
"x":230.2857208251953,
"y":342.0
},
{
"type":"pointermove",
"timeStamp":216694270.0,
"pointer":6,
"x":228.85714721679688,
"y":346.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694278.0,
"pointer":6,
"x":227.14285278320312,
"y":352.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694286.0,
"pointer":6,
"x":225.42857360839844,
"y":359.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694294.0,
"pointer":6,
"x":223.7142791748047,
"y":367.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694301.0,
"pointer":6,
"x":222.57142639160156,
"y":376.0
},
{
"type":"pointermove",
"timeStamp":216694309.0,
"pointer":6,
"x":221.42857360839844,
"y":384.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694317.0,
"pointer":6,
"x":220.85714721679688,
"y":392.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694324.0,
"pointer":6,
"x":220.0,
"y":400.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694332.0,
"pointer":6,
"x":219.14285278320312,
"y":409.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694339.0,
"pointer":6,
"x":218.85714721679688,
"y":419.1428527832031
},
{
"type":"pointermove",
"timeStamp":216694348.0,
"pointer":6,
"x":218.2857208251953,
"y":428.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694356.0,
"pointer":6,
"x":218.2857208251953,
"y":438.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694363.0,
"pointer":6,
"x":218.2857208251953,
"y":447.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694371.0,
"pointer":6,
"x":218.2857208251953,
"y":455.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694379.0,
"pointer":6,
"x":219.14285278320312,
"y":462.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694386.0,
"pointer":6,
"x":220.0,
"y":469.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694394.0,
"pointer":6,
"x":221.14285278320312,
"y":475.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694401.0,
"pointer":6,
"x":222.0,
"y":480.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694409.0,
"pointer":6,
"x":222.85714721679688,
"y":485.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694417.0,
"pointer":6,
"x":224.0,
"y":489.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694425.0,
"pointer":6,
"x":224.85714721679688,
"y":492.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694433.0,
"pointer":6,
"x":225.42857360839844,
"y":495.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694440.0,
"pointer":6,
"x":226.0,
"y":497.1428527832031
},
{
"type":"pointermove",
"timeStamp":216694448.0,
"pointer":6,
"x":226.2857208251953,
"y":498.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694456.0,
"pointer":6,
"x":226.2857208251953,
"y":498.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694471.0,
"pointer":6,
"x":226.2857208251953,
"y":498.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694479.0,
"pointer":6,
"x":226.2857208251953,
"y":496.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694486.0,
"pointer":6,
"x":226.2857208251953,
"y":493.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694494.0,
"pointer":6,
"x":226.2857208251953,
"y":490.0
},
{
"type":"pointermove",
"timeStamp":216694502.0,
"pointer":6,
"x":226.2857208251953,
"y":486.0
},
{
"type":"pointermove",
"timeStamp":216694510.0,
"pointer":6,
"x":226.2857208251953,
"y":480.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694518.0,
"pointer":6,
"x":226.2857208251953,
"y":475.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694525.0,
"pointer":6,
"x":226.2857208251953,
"y":468.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694533.0,
"pointer":6,
"x":226.2857208251953,
"y":461.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694541.0,
"pointer":6,
"x":226.2857208251953,
"y":452.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694548.0,
"pointer":6,
"x":226.57142639160156,
"y":442.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694556.0,
"pointer":6,
"x":226.57142639160156,
"y":432.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694564.0,
"pointer":6,
"x":226.85714721679688,
"y":423.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694571.0,
"pointer":6,
"x":227.42857360839844,
"y":416.0
},
{
"type":"pointermove",
"timeStamp":216694580.0,
"pointer":6,
"x":227.7142791748047,
"y":410.0
},
{
"type":"pointermove",
"timeStamp":216694587.0,
"pointer":6,
"x":228.2857208251953,
"y":404.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694595.0,
"pointer":6,
"x":228.85714721679688,
"y":399.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694603.0,
"pointer":6,
"x":229.14285278320312,
"y":395.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694610.0,
"pointer":6,
"x":229.42857360839844,
"y":392.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694618.0,
"pointer":6,
"x":229.7142791748047,
"y":390.0
},
{
"type":"pointermove",
"timeStamp":216694625.0,
"pointer":6,
"x":229.7142791748047,
"y":388.0
},
{
"type":"pointermove",
"timeStamp":216694633.0,
"pointer":6,
"x":229.7142791748047,
"y":386.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694641.0,
"pointer":6,
"x":229.7142791748047,
"y":386.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694648.0,
"pointer":6,
"x":229.7142791748047,
"y":386.0
},
{
"type":"pointermove",
"timeStamp":216694657.0,
"pointer":6,
"x":228.85714721679688,
"y":386.0
},
{
"type":"pointermove",
"timeStamp":216694665.0,
"pointer":6,
"x":228.0,
"y":388.0
},
{
"type":"pointermove",
"timeStamp":216694672.0,
"pointer":6,
"x":226.0,
"y":392.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694680.0,
"pointer":6,
"x":224.0,
"y":397.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694688.0,
"pointer":6,
"x":222.0,
"y":404.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694695.0,
"pointer":6,
"x":219.7142791748047,
"y":411.1428527832031
},
{
"type":"pointermove",
"timeStamp":216694703.0,
"pointer":6,
"x":218.2857208251953,
"y":418.0
},
{
"type":"pointermove",
"timeStamp":216694710.0,
"pointer":6,
"x":217.14285278320312,
"y":425.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694718.0,
"pointer":6,
"x":215.7142791748047,
"y":433.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694726.0,
"pointer":6,
"x":214.85714721679688,
"y":442.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694734.0,
"pointer":6,
"x":214.0,
"y":454.0
},
{
"type":"pointermove",
"timeStamp":216694742.0,
"pointer":6,
"x":214.0,
"y":469.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694749.0,
"pointer":6,
"x":215.42857360839844,
"y":485.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694757.0,
"pointer":6,
"x":217.7142791748047,
"y":502.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694765.0,
"pointer":6,
"x":221.14285278320312,
"y":521.4285888671875
},
{
"type":"pointermove",
"timeStamp":216694772.0,
"pointer":6,
"x":224.57142639160156,
"y":541.1428833007812
},
{
"type":"pointermove",
"timeStamp":216694780.0,
"pointer":6,
"x":229.14285278320312,
"y":561.1428833007812
},
{
"type":"pointermove",
"timeStamp":216694788.0,
"pointer":6,
"x":233.42857360839844,
"y":578.8571166992188
},
{
"type":"pointerup",
"timeStamp":216694802.0,
"pointer":6,
"x":233.42857360839844,
"y":578.8571166992188
},
{
"type":"pointerdown",
"timeStamp":216695344.0,
"pointer":7,
"x":253.42857360839844,
"y":310.5714416503906
},
{
"type":"pointermove",
"timeStamp":216695352.0,
"pointer":7,
"x":253.42857360839844,
"y":310.5714416503906
},
{
"type":"pointermove",
"timeStamp":216695359.0,
"pointer":7,
"x":252.85714721679688,
"y":318.0
},
{
"type":"pointermove",
"timeStamp":216695367.0,
"pointer":7,
"x":251.14285278320312,
"y":322.0
},
{
"type":"pointermove",
"timeStamp":216695375.0,
"pointer":7,
"x":248.85714721679688,
"y":327.1428527832031
},
{
"type":"pointermove",
"timeStamp":216695382.0,
"pointer":7,
"x":246.0,
"y":334.8571472167969
},
{
"type":"pointermove",
"timeStamp":216695390.0,
"pointer":7,
"x":242.57142639160156,
"y":344.5714416503906
},
{
"type":"pointermove",
"timeStamp":216695397.0,
"pointer":7,
"x":238.85714721679688,
"y":357.4285583496094
},
{
"type":"pointermove",
"timeStamp":216695406.0,
"pointer":7,
"x":235.7142791748047,
"y":371.71429443359375
},
{
"type":"pointermove",
"timeStamp":216695414.0,
"pointer":7,
"x":232.2857208251953,
"y":386.8571472167969
},
{
"type":"pointermove",
"timeStamp":216695421.0,
"pointer":7,
"x":229.42857360839844,
"y":402.0
},
{
"type":"pointermove",
"timeStamp":216695429.0,
"pointer":7,
"x":227.42857360839844,
"y":416.8571472167969
},
{
"type":"pointermove",
"timeStamp":216695437.0,
"pointer":7,
"x":226.2857208251953,
"y":431.4285583496094
},
{
"type":"pointermove",
"timeStamp":216695444.0,
"pointer":7,
"x":226.2857208251953,
"y":446.0
},
{
"type":"pointermove",
"timeStamp":216695452.0,
"pointer":7,
"x":227.7142791748047,
"y":460.28570556640625
},
{
"type":"pointermove",
"timeStamp":216695459.0,
"pointer":7,
"x":230.0,
"y":475.1428527832031
},
{
"type":"pointermove",
"timeStamp":216695467.0,
"pointer":7,
"x":232.2857208251953,
"y":489.71429443359375
},
{
"type":"pointermove",
"timeStamp":216695475.0,
"pointer":7,
"x":235.7142791748047,
"y":504.0
},
{
"type":"pointerup",
"timeStamp":216695490.0,
"pointer":7,
"x":235.7142791748047,
"y":504.0
},
{
"type":"pointerdown",
"timeStamp":216695885.0,
"pointer":8,
"x":238.85714721679688,
"y":524.0
},
{
"type":"pointermove",
"timeStamp":216695908.0,
"pointer":8,
"x":236.2857208251953,
"y":515.7142944335938
},
{
"type":"pointermove",
"timeStamp":216695916.0,
"pointer":8,
"x":234.85714721679688,
"y":509.1428527832031
},
{
"type":"pointermove",
"timeStamp":216695924.0,
"pointer":8,
"x":232.57142639160156,
"y":498.5714416503906
},
{
"type":"pointermove",
"timeStamp":216695931.0,
"pointer":8,
"x":230.57142639160156,
"y":483.71429443359375
},
{
"type":"pointermove",
"timeStamp":216695939.0,
"pointer":8,
"x":229.14285278320312,
"y":466.5714416503906
},
{
"type":"pointermove",
"timeStamp":216695947.0,
"pointer":8,
"x":229.14285278320312,
"y":446.5714416503906
},
{
"type":"pointermove",
"timeStamp":216695955.0,
"pointer":8,
"x":230.57142639160156,
"y":424.8571472167969
},
{
"type":"pointermove",
"timeStamp":216695963.0,
"pointer":8,
"x":232.57142639160156,
"y":402.28570556640625
},
{
"type":"pointermove",
"timeStamp":216695970.0,
"pointer":8,
"x":235.14285278320312,
"y":380.0
},
{
"type":"pointermove",
"timeStamp":216695978.0,
"pointer":8,
"x":238.57142639160156,
"y":359.4285583496094
},
{
"type":"pointerup",
"timeStamp":216695993.0,
"pointer":8,
"x":238.57142639160156,
"y":359.4285583496094
},
{
"type":"pointerdown",
"timeStamp":216696429.0,
"pointer":9,
"x":238.2857208251953,
"y":568.5714111328125
},
{
"type":"pointermove",
"timeStamp":216696459.0,
"pointer":9,
"x":234.0,
"y":560.0
},
{
"type":"pointermove",
"timeStamp":216696467.0,
"pointer":9,
"x":231.42857360839844,
"y":553.1428833007812
},
{
"type":"pointermove",
"timeStamp":216696475.0,
"pointer":9,
"x":228.2857208251953,
"y":543.1428833007812
},
{
"type":"pointermove",
"timeStamp":216696483.0,
"pointer":9,
"x":225.42857360839844,
"y":528.8571166992188
},
{
"type":"pointermove",
"timeStamp":216696491.0,
"pointer":9,
"x":223.14285278320312,
"y":512.2857055664062
},
{
"type":"pointermove",
"timeStamp":216696498.0,
"pointer":9,
"x":222.0,
"y":495.4285583496094
},
{
"type":"pointermove",
"timeStamp":216696506.0,
"pointer":9,
"x":221.7142791748047,
"y":477.4285583496094
},
{
"type":"pointermove",
"timeStamp":216696514.0,
"pointer":9,
"x":221.7142791748047,
"y":458.28570556640625
},
{
"type":"pointermove",
"timeStamp":216696521.0,
"pointer":9,
"x":223.14285278320312,
"y":438.0
},
{
"type":"pointermove",
"timeStamp":216696529.0,
"pointer":9,
"x":224.2857208251953,
"y":416.28570556640625
},
{
"type":"pointerup",
"timeStamp":216696544.0,
"pointer":9,
"x":224.2857208251953,
"y":416.28570556640625
},
{
"type":"pointerdown",
"timeStamp":216696974.0,
"pointer":10,
"x":218.57142639160156,
"y":530.5714111328125
},
{
"type":"pointermove",
"timeStamp":216697012.0,
"pointer":10,
"x":220.2857208251953,
"y":522.0
},
{
"type":"pointermove",
"timeStamp":216697020.0,
"pointer":10,
"x":221.14285278320312,
"y":517.7142944335938
},
{
"type":"pointermove",
"timeStamp":216697028.0,
"pointer":10,
"x":222.2857208251953,
"y":511.71429443359375
},
{
"type":"pointermove",
"timeStamp":216697036.0,
"pointer":10,
"x":224.0,
"y":504.28570556640625
},
{
"type":"pointermove",
"timeStamp":216697044.0,
"pointer":10,
"x":227.14285278320312,
"y":490.5714416503906
},
{
"type":"pointermove",
"timeStamp":216697052.0,
"pointer":10,
"x":229.42857360839844,
"y":474.0
},
{
"type":"pointermove",
"timeStamp":216697059.0,
"pointer":10,
"x":231.42857360839844,
"y":454.5714416503906
},
{
"type":"pointermove",
"timeStamp":216697067.0,
"pointer":10,
"x":233.7142791748047,
"y":431.1428527832031
},
{
"type":"pointerup",
"timeStamp":216697082.0,
"pointer":10,
"x":233.7142791748047,
"y":431.1428527832031
},
{
"type":"pointerdown",
"timeStamp":216697435.0,
"pointer":11,
"x":257.1428527832031,
"y":285.1428527832031
},
{
"type":"pointermove",
"timeStamp":216697465.0,
"pointer":11,
"x":251.7142791748047,
"y":296.8571472167969
},
{
"type":"pointermove",
"timeStamp":216697473.0,
"pointer":11,
"x":248.2857208251953,
"y":304.0
},
{
"type":"pointermove",
"timeStamp":216697481.0,
"pointer":11,
"x":244.57142639160156,
"y":314.8571472167969
},
{
"type":"pointermove",
"timeStamp":216697489.0,
"pointer":11,
"x":240.2857208251953,
"y":329.1428527832031
},
{
"type":"pointermove",
"timeStamp":216697497.0,
"pointer":11,
"x":236.85714721679688,
"y":345.1428527832031
},
{
"type":"pointermove",
"timeStamp":216697505.0,
"pointer":11,
"x":233.7142791748047,
"y":361.4285583496094
},
{
"type":"pointermove",
"timeStamp":216697512.0,
"pointer":11,
"x":231.14285278320312,
"y":378.28570556640625
},
{
"type":"pointermove",
"timeStamp":216697520.0,
"pointer":11,
"x":229.42857360839844,
"y":395.4285583496094
},
{
"type":"pointermove",
"timeStamp":216697528.0,
"pointer":11,
"x":229.42857360839844,
"y":412.8571472167969
},
{
"type":"pointermove",
"timeStamp":216697535.0,
"pointer":11,
"x":230.85714721679688,
"y":430.8571472167969
},
{
"type":"pointermove",
"timeStamp":216697543.0,
"pointer":11,
"x":233.42857360839844,
"y":449.71429443359375
},
{
"type":"pointerup",
"timeStamp":216697558.0,
"pointer":11,
"x":233.42857360839844,
"y":449.71429443359375
},
{
"type":"pointerdown",
"timeStamp":216697749.0,
"pointer":12,
"x":246.0,
"y":311.4285583496094
},
{
"type":"pointermove",
"timeStamp":216697780.0,
"pointer":12,
"x":244.57142639160156,
"y":318.28570556640625
},
{
"type":"pointermove",
"timeStamp":216697787.0,
"pointer":12,
"x":243.14285278320312,
"y":325.4285583496094
},
{
"type":"pointermove",
"timeStamp":216697795.0,
"pointer":12,
"x":241.42857360839844,
"y":336.0
},
{
"type":"pointermove",
"timeStamp":216697803.0,
"pointer":12,
"x":239.7142791748047,
"y":351.1428527832031
},
{
"type":"pointermove",
"timeStamp":216697811.0,
"pointer":12,
"x":238.2857208251953,
"y":368.5714416503906
},
{
"type":"pointermove",
"timeStamp":216697819.0,
"pointer":12,
"x":238.0,
"y":389.4285583496094
},
{
"type":"pointermove",
"timeStamp":216697826.0,
"pointer":12,
"x":239.14285278320312,
"y":412.0
},
{
"type":"pointermove",
"timeStamp":216697834.0,
"pointer":12,
"x":242.2857208251953,
"y":438.0
},
{
"type":"pointermove",
"timeStamp":216697842.0,
"pointer":12,
"x":247.42857360839844,
"y":466.8571472167969
},
{
"type":"pointermove",
"timeStamp":216697849.0,
"pointer":12,
"x":254.2857208251953,
"y":497.71429443359375
},
{
"type":"pointerup",
"timeStamp":216697864.0,
"pointer":12,
"x":254.2857208251953,
"y":497.71429443359375
},
{
"type":"pointerdown",
"timeStamp":216698321.0,
"pointer":13,
"x":250.0,
"y":306.0
},
{
"type":"pointermove",
"timeStamp":216698328.0,
"pointer":13,
"x":250.0,
"y":306.0
},
{
"type":"pointermove",
"timeStamp":216698344.0,
"pointer":13,
"x":249.14285278320312,
"y":314.0
},
{
"type":"pointermove",
"timeStamp":216698351.0,
"pointer":13,
"x":247.42857360839844,
"y":319.4285583496094
},
{
"type":"pointermove",
"timeStamp":216698359.0,
"pointer":13,
"x":245.14285278320312,
"y":326.8571472167969
},
{
"type":"pointermove",
"timeStamp":216698366.0,
"pointer":13,
"x":241.7142791748047,
"y":339.4285583496094
},
{
"type":"pointermove",
"timeStamp":216698374.0,
"pointer":13,
"x":238.57142639160156,
"y":355.71429443359375
},
{
"type":"pointermove",
"timeStamp":216698382.0,
"pointer":13,
"x":236.2857208251953,
"y":374.28570556640625
},
{
"type":"pointermove",
"timeStamp":216698390.0,
"pointer":13,
"x":235.14285278320312,
"y":396.5714416503906
},
{
"type":"pointermove",
"timeStamp":216698398.0,
"pointer":13,
"x":236.57142639160156,
"y":421.4285583496094
},
{
"type":"pointermove",
"timeStamp":216698406.0,
"pointer":13,
"x":241.14285278320312,
"y":451.4285583496094
},
{
"type":"pointerup",
"timeStamp":216698421.0,
"pointer":13,
"x":241.14285278320312,
"y":451.4285583496094
}
];
import 'package:flutter/gestures.dart';
import 'package:test/test.dart';
void main() {
approx(double value, double expectation) {
const double eps = 1e-6;
return (value - expectation).abs() < eps;
}
test('Least-squares fit: linear polynomial to line', () {
List<double> x = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
List<double> y = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
List<double> w = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
LeastSquaresSolver solver = new LeastSquaresSolver(x, y, w);
PolynomialFit fit = solver.solve(1);
expect(fit.coefficients.length, 2);
expect(approx(fit.coefficients[0], 1.0), isTrue);
expect(approx(fit.coefficients[1], 0.0), isTrue);
expect(approx(fit.confidence, 1.0), isTrue);
});
test('Least-squares fit: linear polynomial to sloped line', () {
List<double> x = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
List<double> y = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
List<double> w = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
LeastSquaresSolver solver = new LeastSquaresSolver(x, y, w);
PolynomialFit fit = solver.solve(1);
expect(fit.coefficients.length, 2);
expect(approx(fit.coefficients[0], 1.0), isTrue);
expect(approx(fit.coefficients[1], 1.0), isTrue);
expect(approx(fit.confidence, 1.0), isTrue);
});
test('Least-squares fit: quadratic polynomial to line', () {
List<double> x = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
List<double> y = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
List<double> w = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
LeastSquaresSolver solver = new LeastSquaresSolver(x, y, w);
PolynomialFit fit = solver.solve(2);
expect(fit.coefficients.length, 3);
expect(approx(fit.coefficients[0], 1.0), isTrue);
expect(approx(fit.coefficients[1], 0.0), isTrue);
expect(approx(fit.coefficients[2], 0.0), isTrue);
expect(approx(fit.confidence, 1.0), isTrue);
});
test('Least-squares fit: quadratic polynomial to sloped line', () {
List<double> x = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
List<double> y = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
List<double> w = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
LeastSquaresSolver solver = new LeastSquaresSolver(x, y, w);
PolynomialFit fit = solver.solve(2);
expect(fit.coefficients.length, 3);
expect(approx(fit.coefficients[0], 1.0), isTrue);
expect(approx(fit.coefficients[1], 1.0), isTrue);
expect(approx(fit.coefficients[2], 0.0), isTrue);
expect(approx(fit.confidence, 1.0), isTrue);
});
}
final List<Map> velocityEventData = [
{
"type":"pointerdown",
"timeStamp":216690896.0,
"pointer":1,
"x":270.0,
"y":538.2857055664062
},
{
"type":"pointermove",
"timeStamp":216690906.0,
"pointer":1,
"x":270.0,
"y":538.2857055664062
},
{
"type":"pointermove",
"timeStamp":216690951.0,
"pointer":1,
"x":270.0,
"y":530.8571166992188
},
{
"type":"pointermove",
"timeStamp":216690959.0,
"pointer":1,
"x":270.0,
"y":526.8571166992188
},
{
"type":"pointermove",
"timeStamp":216690967.0,
"pointer":1,
"x":270.0,
"y":521.4285888671875
},
{
"type":"pointermove",
"timeStamp":216690975.0,
"pointer":1,
"x":270.0,
"y":515.4285888671875
},
{
"type":"pointermove",
"timeStamp":216690983.0,
"pointer":1,
"x":270.0,
"y":506.8571472167969
},
{
"type":"pointermove",
"timeStamp":216690991.0,
"pointer":1,
"x":268.8571472167969,
"y":496.0
},
{
"type":"pointermove",
"timeStamp":216690998.0,
"pointer":1,
"x":267.4285583496094,
"y":483.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691006.0,
"pointer":1,
"x":266.28570556640625,
"y":469.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691014.0,
"pointer":1,
"x":265.4285583496094,
"y":456.8571472167969
},
{
"type":"pointermove",
"timeStamp":216691021.0,
"pointer":1,
"x":264.28570556640625,
"y":443.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691029.0,
"pointer":1,
"x":264.0,
"y":431.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691036.0,
"pointer":1,
"x":263.4285583496094,
"y":421.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691044.0,
"pointer":1,
"x":263.4285583496094,
"y":412.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691052.0,
"pointer":1,
"x":263.4285583496094,
"y":404.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691060.0,
"pointer":1,
"x":263.4285583496094,
"y":396.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691068.0,
"pointer":1,
"x":264.5714416503906,
"y":390.0
},
{
"type":"pointermove",
"timeStamp":216691075.0,
"pointer":1,
"x":265.1428527832031,
"y":384.8571472167969
},
{
"type":"pointermove",
"timeStamp":216691083.0,
"pointer":1,
"x":266.0,
"y":380.28570556640625
},
{
"type":"pointermove",
"timeStamp":216691091.0,
"pointer":1,
"x":266.5714416503906,
"y":376.28570556640625
},
{
"type":"pointermove",
"timeStamp":216691098.0,
"pointer":1,
"x":267.1428527832031,
"y":373.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691106.0,
"pointer":1,
"x":267.71429443359375,
"y":370.28570556640625
},
{
"type":"pointermove",
"timeStamp":216691114.0,
"pointer":1,
"x":268.28570556640625,
"y":367.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691121.0,
"pointer":1,
"x":268.5714416503906,
"y":366.0
},
{
"type":"pointermove",
"timeStamp":216691130.0,
"pointer":1,
"x":268.8571472167969,
"y":364.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691137.0,
"pointer":1,
"x":269.1428527832031,
"y":363.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691145.0,
"pointer":1,
"x":269.1428527832031,
"y":362.8571472167969
},
{
"type":"pointermove",
"timeStamp":216691153.0,
"pointer":1,
"x":269.4285583496094,
"y":362.8571472167969
},
{
"type":"pointermove",
"timeStamp":216691168.0,
"pointer":1,
"x":268.5714416503906,
"y":365.4285583496094
},
{
"type":"pointermove",
"timeStamp":216691176.0,
"pointer":1,
"x":267.1428527832031,
"y":370.28570556640625
},
{
"type":"pointermove",
"timeStamp":216691183.0,
"pointer":1,
"x":265.4285583496094,
"y":376.8571472167969
},
{
"type":"pointermove",
"timeStamp":216691191.0,
"pointer":1,
"x":263.1428527832031,
"y":385.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691199.0,
"pointer":1,
"x":261.4285583496094,
"y":396.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691207.0,
"pointer":1,
"x":259.71429443359375,
"y":408.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691215.0,
"pointer":1,
"x":258.28570556640625,
"y":419.4285583496094
},
{
"type":"pointermove",
"timeStamp":216691222.0,
"pointer":1,
"x":257.4285583496094,
"y":428.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691230.0,
"pointer":1,
"x":256.28570556640625,
"y":436.0
},
{
"type":"pointermove",
"timeStamp":216691238.0,
"pointer":1,
"x":255.7142791748047,
"y":442.0
},
{
"type":"pointermove",
"timeStamp":216691245.0,
"pointer":1,
"x":255.14285278320312,
"y":447.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691253.0,
"pointer":1,
"x":254.85714721679688,
"y":453.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691261.0,
"pointer":1,
"x":254.57142639160156,
"y":458.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691268.0,
"pointer":1,
"x":254.2857208251953,
"y":463.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691276.0,
"pointer":1,
"x":254.2857208251953,
"y":470.28570556640625
},
{
"type":"pointermove",
"timeStamp":216691284.0,
"pointer":1,
"x":254.2857208251953,
"y":477.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691292.0,
"pointer":1,
"x":255.7142791748047,
"y":487.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691300.0,
"pointer":1,
"x":256.8571472167969,
"y":498.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691307.0,
"pointer":1,
"x":258.28570556640625,
"y":507.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691315.0,
"pointer":1,
"x":259.4285583496094,
"y":516.0
},
{
"type":"pointermove",
"timeStamp":216691323.0,
"pointer":1,
"x":260.28570556640625,
"y":521.7142944335938
},
{
"type":"pointerup",
"timeStamp":216691338.0,
"pointer":1,
"x":260.28570556640625,
"y":521.7142944335938
},
{
"type":"pointerdown",
"timeStamp":216691573.0,
"pointer":2,
"x":266.0,
"y":327.4285583496094
},
{
"type":"pointermove",
"timeStamp":216691588.0,
"pointer":2,
"x":266.0,
"y":327.4285583496094
},
{
"type":"pointermove",
"timeStamp":216691626.0,
"pointer":2,
"x":261.1428527832031,
"y":337.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691634.0,
"pointer":2,
"x":258.28570556640625,
"y":343.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691642.0,
"pointer":2,
"x":254.57142639160156,
"y":354.0
},
{
"type":"pointermove",
"timeStamp":216691650.0,
"pointer":2,
"x":250.2857208251953,
"y":368.28570556640625
},
{
"type":"pointermove",
"timeStamp":216691657.0,
"pointer":2,
"x":247.42857360839844,
"y":382.8571472167969
},
{
"type":"pointermove",
"timeStamp":216691665.0,
"pointer":2,
"x":245.14285278320312,
"y":397.4285583496094
},
{
"type":"pointermove",
"timeStamp":216691673.0,
"pointer":2,
"x":243.14285278320312,
"y":411.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691680.0,
"pointer":2,
"x":242.2857208251953,
"y":426.28570556640625
},
{
"type":"pointermove",
"timeStamp":216691688.0,
"pointer":2,
"x":241.7142791748047,
"y":440.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691696.0,
"pointer":2,
"x":241.7142791748047,
"y":454.5714416503906
},
{
"type":"pointermove",
"timeStamp":216691703.0,
"pointer":2,
"x":242.57142639160156,
"y":467.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691712.0,
"pointer":2,
"x":243.42857360839844,
"y":477.4285583496094
},
{
"type":"pointermove",
"timeStamp":216691720.0,
"pointer":2,
"x":244.85714721679688,
"y":485.71429443359375
},
{
"type":"pointermove",
"timeStamp":216691727.0,
"pointer":2,
"x":246.2857208251953,
"y":493.1428527832031
},
{
"type":"pointermove",
"timeStamp":216691735.0,
"pointer":2,
"x":248.0,
"y":499.71429443359375
},
{
"type":"pointerup",
"timeStamp":216691750.0,
"pointer":2,
"x":248.0,
"y":499.71429443359375
},
{
"type":"pointerdown",
"timeStamp":216692255.0,
"pointer":3,
"x":249.42857360839844,
"y":351.4285583496094
},
{
"type":"pointermove",
"timeStamp":216692270.0,
"pointer":3,
"x":249.42857360839844,
"y":351.4285583496094
},
{
"type":"pointermove",
"timeStamp":216692309.0,
"pointer":3,
"x":246.2857208251953,
"y":361.71429443359375
},
{
"type":"pointermove",
"timeStamp":216692317.0,
"pointer":3,
"x":244.0,
"y":368.5714416503906
},
{
"type":"pointermove",
"timeStamp":216692325.0,
"pointer":3,
"x":241.42857360839844,
"y":377.71429443359375
},
{
"type":"pointermove",
"timeStamp":216692333.0,
"pointer":3,
"x":237.7142791748047,
"y":391.71429443359375
},
{
"type":"pointermove",
"timeStamp":216692340.0,
"pointer":3,
"x":235.14285278320312,
"y":406.5714416503906
},
{
"type":"pointermove",
"timeStamp":216692348.0,
"pointer":3,
"x":232.57142639160156,
"y":421.4285583496094
},
{
"type":"pointermove",
"timeStamp":216692356.0,
"pointer":3,
"x":230.2857208251953,
"y":436.5714416503906
},
{
"type":"pointermove",
"timeStamp":216692363.0,
"pointer":3,
"x":228.2857208251953,
"y":451.71429443359375
},
{
"type":"pointermove",
"timeStamp":216692371.0,
"pointer":3,
"x":227.42857360839844,
"y":466.0
},
{
"type":"pointermove",
"timeStamp":216692378.0,
"pointer":3,
"x":226.2857208251953,
"y":479.71429443359375
},
{
"type":"pointermove",
"timeStamp":216692387.0,
"pointer":3,
"x":225.7142791748047,
"y":491.71429443359375
},
{
"type":"pointermove",
"timeStamp":216692395.0,
"pointer":3,
"x":225.14285278320312,
"y":501.71429443359375
},
{
"type":"pointermove",
"timeStamp":216692402.0,
"pointer":3,
"x":224.85714721679688,
"y":509.1428527832031
},
{
"type":"pointermove",
"timeStamp":216692410.0,
"pointer":3,
"x":224.57142639160156,
"y":514.8571166992188
},
{
"type":"pointermove",
"timeStamp":216692418.0,
"pointer":3,
"x":224.2857208251953,
"y":519.4285888671875
},
{
"type":"pointermove",
"timeStamp":216692425.0,
"pointer":3,
"x":224.0,
"y":523.4285888671875
},
{
"type":"pointermove",
"timeStamp":216692433.0,
"pointer":3,
"x":224.0,
"y":527.1428833007812
},
{
"type":"pointermove",
"timeStamp":216692441.0,
"pointer":3,
"x":224.0,
"y":530.5714111328125
},
{
"type":"pointermove",
"timeStamp":216692448.0,
"pointer":3,
"x":224.0,
"y":533.1428833007812
},
{
"type":"pointermove",
"timeStamp":216692456.0,
"pointer":3,
"x":224.0,
"y":535.4285888671875
},
{
"type":"pointermove",
"timeStamp":216692464.0,
"pointer":3,
"x":223.7142791748047,
"y":536.8571166992188
},
{
"type":"pointermove",
"timeStamp":216692472.0,
"pointer":3,
"x":223.7142791748047,
"y":538.2857055664062
},
{
"type":"pointerup",
"timeStamp":216692487.0,
"pointer":3,
"x":223.7142791748047,
"y":538.2857055664062
},
{
"type":"pointerdown",
"timeStamp":216692678.0,
"pointer":4,
"x":221.42857360839844,
"y":526.2857055664062
},
{
"type":"pointermove",
"timeStamp":216692701.0,
"pointer":4,
"x":220.57142639160156,
"y":514.8571166992188
},
{
"type":"pointermove",
"timeStamp":216692708.0,
"pointer":4,
"x":220.2857208251953,
"y":508.0
},
{
"type":"pointermove",
"timeStamp":216692716.0,
"pointer":4,
"x":220.2857208251953,
"y":498.0
},
{
"type":"pointermove",
"timeStamp":216692724.0,
"pointer":4,
"x":221.14285278320312,
"y":484.28570556640625
},
{
"type":"pointermove",
"timeStamp":216692732.0,
"pointer":4,
"x":221.7142791748047,
"y":469.4285583496094
},
{
"type":"pointermove",
"timeStamp":216692740.0,
"pointer":4,
"x":223.42857360839844,
"y":453.1428527832031
},
{
"type":"pointermove",
"timeStamp":216692748.0,
"pointer":4,
"x":225.7142791748047,
"y":436.28570556640625
},
{
"type":"pointermove",
"timeStamp":216692755.0,
"pointer":4,
"x":229.14285278320312,
"y":418.28570556640625
},
{
"type":"pointermove",
"timeStamp":216692763.0,
"pointer":4,
"x":232.85714721679688,
"y":400.28570556640625
},
{
"type":"pointermove",
"timeStamp":216692770.0,
"pointer":4,
"x":236.85714721679688,
"y":382.5714416503906
},
{
"type":"pointermove",
"timeStamp":216692778.0,
"pointer":4,
"x":241.14285278320312,
"y":366.0
},
{
"type":"pointermove",
"timeStamp":216692786.0,
"pointer":4,
"x":244.85714721679688,
"y":350.28570556640625
},
{
"type":"pointermove",
"timeStamp":216692793.0,
"pointer":4,
"x":249.14285278320312,
"y":335.4285583496094
},
{
"type":"pointerup",
"timeStamp":216692809.0,
"pointer":4,
"x":249.14285278320312,
"y":335.4285583496094
},
{
"type":"pointerdown",
"timeStamp":216693222.0,
"pointer":5,
"x":224.0,
"y":545.4285888671875
},
{
"type":"pointermove",
"timeStamp":216693245.0,
"pointer":5,
"x":224.0,
"y":545.4285888671875
},
{
"type":"pointermove",
"timeStamp":216693275.0,
"pointer":5,
"x":222.85714721679688,
"y":535.1428833007812
},
{
"type":"pointermove",
"timeStamp":216693284.0,
"pointer":5,
"x":222.85714721679688,
"y":528.8571166992188
},
{
"type":"pointermove",
"timeStamp":216693291.0,
"pointer":5,
"x":222.2857208251953,
"y":518.5714111328125
},
{
"type":"pointermove",
"timeStamp":216693299.0,
"pointer":5,
"x":222.0,
"y":503.4285583496094
},
{
"type":"pointermove",
"timeStamp":216693307.0,
"pointer":5,
"x":222.0,
"y":485.4285583496094
},
{
"type":"pointermove",
"timeStamp":216693314.0,
"pointer":5,
"x":221.7142791748047,
"y":464.0
},
{
"type":"pointermove",
"timeStamp":216693322.0,
"pointer":5,
"x":222.2857208251953,
"y":440.28570556640625
},
{
"type":"pointerup",
"timeStamp":216693337.0,
"pointer":5,
"x":222.2857208251953,
"y":440.28570556640625
},
{
"type":"pointerdown",
"timeStamp":216693985.0,
"pointer":6,
"x":208.0,
"y":544.0
},
{
"type":"pointermove",
"timeStamp":216694047.0,
"pointer":6,
"x":208.57142639160156,
"y":532.2857055664062
},
{
"type":"pointermove",
"timeStamp":216694054.0,
"pointer":6,
"x":208.85714721679688,
"y":525.7142944335938
},
{
"type":"pointermove",
"timeStamp":216694062.0,
"pointer":6,
"x":208.85714721679688,
"y":515.1428833007812
},
{
"type":"pointermove",
"timeStamp":216694070.0,
"pointer":6,
"x":208.0,
"y":501.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694077.0,
"pointer":6,
"x":207.42857360839844,
"y":487.1428527832031
},
{
"type":"pointermove",
"timeStamp":216694085.0,
"pointer":6,
"x":206.57142639160156,
"y":472.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694092.0,
"pointer":6,
"x":206.57142639160156,
"y":458.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694100.0,
"pointer":6,
"x":206.57142639160156,
"y":446.0
},
{
"type":"pointermove",
"timeStamp":216694108.0,
"pointer":6,
"x":206.57142639160156,
"y":434.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694116.0,
"pointer":6,
"x":207.14285278320312,
"y":423.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694124.0,
"pointer":6,
"x":208.57142639160156,
"y":412.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694131.0,
"pointer":6,
"x":209.7142791748047,
"y":402.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694139.0,
"pointer":6,
"x":211.7142791748047,
"y":393.1428527832031
},
{
"type":"pointermove",
"timeStamp":216694147.0,
"pointer":6,
"x":213.42857360839844,
"y":385.1428527832031
},
{
"type":"pointermove",
"timeStamp":216694154.0,
"pointer":6,
"x":215.42857360839844,
"y":378.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694162.0,
"pointer":6,
"x":217.42857360839844,
"y":371.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694169.0,
"pointer":6,
"x":219.42857360839844,
"y":366.0
},
{
"type":"pointermove",
"timeStamp":216694177.0,
"pointer":6,
"x":221.42857360839844,
"y":360.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694185.0,
"pointer":6,
"x":223.42857360839844,
"y":356.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694193.0,
"pointer":6,
"x":225.14285278320312,
"y":352.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694201.0,
"pointer":6,
"x":226.85714721679688,
"y":348.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694209.0,
"pointer":6,
"x":228.2857208251953,
"y":346.0
},
{
"type":"pointermove",
"timeStamp":216694216.0,
"pointer":6,
"x":229.14285278320312,
"y":343.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694224.0,
"pointer":6,
"x":230.0,
"y":342.0
},
{
"type":"pointermove",
"timeStamp":216694232.0,
"pointer":6,
"x":230.57142639160156,
"y":340.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694239.0,
"pointer":6,
"x":230.85714721679688,
"y":339.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694247.0,
"pointer":6,
"x":230.85714721679688,
"y":339.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694262.0,
"pointer":6,
"x":230.2857208251953,
"y":342.0
},
{
"type":"pointermove",
"timeStamp":216694270.0,
"pointer":6,
"x":228.85714721679688,
"y":346.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694278.0,
"pointer":6,
"x":227.14285278320312,
"y":352.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694286.0,
"pointer":6,
"x":225.42857360839844,
"y":359.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694294.0,
"pointer":6,
"x":223.7142791748047,
"y":367.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694301.0,
"pointer":6,
"x":222.57142639160156,
"y":376.0
},
{
"type":"pointermove",
"timeStamp":216694309.0,
"pointer":6,
"x":221.42857360839844,
"y":384.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694317.0,
"pointer":6,
"x":220.85714721679688,
"y":392.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694324.0,
"pointer":6,
"x":220.0,
"y":400.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694332.0,
"pointer":6,
"x":219.14285278320312,
"y":409.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694339.0,
"pointer":6,
"x":218.85714721679688,
"y":419.1428527832031
},
{
"type":"pointermove",
"timeStamp":216694348.0,
"pointer":6,
"x":218.2857208251953,
"y":428.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694356.0,
"pointer":6,
"x":218.2857208251953,
"y":438.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694363.0,
"pointer":6,
"x":218.2857208251953,
"y":447.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694371.0,
"pointer":6,
"x":218.2857208251953,
"y":455.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694379.0,
"pointer":6,
"x":219.14285278320312,
"y":462.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694386.0,
"pointer":6,
"x":220.0,
"y":469.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694394.0,
"pointer":6,
"x":221.14285278320312,
"y":475.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694401.0,
"pointer":6,
"x":222.0,
"y":480.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694409.0,
"pointer":6,
"x":222.85714721679688,
"y":485.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694417.0,
"pointer":6,
"x":224.0,
"y":489.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694425.0,
"pointer":6,
"x":224.85714721679688,
"y":492.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694433.0,
"pointer":6,
"x":225.42857360839844,
"y":495.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694440.0,
"pointer":6,
"x":226.0,
"y":497.1428527832031
},
{
"type":"pointermove",
"timeStamp":216694448.0,
"pointer":6,
"x":226.2857208251953,
"y":498.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694456.0,
"pointer":6,
"x":226.2857208251953,
"y":498.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694471.0,
"pointer":6,
"x":226.2857208251953,
"y":498.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694479.0,
"pointer":6,
"x":226.2857208251953,
"y":496.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694486.0,
"pointer":6,
"x":226.2857208251953,
"y":493.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694494.0,
"pointer":6,
"x":226.2857208251953,
"y":490.0
},
{
"type":"pointermove",
"timeStamp":216694502.0,
"pointer":6,
"x":226.2857208251953,
"y":486.0
},
{
"type":"pointermove",
"timeStamp":216694510.0,
"pointer":6,
"x":226.2857208251953,
"y":480.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694518.0,
"pointer":6,
"x":226.2857208251953,
"y":475.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694525.0,
"pointer":6,
"x":226.2857208251953,
"y":468.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694533.0,
"pointer":6,
"x":226.2857208251953,
"y":461.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694541.0,
"pointer":6,
"x":226.2857208251953,
"y":452.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694548.0,
"pointer":6,
"x":226.57142639160156,
"y":442.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694556.0,
"pointer":6,
"x":226.57142639160156,
"y":432.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694564.0,
"pointer":6,
"x":226.85714721679688,
"y":423.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694571.0,
"pointer":6,
"x":227.42857360839844,
"y":416.0
},
{
"type":"pointermove",
"timeStamp":216694580.0,
"pointer":6,
"x":227.7142791748047,
"y":410.0
},
{
"type":"pointermove",
"timeStamp":216694587.0,
"pointer":6,
"x":228.2857208251953,
"y":404.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694595.0,
"pointer":6,
"x":228.85714721679688,
"y":399.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694603.0,
"pointer":6,
"x":229.14285278320312,
"y":395.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694610.0,
"pointer":6,
"x":229.42857360839844,
"y":392.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694618.0,
"pointer":6,
"x":229.7142791748047,
"y":390.0
},
{
"type":"pointermove",
"timeStamp":216694625.0,
"pointer":6,
"x":229.7142791748047,
"y":388.0
},
{
"type":"pointermove",
"timeStamp":216694633.0,
"pointer":6,
"x":229.7142791748047,
"y":386.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694641.0,
"pointer":6,
"x":229.7142791748047,
"y":386.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694648.0,
"pointer":6,
"x":229.7142791748047,
"y":386.0
},
{
"type":"pointermove",
"timeStamp":216694657.0,
"pointer":6,
"x":228.85714721679688,
"y":386.0
},
{
"type":"pointermove",
"timeStamp":216694665.0,
"pointer":6,
"x":228.0,
"y":388.0
},
{
"type":"pointermove",
"timeStamp":216694672.0,
"pointer":6,
"x":226.0,
"y":392.5714416503906
},
{
"type":"pointermove",
"timeStamp":216694680.0,
"pointer":6,
"x":224.0,
"y":397.71429443359375
},
{
"type":"pointermove",
"timeStamp":216694688.0,
"pointer":6,
"x":222.0,
"y":404.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694695.0,
"pointer":6,
"x":219.7142791748047,
"y":411.1428527832031
},
{
"type":"pointermove",
"timeStamp":216694703.0,
"pointer":6,
"x":218.2857208251953,
"y":418.0
},
{
"type":"pointermove",
"timeStamp":216694710.0,
"pointer":6,
"x":217.14285278320312,
"y":425.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694718.0,
"pointer":6,
"x":215.7142791748047,
"y":433.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694726.0,
"pointer":6,
"x":214.85714721679688,
"y":442.28570556640625
},
{
"type":"pointermove",
"timeStamp":216694734.0,
"pointer":6,
"x":214.0,
"y":454.0
},
{
"type":"pointermove",
"timeStamp":216694742.0,
"pointer":6,
"x":214.0,
"y":469.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694749.0,
"pointer":6,
"x":215.42857360839844,
"y":485.4285583496094
},
{
"type":"pointermove",
"timeStamp":216694757.0,
"pointer":6,
"x":217.7142791748047,
"y":502.8571472167969
},
{
"type":"pointermove",
"timeStamp":216694765.0,
"pointer":6,
"x":221.14285278320312,
"y":521.4285888671875
},
{
"type":"pointermove",
"timeStamp":216694772.0,
"pointer":6,
"x":224.57142639160156,
"y":541.1428833007812
},
{
"type":"pointermove",
"timeStamp":216694780.0,
"pointer":6,
"x":229.14285278320312,
"y":561.1428833007812
},
{
"type":"pointermove",
"timeStamp":216694788.0,
"pointer":6,
"x":233.42857360839844,
"y":578.8571166992188
},
{
"type":"pointerup",
"timeStamp":216694802.0,
"pointer":6,
"x":233.42857360839844,
"y":578.8571166992188
},
{
"type":"pointerdown",
"timeStamp":216695344.0,
"pointer":7,
"x":253.42857360839844,
"y":310.5714416503906
},
{
"type":"pointermove",
"timeStamp":216695352.0,
"pointer":7,
"x":253.42857360839844,
"y":310.5714416503906
},
{
"type":"pointermove",
"timeStamp":216695359.0,
"pointer":7,
"x":252.85714721679688,
"y":318.0
},
{
"type":"pointermove",
"timeStamp":216695367.0,
"pointer":7,
"x":251.14285278320312,
"y":322.0
},
{
"type":"pointermove",
"timeStamp":216695375.0,
"pointer":7,
"x":248.85714721679688,
"y":327.1428527832031
},
{
"type":"pointermove",
"timeStamp":216695382.0,
"pointer":7,
"x":246.0,
"y":334.8571472167969
},
{
"type":"pointermove",
"timeStamp":216695390.0,
"pointer":7,
"x":242.57142639160156,
"y":344.5714416503906
},
{
"type":"pointermove",
"timeStamp":216695397.0,
"pointer":7,
"x":238.85714721679688,
"y":357.4285583496094
},
{
"type":"pointermove",
"timeStamp":216695406.0,
"pointer":7,
"x":235.7142791748047,
"y":371.71429443359375
},
{
"type":"pointermove",
"timeStamp":216695414.0,
"pointer":7,
"x":232.2857208251953,
"y":386.8571472167969
},
{
"type":"pointermove",
"timeStamp":216695421.0,
"pointer":7,
"x":229.42857360839844,
"y":402.0
},
{
"type":"pointermove",
"timeStamp":216695429.0,
"pointer":7,
"x":227.42857360839844,
"y":416.8571472167969
},
{
"type":"pointermove",
"timeStamp":216695437.0,
"pointer":7,
"x":226.2857208251953,
"y":431.4285583496094
},
{
"type":"pointermove",
"timeStamp":216695444.0,
"pointer":7,
"x":226.2857208251953,
"y":446.0
},
{
"type":"pointermove",
"timeStamp":216695452.0,
"pointer":7,
"x":227.7142791748047,
"y":460.28570556640625
},
{
"type":"pointermove",
"timeStamp":216695459.0,
"pointer":7,
"x":230.0,
"y":475.1428527832031
},
{
"type":"pointermove",
"timeStamp":216695467.0,
"pointer":7,
"x":232.2857208251953,
"y":489.71429443359375
},
{
"type":"pointermove",
"timeStamp":216695475.0,
"pointer":7,
"x":235.7142791748047,
"y":504.0
},
{
"type":"pointerup",
"timeStamp":216695490.0,
"pointer":7,
"x":235.7142791748047,
"y":504.0
},
{
"type":"pointerdown",
"timeStamp":216695885.0,
"pointer":8,
"x":238.85714721679688,
"y":524.0
},
{
"type":"pointermove",
"timeStamp":216695908.0,
"pointer":8,
"x":236.2857208251953,
"y":515.7142944335938
},
{
"type":"pointermove",
"timeStamp":216695916.0,
"pointer":8,
"x":234.85714721679688,
"y":509.1428527832031
},
{
"type":"pointermove",
"timeStamp":216695924.0,
"pointer":8,
"x":232.57142639160156,
"y":498.5714416503906
},
{
"type":"pointermove",
"timeStamp":216695931.0,
"pointer":8,
"x":230.57142639160156,
"y":483.71429443359375
},
{
"type":"pointermove",
"timeStamp":216695939.0,
"pointer":8,
"x":229.14285278320312,
"y":466.5714416503906
},
{
"type":"pointermove",
"timeStamp":216695947.0,
"pointer":8,
"x":229.14285278320312,
"y":446.5714416503906
},
{
"type":"pointermove",
"timeStamp":216695955.0,
"pointer":8,
"x":230.57142639160156,
"y":424.8571472167969
},
{
"type":"pointermove",
"timeStamp":216695963.0,
"pointer":8,
"x":232.57142639160156,
"y":402.28570556640625
},
{
"type":"pointermove",
"timeStamp":216695970.0,
"pointer":8,
"x":235.14285278320312,
"y":380.0
},
{
"type":"pointermove",
"timeStamp":216695978.0,
"pointer":8,
"x":238.57142639160156,
"y":359.4285583496094
},
{
"type":"pointerup",
"timeStamp":216695993.0,
"pointer":8,
"x":238.57142639160156,
"y":359.4285583496094
},
{
"type":"pointerdown",
"timeStamp":216696429.0,
"pointer":9,
"x":238.2857208251953,
"y":568.5714111328125
},
{
"type":"pointermove",
"timeStamp":216696459.0,
"pointer":9,
"x":234.0,
"y":560.0
},
{
"type":"pointermove",
"timeStamp":216696467.0,
"pointer":9,
"x":231.42857360839844,
"y":553.1428833007812
},
{
"type":"pointermove",
"timeStamp":216696475.0,
"pointer":9,
"x":228.2857208251953,
"y":543.1428833007812
},
{
"type":"pointermove",
"timeStamp":216696483.0,
"pointer":9,
"x":225.42857360839844,
"y":528.8571166992188
},
{
"type":"pointermove",
"timeStamp":216696491.0,
"pointer":9,
"x":223.14285278320312,
"y":512.2857055664062
},
{
"type":"pointermove",
"timeStamp":216696498.0,
"pointer":9,
"x":222.0,
"y":495.4285583496094
},
{
"type":"pointermove",
"timeStamp":216696506.0,
"pointer":9,
"x":221.7142791748047,
"y":477.4285583496094
},
{
"type":"pointermove",
"timeStamp":216696514.0,
"pointer":9,
"x":221.7142791748047,
"y":458.28570556640625
},
{
"type":"pointermove",
"timeStamp":216696521.0,
"pointer":9,
"x":223.14285278320312,
"y":438.0
},
{
"type":"pointermove",
"timeStamp":216696529.0,
"pointer":9,
"x":224.2857208251953,
"y":416.28570556640625
},
{
"type":"pointerup",
"timeStamp":216696544.0,
"pointer":9,
"x":224.2857208251953,
"y":416.28570556640625
},
{
"type":"pointerdown",
"timeStamp":216696974.0,
"pointer":10,
"x":218.57142639160156,
"y":530.5714111328125
},
{
"type":"pointermove",
"timeStamp":216697012.0,
"pointer":10,
"x":220.2857208251953,
"y":522.0
},
{
"type":"pointermove",
"timeStamp":216697020.0,
"pointer":10,
"x":221.14285278320312,
"y":517.7142944335938
},
{
"type":"pointermove",
"timeStamp":216697028.0,
"pointer":10,
"x":222.2857208251953,
"y":511.71429443359375
},
{
"type":"pointermove",
"timeStamp":216697036.0,
"pointer":10,
"x":224.0,
"y":504.28570556640625
},
{
"type":"pointermove",
"timeStamp":216697044.0,
"pointer":10,
"x":227.14285278320312,
"y":490.5714416503906
},
{
"type":"pointermove",
"timeStamp":216697052.0,
"pointer":10,
"x":229.42857360839844,
"y":474.0
},
{
"type":"pointermove",
"timeStamp":216697059.0,
"pointer":10,
"x":231.42857360839844,
"y":454.5714416503906
},
{
"type":"pointermove",
"timeStamp":216697067.0,
"pointer":10,
"x":233.7142791748047,
"y":431.1428527832031
},
{
"type":"pointerup",
"timeStamp":216697082.0,
"pointer":10,
"x":233.7142791748047,
"y":431.1428527832031
},
{
"type":"pointerdown",
"timeStamp":216697435.0,
"pointer":11,
"x":257.1428527832031,
"y":285.1428527832031
},
{
"type":"pointermove",
"timeStamp":216697465.0,
"pointer":11,
"x":251.7142791748047,
"y":296.8571472167969
},
{
"type":"pointermove",
"timeStamp":216697473.0,
"pointer":11,
"x":248.2857208251953,
"y":304.0
},
{
"type":"pointermove",
"timeStamp":216697481.0,
"pointer":11,
"x":244.57142639160156,
"y":314.8571472167969
},
{
"type":"pointermove",
"timeStamp":216697489.0,
"pointer":11,
"x":240.2857208251953,
"y":329.1428527832031
},
{
"type":"pointermove",
"timeStamp":216697497.0,
"pointer":11,
"x":236.85714721679688,
"y":345.1428527832031
},
{
"type":"pointermove",
"timeStamp":216697505.0,
"pointer":11,
"x":233.7142791748047,
"y":361.4285583496094
},
{
"type":"pointermove",
"timeStamp":216697512.0,
"pointer":11,
"x":231.14285278320312,
"y":378.28570556640625
},
{
"type":"pointermove",
"timeStamp":216697520.0,
"pointer":11,
"x":229.42857360839844,
"y":395.4285583496094
},
{
"type":"pointermove",
"timeStamp":216697528.0,
"pointer":11,
"x":229.42857360839844,
"y":412.8571472167969
},
{
"type":"pointermove",
"timeStamp":216697535.0,
"pointer":11,
"x":230.85714721679688,
"y":430.8571472167969
},
{
"type":"pointermove",
"timeStamp":216697543.0,
"pointer":11,
"x":233.42857360839844,
"y":449.71429443359375
},
{
"type":"pointerup",
"timeStamp":216697558.0,
"pointer":11,
"x":233.42857360839844,
"y":449.71429443359375
},
{
"type":"pointerdown",
"timeStamp":216697749.0,
"pointer":12,
"x":246.0,
"y":311.4285583496094
},
{
"type":"pointermove",
"timeStamp":216697780.0,
"pointer":12,
"x":244.57142639160156,
"y":318.28570556640625
},
{
"type":"pointermove",
"timeStamp":216697787.0,
"pointer":12,
"x":243.14285278320312,
"y":325.4285583496094
},
{
"type":"pointermove",
"timeStamp":216697795.0,
"pointer":12,
"x":241.42857360839844,
"y":336.0
},
{
"type":"pointermove",
"timeStamp":216697803.0,
"pointer":12,
"x":239.7142791748047,
"y":351.1428527832031
},
{
"type":"pointermove",
"timeStamp":216697811.0,
"pointer":12,
"x":238.2857208251953,
"y":368.5714416503906
},
{
"type":"pointermove",
"timeStamp":216697819.0,
"pointer":12,
"x":238.0,
"y":389.4285583496094
},
{
"type":"pointermove",
"timeStamp":216697826.0,
"pointer":12,
"x":239.14285278320312,
"y":412.0
},
{
"type":"pointermove",
"timeStamp":216697834.0,
"pointer":12,
"x":242.2857208251953,
"y":438.0
},
{
"type":"pointermove",
"timeStamp":216697842.0,
"pointer":12,
"x":247.42857360839844,
"y":466.8571472167969
},
{
"type":"pointermove",
"timeStamp":216697849.0,
"pointer":12,
"x":254.2857208251953,
"y":497.71429443359375
},
{
"type":"pointerup",
"timeStamp":216697864.0,
"pointer":12,
"x":254.2857208251953,
"y":497.71429443359375
},
{
"type":"pointerdown",
"timeStamp":216698321.0,
"pointer":13,
"x":250.0,
"y":306.0
},
{
"type":"pointermove",
"timeStamp":216698328.0,
"pointer":13,
"x":250.0,
"y":306.0
},
{
"type":"pointermove",
"timeStamp":216698344.0,
"pointer":13,
"x":249.14285278320312,
"y":314.0
},
{
"type":"pointermove",
"timeStamp":216698351.0,
"pointer":13,
"x":247.42857360839844,
"y":319.4285583496094
},
{
"type":"pointermove",
"timeStamp":216698359.0,
"pointer":13,
"x":245.14285278320312,
"y":326.8571472167969
},
{
"type":"pointermove",
"timeStamp":216698366.0,
"pointer":13,
"x":241.7142791748047,
"y":339.4285583496094
},
{
"type":"pointermove",
"timeStamp":216698374.0,
"pointer":13,
"x":238.57142639160156,
"y":355.71429443359375
},
{
"type":"pointermove",
"timeStamp":216698382.0,
"pointer":13,
"x":236.2857208251953,
"y":374.28570556640625
},
{
"type":"pointermove",
"timeStamp":216698390.0,
"pointer":13,
"x":235.14285278320312,
"y":396.5714416503906
},
{
"type":"pointermove",
"timeStamp":216698398.0,
"pointer":13,
"x":236.57142639160156,
"y":421.4285583496094
},
{
"type":"pointermove",
"timeStamp":216698406.0,
"pointer":13,
"x":241.14285278320312,
"y":451.4285583496094
},
{
"type":"pointerup",
"timeStamp":216698421.0,
"pointer":13,
"x":241.14285278320312,
"y":451.4285583496094
}
];
import 'package:flutter/gestures.dart';
import 'package:test/test.dart';
import 'velocity_tracker_data.dart';
bool _withinTolerance(double actual, double expected) {
const double kTolerance = 0.001; // Within .1% of expected value
double diff = (actual - expected)/expected;
return diff.abs() < kTolerance;
}
bool _checkVelocity(GestureVelocity actual, GestureVelocity expected) {
return (actual.isValid == expected.isValid) &&
_withinTolerance(actual.x, expected.x) &&
_withinTolerance(actual.y, expected.y);
}
List<PointerInputEvent> _eventFromMap(List<Map> intermediate) {
List<PointerInputEvent> events = new List<PointerInputEvent>();
for (Map entry in intermediate)
events.add(_eventFor(entry));
return events;
}
PointerInputEvent _eventFor(Map entry) {
PointerInputEvent result = new PointerInputEvent(
type: entry['type'],
timeStamp: entry['timeStamp'],
pointer: entry['pointer'],
x: entry['x'],
y: entry['y']
);
return result;
}
void main() {
List<PointerInputEvent> events = _eventFromMap(velocityEventData);
List<GestureVelocity> expected = new List<GestureVelocity>(13);
expected[0] = new GestureVelocity(isValid: true, x: 219.5762939453125, y: 1304.6705322265625);
expected[1] = new GestureVelocity(isValid: true, x: 355.6900939941406, y: 967.1700439453125);
expected[2] = new GestureVelocity(isValid: true, x: 12.651158332824707, y: -36.9227180480957);
expected[3] = new GestureVelocity(isValid: true, x: 714.1383056640625, y: -2561.540283203125);
expected[4] = new GestureVelocity(isValid: true, x: -19.658065795898438, y: -2910.080322265625);
expected[5] = new GestureVelocity(isValid: true, x: 646.8700561523438, y: 2976.982421875);
expected[6] = new GestureVelocity(isValid: true, x: 396.6878967285156, y: 2106.204833984375);
expected[7] = new GestureVelocity(isValid: true, x: 298.3150634765625, y: -3660.821044921875);
expected[8] = new GestureVelocity(isValid: true, x: -1.7460877895355225, y: -3288.16162109375);
expected[9] = new GestureVelocity(isValid: true, x: 384.6415710449219, y: -2645.6484375);
expected[10] = new GestureVelocity(isValid: true, x: 176.3752899169922, y: 2711.24609375);
expected[11] = new GestureVelocity(isValid: true, x: 396.9254455566406, y: 4280.640625);
expected[12] = new GestureVelocity(isValid: true, x: -71.51288604736328, y: 3716.74560546875);
test('Velocity tracker gives expected results', () {
VelocityTracker tracker = new VelocityTracker();
int i = 0;
for (PointerInputEvent event in events) {
if (event.type == 'pointerdown' || event.type == 'pointermove')
tracker.addPosition(event.timeStamp, event.x, event.y);
if (event.type == 'pointerup') {
_checkVelocity(tracker.getVelocity(), expected[i++]);
}
}
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment