您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
233 行
7.5 KiB
233 行
7.5 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.ui;
|
|
using UnityEngine;
|
|
|
|
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(float minValue, float maxValue) {
|
|
D.assert(minValue >= 0.0);
|
|
D.assert(maxValue >= 0.0 && maxValue >= minValue);
|
|
float valueSquared = pixelsPerSecond.distanceSquared;
|
|
if (valueSquared > maxValue * maxValue) {
|
|
return new Velocity(pixelsPerSecond: (pixelsPerSecond / pixelsPerSecond.distance) * maxValue);
|
|
}
|
|
|
|
if (valueSquared < minValue * minValue) {
|
|
return new Velocity(pixelsPerSecond: (pixelsPerSecond / pixelsPerSecond.distance) * minValue);
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
public bool Equals(Velocity other) {
|
|
if (ReferenceEquals(null, other)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, other)) {
|
|
return true;
|
|
}
|
|
|
|
return Equals(pixelsPerSecond, other.pixelsPerSecond);
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (ReferenceEquals(null, obj)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, obj)) {
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != GetType()) {
|
|
return false;
|
|
}
|
|
|
|
return Equals((Velocity) obj);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
return (pixelsPerSecond != null ? 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({pixelsPerSecond.dx:F1}, {pixelsPerSecond.dy:F1})";
|
|
}
|
|
}
|
|
|
|
public class VelocityEstimate {
|
|
public VelocityEstimate(
|
|
Offset pixelsPerSecond,
|
|
float 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 float confidence;
|
|
|
|
public readonly TimeSpan duration;
|
|
|
|
public readonly Offset offset;
|
|
|
|
public override string ToString() {
|
|
return
|
|
$"VelocityEstimate({pixelsPerSecond.dx:F1}, {pixelsPerSecond.dy:F1}; offset: {offset}, duration: {duration}, confidence: {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({point} at {time})";
|
|
}
|
|
}
|
|
|
|
public class VelocityTracker {
|
|
const int _assumePointerMoveStoppedMilliseconds = 40;
|
|
const int _historySize = 20;
|
|
const int _horizonMilliseconds = 100;
|
|
const int _minSampleSize = 3;
|
|
|
|
readonly List<_PointAtTime> _samples =
|
|
CollectionUtils.CreateRepeatedList<_PointAtTime>(null, _historySize);
|
|
|
|
int _index = 0;
|
|
|
|
public void addPosition(TimeSpan time, Offset position) {
|
|
_index += 1;
|
|
if (_index == _historySize) {
|
|
_index = 0;
|
|
}
|
|
|
|
_samples[_index] = new _PointAtTime(position, time);
|
|
}
|
|
|
|
public VelocityEstimate getVelocityEstimate() {
|
|
List<float> x = new List<float>();
|
|
List<float> y = new List<float>();
|
|
List<float> w = new List<float>();
|
|
List<float> time = new List<float>();
|
|
int sampleCount = 0;
|
|
int index = _index;
|
|
|
|
_PointAtTime newestSample = _samples[index];
|
|
if (newestSample == null) {
|
|
return null;
|
|
}
|
|
|
|
_PointAtTime previousSample = newestSample;
|
|
_PointAtTime oldestSample = newestSample;
|
|
|
|
do {
|
|
_PointAtTime sample = _samples[index];
|
|
if (sample == null) {
|
|
break;
|
|
}
|
|
|
|
float age = (float) (newestSample.time - sample.time).TotalMilliseconds;
|
|
float delta = Mathf.Abs((float) (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.0f);
|
|
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.0f,
|
|
duration: newestSample.time - oldestSample.time,
|
|
offset: newestSample.point - oldestSample.point
|
|
);
|
|
}
|
|
|
|
public Velocity getVelocity() {
|
|
VelocityEstimate estimate = getVelocityEstimate();
|
|
if (estimate == null || estimate.pixelsPerSecond == Offset.zero) {
|
|
return Velocity.zero;
|
|
}
|
|
|
|
return new Velocity(pixelsPerSecond: estimate.pixelsPerSecond);
|
|
}
|
|
}
|
|
}
|