Last active
December 26, 2022 16:36
-
-
Save phi16/936fad0969028ee36155ca9ad45622c4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using MathNet.Numerics; | |
using System; | |
using System.Collections.Generic; | |
class AnimCurve { | |
public enum CurveType { | |
Discrete, | |
Linear, | |
Smooth, | |
Degree, | |
Radian, | |
} | |
public string relativePath; | |
public Type type; | |
public string property; | |
private Func<float> value; | |
private CurveType curveType; | |
public AnimationCurve curve; | |
private int order; | |
private double threshold; | |
private bool first; | |
private float lastValue; | |
private double queueHeadTime; | |
private double queueHeadValue; | |
private Stack<double> queueTimes; | |
private Stack<double> queueValues; | |
private bool hasLastKeyframe; | |
private Keyframe lastKeyframe; | |
public AnimCurve(string relativePath, Type type, string property, Func<float> value, CurveType curveType = CurveType.Smooth) { | |
this.relativePath = relativePath; | |
this.type = type; | |
this.property = property; | |
this.value = value; | |
this.curveType = curveType; | |
if(curveType == CurveType.Discrete) { | |
order = 0; | |
} else if(curveType == CurveType.Linear) { | |
order = 1; | |
} else { | |
order = 3; | |
} | |
threshold = 0.000001; | |
curve = new AnimationCurve(); | |
first = true; | |
queueHeadTime = 0; | |
queueHeadValue = 0; | |
queueTimes = new Stack<double>(); | |
queueValues = new Stack<double>(); | |
hasLastKeyframe = false; | |
lastKeyframe = new Keyframe(0, 0); | |
} | |
public List<float> timeQueue = new List<float>(); | |
private float PickValue() { | |
float c = value(); | |
if(first) { | |
first = false; | |
lastValue = c; | |
} | |
if(curveType == CurveType.Degree) { | |
c -= lastValue; | |
c = (((c + 180) % 360 + 360) % 360) - 180; | |
c += lastValue; | |
lastValue = c; | |
} | |
return c; | |
} | |
public void Tick(float t) { | |
float v = PickValue(); | |
if(queueTimes.Count == 0) { | |
queueHeadTime = t; | |
queueHeadValue = v; | |
} | |
queueTimes.Push(t - queueHeadTime); | |
queueValues.Push(v - queueHeadValue); | |
if(queueTimes.Count <= order + 1) { | |
return; | |
} | |
double[] ts = queueTimes.ToArray(); | |
double[] vs = queueValues.ToArray(); | |
double[] coeffs = Fit.Polynomial(ts, vs, order); | |
double res = 0; | |
for(int i=0;i<queueTimes.Count;i++) { | |
double ev = Polynomial.Evaluate(ts[i], coeffs); | |
double d = vs[i] - ev; | |
res += d * d; | |
} | |
if(res > threshold) { | |
Flush(false); | |
} | |
} | |
public void Done() { | |
Flush(true); | |
} | |
private Keyframe CreateKeyframe(double t, double[] coeffs) { | |
double t0 = queueHeadTime; | |
double t1 = queueHeadTime + t; | |
double p0 = queueHeadValue + coeffs[0]; | |
double p1 = p0; | |
for(int j=1;j<=order;j++) { | |
p1 += coeffs[j] * Math.Pow(t, j); | |
} | |
double v0 = 0; | |
double v1 = 0; | |
if(order == 1) { | |
v0 = v1 = coeffs[1]; | |
} else if(order == 3) { | |
v0 = coeffs[1]; | |
v1 = coeffs[1] + 2 * coeffs[2] * t + 3 * coeffs[3] * t * t; | |
} | |
if(!hasLastKeyframe) { | |
hasLastKeyframe = true; | |
lastKeyframe = new Keyframe((float) t0, (float) p0, 0, 0); | |
} | |
lastKeyframe.outTangent = (float) v0; | |
Keyframe keyframe = new Keyframe((float) t1, (float) p1, (float) v1, 0); | |
return keyframe; | |
} | |
private void Flush(bool final) { | |
if(queueTimes.Count == 0) return; | |
double qht = 0; | |
double qhv = 0; | |
Stack<double> qt = new Stack<double>(); | |
Stack<double> qv = new Stack<double>(); | |
if(!final) { | |
if(order == 3) { | |
double t1 = queueTimes.Pop(); | |
double t0 = queueTimes.Pop(); | |
qht = queueHeadTime + t0; | |
qt.Push(0); | |
qt.Push(t1 - t0); | |
double v1 = queueValues.Pop(); | |
double v0 = queueValues.Pop(); | |
qhv = queueHeadValue + v0; | |
qv.Push(0); | |
qv.Push(v1 - v0); | |
queueTimes.Push(t0); | |
queueValues.Push(v0); | |
} else { | |
double t0 = queueTimes.Pop(); | |
qht = queueHeadTime + t0; | |
qt.Push(0); | |
double v0 = queueValues.Pop(); | |
qhv = queueHeadValue + v0; | |
qv.Push(0); | |
} | |
} | |
double[] ts = queueTimes.ToArray(); | |
double[] vs = queueValues.ToArray(); | |
double[] coeffs; | |
if(ts.Length >= order + 1) { | |
coeffs = Fit.Polynomial(ts, vs, order); | |
} else { | |
double[] cs = Fit.Polynomial(ts, vs, ts.Length - 1); | |
coeffs = new double[order+1]; | |
cs.CopyTo(coeffs, 0); | |
} | |
Keyframe keyframe = CreateKeyframe(ts[0], coeffs); | |
int res = curve.AddKey(lastKeyframe); | |
if(res == -1) { | |
Debug.LogError("Failed to add keyframe"); | |
} | |
if(final || order != 3) { | |
curve.AddKey(keyframe); | |
hasLastKeyframe = false; | |
} else { | |
lastKeyframe = keyframe; | |
} | |
queueHeadTime = qht; | |
queueHeadValue = qhv; | |
queueTimes = qt; | |
queueValues = qv; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment