您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
812 行
28 KiB
812 行
28 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.ui;
|
|
using UnityEngine;
|
|
using Random = System.Random;
|
|
|
|
namespace Unity.UIWidgets.animation {
|
|
public abstract class ParametricCurve<T> {
|
|
public virtual T transform(float t) {
|
|
D.assert(t >= 0.0f && t <= 1.0f, () => $"parametric value {t} is outside of [0, 1] range.");
|
|
return transformInternal(t);
|
|
}
|
|
|
|
protected virtual T transformInternal(float t) {
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override string ToString() {
|
|
return $"{GetType()}";
|
|
}
|
|
}
|
|
|
|
public abstract class Curve : ParametricCurve<float> {
|
|
public override float transform(float t) {
|
|
if (t == 0.0f || t == 1.0f) {
|
|
return t;
|
|
}
|
|
|
|
return base.transform(t);
|
|
}
|
|
|
|
public Curve flipped {
|
|
get { return new FlippedCurve(this); }
|
|
}
|
|
}
|
|
|
|
class _Linear : Curve {
|
|
protected override float transformInternal(float t) {
|
|
return t;
|
|
}
|
|
}
|
|
|
|
public class SawTooth : Curve {
|
|
public SawTooth(int count) {
|
|
this.count = count;
|
|
}
|
|
|
|
public readonly int count;
|
|
|
|
protected override float transformInternal(float t) {
|
|
t *= count;
|
|
return t - (int) t;
|
|
}
|
|
|
|
public override string ToString() {
|
|
return $"{GetType()}({count})";
|
|
}
|
|
}
|
|
|
|
public class Interval : Curve {
|
|
public Interval(float begin, float end, Curve curve = null) {
|
|
this.begin = begin;
|
|
this.end = end;
|
|
this.curve = curve ?? Curves.linear;
|
|
}
|
|
|
|
public readonly float begin;
|
|
|
|
public readonly float end;
|
|
|
|
public readonly Curve curve;
|
|
|
|
protected override float transformInternal(float t) {
|
|
D.assert(t >= 0.0 && t <= 1.0);
|
|
D.assert(begin >= 0.0);
|
|
D.assert(begin <= 1.0);
|
|
D.assert(end >= 0.0);
|
|
D.assert(end <= 1.0);
|
|
D.assert(end >= begin);
|
|
t = ((t - begin) / (end - begin)).clamp(0.0f, 1.0f);
|
|
if (t == 0.0 || t == 1.0) {
|
|
return t;
|
|
}
|
|
|
|
return curve.transform(t);
|
|
}
|
|
|
|
public override string ToString() {
|
|
if (!(curve is _Linear)) {
|
|
return $"{GetType()}({begin}\u22EF{end}\u27A9{curve}";
|
|
}
|
|
|
|
return $"{GetType()}({begin}\u22EF{end})";
|
|
}
|
|
}
|
|
|
|
public class Threshold : Curve {
|
|
public Threshold(float threshold) {
|
|
this.threshold = threshold;
|
|
}
|
|
|
|
public readonly float threshold;
|
|
|
|
protected override float transformInternal(float t) {
|
|
D.assert(threshold >= 0.0);
|
|
D.assert(threshold <= 1.0);
|
|
return t < threshold ? 0.0f : 1.0f;
|
|
}
|
|
}
|
|
|
|
public class Cubic : Curve {
|
|
public Cubic(float a, float b, float c, float d) {
|
|
this.a = a;
|
|
this.b = b;
|
|
this.c = c;
|
|
this.d = d;
|
|
}
|
|
|
|
public readonly float a;
|
|
|
|
public readonly float b;
|
|
|
|
public readonly float c;
|
|
|
|
public readonly float d;
|
|
|
|
const float _cubicErrorBound = 0.001f;
|
|
|
|
float _evaluateCubic(float a, float b, float m) {
|
|
return 3 * a * (1 - m) * (1 - m) * m +
|
|
3 * b * (1 - m) * m * m +
|
|
m * m * m;
|
|
}
|
|
|
|
protected override float transformInternal(float t) {
|
|
float start = 0.0f;
|
|
float end = 1.0f;
|
|
while (true) {
|
|
float midpoint = (start + end) / 2;
|
|
float estimate = _evaluateCubic(a, c, midpoint);
|
|
if ((t - estimate).abs() < _cubicErrorBound) {
|
|
return _evaluateCubic(b, d, midpoint);
|
|
}
|
|
|
|
if (estimate < t) {
|
|
start = midpoint;
|
|
}
|
|
else {
|
|
end = midpoint;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override string ToString() {
|
|
return $"{GetType()}({a:F2}, {b:F2}, {c:F2}, {d:F2})";
|
|
}
|
|
}
|
|
|
|
public abstract class Curve2D : ParametricCurve<Offset> {
|
|
internal IEnumerable<Curve2DSample> generateSamples(
|
|
float start = 0.0f,
|
|
float end = 1.0f,
|
|
float tolerance = 1e-10f) {
|
|
D.assert(end > start);
|
|
|
|
Random rand = new Random(samplingSeed);
|
|
bool isFlat(Offset p, Offset q, Offset r) {
|
|
Offset pr = p - r;
|
|
Offset qr = q - r;
|
|
float z = pr.dx * qr.dy - qr.dx * pr.dy;
|
|
return z * z < tolerance;
|
|
}
|
|
|
|
Curve2DSample first = new Curve2DSample(start, transform(start));
|
|
Curve2DSample last = new Curve2DSample(end, transform(end));
|
|
List<Curve2DSample> samples = new List<Curve2DSample>(){first};
|
|
|
|
void sample(Curve2DSample p, Curve2DSample q, bool forceSubdivide = false) {
|
|
float t = p.t + (0.45f + 0.1f * (float)rand.NextDouble() * (q.t - p.t));
|
|
Curve2DSample r = new Curve2DSample(t, transform(t));
|
|
|
|
if (!forceSubdivide && isFlat(p.value, q.value, r.value)) {
|
|
samples.Add(q);
|
|
}
|
|
else {
|
|
sample(p, r);
|
|
sample(r, q);
|
|
}
|
|
}
|
|
|
|
sample(first, last,
|
|
forceSubdivide: (first.value.dx - last.value.dx).abs() < tolerance &&
|
|
(first.value.dy - last.value.dy).abs() < tolerance);
|
|
|
|
return samples;
|
|
}
|
|
|
|
protected virtual int samplingSeed {
|
|
get { return 0; }
|
|
}
|
|
|
|
public float findInverse(float x) {
|
|
float start = 0.0f;
|
|
float end = 1.0f;
|
|
float mid = 0f;
|
|
|
|
float offsetToOrigin(float pos) {
|
|
return x - transform(pos).dx;
|
|
}
|
|
|
|
const float errorLimit = 1e-6f;
|
|
int count = 100;
|
|
float startValue = offsetToOrigin(start);
|
|
while ((end - start / 2.0f) > errorLimit && count > 0) {
|
|
mid = (end + start) / 2.0f;
|
|
float value = offsetToOrigin(mid);
|
|
if (value.sign() == startValue.sign()) {
|
|
start = mid;
|
|
}
|
|
else {
|
|
end = mid;
|
|
}
|
|
|
|
count--;
|
|
}
|
|
|
|
return mid;
|
|
}
|
|
}
|
|
|
|
internal class Curve2DSample {
|
|
public Curve2DSample(float t, Offset value) {
|
|
this.t = t;
|
|
this.value = value;
|
|
}
|
|
|
|
public readonly float t;
|
|
public readonly Offset value;
|
|
|
|
public override string ToString() {
|
|
return $"[{value.dx:F2}, {value.dy:F2}, {t:F2}]";
|
|
}
|
|
}
|
|
|
|
class CatmullRomSpline : Curve2D {
|
|
CatmullRomSpline(
|
|
List<Offset> controlPoints,
|
|
float? tension = 0.0f,
|
|
Offset startHandle = null,
|
|
Offset endHandle = null
|
|
) {
|
|
D.assert(controlPoints != null);
|
|
D.assert(tension != null);
|
|
D.assert(tension <= 1.0f, () => $"tension {tension} must not be greater than 1.0.");
|
|
D.assert(tension >= 0.0f, () => $"tension {tension} must not be negative.");
|
|
D.assert(controlPoints.Count > 3, () => "There must be at least four control points to create a CatmullRomSpline.");
|
|
_controlPoints = controlPoints;
|
|
_startHandle = startHandle;
|
|
_endHandle = endHandle;
|
|
_tension = tension;
|
|
_cubicSegments = new List<List<Offset>>();
|
|
}
|
|
|
|
internal CatmullRomSpline(
|
|
List<Offset> controlPoints,
|
|
float? tension = 0.0f,
|
|
Offset startHandle = null,
|
|
Offset endHandle = null,
|
|
List<List<Offset>> cubicSegments = null
|
|
) {
|
|
D.assert(cubicSegments != null);
|
|
_controlPoints = controlPoints;
|
|
_startHandle = startHandle;
|
|
_endHandle = endHandle;
|
|
_tension = tension;
|
|
_cubicSegments = new List<List<Offset>>();
|
|
}
|
|
|
|
public static CatmullRomSpline precompute(
|
|
List<Offset> controlPoints,
|
|
float? tension = 0.0f,
|
|
Offset startHandle = null,
|
|
Offset endHandle = null
|
|
) {
|
|
D.assert(controlPoints != null);
|
|
D.assert(tension != null);
|
|
D.assert(tension <= 1.0f, () => $"tension {tension} must not be greater than 1.0.");
|
|
D.assert(tension >= 0.0f, () => $"tension {tension} must not be negative.");
|
|
D.assert(controlPoints.Count > 3, () => "There must be at least four control points to create a CatmullRomSpline.");
|
|
return new CatmullRomSpline(
|
|
controlPoints: null,
|
|
tension: null,
|
|
startHandle: null,
|
|
endHandle: null,
|
|
cubicSegments: _computeSegments(controlPoints, tension, startHandle: startHandle, endHandle: endHandle)
|
|
);
|
|
}
|
|
|
|
static List<List<Offset>> _computeSegments(
|
|
List<Offset> controlPoints,
|
|
float? tension,
|
|
Offset startHandle,
|
|
Offset endHandle) {
|
|
startHandle = startHandle ?? controlPoints[0] * 2.0f - controlPoints[1];
|
|
endHandle = endHandle ?? controlPoints.last() * 2.0f - controlPoints[controlPoints.Count - 2];
|
|
|
|
List<Offset> allPoints = new List<Offset>();
|
|
allPoints.Add(startHandle);
|
|
allPoints.AddRange(controlPoints);
|
|
allPoints.Add(endHandle);
|
|
|
|
const float alpha = 0.5f;
|
|
float reverseTension = 1.0f - tension.Value;
|
|
List<List<Offset>> result = new List<List<Offset>>();
|
|
for (int i = 0; i < allPoints.Count - 3; ++i) {
|
|
List<Offset> curve = new List<Offset>{allPoints[i], allPoints[i + 1], allPoints[i + 2], allPoints[i + 3]};
|
|
Offset diffCurve10 = curve[1] - curve[0];
|
|
Offset diffCurve21 = curve[2] - curve[1];
|
|
Offset diffCurve32 = curve[3] - curve[2];
|
|
float t01 = Mathf.Pow(diffCurve10.distance, alpha);
|
|
float t12 = Mathf.Pow(diffCurve21.distance, alpha);
|
|
float t23 = Mathf.Pow(diffCurve32.distance, alpha);
|
|
|
|
Offset m1 = (diffCurve21 + (diffCurve10 / t01 - (curve[2] - curve[0]) / (t01 + t12)) * t12) * reverseTension;
|
|
Offset m2 = (diffCurve21 + (diffCurve32 / t23 - (curve[3] - curve[1]) / (t12 + t23)) * t12) * reverseTension;
|
|
Offset sumM12 = m1 + m2;
|
|
|
|
List<Offset> segment = new List<Offset> {
|
|
diffCurve21 * -2.0f + sumM12,
|
|
diffCurve21 * 3.0f - m1 - sumM12,
|
|
m1,
|
|
curve[1]
|
|
};
|
|
result.Add(segment);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
readonly List<List<Offset>> _cubicSegments;
|
|
|
|
readonly List<Offset> _controlPoints;
|
|
readonly Offset _startHandle;
|
|
readonly Offset _endHandle;
|
|
readonly float? _tension;
|
|
|
|
void _initializeIfNeeded() {
|
|
if (_cubicSegments.isNotEmpty()) {
|
|
return;
|
|
}
|
|
_cubicSegments.AddRange(_computeSegments(_controlPoints, _tension, startHandle: _startHandle, endHandle: _endHandle));
|
|
}
|
|
|
|
protected override int samplingSeed {
|
|
get {
|
|
_initializeIfNeeded();
|
|
Offset seedPoint = _cubicSegments[0][1];
|
|
return ((seedPoint.dx + seedPoint.dy) * 10000).round();
|
|
}
|
|
}
|
|
|
|
protected override Offset transformInternal(float t) {
|
|
_initializeIfNeeded();
|
|
float length = _cubicSegments.Count;
|
|
float position;
|
|
float localT;
|
|
int index;
|
|
|
|
if (t < 1.0f) {
|
|
position = t * length;
|
|
localT = position - position.floor();
|
|
index = position.floor();
|
|
} else {
|
|
position = length;
|
|
localT = 1.0f;
|
|
index = _cubicSegments.Count - 1;
|
|
}
|
|
List<Offset> cubicControlPoints = _cubicSegments[index];
|
|
float localT2 = localT * localT;
|
|
return cubicControlPoints[0] * localT2 * localT
|
|
+ cubicControlPoints[1] * localT2
|
|
+ cubicControlPoints[2] * localT
|
|
+ cubicControlPoints[3];
|
|
}
|
|
}
|
|
|
|
public class CatmullRomCurve : Curve {
|
|
public CatmullRomCurve(
|
|
List<Offset> controlPoints,
|
|
float? tension = 0.0f) {
|
|
D.assert(tension != null);
|
|
|
|
this.controlPoints = controlPoints;
|
|
this.tension = tension;
|
|
|
|
D.assert(() => {
|
|
_debugAssertReasons.Clear();
|
|
return validateControlPoints(controlPoints,
|
|
tension: tension,
|
|
reasons: _debugAssertReasons);
|
|
}, () => $"control points {controlPoints} could not be validated:\n {string.Join("\n ", _debugAssertReasons)}");
|
|
_precomputedSamples = new List<Curve2DSample>();
|
|
}
|
|
|
|
internal CatmullRomCurve(
|
|
List<Offset> controlPoints,
|
|
float? tension = 0.0f,
|
|
List<Curve2DSample> precomputedSamples = null) {
|
|
D.assert(precomputedSamples != null);
|
|
D.assert(tension != null);
|
|
|
|
this.controlPoints = controlPoints;
|
|
this.tension = tension;
|
|
|
|
D.assert(() => {
|
|
_debugAssertReasons.Clear();
|
|
return validateControlPoints(controlPoints,
|
|
tension: tension,
|
|
reasons: _debugAssertReasons);
|
|
}, () => $"control points {controlPoints} could not be validated:\n {string.Join("\n ", _debugAssertReasons)}");
|
|
|
|
_precomputedSamples = precomputedSamples;
|
|
}
|
|
|
|
public static CatmullRomCurve precompute(
|
|
List<Offset> controlPoints, float? tension = 0.0f) {
|
|
return new CatmullRomCurve(
|
|
controlPoints,
|
|
tension,
|
|
_computeSamples(controlPoints, tension));
|
|
}
|
|
|
|
static List<Curve2DSample> _computeSamples(List<Offset> controlPoints, float? tension) {
|
|
List<Offset> _controlPoints = new List<Offset>();
|
|
_controlPoints.Add(Offset.zero);
|
|
_controlPoints.AddRange(controlPoints);
|
|
_controlPoints.Add(new Offset(1.0f, 1.0f));
|
|
|
|
return CatmullRomSpline.precompute(_controlPoints, tension: tension).generateSamples(
|
|
start: 0.0f, end: 1.0f, tolerance: 1e-12f).ToList();
|
|
}
|
|
|
|
static readonly List<string> _debugAssertReasons = new List<string>();
|
|
|
|
readonly List<Curve2DSample> _precomputedSamples;
|
|
|
|
public readonly List<Offset> controlPoints;
|
|
|
|
public readonly float? tension;
|
|
|
|
static bool validateControlPoints(
|
|
List<Offset> controlPoints,
|
|
float? tension = 0.0f,
|
|
List<string> reasons = null) {
|
|
D.assert(tension != null);
|
|
if (controlPoints == null) {
|
|
D.assert(() => {
|
|
reasons?.Add("Supplied control points cannot be null");
|
|
return true;
|
|
});
|
|
return false;
|
|
}
|
|
|
|
if (controlPoints.Count < 2) {
|
|
D.assert(() => {
|
|
reasons?.Add("There must be at least two points supplied to create a valid curve.");
|
|
return true;
|
|
});
|
|
return false;
|
|
}
|
|
|
|
List<Offset> _controlPoints = new List<Offset>();
|
|
_controlPoints.AddRange(controlPoints);
|
|
|
|
_controlPoints.Insert(0, Offset.zero);
|
|
_controlPoints.Add(new Offset(1.0f, 1.0f));
|
|
|
|
Offset startHandle = _controlPoints[0] * 2.0f - _controlPoints[1];
|
|
Offset endHandle = _controlPoints.last() * 2.0f - _controlPoints[_controlPoints.Count - 2];
|
|
_controlPoints.Insert(0, startHandle);
|
|
_controlPoints.Add(endHandle);
|
|
|
|
float lastX = -float.PositiveInfinity;
|
|
for (int i = 0; i < _controlPoints.Count; ++i) {
|
|
if (i > 1 &&
|
|
i < _controlPoints.Count - 2 &&
|
|
(_controlPoints[i].dx <= 0.0f || _controlPoints[i].dx >= 1.0f)) {
|
|
D.assert(() => {
|
|
reasons?.Add("Control points must have X values between 0.0 and 1.0, exclusive. " +
|
|
$"Point {i} has an x value ({_controlPoints[i].dx}) which is outside the range.");
|
|
return true;
|
|
});
|
|
return false;
|
|
}
|
|
if (_controlPoints[i].dx <= lastX) {
|
|
D.assert(() => {
|
|
reasons?.Add("Each X coordinate must be greater than the preceding X coordinate " +
|
|
$"(i.e. must be monotonically increasing in X). Point {i} has an x value of " +
|
|
$"{_controlPoints[i].dx}, which is not greater than {lastX}");
|
|
return true;
|
|
});
|
|
return false;
|
|
}
|
|
lastX = _controlPoints[i].dx;
|
|
}
|
|
|
|
bool success = true;
|
|
lastX = -float.PositiveInfinity;
|
|
const float tolerance = 1e-3f;
|
|
CatmullRomSpline testSpline = new CatmullRomSpline(_controlPoints, tension: tension);
|
|
float start = testSpline.findInverse(0.0f);
|
|
float end = testSpline.findInverse(1.0f);
|
|
IEnumerable<Curve2DSample> samplePoints = testSpline.generateSamples(start: start, end: end);
|
|
|
|
if (samplePoints.First().value.dy.abs() > tolerance ||
|
|
(1.0f - samplePoints.Last().value.dy).abs() > tolerance) {
|
|
bool bail = true;
|
|
success = false;
|
|
D.assert(() => {
|
|
reasons?.Add($"The curve has more than one Y value at X = {samplePoints.First().value.dx}. " +
|
|
"Try moving some control points further away from this value of X, or increasing " +
|
|
"the tension.");
|
|
bail = reasons == null;
|
|
return true;
|
|
});
|
|
if (bail) {
|
|
return false;
|
|
}
|
|
}
|
|
foreach (Curve2DSample sample in samplePoints) {
|
|
Offset point = sample.value;
|
|
float t = sample.t;
|
|
float x = point.dx;
|
|
if (t >= start && t <= end && (x < -1e-3f || x > 1.0f + 1e-3f)) {
|
|
bool bail = true;
|
|
success = false;
|
|
D.assert(() => {
|
|
reasons?.Add($"The resulting curve has an X value ({x}) which is outside " +
|
|
"the range [0.0, 1.0], inclusive.");
|
|
bail = reasons == null;
|
|
return true;
|
|
});
|
|
if (bail) {
|
|
return false;
|
|
}
|
|
}
|
|
if (x < lastX) {
|
|
bool bail = true;
|
|
success = false;
|
|
D.assert(() => {
|
|
reasons?.Add($"The curve has more than one Y value at x = {x}. Try moving " +
|
|
"some control points further apart in X, or increasing the tension.");
|
|
bail = reasons == null;
|
|
return true;
|
|
});
|
|
if (bail) {
|
|
return false;
|
|
}
|
|
}
|
|
lastX = x;
|
|
}
|
|
return success;
|
|
}
|
|
|
|
protected override float transformInternal(float t) {
|
|
if (_precomputedSamples.isEmpty()) {
|
|
// Compute the samples now if we were constructed lazily.
|
|
_precomputedSamples.AddRange(_computeSamples(controlPoints, tension));
|
|
}
|
|
int start = 0;
|
|
int end = _precomputedSamples.Count - 1;
|
|
int mid;
|
|
Offset value;
|
|
Offset startValue = _precomputedSamples[start].value;
|
|
Offset endValue = _precomputedSamples[end].value;
|
|
while (end - start > 1) {
|
|
mid = (end + start) / 2;
|
|
value = _precomputedSamples[mid].value;
|
|
if (t >= value.dx) {
|
|
start = mid;
|
|
startValue = value;
|
|
} else {
|
|
end = mid;
|
|
endValue = value;
|
|
}
|
|
}
|
|
float t2 = (t - startValue.dx) / (endValue.dx - startValue.dx);
|
|
return MathUtils.lerpNullableFloat(startValue.dy, endValue.dy, t2);
|
|
}
|
|
}
|
|
|
|
public class FlippedCurve : Curve {
|
|
public FlippedCurve(Curve curve) {
|
|
D.assert(curve != null);
|
|
this.curve = curve;
|
|
}
|
|
|
|
public readonly Curve curve;
|
|
|
|
protected override float transformInternal(float t) {
|
|
return 1.0f - curve.transform(1.0f - t);
|
|
}
|
|
|
|
public override string ToString() {
|
|
return $"{GetType()}({curve})";
|
|
}
|
|
}
|
|
|
|
class _DecelerateCurve : Curve {
|
|
internal _DecelerateCurve() {
|
|
}
|
|
|
|
protected override float transformInternal(float t) {
|
|
t = 1.0f - t;
|
|
return 1.0f - t * t;
|
|
}
|
|
}
|
|
|
|
class _BounceInCurve : Curve {
|
|
internal _BounceInCurve() {
|
|
}
|
|
|
|
protected override float transformInternal(float t) {
|
|
return 1.0f - Curves._bounce(1.0f - t);
|
|
}
|
|
}
|
|
|
|
class _BounceOutCurve : Curve {
|
|
internal _BounceOutCurve() {
|
|
}
|
|
|
|
protected override float transformInternal(float t) {
|
|
return Curves._bounce(t);
|
|
}
|
|
}
|
|
|
|
class _BounceInOutCurve : Curve {
|
|
internal _BounceInOutCurve() {
|
|
}
|
|
|
|
protected override float transformInternal(float t) {
|
|
if (t < 0.5f) {
|
|
return (1.0f - Curves._bounce(1.0f - t)) * 0.5f;
|
|
}
|
|
else {
|
|
return Curves._bounce(t * 2.0f - 1.0f) * 0.5f + 0.5f;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class ElasticInCurve : Curve {
|
|
public ElasticInCurve(float period = 0.4f) {
|
|
this.period = period;
|
|
}
|
|
|
|
public readonly float period;
|
|
|
|
protected override float transformInternal(float t) {
|
|
float s = period / 4.0f;
|
|
t = t - 1.0f;
|
|
return -Mathf.Pow(2.0f, 10.0f * t) * Mathf.Sin((t - s) * (Mathf.PI * 2.0f) / period);
|
|
}
|
|
|
|
public override string ToString() {
|
|
return $"{GetType()}({period})";
|
|
}
|
|
}
|
|
|
|
public class ElasticOutCurve : Curve {
|
|
public ElasticOutCurve(float period = 0.4f) {
|
|
this.period = period;
|
|
}
|
|
|
|
public readonly float period;
|
|
|
|
protected override float transformInternal(float t) {
|
|
float s = period / 4.0f;
|
|
return Mathf.Pow(2.0f, -10.0f * t) * Mathf.Sin((t - s) * (Mathf.PI * 2.0f) / period) + 1.0f;
|
|
}
|
|
|
|
public override string ToString() {
|
|
return $"{GetType()}({period})";
|
|
}
|
|
}
|
|
|
|
public class ElasticInOutCurve : Curve {
|
|
public ElasticInOutCurve(float period = 0.4f) {
|
|
this.period = period;
|
|
}
|
|
|
|
public readonly float period;
|
|
|
|
protected override float transformInternal(float t) {
|
|
float s = period / 4.0f;
|
|
t = 2.0f * t - 1.0f;
|
|
if (t < 0.0) {
|
|
return -0.5f * Mathf.Pow(2.0f, 10.0f * t) * Mathf.Sin((t - s) * (Mathf.PI * 2.0f) / period);
|
|
}
|
|
else {
|
|
return Mathf.Pow(2.0f, -10.0f * t) * Mathf.Sin((t - s) * (Mathf.PI * 2.0f) / period) * 0.5f +
|
|
1.0f;
|
|
}
|
|
}
|
|
|
|
public override string ToString() {
|
|
return $"{GetType()}({period})";
|
|
}
|
|
}
|
|
|
|
public static class Curves {
|
|
public static readonly Curve linear = new _Linear();
|
|
|
|
public static readonly Curve decelerate = new _DecelerateCurve();
|
|
|
|
public static readonly Cubic fastLinearToSlowEaseIn = new Cubic(0.18f, 1.0f, 0.04f, 1.0f);
|
|
|
|
public static readonly Curve ease = new Cubic(0.25f, 0.1f, 0.25f, 1.0f);
|
|
|
|
public static readonly Curve easeIn = new Cubic(0.42f, 0.0f, 1.0f, 1.0f);
|
|
|
|
public static readonly Cubic easeInToLinear = new Cubic(0.67f, 0.03f, 0.65f, 0.09f);
|
|
|
|
public static readonly Cubic easeInSine = new Cubic(0.47f, 0, 0.745f, 0.715f);
|
|
|
|
public static readonly Cubic easeInQuad = new Cubic(0.55f, 0.085f, 0.68f, 0.53f);
|
|
|
|
public static readonly Cubic easeInCubic = new Cubic(0.55f, 0.055f, 0.675f, 0.19f);
|
|
|
|
public static readonly Cubic easeInQuart = new Cubic(0.895f, 0.03f, 0.685f, 0.22f);
|
|
|
|
public static readonly Cubic easeInQuint = new Cubic(0.755f, 0.05f, 0.855f, 0.06f);
|
|
|
|
public static readonly Cubic easeInExpo = new Cubic(0.95f, 0.05f, 0.795f, 0.035f);
|
|
|
|
public static readonly Cubic easeInCirc = new Cubic(0.6f, 0.04f, 0.98f, 0.335f);
|
|
|
|
public static readonly Cubic easeInBack = new Cubic(0.6f, -0.28f, 0.735f, 0.045f);
|
|
|
|
public static readonly Curve easeOut = new Cubic(0.0f, 0.0f, 0.58f, 1.0f);
|
|
|
|
public static readonly Cubic linearToEaseOut = new Cubic(0.35f, 0.91f, 0.33f, 0.97f);
|
|
|
|
public static readonly Cubic easeOutSine = new Cubic(0.39f, 0.575f, 0.565f, 1.0f);
|
|
|
|
public static readonly Cubic easeOutQuad = new Cubic(0.25f, 0.46f, 0.45f, 0.94f);
|
|
|
|
public static readonly Cubic easeOutCubic = new Cubic(0.215f, 0.61f, 0.355f, 1.0f);
|
|
|
|
public static readonly Cubic easeOutQuart = new Cubic(0.165f, 0.84f, 0.44f, 1.0f);
|
|
|
|
public static readonly Cubic easeOutQuint = new Cubic(0.23f, 1.0f, 0.32f, 1.0f);
|
|
|
|
public static readonly Cubic easeOutExpo = new Cubic(0.19f, 1.0f, 0.22f, 1.0f);
|
|
|
|
public static readonly Cubic easeOutCirc = new Cubic(0.075f, 0.82f, 0.165f, 1.0f);
|
|
|
|
public static readonly Cubic easeOutBack = new Cubic(0.175f, 0.885f, 0.32f, 1.275f);
|
|
|
|
public static readonly Curve easeInOut = new Cubic(0.42f, 0.0f, 0.58f, 1.0f);
|
|
|
|
public static readonly Cubic easeInOutSine = new Cubic(0.445f, 0.05f, 0.55f, 0.95f);
|
|
|
|
public static readonly Cubic easeInOutQuad = new Cubic(0.455f, 0.03f, 0.515f, 0.955f);
|
|
|
|
public static readonly Cubic easeInOutCubic = new Cubic(0.645f, 0.045f, 0.355f, 1.0f);
|
|
|
|
public static readonly Cubic easeInOutQuart = new Cubic(0.77f, 0, 0.175f, 1.0f);
|
|
|
|
public static readonly Cubic easeInOutQuint = new Cubic(0.86f, 0, 0.07f, 1.0f);
|
|
|
|
public static readonly Cubic easeInOutExpo = new Cubic(1.0f, 0, 0, 1.0f);
|
|
|
|
public static readonly Cubic easeInOutCirc = new Cubic(0.785f, 0.135f, 0.15f, 0.86f);
|
|
|
|
public static readonly Cubic easeInOutBack = new Cubic(0.68f, -0.55f, 0.265f, 1.55f);
|
|
|
|
public static readonly Cubic fastOutSlowIn = new Cubic(0.4f, 0.0f, 0.2f, 1.0f);
|
|
|
|
public static readonly Cubic slowMiddle = new Cubic(0.15f, 0.85f, 0.85f, 0.15f);
|
|
|
|
public static readonly Curve bounceIn = new _BounceInCurve();
|
|
|
|
public static readonly Curve bounceOut = new _BounceOutCurve();
|
|
|
|
public static readonly Curve bounceInOut = new _BounceInOutCurve();
|
|
|
|
public static readonly Curve elasticIn = new ElasticInCurve();
|
|
|
|
public static readonly Curve elasticOut = new ElasticOutCurve();
|
|
|
|
public static readonly Curve elasticInOut = new ElasticInOutCurve();
|
|
|
|
internal static float _bounce(float t) {
|
|
if (t < 1.0f / 2.75f) {
|
|
return 7.5625f * t * t;
|
|
}
|
|
else if (t < 2 / 2.75f) {
|
|
t -= 1.5f / 2.75f;
|
|
return 7.5625f * t * t + 0.75f;
|
|
}
|
|
else if (t < 2.5f / 2.75f) {
|
|
t -= 2.25f / 2.75f;
|
|
return 7.5625f * t * t + 0.9375f;
|
|
}
|
|
|
|
t -= 2.625f / 2.75f;
|
|
return 7.5625f * t * t + 0.984375f;
|
|
}
|
|
}
|
|
}
|