您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

228 行
7.7 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.gestures {
public class Velocity : IEquatable<Velocity> {
public Velocity(
Offset pixelsPerSecond = null
) {
this.pixelsPerSecond = pixelsPerSecond ?? Offset.zero;
}
public static readonly Velocity zero = new Velocity();
public readonly Offset pixelsPerSecond;
public static Velocity operator -(Velocity a) {
return new Velocity(pixelsPerSecond: -a.pixelsPerSecond);
}
public static Velocity operator -(Velocity a, Velocity b) {
return new Velocity(
pixelsPerSecond: a.pixelsPerSecond - b.pixelsPerSecond);
}
public static Velocity operator +(Velocity a, Velocity b) {
return new Velocity(
pixelsPerSecond: a.pixelsPerSecond + b.pixelsPerSecond);
}
public Velocity clampMagnitude(double minValue, double maxValue) {
D.assert(minValue >= 0.0);
D.assert(maxValue >= 0.0 && maxValue >= minValue);
double valueSquared = this.pixelsPerSecond.distanceSquared;
if (valueSquared > maxValue * maxValue) {
return new Velocity(pixelsPerSecond: (this.pixelsPerSecond / this.pixelsPerSecond.distance) * maxValue);
}
if (valueSquared < minValue * minValue) {
return new Velocity(pixelsPerSecond: (this.pixelsPerSecond / this.pixelsPerSecond.distance) * minValue);
}
return this;
}
public bool Equals(Velocity other) {
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return Equals(this.pixelsPerSecond, other.pixelsPerSecond);
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {
return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != this.GetType()) {
return false;
}
return this.Equals((Velocity) obj);
}
public override int GetHashCode() {
return (this.pixelsPerSecond != null ? this.pixelsPerSecond.GetHashCode() : 0);
}
public static bool operator ==(Velocity left, Velocity right) {
return Equals(left, right);
}
public static bool operator !=(Velocity left, Velocity right) {
return !Equals(left, right);
}
public override string ToString() {
return $"Velocity({this.pixelsPerSecond.dx:F1}, {this.pixelsPerSecond.dy:F1})";
}
}
public class VelocityEstimate {
public VelocityEstimate(
Offset pixelsPerSecond,
double confidence,
TimeSpan duration,
Offset offset
) {
D.assert(pixelsPerSecond != null);
D.assert(offset != null);
this.pixelsPerSecond = pixelsPerSecond;
this.confidence = confidence;
this.duration = duration;
this.offset = offset;
}
public readonly Offset pixelsPerSecond;
public readonly double confidence;
public readonly TimeSpan duration;
public readonly Offset offset;
public override string ToString() {
return
$"VelocityEstimate({this.pixelsPerSecond.dx:F1}, {this.pixelsPerSecond.dy:F1}; offset: {this.offset}, duration: {this.duration}, confidence: {this.confidence:F1})";
}
}
class _PointAtTime {
internal _PointAtTime(Offset point, TimeSpan time) {
D.assert(point != null);
this.point = point;
this.time = time;
}
public readonly Offset point;
public readonly TimeSpan time;
public override string ToString() {
return $"_PointAtTime({this.point} at {this.time})";
}
}
public class VelocityTracker {
const int _assumePointerMoveStoppedMilliseconds = 40;
const int _historySize = 20;
const int _horizonMilliseconds = 100;
const int _minSampleSize = 3;
readonly List<_PointAtTime> _samples =
Enumerable.Repeat<_PointAtTime>(null, _historySize).ToList();
int _index = 0;
public void addPosition(TimeSpan time, Offset position) {
this._index += 1;
if (this._index == _historySize) {
this._index = 0;
}
this._samples[this._index] = new _PointAtTime(position, time);
}
public VelocityEstimate getVelocityEstimate() {
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 sampleCount = 0;
int index = this._index;
_PointAtTime newestSample = this._samples[index];
if (newestSample == null) {
return null;
}
_PointAtTime previousSample = newestSample;
_PointAtTime oldestSample = newestSample;
do {
_PointAtTime sample = this._samples[index];
if (sample == null) {
break;
}
double age = (newestSample.time - sample.time).TotalMilliseconds;
double delta = Math.Abs((sample.time - previousSample.time).TotalMilliseconds);
previousSample = sample;
if (age > _horizonMilliseconds ||
delta > _assumePointerMoveStoppedMilliseconds) {
break;
}
oldestSample = sample;
Offset position = sample.point;
x.Add(position.dx);
y.Add(position.dy);
w.Add(1.0);
time.Add(-age);
index = (index == 0 ? _historySize : index) - 1;
sampleCount += 1;
} while (sampleCount < _historySize);
if (sampleCount >= _minSampleSize) {
LeastSquaresSolver xSolver = new LeastSquaresSolver(time, x, w);
PolynomialFit xFit = xSolver.solve(2);
if (xFit != null) {
LeastSquaresSolver ySolver = new LeastSquaresSolver(time, y, w);
PolynomialFit yFit = ySolver.solve(2);
if (yFit != null) {
return new VelocityEstimate(
pixelsPerSecond: new Offset(xFit.coefficients[1] * 1000, yFit.coefficients[1] * 1000),
confidence: xFit.confidence * yFit.confidence,
duration: newestSample.time - oldestSample.time,
offset: newestSample.point - oldestSample.point
);
}
}
}
return new VelocityEstimate(
pixelsPerSecond: Offset.zero,
confidence: 1.0,
duration: newestSample.time - oldestSample.time,
offset: newestSample.point - oldestSample.point
);
}
public Velocity getVelocity() {
VelocityEstimate estimate = this.getVelocityEstimate();
if (estimate == null || estimate.pixelsPerSecond == Offset.zero) {
return Velocity.zero;
}
return new Velocity(pixelsPerSecond: estimate.pixelsPerSecond);
}
}
}